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
119 lines
3.0 KiB
Go
119 lines
3.0 KiB
Go
package postgresql
|
|
|
|
import (
|
|
"context"
|
|
|
|
pgx "github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
// Relations are the relations available in pg_tables and pg_views
|
|
// such as pg_catalog.pg_timezone_names
|
|
const relationQuery = `
|
|
with relations as (
|
|
select schemaname, tablename as name from pg_catalog.pg_tables
|
|
UNION ALL
|
|
select schemaname, viewname as name from pg_catalog.pg_views
|
|
)
|
|
select
|
|
relations.schemaname,
|
|
relations.name as tablename,
|
|
pg_attribute.attname as column_name,
|
|
attnotnull as column_notnull,
|
|
-- An array column's type is its element's, with the array flag set,
|
|
-- rather than pg_type's own _text spelling of the array type. Only the
|
|
-- _-prefixed array types count: int2vector and oidvector are in the
|
|
-- array category and carry an element, but are types of their own.
|
|
coalesce(element_type.typname, column_type.typname) as column_type,
|
|
nullif(coalesce(element_type.typlen, column_type.typlen), -1) as column_length,
|
|
element_type.oid is not null as column_isarray
|
|
from relations
|
|
inner join pg_catalog.pg_class on pg_class.relname = relations.name
|
|
left join pg_catalog.pg_attribute on pg_attribute.attrelid = pg_class.oid
|
|
inner join pg_catalog.pg_type column_type on pg_attribute.atttypid = column_type.oid
|
|
left join pg_catalog.pg_type element_type
|
|
on column_type.typcategory = 'A' and column_type.typname like '\_%'
|
|
and element_type.oid = column_type.typelem
|
|
where relations.schemaname = $1
|
|
-- Make sure these columns are always generated in the same order
|
|
-- so that the output is stable
|
|
order by
|
|
relations.schemaname ASC,
|
|
relations.name ASC,
|
|
pg_attribute.attnum ASC
|
|
`
|
|
|
|
type Relation struct {
|
|
Catalog string
|
|
SchemaName string
|
|
Name string
|
|
Columns []RelationColumn
|
|
}
|
|
|
|
type RelationColumn struct {
|
|
Name string
|
|
Type string
|
|
IsNotNull bool
|
|
IsArray bool
|
|
Length *int
|
|
}
|
|
|
|
func scanRelations(rows pgx.Rows) ([]Relation, error) {
|
|
defer rows.Close()
|
|
// Iterate through the result set
|
|
var relations []Relation
|
|
var prevRel *Relation
|
|
|
|
for rows.Next() {
|
|
var schemaName string
|
|
var tableName string
|
|
var columnName string
|
|
var columnNotNull bool
|
|
var columnType string
|
|
var columnLength *int
|
|
var columnIsArray bool
|
|
err := rows.Scan(
|
|
&schemaName,
|
|
&tableName,
|
|
&columnName,
|
|
&columnNotNull,
|
|
&columnType,
|
|
&columnLength,
|
|
&columnIsArray,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if prevRel == nil || tableName != prevRel.Name {
|
|
// We are on the same table, just keep adding columns
|
|
r := Relation{
|
|
Catalog: "pg_catalog",
|
|
SchemaName: schemaName,
|
|
Name: tableName,
|
|
}
|
|
|
|
relations = append(relations, r)
|
|
prevRel = &relations[len(relations)-1]
|
|
}
|
|
|
|
prevRel.Columns = append(prevRel.Columns, RelationColumn{
|
|
Name: columnName,
|
|
Type: columnType,
|
|
IsNotNull: columnNotNull,
|
|
IsArray: columnIsArray,
|
|
Length: columnLength,
|
|
})
|
|
}
|
|
|
|
return relations, rows.Err()
|
|
}
|
|
|
|
func readRelations(ctx context.Context, conn *pgx.Conn, schemaName string) ([]Relation, error) {
|
|
rows, err := conn.Query(ctx, relationQuery, schemaName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return scanRelations(rows)
|
|
}
|