Skip to content

Commit b305882

Browse files
committed
Fix handling of schema set by search-path in pg.
1 parent acb79b6 commit b305882

3 files changed

Lines changed: 60 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ Backwards-incompatible:
2929

3030
Improvements:
3131

32+
* Postgres introspection (`get_tables()`, `get_columns()`, `get_indexes()`,
33+
`get_primary_keys()`, `get_foreign_keys()`, `get_views()`) with no `schema`
34+
now follows the search path via `current_schema()` instead of assuming
35+
`public`, matching MySQL's `DATABASE()` behavior.
3236
* Add `SchemaMigrator(db, schema=...)`, which qualifies every table name w/the
3337
given schema (or database on MySQL). Not supported for SQLite, as the
3438
table-rebuild rewrites DDL straight from `sqlite_master`.

peewee.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4795,14 +4795,16 @@ def begin(self, isolation_level=None):
47954795

47964796
def get_tables(self, schema=None):
47974797
query = ('SELECT tablename FROM pg_catalog.pg_tables '
4798-
'WHERE schemaname = %s ORDER BY tablename')
4799-
cursor = self.execute_sql(query, (schema or 'public',))
4798+
'WHERE schemaname = COALESCE(%s, current_schema()) '
4799+
'ORDER BY tablename')
4800+
cursor = self.execute_sql(query, (schema,))
48004801
return [table for table, in cursor.fetchall()]
48014802

48024803
def get_views(self, schema=None):
48034804
query = ('SELECT viewname, definition FROM pg_catalog.pg_views '
4804-
'WHERE schemaname = %s ORDER BY viewname')
4805-
cursor = self.execute_sql(query, (schema or 'public',))
4805+
'WHERE schemaname = COALESCE(%s, current_schema()) '
4806+
'ORDER BY viewname')
4807+
cursor = self.execute_sql(query, (schema,))
48064808
return [ViewMetadata(view_name, sql.strip(' \t;'))
48074809
for (view_name, sql) in cursor.fetchall()]
48084810

@@ -4822,9 +4824,10 @@ def get_indexes(self, table, schema=None):
48224824
idxs.tablename = t.relname
48234825
AND idxs.indexname = i.relname
48244826
AND idxs.schemaname = n.nspname)
4825-
WHERE t.relname = %s AND t.relkind = %s AND n.nspname = %s
4827+
WHERE t.relname = %s AND t.relkind = %s
4828+
AND n.nspname = COALESCE(%s, current_schema())
48264829
ORDER BY idx.indisunique DESC, i.relname;"""
4827-
cursor = self.execute_sql(query, (table, 'r', schema or 'public'))
4830+
cursor = self.execute_sql(query, (table, 'r', schema))
48284831
return [IndexMetadata(name, sql.rstrip(' ;'),
48294832
[unqesc(c) for c in cols], unique, table)
48304833
for name, sql, unique, cols in cursor.fetchall()]
@@ -4842,10 +4845,11 @@ def get_columns(self, table, schema=None):
48424845
ON (t.relname = c.table_name AND t.relnamespace = n.oid)
48434846
INNER JOIN pg_catalog.pg_attribute AS a
48444847
ON (a.attrelid = t.oid AND a.attname = c.column_name)
4845-
WHERE c.table_name = %s AND c.table_schema = %s
4848+
WHERE c.table_name = %s
4849+
AND c.table_schema = COALESCE(%s, current_schema())
48464850
AND NOT a.attisdropped
48474851
ORDER BY c.ordinal_position"""
4848-
cursor = self.execute_sql(query, (table, schema or 'public'))
4852+
cursor = self.execute_sql(query, (table, schema))
48494853
pks = set(self.get_primary_keys(table, schema))
48504854
def is_ident(ident, df):
48514855
return ident == 'YES' or (df or '').startswith(
@@ -4865,9 +4869,9 @@ def get_primary_keys(self, table, schema=None):
48654869
WHERE
48664870
tc.constraint_type = %s AND
48674871
tc.table_name = %s AND
4868-
tc.table_schema = %s"""
4872+
tc.table_schema = COALESCE(%s, current_schema())"""
48694873
ctype = 'PRIMARY KEY'
4870-
cursor = self.execute_sql(query, (ctype, table, schema or 'public'))
4874+
cursor = self.execute_sql(query, (ctype, table, schema))
48714875
return [pk for pk, in cursor.fetchall()]
48724876

48734877
def get_foreign_keys(self, table, schema=None):
@@ -4890,8 +4894,8 @@ def get_foreign_keys(self, table, schema=None):
48904894
WHERE
48914895
tc.constraint_type = 'FOREIGN KEY' AND
48924896
tc.table_name = %s AND
4893-
tc.table_schema = %s"""
4894-
cursor = self.execute_sql(sql, (table, schema or 'public'))
4897+
tc.table_schema = COALESCE(%s, current_schema())"""
4898+
cursor = self.execute_sql(sql, (table, schema))
48954899
return [ForeignKeyMetadata(row[0], row[1], row[2], table, row[3],
48964900
row[4], row[5])
48974901
for row in cursor.fetchall()]

tests/db_tests.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,46 @@ def test_quoted_name(self):
756756
self.assertEqual(idx.columns, ['da"ta'])
757757

758758

759+
@requires_postgresql
760+
class TestIntrospectionSearchPath(DatabaseTestCase):
761+
"""With no schema given, introspection follows the search path."""
762+
schema = 'ispath'
763+
764+
def setUp(self):
765+
super(TestIntrospectionSearchPath, self).setUp()
766+
self.execute('DROP SCHEMA IF EXISTS %s CASCADE' % self.schema)
767+
self.execute('CREATE SCHEMA %s' % self.schema)
768+
self.execute('CREATE TABLE %s.parent (id SERIAL PRIMARY KEY)'
769+
% self.schema)
770+
self.execute('CREATE TABLE %s.child (id SERIAL PRIMARY KEY, '
771+
'parent_id INTEGER REFERENCES %s.parent (id), '
772+
'name TEXT)' % (self.schema, self.schema))
773+
self.execute('CREATE INDEX child_name ON %s.child (name)'
774+
% self.schema)
775+
self.execute('CREATE VIEW %s.child_names AS SELECT name FROM '
776+
'%s.child' % (self.schema, self.schema))
777+
self.execute('SET search_path TO %s' % self.schema)
778+
779+
def tearDown(self):
780+
try:
781+
self.execute('DROP SCHEMA IF EXISTS %s CASCADE' % self.schema)
782+
finally:
783+
super(TestIntrospectionSearchPath, self).tearDown()
784+
785+
def test_search_path(self):
786+
db = self.database
787+
self.assertEqual(db.get_tables(), ['child', 'parent'])
788+
self.assertEqual([v.name for v in db.get_views()], ['child_names'])
789+
self.assertEqual([c.name for c in db.get_columns('child')],
790+
['id', 'parent_id', 'name'])
791+
self.assertEqual(db.get_primary_keys('child'), ['id'])
792+
self.assertEqual([(fk.column, fk.dest_table)
793+
for fk in db.get_foreign_keys('child')],
794+
[('parent_id', 'parent')])
795+
self.assertTrue('child_name' in
796+
[i.name for i in db.get_indexes('child')])
797+
798+
759799
# ===========================================================================
760800
# Thread safety
761801
# ===========================================================================

0 commit comments

Comments
 (0)