Add support for materialized views.

This commit is contained in:
go-jet 2024-02-01 15:20:49 +01:00
parent b6d57075e8
commit 7f48e9fb67
8 changed files with 127 additions and 63 deletions

View file

@ -33,6 +33,7 @@ const (
EnumType DataTypeKind = "enum"
UserDefinedType DataTypeKind = "user-defined"
ArrayType DataTypeKind = "array"
RangeType DataTypeKind = "range"
)
// DataType contains information about column data type

View file

@ -26,8 +26,25 @@ ORDER BY table_name;
return nil, fmt.Errorf("failed to query %s metadata: %w", tableType, err)
}
// add materialized views separately, because materialized views are not part of standard information schema
if tableType == metadata.ViewTable {
matViewQuery := `
select matviewname as "table.name"
from pg_matviews
where schemaname = $1;
`
var matViews []metadata.Table
_, err := qrm.Query(context.Background(), db, matViewQuery, []interface{}{schemaName}, &matViews)
if err != nil {
return nil, fmt.Errorf("failed to query materialized view metadata: %w", err)
}
tables = append(tables, matViews...)
}
for i := range tables {
tables[i].Columns, err = p.GetTableColumnsMetaData(db, schemaName, tables[i].Name)
tables[i].Columns, err = getColumnsMetaData(db, schemaName, tables[i].Name)
if err != nil {
return nil, fmt.Errorf("failed to query %s columns metadata: %w", tableType, err)
}
@ -36,39 +53,39 @@ ORDER BY table_name;
return tables, nil
}
func (p postgresQuerySet) GetTableColumnsMetaData(db *sql.DB, schemaName string, tableName string) ([]metadata.Column, error) {
func getColumnsMetaData(db *sql.DB, schemaName string, tableName string) ([]metadata.Column, error) {
query := `
WITH primaryKeys AS (
SELECT column_name
FROM information_schema.key_column_usage AS c
LEFT JOIN information_schema.table_constraints AS t
ON t.constraint_name = c.constraint_name AND
c.table_schema = t.table_schema AND
c.table_name = t.table_name
WHERE t.table_schema = $1 AND t.table_name = $2 AND t.constraint_type = 'PRIMARY KEY'
)
SELECT column_name as "column.Name",
is_nullable = 'YES' as "column.isNullable",
is_generated = 'ALWAYS' or is_generated = 'YES' as "column.isGenerated",
(EXISTS(SELECT 1 from primaryKeys as pk where pk.column_name = columns.column_name)) as "column.IsPrimaryKey",
dataType.kind as "dataType.Kind",
(case dataType.Kind when 'base' then data_type else LTRIM(udt_name, '_') end) as "dataType.Name",
FALSE as "dataType.isUnsigned"
FROM information_schema.columns,
LATERAL (select (case data_type
when 'ARRAY' then 'array'
when 'USER-DEFINED' then
case (select t.typtype
from pg_type as t
join pg_namespace as p on p.oid = t.typnamespace
where t.typname = columns.udt_name and p.nspname = $1)
when 'e' then 'enum'
else 'user-defined'
end
else 'base'
end) as Kind) as dataType
where table_schema = $1 and table_name = $2
order by ordinal_position;
select
attr.attname as "column.Name",
exists(
select 1
from pg_catalog.pg_index indx
where attr.attrelid = indx.indrelid and attr.attnum = any(indx.indkey) and indx.indisprimary
) as "column.IsPrimaryKey",
not attr.attnotnull as "column.isNullable",
attr.attgenerated = 's' as "column.isGenerated",
(case tp.typtype
when 'b' then 'base'
when 'd' then 'base'
when 'e' then 'enum'
when 'r' then 'range'
end) as "dataType.Kind",
(case when tp.typtype = 'd' then (select pg_type.typname from pg_catalog.pg_type where pg_type.oid = tp.typbasetype)
when tp.typcategory = 'A' then pg_catalog.format_type(attr.atttypid, attr.atttypmod)
else tp.typname
end) as "dataType.Name",
false as "dataType.isUnsigned"
from pg_catalog.pg_attribute as attr
join pg_catalog.pg_class as cls on cls.oid = attr.attrelid
join pg_catalog.pg_namespace as ns on ns.oid = cls.relnamespace
join pg_catalog.pg_type as tp on tp.oid = attr.atttypid
where
ns.nspname = $1 and
cls.relname = $2 and
not attr.attisdropped and
attr.attnum > 0
order by
attr.attnum;
`
var columns []metadata.Column
_, err := qrm.Query(context.Background(), db, query, []interface{}{schemaName, tableName}, &columns)

View file

@ -142,14 +142,15 @@ func DefaultTableSQLBuilderColumn(columnMetaData metadata.Column) TableSQLBuilde
// getSqlBuilderColumnType returns type of jet sql builder column
func getSqlBuilderColumnType(columnMetaData metadata.Column) string {
if columnMetaData.DataType.Kind != metadata.BaseType {
if columnMetaData.DataType.Kind != metadata.BaseType &&
columnMetaData.DataType.Kind != metadata.RangeType {
return "String"
}
switch strings.ToLower(columnMetaData.DataType.Name) {
case "boolean":
case "boolean", "bool":
return "Bool"
case "smallint", "integer", "bigint",
case "smallint", "integer", "bigint", "int2", "int4", "int8",
"tinyint", "mediumint", "int", "year": //MySQL
return "Integer"
case "date":
@ -157,21 +158,21 @@ func getSqlBuilderColumnType(columnMetaData metadata.Column) string {
case "timestamp without time zone",
"timestamp", "datetime": //MySQL:
return "Timestamp"
case "timestamp with time zone":
case "timestamp with time zone", "timestamptz":
return "Timestampz"
case "time without time zone",
"time": //MySQL
return "Time"
case "time with time zone":
case "time with time zone", "timetz":
return "Timez"
case "interval":
return "Interval"
case "user-defined", "enum", "text", "character", "character varying", "bytea", "uuid",
"tsvector", "bit", "bit varying", "money", "json", "jsonb", "xml", "point", "line", "ARRAY",
"char", "varchar", "nvarchar", "binary", "varbinary",
"char", "varchar", "nvarchar", "binary", "varbinary", "bpchar", "varbit",
"tinyblob", "blob", "mediumblob", "longblob", "tinytext", "mediumtext", "longtext": // MySQL
return "String"
case "real", "numeric", "decimal", "double precision", "float",
case "real", "numeric", "decimal", "double precision", "float", "float4", "float8",
"double": // MySQL
return "Float"
default: