Repository: ariga/atlas
Pull request #551 (2022-02-07 15:33:53)

sql/postgres: same inspect, but faster


Patch:
diff --git a/cmd/atlas/doc.tmpl b/cmd/atlas/doc.tmpl
index 165210f3..79850144 100644
--- a/cmd/atlas/doc.tmpl
+++ b/cmd/atlas/doc.tmpl
@@ -22,7 +22,7 @@ Starting [v0.3.0](https://github.com/ariga/atlas/releases/tag/v0.3.0),
 core atlas engine that is not currently released publicly. The binaries
 themselves are still released under the same [Apache License 2.0](https://github.com/ariga/atlas/blob/master/LICENSE).
 
-### Buliding from Source
+### Building from Source
 
 If you would like to build Atlas from source without the UI code run:
 ```shell
@@ -61,4 +61,4 @@ go get ariga.io/atlas/cmd/atlas
 ```
 {{ end }}
 {{ end }}
-{{ end }}
\ No newline at end of file
+{{ end }}
diff --git a/doc/md/cli/reference.md b/doc/md/cli/reference.md
index 8d9d0f68..f8c50f77 100644
--- a/doc/md/cli/reference.md
+++ b/doc/md/cli/reference.md
@@ -19,7 +19,7 @@ Starting [v0.3.0](https://github.com/ariga/atlas/releases/tag/v0.3.0),
 core atlas engine that is not currently released publicly. The binaries
 themselves are still released under the same [Apache License 2.0](https://github.com/ariga/atlas/blob/master/LICENSE).
 
-### Buliding from Source
+### Building from Source
 
 If you would like to build Atlas from source without the UI code run:
 ```shell
diff --git a/doc/md/ddl/sql_types.md b/doc/md/ddl/sql_types.md
index cabb5156..21713054 100644
--- a/doc/md/ddl/sql_types.md
+++ b/doc/md/ddl/sql_types.md
@@ -187,6 +187,7 @@ slug: /ddl/sql-types
             <td>decimal</td>
             <td>
                 <ul>
+                        <li>unsigned (bool)</li>
                         <li>precision (int)</li>
                         <li>scale (int)</li>
                 </ul>
@@ -197,6 +198,10 @@ slug: /ddl/sql-types
                     type = decimal(10,2)
                 </pre>
                 
+                <pre>
+                    unsigned = true
+                </pre>
+                
                 
             </td>
         </tr>
@@ -206,6 +211,7 @@ slug: /ddl/sql-types
             <td>double</td>
             <td>
                 <ul>
+                        <li>unsigned (bool)</li>
                         <li>precision (int)</li>
                         <li>scale (int)</li>
                 </ul>
@@ -216,6 +222,10 @@ slug: /ddl/sql-types
                     type = double(10,2)
                 </pre>
                 
+                <pre>
+                    unsigned = true
+                </pre>
+                
                 
             </td>
         </tr>
@@ -243,6 +253,7 @@ slug: /ddl/sql-types
             <td>float</td>
             <td>
                 <ul>
+                        <li>unsigned (bool)</li>
                         <li>precision (int)</li>
                         <li>scale (int)</li>
                 </ul>
@@ -253,6 +264,10 @@ slug: /ddl/sql-types
                     type = float(10,2)
                 </pre>
                 
+                <pre>
+                    unsigned = true
+                </pre>
+                
                 
             </td>
         </tr>
@@ -495,6 +510,7 @@ slug: /ddl/sql-types
             <td>numeric</td>
             <td>
                 <ul>
+                        <li>unsigned (bool)</li>
                         <li>precision (int)</li>
                         <li>scale (int)</li>
                 </ul>
@@ -505,6 +521,10 @@ slug: /ddl/sql-types
                     type = numeric(10,2)
                 </pre>
                 
+                <pre>
+                    unsigned = true
+                </pre>
+                
                 
             </td>
         </tr>
@@ -548,6 +568,7 @@ slug: /ddl/sql-types
             <td>real</td>
             <td>
                 <ul>
+                        <li>unsigned (bool)</li>
                         <li>precision (int)</li>
                         <li>scale (int)</li>
                 </ul>
@@ -558,6 +579,10 @@ slug: /ddl/sql-types
                     type = real(10,2)
                 </pre>
                 
+                <pre>
+                    unsigned = true
+                </pre>
+                
                 
             </td>
         </tr>
diff --git a/doc/md/getting-started/01-introduction.mdx b/doc/md/getting-started/01-introduction.mdx
index b96ee489..ebab8392 100644
--- a/doc/md/getting-started/01-introduction.mdx
+++ b/doc/md/getting-started/01-introduction.mdx
@@ -73,7 +73,7 @@ Starting with release [v0.3.0](https://github.com/ariga/atlas/releases/tag/v0.3.
 ֿthe distributed binaries include code for a [Management UI](../ui/intro.md) wrapping the
 core Atlas engine that is not currently released publicly. The binaries
 themselves are still released under the same [Apache License 2.0](https://github.com/ariga/atlas/blob/master/LICENSE).
-If you would like to build Atlas from source without the UI follow the instructions [here](../cli.md#building-from-source).
+If you would like to build Atlas from source without the UI follow the instructions [here](../cli/reference.md#building-from-source).
 
 To verify that the CLI is installed correctly run:
 ```shell
diff --git a/doc/package-lock.json b/doc/package-lock.json
new file mode 100644
index 00000000..5819c008
--- /dev/null
+++ b/doc/package-lock.json
@@ -0,0 +1,6 @@
+{
+  "name": "doc",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {}
+}
diff --git a/doc/website/package-lock.json b/doc/website/package-lock.json
index bbf7b699..32a1872f 100644
--- a/doc/website/package-lock.json
+++ b/doc/website/package-lock.json
@@ -5,6 +5,7 @@
   "requires": true,
   "packages": {
     "": {
+      "name": "website",
       "version": "0.0.0",
       "dependencies": {
         "@docusaurus/core": "2.0.0-beta.15",
diff --git a/sql/mysql/inspect.go b/sql/mysql/inspect.go
index a17c4e10..605dce4b 100644
--- a/sql/mysql/inspect.go
+++ b/sql/mysql/inspect.go
@@ -191,10 +191,7 @@ func (i *inspect) tables(ctx context.Context, realm *schema.Realm, opts *schema.
 			})
 		}
 	}
-	if err := rows.Close(); err != nil {
-		return err
-	}
-	return nil
+	return rows.Close()
 }
 
 // columns queries and appends the columns of the given table.
diff --git a/sql/postgres/inspect.go b/sql/postgres/inspect.go
index 2324e5a1..489ece46 100644
--- a/sql/postgres/inspect.go
+++ b/sql/postgres/inspect.go
@@ -26,157 +26,141 @@ func (i *inspect) InspectRealm(ctx context.Context, opts *schema.InspectRealmOpt
 	if err != nil {
 		return nil, err
 	}
-	realm := &schema.Realm{Schemas: schemas, Attrs: []schema.Attr{&schema.Collation{V: i.collate}, &CType{V: i.ctype}}}
-	for _, s := range schemas {
-		names, err := i.tableNames(ctx, s.Name, nil)
-		if err != nil {
-			return nil, err
-		}
-		for _, name := range names {
-			t, err := i.inspectTable(ctx, name, &schema.InspectTableOptions{Schema: s.Name}, s)
-			if err != nil {
-				return nil, err
-			}
-			s.Tables = append(s.Tables, t)
-		}
-		s.Realm = realm
+	r := schema.NewRealm(schemas...).SetCollation(i.collate)
+	r.Attrs = append(r.Attrs, &CType{V: i.ctype})
+	if err := i.inspectTables(ctx, r, nil); err != nil {
+		return nil, err
 	}
 	sqlx.LinkSchemaTables(schemas)
-	return realm, nil
+	return r, nil
 }
 
 // InspectSchema returns schema descriptions of the tables in the given schema.
 // If the schema name is empty, the result will be the attached schema.
 func (i *inspect) InspectSchema(ctx context.Context, name string, opts *schema.InspectOptions) (s *schema.Schema, err error) {
-	var schemas []*schema.Schema
-	switch name {
-	case "":
-		rows, err := i.QueryContext(ctx, "SELECT CURRENT_SCHEMA()")
-		if err != nil {
-			return nil, fmt.Errorf("postgres: query attached schema: %w", err)
-		}
-		if err := sqlx.ScanOne(rows, &name); err != nil {
-			return nil, fmt.Errorf("postgres: scan attached schema: %w", err)
-		}
-		schemas = append(schemas, &schema.Schema{Name: name})
-	default:
-		if schemas, err = i.schemas(ctx, &schema.InspectRealmOption{Schemas: []string{name}}); err != nil {
-			return nil, err
-		}
-		if len(schemas) == 0 {
-			return nil, &schema.NotExistError{
-				Err: fmt.Errorf("postgres: schema %q was not found", name),
-			}
-		}
-	}
-	names, err := i.tableNames(ctx, name, opts)
+	schemas, err := i.schemas(ctx, &schema.InspectRealmOption{Schemas: []string{name}})
 	if err != nil {
 		return nil, err
 	}
-	s = schemas[0]
-	for _, name := range names {
-		t, err := i.inspectTable(ctx, name, &schema.InspectTableOptions{Schema: s.Name}, s)
-		if err != nil {
-			return nil, err
-		}
-		s.Tables = append(s.Tables, t)
+	switch n := len(schemas); {
+	case n == 0:
+		return nil, &schema.NotExistError{Err: fmt.Errorf("postgres: schema %q was not found", name)}
+	case n > 1:
+		return nil, fmt.Errorf("postgres: %d schemas were found for %q", n, name)
+	}
+	r := schema.NewRealm(schemas...).SetCollation(i.collate)
+	r.Attrs = append(r.Attrs, &CType{V: i.ctype})
+	if err := i.inspectTables(ctx, r, opts); err != nil {
+		return nil, err
 	}
 	sqlx.LinkSchemaTables(schemas)
-	s.Realm = &schema.Realm{Schemas: schemas, Attrs: []schema.Attr{&schema.Collation{V: i.collate}, &CType{V: i.ctype}}}
-	return s, nil
+	return r.Schemas[0], nil
 }
 
-func (i *inspect) inspectTable(ctx context.Context, name string, opts *schema.InspectTableOptions, top *schema.Schema) (*schema.Table, error) {
-	t, err := i.table(ctx, name, opts)
-	if err != nil {
-		return nil, err
-	}
-	if top != nil {
-		// Link the table to its top element if provided.
-		t.Schema = top
-	}
-	if err := i.columns(ctx, t); err != nil {
-		return nil, err
-	}
-	if err := i.indexes(ctx, t); err != nil {
-		return nil, err
-	}
-	if err := i.fks(ctx, t); err != nil {
-		return nil, err
+func (i *inspect) inspectTables(ctx context.Context, r *schema.Realm, opts *schema.InspectOptions) error {
+	if err := i.tables(ctx, r, opts); err != nil {
+		return err
 	}
-	if err := i.checks(ctx, t); err != nil {
-		return nil, err
+	for _, s := range r.Schemas {
+		if len(s.Tables) == 0 {
+			continue
+		}
+		if err := i.columns(ctx, s); err != nil {
+			return err
+		}
+		if err := i.indexes(ctx, s); err != nil {
+			return err
+		}
+		if err := i.fks(ctx, s); err != nil {
+			return err
+		}
+		if err := i.checks(ctx, s); err != nil {
+			return err
+		}
 	}
-	return t, nil
+	return nil
 }
 
 // table returns the table from the database, or a NotExistError if the table was not found.
-func (i *inspect) table(ctx context.Context, name string, opts *schema.InspectTableOptions) (*schema.Table, error) {
+func (i *inspect) tables(ctx context.Context, realm *schema.Realm, opts *schema.InspectOptions) error {
 	var (
-		args  = []interface{}{name}
-		query = tableQuery
+		args  []interface{}
+		query = fmt.Sprintf(tablesQuery, nArgs(0, len(realm.Schemas)))
 	)
-	if opts != nil && opts.Schema != "" {
-		query = tableSchemaQuery
-		args = append(args, opts.Schema)
+	for _, s := range realm.Schemas {
+		args = append(args, s.Name)
 	}
-	var (
-		tSchema, comment sql.NullString
-		rows, err        = i.QueryContext(ctx, query, args...)
-	)
+	if opts != nil && len(opts.Tables) > 0 {
+		for _, t := range opts.Tables {
+			args = append(args, t)
+		}
+		query = fmt.Sprintf(tablesQueryArgs, nArgs(0, len(realm.Schemas)), nArgs(len(realm.Schemas), len(opts.Tables)))
+	}
+	rows, err := i.QueryContext(ctx, query, args...)
 	if err != nil {
-		return nil, err
+		return err
 	}
-	if err := sqlx.ScanOne(rows, &tSchema, &comment); err != nil {
-		if err == sql.ErrNoRows {
-			return nil, &schema.NotExistError{
-				Err: fmt.Errorf("postgres: table %q was not found", name),
-			}
+	defer rows.Close()
+	for rows.Next() {
+		var tSchema, name, comment sql.NullString
+		if err := rows.Scan(&tSchema, &name, &comment); err != nil {
+			return fmt.Errorf("scan table information: %w", err)
+		}
+		if !sqlx.ValidString(tSchema) || !sqlx.ValidString(name) {
+			return fmt.Errorf("invalid schema or table name: %q.%q", tSchema.String, name.String)
+		}
+		s, ok := realm.Schema(tSchema.String)
+		if !ok {
+			return fmt.Errorf("schema %q was not found in realm", tSchema.String)
+		}
+		t := &schema.Table{Name: name.String}
+		s.AddTables(t)
+		if sqlx.ValidString(comment) {
+			t.Attrs = append(t.Attrs, &schema.Comment{
+				Text: comment.String,
+			})
 		}
-		return nil, err
-	}
-	t := &schema.Table{Name: name, Schema: &schema.Schema{Name: tSchema.String}}
-	if sqlx.ValidString(comment) {
-		t.Attrs = append(t.Attrs, &schema.Comment{
-			Text: comment.String,
-		})
 	}
-	return t, nil
+	return rows.Close()
 }
 
 // columns queries and appends the columns of the given table.
-func (i *inspect) columns(ctx context.Context, t *schema.Table) error {
-	rows, err := i.QueryContext(ctx, columnsQuery, t.Schema.Name, t.Name)
+func (i *inspect) columns(ctx context.Context, s *schema.Schema) error {
+	rows, err := i.querySchema(ctx, columnsQuery, s)
 	if err != nil {
-		return fmt.Errorf("postgres: querying %q columns: %w", t.Name, err)
+		return fmt.Errorf("postgres: querying schema %q columns: %w", s.Name, err)
 	}
 	defer rows.Close()
 	for rows.Next() {
-		if err := i.addColumn(t, rows); err != nil {
+		if err := i.addColumn(s, rows); err != nil {
 			return fmt.Errorf("postgres: %w", err)
 		}
 	}
 	if err := rows.Close(); err != nil {
 		return err
 	}
-	if err := i.enumValues(ctx, t.Columns); err != nil {
+	if err := i.enumValues(ctx, s); err != nil {
 		return err
 	}
 	return nil
 }
 
 // addColumn scans the current row and adds a new column from it to the table.
-func (i *inspect) addColumn(t *schema.Table, rows *sql.Rows) error {
+func (i *inspect) addColumn(s *schema.Schema, rows *sql.Rows) error {
 	var (
-		typid, maxlen, precision, timeprecision, scale, seqstart, seqinc                               sql.NullInt64
-		name, typ, nullable, defaults, udt, identity, generation, charset, collation, comment, typtype sql.NullString
+		typid, maxlen, precision, timeprecision, scale, seqstart, seqinc                                    sql.NullInt64
+		table, name, typ, nullable, defaults, udt, identity, generation, charset, collate, comment, typtype sql.NullString
 	)
 	if err := rows.Scan(
-		&name, &typ, &nullable, &defaults, &maxlen, &precision, &timeprecision, &scale, &charset,
-		&collation, &udt, &identity, &seqstart, &seqinc, &generation, &comment, &typtype, &typid,
+		&table, &name, &typ, &nullable, &defaults, &maxlen, &precision, &timeprecision, &scale,
+		&charset, &collate, &udt, &identity, &seqstart, &seqinc, &generation, &comment, &typtype, &typid,
 	); err != nil {
 		return err
 	}
+	t, ok := s.Table(table.String)
+	if !ok {
+		return fmt.Errorf("table %q was not found in schema", table.String)
+	}
 	c := &schema.Column{
 		Name: name.String,
 		Type: &schema.ColumnType{
@@ -216,9 +200,9 @@ func (i *inspect) addColumn(t *schema.Table, rows *sql.Rows) error {
 			V: charset.String,
 		})
 	}
-	if sqlx.ValidString(collation) {
+	if sqlx.ValidString(collate) {
 		c.Attrs = append(c.Attrs, &schema.Collation{
-			V: collation.String,
+			V: collate.String,
 		})
 	}
 	t.Columns = append(t.Columns, c)
@@ -285,33 +269,31 @@ func columnType(c *columnDesc) schema.Type {
 }
 
 // enumValues fills enum columns with their values from the database.
-func (i *inspect) enumValues(ctx context.Context, columns []*schema.Column) error {
+func (i *inspect) enumValues(ctx context.Context, s *schema.Schema) error {
 	var (
 		args  []interface{}
 		ids   = make(map[int64][]*schema.EnumType)
-		query = "SELECT enumtypid, enumlabel FROM pg_enum WHERE enumtypid IN ("
+		query = "SELECT enumtypid, enumlabel FROM pg_enum WHERE enumtypid IN (%s)"
 	)
-	for _, c := range columns {
-		if enum, ok := c.Type.Type.(*enumType); ok {
-			if _, ok := ids[enum.ID]; !ok {
-				if len(args) > 0 {
-					query += ", "
+	for _, t := range s.Tables {
+		for _, c := range t.Columns {
+			if enum, ok := c.Type.Type.(*enumType); ok {
+				if _, ok := ids[enum.ID]; !ok {
+					args = append(args, enum.ID)
 				}
-				args = append(args, enum.ID)
-				query += fmt.Sprintf("$%d", len(args))
+				// Convert the intermediate type to the
+				// standard schema.EnumType.
+				e := &schema.EnumType{T: enum.T}
+				c.Type.Type = e
+				c.Type.Raw = enum.T
+				ids[enum.ID] = append(ids[enum.ID], e)
 			}
-			// Convert the intermediate type to the standard schema.enumType.
-			e := &schema.EnumType{T: enum.T}
-			c.Type.Type = e
-			c.Type.Raw = enum.T
-			ids[enum.ID] = append(ids[enum.ID], e)
 		}
 	}
-	query += ")"
 	if len(ids) == 0 {
 		return nil
 	}
-	rows, err := i.QueryContext(ctx, query, args...)
+	rows, err := i.QueryContext(ctx, fmt.Sprintf(query, nArgs(0, len(args))), args...)
 	if err != nil {
 		return fmt.Errorf("postgres: querying enum values: %w", err)
 	}
@@ -332,30 +314,34 @@ func (i *inspect) enumValues(ctx context.Context, columns []*schema.Column) erro
 }
 
 // indexes queries and appends the indexes of the given table.
-func (i *inspect) indexes(ctx context.Context, t *schema.Table) error {
-	rows, err := i.QueryContext(ctx, indexesQuery, t.Schema.Name, t.Name)
+func (i *inspect) indexes(ctx context.Context, s *schema.Schema) error {
+	rows, err := i.querySchema(ctx, indexesQuery, s)
 	if err != nil {
-		return fmt.Errorf("postgres: querying %q indexes: %w", t.Name, err)
+		return fmt.Errorf("postgres: querying schema %q indexes: %w", s.Name, err)
 	}
 	defer rows.Close()
-	if err := i.addIndexes(t, rows); err != nil {
+	if err := i.addIndexes(s, rows); err != nil {
 		return err
 	}
 	return rows.Err()
 }
 
 // addIndexes scans the rows and adds the indexes to the table.
-func (i *inspect) addIndexes(t *schema.Table, rows *sql.Rows) error {
+func (i *inspect) addIndexes(s *schema.Schema, rows *sql.Rows) error {
 	names := make(map[string]*schema.Index)
 	for rows.Next() {
 		var (
-			name, typ                            string
 			uniq, primary                        bool
+			table, name, typ                     string
 			desc, nullsfirst, nullslast          sql.NullBool
 			column, contype, pred, expr, comment sql.NullString
 		)
-		if err := rows.Scan(&name, &typ, &column, &primary, &uniq, &contype, &pred, &expr, &desc, &nullsfirst, &nullslast, &comment); err != nil {
-			return fmt.Errorf("postgres: scanning indexes for table %q: %w", t.Name, err)
+		if err := rows.Scan(&table, &name, &typ, &column, &primary, &uniq, &contype, &pred, &expr, &desc, &nullsfirst, &nullslast, &comment); err != nil {
+			return fmt.Errorf("postgres: scanning indexes for schema %q: %w", s.Name, err)
+		}
+		t, ok := s.Table(table)
+		if !ok {
+			return fmt.Errorf("table %q was not found in schema", table)
 		}
 		idx, ok := names[name]
 		if !ok {
@@ -410,42 +396,46 @@ func (i *inspect) addIndexes(t *schema.Table, rows *sql.Rows) error {
 }
 
 // fks queries and appends the foreign keys of the given table.
-func (i *inspect) fks(ctx context.Context, t *schema.Table) error {
-	rows, err := i.QueryContext(ctx, fksQuery, t.Schema.Name, t.Name)
+func (i *inspect) fks(ctx context.Context, s *schema.Schema) error {
+	rows, err := i.querySchema(ctx, fksQuery, s)
 	if err != nil {
-		return fmt.Errorf("postgres: querying %q foreign keys: %w", t.Name, err)
+		return fmt.Errorf("postgres: querying schema %q foreign keys: %w", s.Name, err)
 	}
 	defer rows.Close()
-	if err := sqlx.ScanFKs(t, rows); err != nil {
+	if err := sqlx.ScanSchemaFKs(s, rows); err != nil {
 		return fmt.Errorf("postgres: %w", err)
 	}
 	return rows.Err()
 }
 
 // checks queries and appends the check constraints of the given table.
-func (i *inspect) checks(ctx context.Context, t *schema.Table) error {
-	rows, err := i.QueryContext(ctx, checksQuery, t.Schema.Name, t.Name)
+func (i *inspect) checks(ctx context.Context, s *schema.Schema) error {
+	rows, err := i.querySchema(ctx, checksQuery, s)
 	if err != nil {
-		return fmt.Errorf("mysql: querying %q check constraints: %w", t.Name, err)
+		return fmt.Errorf("postgres: querying schema %q check constraints: %w", s.Name, err)
 	}
 	defer rows.Close()
-	if err := i.addChecks(t, rows); err != nil {
+	if err := i.addChecks(s, rows); err != nil {
 		return err
 	}
 	return rows.Err()
 }
 
 // addChecks scans the rows and adds the checks to the table.
-func (i *inspect) addChecks(t *schema.Table, rows *sql.Rows) error {
+func (i *inspect) addChecks(s *schema.Schema, rows *sql.Rows) error {
 	names := make(map[string]*schema.Check)
 	for rows.Next() {
 		var (
-			noInherit                     bool
-			name, column, clause, indexes string
+			noInherit                            bool
+			table, name, column, clause, indexes string
 		)
-		if err := rows.Scan(&name, &clause, &column, &indexes, &noInherit); err != nil {
+		if err := rows.Scan(&table, &name, &clause, &column, &indexes, &noInherit); err != nil {
 			return fmt.Errorf("postgres: scanning check: %w", err)
 		}
+		t, ok := s.Table(table)
+		if !ok {
+			return fmt.Errorf("table %q was not found in schema", table)
+		}
 		if _, ok := t.Column(column); !ok {
 			return fmt.Errorf("postgres: column %q was not found for check %q", column, name)
 		}
@@ -470,8 +460,19 @@ func (i *inspect) schemas(ctx context.Context, opts *schema.InspectRealmOption)
 		args  []interface{}
 		query = schemasQuery
 	)
-	if opts != nil && len(opts.Schemas) > 0 {
-		query, args = inStrings(opts.Schemas, schemasQueryArgs, args)
+	if opts != nil {
+		switch n := len(opts.Schemas); {
+		case n == 1 && opts.Schemas[0] == "":
+			query = fmt.Sprintf(schemasQueryArgs, "= CURRENT_SCHEMA()")
+		case n == 1 && opts.Schemas[0] != "":
+			query = fmt.Sprintf(schemasQueryArgs, "= $1")
+			args = append(args, opts.Schemas[0])
+		case n > 0:
+			query = fmt.Sprintf(schemasQueryArgs, "IN ("+nArgs(0, len(opts.Schemas))+")")
+			for _, s := range opts.Schemas {
+				args = append(args, s)
+			}
+		}
 	}
 	rows, err := i.QueryContext(ctx, query, args...)
 	if err != nil {
@@ -488,46 +489,30 @@ func (i *inspect) schemas(ctx context.Context, opts *schema.InspectRealmOption)
 			Name: name,
 		})
 	}
+	if err := rows.Close(); err != nil {
+		return nil, err
+	}
 	return schemas, nil
 }
 
-// tableNames returns a list of all tables exist in the schema.
-func (i *inspect) tableNames(ctx context.Context, schema string, opts *schema.InspectOptions) ([]string, error) {
-	query, args := tablesQuery, []interface{}{schema}
-	if opts != nil && len(opts.Tables) > 0 {
-		query, args = inStrings(opts.Tables, tablesQueryArgs, args)
-	}
-	rows, err := i.QueryContext(ctx, query, args...)
-	if err != nil {
-		return nil, fmt.Errorf("postgres: querying schema tables: %w", err)
-	}
-	names, err := sqlx.ScanStrings(rows)
-	if err != nil {
-		return nil, fmt.Errorf("postgres: scanning table names: %w", err)
+func (i *inspect) querySchema(ctx context.Context, query string, s *schema.Schema) (*sql.Rows, error) {
+	args := []interface{}{s.Name}
+	for _, t := range s.Tables {
+		args = append(args, t.Name)
 	}
-	return names, nil
+	return i.QueryContext(ctx, fmt.Sprintf(query, nArgs(1, len(s.Tables))), args...)
 }
 
-func inStrings(s []string, query string, args []interface{}) (string, []interface{}) {
+func nArgs(start, n int) string {
 	var b strings.Builder
-	switch len(s) {
-	case 1:
-		args = append(args, s[0])
-		b.WriteString("= $")
-		b.WriteString(strconv.Itoa(len(args)))
-	default:
-		b.WriteString("IN (")
-		for i := range s {
-			args = append(args, s[i])
-			if i > 0 {
-				b.WriteString(", ")
-			}
-			b.WriteByte('$')
-			b.WriteString(strconv.Itoa(len(args)))
+	for i := 1; i <= n; i++ {
+		if i > 1 {
+			b.WriteString(", ")
 		}
-		b.WriteByte(')')
+		b.WriteByte('$')
+		b.WriteString(strconv.Itoa(start + i))
 	}
-	return fmt.Sprintf(query, b.String()), args
+	return b.String()
 }
 
 func defaultExpr(c *schema.Column, x string) schema.Expr {
@@ -708,42 +693,40 @@ const (
 	// Query to list specific database schemas.
 	schemasQueryArgs = "SELECT schema_name FROM information_schema.schemata WHERE schema_name %s ORDER BY schema_name"
 
-	// Query to list schema tables.
-	tablesQuery = "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema = $1 ORDER BY table_name"
-
-	// Query to list specific schema tables.
-	tablesQueryArgs = "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema = $1 AND table_name %s ORDER BY table_name"
-
 	// Query to list table information.
-	tableQuery = `
+	tablesQuery = `
 SELECT
 	t1.table_schema,
+	t1.table_name,
 	pg_catalog.obj_description(t2.oid, 'pg_class') AS COMMENT
 FROM
-	information_schema.tables AS t1
-	INNER JOIN pg_catalog.pg_class AS t2
-	ON t1.table_name = t2.relname
+	INFORMATION_SCHEMA.TABLES AS t1
+	JOIN pg_catalog.pg_class AS t2 ON t2.oid = to_regclass(t1.table_schema || '.' || t1.table_name)::oid
 WHERE
 	t1.table_type = 'BASE TABLE'
-	AND t1.table_name = $1
-	AND t1.table_schema = (CURRENT_SCHEMA())
+	AND t1.table_schema IN (%s)
+ORDER BY
+	t1.table_schema, t1.table_name
 `
-	tableSchemaQuery = `
+	tablesQueryArgs = `
 SELECT
-	t1.TABLE_SCHEMA,
+	t1.table_schema,
+	t1.table_name,
 	pg_catalog.obj_description(t2.oid, 'pg_class') AS COMMENT
 FROM
 	INFORMATION_SCHEMA.TABLES AS t1
-	JOIN pg_catalog.pg_class AS t2
-	ON t1.table_name = t2.relname
+	JOIN pg_catalog.pg_class AS t2 ON t2.oid = to_regclass(t1.table_schema || '.' || t1.table_name)::oid
 WHERE
-	t1.TABLE_TYPE = 'BASE TABLE'
-	AND t1.TABLE_NAME = $1
-	AND t1.TABLE_SCHEMA = $2
+	t1.table_type = 'BASE TABLE'
+	AND t1.table_schema IN (%s)
+	AND t1.table_name IN (%s)
+ORDER BY
+	t1.table_schema, t1.table_name
 `
 	// Query to list table columns.
 	columnsQuery = `
 SELECT
+	t1.table_name,
 	t1.column_name,
 	t1.data_type,
 	t1.is_nullable,
@@ -767,12 +750,15 @@ FROM
 	LEFT JOIN pg_catalog.pg_type AS t2
 	ON t1.udt_name = t2.typname
 WHERE
-	TABLE_SCHEMA = $1 AND TABLE_NAME = $2
+	table_schema = $1 AND table_name IN (%s)
+ORDER BY
+	t1.table_name, t1.ordinal_position
 `
 
 	// Query to list table indexes.
 	indexesQuery = `
 SELECT
+	t.relname AS table_name,
 	i.relname AS index_name,
 	am.amname AS index_type,
 	a.attname AS column_name,
@@ -787,19 +773,18 @@ SELECT
 	obj_description(to_regclass($1 || i.relname)::oid) AS comment
 FROM
 	pg_index idx
-	JOIN pg_class i
-	ON i.oid = idx.indexrelid
-	LEFT JOIN pg_constraint c
-	ON idx.indexrelid = c.conindid
-	LEFT JOIN pg_attribute a
-	ON a.attrelid = idx.indexrelid
-	JOIN pg_am am
-	ON am.oid = i.relam
+	JOIN pg_class i ON i.oid = idx.indexrelid
+	JOIN pg_class t ON t.oid = idx.indrelid
+	JOIN pg_namespace n ON n.oid = t.relnamespace
+	LEFT JOIN pg_constraint c ON idx.indexrelid = c.conindid
+	LEFT JOIN pg_attribute a ON a.attrelid = idx.indexrelid
+	JOIN pg_am am ON am.oid = i.relam
 WHERE
-	idx.indrelid = to_regclass($1 || '.' || $2)::oid
+	n.nspname = $1
+	AND t.relname IN (%s)
 	AND COALESCE(c.contype, '') <> 'f'
 ORDER BY
-	index_name, a.attnum
+	table_name, index_name, a.attnum
 `
 	fksQuery = `
 SELECT
@@ -826,7 +811,7 @@ FROM
 WHERE
     t1.constraint_type = 'FOREIGN KEY'
     AND t1.table_schema = $1
-    AND t1.table_name = $2
+    AND t1.table_name IN (%s)
 ORDER BY
     t1.constraint_name,
     t2.ordinal_position
@@ -835,19 +820,25 @@ ORDER BY
 	// Query to list table check constraints.
 	checksQuery = `
 SELECT
+	rel.relname AS table_name,
 	t1.conname AS constraint_name,
-	pg_get_expr(t1.conbin, to_regclass($1 || '.' || $2)::oid) as expression,
+	pg_get_expr(t1.conbin, to_regclass(nsp.nspname || '.' || rel.relname)::oid) as expression,
 	t2.attname as column_name,
 	t1.conkey as column_indexes,
 	t1.connoinherit as no_inherit
 FROM
-	pg_catalog.pg_constraint t1
+	pg_constraint t1
 	JOIN pg_attribute t2
 	ON t2.attrelid = t1.conrelid
 	AND t2.attnum = ANY (t1.conkey)
+	JOIN pg_class rel
+	ON rel.oid = t1.conrelid
+	JOIN pg_namespace nsp
+	ON nsp.oid = t1.connamespace
 WHERE
 	t1.contype = 'c'
-	AND t1.conrelid = to_regclass($1 || '.' || $2)::oid
+	AND nsp.nspname = $1
+	AND rel.relname IN (%s)
 ORDER BY
 	t1.conname, array_position(t1.conkey, t2.attnum)
 `


Test patch:
diff --git a/sql/postgres/inspect_test.go b/sql/postgres/inspect_test.go
index 6e43e8e3..cdc194c4 100644
--- a/sql/postgres/inspect_test.go
+++ b/sql/postgres/inspect_test.go
@@ -16,6 +16,15 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
+// Single table queries used by the different tests.
+var (
+	queryFKs     = sqltest.Escape(fmt.Sprintf(fksQuery, "$2"))
+	queryTables  = sqltest.Escape(fmt.Sprintf(tablesQuery, "$1"))
+	queryChecks  = sqltest.Escape(fmt.Sprintf(checksQuery, "$2"))
+	queryColumns = sqltest.Escape(fmt.Sprintf(columnsQuery, "$2"))
+	queryIndexes = sqltest.Escape(fmt.Sprintf(indexesQuery, "$2"))
+)
+
 func TestDriver_InspectTable(t *testing.T) {
 	tests := []struct {
 		name   string
@@ -27,40 +36,40 @@ func TestDriver_InspectTable(t *testing.T) {
 			name: "column types",
 			before: func(m mock) {
 				m.tableExists("public", "users", true)
-				m.ExpectQuery(sqltest.Escape(columnsQuery)).
+				m.ExpectQuery(queryColumns).
 					WithArgs("public", "users").
 					WillReturnRows(sqltest.Rows(`
- column_name |          data_type          | is_nullable |         column_default          | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | character_set_name | collation_name |  udt_name   | is_identity | identity_start | identity_increment | identity_generation | comment | typtype |  oid  
--------------+-----------------------------+-------------+---------------------------------+--------------------------+-------------------+--------------------+---------------+--------------------+----------------+-------------+-------------+----------------+--------------------+---------------------+---------+---------+-------
- id          | bigint                      | NO          |                                 |                          |                64 |                    |             0 |                    |                | int8        | YES         |      100       |          1         |    BY DEFAULT       |         | b       |    20
- rank        | integer                     | YES         |                                 |                          |                32 |                    |             0 |                    |                | int4        | NO          |                |                    |                     | rank    | b       |    23
- c1          | smallint                    | NO          |           1000                  |                          |                16 |                    |             0 |                    |                | int2        | NO          |                |                    |                     |         | b       |    21
- c2          | bit                         | NO          |                                 |                        1 |                   |                    |               |                    |                | bit         | NO          |                |                    |                     |         | b       |  1560
- c3          | bit varying                 | NO          |                                 |                       10 |                   |                    |               |                    |                | varbit      | NO          |                |                    |                     |         | b       |  1562
- c4          | boolean                     | NO          |                                 |                          |                   |                    |               |                    |                | bool        | NO          |                |                    |                     |         | b       |    16
- c5          | bytea                       | NO          |                                 |                          |                   |                    |               |                    |                | bytea       | NO          |                |                    |                     |         | b       |    17
- c6          | character                   | NO          |                                 |                      100 |                   |                    |               |                    |                | bpchar      | NO          |                |                    |                     |         | b       |  1042
- c7          | character varying           | NO          |                                 |                          |                   |                    |               |                    |                | varchar     | NO          |                |                    |                     |         | b       |  1043
- c8          | cidr                        | NO          |                                 |                          |                   |                    |               |                    |                | cidr        | NO          |                |                    |                     |         | b       |   650
- c9          | circle                      | NO          |                                 |                          |                   |                    |               |                    |                | circle      | NO          |                |                    |                     |         | b       |   718
- c10         | date                        | NO          |                                 |                          |                   |                    |               |                    |                | date        | NO          |                |                    |                     |         | b       |  1082
- c11         | time with time zone         | NO          |                                 |                          |                   |                    |               |                    |                | timetz      | NO          |                |                    |                     |         | b       |  1266
- c12         | double precision            | NO          |                                 |                          |                53 |                    |               |                    |                | float8      | NO          |                |                    |                     |         | b       |   701
- c13         | real                        | NO          |           random()              |                          |                24 |                    |               |                    |                | float4      | NO          |                |                    |                     |         | b       |   700
- c14         | json                        | NO          |           '{}'::json            |                          |                   |                    |               |                    |                | json        | NO          |                |                    |                     |         | b       |   114
- c15         | jsonb                       | NO          |           '{}'::jsonb           |                          |                   |                    |               |                    |                | jsonb       | NO          |                |                    |                     |         | b       |  3802
- c16         | money                       | NO          |                                 |                          |                   |                    |               |                    |                | money       | NO          |                |                    |                     |         | b       |   790
- c17         | numeric                     | NO          |                                 |                          |                   |                    |               |                    |                | numeric     | NO          |                |                    |                     |         | b       |  1700
- c18         | numeric                     | NO          |                                 |                          |                 4 |                    |             4 |                    |                | numeric     | NO          |                |                    |                     |         | b       |  1700
- c19         | integer                     | NO          | nextval('t1_c19_seq'::regclass) |                          |                32 |                    |             0 |                    |                | int4        | NO          |                |                    |                     |         | b       |    23
- c20         | uuid                        | NO          |                                 |                          |                   |                    |               |                    |                | uuid        | NO          |                |                    |                     |         | b       |  2950
- c21         | xml                         | NO          |                                 |                          |                   |                    |               |                    |                | xml         | NO          |                |                    |                     |         | b       |   142
- c22         | ARRAY                       | YES         |                                 |                          |                   |                    |               |                    |                | _int4       | NO          |                |                    |                     |         | b       |  1007
- c23         | USER-DEFINED                | YES         |                                 |                          |                   |                    |               |                    |                | ltree       | NO          |                |                    |                     |         | b       | 16535
- c24         | USER-DEFINED                | NO          |                                 |                          |                   |                    |               |                    |                | state       | NO          |                |                    |                     |         | e       | 16774
- c25         | timestamp without time zone | NO          |            now()                |                          |                   |                  4 |               |                    |                | timestamp   | NO          |                |                    |                     |         | b       |  1114
- c26         | timestamp with time zone    | NO          |                                 |                          |                   |                  6 |               |                    |                | timestamptz | NO          |                |                    |                     |         | b       |  1184
- c27         | time without time zone      | NO          |                                 |                          |                   |                  6 |               |                    |                | time        | NO          |                |                    |                     |         | b       |  1266
+ table_name  |  column_name |          data_type          | is_nullable |         column_default          | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | character_set_name | collation_name |  udt_name   | is_identity | identity_start | identity_increment | identity_generation | comment | typtype |  oid  
+-------------+--------------+-----------------------------+-------------+---------------------------------+--------------------------+-------------------+--------------------+---------------+--------------------+----------------+-------------+-------------+----------------+--------------------+---------------------+---------+---------+-------
+ users       |  id          | bigint                      | NO          |                                 |                          |                64 |                    |             0 |                    |                | int8        | YES         |      100       |          1         |    BY DEFAULT       |         | b       |    20
+ users       |  rank        | integer                     | YES         |                                 |                          |                32 |                    |             0 |                    |                | int4        | NO          |                |                    |                     | rank    | b       |    23
+ users       |  c1          | smallint                    | NO          |           1000                  |                          |                16 |                    |             0 |                    |                | int2        | NO          |                |                    |                     |         | b       |    21
+ users       |  c2          | bit                         | NO          |                                 |                        1 |                   |                    |               |                    |                | bit         | NO          |                |                    |                     |         | b       |  1560
+ users       |  c3          | bit varying                 | NO          |                                 |                       10 |                   |                    |               |                    |                | varbit      | NO          |                |                    |                     |         | b       |  1562
+ users       |  c4          | boolean                     | NO          |                                 |                          |                   |                    |               |                    |                | bool        | NO          |                |                    |                     |         | b       |    16
+ users       |  c5          | bytea                       | NO          |                                 |                          |                   |                    |               |                    |                | bytea       | NO          |                |                    |                     |         | b       |    17
+ users       |  c6          | character                   | NO          |                                 |                      100 |                   |                    |               |                    |                | bpchar      | NO          |                |                    |                     |         | b       |  1042
+ users       |  c7          | character varying           | NO          |                                 |                          |                   |                    |               |                    |                | varchar     | NO          |                |                    |                     |         | b       |  1043
+ users       |  c8          | cidr                        | NO          |                                 |                          |                   |                    |               |                    |                | cidr        | NO          |                |                    |                     |         | b       |   650
+ users       |  c9          | circle                      | NO          |                                 |                          |                   |                    |               |                    |                | circle      | NO          |                |                    |                     |         | b       |   718
+ users       |  c10         | date                        | NO          |                                 |                          |                   |                    |               |                    |                | date        | NO          |                |                    |                     |         | b       |  1082
+ users       |  c11         | time with time zone         | NO          |                                 |                          |                   |                    |               |                    |                | timetz      | NO          |                |                    |                     |         | b       |  1266
+ users       |  c12         | double precision            | NO          |                                 |                          |                53 |                    |               |                    |                | float8      | NO          |                |                    |                     |         | b       |   701
+ users       |  c13         | real                        | NO          |           random()              |                          |                24 |                    |               |                    |                | float4      | NO          |                |                    |                     |         | b       |   700
+ users       |  c14         | json                        | NO          |           '{}'::json            |                          |                   |                    |               |                    |                | json        | NO          |                |                    |                     |         | b       |   114
+ users       |  c15         | jsonb                       | NO          |           '{}'::jsonb           |                          |                   |                    |               |                    |                | jsonb       | NO          |                |                    |                     |         | b       |  3802
+ users       |  c16         | money                       | NO          |                                 |                          |                   |                    |               |                    |                | money       | NO          |                |                    |                     |         | b       |   790
+ users       |  c17         | numeric                     | NO          |                                 |                          |                   |                    |               |                    |                | numeric     | NO          |                |                    |                     |         | b       |  1700
+ users       |  c18         | numeric                     | NO          |                                 |                          |                 4 |                    |             4 |                    |                | numeric     | NO          |                |                    |                     |         | b       |  1700
+ users       |  c19         | integer                     | NO          | nextval('t1_c19_seq'::regclass) |                          |                32 |                    |             0 |                    |                | int4        | NO          |                |                    |                     |         | b       |    23
+ users       |  c20         | uuid                        | NO          |                                 |                          |                   |                    |               |                    |                | uuid        | NO          |                |                    |                     |         | b       |  2950
+ users       |  c21         | xml                         | NO          |                                 |                          |                   |                    |               |                    |                | xml         | NO          |                |                    |                     |         | b       |   142
+ users       |  c22         | ARRAY                       | YES         |                                 |                          |                   |                    |               |                    |                | _int4       | NO          |                |                    |                     |         | b       |  1007
+ users       |  c23         | USER-DEFINED                | YES         |                                 |                          |                   |                    |               |                    |                | ltree       | NO          |                |                    |                     |         | b       | 16535
+ users       |  c24         | USER-DEFINED                | NO          |                                 |                          |                   |                    |               |                    |                | state       | NO          |                |                    |                     |         | e       | 16774
+ users       |  c25         | timestamp without time zone | NO          |            now()                |                          |                   |                  4 |               |                    |                | timestamp   | NO          |                |                    |                     |         | b       |  1114
+ users       |  c26         | timestamp with time zone    | NO          |                                 |                          |                   |                  6 |               |                    |                | timestamptz | NO          |                |                    |                     |         | b       |  1184
+ users       |  c27         | time without time zone      | NO          |                                 |                          |                   |                  6 |               |                    |                | time        | NO          |                |                    |                     |         | b       |  1266
 `))
 				m.ExpectQuery(sqltest.Escape(`SELECT enumtypid, enumlabel FROM pg_enum WHERE enumtypid IN ($1)`)).
 					WithArgs(16774).
@@ -114,26 +123,25 @@ func TestDriver_InspectTable(t *testing.T) {
 			name: "table indexes",
 			before: func(m mock) {
 				m.tableExists("public", "users", true)
-				m.ExpectQuery(sqltest.Escape(columnsQuery)).
+				m.ExpectQuery(queryColumns).
 					WithArgs("public", "users").
 					WillReturnRows(sqltest.Rows(`
- column_name |      data_type      | is_nullable |         column_default          | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | character_set_name | collation_name | udt_name | is_identity | identity_start | identity_increment | identity_generation | comment | typtype |  oid
--------------+---------------------+-------------+---------------------------------+--------------------------+-------------------+--------------------+---------------+--------------------+----------------+----------+-------------+----------------+--------------------+---------------------+---------+---------+-------
- id          | bigint              | NO          |                                 |                          |                64 |                    |             0 |                    |                | int8     | NO          |                |                    |                     |         | b       |    20
- c1          | smallint            | NO          |                                 |                          |                16 |                    |             0 |                    |                | int2     | NO          |                |                    |                     |         | b       |    21
+table_name | column_name |      data_type      | is_nullable |         column_default          | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | character_set_name | collation_name | udt_name | is_identity | identity_start | identity_increment | identity_generation | comment | typtype |  oid
+-----------+-------------+---------------------+-------------+---------------------------------+--------------------------+-------------------+--------------------+---------------+--------------------+----------------+----------+-------------+----------------+--------------------+---------------------+---------+---------+-------
+users      | id          | bigint              | NO          |                                 |                          |                64 |                    |             0 |                    |                | int8     | NO          |                |                    |                     |         | b       |    20
+users      | c1          | smallint            | NO          |                                 |                          |                16 |                    |             0 |                    |                | int2     | NO          |                |                    |                     |         | b       |    21
 `))
-				m.ExpectQuery(sqltest.Escape(indexesQuery)).
+				m.ExpectQuery(queryIndexes).
 					WithArgs("public", "users").
 					WillReturnRows(sqltest.Rows(`
-    index_name   | index_type  | column_name | primary | unique | constraint_type | predicate             |   expression              | desc | nulls_first | nulls_last | comment
------------------+-------------+-------------+---------+--------+-----------------+-----------------------+---------------------------+------+-------------+------------+-----------
- idx             | hash        | left        | f       | f      |                 |                       | "left"((c11)::text, 100)  | t    | t           | f          | boring
- idx1            | btree       | left        | f       | f      |                 | (id <> NULL::integer) | "left"((c11)::text, 100)  | t    | t           | f          |
- t1_c1_key       | btree       | c1          | f       | t      | u               |                       |                           | t    | t           | f          |
- t1_pkey         | btree       | id          | t       | t      | p               |                       |                           | t    | f           | f          |
- idx4            | btree       | c1          | f       | t      |                 |                       |                           | f    | f           | f          |
- idx4            | btree       | id          | f       | t      |                 |                       |                           | f    | f           | t          |
-
+   table_name   |    index_name   | index_type  | column_name | primary | unique | constraint_type | predicate             |   expression              | desc | nulls_first | nulls_last | comment
+----------------+-----------------+-------------+-------------+---------+--------+-----------------+-----------------------+---------------------------+------+-------------+------------+-----------
+users           | idx             | hash        | left        | f       | f      |                 |                       | "left"((c11)::text, 100)  | t    | t           | f          | boring
+users           | idx1            | btree       | left        | f       | f      |                 | (id <> NULL::integer) | "left"((c11)::text, 100)  | t    | t           | f          |
+users           | t1_c1_key       | btree       | c1          | f       | t      | u               |                       |                           | t    | t           | f          |
+users           | t1_pkey         | btree       | id          | t       | t      | p               |                       |                           | t    | f           | f          |
+users           | idx4            | btree       | c1          | f       | t      |                 |                       |                           | f    | f           | f          |
+users           | idx4            | btree       | id          | f       | t      |                 |                       |                           | f    | f           | t          |	
 `))
 				m.noFKs()
 				m.noChecks()
@@ -169,27 +177,26 @@ func TestDriver_InspectTable(t *testing.T) {
 			name: "fks",
 			before: func(m mock) {
 				m.tableExists("public", "users", true)
-				m.ExpectQuery(sqltest.Escape(columnsQuery)).
+				m.ExpectQuery(queryColumns).
 					WithArgs("public", "users").
 					WillReturnRows(sqltest.Rows(`
- column_name |      data_type      | is_nullable |         column_default          | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | character_set_name | collation_name | udt_name | is_identity | identity_start | identity_increment | identity_generation | comment | typtype |  oid
--------------+---------------------+-------------+---------------------------------+--------------------------+-------------------+--------------------+---------------+--------------------+----------------+----------+-------------+----------------+--------------------+---------------------+---------+---------+-------
- id          | integer             | NO          |                                 |                          |                32 |                    |             0 |                    |                | int      | NO          |                |                    |                     |         | b       |    20
- oid         | integer             | NO          |                                 |                          |                32 |                    |             0 |                    |                | int      | NO          |                |                    |                     |         | b       |    21
- uid         | integer             | NO          |                                 |                          |                32 |                    |             0 |                    |                | int      | NO          |                |                    |                     |         | b       |    21
+table_name | column_name |      data_type      | is_nullable |         column_default          | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | character_set_name | collation_name | udt_name | is_identity | identity_start | identity_increment | identity_generation | comment | typtype |  oid
+-----------+-------------+---------------------+-------------+---------------------------------+--------------------------+-------------------+--------------------+---------------+--------------------+----------------+----------+-------------+----------------+--------------------+---------------------+---------+---------+-------
+users      | id          | integer             | NO          |                                 |                          |                32 |                    |             0 |                    |                | int      | NO          |                |                    |                     |         | b       |    20
+users      | oid         | integer             | NO          |                                 |                          |                32 |                    |             0 |                    |                | int      | NO          |                |                    |                     |         | b       |    21
+users      | uid         | integer             | NO          |                                 |                          |                32 |                    |             0 |                    |                | int      | NO          |                |                    |                     |         | b       |    21
 `))
 				m.noIndexes()
-				m.ExpectQuery(sqltest.Escape(fksQuery)).
+				m.ExpectQuery(queryFKs).
 					WithArgs("public", "users").
 					WillReturnRows(sqltest.Rows(`
- constraint_name | table_name | column_name | table_schema | referenced_table_name | referenced_column_name | referenced_schema_name | update_rule | delete_rule
+constraint_name | table_name | column_name | table_schema | referenced_table_name | referenced_column_name | referenced_schema_name | update_rule | delete_rule
 -----------------+------------+-------------+--------------+-----------------------+------------------------+------------------------+-------------+-------------
- multi_column    | users      | id          | public       | t1                    | gid                    | public                 | NO ACTION   | CASCADE
- multi_column    | users      | id          | public       | t1                    | xid                    | public                 | NO ACTION   | CASCADE
- multi_column    | users      | oid         | public       | t1                    | gid                    | public                 | NO ACTION   | CASCADE
- multi_column    | users      | oid         | public       | t1                    | xid                    | public                 | NO ACTION   | CASCADE
- self_reference  | users      | uid         | public       | users                 | id                     | public                 | NO ACTION   | CASCADE
-
+multi_column    | users      | id          | public       | t1                    | gid                    | public                 | NO ACTION   | CASCADE
+multi_column    | users      | id          | public       | t1                    | xid                    | public                 | NO ACTION   | CASCADE
+multi_column    | users      | oid         | public       | t1                    | gid                    | public                 | NO ACTION   | CASCADE
+multi_column    | users      | oid         | public       | t1                    | xid                    | public                 | NO ACTION   | CASCADE
+self_reference  | users      | uid         | public       | users                 | id                     | public                 | NO ACTION   | CASCADE
 `))
 				m.noChecks()
 			},
@@ -198,7 +205,7 @@ func TestDriver_InspectTable(t *testing.T) {
 				require.Equal("users", t.Name)
 				require.Equal("public", t.Schema.Name)
 				fks := []*schema.ForeignKey{
-					{Symbol: "multi_column", Table: t, OnUpdate: schema.NoAction, OnDelete: schema.Cascade, RefTable: &schema.Table{Name: "t1", Schema: &schema.Schema{Name: "public"}}, RefColumns: []*schema.Column{{Name: "gid"}, {Name: "xid"}}},
+					{Symbol: "multi_column", Table: t, OnUpdate: schema.NoAction, OnDelete: schema.Cascade, RefTable: &schema.Table{Name: "t1", Schema: t.Schema}, RefColumns: []*schema.Column{{Name: "gid"}, {Name: "xid"}}},
 					{Symbol: "self_reference", Table: t, OnUpdate: schema.NoAction, OnDelete: schema.Cascade, RefTable: t},
 				}
 				columns := []*schema.Column{
@@ -217,31 +224,30 @@ func TestDriver_InspectTable(t *testing.T) {
 			name: "check",
 			before: func(m mock) {
 				m.tableExists("public", "users", true)
-				m.ExpectQuery(sqltest.Escape(columnsQuery)).
-					WithArgs("public", "users").
+				m.ExpectQuery(queryColumns).
 					WithArgs("public", "users").
 					WillReturnRows(sqltest.Rows(`
- column_name | data_type | is_nullable | column_default | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | character_set_name | collation_name | udt_name | is_identity | identity_start | identity_increment | identity_generation | comment | typtype | oid
--------------+-----------+-------------+----------------+--------------------------+-------------------+--------------------+---------------+--------------------+----------------+----------+-------------+----------------+--------------------+---------------------+---------+---------+-----
- c1          | integer   | NO          |                |                          |                32 |                    |             0 |                    |                | int4     | NO          |                |                    |                     |         | b       |  23
- c2          | integer   | NO          |                |                          |                32 |                    |             0 |                    |                | int4     | NO          |                |                    |                     |         | b       |  23
- c3          | integer   | NO          |                |                          |                32 |                    |             0 |                    |                | int4     | NO          |                |                    |                     |         | b       |  23
+table_name |column_name | data_type | is_nullable | column_default | character_maximum_length | numeric_precision | datetime_precision | numeric_scale | character_set_name | collation_name | udt_name | is_identity | identity_start | identity_increment | identity_generation | comment | typtype | oid
+-----------+------------+-----------+-------------+----------------+--------------------------+-------------------+--------------------+---------------+--------------------+----------------+----------+-------------+----------------+--------------------+---------------------+---------+---------+-----
+users      | c1         | integer   | NO          |                |                          |                32 |                    |             0 |                    |                | int4     | NO          |                |                    |                     |         | b       |  23
+users      | c2         | integer   | NO          |                |                          |                32 |                    |             0 |                    |                | int4     | NO          |                |                    |                     |         | b       |  23
+users      | c3         | integer   | NO          |                |                          |                32 |                    |             0 |                    |                | int4     | NO          |                |                    |                     |         | b       |  23
 `))
 				m.noIndexes()
 				m.noFKs()
-				m.ExpectQuery(sqltest.Escape(checksQuery)).
+				m.ExpectQuery(queryChecks).
 					WithArgs("public", "users").
 					WillReturnRows(sqltest.Rows(`
- constraint_name    |       expression        | column_name | column_indexes | no_inherit
---------------------+-------------------------+-------------+----------------+----------------
- boring             | (c1 > 1)                | c1          | {1}            | t
- users_c2_check     | (c2 > 0)                | c2          | {2}            | f
- users_c2_check1    | (c2 > 0)                | c2          | {2}            | f
- users_check        | ((c2 + c1) > 2)         | c2          | {2,1}          | f
- users_check        | ((c2 + c1) > 2)         | c1          | {2,1}          | f
- users_check1       | (((c2 + c1) + c3) > 10) | c2          | {2,1,3}        | f
- users_check1       | (((c2 + c1) + c3) > 10) | c1          | {2,1,3}        | f
- users_check1       | (((c2 + c1) + c3) > 10) | c3          | {2,1,3}        | f
+table_name   | constraint_name    |       expression        | column_name | column_indexes | no_inherit
+-------------+--------------------+-------------------------+-------------+----------------+----------------
+users        | boring             | (c1 > 1)                | c1          | {1}            | t
+users        | users_c2_check     | (c2 > 0)                | c2          | {2}            | f
+users        | users_c2_check1    | (c2 > 0)                | c2          | {2}            | f
+users        | users_check        | ((c2 + c1) > 2)         | c2          | {2,1}          | f
+users        | users_check        | ((c2 + c1) > 2)         | c1          | {2,1}          | f
+users        | users_check1       | (((c2 + c1) + c3) > 10) | c2          | {2,1,3}        | f
+users        | users_check1       | (((c2 + c1) + c3) > 10) | c1          | {2,1,3}        | f
+users        | users_check1       | (((c2 + c1) + c3) > 10) | c3          | {2,1,3}        | f
 `))
 				m.noChecks()
 			},
@@ -279,7 +285,6 @@ func TestDriver_InspectTable(t *testing.T) {
 --------------------
  public
 `))
-			mk.tables("public", "users")
 			tt.before(mk)
 			s, err := drv.InspectSchema(context.Background(), "public", nil)
 			require.NoError(t, err)
@@ -295,13 +300,15 @@ func TestDriver_InspectSchema(t *testing.T) {
 	mk.version("130000")
 	drv, err := Open(db)
 	require.NoError(t, err)
-	mk.ExpectQuery(sqltest.Escape("SELECT CURRENT_SCHEMA()")).
+	mk.ExpectQuery(sqltest.Escape(fmt.Sprintf(schemasQueryArgs, "= CURRENT_SCHEMA()"))).
 		WillReturnRows(sqltest.Rows(`
-    schema_name
+   schema_name
 --------------------
- test
+test
 `))
-	mk.tables("test")
+	m.ExpectQuery(sqltest.Escape(fmt.Sprintf(tablesQuery, "$1"))).
+		WithArgs("test").
+		WillReturnRows(sqlmock.NewRows([]string{"table_schema", "table_name", "comment"}))
 	s, err := drv.InspectSchema(context.Background(), "", &schema.InspectOptions{})
 	require.NoError(t, err)
 	require.EqualValues(t, func() *schema.Schema {
@@ -335,13 +342,14 @@ func TestDriver_Realm(t *testing.T) {
 	require.NoError(t, err)
 	mk.ExpectQuery(sqltest.Escape(schemasQuery)).
 		WillReturnRows(sqltest.Rows(`
-    schema_name
+   schema_name
 --------------------
- test
- public
+test
+public
 `))
-	mk.tables("test")
-	mk.tables("public")
+	m.ExpectQuery(sqltest.Escape(fmt.Sprintf(tablesQuery, "$1, $2"))).
+		WithArgs("test", "public").
+		WillReturnRows(sqlmock.NewRows([]string{"table_schema", "table_name", "comment"}))
 	realm, err := drv.InspectRealm(context.Background(), &schema.InspectRealmOption{})
 	require.NoError(t, err)
 	require.EqualValues(t, func() *schema.Realm {
@@ -372,13 +380,14 @@ func TestDriver_Realm(t *testing.T) {
 	mk.ExpectQuery(sqltest.Escape(fmt.Sprintf(schemasQueryArgs, "IN ($1, $2)"))).
 		WithArgs("test", "public").
 		WillReturnRows(sqltest.Rows(`
-    schema_name
+   schema_name
 --------------------
- test
- public
+  test
+  public
 `))
-	mk.tables("test")
-	mk.tables("public")
+	m.ExpectQuery(sqltest.Escape(fmt.Sprintf(tablesQuery, "$1, $2"))).
+		WithArgs("test", "public").
+		WillReturnRows(sqlmock.NewRows([]string{"table_schema", "table_name", "comment"}))
 	realm, err = drv.InspectRealm(context.Background(), &schema.InspectRealmOption{Schemas: []string{"test", "public"}})
 	require.NoError(t, err)
 	require.EqualValues(t, func() *schema.Realm {
@@ -409,11 +418,13 @@ func TestDriver_Realm(t *testing.T) {
 	mk.ExpectQuery(sqltest.Escape(fmt.Sprintf(schemasQueryArgs, "= $1"))).
 		WithArgs("test").
 		WillReturnRows(sqltest.Rows(`
-    schema_name
+ schema_name
 --------------------
  test
 `))
-	mk.tables("test")
+	m.ExpectQuery(sqltest.Escape(fmt.Sprintf(tablesQuery, "$1"))).
+		WithArgs("test").
+		WillReturnRows(sqlmock.NewRows([]string{"table_schema", "table_name", "comment"}))
 	realm, err = drv.InspectRealm(context.Background(), &schema.InspectRealmOption{Schemas: []string{"test"}})
 	require.NoError(t, err)
 	require.EqualValues(t, func() *schema.Realm {
@@ -454,36 +465,26 @@ func (m mock) version(version string) {
 }
 
 func (m mock) tableExists(schema, table string, exists bool) {
-	rows := sqlmock.NewRows([]string{"table_schema", "table_comment"})
+	rows := sqlmock.NewRows([]string{"table_schema", "table_name", "table_comment"})
 	if exists {
-		rows.AddRow(schema, nil)
+		rows.AddRow(schema, table, nil)
 	}
-	m.ExpectQuery(sqltest.Escape(tableSchemaQuery)).
-		WithArgs(table, schema).
+	m.ExpectQuery(queryTables).
+		WithArgs(schema).
 		WillReturnRows(rows)
 }
 
 func (m mock) noIndexes() {
-	m.ExpectQuery(sqltest.Escape(indexesQuery)).
-		WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "primary", "unique", "constraint_type", "predicate", "expression"}))
+	m.ExpectQuery(queryIndexes).
+		WillReturnRows(sqlmock.NewRows([]string{"table_name", "index_name", "column_name", "primary", "unique", "constraint_type", "predicate", "expression"}))
 }
 
 func (m mock) noFKs() {
-	m.ExpectQuery(sqltest.Escape(fksQuery)).
+	m.ExpectQuery(queryFKs).
 		WillReturnRows(sqlmock.NewRows([]string{"constraint_name", "table_name", "column_name", "referenced_table_name", "referenced_column_name", "referenced_table_schema", "update_rule", "delete_rule"}))
 }
 
 func (m mock) noChecks() {
-	m.ExpectQuery(sqltest.Escape(checksQuery)).
-		WillReturnRows(sqlmock.NewRows([]string{"constraint_name", "expression", "column_name", "column_indexes"}))
-}
-
-func (m mock) tables(schema string, names ...string) {
-	rows := sqlmock.NewRows([]string{"table_name"})
-	for i := range names {
-		rows.AddRow(names[i])
-	}
-	m.ExpectQuery(sqltest.Escape(tablesQuery)).
-		WithArgs(schema).
-		WillReturnRows(rows)
+	m.ExpectQuery(queryChecks).
+		WillReturnRows(sqlmock.NewRows([]string{"table_name", "constraint_name", "expression", "column_name", "column_indexes"}))
 }