Files
upstream-sqlc/internal/goldeneye/mysql/relations.go
upstream-mirror 0f4df23290
Some checks failed
kotlin / test (push) Has been skipped
python / test (push) Has been skipped
typescript / test (push) Has been skipped
go / build darwin/amd64 (push) Has been cancelled
go / build linux/amd64 (push) Has been cancelled
go / build windows/amd64 (push) Has been cancelled
go / build darwin/arm64 (push) Has been cancelled
go / build linux/arm64 (push) Has been cancelled
go / build windows/arm64 (push) Has been cancelled
go / test (push) Has been cancelled
go / vuln_check (push) Has been cancelled
snapshot of https://github.com/sqlc-dev/sqlc.git (main, 2026-09-16; blobs > 131072B omitted)
2026-09-16 02:17:34 +05:00

100 lines
3.2 KiB
Go

package mysql
import (
"context"
"database/sql"
"fmt"
"strings"
"github.com/sqlc-dev/sqlc/internal/goldeneye/dialect"
)
// systemSchemas are the schemas whose relations are generated: the data
// dictionary's views, which are what a query of the server's catalog
// reads. The other schemas mysqld --initialize creates — mysql,
// performance_schema and sys — are tables rather than views, and every
// table a dialect seeds is one the analysis core hands codegen as a model,
// so they wait until codegen knows a system schema when it sees one.
var systemSchemas = []string{
"information_schema",
}
// relationQuery lists a schema's relations and their columns, in the order
// the columns were declared, so that the output is stable.
const relationQuery = `
SELECT t.TABLE_NAME, t.TABLE_TYPE,
c.COLUMN_NAME, c.DATA_TYPE, c.COLUMN_TYPE, c.IS_NULLABLE
FROM information_schema.TABLES t
JOIN information_schema.COLUMNS c
ON c.TABLE_SCHEMA = t.TABLE_SCHEMA AND c.TABLE_NAME = t.TABLE_NAME
WHERE t.TABLE_SCHEMA = ?
ORDER BY t.TABLE_NAME, c.ORDINAL_POSITION`
// readRelations reads a schema's tables and views. Their names are written
// in lower case: information_schema names its views in upper case and
// matches them in any case, MySQL matches every column name in any case,
// and sqlc's MySQL parser lowercases every identifier, so lower case is
// how a query reaches them. A column's type is spelled the way a
// declaration does, as COLUMN_TYPE reports it, arguments and UNSIGNED
// included: varchar(64), bigint unsigned.
func readRelations(ctx context.Context, conn *sql.Conn, schema string) ([]dialect.Relation, error) {
rows, err := conn.QueryContext(ctx, relationQuery, schema)
if err != nil {
return nil, fmt.Errorf("%s: %w", schema, err)
}
defer rows.Close()
var relations []dialect.Relation
var cur *dialect.Relation
for rows.Next() {
var name, kind, colName, dataType, columnType, nullable string
if err := rows.Scan(&name, &kind, &colName, &dataType, &columnType, &nullable); err != nil {
return nil, err
}
name = strings.ToLower(name)
if cur == nil || cur.Name != name {
rel := dialect.Relation{Catalog: "def", Schema: schema, Name: name}
if kind != "BASE TABLE" {
rel.Kind = "v"
}
relations = append(relations, rel)
cur = &relations[len(relations)-1]
}
cur.Columns = append(cur.Columns, dialect.Column{
Name: strings.ToLower(colName),
Type: typeName(dataType, columnType),
NotNull: nullable == "NO",
})
}
return relations, rows.Err()
}
// typeName spells a column's type the way a declaration does, which is
// COLUMN_TYPE as information_schema reports it: "varchar(64)", "bigint
// unsigned", "enum('a','b')". The family is spelled in lower case; the
// members of an enum or set are values and keep theirs.
func typeName(dataType, columnType string) string {
if columnType == "" {
return strings.ToLower(dataType)
}
var out strings.Builder
quoted := false
for i := 0; i < len(columnType); i++ {
c := columnType[i]
switch {
case quoted && c == '\\' && i+1 < len(columnType):
out.WriteByte(c)
i++
out.WriteByte(columnType[i])
continue
case c == '\'':
quoted = !quoted
}
if quoted {
out.WriteByte(c)
} else {
out.WriteString(strings.ToLower(string(c)))
}
}
return out.String()
}