From e471171b7286a2f80a5c049723fbed1d4ae14674 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 10 Sep 2026 17:59:11 +0000 Subject: [PATCH 01/16] core: evaluate how types are represented, and design full type expressions in the catalog Every engine's type system was run through sqlc analyze and compared with what the engine itself reports. The catalog holds one row per flat name, so a type argument, a struct field, an enum label, a domain's base, a range's subtype, a MySQL UNSIGNED and a second array dimension all have nowhere to go, and a ClickHouse cast to Nullable(String) reports "nullable". The note proposes one row per type expression: family rows the dialect and the schema declare, instance rows for a family applied to arguments, the arguments in sql_type_arg, and family, element, base and canonical pointers denormalized so that a bare name and a structured expression both resolve to a row in one lookup. Interning stays at schema load, since the cached catalog is read-only; the analyzer carries the expression and looks up. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- internal/core/types.md | 323 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 internal/core/types.md diff --git a/internal/core/types.md b/internal/core/types.md new file mode 100644 index 0000000000..d9dcb44f45 --- /dev/null +++ b/internal/core/types.md @@ -0,0 +1,323 @@ +# Types in the analysis core + +How the core catalog, the analyzer and `sqlc analyze` represent a type today, +where that falls short of each engine's type system, and the design that +closes the gap: every type the schema or the dialect declares is a row holding +its full expression, denormalized so that a bare name like `integer` and a +structured expression like `numeric(10, 2)` both resolve to one. + +## What a type is today + +The catalog (`catalogdef/schema.sql`) has one row per *name* in `sql_type`: +a lowercased string, a category letter, a `typtype` that is only ever `b` or +`e`, and an `element_oid` that is written for arrays but never read. A +dialect's `types.jsonl` seeds one row per type and one more per alias, then +joins every spelling of a type to every other with implicit casts. Arrays are +rows named after their element with `[]` appended, created on first use. +Anything else — a type argument, a struct field, an enum label, a domain's +base, a range's subtype — has nowhere to go: `sql_attribute` keeps the +column's verbatim spelling in `decl_type` (only SQLite and ClickHouse set it) +and has `type_length` and `type_scale` columns that no DDL path fills. + +The analyzer (`analyzer/expr.go`) types an expression as `exprType`: a type +OID, or a bare name when the catalog has no row, plus nullability. A type is +therefore a row, and a type that is not a row degrades to whatever row its +spelling's first word finds. + +The output (`TypeExpr` in `typeexpr.go`, printed by `sqlc analyze`) already +has the right shape: a name applied to labelled arguments that are types, +integers, booleans or strings, with `nullable` at any depth. It is complete +only where a source column's `decl_type` spelling exists to be parsed; every +other column and every parameter is rebuilt from the flat row name. + +## What each engine loses + +The table is what `sqlc analyze` reports today for a schema exercising each +engine's type system, against what the engine itself says the type is. + +| Engine | Declared | Reported | The engine says | +|---|---|---|---| +| PostgreSQL | `numeric(10,2)`, `varchar(255)`, `timestamp(3)`, `bit(8)` | `numeric`, `varchar`, `timestamp`, `bit` | typmods are part of the type | +| PostgreSQL | `int[][]` | `array(int4)` | two dimensions | +| PostgreSQL | `CREATE DOMAIN posint AS integer` | `posint`, category U | an integer with a constraint | +| PostgreSQL | `CREATE TYPE point2 AS (x float8, y float8)` | `point2`, category U | two named fields | +| PostgreSQL | `CREATE TYPE mood AS ENUM (...)` | `mood` | the labels | +| PostgreSQL | `CREATE TYPE floatrange AS RANGE (subtype = float8)` | `floatrange`, category U | a range over float8 | +| PostgreSQL | `myschema.mood` | `mood` for a column, `myschema.mood` for a cast: two rows, both in `public` | one type in a namespace | +| PostgreSQL | `interval day to second` | `interval` | fields are a typmod | +| MySQL | `BIGINT UNSIGNED`, `INT UNSIGNED` | `bigint`, `int` | a different value range; codegen picks `int64` over `uint64` | +| MySQL | `TINYINT(1)` | `tinyint` | the display width is how drivers and codegen spot a boolean | +| MySQL | `DECIMAL(10,2) UNSIGNED`, `DATETIME(6)`, `VARCHAR(255)` | `decimal`, `datetime`, `varchar` | precision, fractional seconds, length | +| MySQL | `ENUM('a','b')`, `SET('x','y')` | `enum`, `set` | the members | +| MySQL | `CAST(? AS CHAR(10))` | `var_string` | `char`; the parser's internal name leaks | +| SQLite | `FOO BAR(3)`, `VARCHAR(255)` | `foo bar(3)`, `varchar(255)` | correct spelling, but each is a row of category U that compares with nothing, and the affinity SQLite gives it (NUMERIC, TEXT) is not modelled | +| ClickHouse | every column type | complete | complete, from the spelling | +| ClickHouse | `CAST(x AS Nullable(String))` | `nullable` | `Nullable(String)` | +| ClickHouse | `CAST(x AS Array(UInt8))` | `array` | `Array(UInt8)` | +| ClickHouse | `toDecimal64(x, 4)`, `toDateTime64(x, 3)` | `decimal64`, `datetime64` | `Decimal(18, 4)`, `DateTime64(3)`: the result depends on an argument's value | +| ClickHouse | `SimpleAggregateFunction(sum, UInt64)` | `simpleaggregatefunction(sum, uint64)` | `sum` is a function name, which the expression reads as a type | +| ClickHouse | `n Nested(a UInt8, b String)` | one column `n` | two columns `n.a Array(UInt8)`, `n.b Array(String)` | +| DuckDB | `STRUCT(a INTEGER, b VARCHAR)`, `MAP(VARCHAR, INTEGER)`, `UNION(num INTEGER, str VARCHAR)` | `struct`, `map`, `union` | the fields | +| DuckDB | `INTEGER[]`, `INTEGER[3]`, `INTEGER[][]` | `array(integer)` for all three | LIST, fixed-size ARRAY, nested LIST | +| DuckDB | `DECIMAL(18,3)`, `VARCHAR(10)`, `ENUM('a','b')` | `decimal`, `varchar`, `enum` | arguments and members | +| GoogleSQL | `ARRAY` | a row *named* `array` | an array of int64 | +| GoogleSQL | `STRUCT`, `ARRAY>` | `struct`, `array` | the fields | +| GoogleSQL | `STRING(10)`, `NUMERIC(10,2)`, `[1, 2]`, `STRUCT(1 AS x)` | `string`, `numeric`, untyped, untyped | parameters; a constructed array and struct | +| SQL Server | `NVARCHAR(MAX)`, `VARBINARY(MAX)` | `nvarchar`, `varbinary` | MAX decides the Go type | +| SQL Server | `DECIMAL(10,2)`, `DATETIME2(3)`, `FLOAT(24)`, `VECTOR(3)` | `decimal`, `datetime2`, `float`, `vector` | arguments; `FLOAT(24)` is `real` | +| SQL Server | `CREATE TYPE dbo.PhoneNumber FROM varchar(20) NOT NULL` | `phonenumber`, category U | a `varchar(20)` that is never null, in schema `dbo` | + +Three of these are regressions against the legacy compiler rather than gaps +shared with it: the plugin protocol's `Column` carries `unsigned`, `length` +and `array_dims`, codegen reads all three (`golang/mysql_type.go` turns +`tinyint` with length 1 into `bool` and `unsigned` into `uint64`; +`golang/go_type.go` nests one slice per dimension), and the core bridge in +`compiler/parse_core.go` sets none of them from what the core reports. + +Two are broken outside column declarations only: ClickHouse's columns are +whole because the engine hands the core a spelling and the core keeps it on +the attribute. The same spelling in a cast, a function result or a typed +placeholder has no attribute to live on, so it degrades to its first word. +That is the tell: the expression belongs to the type, not to the column. + +## The design + +### One row per type expression + +`sql_type` keeps one row per distinct type expression. A row is either a +**family** — a name the dialect or the schema declares, such as `numeric`, +`array`, `struct`, `mood` — or an **instance**, a family applied to +arguments, such as `numeric(10, 2)`, `array(int4)` or +`struct(a: int4, b: text)`. The row's `expr` is the expression's canonical +string, which is its interning key; the row's `name` is the family's name, +so the index that turns `integer` into a row keeps working for instances, +and an instance points at its family. + +```sql +CREATE TABLE sql_type ( + oid INTEGER PRIMARY KEY AUTOINCREMENT, + namespace_oid INTEGER NOT NULL REFERENCES sql_namespace(oid), + dialect_oid INTEGER REFERENCES sql_dialect(oid), + name TEXT NOT NULL, -- the family name: 'numeric', 'array', 'mood' + expr TEXT NOT NULL, -- the whole expression, canonical: 'numeric(10, 2)'; equals name for a family + typtype TEXT NOT NULL DEFAULT 'b', -- b base, c composite, d domain, e enum, r range, p pseudo + category TEXT, + preferred INTEGER NOT NULL DEFAULT 0, + family_oid INTEGER REFERENCES sql_type(oid), -- NULL on a family; the family on an instance + element_oid INTEGER REFERENCES sql_type(oid), -- what the type holds: an array's element, a map's value, a range's subtype + base_oid INTEGER REFERENCES sql_type(oid), -- what the type stands on: a domain's or alias type's base, a wrapper's inner type + canonical_oid INTEGER REFERENCES sql_type(oid), -- the row an alias spelling means: integer -> int4 + not_null INTEGER NOT NULL DEFAULT 0, -- a domain or alias type declared NOT NULL + UNIQUE (namespace_oid, expr) +); +CREATE INDEX idx_sql_type_name ON sql_type(name); + +-- sql_type_arg: the arguments of an instance, or the fields, labels or +-- members of a declared composite, enum or set, in order. Exactly one of +-- arg_type_oid, int_value, bool_value, string_value and ident is set. +CREATE TABLE sql_type_arg ( + type_oid INTEGER NOT NULL REFERENCES sql_type(oid), + ord INTEGER NOT NULL, + label TEXT NOT NULL DEFAULT '', -- a struct field, tuple element or enum label + arg_type_oid INTEGER REFERENCES sql_type(oid), + nullable INTEGER NOT NULL DEFAULT 0, -- the argument type is nullable here: Array(Nullable(String)) + int_value INTEGER, + bool_value INTEGER, + string_value TEXT, + ident TEXT, -- a bare word that is not a type: max, sum, day to second + PRIMARY KEY (type_oid, ord) +); +``` + +`size` goes: nothing reads it. `type_length` and `type_scale` leave +`sql_attribute`: they were one engine's two arguments, and now every +engine's arguments are rows. `decl_type` stays, since the verbatim spelling +is what the formatter prints back. + +The four pointer columns are the denormalization. Each is derivable by +walking `sql_type_arg`, and each is what resolution asks for in one +statement: + +- `family_oid` is what operator and function lookup fall back to when there + is no overload on the instance: `numeric(10, 2) + numeric(5, 1)` finds no + operator on either instance and resolves on `numeric`. +- `element_oid` is what a subscript, `ANY($1)`, `unnest` and a star over a + map yield, and what `IsArray` was. +- `base_oid` is what a domain, an alias type, `LowCardinality(T)` or SQLite's + affinity resolves through: `posint = 1` finds no operator on `posint` and + resolves on `int4`. +- `canonical_oid` replaces the n² implicit casts between spellings of one + type: `integer` and `int4` are two rows, so a column reports the spelling + it was declared with, and one canonical row, so resolution treats them as + one type. + +A declared type is a family row with arguments of its own. `CREATE TYPE +point2 AS (x float8, y float8)` is a row named `point2`, `typtype` `c`, with +two labelled type arguments; `CREATE TYPE mood AS ENUM ('sad', 'ok')` is a +row with `typtype` `e` and two string arguments; ClickHouse's +`Enum8('active' = 1, 'deleted' = 2)` is an instance of `enum8` with two +labelled integer arguments and `base_oid` pointing at `int8`; `CREATE DOMAIN +posint AS integer` and SQL Server's `CREATE TYPE PhoneNumber FROM varchar(20) +NOT NULL` are rows with `typtype` `d`, `base_oid` at the base instance and +`not_null` set. The same table holds what the schema names and what it +constructs anonymously. + +### Nullability is not a type + +A row is never nullable. Outer nullability stays where it is: on the +attribute, on the analyzer's `exprType`, on the reported column. Inner +nullability is a flag on the argument position, so `Array(Nullable(String))` +is an instance of `array` whose one argument is `string` with `nullable` +set, and `Nullable(String)` in a cast is `string` with the expression's own +nullability set. This is what `TypeExpr` already says — `nullable` at +whatever depth it applies, never a wrapper — and it keeps `string` and +`string nullable` from being two types that need their own operators. + +### The catalog interns at schema time; the analyzer looks up at query time + +The cached catalog is opened read-only (`catalog.go` opens it with +`mode=ro&immutable=1`), and `exprType` carries a bare name precisely so that +analysis never has to write. That constraint stands, and it decides which +expressions become rows: + +- **Interned**: what the dialect seeds and what the schema declares. Every + column type, every declared type's fields and base, every function + signature's argument and return type becomes a row on load, through one + entry point, `ResolveTypeExpr(*TypeExpr) (oid, error)`, which walks the + expression bottom-up, interning each argument type first. `ResolveType` + and `ResolveTypeName` become callers of it. +- **Looked up**: what a query writes. A cast, a constructed array or struct, + a typed placeholder and a function result are resolved by + `LookupTypeExpr(*TypeExpr) (oid, familyOID, bool)`, which finds the + instance row when the schema happened to declare the same expression and + otherwise the family row, and never writes. + +`exprType` becomes the pair: + +```go +type exprType struct { + typeOID int64 // the instance row when the catalog has one, else the family row, else 0 + expr *core.TypeExpr // the whole expression, whenever anything is known about it + nullable bool + ... +} +``` + +Resolution uses `typeOID` and its `family_oid`, `base_oid` and +`canonical_oid` chain; reporting uses `expr`. The `typeName` fallback and +the `[]` suffix convention go away: an array is `array` applied to its +element, in the catalog as in the output, and `TypeNameString` is replaced +by a function that reads an `ast.TypeName` into a `TypeExpr`, folding +`Typmods` into integer arguments, `ArrayBounds` into one `array` per +dimension, `Names` into a namespace and a name, and `Spelling` through +`ParseTypeExpr`. + +### What each engine hands the core + +The contract with an engine is that its `ast.TypeName` reads into a +`TypeExpr` that says everything the engine's own catalog would. Where an +engine already folds its type into a spelling, `ParseTypeExpr` reads it; +where it does not, the converter has a small change to make. + +| Engine | Form | Expression | +|---|---|---| +| PostgreSQL | `numeric(10,2)`, `varchar(255)`, `timestamp(3) with time zone` | `numeric(10, 2)`, `varchar(255)`, `timestamptz(3)`: typmods become integer arguments on the canonical family | +| PostgreSQL | `int[]`, `int[][]` | `array(int4)`, `array(array(int4))`: one per array bound | +| PostgreSQL | `interval day to second` | `interval('day to second')`: the fields are one identifier argument, as `format_type` prints them | +| PostgreSQL | domain, composite, enum, range | declared rows, as above; `CreateDomainStmt`, `CompositeTypeStmt` and `CreateRangeStmt` gain `schema.Apply` cases | +| PostgreSQL | `myschema.mood` | a row in namespace `myschema`; `Names` resolves to a namespace rather than a dotted name | +| MySQL | `BIGINT UNSIGNED`, `DECIMAL(10,2) UNSIGNED` | `bigint unsigned`, `decimal unsigned(10, 2)`: unsigned is a family of its own, as the server reports it, rather than the alias of the signed type `types.jsonl` lists today, since the value range differs; the converter puts it in the name instead of on `ColumnDef.IsUnsigned`, which the core ignores | +| MySQL | `TINYINT(1)`, `DATETIME(6)`, `VARCHAR(255)` | `tinyint(1)`, `datetime(6)`, `varchar(255)`: the converter's `Typmods` are read | +| MySQL | `ENUM('a','b')`, `SET('x','y')` | `enum('a', 'b')`, `set('x', 'y')`: the converter renders `Vals` into the spelling | +| MySQL | `CAST(x AS CHAR(10))` | `char(10)`: the cast converter names the SQL type, not TiDB's `var_string` | +| MySQL | `CHARACTER SET binary`, `COLLATE` | a labelled string argument, `varchar(255, charset: 'binary')`, if codegen ever needs it; open | +| SQLite | any spelling | the spelling as an instance, `varchar(255)`, `foo bar(3)`, with `base_oid` set by the affinity rules — INT anywhere is INTEGER, CHAR, CLOB or TEXT is TEXT, BLOB or nothing is BLOB, REAL, FLOA or DOUB is REAL, else NUMERIC — applied when the dialect resolves an unknown name; `types.jsonl`'s alias lists become the rule | +| SQLite | `STRICT` tables, `ANY` | the family rows; a strict table's column names one of them or fails | +| ClickHouse | every parametric type | the spelling, read as today, now also for casts, `{p:T}` placeholders and results | +| ClickHouse | `Nullable(T)`, `LowCardinality(T)` | `T` with `nullable`; `lowcardinality(T)` with `base_oid` at `T` | +| ClickHouse | `SimpleAggregateFunction(sum, UInt64)` | `simpleaggregatefunction(sum, uint64)` with `sum` an identifier argument | +| ClickHouse | `toDecimal64(x, s)` | the seed's return type may name an argument's *value*: `"returns": "Decimal(18, $2)"`; the analyzer substitutes the literal when it is one and reports the family otherwise | +| ClickHouse | `Nested(a UInt8, b String)` | a relation-shape rule, not a type: the column becomes `n.a array(uint8)` and `n.b array(string)` on load | +| DuckDB | `STRUCT(a INTEGER, b VARCHAR)`, `MAP(K, V)`, `UNION(...)` | `struct(a: integer, b: varchar)`, `map(varchar, integer)`, `union(num: integer, str: varchar)`: the converter renders the darkwing type expression it already has instead of keeping its name | +| DuckDB | `INTEGER[]`, `INTEGER[3]` | `array(integer)` and `array(integer, 3)`: a list is the cross-dialect array, a fixed size is its second argument | +| GoogleSQL | `ARRAY`, `STRUCT`, `RANGE` | `array(int64)`, `struct(a: int64, b: string)`, `range(date)`: `ParseTypeExpr` accepts `<...>` as well as `(...)`, or the converter renders the column schema in call form | +| GoogleSQL | `STRING(10)`, `NUMERIC(10,2)`, `[1, 2]`, `STRUCT(1 AS x)` | the typmods are read; the array and struct constructors are typed from their elements | +| SQL Server | `NVARCHAR(MAX)` | `nvarchar(max)` with `max` an identifier argument | +| SQL Server | `FLOAT(24)` | `float(24)`, with `canonical_oid` at `real`: a dialect may declare an instance canonical to a family, `"canonical": {"float(1..24)": "real"}`; open | +| SQL Server | `dbo.PhoneNumber` | a row in namespace `dbo`, `typtype` `d`, `base_oid` at `varchar(20)`, `not_null` set | + +The `ident` argument is the one addition to `TypeExpr` and to +`goldeneye/analysis.TypeExpr`, which mirrors it. Without it `max`, `sum` and +`day to second` read as types, and today they do: `SimpleAggregateFunction` +reports `sum` as a type. `ParseTypeExpr` writes a bare word that is not a +known family as an identifier only when a dialect says so — ClickHouse for +the first argument of an aggregate-function type, SQL Server for `max` — and +otherwise as a type, since a struct field's type is also a bare word. + +### What the analyzer reports + +`Column` and `Parameter` in `analysis.go` keep `Type` as the expression and +`TypeOID` as the row, instance or family. `DataType` and `IsArray` stay as +the flat view for the legacy compiler bridge, and that bridge derives what +codegen reads from the expression rather than dropping it: `ArrayDims` is +the depth of `array` nesting, `Length` is the first integer argument, +`Unsigned` is a family name ending in ` unsigned`. `sqlc analyze` prints the +expression as it does today, with `ident` as a fifth argument kind. + +`TypeNameString`, `ArraySuffix`, `CreateArrayType`, `TypeLength` and +`TypeScale` are the API that goes; `ResolveTypeExpr`, `LookupTypeExpr` and +`TypeExprOf(oid)` — the expression a row stands for, read back from +`sql_type_arg` — are the API that replaces it. + +### What the seed files gain + +`types.jsonl` is unchanged for a family; an alias becomes a row with +`canonical_oid` instead of a mesh of casts. A function's argument and return +types may be expressions, and the return type may reference an argument's +value as well as its type. The category rules in `dialect.json` apply to +families; an instance inherits its family's category, which is how +`numeric(10, 2)` joins the numeric casts without being seeded. + +`goldeneye` checks the analyze cases against what each database reports, and +its answer shape is the same `TypeExpr`. ClickHouse reports whole +expressions already. MySQL's driver reports the family, unsigned and +nullability but not precision or length, and `ColumnType.DecimalSize` and +`ColumnType.Length` can add them, with the length divided by the charset's +bytes per character. SQLite reports a declared spelling for a table column +and a storage class for an expression, which are a family row and an +instance row respectively. Every check keeps passing on the way, since a +family with no arguments prints as it does now. + +## Order of work + +1. The tables and the interning entry point: `sql_type.expr`, `family_oid`, + `element_oid`, `base_oid`, `canonical_oid`, `not_null`, `sql_type_arg`, + `ResolveTypeExpr`, `LookupTypeExpr`, `TypeExprOf`. Arrays become + instances of `array`; the `[]` convention goes. Every existing golden + holds, since a bare name prints the same. +2. The analyzer: `exprType` carries the expression; casts, constructors, + placeholders and function results report it; resolution falls back + through the pointer chain. This is where ClickHouse's casts and + parameters come right. +3. The engines, one at a time, each with an `analyze_types/` case + alongside ClickHouse's: PostgreSQL typmods, dimensions and declared types; + MySQL unsigned, typmods and members, plus the `var_string` leak; DuckDB's + nested types; GoogleSQL's angle brackets; SQL Server's `max` and alias + types; SQLite's affinity rule. +4. The legacy bridge: `parse_core.go` derives `Unsigned`, `Length` and + `ArrayDims` from the expression, and the `experiment_coreanalyzer` cases + grow MySQL unsigned and boolean columns and a PostgreSQL two-dimensional + array, so the core path generates what the legacy path does. +5. Aliases through `canonical_oid`, dropping the alias casts, and the + value-dependent return types for ClickHouse. + +## Open questions + +- Whether MySQL's character set and collation are worth an argument. Codegen + reads neither today, but a `binary` charset changes what a driver returns. +- Whether a dialect should be able to declare an instance canonical to + another family, as SQL Server's `float(24)` is `real`, or whether reporting + `float(24)` and leaving the mapping to codegen is enough. +- How far a value-dependent return type goes. ClickHouse's `toDecimal64(x, + 4)` is the common case and a literal covers it; `arrayMap(f, arr)` returns + an array of the lambda's result, which no seed can spell. From 96c085ecd365a35cb2a9e4c3f1793835fb9423d3 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 16:43:08 +0000 Subject: [PATCH 02/16] core: review each engine's own type catalog, and canonicalize types on the way in PostgreSQL, MySQL, SQLite, ClickHouse and DuckDB were asked live what they record about a type, how they spell a column's type back, what a result's type is and how they describe a function; SQL Server and GoogleSQL from their documented catalogs and the parsers sqlc uses for them. No engine keeps arguments inside its type table: PostgreSQL and SQL Server put them on the use site, the rest in the spelling. Every engine with a catalog canonicalizes a declared type and reports the canonical form, and MySQL, ClickHouse and DuckDB compute full result types in the binder that no catalog holds. The design now canonicalizes at interning through dialect data and an engine hook, reports the canonical expression with the declared spelling kept on the attribute, nests array dimensions the way codegen needs them, and leaves result-type arguments to a per-dialect hook over the catalog's family-level answer. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- internal/core/types.md | 411 +++++++++++++++++++++++++++++++++-------- 1 file changed, 333 insertions(+), 78 deletions(-) diff --git a/internal/core/types.md b/internal/core/types.md index d9dcb44f45..2d9df5b8a2 100644 --- a/internal/core/types.md +++ b/internal/core/types.md @@ -1,10 +1,12 @@ # Types in the analysis core How the core catalog, the analyzer and `sqlc analyze` represent a type today, -where that falls short of each engine's type system, and the design that -closes the gap: every type the schema or the dialect declares is a row holding -its full expression, denormalized so that a bare name like `integer` and a -structured expression like `numeric(10, 2)` both resolve to one. +where that falls short of each engine's type system, how each engine's own +catalog represents one, and the design that closes the gap: every type the +schema or the dialect declares is a row holding its full expression, +canonicalized the way the engine would and denormalized so that a bare name +like `integer` and a structured expression like `numeric(10, 2)` both +resolve to one. ## What a type is today @@ -80,6 +82,171 @@ the attribute. The same spelling in a cast, a function result or a typed placeholder has no attribute to live on, so it degrades to its first word. That is the tell: the expression belongs to the type, not to the column. +## What each engine's own catalog says + +Each engine was asked, on a live system where one could be had, what it +records about a type, how it spells a column's type back, what it says a +query result's type is, and how it describes a function. PostgreSQL 16, +MySQL 8, SQLite 3.53, ClickHouse 25.8 and DuckDB 1.5 answered directly; SQL +Server and GoogleSQL are from their documented catalogs and the parsers +sqlc uses for them. + +**PostgreSQL** has one `pg_type` row per family, and a second row per array +type (`_int4`, with `typelem` pointing at the element). Arguments are not +part of the type: they are an opaque `int32` typmod on the use site — +`pg_attribute.atttypmod`, a domain's `typtypmod`, a function argument's +type has none — whose encoding is the type's own business and which +`format_type(oid, typmod)` decodes back into a spelling. Dimensions are +likewise on the attribute (`attndims`) and informational: `int[3]` and +`int[][]` are both the type `integer[]`. Declared types are rows with a +`typtype`: `e` with labels and their order in `pg_enum`, `c` with fields as +the attributes of a hidden relation (`typrelid`), `d` with `typbasetype`, +`typtypmod` and `typnotnull`, `r` and `m` with the subtype in `pg_range`; +pseudo-types (`anyarray`, `record`) are rows of `typtype` `p` and category +`P`. What it reports is canonical and not what was written: `int` comes back +as `integer`, `varchar(255)` as `character varying(255)`, `timestamp(3)` as +`timestamp(3) without time zone`, `int[3]` as `integer[]`, and a domain +column as the domain's name. A view keeps its columns' typmods; a prepared +statement's result types (`pg_prepared_statements.result_types`, the +protocol's RowDescription) carry the typmod only for a column read straight +from a table and `-1` for an expression, so `numeric(10,2) + 1` is +`numeric`. `pg_proc` has full signatures over families and pseudo-types, +`pg_operator` likewise, and `pg_cast` the contexts. + +**MySQL** has no catalog of types, functions or operators. What it has is +`information_schema.COLUMNS`, which describes a column twice: `DATA_TYPE` is +the family (`bigint`, `decimal`, `enum`) and `COLUMN_TYPE` is the whole +canonical spelling — `bigint unsigned`, `tinyint(1)`, `decimal(10,2) +unsigned`, `enum('a','b')`, `bigint(20) unsigned zerofill` — beside the +arguments decoded into `NUMERIC_PRECISION`, `NUMERIC_SCALE`, +`CHARACTER_MAXIMUM_LENGTH`, `DATETIME_PRECISION`, `CHARACTER_SET_NAME` and +`COLLATION_NAME`. The spelling is canonical: `BOOLEAN` becomes `tinyint(1)`, +`INTEGER` becomes `int`, `VARCHAR(10) CHARACTER SET binary` becomes +`varbinary(10)`. A view's columns show that the binder computes a full type +for every expression: `decimal(10,2) + 1` is `decimal(11,2)`, `decimal(10,2) +* decimal(10,2)` is `decimal(20,4)`, `int unsigned + 1` is `bigint +unsigned`, `CONCAT(varchar(255), 'x')` is `varchar(256)`, `SUM(int unsigned)` +is `decimal(32,0)`, `AVG(int)` is `decimal(14,4)`, `->>` is `longtext`. User +routines are described the same way in `ROUTINES` and `PARAMETERS` +(`DTD_IDENTIFIER` is `decimal(5,2)`, `bigint unsigned`); built-in functions +are not described at all. A result set on the wire is coarser than the +catalog: a storage type code (`TINY`, `LONG`, `NEWDECIMAL`, `VAR_STRING`), +a length in bytes, a decimals count and flags (`UNSIGNED`, `NOT_NULL`, +`ENUM`, `SET`, `BINARY`), so a `varchar(255)` column and `CAST(x AS +CHAR(10))` are both `VAR_STRING`, an enum is `STRING` with the `ENUM` flag, +and a `tinyint(1)` is `TINY` with length 1. That code is where the parser's +`var_string` comes from. + +**SQLite** has no catalog of types either. `pragma_table_xinfo` returns a +column's declared type verbatim — `VARCHAR(255)`, `FOO BAR(3)`, `my type`, +or nothing — and the type system is affinity: a rule over the spelling's +substrings (INT anywhere is INTEGER; CHAR, CLOB or TEXT is TEXT; BLOB or no +type is BLOB; REAL, FLOA or DOUB is REAL; anything else is NUMERIC) that +decides how a stored value is coerced, so `FOO BAR(3)` holding `'12'` +stores the integer 12. Arguments are decoration: `CAST(x AS DECIMAL(5,2))` +applies NUMERIC affinity and nothing else. A `STRICT` table admits only +`INT`, `INTEGER`, `REAL`, `TEXT`, `BLOB` and `ANY`, and rejects +`VARCHAR(10)`. An expression has no type; a value has a storage class +(`typeof()` is `integer`, `real`, `text`, `blob` or `null`), and a result +column's `sqlite3_column_decltype` is the declared spelling for a column +read from a table and nothing for anything else. `pragma_function_list` +names functions and their arity and nothing more. + +**ClickHouse** models a type as a call expression, which is why the output +shape was chosen. `system.data_type_families` lists 66 families and 73 +aliases (`INT` is an alias of `Int32`); a column's type in `system.columns` +is the whole expression, canonicalized: `Decimal32(4)` is stored as +`Decimal(9, 4)`, `Enum('a', 'b')` as `Enum8('a' = 1, 'b' = 2)`, +`Variant(String, Int64)` with its members sorted, and `Nested(a UInt8, b +String)` as two columns `n.a Array(UInt8)` and `n.b Array(String)`. +`Nullable` and `LowCardinality` are spelled as wrappers and reported as part +of the type, at any depth. Every expression has a full type computed by the +binder, from argument types with promotion — `Int8 + UInt8` is `Int16`, +`Int32 + UInt64` is `Int64`, `Int32 / Int32` is `Float64`, `Decimal(9, 4) * +Decimal(38, 10)` is `Decimal(38, 14)` — from argument values — +`toDecimal64(x, 4)` is `Decimal(18, 4)`, `toDateTime64(x, 3)` is +`DateTime64(3)` — and from literals by value: `1` is `UInt8`, `-1` is +`Int8`, `[1, NULL]` is `Array(Nullable(UInt8))`. Wrappers propagate: +`concat(lc, 'x')` is `LowCardinality(String)`, `ns = 'x'` is +`Nullable(UInt8)`, `NULL` is `Nullable(Nothing)`. A typed placeholder +`{p:Decimal(10, 2)}` is exactly its declared type. `system.functions` has a +name, an aggregate flag, an alias and a description, and no signature. + +**DuckDB** has `duckdb_types()`, one row per spelling with a `logical_type` +naming the family (`int`, `int4` and `integer` are three rows over +`INTEGER`), a category (`NUMERIC`, `STRING`, `DATETIME`, `BOOLEAN`, +`COMPOSITE`, or none for `bit` and `enum`), and `labels` for an enum, which +a `CREATE TYPE ... AS ENUM` adds to as a non-internal row in the user's +schema. `duckdb_columns().data_type` is the whole canonical expression: +`INTEGER[]`, `INTEGER[3]`, `STRUCT(a INTEGER, b VARCHAR)`, `MAP(VARCHAR, +INTEGER)`, `UNION(num INTEGER, str VARCHAR)`, `DECIMAL(18,3)`, with the +precision and scale also decoded into their own columns. Canonicalization +goes further than anywhere else: `TEXT` is `VARCHAR`, `VARCHAR(10)` is +`VARCHAR` (the length is dropped), `NUMERIC` is `DECIMAL(18,3)`, `VARINT` is +`BIGNUM`, `FLOAT4` is `FLOAT`, `JSON` keeps its name over `VARCHAR`'s id, +and a column of the named enum `mood` is reported as `ENUM('sad', 'ok')` +with the name gone. Expressions have full binder-computed types: +`DECIMAL(18,3) * DECIMAL(18,3)` is `DECIMAL(18,6)`, `1.5` is +`DECIMAL(2,1)`, `SUM(INTEGER)` is `HUGEINT`, `SUM(DECIMAL(18,3))` is +`DECIMAL(38,3)`, `INTEGER / 2` is `DOUBLE`, `[1, NULL]` is `INTEGER[]` with +no inner nullability, and `NULL` is a type of its own. `duckdb_functions()` +has signatures over families and generics — `parameter_types` are +`DECIMAL`, `INTEGER[]`, `ANY`, `T`, `K`, `V` and `return_type` a family — +so the arguments of a result are the binder's, not the catalog's. A +prepared statement's parameter types are `UNKNOWN` until bound. + +**SQL Server** describes a column in `sys.columns` as a family plus decoded +arguments: `system_type_id` and `user_type_id`, `max_length` in bytes with +`-1` for `MAX`, `precision`, `scale`, `collation_name`, `is_nullable`, +`is_identity`. `sys.types` has one row per system type — `decimal` and +`numeric` are two — and one per alias type from `CREATE TYPE ... FROM`, with +`is_user_defined`, its own `max_length`, `precision`, `scale` and +`is_nullable`, and its base's `system_type_id`; `sysname` is such a row over +`nvarchar(128)`, and CLR types like `geography` and `hierarchyid` are +assembly types sharing one `system_type_id`. Canonicalization fills in what +was left out and folds one family into another: `varchar` alone is +`varchar(1)` in a declaration and `varchar(30)` in a `CAST`, `decimal` is +`decimal(18,0)`, `datetime2` is `datetime2(7)`, `FLOAT(24)` is stored as +`real` and `FLOAT(25)` and up as `float`. `sys.all_parameters` describes a +function's parameters the same way as a column. The T-SQL parser sqlc uses +(`teesql`) reports a type as a name with a parameter list in which `MAX` is +a literal node of its own. + +**GoogleSQL** has no catalog sqlc can read offline; its two hosts describe a +column as a whole string. BigQuery's `INFORMATION_SCHEMA.COLUMNS.DATA_TYPE` +is `ARRAY>`, `NUMERIC(10, 2)` or `STRING(10)`, +and `COLUMN_FIELD_PATHS` lists every nested struct field with a path and a +`DATA_TYPE` of its own. Spanner's `SPANNER_TYPE` is `STRING(MAX)`, +`ARRAY`, `PROTO` or `ENUM`, and a column +cannot be a struct. The parser sqlc uses (`zetajones`) has a node per type +form — `SimpleType` with a `TypeParameterList`, `ArrayType`, `StructType` +with named fields, `RangeType`, `MapType`, `FunctionType` — and, as in T-SQL, +`MAX` is a literal node in the parameter list. Arrays do not nest. + +What the review settles: + +| | Unit of identity | Where arguments live | Column spelling reported | Result types | Function signatures | +|---|---|---|---|---|---| +| PostgreSQL | family row, plus one row per array type | opaque typmod and dims on the use site | canonical, decoded by `format_type` | family, typmod only for a table column | full, over families and pseudo-types | +| MySQL | none: a spelling | decoded columns beside the spelling | canonical spelling | full, computed by the binder | none for built-ins | +| SQLite | none: the declared text | none: decoration | verbatim | storage class of the value | arity only | +| ClickHouse | family, with aliases | in the spelling | canonical expression | full, computed by the binder | none | +| DuckDB | family, with aliases | in the spelling, plus decoded columns | canonical expression, some arguments dropped | full, computed by the binder | families and generics | +| SQL Server | family row and alias-type row | decoded columns on the use site | canonical, defaults filled in | family and decoded arguments | families and decoded arguments | +| GoogleSQL | none offline: a spelling | in the spelling | canonical spelling | full | none offline | + +Two conclusions follow. First, no engine keeps arguments inside its type +table: PostgreSQL and SQL Server put them on the use site and everything +else puts them in the spelling. A row per expression is nonetheless the +right shape for sqlc, because sqlc's job is to *report* the expression, and +every engine that decodes its use-site arguments decodes them into exactly +the call expression `TypeExpr` already is; PostgreSQL's typmod is a +per-type encoding that only PostgreSQL can read, so sqlc would store the +decoded form either way. Second, every engine with a catalog canonicalizes +on the way in and reports the canonical form, never the declared spelling, +and the same is true of what `goldeneye` checks the analyze cases against. +The design below changes on that point. + ## The design ### One row per type expression @@ -105,8 +272,8 @@ CREATE TABLE sql_type ( preferred INTEGER NOT NULL DEFAULT 0, family_oid INTEGER REFERENCES sql_type(oid), -- NULL on a family; the family on an instance element_oid INTEGER REFERENCES sql_type(oid), -- what the type holds: an array's element, a map's value, a range's subtype - base_oid INTEGER REFERENCES sql_type(oid), -- what the type stands on: a domain's or alias type's base, a wrapper's inner type - canonical_oid INTEGER REFERENCES sql_type(oid), -- the row an alias spelling means: integer -> int4 + base_oid INTEGER REFERENCES sql_type(oid), -- what the type stands on: a domain's or alias type's base, a wrapper's inner type, a SQLite spelling's affinity + canonical_oid INTEGER REFERENCES sql_type(oid), -- the row the engine reports this one as: integer -> int4, float(24) -> real, mood -> enum('sad', 'ok') in DuckDB not_null INTEGER NOT NULL DEFAULT 0, -- a domain or alias type declared NOT NULL UNIQUE (namespace_oid, expr) ); @@ -132,7 +299,7 @@ CREATE TABLE sql_type_arg ( `size` goes: nothing reads it. `type_length` and `type_scale` leave `sql_attribute`: they were one engine's two arguments, and now every engine's arguments are rows. `decl_type` stays, since the verbatim spelling -is what the formatter prints back. +is what the formatter prints back and what SQLite reports. The four pointer columns are the denormalization. Each is derivable by walking `sql_type_arg`, and each is what resolution asks for in one @@ -140,27 +307,68 @@ statement: - `family_oid` is what operator and function lookup fall back to when there is no overload on the instance: `numeric(10, 2) + numeric(5, 1)` finds no - operator on either instance and resolves on `numeric`. + operator on either instance and resolves on `numeric`, which is exactly + what `pg_operator` and `duckdb_functions()` hold. - `element_oid` is what a subscript, `ANY($1)`, `unnest` and a star over a - map yield, and what `IsArray` was. -- `base_oid` is what a domain, an alias type, `LowCardinality(T)` or SQLite's - affinity resolves through: `posint = 1` finds no operator on `posint` and - resolves on `int4`. -- `canonical_oid` replaces the n² implicit casts between spellings of one - type: `integer` and `int4` are two rows, so a column reports the spelling - it was declared with, and one canonical row, so resolution treats them as - one type. + map yield, and what `IsArray` was; it is PostgreSQL's `typelem` and + `pg_range.rngsubtype`. +- `base_oid` is what a domain, an alias type, `LowCardinality(T)` or a + SQLite spelling resolves through: `posint = 1` finds no operator on + `posint` and resolves on `int4`; `FOO BAR(3) = 1` resolves on `numeric`. + It is `typbasetype`, SQL Server's `system_type_id` behind a + `user_type_id`, and SQLite's affinity rule. +- `canonical_oid` is the row the engine would report this one as. An alias + spelling points at its family (`integer` at `int4`); a declared type + points at what the engine reports instead of its name when it does so + (DuckDB's `mood` at `enum('sad', 'ok')`); an instance points at the + instance canonicalization rewrote it to when the rewrite changes the + family (SQL Server's `float(24)` at `real`). It replaces the n² implicit + casts between spellings of one type. A declared type is a family row with arguments of its own. `CREATE TYPE point2 AS (x float8, y float8)` is a row named `point2`, `typtype` `c`, with -two labelled type arguments; `CREATE TYPE mood AS ENUM ('sad', 'ok')` is a -row with `typtype` `e` and two string arguments; ClickHouse's -`Enum8('active' = 1, 'deleted' = 2)` is an instance of `enum8` with two -labelled integer arguments and `base_oid` pointing at `int8`; `CREATE DOMAIN -posint AS integer` and SQL Server's `CREATE TYPE PhoneNumber FROM varchar(20) -NOT NULL` are rows with `typtype` `d`, `base_oid` at the base instance and -`not_null` set. The same table holds what the schema names and what it -constructs anonymously. +two labelled type arguments, the way PostgreSQL keeps them as the attributes +of `typrelid`; `CREATE TYPE mood AS ENUM ('sad', 'ok')` is a row with +`typtype` `e` and two string arguments in order, which is `pg_enum` and +DuckDB's `labels`; ClickHouse's `Enum8('active' = 1, 'deleted' = 2)` is an +instance of `enum8` with two labelled integer arguments and `base_oid` at +`int8`; `CREATE DOMAIN posint AS integer` and SQL Server's `CREATE TYPE +PhoneNumber FROM varchar(20) NOT NULL` are rows with `typtype` `d`, +`base_oid` at the base instance and `not_null` set. The same table holds +what the schema names and what it constructs anonymously. + +### Canonicalize on the way in; report the canonical form + +Every engine with a catalog rewrites a declared type before storing it and +reports the rewritten form, and `goldeneye` checks the analyze cases against +what the engine reports. So interning canonicalizes, in three steps that +are dialect data or dialect code: + +1. **Aliases**, which `types.jsonl` already lists: `int` and `integer` + resolve to the canonical family. This also settles what the canonical + *name* is: the one the engine reports, which for PostgreSQL is + `format_type`'s (`integer`, `bigint`, `character varying`) rather than + `pg_type`'s (`int4`, `int8`, `varchar`), so PostgreSQL's `types.jsonl` + flips which spelling is the name and which the alias. Codegen accepts + both spellings already. +2. **Argument defaults and drops**, which are data: SQL Server's `varchar` + is `varchar(1)` and `decimal` is `decimal(18, 0)`; DuckDB's `numeric` is + `decimal(18, 3)` and `varchar(10)` is `varchar`; PostgreSQL's `int[3]` is + `array(int4)`. A family in `types.jsonl` may say `"defaults": [18, 0]` + or `"args": 0`. +3. **Rewrites that change the family by argument or member**, which are + code, since they are ClickHouse's enum numbering and MySQL's charset + folding: `Decimal32(s)` is `Decimal(9, s)`, `Enum('a', 'b')` is + `Enum8('a' = 1, 'b' = 2)`, `Variant(...)` sorts its members, `varchar(n) + character set binary` is `varbinary(n)`, `boolean` is `tinyint(1)`, + `float(24)` is `real`. An engine package registers a `Canonicalize(*TypeExpr)` + hook with its seed, and the catalog applies it before interning. + +The reported type is the canonical row's expression. The declared spelling +is kept on the attribute in `decl_type`, for the formatter and for SQLite, +whose canonical form is the spelling itself. This reverses the earlier +choice of reporting a column as it was declared: the engines do not, and +the check that keeps the analyzer honest compares against the engines. ### Nullability is not a type @@ -171,7 +379,23 @@ is an instance of `array` whose one argument is `string` with `nullable` set, and `Nullable(String)` in a cast is `string` with the expression's own nullability set. This is what `TypeExpr` already says — `nullable` at whatever depth it applies, never a wrapper — and it keeps `string` and -`string nullable` from being two types that need their own operators. +`string nullable` from being two types that need their own operators. The +review confirms only ClickHouse spells inner nullability at all: DuckDB's +`[1, NULL]` is `INTEGER[]` and PostgreSQL has no such thing. + +### Array dimensions + +PostgreSQL's type for `int[][]` is `integer[]`, with the dimensions on the +attribute and unenforced; DuckDB and ClickHouse nest, `INTEGER[][]` and +`Array(Array(UInt8))`, and DuckDB distinguishes the fixed-size +`INTEGER[3]`. The expression nests one `array` per declared dimension in +every dialect, with a fixed size as a second argument (`array(integer, 3)`), +because codegen renders one slice per dimension and a two-dimensional +PostgreSQL column has to come out `[][]int32` as it does on the legacy path. +PostgreSQL's canonicalization therefore keeps the dimensions its own +catalog drops, and a future PostgreSQL check in `goldeneye` reads `attndims` +to reproduce them. Subscripting follows the dialect: PostgreSQL yields the +innermost element however many subscripts are applied. ### The catalog interns at schema time; the analyzer looks up at query time @@ -183,14 +407,15 @@ expressions become rows: - **Interned**: what the dialect seeds and what the schema declares. Every column type, every declared type's fields and base, every function signature's argument and return type becomes a row on load, through one - entry point, `ResolveTypeExpr(*TypeExpr) (oid, error)`, which walks the - expression bottom-up, interning each argument type first. `ResolveType` - and `ResolveTypeName` become callers of it. + entry point, `ResolveTypeExpr(*TypeExpr) (oid, error)`, which + canonicalizes, then walks the expression bottom-up, interning each + argument type first. `ResolveType` and `ResolveTypeName` become callers + of it. - **Looked up**: what a query writes. A cast, a constructed array or struct, a typed placeholder and a function result are resolved by - `LookupTypeExpr(*TypeExpr) (oid, familyOID, bool)`, which finds the - instance row when the schema happened to declare the same expression and - otherwise the family row, and never writes. + `LookupTypeExpr(*TypeExpr) (oid, familyOID, bool)`, which canonicalizes, + finds the instance row when the schema happened to declare the same + expression and otherwise the family row, and never writes. `exprType` becomes the pair: @@ -212,6 +437,23 @@ by a function that reads an `ast.TypeName` into a `TypeExpr`, folding dimension, `Names` into a namespace and a name, and `Spelling` through `ParseTypeExpr`. +### Result types: the family from the catalog, the arguments from the dialect + +MySQL, ClickHouse and DuckDB compute a full type for every expression in the +binder — `decimal(10,2) + 1` is `decimal(11,2)`, `Int8 + UInt8` is `Int16`, +`SUM(INTEGER)` is `HUGEINT` — and none of them keeps those rules in a +catalog; PostgreSQL's catalog says `numeric + numeric` is `numeric` and its +results drop the typmod. The catalog can only ever answer at the family +level, and that is the baseline every dialect gets: an operator or function +result is the family the overload names, with the arguments of an `$n` +result carried over from the argument it stands for. A dialect that reports +more registers a `ResultType(op, args []*TypeExpr) *TypeExpr` hook beside +its `Canonicalize` hook, and the analyzer applies it to the expression it +reports. ClickHouse needs it for its arithmetic and for value-dependent +results like `toDecimal64(x, 4)`, since its check compares whole +expressions; MySQL's check reads the wire type, which is the family with +its flags, so the baseline passes it. + ### What each engine hands the core The contract with an engine is that its `ast.TypeName` reads into a @@ -221,38 +463,41 @@ where it does not, the converter has a small change to make. | Engine | Form | Expression | |---|---|---| -| PostgreSQL | `numeric(10,2)`, `varchar(255)`, `timestamp(3) with time zone` | `numeric(10, 2)`, `varchar(255)`, `timestamptz(3)`: typmods become integer arguments on the canonical family | -| PostgreSQL | `int[]`, `int[][]` | `array(int4)`, `array(array(int4))`: one per array bound | +| PostgreSQL | `numeric(10,2)`, `varchar(255)`, `timestamp(3) with time zone` | `numeric(10, 2)`, `character varying(255)`, `timestamp with time zone(3)`: typmods become integer arguments on the canonical family, named as `format_type` names it | +| PostgreSQL | `int[]`, `int[][]`, `int[3]` | `array(integer)`, `array(array(integer))`, `array(integer)`: one per array bound, the bound itself dropped as PostgreSQL drops it | | PostgreSQL | `interval day to second` | `interval('day to second')`: the fields are one identifier argument, as `format_type` prints them | | PostgreSQL | domain, composite, enum, range | declared rows, as above; `CreateDomainStmt`, `CompositeTypeStmt` and `CreateRangeStmt` gain `schema.Apply` cases | | PostgreSQL | `myschema.mood` | a row in namespace `myschema`; `Names` resolves to a namespace rather than a dotted name | -| MySQL | `BIGINT UNSIGNED`, `DECIMAL(10,2) UNSIGNED` | `bigint unsigned`, `decimal unsigned(10, 2)`: unsigned is a family of its own, as the server reports it, rather than the alias of the signed type `types.jsonl` lists today, since the value range differs; the converter puts it in the name instead of on `ColumnDef.IsUnsigned`, which the core ignores | -| MySQL | `TINYINT(1)`, `DATETIME(6)`, `VARCHAR(255)` | `tinyint(1)`, `datetime(6)`, `varchar(255)`: the converter's `Typmods` are read | +| MySQL | `BIGINT UNSIGNED`, `DECIMAL(10,2) UNSIGNED` | `bigint unsigned`, `decimal unsigned(10, 2)`: unsigned is a family of its own, as `COLUMN_TYPE` and the wire flag report it, rather than the alias of the signed type `types.jsonl` lists today; the converter puts it in the name instead of on `ColumnDef.IsUnsigned`, which the core ignores | +| MySQL | `TINYINT(1)`, `DATETIME(6)`, `VARCHAR(255)`, `BOOLEAN` | `tinyint(1)`, `datetime(6)`, `varchar(255)`, `tinyint(1)`: the converter's `Typmods` are read, and `boolean` canonicalizes as MySQL does | | MySQL | `ENUM('a','b')`, `SET('x','y')` | `enum('a', 'b')`, `set('x', 'y')`: the converter renders `Vals` into the spelling | -| MySQL | `CAST(x AS CHAR(10))` | `char(10)`: the cast converter names the SQL type, not TiDB's `var_string` | -| MySQL | `CHARACTER SET binary`, `COLLATE` | a labelled string argument, `varchar(255, charset: 'binary')`, if codegen ever needs it; open | -| SQLite | any spelling | the spelling as an instance, `varchar(255)`, `foo bar(3)`, with `base_oid` set by the affinity rules — INT anywhere is INTEGER, CHAR, CLOB or TEXT is TEXT, BLOB or nothing is BLOB, REAL, FLOA or DOUB is REAL, else NUMERIC — applied when the dialect resolves an unknown name; `types.jsonl`'s alias lists become the rule | -| SQLite | `STRICT` tables, `ANY` | the family rows; a strict table's column names one of them or fails | +| MySQL | `CAST(x AS CHAR(10))` | `char(10)`: the cast converter names the SQL type, not the wire code `var_string` | +| MySQL | `VARCHAR(10) CHARACTER SET binary` | `varbinary(10)`, by the canonicalization hook; collation is not part of the type | +| SQLite | any spelling | the spelling as an instance, `varchar(255)`, `foo bar(3)`, verbatim as `pragma_table_xinfo` reports it, with `base_oid` set by the affinity rule applied when the dialect resolves an unknown name; `types.jsonl`'s alias lists become the rule | +| SQLite | `STRICT` tables, `ANY` | the family rows; a strict table's column names one of them or fails, as SQLite does | +| SQLite | an expression | its storage class, `integer`, `real`, `text` or `blob`, which is what `typeof()` and the check report | | ClickHouse | every parametric type | the spelling, read as today, now also for casts, `{p:T}` placeholders and results | | ClickHouse | `Nullable(T)`, `LowCardinality(T)` | `T` with `nullable`; `lowcardinality(T)` with `base_oid` at `T` | +| ClickHouse | `Decimal32(4)`, `Enum('a', 'b')`, `Variant(String, Int64)`, `INT` | `decimal(9, 4)`, `enum8(a: 1, b: 2)`, `variant(int64, string)`, `int32`, by the canonicalization hook | | ClickHouse | `SimpleAggregateFunction(sum, UInt64)` | `simpleaggregatefunction(sum, uint64)` with `sum` an identifier argument | -| ClickHouse | `toDecimal64(x, s)` | the seed's return type may name an argument's *value*: `"returns": "Decimal(18, $2)"`; the analyzer substitutes the literal when it is one and reports the family otherwise | -| ClickHouse | `Nested(a UInt8, b String)` | a relation-shape rule, not a type: the column becomes `n.a array(uint8)` and `n.b array(string)` on load | +| ClickHouse | `toDecimal64(x, s)`, `Int8 + UInt8` | `decimal(18, s)`, `int16`, by the result-type hook | +| ClickHouse | `Nested(a UInt8, b String)` | a relation-shape rule, not a type: the column becomes `n.a array(uint8)` and `n.b array(string)` on load, as `system.columns` has them | | DuckDB | `STRUCT(a INTEGER, b VARCHAR)`, `MAP(K, V)`, `UNION(...)` | `struct(a: integer, b: varchar)`, `map(varchar, integer)`, `union(num: integer, str: varchar)`: the converter renders the darkwing type expression it already has instead of keeping its name | | DuckDB | `INTEGER[]`, `INTEGER[3]` | `array(integer)` and `array(integer, 3)`: a list is the cross-dialect array, a fixed size is its second argument | -| GoogleSQL | `ARRAY`, `STRUCT`, `RANGE` | `array(int64)`, `struct(a: int64, b: string)`, `range(date)`: `ParseTypeExpr` accepts `<...>` as well as `(...)`, or the converter renders the column schema in call form | -| GoogleSQL | `STRING(10)`, `NUMERIC(10,2)`, `[1, 2]`, `STRUCT(1 AS x)` | the typmods are read; the array and struct constructors are typed from their elements | -| SQL Server | `NVARCHAR(MAX)` | `nvarchar(max)` with `max` an identifier argument | -| SQL Server | `FLOAT(24)` | `float(24)`, with `canonical_oid` at `real`: a dialect may declare an instance canonical to a family, `"canonical": {"float(1..24)": "real"}`; open | -| SQL Server | `dbo.PhoneNumber` | a row in namespace `dbo`, `typtype` `d`, `base_oid` at `varchar(20)`, `not_null` set | +| DuckDB | `TEXT`, `VARCHAR(10)`, `NUMERIC`, `mood` | `varchar`, `varchar`, `decimal(18, 3)`, `enum('sad', 'ok')`, by aliases, argument rules and `canonical_oid` on the declared enum | +| GoogleSQL | `ARRAY`, `STRUCT`, `RANGE` | `array(int64)`, `struct(a: int64, b: string)`, `range(date)`: the converter renders the zetajones type node in call form, or `ParseTypeExpr` accepts `<...>` | +| GoogleSQL | `STRING(10)`, `STRING(MAX)`, `NUMERIC(10,2)`, `[1, 2]`, `STRUCT(1 AS x)` | the typmods are read, with `max` an identifier argument; the array and struct constructors are typed from their elements | +| SQL Server | `NVARCHAR(MAX)`, `VARCHAR`, `DECIMAL` | `nvarchar(max)` with `max` an identifier argument; `varchar(1)` and `decimal(18, 0)` by the defaults `sys.types` applies | +| SQL Server | `FLOAT(24)` | `float(24)` with `canonical_oid` at `real`, by the hook | +| SQL Server | `dbo.PhoneNumber`, `sysname` | a row in namespace `dbo`, `typtype` `d`, `base_oid` at `varchar(20)`, `not_null` set; `sysname` seeded the same way over `nvarchar(128)` | The `ident` argument is the one addition to `TypeExpr` and to `goldeneye/analysis.TypeExpr`, which mirrors it. Without it `max`, `sum` and `day to second` read as types, and today they do: `SimpleAggregateFunction` -reports `sum` as a type. `ParseTypeExpr` writes a bare word that is not a -known family as an identifier only when a dialect says so — ClickHouse for -the first argument of an aggregate-function type, SQL Server for `max` — and -otherwise as a type, since a struct field's type is also a bare word. +reports `sum` as a type. Both parsers that have `MAX` model it as a literal +node, and the converters hand it over as an identifier; `ParseTypeExpr` +writes a bare word that is not a known family as an identifier only when a +dialect says so, since a struct field's type is also a bare word. ### What the analyzer reports @@ -271,53 +516,63 @@ expression as it does today, with `ident` as a fifth argument kind. ### What the seed files gain -`types.jsonl` is unchanged for a family; an alias becomes a row with -`canonical_oid` instead of a mesh of casts. A function's argument and return -types may be expressions, and the return type may reference an argument's -value as well as its type. The category rules in `dialect.json` apply to -families; an instance inherits its family's category, which is how -`numeric(10, 2)` joins the numeric casts without being seeded. +`types.jsonl` is unchanged for a family beyond the optional argument +defaults; an alias becomes a row with `canonical_oid` instead of a mesh of +casts. A function's argument and return types may be expressions, and the +return type may reference an argument's value as well as its type. The +category rules in `dialect.json` apply to families; an instance inherits +its family's category, which is how `numeric(10, 2)` joins the numeric +casts without being seeded. An engine package may register two hooks with +its seed, `Canonicalize` and `ResultType`, for what its catalog does in +code. `goldeneye` checks the analyze cases against what each database reports, and -its answer shape is the same `TypeExpr`. ClickHouse reports whole +its answer shape is the same `TypeExpr`. ClickHouse and DuckDB report whole expressions already. MySQL's driver reports the family, unsigned and nullability but not precision or length, and `ColumnType.DecimalSize` and `ColumnType.Length` can add them, with the length divided by the charset's -bytes per character. SQLite reports a declared spelling for a table column -and a storage class for an expression, which are a family row and an -instance row respectively. Every check keeps passing on the way, since a -family with no arguments prints as it does now. +bytes per character since the wire reports bytes. SQLite reports a declared +spelling for a table column and a storage class for an expression, which +are an instance row and a family row respectively. A PostgreSQL check reads +`format_type(atttypid, atttypmod)` and `attndims`. Every check keeps passing +on the way, since a family with no arguments prints as it does now. ## Order of work 1. The tables and the interning entry point: `sql_type.expr`, `family_oid`, `element_oid`, `base_oid`, `canonical_oid`, `not_null`, `sql_type_arg`, - `ResolveTypeExpr`, `LookupTypeExpr`, `TypeExprOf`. Arrays become - instances of `array`; the `[]` convention goes. Every existing golden - holds, since a bare name prints the same. + `ResolveTypeExpr`, `LookupTypeExpr`, `TypeExprOf`, with the alias step + of canonicalization. Arrays become instances of `array`; the `[]` + convention goes. Every existing golden holds, since a bare name prints + the same. 2. The analyzer: `exprType` carries the expression; casts, constructors, placeholders and function results report it; resolution falls back through the pointer chain. This is where ClickHouse's casts and parameters come right. 3. The engines, one at a time, each with an `analyze_types/` case - alongside ClickHouse's: PostgreSQL typmods, dimensions and declared types; - MySQL unsigned, typmods and members, plus the `var_string` leak; DuckDB's - nested types; GoogleSQL's angle brackets; SQL Server's `max` and alias - types; SQLite's affinity rule. + alongside ClickHouse's and each with its `Canonicalize` hook: PostgreSQL + typmods, dimensions, declared types and `format_type` names; MySQL + unsigned, typmods, members and `boolean`, plus the `var_string` leak; + DuckDB's nested types and dropped arguments; GoogleSQL's angle brackets; + SQL Server's `max`, defaults and alias types; SQLite's affinity rule. 4. The legacy bridge: `parse_core.go` derives `Unsigned`, `Length` and `ArrayDims` from the expression, and the `experiment_coreanalyzer` cases grow MySQL unsigned and boolean columns and a PostgreSQL two-dimensional array, so the core path generates what the legacy path does. -5. Aliases through `canonical_oid`, dropping the alias casts, and the - value-dependent return types for ClickHouse. +5. Result-type hooks, ClickHouse first, and the value-dependent return + types its seed needs; then MySQL's precision arithmetic once its check + reads precision from the wire. ## Open questions -- Whether MySQL's character set and collation are worth an argument. Codegen - reads neither today, but a `binary` charset changes what a driver returns. -- Whether a dialect should be able to declare an instance canonical to - another family, as SQL Server's `float(24)` is `real`, or whether reporting - `float(24)` and leaving the mapping to codegen is enough. -- How far a value-dependent return type goes. ClickHouse's `toDecimal64(x, - 4)` is the common case and a literal covers it; `arrayMap(f, arr)` returns - an array of the lambda's result, which no seed can spell. +- Whether PostgreSQL's canonical names should be `format_type`'s. It is + what the engine reports and what a check would compare against, but it + changes what `sqlc analyze` prints for every PostgreSQL column today + (`bigint` for `bigserial`, `integer` for `int4`), and `bigserial` is not + a type PostgreSQL has at all. +- How far a result-type hook goes. ClickHouse's arithmetic promotion and + `toDecimal64(x, 4)` are finite rules; `arrayMap(f, arr)` returns an array + of the lambda's result, which needs the lambda typed first. +- Whether DuckDB's dropped `VARCHAR(10)` length and reported anonymous enum + should be canonicalized away as DuckDB does, or kept because a user + declared them. The check decides for the former. From 9dcdbf70a6fb136ed0f066cd89d1cd213fd07783 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 16:47:42 +0000 Subject: [PATCH 03/16] core: PostgreSQL's canonical type names are format_type's Settles the open question: a PostgreSQL type is reported as format_type spells it, read into the same expression every dialect reports, with the typmod lifted out of a multi-word name or from before an array suffix. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- internal/core/types.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/internal/core/types.md b/internal/core/types.md index 2d9df5b8a2..2d836f9dbd 100644 --- a/internal/core/types.md +++ b/internal/core/types.md @@ -351,6 +351,22 @@ are dialect data or dialect code: `pg_type`'s (`int4`, `int8`, `varchar`), so PostgreSQL's `types.jsonl` flips which spelling is the name and which the alias. Codegen accepts both spellings already. + + `format_type` is a string and the output is an expression, so the + spelling is read into one, and its grammar is slightly wider than + `name(args)`: the typmod may sit inside a multi-word name + (`timestamp(3) without time zone`, `time(4) with time zone`), after + trailing words (`interval day to second(3)`), or before an array suffix + (`numeric(10,2)[]`, `character varying(255)[]`), and one name is quoted + (`"char"`). The family is the words with the parenthesis lifted out, so + `timestamp(3) without time zone` is `{name: "timestamp without time + zone", args: [3]}`, `interval day to second(3)` is `interval` with an + identifier argument and an integer one, and `numeric(10,2)[]` is + `array(numeric(10, 2))`. Two spellings are the same family under + different typmods, `bpchar` and `character(5)`, and canonicalize to + `character`. The PostgreSQL goldens change with this: `bigserial` + becomes `bigint` (a serial is a default, not a type PostgreSQL reports), + `int4` becomes `integer`, `varchar` becomes `character varying`. 2. **Argument defaults and drops**, which are data: SQL Server's `varchar` is `varchar(1)` and `decimal` is `decimal(18, 0)`; DuckDB's `numeric` is `decimal(18, 3)` and `varchar(10)` is `varchar`; PostgreSQL's `int[3]` is @@ -565,11 +581,6 @@ on the way, since a family with no arguments prints as it does now. ## Open questions -- Whether PostgreSQL's canonical names should be `format_type`'s. It is - what the engine reports and what a check would compare against, but it - changes what `sqlc analyze` prints for every PostgreSQL column today - (`bigint` for `bigserial`, `integer` for `int4`), and `bigserial` is not - a type PostgreSQL has at all. - How far a result-type hook goes. ClickHouse's arithmetic promotion and `toDecimal64(x, 4)` are finite rules; `arrayMap(f, arr)` returns an array of the lambda's result, which needs the lambda typed first. From b4271e2168bfa8cc6bd09827aa439b5d4f4b13cb Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 16:52:37 +0000 Subject: [PATCH 04/16] core: add a worked example of an array of arrays to the types note Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- internal/core/types.md | 92 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/internal/core/types.md b/internal/core/types.md index 2d836f9dbd..0b0b27a9fb 100644 --- a/internal/core/types.md +++ b/internal/core/types.md @@ -553,6 +553,98 @@ are an instance row and a family row respectively. A PostgreSQL check reads `format_type(atttypid, atttypmod)` and `attndims`. Every check keeps passing on the way, since a family with no arguments prints as it does now. +## A worked example: an array of arrays of integers + +```sql +CREATE TABLE grids ( + id bigint PRIMARY KEY, + cells int[][] NOT NULL +); + +-- name: GetGrid :one +SELECT id, cells, cells[1][2] AS cell FROM grids WHERE cells = $1; +``` + +The PostgreSQL parser hands over `pg_catalog.int4` with two array bounds, +which reads into `array(array(int4))`; canonicalization makes it +`array(array(integer))`. Interning walks it bottom-up, so the inner array +gets its row before the outer one. With illustrative OIDs, `sql_type` +holds the seeded families and the two rows the schema added: + +| oid | name | expr | typtype | category | family_oid | element_oid | canonical_oid | +|---|---|---|---|---|---|---|---| +| 23 | integer | integer | b | N | | | | +| 24 | int4 | int4 | b | N | | | 23 | +| 20 | bigint | bigint | b | N | | | | +| 100 | array | array | b | A | | | | +| 1001 | array | array(integer) | b | A | 100 | 23 | | +| 1002 | array | array(array(integer)) | b | A | 100 | 1001 | | + +Row 1001 is what PostgreSQL calls `_int4`; row 1002 is the one PostgreSQL +does not have, since its own type collapses the dimensions onto `attndims`. +An instance takes its family's namespace. `sql_type_arg` has one row per +argument position: + +| type_oid | ord | label | arg_type_oid | nullable | +|---|---|---|---|---| +| 1001 | 1 | | 23 | 0 | +| 1002 | 1 | | 1001 | 0 | + +and `sql_attribute` points `grids.cells` at row 1002, `not_null` set, with +`int[][]` in `decl_type`. + +Analysis resolves `cells` to that attribute, so its `exprType` is +`{typeOID: 1002, expr: array(array(integer)), nullable: false}`, and the +parameter compared with it takes the same type. `cells[1][2]` follows the +dialect's subscript rule — PostgreSQL yields the innermost element however +many subscripts are applied — which is a walk down `element_oid` from 1002 +to 1001 to 23, nullable because a subscript can miss. `sqlc analyze` prints: + +```json +[ + { + "name": "GetGrid", + "cmd": ":one", + "columns": [ + { "name": "id", "type": { "name": "bigint" }, "table": "grids" }, + { + "name": "cells", + "type": { + "name": "array", + "args": [ + { "type": { "name": "array", "args": [ { "type": { "name": "integer" } } ] } } + ] + }, + "table": "grids" + }, + { "name": "cell", "type": { "name": "integer", "nullable": true } } + ], + "params": [ + { + "number": 1, + "column": { + "name": "cells", + "type": { + "name": "array", + "args": [ + { "type": { "name": "array", "args": [ { "type": { "name": "integer" } } ] } } + ] + }, + "table": "grids" + } + } + ] + } +] +``` + +Its string form is `array(array(integer))`; today the same column prints +`array(int4)`, one level, with `type_oid` pointing at a row named `int4[]`. +The legacy bridge derives `DataType` `integer` and `ArrayDims` 2 from the +nesting, so Go codegen renders `[][]int32` as the legacy path does. +DuckDB's `INTEGER[][]` and ClickHouse's `Array(Array(Int32))` produce the +same rows and output, with `int32` in ClickHouse's case. + ## Order of work 1. The tables and the interning entry point: `sql_type.expr`, `family_oid`, From 895ed0107d5b5a0cfe1e0cb538b715139a3819d8 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 17:16:03 +0000 Subject: [PATCH 05/16] core: one type row per expression, canonicalized, with the analyzer carrying the expression sql_type now holds a row per type expression rather than per name: a family row for a name the dialect or the schema declares, an instance row for a family applied to arguments, with the arguments in sql_type_arg and family_oid, element_oid, base_oid and canonical_oid pointing at what resolution falls back to. An alias spelling is a row pointing at the type it names, or at a base type it stands on for SQLite, instead of a mesh of implicit casts. Interning canonicalizes and reads array suffixes into instances of the array family, so the "name[]" convention is gone. The analyzer carries the expression beside the row, so a cast, a constructed array, a placeholder or a function result reports the whole type, and operator and function resolution walk the pointer chain, so numeric(10, 2) + numeric(5, 1) resolves on numeric. The legacy bridge derives array dimensions, length and unsignedness from the expression. PostgreSQL's seed names its types as format_type does, integer rather than int4, and codegen accepts those spellings. The ClickHouse converter marks a column nullable only for a Nullable at the top of its type. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- internal/codegen/golang/postgresql_type.go | 12 +- internal/compiler/catalog_core.go | 28 +- internal/compiler/parse_core.go | 32 +- internal/core/analysis.go | 2 - internal/core/analyzer/analyzer.go | 5 +- internal/core/analyzer/dml.go | 1 + internal/core/analyzer/expr.go | 186 +++++--- internal/core/analyzer/projection.go | 40 +- internal/core/attribute.go | 30 +- internal/core/catalog.go | 3 + internal/core/catalogdb/models.go | 20 +- internal/core/catalogdb/query.sql.go | 208 ++++++-- internal/core/catalogdef/query.sql | 59 ++- internal/core/catalogdef/schema.sql | 70 ++- internal/core/schema/schema.go | 23 +- internal/core/seed/extension.go | 10 +- internal/core/seed/seed.go | 109 +++-- internal/core/typeexpr.go | 92 +++- internal/core/typename.go | 113 ++++- internal/core/types.go | 446 ++++++++++++++++-- .../analyze_ast/postgresql/stdout.json | 2 +- .../testdata/analyze_basic/duckdb/stdout.json | 4 +- .../testdata/analyze_basic/mysql/stdout.json | 7 +- .../analyze_basic/postgresql/stdout.json | 4 +- .../testdata/analyze_dml/duckdb/stdout.json | 12 +- .../testdata/analyze_dml/mysql/stdout.json | 14 +- .../analyze_dml/postgresql/stdout.json | 14 +- .../analyze_extension/postgresql/stdout.json | 6 +- .../analyze_params/duckdb/stdout.json | 10 +- .../testdata/analyze_params/mysql/stdout.json | 28 +- .../analyze_params/postgresql/stdout.json | 18 +- .../analyze_select/duckdb/stdout.json | 12 +- .../analyze_select/googlesql/stdout.json | 7 +- .../testdata/analyze_select/mysql/stdout.json | 49 +- .../analyze_select/postgresql/stdout.json | 20 +- .../postgresql/stdout.json | 2 +- internal/engine/clickhouse/convert.go | 10 +- .../engine/postgresql/dialect/dialect.json | 6 +- .../engine/postgresql/dialect/types.jsonl | 32 +- internal/engine/sqlite/dialect/dialect.json | 1 + 40 files changed, 1297 insertions(+), 450 deletions(-) diff --git a/internal/codegen/golang/postgresql_type.go b/internal/codegen/golang/postgresql_type.go index 048c8533d7..3bdfed6a1a 100644 --- a/internal/codegen/golang/postgresql_type.go +++ b/internal/codegen/golang/postgresql_type.go @@ -216,7 +216,7 @@ func postgresType(req *plugin.GenerateRequest, options *opts.Options, col *plugi } return "sql.NullTime" - case "pg_catalog.time": + case "pg_catalog.time", "time", "time without time zone": if driver == opts.SQLDriverPGXV5 { return "pgtype.Time" } @@ -228,7 +228,7 @@ func postgresType(req *plugin.GenerateRequest, options *opts.Options, col *plugi } return "sql.NullTime" - case "pg_catalog.timetz": + case "pg_catalog.timetz", "timetz", "time with time zone": if notNull { return "time.Time" } @@ -237,7 +237,7 @@ func postgresType(req *plugin.GenerateRequest, options *opts.Options, col *plugi } return "sql.NullTime" - case "pg_catalog.timestamp", "timestamp": + case "pg_catalog.timestamp", "timestamp", "timestamp without time zone": if driver == opts.SQLDriverPGXV5 { return "pgtype.Timestamp" } @@ -249,7 +249,7 @@ func postgresType(req *plugin.GenerateRequest, options *opts.Options, col *plugi } return "sql.NullTime" - case "pg_catalog.timestamptz", "timestamptz": + case "pg_catalog.timestamptz", "timestamptz", "timestamp with time zone": if driver == opts.SQLDriverPGXV5 { return "pgtype.Timestamptz" } @@ -261,7 +261,7 @@ func postgresType(req *plugin.GenerateRequest, options *opts.Options, col *plugi } return "sql.NullTime" - case "text", "pg_catalog.varchar", "pg_catalog.bpchar", "string", "citext", "name": + case "text", "pg_catalog.varchar", "varchar", "character varying", "pg_catalog.bpchar", "bpchar", "character", "string", "citext", "name": if notNull { return "string" } @@ -470,7 +470,7 @@ func postgresType(req *plugin.GenerateRequest, options *opts.Options, col *plugi } return "any" - case "bit", "varbit", "pg_catalog.bit", "pg_catalog.varbit": + case "bit", "varbit", "bit varying", "pg_catalog.bit", "pg_catalog.varbit": if driver == opts.SQLDriverPGXV5 { return "pgtype.Bits" } diff --git a/internal/compiler/catalog_core.go b/internal/compiler/catalog_core.go index ce89416926..dd7cbaed8d 100644 --- a/internal/compiler/catalog_core.go +++ b/internal/compiler/catalog_core.go @@ -32,19 +32,25 @@ func coreResultCatalog(c *core.Catalog) (*catalog.Catalog, error) { } t := &catalog.Table{Rel: &ast.TableName{Schema: ns.Name, Name: table.Name}} for _, col := range cols { - // The catalog names an array type after its element with the - // suffix appended, which is codegen's data type and array - // flag in one string. The core catalog holds one dimension, - // and codegen renders a "[]" per dimension. - dataType, isArray := strings.CutSuffix(col.TypeName, core.ArraySuffix) + // Codegen reads a data type and an array flag, and renders + // a "[]" per dimension, so an array of arrays of integers + // is the type integer with two dimensions. + expr, err := c.TypeExprOf(col.TypeOID) + if err != nil { + return nil, err + } + inner := expr.Innermost() column := &catalog.Column{ - Name: col.Name, - Type: ast.TypeName{Name: dataType}, - IsNotNull: col.NotNull, - IsArray: isArray, + Name: col.Name, + Type: ast.TypeName{Name: inner.Name}, + IsNotNull: col.NotNull, + IsArray: expr.IsArray(), + ArrayDims: expr.ArrayDims(), + IsUnsigned: strings.HasSuffix(inner.Name, " unsigned"), } - if isArray { - column.ArrayDims = 1 + if len(inner.Args) > 0 && inner.Args[0].Int != nil { + l := int(*inner.Args[0].Int) + column.Length = &l } t.Columns = append(t.Columns, column) } diff --git a/internal/compiler/parse_core.go b/internal/compiler/parse_core.go index 7e5685a962..4c36d7d08c 100644 --- a/internal/compiler/parse_core.go +++ b/internal/compiler/parse_core.go @@ -107,21 +107,33 @@ func coreColumn(c core.Column) *Column { IsArray: c.IsArray, TypeExpr: c.Type, } - // The core reports arrays without dimensions, and codegen renders one - // "[]" per dimension. - if c.IsArray { - col.ArrayDims = 1 - } + describeType(col, c.Type) if c.Source != nil && c.Source.Table != "" { col.Table = &ast.TableName{Schema: c.Source.Schema, Name: c.Source.Table} col.TableAlias = c.Source.TableAlias col.OriginalName = c.Source.Column } - if c.TypeLength > 0 { - l := c.TypeLength + return col +} + +// describeType fills in what codegen reads about a type from its +// expression: one array dimension per nesting, the length that is the +// innermost type's first integer argument (which is how a MySQL tinyint(1) +// is told from a tinyint), and whether the innermost type is unsigned. +func describeType(col *Column, t *core.TypeExpr) { + if t == nil { + if col.IsArray { + col.ArrayDims = 1 + } + return + } + col.ArrayDims = t.ArrayDims() + inner := t.Innermost() + if len(inner.Args) > 0 && inner.Args[0].Int != nil { + l := int(*inner.Args[0].Int) col.Length = &l } - return col + col.Unsigned = strings.HasSuffix(inner.Name, " unsigned") } func coreParamColumn(p core.Parameter, params *named.ParamSet) *Column { @@ -132,9 +144,7 @@ func coreParamColumn(p core.Parameter, params *named.ParamSet) *Column { IsArray: p.IsArray, TypeExpr: p.Type, } - if p.IsArray { - col.ArrayDims = 1 - } + describeType(col, p.Type) if p.Source != nil && p.Source.Table != "" { col.Table = &ast.TableName{Schema: p.Source.Schema, Name: p.Source.Table} col.OriginalName = p.Source.Column diff --git a/internal/core/analysis.go b/internal/core/analysis.go index 7542d12b1e..b0854bf457 100644 --- a/internal/core/analysis.go +++ b/internal/core/analysis.go @@ -65,8 +65,6 @@ type Column struct { SourceAttributeOID int64 `json:"source_attribute_oid,omitempty"` Source *ColumnSource `json:"source,omitempty"` DeclType string `json:"decl_type,omitempty"` - TypeLength int `json:"type_length,omitempty"` - TypeScale int `json:"type_scale,omitempty"` IsPrimaryKey bool `json:"is_primary_key,omitempty"` IsUnique bool `json:"is_unique,omitempty"` IsAutoIncrement bool `json:"is_auto_increment,omitempty"` diff --git a/internal/core/analyzer/analyzer.go b/internal/core/analyzer/analyzer.go index 8bc92bf5b3..98076c429a 100644 --- a/internal/core/analyzer/analyzer.go +++ b/internal/core/analyzer/analyzer.go @@ -112,6 +112,7 @@ func derivedRel(alias string, cols []core.Column) scopeRel { AttOID: col.SourceAttributeOID, Name: col.Name, TypeOID: col.TypeOID, + Type: col.Type.WithNullable(false), NotNull: col.NotNull, }) } @@ -123,12 +124,12 @@ func (a *analyzer) result() core.PrepareResult { // the dialect has such a type. if oid, ok := a.cat.UntypedTypeOID(); ok { for n, p := range a.params { - if p.TypeOID == 0 && p.DataType == "" { + if p.TypeOID == 0 && p.Type == nil { t := exprType{typeOID: oid, nullable: true} p.TypeOID = oid p.DataType, p.IsArray = a.typeNameOf(t) p.NotNull = false - p.Type = a.typeExprOf(t, "") + p.Type = a.typeExprOf(t) a.params[n] = p } } diff --git a/internal/core/analyzer/dml.go b/internal/core/analyzer/dml.go index 28cbc5193c..8f188b82da 100644 --- a/internal/core/analyzer/dml.go +++ b/internal/core/analyzer/dml.go @@ -202,6 +202,7 @@ func findColumn(rel scopeRel, name string) (core.ClassColumn, bool) { func columnType(rel scopeRel, col core.ClassColumn) exprType { return exprType{ typeOID: col.TypeOID, + expr: col.Type, nullable: !col.NotNull, sourceClassOID: rel.classOID, sourceAttributeOID: col.AttOID, diff --git a/internal/core/analyzer/expr.go b/internal/core/analyzer/expr.go index 904993e703..a82e3bd55a 100644 --- a/internal/core/analyzer/expr.go +++ b/internal/core/analyzer/expr.go @@ -2,6 +2,7 @@ package analyzer import ( "fmt" + "slices" "strings" "github.com/sqlc-dev/sqlc/internal/core" @@ -9,12 +10,16 @@ import ( ) type exprType struct { + // typeOID is the type's row: the instance when the catalog holds one, + // else the family, else 0 for a type no dialect seeded and no schema + // declared. typeOID int64 - // typeName names a type the catalog does not hold — a cast to a type no - // dialect seeded and no schema declared, or an array of one. Analysis - // never adds a type: it reports the name the query used and carries on, - // so a query can be analyzed against a catalog it cannot write to. - typeName string + // expr is the whole expression when it says more than the row does — a + // cast to numeric(5, 1) when only numeric is a row — or names a type + // the catalog does not hold. Analysis never adds a type: it reports the + // expression the query used and carries on, so a query can be analyzed + // against a catalog it cannot write to. + expr *core.TypeExpr nullable bool sourceClassOID int64 sourceAttributeOID int64 @@ -167,13 +172,7 @@ func (a *analyzer) typeColumnRef(c *ast.ColumnRef) (exprType, error) { } return exprType{}, fmt.Errorf("unknown column %q", column) } - return exprType{ - typeOID: col.TypeOID, - nullable: !col.NotNull, - sourceClassOID: rel.classOID, - sourceAttributeOID: col.AttOID, - sourceTableAlias: rel.alias, - }, nil + return columnType(rel, col), nil } func flattenFields(fields *ast.List) []string { @@ -207,12 +206,12 @@ func (a *analyzer) inferParam(number int, t exprType) { if !ok { cur = core.Parameter{Number: number} } - typed := cur.TypeOID == 0 && cur.DataType == "" && (t.typeOID != 0 || t.typeName != "") + typed := cur.TypeOID == 0 && cur.Type == nil && (t.typeOID != 0 || t.expr != nil) if typed { cur.TypeOID = t.typeOID cur.DataType, cur.IsArray = a.typeNameOf(t) cur.NotNull = !t.nullable - cur.Type = a.typeExprOf(t, "") + cur.Type = a.typeExprOf(t) } if cur.Source == nil && t.sourceAttributeOID != 0 { ad, err := a.cat.LookupAttribute(t.sourceAttributeOID) @@ -223,9 +222,6 @@ func (a *analyzer) inferParam(number int, t exprType) { TableAlias: t.sourceTableAlias, Column: ad.Column, } - if typed { - cur.Type = a.typeExprOf(t, ad.DeclType) - } } } a.params[number] = cur @@ -307,7 +303,7 @@ func (a *analyzer) typeAExpr(e *ast.A_Expr) (exprType, error) { rightT = leftT } - overload, err := a.resolveOperator(opName, leftT.typeOID, rightT.typeOID) + overload, err := a.resolveOperator(opName, leftT, rightT) if err != nil { return exprType{}, err } @@ -384,7 +380,7 @@ func (a *analyzer) typeIn(e *ast.In) (exprType, error) { return exprType{}, err } if len(cols) > 0 { - if err := a.typeOperands(e.Expr, exprType{typeOID: cols[0].TypeOID, nullable: !cols[0].NotNull}); err != nil { + if err := a.typeOperands(e.Expr, columnExprType(cols[0])); err != nil { return exprType{}, err } } @@ -450,9 +446,9 @@ func (a *analyzer) typeCoalesce(e *ast.CoalesceExpr) (exprType, error) { if err != nil { return exprType{}, err } - if !found && t.typeOID != 0 { + if !found && (t.typeOID != 0 || t.expr != nil) { // The result is an expression's, not the column's it came from. - out = exprType{typeOID: t.typeOID, typeName: t.typeName} + out = exprType{typeOID: t.typeOID, expr: t.expr} found = true } nullable = nullable && t.nullable @@ -472,7 +468,7 @@ func (a *analyzer) typeFirstOf(nodes []ast.Node, nullable bool) (exprType, error if err != nil { return exprType{}, err } - if !found && t.typeOID != 0 { + if !found && (t.typeOID != 0 || t.expr != nil) { out = t found = true } @@ -488,36 +484,85 @@ func (a *analyzer) typeArrayExpr(e *ast.A_ArrayExpr) (exprType, error) { if err != nil { return exprType{}, err } - element, _ := a.typeNameOf(elemT) - if element == "" { + element := a.exprOf(elemT) + if element == nil { return exprType{}, nil } - return a.namedType(element + core.ArraySuffix), nil + return a.lookupType(core.Array(element.WithNullable(false))), nil +} + +// lookupType is the type an expression refers to: its row when the catalog +// holds one, its family's row when it holds only that, and the expression +// alone when it holds neither. +func (a *analyzer) lookupType(t *core.TypeExpr) exprType { + if t == nil { + return exprType{} + } + found, ok := a.cat.LookupTypeExpr(t) + if !ok { + return exprType{expr: t.WithNullable(false)} + } + out := exprType{typeOID: found.OID} + if found.OID == found.FamilyOID && len(found.Expr.Args) > 0 { + out.expr = found.Expr + } + return out } // namedType is the type a name refers to, or the name itself when the catalog // has no such type. func (a *analyzer) namedType(name string) exprType { - if oid, err := a.cat.TypeOID(name); err == nil { - return exprType{typeOID: oid} + return a.lookupType(core.ParseTypeExpr(name)) +} + +// exprOf is a type's expression: the one the analysis carries, or the one +// its row stands for. It is a copy, and nil for an untyped expression. +func (a *analyzer) exprOf(t exprType) *core.TypeExpr { + if t.expr != nil { + return t.expr.Clone() + } + if t.typeOID == 0 { + return nil } - return exprType{typeName: name} + e, err := a.cat.TypeExprOf(t.typeOID) + if err != nil { + return nil + } + return e } -// typeNameOf reports a type's name and whether it is an array of that name, -// whether the type is one the catalog holds or one only the query named. -func (a *analyzer) typeNameOf(t exprType) (string, bool) { - name := t.typeName - if t.typeOID != 0 { - var err error - if name, err = a.cat.TypeName(t.typeOID); err != nil { - return "", false - } +// typeExprOf writes a type as the expression a result reports, with the +// expression's own nullability set from the analysis. +func (a *analyzer) typeExprOf(t exprType) *core.TypeExpr { + e := a.exprOf(t) + if e == nil { + return nil } - if element, ok := strings.CutSuffix(name, core.ArraySuffix); ok { - return element, true + e.Nullable = t.nullable + return e +} + +// typeNameOf reports a type's innermost family name and whether the type is +// an array, which is the flat view the legacy compiler reads. +func (a *analyzer) typeNameOf(t exprType) (string, bool) { + e := a.exprOf(t) + if e == nil { + return "", false } - return name, false + return e.Innermost().Name, e.IsArray() +} + +// columnExprType is the type a result column of a nested query has, as an +// operand of the query around it. +func columnExprType(col core.Column) exprType { + return exprType{typeOID: col.TypeOID, expr: col.Type.WithNullable(false), nullable: !col.NotNull} +} + +// familyOID is the row everything about a type is registered on: the end of +// its resolution chain. +func (a *analyzer) familyOID(oid int64) int64 { + chain := a.cat.ResolutionChain(oid) + return chain[len(chain)-1] } // typeSubLink types a subquery used as an expression: EXISTS and IN yield a @@ -543,7 +588,9 @@ func (a *analyzer) typeSubLink(e *ast.SubLink) (exprType, error) { return exprType{nullable: true}, nil } // A subquery that matches no row yields NULL. - return exprType{typeOID: cols[0].TypeOID, nullable: true}, nil + t := columnExprType(cols[0]) + t.nullable = true + return t, nil default: return a.boolType(false) } @@ -586,7 +633,7 @@ func (a *analyzer) typeNullIf(e *ast.A_Expr) (exprType, error) { // placeholder that type. func (a *analyzer) typeOperands(n ast.Node, other exprType) error { if pr, ok := n.(*ast.ParamRef); ok { - if other.typeOID != 0 || other.typeName != "" { + if other.typeOID != 0 || other.expr != nil { a.inferParam(pr.Number, other) } return nil @@ -620,14 +667,26 @@ func opNameFromList(l *ast.List) string { return strings.Join(parts, ".") } -func (a *analyzer) resolveOperator(name string, leftOID, rightOID int64) (core.OperatorOverload, error) { - candidates, err := a.cat.FindOperators(name, leftOID, rightOID) - if err != nil { - return core.OperatorOverload{}, err - } - if len(candidates) > 0 { - return candidates[0], nil +// resolveOperator finds the overload of an operator over two operand types. +// An operator is registered on a family, so an operand that is an instance, +// an alias or a domain is looked up along its resolution chain: numeric(10, +// 2) + numeric(5, 1) resolves on numeric. +func (a *analyzer) resolveOperator(name string, leftT, rightT exprType) (core.OperatorOverload, error) { + leftChain := a.cat.ResolutionChain(leftT.typeOID) + rightChain := a.cat.ResolutionChain(rightT.typeOID) + for _, l := range leftChain { + for _, r := range rightChain { + candidates, err := a.cat.FindOperators(name, l, r) + if err != nil { + return core.OperatorOverload{}, err + } + if len(candidates) > 0 { + return candidates[0], nil + } + } } + leftOID := leftChain[len(leftChain)-1] + rightOID := rightChain[len(rightChain)-1] all, err := a.cat.FindOperators(name, 0, 0) if err != nil { @@ -693,7 +752,7 @@ func (a *analyzer) typeFuncCall(f *ast.FuncCall) (exprType, error) { if overloads, err := a.cat.FindProcs("count", nil); err == nil && len(overloads) > 0 { return exprType{typeOID: overloads[0].ReturnTypeOID, nullable: overloads[0].ReturnNullable}, nil } - oid, err := a.cat.TypeOID("int8") + oid, err := a.cat.TypeOID("bigint") if err != nil { return exprType{}, err } @@ -759,12 +818,12 @@ func (a *analyzer) returnType(p core.ProcOverload, argTypes []exprType) exprType } if n, ok := argIndex(name); ok { if n < len(argTypes) { - return exprType{typeOID: argTypes[n].typeOID, typeName: argTypes[n].typeName} + return exprType{typeOID: argTypes[n].typeOID, expr: argTypes[n].expr} } return exprType{} } - if isPolymorphic(name) && argTypes[0].typeOID != 0 { - return exprType{typeOID: argTypes[0].typeOID} + if isPolymorphic(name) && (argTypes[0].typeOID != 0 || argTypes[0].expr != nil) { + return exprType{typeOID: argTypes[0].typeOID, expr: argTypes[0].expr} } return exprType{typeOID: p.ReturnTypeOID} } @@ -815,12 +874,19 @@ func isPolymorphic(typeName string) bool { } // pickOverload chooses the overload whose parameters the call's arguments -// match best: an exact type match on a parameter beats a polymorphic one, -// which beats a mismatch, and any overload of the right arity beats one of -// the wrong arity. +// match best: an exact type match on a parameter beats a match on the +// argument's family, which beats a polymorphic parameter, which beats a +// mismatch, and any overload of the right arity beats one of the wrong +// arity. func (a *analyzer) pickOverload(overloads []core.ProcOverload, argTypes []int64) core.ProcOverload { best := -1 bestScore := -1 + chains := make([][]int64, len(argTypes)) + for j, oid := range argTypes { + if oid != 0 { + chains[j] = a.cat.ResolutionChain(oid) + } + } for i := range overloads { ov := &overloads[i] if len(ov.ArgTypes) != len(argTypes) { @@ -830,6 +896,8 @@ func (a *analyzer) pickOverload(overloads []core.ProcOverload, argTypes []int64) for j, oid := range argTypes { switch { case oid != 0 && oid == ov.ArgTypes[j]: + score += 3 + case oid != 0 && slices.Contains(chains[j], ov.ArgTypes[j]): score += 2 case a.isPolymorphicOID(ov.ArgTypes[j]): score += 1 @@ -859,11 +927,11 @@ func (a *analyzer) typeTypeCast(c *ast.TypeCast) (exprType, error) { if c.TypeName == nil { return exprType{}, fmt.Errorf("cast: missing target type") } - name := core.TypeNameString(c.TypeName) - if name == "" { + target := core.TypeExprOfTypeName(c.TypeName) + if target == nil { return exprType{}, fmt.Errorf("cast: missing target type") } - t := a.namedType(name) + t := a.lookupType(target) // A cast is how a query says what an otherwise untyped placeholder holds. if err := a.typeOperands(c.Arg, t); err != nil { return exprType{}, err diff --git a/internal/core/analyzer/projection.go b/internal/core/analyzer/projection.go index f3293dcbc6..7366997d8a 100644 --- a/internal/core/analyzer/projection.go +++ b/internal/core/analyzer/projection.go @@ -32,7 +32,7 @@ func (a *analyzer) projectTarget(rt *ast.ResTarget) error { a.params[pr.Number] = p } } - if t.typeOID == 0 && t.typeName == "" { + if t.typeOID == 0 && t.expr == nil { if oid, ok := a.cat.UntypedTypeOID(); ok { t = exprType{typeOID: oid, nullable: true} } @@ -46,8 +46,8 @@ func (a *analyzer) projectTarget(rt *ast.ResTarget) error { SourceAttributeOID: t.sourceAttributeOID, } col.DataType, col.IsArray = a.typeNameOf(t) + col.Type = a.typeExprOf(t) a.decorateSource(&col, t.sourceAttributeOID, t.sourceTableAlias) - col.Type = a.typeExprOf(t, col.DeclType) if rt.Name == nil || *rt.Name == "" { a.qualifyDuplicate(&col, t.sourceTableAlias) } @@ -77,35 +77,6 @@ func (a *analyzer) qualifyDuplicate(col *core.Column, alias string) { } } -// typeExprOf writes a type as an expression. A source column's declared -// spelling carries what the catalog's flat name cannot, so it is parsed -// when there is one; otherwise the expression is the type's name, wrapped -// in an array when the type is one. Nullability comes from the spelling -// when the spelling says anything about it, and from the analysis -// otherwise. -func (a *analyzer) typeExprOf(t exprType, declType string) *core.TypeExpr { - name, isArray := a.typeNameOf(t) - if name == "" && declType == "" { - return nil - } - var expr *core.TypeExpr - if declType != "" { - expr = core.ParseTypeExpr(declType) - if isArray && expr.Name != "array" { - expr = &core.TypeExpr{Name: "array", Args: []core.TypeArg{{Type: expr}}} - } - } else { - expr = core.ParseTypeExpr(name) - if isArray { - expr = &core.TypeExpr{Name: "array", Args: []core.TypeArg{{Type: expr}}} - } - } - if !expr.HasNullable() { - expr.Nullable = t.nullable - } - return expr -} - func (a *analyzer) decorateSource(col *core.Column, attOID int64, tableAlias string) { if attOID == 0 { return @@ -121,8 +92,6 @@ func (a *analyzer) decorateSource(col *core.Column, attOID int64, tableAlias str Column: ad.Column, } col.DeclType = ad.DeclType - col.TypeLength = ad.TypeLength - col.TypeScale = ad.TypeScale col.IsPrimaryKey = ad.IsPrimaryKey col.IsUnique = ad.IsUnique col.IsAutoIncrement = ad.AutoIncrement @@ -177,9 +146,10 @@ func (a *analyzer) emitStar(rt *ast.ResTarget, fields []string) { SourceClassOID: rel.classOID, SourceAttributeOID: c.AttOID, } - col.DataType, col.IsArray = a.typeNameOf(exprType{typeOID: c.TypeOID}) + t := exprType{typeOID: c.TypeOID, expr: c.Type, nullable: !c.NotNull} + col.DataType, col.IsArray = a.typeNameOf(t) + col.Type = a.typeExprOf(t) a.decorateSource(&col, c.AttOID, rel.alias) - col.Type = a.typeExprOf(exprType{typeOID: c.TypeOID, nullable: !c.NotNull}, col.DeclType) a.qualifyDuplicate(&col, rel.alias) a.columns = append(a.columns, col) star.Columns = append(star.Columns, core.StarColumn{ diff --git a/internal/core/attribute.go b/internal/core/attribute.go index 7a0a58a4ac..9c2a927f5b 100644 --- a/internal/core/attribute.go +++ b/internal/core/attribute.go @@ -15,8 +15,6 @@ type AttributeSpec struct { NotNull bool HasDefault bool DeclType string - TypeLength int - TypeScale int AutoIncrement bool IsPrimaryKey bool IsUnique bool @@ -35,8 +33,6 @@ func (c *Catalog) CreateAttributeSpec(s AttributeSpec) error { HasDefault: boolToInt64(s.HasDefault), Num: int64(s.Num), DeclType: s.DeclType, - TypeLength: int64(s.TypeLength), - TypeScale: int64(s.TypeScale), AutoIncrement: boolToInt64(s.AutoIncrement), IsPrimaryKey: boolToInt64(s.IsPrimaryKey), IsUnique: boolToInt64(s.IsUnique), @@ -154,8 +150,6 @@ type ColumnInfo struct { TypeOID int64 NotNull bool DeclType string - TypeLength int - TypeScale int AutoIncrement bool IsPrimaryKey bool IsUnique bool @@ -179,8 +173,6 @@ func (c *Catalog) ResolveColumn(table, column string) (*ColumnInfo, error) { TypeOID: r.TypeOid, NotNull: r.NotNull != 0, DeclType: r.DeclType, - TypeLength: int(r.TypeLength), - TypeScale: int(r.TypeScale), AutoIncrement: r.AutoIncrement != 0, IsPrimaryKey: r.IsPrimaryKey != 0, IsUnique: r.IsUnique != 0, @@ -203,8 +195,6 @@ func (c *Catalog) TableColumns(table string) ([]ColumnInfo, error) { TypeOID: r.TypeOid, NotNull: r.NotNull != 0, DeclType: r.DeclType, - TypeLength: int(r.TypeLength), - TypeScale: int(r.TypeScale), AutoIncrement: r.AutoIncrement != 0, IsPrimaryKey: r.IsPrimaryKey != 0, IsUnique: r.IsUnique != 0, @@ -217,6 +207,10 @@ type ClassColumn struct { AttOID int64 Name string TypeOID int64 + // Type is the column's type as an expression, set for a column of a + // derived relation whose type the catalog holds no row for, or holds + // only the family of. + Type *TypeExpr NotNull bool Hidden bool } @@ -241,9 +235,9 @@ func (c *Catalog) ClassColumns(classOID int64) ([]ClassColumn, error) { } type CodegenColumn struct { - Name string - TypeName string - NotNull bool + Name string + TypeOID int64 + NotNull bool } func (c *Catalog) ClassCodegenColumns(classOID int64) ([]CodegenColumn, error) { @@ -254,9 +248,9 @@ func (c *Catalog) ClassCodegenColumns(classOID int64) ([]CodegenColumn, error) { out := make([]CodegenColumn, 0, len(rows)) for _, r := range rows { out = append(out, CodegenColumn{ - Name: r.ColumnName, - TypeName: r.TypeName, - NotNull: r.NotNull != 0, + Name: r.ColumnName, + TypeOID: r.TypeOid, + NotNull: r.NotNull != 0, }) } return out, nil @@ -268,8 +262,6 @@ type AttributeDetails struct { Column string Num int DeclType string - TypeLength int - TypeScale int AutoIncrement bool IsPrimaryKey bool IsUnique bool @@ -287,8 +279,6 @@ func (c *Catalog) LookupAttribute(attOID int64) (AttributeDetails, error) { Column: r.ColumnName, Num: int(r.Num), DeclType: r.DeclType, - TypeLength: int(r.TypeLength), - TypeScale: int(r.TypeScale), AutoIncrement: r.AutoIncrement != 0, IsPrimaryKey: r.IsPrimaryKey != 0, IsUnique: r.IsUnique != 0, diff --git a/internal/core/catalog.go b/internal/core/catalog.go index 68d05196e4..0f48dcb328 100644 --- a/internal/core/catalog.go +++ b/internal/core/catalog.go @@ -28,6 +28,9 @@ type Catalog struct { // once per extension name: a schema is free to say CREATE EXTENSION twice. loadExtension func(name string) error extensions map[string]bool + + // types remembers the rows and expressions looked up so far. + types typeCache } type Option func(*Catalog) error diff --git a/internal/core/catalogdb/models.go b/internal/core/catalogdb/models.go index 4c79ec8c1b..c3609307df 100644 --- a/internal/core/catalogdb/models.go +++ b/internal/core/catalogdb/models.go @@ -17,8 +17,6 @@ type SqlAttribute struct { HasDefault int64 Num int64 DeclType string - TypeLength int64 - TypeScale int64 AutoIncrement int64 IsPrimaryKey int64 IsUnique int64 @@ -104,9 +102,25 @@ type SqlType struct { NamespaceOid int64 DialectOid sql.NullInt64 Name string - Size int64 + Expr string Typtype string Category sql.NullString Preferred int64 + FamilyOid sql.NullInt64 ElementOid sql.NullInt64 + BaseOid sql.NullInt64 + CanonicalOid sql.NullInt64 + NotNull int64 +} + +type SqlTypeArg struct { + TypeOid int64 + Ord int64 + Label string + ArgTypeOid sql.NullInt64 + Nullable int64 + IntValue sql.NullInt64 + BoolValue sql.NullInt64 + StringValue sql.NullString + Ident sql.NullString } diff --git a/internal/core/catalogdb/query.sql.go b/internal/core/catalogdb/query.sql.go index b14851dce4..acdb93a2a2 100644 --- a/internal/core/catalogdb/query.sql.go +++ b/internal/core/catalogdb/query.sql.go @@ -86,9 +86,8 @@ const createAttribute = `-- name: CreateAttribute :exec INSERT INTO sql_attribute ( class_oid, name, type_oid, not_null, has_default, num, - decl_type, type_length, type_scale, - auto_increment, is_primary_key, is_unique, hidden -) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + decl_type, auto_increment, is_primary_key, is_unique, hidden +) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ` type CreateAttributeParams struct { @@ -99,8 +98,6 @@ type CreateAttributeParams struct { HasDefault int64 Num int64 DeclType string - TypeLength int64 - TypeScale int64 AutoIncrement int64 IsPrimaryKey int64 IsUnique int64 @@ -117,8 +114,6 @@ func (q *Queries) CreateAttribute(ctx context.Context, arg CreateAttributeParams arg.HasDefault, arg.Num, arg.DeclType, - arg.TypeLength, - arg.TypeScale, arg.AutoIncrement, arg.IsPrimaryKey, arg.IsUnique, @@ -330,32 +325,41 @@ func (q *Queries) CreateProcArg(ctx context.Context, arg CreateProcArgParams) er const createType = `-- name: CreateType :execlastid INSERT INTO sql_type - (name, size, typtype, category, preferred, namespace_oid, dialect_oid, element_oid) -VALUES (?, ?, ?, ?, ?, ?, ?, ?) + (name, expr, typtype, category, preferred, namespace_oid, dialect_oid, + family_oid, element_oid, base_oid, canonical_oid, not_null) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ` type CreateTypeParams struct { Name string - Size int64 + Expr string Typtype string Category sql.NullString Preferred int64 NamespaceOid int64 DialectOid sql.NullInt64 + FamilyOid sql.NullInt64 ElementOid sql.NullInt64 + BaseOid sql.NullInt64 + CanonicalOid sql.NullInt64 + NotNull int64 } // =============================== sql_type ============================== func (q *Queries) CreateType(ctx context.Context, arg CreateTypeParams) (int64, error) { result, err := q.db.ExecContext(ctx, createType, arg.Name, - arg.Size, + arg.Expr, arg.Typtype, arg.Category, arg.Preferred, arg.NamespaceOid, arg.DialectOid, + arg.FamilyOid, arg.ElementOid, + arg.BaseOid, + arg.CanonicalOid, + arg.NotNull, ) if err != nil { return 0, err @@ -363,6 +367,39 @@ func (q *Queries) CreateType(ctx context.Context, arg CreateTypeParams) (int64, return result.LastInsertId() } +const createTypeArg = `-- name: CreateTypeArg :exec +INSERT INTO sql_type_arg + (type_oid, ord, label, arg_type_oid, nullable, int_value, bool_value, string_value, ident) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) +` + +type CreateTypeArgParams struct { + TypeOid int64 + Ord int64 + Label string + ArgTypeOid sql.NullInt64 + Nullable int64 + IntValue sql.NullInt64 + BoolValue sql.NullInt64 + StringValue sql.NullString + Ident sql.NullString +} + +func (q *Queries) CreateTypeArg(ctx context.Context, arg CreateTypeArgParams) error { + _, err := q.db.ExecContext(ctx, createTypeArg, + arg.TypeOid, + arg.Ord, + arg.Label, + arg.ArgTypeOid, + arg.Nullable, + arg.IntValue, + arg.BoolValue, + arg.StringValue, + arg.Ident, + ) + return err +} + const deleteAttribute = `-- name: DeleteAttribute :exec DELETE FROM sql_attribute WHERE class_oid = ? AND name = ? ` @@ -603,7 +640,7 @@ func (q *Queries) FindProcsInNamespaces(ctx context.Context, arg FindProcsInName } const listClassColumns = `-- name: ListClassColumns :many -SELECT a.name AS column_name, t.name AS type_name, a.not_null +SELECT a.name AS column_name, a.type_oid, a.not_null FROM sql_attribute a JOIN sql_type t ON t.oid = a.type_oid WHERE a.class_oid = ? AND a.hidden = 0 @@ -612,7 +649,7 @@ ORDER BY a.num type ListClassColumnsRow struct { ColumnName string - TypeName string + TypeOid int64 NotNull int64 } @@ -625,7 +662,7 @@ func (q *Queries) ListClassColumns(ctx context.Context, classOid int64) ([]ListC var items []ListClassColumnsRow for rows.Next() { var i ListClassColumnsRow - if err := rows.Scan(&i.ColumnName, &i.TypeName, &i.NotNull); err != nil { + if err := rows.Scan(&i.ColumnName, &i.TypeOid, &i.NotNull); err != nil { return nil, err } items = append(items, i) @@ -702,8 +739,7 @@ func (q *Queries) ListTablesInNamespace(ctx context.Context, namespaceOid int64) const lookupAttribute = `-- name: LookupAttribute :one SELECT ns.name AS schema_name, cls.name AS table_name, a.name AS column_name, a.num, - a.decl_type, a.type_length, a.type_scale, - a.auto_increment, a.is_primary_key, a.is_unique, a.not_null + a.decl_type, a.auto_increment, a.is_primary_key, a.is_unique, a.not_null FROM sql_attribute a JOIN sql_class cls ON cls.oid = a.class_oid JOIN sql_namespace ns ON ns.oid = cls.namespace_oid @@ -716,8 +752,6 @@ type LookupAttributeRow struct { ColumnName string Num int64 DeclType string - TypeLength int64 - TypeScale int64 AutoIncrement int64 IsPrimaryKey int64 IsUnique int64 @@ -733,8 +767,6 @@ func (q *Queries) LookupAttribute(ctx context.Context, oid int64) (LookupAttribu &i.ColumnName, &i.Num, &i.DeclType, - &i.TypeLength, - &i.TypeScale, &i.AutoIncrement, &i.IsPrimaryKey, &i.IsUnique, @@ -744,17 +776,25 @@ func (q *Queries) LookupAttribute(ctx context.Context, oid int64) (LookupAttribu } const lookupType = `-- name: LookupType :one -SELECT oid, name, category, typtype, preferred +SELECT oid, namespace_oid, name, expr, category, typtype, preferred, + family_oid, element_oid, base_oid, canonical_oid, not_null FROM sql_type WHERE oid = ? ` type LookupTypeRow struct { - Oid int64 - Name string - Category sql.NullString - Typtype string - Preferred int64 + Oid int64 + NamespaceOid int64 + Name string + Expr string + Category sql.NullString + Typtype string + Preferred int64 + FamilyOid sql.NullInt64 + ElementOid sql.NullInt64 + BaseOid sql.NullInt64 + CanonicalOid sql.NullInt64 + NotNull int64 } func (q *Queries) LookupType(ctx context.Context, oid int64) (LookupTypeRow, error) { @@ -762,10 +802,17 @@ func (q *Queries) LookupType(ctx context.Context, oid int64) (LookupTypeRow, err var i LookupTypeRow err := row.Scan( &i.Oid, + &i.NamespaceOid, &i.Name, + &i.Expr, &i.Category, &i.Typtype, &i.Preferred, + &i.FamilyOid, + &i.ElementOid, + &i.BaseOid, + &i.CanonicalOid, + &i.NotNull, ) return i, err } @@ -853,8 +900,7 @@ func (q *Queries) RenameClass(ctx context.Context, arg RenameClassParams) error const resolveColumn = `-- name: ResolveColumn :one SELECT a.oid, a.class_oid, a.name, t.name AS type_name, a.type_oid, a.not_null, - a.decl_type, a.type_length, a.type_scale, - a.auto_increment, a.is_primary_key, a.is_unique + a.decl_type, a.auto_increment, a.is_primary_key, a.is_unique FROM sql_attribute a JOIN sql_class c ON c.oid = a.class_oid JOIN sql_type t ON t.oid = a.type_oid @@ -874,8 +920,6 @@ type ResolveColumnRow struct { TypeOid int64 NotNull int64 DeclType string - TypeLength int64 - TypeScale int64 AutoIncrement int64 IsPrimaryKey int64 IsUnique int64 @@ -892,8 +936,6 @@ func (q *Queries) ResolveColumn(ctx context.Context, arg ResolveColumnParams) (R &i.TypeOid, &i.NotNull, &i.DeclType, - &i.TypeLength, - &i.TypeScale, &i.AutoIncrement, &i.IsPrimaryKey, &i.IsUnique, @@ -1001,8 +1043,7 @@ func (q *Queries) SetDialectFlag(ctx context.Context, arg SetDialectFlagParams) const tableColumns = `-- name: TableColumns :many SELECT a.oid, a.class_oid, a.name, t.name AS type_name, a.type_oid, a.not_null, - a.decl_type, a.type_length, a.type_scale, - a.auto_increment, a.is_primary_key, a.is_unique + a.decl_type, a.auto_increment, a.is_primary_key, a.is_unique FROM sql_attribute a JOIN sql_class c ON c.oid = a.class_oid JOIN sql_type t ON t.oid = a.type_oid @@ -1018,8 +1059,6 @@ type TableColumnsRow struct { TypeOid int64 NotNull int64 DeclType string - TypeLength int64 - TypeScale int64 AutoIncrement int64 IsPrimaryKey int64 IsUnique int64 @@ -1042,8 +1081,6 @@ func (q *Queries) TableColumns(ctx context.Context, name string) ([]TableColumns &i.TypeOid, &i.NotNull, &i.DeclType, - &i.TypeLength, - &i.TypeScale, &i.AutoIncrement, &i.IsPrimaryKey, &i.IsUnique, @@ -1061,6 +1098,56 @@ func (q *Queries) TableColumns(ctx context.Context, name string) ([]TableColumns return items, nil } +const typeArgs = `-- name: TypeArgs :many +SELECT ord, label, arg_type_oid, nullable, int_value, bool_value, string_value, ident +FROM sql_type_arg +WHERE type_oid = ? +ORDER BY ord +` + +type TypeArgsRow struct { + Ord int64 + Label string + ArgTypeOid sql.NullInt64 + Nullable int64 + IntValue sql.NullInt64 + BoolValue sql.NullInt64 + StringValue sql.NullString + Ident sql.NullString +} + +func (q *Queries) TypeArgs(ctx context.Context, typeOid int64) ([]TypeArgsRow, error) { + rows, err := q.db.QueryContext(ctx, typeArgs, typeOid) + if err != nil { + return nil, err + } + defer rows.Close() + var items []TypeArgsRow + for rows.Next() { + var i TypeArgsRow + if err := rows.Scan( + &i.Ord, + &i.Label, + &i.ArgTypeOid, + &i.Nullable, + &i.IntValue, + &i.BoolValue, + &i.StringValue, + &i.Ident, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const typeNameByOID = `-- name: TypeNameByOID :one SELECT name FROM sql_type WHERE oid = ? ` @@ -1072,11 +1159,49 @@ func (q *Queries) TypeNameByOID(ctx context.Context, oid int64) (string, error) return name, err } +const typeOIDByExpr = `-- name: TypeOIDByExpr :one +SELECT t.oid +FROM sql_type t +JOIN sql_namespace ns ON ns.oid = t.namespace_oid +WHERE t.expr = ?1 +ORDER BY + CASE ns.name + WHEN 'pg_catalog' THEN 0 + WHEN 'public' THEN 1 + ELSE 2 + END, + ns.name +LIMIT 1 +` + +func (q *Queries) TypeOIDByExpr(ctx context.Context, expr string) (int64, error) { + row := q.db.QueryRowContext(ctx, typeOIDByExpr, expr) + var oid int64 + err := row.Scan(&oid) + return oid, err +} + +const typeOIDByExprInNamespace = `-- name: TypeOIDByExprInNamespace :one +SELECT oid FROM sql_type WHERE namespace_oid = ? AND expr = ? +` + +type TypeOIDByExprInNamespaceParams struct { + NamespaceOid int64 + Expr string +} + +func (q *Queries) TypeOIDByExprInNamespace(ctx context.Context, arg TypeOIDByExprInNamespaceParams) (int64, error) { + row := q.db.QueryRowContext(ctx, typeOIDByExprInNamespace, arg.NamespaceOid, arg.Expr) + var oid int64 + err := row.Scan(&oid) + return oid, err +} + const typeOIDByName = `-- name: TypeOIDByName :one SELECT t.oid FROM sql_type t JOIN sql_namespace ns ON ns.oid = t.namespace_oid -WHERE t.name = ?1 +WHERE t.name = ?1 AND t.family_oid IS NULL ORDER BY CASE ns.name WHEN 'pg_catalog' THEN 0 @@ -1087,6 +1212,8 @@ ORDER BY LIMIT 1 ` +// The family spelled name: an instance carries its family's name too, and +// is found by its expression instead. func (q *Queries) TypeOIDByName(ctx context.Context, name string) (int64, error) { row := q.db.QueryRowContext(ctx, typeOIDByName, name) var oid int64 @@ -1097,6 +1224,7 @@ func (q *Queries) TypeOIDByName(ctx context.Context, name string) (int64, error) const typeOIDsInCategory = `-- name: TypeOIDsInCategory :many SELECT oid FROM sql_type WHERE dialect_oid = ? AND category = ? + AND family_oid IS NULL AND canonical_oid IS NULL ORDER BY oid ` @@ -1105,6 +1233,8 @@ type TypeOIDsInCategoryParams struct { Category sql.NullString } +// The families of a category: an instance inherits its family's category +// and an alias stands for the row it points at, so neither is listed. func (q *Queries) TypeOIDsInCategory(ctx context.Context, arg TypeOIDsInCategoryParams) ([]int64, error) { rows, err := q.db.QueryContext(ctx, typeOIDsInCategory, arg.DialectOid, arg.Category) if err != nil { diff --git a/internal/core/catalogdef/query.sql b/internal/core/catalogdef/query.sql index eb9863470c..5182c3014f 100644 --- a/internal/core/catalogdef/query.sql +++ b/internal/core/catalogdef/query.sql @@ -37,14 +37,45 @@ SELECT value FROM sql_dialect_flag WHERE dialect_oid = ? AND key = ?; -- name: CreateType :execlastid INSERT INTO sql_type - (name, size, typtype, category, preferred, namespace_oid, dialect_oid, element_oid) -VALUES (?, ?, ?, ?, ?, ?, ?, ?); + (name, expr, typtype, category, preferred, namespace_oid, dialect_oid, + family_oid, element_oid, base_oid, canonical_oid, not_null) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + +-- name: CreateTypeArg :exec +INSERT INTO sql_type_arg + (type_oid, ord, label, arg_type_oid, nullable, int_value, bool_value, string_value, ident) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?); + +-- name: TypeArgs :many +SELECT ord, label, arg_type_oid, nullable, int_value, bool_value, string_value, ident +FROM sql_type_arg +WHERE type_oid = ? +ORDER BY ord; + +-- name: TypeOIDByExpr :one +SELECT t.oid +FROM sql_type t +JOIN sql_namespace ns ON ns.oid = t.namespace_oid +WHERE t.expr = sqlc.arg(expr) +ORDER BY + CASE ns.name + WHEN 'pg_catalog' THEN 0 + WHEN 'public' THEN 1 + ELSE 2 + END, + ns.name +LIMIT 1; + +-- name: TypeOIDByExprInNamespace :one +SELECT oid FROM sql_type WHERE namespace_oid = ? AND expr = ?; -- name: TypeOIDByName :one +-- The family spelled name: an instance carries its family's name too, and +-- is found by its expression instead. SELECT t.oid FROM sql_type t JOIN sql_namespace ns ON ns.oid = t.namespace_oid -WHERE t.name = sqlc.arg(name) +WHERE t.name = sqlc.arg(name) AND t.family_oid IS NULL ORDER BY CASE ns.name WHEN 'pg_catalog' THEN 0 @@ -58,12 +89,16 @@ LIMIT 1; SELECT name FROM sql_type WHERE oid = ?; -- name: TypeOIDsInCategory :many +-- The families of a category: an instance inherits its family's category +-- and an alias stands for the row it points at, so neither is listed. SELECT oid FROM sql_type WHERE dialect_oid = ? AND category = ? + AND family_oid IS NULL AND canonical_oid IS NULL ORDER BY oid; -- name: LookupType :one -SELECT oid, name, category, typtype, preferred +SELECT oid, namespace_oid, name, expr, category, typtype, preferred, + family_oid, element_oid, base_oid, canonical_oid, not_null FROM sql_type WHERE oid = ?; @@ -94,9 +129,8 @@ UPDATE sql_class SET name = sqlc.arg(new_name) WHERE oid = sqlc.arg(oid); -- name: CreateAttribute :exec INSERT INTO sql_attribute ( class_oid, name, type_oid, not_null, has_default, num, - decl_type, type_length, type_scale, - auto_increment, is_primary_key, is_unique, hidden -) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + decl_type, auto_increment, is_primary_key, is_unique, hidden +) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); -- name: SetAttributePrimaryKey :exec UPDATE sql_attribute SET is_primary_key = 1, not_null = 1 @@ -129,8 +163,7 @@ SELECT CAST(COALESCE(MAX(num), 0) AS INTEGER) AS num FROM sql_attribute WHERE cl -- name: ResolveColumn :one SELECT a.oid, a.class_oid, a.name, t.name AS type_name, a.type_oid, a.not_null, - a.decl_type, a.type_length, a.type_scale, - a.auto_increment, a.is_primary_key, a.is_unique + a.decl_type, a.auto_increment, a.is_primary_key, a.is_unique FROM sql_attribute a JOIN sql_class c ON c.oid = a.class_oid JOIN sql_type t ON t.oid = a.type_oid @@ -138,8 +171,7 @@ WHERE c.name = sqlc.arg(table_name) AND a.name = sqlc.arg(column_name); -- name: TableColumns :many SELECT a.oid, a.class_oid, a.name, t.name AS type_name, a.type_oid, a.not_null, - a.decl_type, a.type_length, a.type_scale, - a.auto_increment, a.is_primary_key, a.is_unique + a.decl_type, a.auto_increment, a.is_primary_key, a.is_unique FROM sql_attribute a JOIN sql_class c ON c.oid = a.class_oid JOIN sql_type t ON t.oid = a.type_oid @@ -153,7 +185,7 @@ WHERE class_oid = ? ORDER BY num; -- name: ListClassColumns :many -SELECT a.name AS column_name, t.name AS type_name, a.not_null +SELECT a.name AS column_name, a.type_oid, a.not_null FROM sql_attribute a JOIN sql_type t ON t.oid = a.type_oid WHERE a.class_oid = ? AND a.hidden = 0 @@ -161,8 +193,7 @@ ORDER BY a.num; -- name: LookupAttribute :one SELECT ns.name AS schema_name, cls.name AS table_name, a.name AS column_name, a.num, - a.decl_type, a.type_length, a.type_scale, - a.auto_increment, a.is_primary_key, a.is_unique, a.not_null + a.decl_type, a.auto_increment, a.is_primary_key, a.is_unique, a.not_null FROM sql_attribute a JOIN sql_class cls ON cls.oid = a.class_oid JOIN sql_namespace ns ON ns.oid = cls.namespace_oid diff --git a/internal/core/catalogdef/schema.sql b/internal/core/catalogdef/schema.sql index 1e48501d58..e086c6cc1c 100644 --- a/internal/core/catalogdef/schema.sql +++ b/internal/core/catalogdef/schema.sql @@ -20,27 +20,67 @@ CREATE TABLE sql_dialect_flag ( PRIMARY KEY (dialect_oid, key) ); --- sql_type: data types. Modeled on pg_type. --- typtype: 'b'ase | 'c'omposite | 'd'omain | 'e'num | 'p'seudo | 'r'ange --- category: 'N'umeric | 'S'tring | 'B'oolean | 'D'atetime | 'A'rray | --- 'C'omposite | 'E'num | 'U'serdef | 'X'unknown --- preferred: tie-breaker for implicit cast resolution within a category --- element_oid: for arrays, points at the element type --- dialect_oid: NULL = standard / shared across dialects +-- sql_type: data types, one row per type expression. Modeled on pg_type, +-- with the arguments PostgreSQL keeps as a typmod on the use site held in +-- the row instead. +-- +-- A row is a family — a name the dialect or the schema declares: numeric, +-- array, mood — or an instance, a family applied to arguments: numeric(10, 2), +-- array(integer). expr is the canonical spelling of the whole expression and +-- the row's identity; name is the family's name, on an instance too, so a +-- lookup by name finds the family and an instance points at it. +-- +-- typtype: 'b'ase | 'c'omposite | 'd'omain | 'e'num | 'p'seudo | 'r'ange +-- category: 'N'umeric | 'S'tring | 'B'oolean | 'D'atetime | 'A'rray | +-- 'C'omposite | 'E'num | 'U'serdef | 'X'unknown +-- preferred: tie-breaker for implicit cast resolution within a category +-- family_oid: NULL on a family; the family on an instance +-- element_oid: what the type holds: an array's element, a map's value, +-- a range's subtype +-- base_oid: what the type stands on: a domain's or alias type's base, +-- a wrapper's inner type, a SQLite spelling's affinity +-- canonical_oid: the row the engine reports this one as: an alias spelling +-- points at the type it names +-- not_null: a domain or alias type declared NOT NULL +-- dialect_oid: NULL = standard / shared across dialects CREATE TABLE sql_type ( oid INTEGER PRIMARY KEY AUTOINCREMENT, namespace_oid INTEGER NOT NULL REFERENCES sql_namespace(oid), dialect_oid INTEGER REFERENCES sql_dialect(oid), name TEXT NOT NULL, - size INTEGER NOT NULL DEFAULT 0, + expr TEXT NOT NULL, typtype TEXT NOT NULL DEFAULT 'b', category TEXT, preferred INTEGER NOT NULL DEFAULT 0, + family_oid INTEGER REFERENCES sql_type(oid), element_oid INTEGER REFERENCES sql_type(oid), - UNIQUE (namespace_oid, name) + base_oid INTEGER REFERENCES sql_type(oid), + canonical_oid INTEGER REFERENCES sql_type(oid), + not_null INTEGER NOT NULL DEFAULT 0, + UNIQUE (namespace_oid, expr) ); CREATE INDEX idx_sql_type_name ON sql_type(name); +-- sql_type_arg: the arguments of an instance, or the fields, labels or +-- members of a declared composite, enum or set, in order. Exactly one of +-- arg_type_oid, int_value, bool_value, string_value and ident is set. +-- label: a struct field, tuple element or enum label +-- nullable: the argument type is nullable at this position, as the inner +-- type of Array(Nullable(String)) is +-- ident: a bare word that is not a type: max, sum, day to second +CREATE TABLE sql_type_arg ( + type_oid INTEGER NOT NULL REFERENCES sql_type(oid), + ord INTEGER NOT NULL, + label TEXT NOT NULL DEFAULT '', + arg_type_oid INTEGER REFERENCES sql_type(oid), + nullable INTEGER NOT NULL DEFAULT 0, + int_value INTEGER, + bool_value INTEGER, + string_value TEXT, + ident TEXT, + PRIMARY KEY (type_oid, ord) +); + -- sql_class: relations (tables, views, indexes). -- kind: 'r' = table, 'v' = view, 'i' = index, 'c' = composite type, 'f' = foreign CREATE TABLE sql_class ( @@ -52,13 +92,9 @@ CREATE TABLE sql_class ( ); -- sql_attribute: columns of a relation. --- decl_type: original declared type string before normalization --- (e.g. VARCHAR(10), BIGINT UNSIGNED, INTEGER PRIMARY KEY). --- Useful for SQLite where multiple syntaxes collapse to --- one of five affinities, and as a debugging aid. --- type_length: length / precision (varchar(N), numeric(p,s).p, --- char(N), bit(N)). 0 = unspecified. --- type_scale: scale for numeric/decimal. 0 = unspecified. +-- decl_type: the type as the schema spelled it, before +-- canonicalization (VARCHAR(10), BIGINT UNSIGNED), which +-- is what a formatter prints back and what SQLite reports. -- auto_increment: rowid alias (sqlite INTEGER PRIMARY KEY), AUTOINCREMENT, -- pg serial/bigserial/identity, mysql AUTO_INCREMENT. -- is_primary_key: this column participates in the relation's primary key. @@ -77,8 +113,6 @@ CREATE TABLE sql_attribute ( has_default INTEGER NOT NULL DEFAULT 0, num INTEGER NOT NULL, -- ordinal position (1-based) decl_type TEXT NOT NULL DEFAULT '', - type_length INTEGER NOT NULL DEFAULT 0, - type_scale INTEGER NOT NULL DEFAULT 0, auto_increment INTEGER NOT NULL DEFAULT 0, is_primary_key INTEGER NOT NULL DEFAULT 0, is_unique INTEGER NOT NULL DEFAULT 0, diff --git a/internal/core/schema/schema.go b/internal/core/schema/schema.go index 25d864c13a..f4005f9c50 100644 --- a/internal/core/schema/schema.go +++ b/internal/core/schema/schema.go @@ -2,7 +2,6 @@ package schema import ( "fmt" - "strings" "github.com/sqlc-dev/sqlc/internal/core" "github.com/sqlc-dev/sqlc/internal/core/analyzer" @@ -264,7 +263,7 @@ func applyAlterTable(cat *core.Catalog, stmt *ast.AlterTableStmt) error { if err != nil { return err } - if err := cat.SetAttributeType(classOID, name, typeOID, cmd.Def.TypeName.Name); err != nil { + if err := cat.SetAttributeType(classOID, name, typeOID, declType(cmd.Def.TypeName)); err != nil { return err } // An engine that reports a column's whole new definition also @@ -355,14 +354,14 @@ func applyCreateEnum(cat *core.Catalog, stmt *ast.CreateEnumStmt) error { if stmt.TypeName == nil { return fmt.Errorf("create type with nil name") } - name := core.TypeNameString(stmt.TypeName) - if name == "" { + t := core.TypeExprOfTypeName(stmt.TypeName) + if t == nil { return fmt.Errorf("create type with empty name") } - if _, err := cat.TypeOID(name); err == nil { + if _, err := cat.TypeOID(t.Name); err == nil { return nil } - _, err := cat.CreateUserType(name, "E") + _, err := cat.CreateUserType(t.Name, "E") return err } @@ -422,17 +421,13 @@ func resolveOrCreateNamespace(cat *core.Catalog, schema string) (int64, error) { return cat.CreateNamespace(name) } -// columnTypeOID resolves a column's type. Engines report an array column -// either on the type name or on the column itself. +// columnTypeOID interns a column's type and returns its row. func columnTypeOID(cat *core.Catalog, col *ast.ColumnDef) (int64, error) { - name := core.TypeNameString(col.TypeName) - if name == "" { + t := core.ColumnTypeExpr(col) + if t == nil { return 0, fmt.Errorf("missing type name") } - if (col.IsArray || col.ArrayDims > 0) && !strings.HasSuffix(name, core.ArraySuffix) { - name += core.ArraySuffix - } - return cat.ResolveTypeName(name) + return cat.ResolveTypeExpr(t) } // declType is the type as the schema spelled it: an engine that folds or diff --git a/internal/core/seed/extension.go b/internal/core/seed/extension.go index ac7eacdde8..c0f3689535 100644 --- a/internal/core/seed/extension.go +++ b/internal/core/seed/extension.go @@ -130,13 +130,5 @@ func (e *extension) funcType(name string) (int64, error) { if name == "" { return 0, nil } - if oid, err := e.cat.TypeOID(name); err == nil { - return oid, nil - } - return e.cat.CreateTypeSpec(core.TypeSpec{ - Name: name, - Typtype: "b", - Category: "U", - DialectOID: e.cat.SeededDialectOID(), - }) + return e.cat.ResolvePseudoTypeExpr(core.ParseTypeExpr(name)) } diff --git a/internal/core/seed/seed.go b/internal/core/seed/seed.go index e5819963ca..5339732dac 100644 --- a/internal/core/seed/seed.go +++ b/internal/core/seed/seed.go @@ -105,13 +105,22 @@ type Settings struct { // enable_fts5 compile option adds. Modules map[string]string `json:"modules,omitempty"` + // Alias says what an alias in types.jsonl is. "canonical", the default, + // makes it another spelling of the type, which a column declared with + // it is reported as, the way PostgreSQL reports int as integer. "base" + // makes it a type of its own that stands on the one it aliases, which + // is how SQLite keeps a column's declared spelling while comparing it + // by its affinity. + Alias string `json:"alias,omitempty"` + // fsys is the dialect directory the settings were read from. fsys fs.FS } -// Type is a type the dialect defines. Aliases are spellings of the same type -// that a schema may use in a column definition; each becomes its own catalog -// type, with implicit casts registered between them. +// Type is a type family the dialect defines. Aliases are other spellings of +// it that a schema may use in a column definition; each becomes a row that +// points at the type, as an alias of it or as a type standing on it, +// depending on the dialect's Alias setting. type Type struct { Name string `json:"name"` Category string `json:"category"` @@ -435,8 +444,9 @@ type builder struct { settings Settings dialectOID int64 - // oids maps a lowercased type name to its OID, and categories records the - // category each was seeded under, in the order they were read. + // oids maps a lowercased type spelling to the row a seed record naming + // it means, and categories records the category each family was seeded + // under, in the order they were read. oids map[string]int64 categories []categorized @@ -455,27 +465,42 @@ type categorized struct { } func (b *builder) addType(t Type) error { - for _, name := range append([]string{t.Name}, t.Aliases...) { - if _, err := b.createType(name, t.Category); err != nil { - return fmt.Errorf("type %q: %w", name, err) + oid, err := b.createType(t.Name, t.Category) + if err != nil { + return fmt.Errorf("type %q: %w", t.Name, err) + } + for _, alias := range t.Aliases { + if err := b.addAlias(alias, oid, t.Category); err != nil { + return fmt.Errorf("type %q: alias %q: %w", t.Name, alias, err) } } - return b.aliasCasts(t) + return nil } -// aliasCasts makes every spelling of a type implicitly castable to every other, -// so that a column declared "integer" and one declared "int4" compare. -func (b *builder) aliasCasts(t Type) error { - names := append([]string{t.Name}, t.Aliases...) - for _, src := range names { - for _, tgt := range names { - if src == tgt { - continue - } - if err := b.addCast(Cast{Source: src, Target: tgt, Context: "i"}); err != nil { - return err - } - } +// addAlias registers another spelling of a type: a row that points at the +// type as its canonical form, or — for a dialect whose aliases are types of +// their own — as its base. +func (b *builder) addAlias(name string, typeOID int64, category string) error { + key := strings.ToLower(name) + if _, ok := b.oids[key]; ok { + return nil + } + spec := core.TypeSpec{Name: key, Typtype: "b", Category: category, DialectOID: b.dialectOID} + if b.settings.Alias == "base" { + spec.BaseOID = typeOID + } else { + spec.CanonicalOID = typeOID + } + oid, err := b.cat.CreateTypeSpec(spec) + if err != nil { + return err + } + // A record naming the alias means the type it stands for, unless the + // alias is a type of its own. + if b.settings.Alias == "base" { + b.oids[key] = oid + } else { + b.oids[key] = typeOID } return nil } @@ -727,22 +752,21 @@ func (b *builder) addRelation(rel Relation) error { return err } for i, col := range rel.Columns { - name := col.Type + t := core.ParseTypeExpr(col.Type) if col.Array { - name += core.ArraySuffix + t = core.Array(t) } - typeOID, err := b.columnType(name) + typeOID, err := b.columnType(t) if err != nil { return fmt.Errorf("relation %q column %q: %w", rel.Name, col.Name, err) } if err := b.cat.CreateAttributeSpec(core.AttributeSpec{ - ClassOID: classOID, - Name: col.Name, - TypeOID: typeOID, - Num: i + 1, - NotNull: col.NotNull, - DeclType: col.Type, - TypeLength: col.Length, + ClassOID: classOID, + Name: col.Name, + TypeOID: typeOID, + Num: i + 1, + NotNull: col.NotNull, + DeclType: col.Type, }); err != nil { return fmt.Errorf("relation %q column %q: %w", rel.Name, col.Name, err) } @@ -769,14 +793,14 @@ func (b *builder) namespace(schema string) (int64, error) { return oid, nil } -// columnType resolves a column's type, which unlike a function signature may -// name an array. -func (b *builder) columnType(name string) (int64, error) { - key := strings.ToLower(name) +// columnType resolves a column's type, which may be an array or carry +// arguments. +func (b *builder) columnType(t *core.TypeExpr) (int64, error) { + key := t.Key() if oid, ok := b.oids[key]; ok { return oid, nil } - oid, err := b.cat.ResolveTypeName(key) + oid, err := b.cat.ResolveTypeExpr(t) if err != nil { return 0, err } @@ -791,5 +815,14 @@ func (b *builder) funcType(name string) (int64, error) { if name == "" { return 0, nil } - return b.createType(name, "U") + key := strings.ToLower(name) + if oid, ok := b.oids[key]; ok { + return oid, nil + } + oid, err := b.cat.ResolvePseudoTypeExpr(core.ParseTypeExpr(name)) + if err != nil { + return 0, err + } + b.oids[key] = oid + return oid, nil } diff --git a/internal/core/typeexpr.go b/internal/core/typeexpr.go index bebb7e83ce..f3cf97e0ed 100644 --- a/internal/core/typeexpr.go +++ b/internal/core/typeexpr.go @@ -18,14 +18,98 @@ type TypeExpr struct { Args []TypeArg `json:"args,omitempty"` } -// TypeArg is one argument of a TypeExpr: exactly one of Type, Int, Bool or -// String is set. +// TypeArg is one argument of a TypeExpr: exactly one of Type, Int, Bool, +// String or Ident is set. Ident is a bare word that is not a type — the max +// of nvarchar(max), the function of SimpleAggregateFunction(sum, UInt64), +// the fields of interval day to second. type TypeArg struct { Label string `json:"label,omitempty"` Type *TypeExpr `json:"type,omitempty"` Int *int64 `json:"int,omitempty"` Bool *bool `json:"bool,omitempty"` String *string `json:"string,omitempty"` + Ident *string `json:"ident,omitempty"` +} + +// ArrayTypeName is the family every dialect's array is an instance of: an +// array of integers is array(integer), whatever the dialect spells it. +const ArrayTypeName = "array" + +// Array wraps element in one array dimension. +func Array(element *TypeExpr) *TypeExpr { + return &TypeExpr{Name: ArrayTypeName, Args: []TypeArg{{Type: element}}} +} + +// IsArray reports whether the expression is an array. +func (t *TypeExpr) IsArray() bool { + return t != nil && t.Name == ArrayTypeName && len(t.Args) > 0 && t.Args[0].Type != nil +} + +// Element is an array's element type, or nil for anything else. +func (t *TypeExpr) Element() *TypeExpr { + if !t.IsArray() { + return nil + } + return t.Args[0].Type +} + +// ArrayDims counts the array dimensions wrapped around the expression's +// innermost type, and Innermost is that type: the integer of an array of +// arrays of integers. +func (t *TypeExpr) ArrayDims() int { + dims := 0 + for t.IsArray() { + dims++ + t = t.Element() + } + return dims +} + +func (t *TypeExpr) Innermost() *TypeExpr { + for t.IsArray() { + t = t.Element() + } + return t +} + +// Clone copies the expression, arguments and all, so that a caller can set +// nullability on the copy without touching a cached one. +func (t *TypeExpr) Clone() *TypeExpr { + if t == nil { + return nil + } + out := &TypeExpr{Name: t.Name, Nullable: t.Nullable} + if len(t.Args) > 0 { + out.Args = make([]TypeArg, len(t.Args)) + for i, a := range t.Args { + out.Args[i] = a + out.Args[i].Type = a.Type.Clone() + } + } + return out +} + +// WithNullable returns a copy of the expression with its own nullability set +// as given; the nullability of a nested type is left alone. +func (t *TypeExpr) WithNullable(nullable bool) *TypeExpr { + out := t.Clone() + if out != nil { + out.Nullable = nullable + } + return out +} + +// Key is the expression's canonical spelling, which identifies its row in +// the catalog: the expression's own nullability is not part of it, since a +// row is never nullable, while the nullability of a nested type is. +func (t *TypeExpr) Key() string { + if t == nil { + return "" + } + if !t.Nullable { + return t.String() + } + return t.WithNullable(false).String() } // ParseTypeExpr reads a type spelled the way every dialect spells one, as a @@ -36,7 +120,7 @@ type TypeArg struct { func ParseTypeExpr(s string) *TypeExpr { s = strings.TrimSpace(s) if element, ok := strings.CutSuffix(s, ArraySuffix); ok { - return &TypeExpr{Name: "array", Args: []TypeArg{{Type: ParseTypeExpr(element)}}} + return Array(ParseTypeExpr(element)) } name, args := splitTypeArgs(s) name = strings.ToLower(name) @@ -187,6 +271,8 @@ func (t *TypeExpr) String() string { b.WriteString(strconv.FormatBool(*a.Bool)) case a.String != nil: b.WriteString("'" + strings.ReplaceAll(*a.String, "'", `\'`) + "'") + case a.Ident != nil: + b.WriteString(*a.Ident) } } b.WriteByte(')') diff --git a/internal/core/typename.go b/internal/core/typename.go index c0a9f6d680..516c4c1ff5 100644 --- a/internal/core/typename.go +++ b/internal/core/typename.go @@ -7,12 +7,19 @@ import ( "github.com/sqlc-dev/sqlc/internal/sql/ast" ) -// TypeNameString is the catalog's name for the type an AST node names: the type -// as it was written, lowercased, with "[]" appended for an array. Engines -// report a type either as a plain name or as a list of qualifying parts. -func TypeNameString(tn *ast.TypeName) string { +// TypeExprOfTypeName reads the type an AST node names into an expression. +// An engine that folds the whole type into a spelling — ClickHouse's +// Array(Nullable(String)), SQLite's VARYING CHARACTER(10) — hands it over +// in Spelling and the spelling is read as written. Otherwise the name comes +// from Name or the qualifying parts of Names, the type modifiers become +// integer or string arguments, and each array bound wraps the result in an +// array. +func TypeExprOfTypeName(tn *ast.TypeName) *TypeExpr { if tn == nil { - return "" + return nil + } + if tn.Spelling != "" { + return ParseTypeExpr(tn.Spelling) } name := strings.TrimSpace(tn.Name) if name == "" && tn.Names != nil { @@ -27,33 +34,87 @@ func TypeNameString(tn *ast.TypeName) string { name = strings.Join(parts, ".") } name = strings.ToLower(name) - if name != "" && tn.ArrayBounds != nil && len(tn.ArrayBounds.Items) > 0 { - name += ArraySuffix + if name == "" { + return nil + } + // A name an engine spelled with its own arguments or array suffix reads + // the same way a spelling does. + t := ParseTypeExpr(name) + for _, item := range listItems(tn.Typmods) { + if arg, ok := typmodArg(item); ok { + t.Args = append(t.Args, arg) + } + } + for range listItems(tn.ArrayBounds) { + t = Array(t) } - return name + return t } -// ResolveType returns the type an AST node names, registering it when the -// dialect's seed did not: a schema is free to declare types of its own. -func (c *Catalog) ResolveType(tn *ast.TypeName) (int64, error) { - name := TypeNameString(tn) - if name == "" { - return 0, fmt.Errorf("missing type name") +// ColumnTypeExpr reads a column definition's type. Engines report an array +// column either on the type name or on the column itself. +func ColumnTypeExpr(col *ast.ColumnDef) *TypeExpr { + if col == nil { + return nil + } + t := TypeExprOfTypeName(col.TypeName) + if t == nil { + return nil + } + if col.TypeName.Spelling != "" || listItems(col.TypeName.ArrayBounds) != nil { + return t + } + dims := col.ArrayDims + if dims == 0 && col.IsArray { + dims = 1 } - return c.ResolveTypeName(name) + for i := 0; i < dims; i++ { + t = Array(t) + } + return t } -// ResolveTypeName is ResolveType for a type already reduced to its name. -func (c *Catalog) ResolveTypeName(name string) (int64, error) { - if oid, err := c.TypeOID(name); err == nil { - return oid, nil - } - if element, ok := strings.CutSuffix(name, ArraySuffix); ok { - elementOID, err := c.ResolveTypeName(element) - if err != nil { - return 0, err +// typmodArg reads one type modifier as an argument: an integer, a string, or +// a bare word, which is an identifier such as the max of nvarchar(max). +func typmodArg(n ast.Node) (TypeArg, bool) { + switch v := n.(type) { + case *ast.A_Const: + return typmodArg(v.Val) + case *ast.Integer: + i := v.Ival + return TypeArg{Int: &i}, true + case *ast.String: + s := v.Str + return TypeArg{String: &s}, true + case *ast.ColumnRef: + parts := make([]string, 0, len(listItems(v.Fields))) + for _, item := range listItems(v.Fields) { + if s, ok := item.(*ast.String); ok { + parts = append(parts, s.Str) + } + } + if len(parts) == 0 { + return TypeArg{}, false } - return c.CreateArrayType(name, elementOID) + ident := strings.ToLower(strings.Join(parts, ".")) + return TypeArg{Ident: &ident}, true + } + return TypeArg{}, false +} + +func listItems(l *ast.List) []ast.Node { + if l == nil { + return nil + } + return l.Items +} + +// ResolveType interns the type an AST node names, registering it when the +// dialect's seed did not: a schema is free to declare types of its own. +func (c *Catalog) ResolveType(tn *ast.TypeName) (int64, error) { + t := TypeExprOfTypeName(tn) + if t == nil { + return 0, fmt.Errorf("missing type name") } - return c.CreateUserType(name, "U") + return c.ResolveTypeExpr(t) } diff --git a/internal/core/types.go b/internal/core/types.go index d97abd7f27..0dfa485de4 100644 --- a/internal/core/types.go +++ b/internal/core/types.go @@ -3,27 +3,96 @@ package core import ( "context" "database/sql" + "errors" "fmt" "strings" + "sync" "github.com/sqlc-dev/sqlc/internal/core/catalogdb" ) +// TypeSpec describes a type row. Name is the family's name; Expr is the +// canonical spelling of the whole expression and defaults to the name, which +// is what a family's is. type TypeSpec struct { Name string - Size int + Expr string Typtype string Category string Preferred bool NamespaceOID int64 DialectOID int64 + FamilyOID int64 ElementOID int64 + BaseOID int64 + CanonicalOID int64 + NotNull bool } -func (c *Catalog) CreateType(name string, size int) (int64, error) { - return c.CreateTypeSpec(TypeSpec{Name: name, Size: size, Typtype: "b"}) +// TypeInfo is a type row as the catalog holds it. +type TypeInfo struct { + OID int64 + NamespaceOID int64 + Name string + Expr string + Category string + Typtype string + Preferred bool + FamilyOID int64 + ElementOID int64 + BaseOID int64 + CanonicalOID int64 + NotNull bool } +// IsFamily reports whether the row is a family rather than an instance. +func (t TypeInfo) IsFamily() bool { return t.FamilyOID == 0 } + +// typeCache remembers what the catalog holds about a type. A row never +// changes once written, and a restored catalog is read-only, so a cached +// answer is good for the life of the catalog. Analysis runs concurrently on +// a restored catalog, so the cache is locked. +type typeCache struct { + mu sync.RWMutex + infos map[int64]TypeInfo + exprs map[int64]*TypeExpr +} + +func (c *typeCache) info(oid int64) (TypeInfo, bool) { + c.mu.RLock() + defer c.mu.RUnlock() + info, ok := c.infos[oid] + return info, ok +} + +func (c *typeCache) expr(oid int64) (*TypeExpr, bool) { + c.mu.RLock() + defer c.mu.RUnlock() + e, ok := c.exprs[oid] + return e, ok +} + +func (c *typeCache) put(info TypeInfo, expr *TypeExpr) { + c.mu.Lock() + defer c.mu.Unlock() + if c.infos == nil { + c.infos = map[int64]TypeInfo{} + c.exprs = map[int64]*TypeExpr{} + } + c.infos[info.OID] = info + if expr != nil { + c.exprs[info.OID] = expr + } +} + +func (c *Catalog) CreateType(name string) (int64, error) { + return c.CreateTypeSpec(TypeSpec{Name: name, Typtype: "b"}) +} + +// CreateTypeSpec inserts a type row. It is the raw insert: nothing is +// canonicalized and no arguments are written, so it is what the seed and +// ResolveTypeExpr build on rather than what a caller with an expression +// wants. func (c *Catalog) CreateTypeSpec(t TypeSpec) (int64, error) { if t.Typtype == "" { t.Typtype = "b" @@ -35,29 +104,39 @@ func (c *Catalog) CreateTypeSpec(t TypeSpec) (int64, error) { } t.NamespaceOID = oid } + name := strings.ToLower(t.Name) + expr := t.Expr + if expr == "" { + expr = name + } oid, err := c.q.CreateType(context.Background(), catalogdb.CreateTypeParams{ - Name: strings.ToLower(t.Name), - Size: int64(t.Size), + Name: name, + Expr: expr, Typtype: t.Typtype, Category: nullString(t.Category), Preferred: boolToInt64(t.Preferred), NamespaceOid: t.NamespaceOID, DialectOid: nullInt64(t.DialectOID), + FamilyOid: nullInt64(t.FamilyOID), ElementOid: nullInt64(t.ElementOID), + BaseOid: nullInt64(t.BaseOID), + CanonicalOid: nullInt64(t.CanonicalOID), + NotNull: boolToInt64(t.NotNull), }) if err != nil { - return 0, fmt.Errorf("create type %q: %w", t.Name, err) + return 0, fmt.Errorf("create type %q: %w", expr, err) } return oid, nil } -// ArraySuffix marks the type of an array of the type it is appended to. The -// catalog names an array type after its element, the way a schema spells it. +// ArraySuffix is the suffix a schema appends to an element type's spelling +// to name an array of it, which ParseTypeExpr reads as one array dimension. const ArraySuffix = "[]" -// CreateUserType registers a type a schema declared rather than the dialect, -// such as an enum or a name the dialect's seed does not list. The type gains -// the dialect's comparison operators, so a column of it can be compared. +// CreateUserType registers a type family a schema declared rather than the +// dialect, such as an enum or a name the dialect's seed does not list. The +// type gains the dialect's comparison operators, so a column of it can be +// compared. func (c *Catalog) CreateUserType(name, category string) (int64, error) { typtype := "b" if category == "E" { @@ -78,24 +157,6 @@ func (c *Catalog) CreateUserType(name, category string) (int64, error) { return oid, nil } -// CreateArrayType registers the array type over elementOID. -func (c *Catalog) CreateArrayType(name string, elementOID int64) (int64, error) { - oid, err := c.CreateTypeSpec(TypeSpec{ - Name: name, - Typtype: "b", - Category: "A", - DialectOID: c.dialectOID, - ElementOID: elementOID, - }) - if err != nil { - return 0, err - } - if err := c.createComparisons(oid); err != nil { - return 0, err - } - return oid, nil -} - // createComparisons gives a type the dialect's comparison operators, which the // seed registered for the types it knew about up front. func (c *Catalog) createComparisons(typeOID int64) error { @@ -128,8 +189,8 @@ func (c *Catalog) createComparisons(typeOID int64) error { return nil } -// TypeOIDsInCategory returns the types the catalog's dialect has in the named -// category, in the order they were created. +// TypeOIDsInCategory returns the type families the catalog's dialect has in +// the named category, in the order they were created. func (c *Catalog) TypeOIDsInCategory(category string) ([]int64, error) { oids, err := c.q.TypeOIDsInCategory(context.Background(), catalogdb.TypeOIDsInCategoryParams{ DialectOid: nullInt64(c.dialectOID), @@ -141,42 +202,327 @@ func (c *Catalog) TypeOIDsInCategory(category string) ([]int64, error) { return oids, nil } +// TypeOID returns the family a name refers to: an alias spelling resolves to +// the type it names. func (c *Catalog) TypeOID(name string) (int64, error) { - oid, err := c.q.TypeOIDByName(context.Background(), strings.ToLower(name)) + oid, err := c.familyOIDByName(strings.ToLower(name)) if err != nil { return 0, fmt.Errorf("type %q: %w", name, err) } - return oid, nil + return c.canonicalOID(oid) } -func (c *Catalog) TypeName(oid int64) (string, error) { - name, err := c.q.TypeNameByOID(context.Background(), oid) +// familyOIDByName finds the family row spelled name, alias rows included. +func (c *Catalog) familyOIDByName(name string) (int64, error) { + return c.q.TypeOIDByName(context.Background(), name) +} + +// canonicalOID follows an alias row to the row it stands for. +func (c *Catalog) canonicalOID(oid int64) (int64, error) { + for i := 0; i < 16; i++ { + info, err := c.LookupType(oid) + if err != nil { + return 0, err + } + if info.CanonicalOID == 0 { + return oid, nil + } + oid = info.CanonicalOID + } + return 0, fmt.Errorf("type oid %d: alias chain does not end", oid) +} + +// ResolutionOID is the row an operator, function or cast over the type is +// looked up on when none is registered on the type itself: an instance's +// family, an alias's canonical type, a domain's or wrapper's base. It +// returns 0 when there is nothing further to fall back to. +func (c *Catalog) ResolutionOID(oid int64) int64 { + info, err := c.LookupType(oid) if err != nil { - return "", fmt.Errorf("type oid %d: %w", oid, err) + return 0 + } + switch { + case info.CanonicalOID != 0: + return info.CanonicalOID + case info.FamilyOID != 0: + return info.FamilyOID + case info.BaseOID != 0: + return info.BaseOID } - return name, nil + return 0 } -type TypeInfo struct { - OID int64 - Name string - Category string - Typtype string - Preferred bool +// ResolutionChain lists the type and every row resolution falls back to, in +// order, ending with the family everything about the type is registered on. +func (c *Catalog) ResolutionChain(oid int64) []int64 { + chain := []int64{oid} + for i := 0; i < 16 && oid != 0; i++ { + oid = c.ResolutionOID(oid) + if oid != 0 { + chain = append(chain, oid) + } + } + return chain +} + +// TypeName returns the family name of a type row. +func (c *Catalog) TypeName(oid int64) (string, error) { + info, err := c.LookupType(oid) + if err != nil { + return "", err + } + return info.Name, nil } +// LookupType returns what the catalog holds about a type row. func (c *Catalog) LookupType(oid int64) (TypeInfo, error) { + if info, ok := c.types.info(oid); ok { + return info, nil + } row, err := c.q.LookupType(context.Background(), oid) if err != nil { return TypeInfo{}, fmt.Errorf("lookup type oid %d: %w", oid, err) } - return TypeInfo{ - OID: row.Oid, - Name: row.Name, - Category: row.Category.String, - Typtype: row.Typtype, - Preferred: row.Preferred != 0, - }, nil + info := TypeInfo{ + OID: row.Oid, + NamespaceOID: row.NamespaceOid, + Name: row.Name, + Expr: row.Expr, + Category: row.Category.String, + Typtype: row.Typtype, + Preferred: row.Preferred != 0, + FamilyOID: orZero(row.FamilyOid), + ElementOID: orZero(row.ElementOid), + BaseOID: orZero(row.BaseOid), + CanonicalOID: orZero(row.CanonicalOid), + NotNull: row.NotNull != 0, + } + c.types.put(info, nil) + return info, nil +} + +// TypeExprOf is the expression a type row stands for, read back from its +// arguments: the family's name for a family, the family applied to its +// arguments for an instance. The result is the caller's to change. +func (c *Catalog) TypeExprOf(oid int64) (*TypeExpr, error) { + if e, ok := c.types.expr(oid); ok { + return e.Clone(), nil + } + info, err := c.LookupType(oid) + if err != nil { + return nil, err + } + expr := &TypeExpr{Name: info.Name} + if !info.IsFamily() { + rows, err := c.q.TypeArgs(context.Background(), oid) + if err != nil { + return nil, fmt.Errorf("type oid %d: arguments: %w", oid, err) + } + for _, r := range rows { + arg := TypeArg{Label: r.Label} + switch { + case r.ArgTypeOid.Valid: + t, err := c.TypeExprOf(r.ArgTypeOid.Int64) + if err != nil { + return nil, err + } + t.Nullable = r.Nullable != 0 + arg.Type = t + case r.IntValue.Valid: + v := r.IntValue.Int64 + arg.Int = &v + case r.BoolValue.Valid: + v := r.BoolValue.Int64 != 0 + arg.Bool = &v + case r.StringValue.Valid: + v := r.StringValue.String + arg.String = &v + case r.Ident.Valid: + v := r.Ident.String + arg.Ident = &v + } + expr.Args = append(expr.Args, arg) + } + } + c.types.put(info, expr) + return expr.Clone(), nil +} + +// ResolveTypeExpr interns the type an expression names and returns its row: +// the family for a bare name, the instance for a family applied to +// arguments, each argument type interned first. Names are canonicalized, so +// integer and int4 intern to one row. A family the dialect did not seed is +// one the schema declared, and is registered as a user type. +func (c *Catalog) ResolveTypeExpr(t *TypeExpr) (int64, error) { + oid, _, err := c.internType(t, func(name string) (int64, error) { + return c.CreateUserType(name, "U") + }) + return oid, err +} + +// ResolvePseudoTypeExpr is ResolveTypeExpr for the type a function signature +// names. Signatures reference pseudo-types ("any", "record") and types no +// dialect bothers to list, so an unknown family is registered as an opaque +// type rather than rejected, and gets no operators of its own. +func (c *Catalog) ResolvePseudoTypeExpr(t *TypeExpr) (int64, error) { + oid, _, err := c.internType(t, func(name string) (int64, error) { + return c.CreateTypeSpec(TypeSpec{Name: name, Category: "U", DialectOID: c.dialectOID}) + }) + return oid, err +} + +// ResolveTypeName is ResolveTypeExpr for a type spelled as a string. +func (c *Catalog) ResolveTypeName(name string) (int64, error) { + return c.ResolveTypeExpr(ParseTypeExpr(name)) +} + +var errUnknownType = errors.New("unknown type") + +// TypeLookup is what LookupTypeExpr found for an expression. +type TypeLookup struct { + // OID is the instance row when the catalog holds one, otherwise the + // family row. + OID int64 + // FamilyOID is the family, which is OID for a family or an instance the + // catalog does not hold. + FamilyOID int64 + // Expr is the expression canonicalized: the family and every argument + // type spelled as the catalog spells them, whether or not the instance + // is a row. + Expr *TypeExpr +} + +// LookupTypeExpr finds the row an expression names without writing, and +// reports false when the family is not one the catalog holds. +func (c *Catalog) LookupTypeExpr(t *TypeExpr) (TypeLookup, bool) { + refuse := func(string) (int64, error) { return 0, errUnknownType } + oid, canonical, err := c.internType(t, refuse) + if errors.Is(err, errUnknownType) && canonical != nil { + // The family is known and the instance is not a row. + familyOID, _, err := c.internType(&TypeExpr{Name: canonical.Name}, refuse) + if err != nil { + return TypeLookup{}, false + } + return TypeLookup{OID: familyOID, FamilyOID: familyOID, Expr: canonical}, true + } + if err != nil { + return TypeLookup{}, false + } + info, err := c.LookupType(oid) + if err != nil { + return TypeLookup{}, false + } + familyOID := oid + if !info.IsFamily() { + familyOID = info.FamilyOID + } + return TypeLookup{OID: oid, FamilyOID: familyOID, Expr: canonical}, true +} + +// internType resolves an expression to its row, creating the instance row +// when there is none and calling newFamily for a family name the catalog +// does not hold, which may refuse. Alongside the row it returns the +// expression canonicalized; when the instance is not a row and newFamily +// refuses, the canonical expression still comes back with the error. +func (c *Catalog) internType(t *TypeExpr, newFamily func(name string) (int64, error)) (int64, *TypeExpr, error) { + if t == nil || strings.TrimSpace(t.Name) == "" { + return 0, nil, fmt.Errorf("missing type name") + } + name := strings.ToLower(strings.TrimSpace(t.Name)) + familyOID, err := c.familyOIDByName(name) + if err != nil { + if name == ArrayTypeName { + // Every dialect has arrays, whether or not its seed lists the + // family; one that does not gets it as an array type rather + // than a user type. + familyOID, err = c.CreateTypeSpec(TypeSpec{Name: name, Category: "A", DialectOID: c.dialectOID}) + } else { + familyOID, err = newFamily(name) + } + if err != nil { + return 0, nil, fmt.Errorf("type %q: %w", name, err) + } + } + if familyOID, err = c.canonicalOID(familyOID); err != nil { + return 0, nil, err + } + family, err := c.LookupType(familyOID) + if err != nil { + return 0, nil, err + } + if len(t.Args) == 0 { + return familyOID, &TypeExpr{Name: family.Name}, nil + } + + // The instance's canonical spelling is the family applied to its + // arguments as the catalog spells them, so each argument type is + // resolved first and read back. + canonical := &TypeExpr{Name: family.Name, Args: make([]TypeArg, len(t.Args))} + argOIDs := make([]int64, len(t.Args)) + for i, a := range t.Args { + canonical.Args[i] = a + if a.Type == nil { + continue + } + oid, argExpr, err := c.internType(a.Type, newFamily) + if err != nil { + return 0, nil, err + } + argOIDs[i] = oid + argExpr.Nullable = a.Type.Nullable + canonical.Args[i].Type = argExpr + } + key := canonical.Key() + ctx := context.Background() + if oid, err := c.q.TypeOIDByExprInNamespace(ctx, catalogdb.TypeOIDByExprInNamespaceParams{ + NamespaceOid: family.NamespaceOID, + Expr: key, + }); err == nil { + return oid, canonical, nil + } else if !errors.Is(err, sql.ErrNoRows) { + return 0, nil, fmt.Errorf("type %q: %w", key, err) + } + // A lookup that may not write stops here, canonical expression in hand. + if _, err := newFamily(""); errors.Is(err, errUnknownType) { + return 0, canonical, errUnknownType + } + + spec := TypeSpec{ + Name: family.Name, + Expr: key, + Typtype: family.Typtype, + Category: family.Category, + NamespaceOID: family.NamespaceOID, + DialectOID: c.dialectOID, + FamilyOID: familyOID, + } + if family.Name == ArrayTypeName && argOIDs[0] != 0 { + spec.ElementOID = argOIDs[0] + } + oid, err := c.CreateTypeSpec(spec) + if err != nil { + return 0, nil, err + } + for i, a := range canonical.Args { + p := catalogdb.CreateTypeArgParams{TypeOid: oid, Ord: int64(i + 1), Label: a.Label} + switch { + case a.Type != nil: + p.ArgTypeOid = nullInt64(argOIDs[i]) + p.Nullable = boolToInt64(a.Type.Nullable) + case a.Int != nil: + p.IntValue = sql.NullInt64{Int64: *a.Int, Valid: true} + case a.Bool != nil: + p.BoolValue = sql.NullInt64{Int64: boolToInt64(*a.Bool), Valid: true} + case a.String != nil: + p.StringValue = sql.NullString{String: *a.String, Valid: true} + case a.Ident != nil: + p.Ident = sql.NullString{String: *a.Ident, Valid: true} + } + if err := c.q.CreateTypeArg(ctx, p); err != nil { + return 0, nil, fmt.Errorf("type %q: argument %d: %w", key, i+1, err) + } + } + return oid, canonical, nil } func nullableOID(oid int64) any { diff --git a/internal/endtoend/testdata/analyze_ast/postgresql/stdout.json b/internal/endtoend/testdata/analyze_ast/postgresql/stdout.json index 427fe5af92..0ab1d7e128 100644 --- a/internal/endtoend/testdata/analyze_ast/postgresql/stdout.json +++ b/internal/endtoend/testdata/analyze_ast/postgresql/stdout.json @@ -17,7 +17,7 @@ "column": { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "authors" } diff --git a/internal/endtoend/testdata/analyze_basic/duckdb/stdout.json b/internal/endtoend/testdata/analyze_basic/duckdb/stdout.json index 6b7631c5de..ab2cee1451 100644 --- a/internal/endtoend/testdata/analyze_basic/duckdb/stdout.json +++ b/internal/endtoend/testdata/analyze_basic/duckdb/stdout.json @@ -13,14 +13,14 @@ { "name": "name", "type": { - "name": "text" + "name": "varchar" }, "table": "authors" }, { "name": "bio", "type": { - "name": "text", + "name": "varchar", "nullable": true }, "table": "authors" diff --git a/internal/endtoend/testdata/analyze_basic/mysql/stdout.json b/internal/endtoend/testdata/analyze_basic/mysql/stdout.json index 83dde3e339..56ae2723c3 100644 --- a/internal/endtoend/testdata/analyze_basic/mysql/stdout.json +++ b/internal/endtoend/testdata/analyze_basic/mysql/stdout.json @@ -13,7 +13,12 @@ { "name": "name", "type": { - "name": "varchar" + "name": "varchar", + "args": [ + { + "int": 255 + } + ] }, "table": "users" } diff --git a/internal/endtoend/testdata/analyze_basic/postgresql/stdout.json b/internal/endtoend/testdata/analyze_basic/postgresql/stdout.json index 36356b73c3..faa4c1acd1 100644 --- a/internal/endtoend/testdata/analyze_basic/postgresql/stdout.json +++ b/internal/endtoend/testdata/analyze_basic/postgresql/stdout.json @@ -6,7 +6,7 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "authors" }, @@ -32,7 +32,7 @@ "column": { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "authors" } diff --git a/internal/endtoend/testdata/analyze_dml/duckdb/stdout.json b/internal/endtoend/testdata/analyze_dml/duckdb/stdout.json index afdd96cfd7..c75a2a8d8b 100644 --- a/internal/endtoend/testdata/analyze_dml/duckdb/stdout.json +++ b/internal/endtoend/testdata/analyze_dml/duckdb/stdout.json @@ -27,7 +27,7 @@ "column": { "name": "name", "type": { - "name": "text" + "name": "varchar" }, "table": "authors" } @@ -37,7 +37,7 @@ "column": { "name": "bio", "type": { - "name": "text", + "name": "varchar", "nullable": true }, "table": "authors" @@ -55,7 +55,7 @@ "column": { "name": "name", "type": { - "name": "text" + "name": "varchar" }, "table": "authors" } @@ -65,7 +65,7 @@ "column": { "name": "bio", "type": { - "name": "text", + "name": "varchar", "nullable": true }, "table": "authors" @@ -93,7 +93,7 @@ "column": { "name": "name", "type": { - "name": "text" + "name": "varchar" }, "table": "authors" } @@ -114,7 +114,7 @@ { "name": "title", "type": { - "name": "text" + "name": "varchar" }, "table": "books" } diff --git a/internal/endtoend/testdata/analyze_dml/mysql/stdout.json b/internal/endtoend/testdata/analyze_dml/mysql/stdout.json index e474c04a04..1d5d37e41d 100644 --- a/internal/endtoend/testdata/analyze_dml/mysql/stdout.json +++ b/internal/endtoend/testdata/analyze_dml/mysql/stdout.json @@ -19,7 +19,12 @@ "column": { "name": "name", "type": { - "name": "varchar" + "name": "varchar", + "args": [ + { + "int": 255 + } + ] }, "table": "users" } @@ -92,7 +97,12 @@ "column": { "name": "name", "type": { - "name": "varchar" + "name": "varchar", + "args": [ + { + "int": 255 + } + ] }, "table": "users" } diff --git a/internal/endtoend/testdata/analyze_dml/postgresql/stdout.json b/internal/endtoend/testdata/analyze_dml/postgresql/stdout.json index 876a1ed726..e9f777ced3 100644 --- a/internal/endtoend/testdata/analyze_dml/postgresql/stdout.json +++ b/internal/endtoend/testdata/analyze_dml/postgresql/stdout.json @@ -6,7 +6,7 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" }, @@ -71,7 +71,7 @@ "column": { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" } @@ -85,7 +85,7 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" }, @@ -113,7 +113,7 @@ "column": { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" } @@ -130,7 +130,7 @@ "column": { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" } @@ -144,7 +144,7 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" } @@ -155,7 +155,7 @@ "column": { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" } diff --git a/internal/endtoend/testdata/analyze_extension/postgresql/stdout.json b/internal/endtoend/testdata/analyze_extension/postgresql/stdout.json index af10afec06..dd4477e89a 100644 --- a/internal/endtoend/testdata/analyze_extension/postgresql/stdout.json +++ b/internal/endtoend/testdata/analyze_extension/postgresql/stdout.json @@ -6,7 +6,7 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" }, @@ -38,7 +38,7 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" } @@ -52,7 +52,7 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" }, diff --git a/internal/endtoend/testdata/analyze_params/duckdb/stdout.json b/internal/endtoend/testdata/analyze_params/duckdb/stdout.json index 0a85a39358..617d664b7b 100644 --- a/internal/endtoend/testdata/analyze_params/duckdb/stdout.json +++ b/internal/endtoend/testdata/analyze_params/duckdb/stdout.json @@ -13,14 +13,14 @@ { "name": "name", "type": { - "name": "text" + "name": "varchar" }, "table": "authors" }, { "name": "bio", "type": { - "name": "text", + "name": "varchar", "nullable": true }, "table": "authors" @@ -53,7 +53,7 @@ { "name": "name", "type": { - "name": "text" + "name": "varchar" }, "table": "authors" } @@ -64,7 +64,7 @@ "column": { "name": "name", "type": { - "name": "text" + "name": "varchar" }, "table": "authors" } @@ -99,7 +99,7 @@ "column": { "name": "name", "type": { - "name": "text" + "name": "varchar" }, "table": "authors" } diff --git a/internal/endtoend/testdata/analyze_params/mysql/stdout.json b/internal/endtoend/testdata/analyze_params/mysql/stdout.json index 7ff80429a7..6232a4d26e 100644 --- a/internal/endtoend/testdata/analyze_params/mysql/stdout.json +++ b/internal/endtoend/testdata/analyze_params/mysql/stdout.json @@ -13,7 +13,12 @@ { "name": "name", "type": { - "name": "varchar" + "name": "varchar", + "args": [ + { + "int": 255 + } + ] }, "table": "users" } @@ -45,7 +50,12 @@ { "name": "name", "type": { - "name": "varchar" + "name": "varchar", + "args": [ + { + "int": 255 + } + ] }, "table": "users" } @@ -56,7 +66,12 @@ "column": { "name": "name", "type": { - "name": "varchar" + "name": "varchar", + "args": [ + { + "int": 255 + } + ] }, "table": "users" } @@ -88,7 +103,12 @@ { "name": "name", "type": { - "name": "varchar" + "name": "varchar", + "args": [ + { + "int": 255 + } + ] }, "table": "users" } diff --git a/internal/endtoend/testdata/analyze_params/postgresql/stdout.json b/internal/endtoend/testdata/analyze_params/postgresql/stdout.json index 3d98c7ff65..faaf6f30ec 100644 --- a/internal/endtoend/testdata/analyze_params/postgresql/stdout.json +++ b/internal/endtoend/testdata/analyze_params/postgresql/stdout.json @@ -6,7 +6,7 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" }, @@ -24,7 +24,7 @@ "column": { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" } @@ -38,7 +38,7 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" }, @@ -81,7 +81,7 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" }, @@ -99,7 +99,7 @@ "column": { "name": "ids", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" } @@ -113,14 +113,14 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "posts" }, { "name": "title", "type": { - "name": "varchar", + "name": "character varying", "nullable": true }, "table": "posts" @@ -132,7 +132,7 @@ "column": { "name": "created", "type": { - "name": "timestamptz" + "name": "timestamp with time zone" }, "table": "posts" } @@ -142,7 +142,7 @@ "column": { "name": "created", "type": { - "name": "timestamptz" + "name": "timestamp with time zone" }, "table": "posts" } diff --git a/internal/endtoend/testdata/analyze_select/duckdb/stdout.json b/internal/endtoend/testdata/analyze_select/duckdb/stdout.json index 2b0e161be2..e0744b9ecd 100644 --- a/internal/endtoend/testdata/analyze_select/duckdb/stdout.json +++ b/internal/endtoend/testdata/analyze_select/duckdb/stdout.json @@ -13,7 +13,7 @@ { "name": "name", "type": { - "name": "text" + "name": "varchar" }, "table": "users" }, @@ -48,7 +48,7 @@ { "name": "name", "type": { - "name": "text" + "name": "varchar" }, "table": "users" }, @@ -69,14 +69,14 @@ { "name": "title", "type": { - "name": "text" + "name": "varchar" }, "table": "posts" }, { "name": "body", "type": { - "name": "text", + "name": "varchar", "nullable": true }, "table": "posts" @@ -88,7 +88,7 @@ "column": { "name": "name", "type": { - "name": "text" + "name": "varchar" }, "table": "users" } @@ -102,7 +102,7 @@ { "name": "name", "type": { - "name": "text" + "name": "varchar" }, "table": "users" }, diff --git a/internal/endtoend/testdata/analyze_select/googlesql/stdout.json b/internal/endtoend/testdata/analyze_select/googlesql/stdout.json index 0bd71ba16a..7f112f964b 100644 --- a/internal/endtoend/testdata/analyze_select/googlesql/stdout.json +++ b/internal/endtoend/testdata/analyze_select/googlesql/stdout.json @@ -70,7 +70,12 @@ "name": "title", "type": { "name": "string", - "nullable": true + "nullable": true, + "args": [ + { + "int": 255 + } + ] }, "table": "posts" }, diff --git a/internal/endtoend/testdata/analyze_select/mysql/stdout.json b/internal/endtoend/testdata/analyze_select/mysql/stdout.json index f50d00465d..3e679fd261 100644 --- a/internal/endtoend/testdata/analyze_select/mysql/stdout.json +++ b/internal/endtoend/testdata/analyze_select/mysql/stdout.json @@ -13,7 +13,12 @@ { "name": "name", "type": { - "name": "varchar" + "name": "varchar", + "args": [ + { + "int": 255 + } + ] }, "table": "users" }, @@ -48,7 +53,12 @@ { "name": "name", "type": { - "name": "varchar" + "name": "varchar", + "args": [ + { + "int": 255 + } + ] }, "table": "users" }, @@ -70,7 +80,12 @@ "name": "title", "type": { "name": "varchar", - "nullable": true + "nullable": true, + "args": [ + { + "int": 255 + } + ] }, "table": "posts" }, @@ -88,7 +103,12 @@ "column": { "name": "name", "type": { - "name": "varchar" + "name": "varchar", + "args": [ + { + "int": 255 + } + ] }, "table": "users" } @@ -110,7 +130,12 @@ "name": "title", "type": { "name": "varchar", - "nullable": true + "nullable": true, + "args": [ + { + "int": 255 + } + ] }, "table": "posts" } @@ -122,7 +147,12 @@ "name": "title", "type": { "name": "varchar", - "nullable": true + "nullable": true, + "args": [ + { + "int": 255 + } + ] }, "table": "posts" } @@ -146,7 +176,12 @@ { "name": "name", "type": { - "name": "varchar" + "name": "varchar", + "args": [ + { + "int": 255 + } + ] }, "table": "users" }, diff --git a/internal/endtoend/testdata/analyze_select/postgresql/stdout.json b/internal/endtoend/testdata/analyze_select/postgresql/stdout.json index 75ae4486c3..807bcb745d 100644 --- a/internal/endtoend/testdata/analyze_select/postgresql/stdout.json +++ b/internal/endtoend/testdata/analyze_select/postgresql/stdout.json @@ -6,7 +6,7 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "users" }, @@ -55,21 +55,21 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "posts" }, { "name": "user_id", "type": { - "name": "int8" + "name": "bigint" }, "table": "posts" }, { "name": "title", "type": { - "name": "varchar", + "name": "character varying", "nullable": true }, "table": "posts" @@ -77,7 +77,7 @@ { "name": "created", "type": { - "name": "timestamptz" + "name": "timestamp with time zone" }, "table": "posts" } @@ -102,14 +102,14 @@ { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "posts" }, { "name": "title", "type": { - "name": "varchar", + "name": "character varying", "nullable": true }, "table": "posts" @@ -121,7 +121,7 @@ "column": { "name": "title", "type": { - "name": "varchar", + "name": "character varying", "nullable": true }, "table": "posts" @@ -132,7 +132,7 @@ "column": { "name": "created", "type": { - "name": "timestamptz" + "name": "timestamp with time zone" }, "table": "posts" } @@ -153,7 +153,7 @@ { "name": "created", "type": { - "name": "timestamptz" + "name": "timestamp with time zone" } } ], diff --git a/internal/endtoend/testdata/analyze_system_catalog/postgresql/stdout.json b/internal/endtoend/testdata/analyze_system_catalog/postgresql/stdout.json index 0432abd33e..dc48b00653 100644 --- a/internal/endtoend/testdata/analyze_system_catalog/postgresql/stdout.json +++ b/internal/endtoend/testdata/analyze_system_catalog/postgresql/stdout.json @@ -59,7 +59,7 @@ "column": { "name": "relkind", "type": { - "name": "char" + "name": "character" }, "table": "pg_class" } diff --git a/internal/engine/clickhouse/convert.go b/internal/engine/clickhouse/convert.go index de2a1419bf..b65df9e966 100644 --- a/internal/engine/clickhouse/convert.go +++ b/internal/engine/clickhouse/convert.go @@ -1079,14 +1079,18 @@ func unwrapTypeString(s string) (name string, isArray, nullable bool) { } return strings.ToLower(base), false, true case "lowcardinality": + // Only a Nullable at the top makes the column nullable: the + // analysis reports LowCardinality(Nullable(String)) with the + // nullability inside, as ClickHouse does. if len(args) == 1 { - return unwrapTypeString(args[0]) + inner, arr, _ := unwrapTypeString(args[0]) + return inner, arr, false } return strings.ToLower(base), false, false case "array": if len(args) == 1 { - inner, _, nul := unwrapTypeString(args[0]) - return inner, true, nul + inner, _, _ := unwrapTypeString(args[0]) + return inner, true, false } return strings.ToLower(base), true, false default: diff --git a/internal/engine/postgresql/dialect/dialect.json b/internal/engine/postgresql/dialect/dialect.json index 2e64c9c571..d8ae14185a 100644 --- a/internal/engine/postgresql/dialect/dialect.json +++ b/internal/engine/postgresql/dialect/dialect.json @@ -1,12 +1,12 @@ { "dialect": "postgresql", "const": { - "integer": "int4", + "integer": "integer", "float": "numeric", "string": "text", - "bool": "bool" + "bool": "boolean" }, - "bool": "bool", + "bool": "boolean", "comparison": ["=", "<>", "!=", "<", "<=", ">", ">=", "IS DISTINCT FROM", "IS NOT DISTINCT FROM"], "comparison_categories": "BNSDTU", "arithmetic": ["+", "-", "*", "/", "%"], diff --git a/internal/engine/postgresql/dialect/types.jsonl b/internal/engine/postgresql/dialect/types.jsonl index da56a7af13..ac394f1616 100644 --- a/internal/engine/postgresql/dialect/types.jsonl +++ b/internal/engine/postgresql/dialect/types.jsonl @@ -1,25 +1,22 @@ -{"name": "bool", "category": "B", "aliases": ["boolean"]} -{"name": "int2", "category": "N", "aliases": ["smallint"]} -{"name": "int4", "category": "N", "aliases": ["integer", "int"]} -{"name": "int8", "category": "N", "aliases": ["bigint"]} -{"name": "float4", "category": "N", "aliases": ["real"]} -{"name": "float8", "category": "N", "aliases": ["double precision"]} +{"name": "boolean", "category": "B", "aliases": ["bool"]} +{"name": "smallint", "category": "N", "aliases": ["int2", "smallserial", "serial2"]} +{"name": "integer", "category": "N", "aliases": ["int4", "int", "serial", "serial4"]} +{"name": "bigint", "category": "N", "aliases": ["int8", "bigserial", "serial8"]} +{"name": "real", "category": "N", "aliases": ["float4"]} +{"name": "double precision", "category": "N", "aliases": ["float8"]} {"name": "numeric", "category": "N", "aliases": ["decimal"]} {"name": "money", "category": "N"} {"name": "oid", "category": "N"} -{"name": "serial2", "category": "N", "aliases": ["smallserial"]} -{"name": "serial4", "category": "N", "aliases": ["serial"]} -{"name": "serial8", "category": "N", "aliases": ["bigserial"]} {"name": "text", "category": "S"} -{"name": "varchar", "category": "S", "aliases": ["character varying"]} -{"name": "bpchar", "category": "S", "aliases": ["char", "character"]} +{"name": "character varying", "category": "S", "aliases": ["varchar"]} +{"name": "character", "category": "S", "aliases": ["bpchar", "char"]} {"name": "name", "category": "S"} {"name": "citext", "category": "S"} {"name": "date", "category": "D"} -{"name": "time", "category": "D", "aliases": ["time without time zone"]} -{"name": "timetz", "category": "D", "aliases": ["time with time zone"]} -{"name": "timestamp", "category": "D", "aliases": ["timestamp without time zone"]} -{"name": "timestamptz", "category": "D", "aliases": ["timestamp with time zone"]} +{"name": "time without time zone", "category": "D", "aliases": ["time"]} +{"name": "time with time zone", "category": "D", "aliases": ["timetz"]} +{"name": "timestamp without time zone", "category": "D", "aliases": ["timestamp"]} +{"name": "timestamp with time zone", "category": "D", "aliases": ["timestamptz"]} {"name": "interval", "category": "T"} {"name": "uuid", "category": "U"} {"name": "bytea", "category": "U"} @@ -31,7 +28,7 @@ {"name": "macaddr", "category": "U"} {"name": "macaddr8", "category": "U"} {"name": "bit", "category": "U"} -{"name": "varbit", "category": "U", "aliases": ["bit varying"]} +{"name": "bit varying", "category": "U", "aliases": ["varbit"]} {"name": "tsvector", "category": "U"} {"name": "tsquery", "category": "U"} {"name": "point", "category": "U"} @@ -47,4 +44,5 @@ {"name": "tsrange", "category": "U"} {"name": "tstzrange", "category": "U"} {"name": "daterange", "category": "U"} -{"name": "anyarray", "category": "A", "aliases": ["array"]} +{"name": "anyarray", "category": "A"} +{"name": "array", "category": "A"} diff --git a/internal/engine/sqlite/dialect/dialect.json b/internal/engine/sqlite/dialect/dialect.json index 36129df83a..caa2e365f7 100644 --- a/internal/engine/sqlite/dialect/dialect.json +++ b/internal/engine/sqlite/dialect/dialect.json @@ -1,5 +1,6 @@ { "dialect": "sqlite", + "alias": "base", "const": { "integer": "integer", "float": "real", From fedc4a1987460d7b1547c2a2c45a211f7a9e7085 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 17:25:53 +0000 Subject: [PATCH 06/16] postgresql: typmods, dimensions, interval fields, declared types and namespaces in the core The parser now hands the core a column's type modifiers, so numeric(10,2), character varying(255), timestamp(3) and bit(8) reach the catalog, with an interval's field mask decoded into the words format_type prints. Domains stand on their base with their NOT NULL, composites carry their fields, ranges their subtype and enums their labels, and a schema-qualified type lives in its namespace. A canonicalizer registered per dialect rewrites what only code can, starting with pg_type's _int4 spelling of an array, and is found by name so a catalog restored from the cache has it too. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- internal/core/catalog.go | 7 + internal/core/catalogdb/query.sql.go | 17 + internal/core/catalogdef/query.sql | 4 + internal/core/hooks.go | 55 ++ internal/core/schema/schema.go | 139 +++- internal/core/typename.go | 18 +- internal/core/types.go | 142 +++- .../analyze_params/postgresql/stdout.json | 7 +- .../analyze_select/postgresql/stdout.json | 21 +- .../analyze_types/postgresql/exec.json | 5 + .../analyze_types/postgresql/query.sql | 21 + .../analyze_types/postgresql/schema.sql | 46 + .../analyze_types/postgresql/stdout.json | 784 ++++++++++++++++++ internal/engine/postgresql/parse.go | 100 ++- internal/engine/postgresql/seed.go | 15 + internal/sql/ast/composite_type_stmt.go | 2 + internal/sql/ast/constr_type.go | 7 + 17 files changed, 1346 insertions(+), 44 deletions(-) create mode 100644 internal/core/hooks.go create mode 100644 internal/endtoend/testdata/analyze_types/postgresql/exec.json create mode 100644 internal/endtoend/testdata/analyze_types/postgresql/query.sql create mode 100644 internal/endtoend/testdata/analyze_types/postgresql/schema.sql create mode 100644 internal/endtoend/testdata/analyze_types/postgresql/stdout.json diff --git a/internal/core/catalog.go b/internal/core/catalog.go index 0f48dcb328..2310d6ca03 100644 --- a/internal/core/catalog.go +++ b/internal/core/catalog.go @@ -5,6 +5,7 @@ import ( "database/sql" "fmt" "runtime" + "sync" "github.com/sqlc-dev/sqlc/internal/core/catalogdb" "github.com/sqlc-dev/sqlc/internal/core/catalogdef" @@ -31,8 +32,14 @@ type Catalog struct { // types remembers the rows and expressions looked up so far. types typeCache + + // dialect is the seeded dialect's name, read back once it is asked for. + dialect string + dialectNameOnce sync.Once } +func contextBackground() context.Context { return context.Background() } + type Option func(*Catalog) error func WithSeed(fn func(*Catalog) error) Option { diff --git a/internal/core/catalogdb/query.sql.go b/internal/core/catalogdb/query.sql.go index acdb93a2a2..80954b4643 100644 --- a/internal/core/catalogdb/query.sql.go +++ b/internal/core/catalogdb/query.sql.go @@ -1221,6 +1221,23 @@ func (q *Queries) TypeOIDByName(ctx context.Context, name string) (int64, error) return oid, err } +const typeOIDByNameInNamespace = `-- name: TypeOIDByNameInNamespace :one +SELECT oid FROM sql_type +WHERE namespace_oid = ? AND name = ? AND family_oid IS NULL +` + +type TypeOIDByNameInNamespaceParams struct { + NamespaceOid int64 + Name string +} + +func (q *Queries) TypeOIDByNameInNamespace(ctx context.Context, arg TypeOIDByNameInNamespaceParams) (int64, error) { + row := q.db.QueryRowContext(ctx, typeOIDByNameInNamespace, arg.NamespaceOid, arg.Name) + var oid int64 + err := row.Scan(&oid) + return oid, err +} + const typeOIDsInCategory = `-- name: TypeOIDsInCategory :many SELECT oid FROM sql_type WHERE dialect_oid = ? AND category = ? diff --git a/internal/core/catalogdef/query.sql b/internal/core/catalogdef/query.sql index 5182c3014f..ff31099c5f 100644 --- a/internal/core/catalogdef/query.sql +++ b/internal/core/catalogdef/query.sql @@ -66,6 +66,10 @@ ORDER BY ns.name LIMIT 1; +-- name: TypeOIDByNameInNamespace :one +SELECT oid FROM sql_type +WHERE namespace_oid = ? AND name = ? AND family_oid IS NULL; + -- name: TypeOIDByExprInNamespace :one SELECT oid FROM sql_type WHERE namespace_oid = ? AND expr = ?; diff --git a/internal/core/hooks.go b/internal/core/hooks.go new file mode 100644 index 0000000000..2b56cba68d --- /dev/null +++ b/internal/core/hooks.go @@ -0,0 +1,55 @@ +package core + +import "sync" + +// A Canonicalizer rewrites a type expression into the form its engine +// stores and reports: ClickHouse turns Decimal32(4) into Decimal(9, 4) and +// Enum('a', 'b') into Enum8('a' = 1, 'b' = 2), SQL Server turns float(24) +// into real. It sees each expression as a whole before its arguments are +// interned, and again on each argument, so it has to be idempotent. Aliases +// and argument defaults are data in the dialect's seed; a canonicalizer is +// for what only code can say. +type Canonicalizer func(*TypeExpr) *TypeExpr + +var ( + hooksMu sync.RWMutex + canonicalizers = map[string]Canonicalizer{} +) + +// RegisterCanonicalizer installs the canonicalizer for a dialect, by the +// name its dialect.json records. An engine registers its own at init, so +// that a catalog restored from the cache — which runs no seed — finds it by +// the dialect it was seeded with. +func RegisterCanonicalizer(dialect string, fn Canonicalizer) { + hooksMu.Lock() + defer hooksMu.Unlock() + canonicalizers[dialect] = fn +} + +// canonicalize applies the catalog's dialect's canonicalizer, if any. +func (c *Catalog) canonicalize(t *TypeExpr) *TypeExpr { + name := c.dialectName() + if name == "" { + return t + } + hooksMu.RLock() + fn := canonicalizers[name] + hooksMu.RUnlock() + if fn == nil { + return t + } + return fn(t) +} + +// dialectName is the name of the dialect the catalog was seeded with. +func (c *Catalog) dialectName() string { + if c.dialectOID == 0 { + return "" + } + c.dialectNameOnce.Do(func() { + if row, err := c.q.SeededDialect(contextBackground()); err == nil { + c.dialect = row.Name + } + }) + return c.dialect +} diff --git a/internal/core/schema/schema.go b/internal/core/schema/schema.go index f4005f9c50..bfdc6c1a37 100644 --- a/internal/core/schema/schema.go +++ b/internal/core/schema/schema.go @@ -2,6 +2,7 @@ package schema import ( "fmt" + "strings" "github.com/sqlc-dev/sqlc/internal/core" "github.com/sqlc-dev/sqlc/internal/core/analyzer" @@ -27,6 +28,12 @@ func Apply(cat *core.Catalog, n ast.Node) error { return applyDropTable(cat, v) case *ast.CreateEnumStmt: return applyCreateEnum(cat, v) + case *ast.CreateDomainStmt: + return applyCreateDomain(cat, v) + case *ast.CompositeTypeStmt: + return applyCompositeType(cat, v) + case *ast.CreateRangeStmt: + return applyCreateRange(cat, v) case *ast.CreateExtensionStmt: if v.Extname == nil { return nil @@ -166,7 +173,7 @@ func applyCreateTable(cat *core.Catalog, stmt *ast.CreateTableStmt) error { Name: col.Colname, TypeOID: typeOID, Num: i + 1, - NotNull: col.IsNotNull || col.PrimaryKey, + NotNull: col.IsNotNull || col.PrimaryKey || typeNotNull(cat, typeOID), IsPrimaryKey: col.PrimaryKey, DeclType: declType(col.TypeName), Hidden: col.IsHidden, @@ -238,7 +245,7 @@ func applyAlterTable(cat *core.Catalog, stmt *ast.AlterTableStmt) error { Name: cmd.Def.Colname, TypeOID: typeOID, Num: num, - NotNull: cmd.Def.IsNotNull || cmd.Def.PrimaryKey, + NotNull: cmd.Def.IsNotNull || cmd.Def.PrimaryKey || typeNotNull(cat, typeOID), IsPrimaryKey: cmd.Def.PrimaryKey, DeclType: declType(cmd.Def.TypeName), }); err != nil { @@ -350,21 +357,134 @@ func listItems(l *ast.List) []ast.Node { return l.Items } +// applyCreateEnum records an enum as a type whose arguments are its labels, +// in order, the way pg_enum keeps them. func applyCreateEnum(cat *core.Catalog, stmt *ast.CreateEnumStmt) error { if stmt.TypeName == nil { return fmt.Errorf("create type with nil name") } - t := core.TypeExprOfTypeName(stmt.TypeName) - if t == nil { + name := declaredTypeName(stmt.TypeName) + if name == "" { return fmt.Errorf("create type with empty name") } - if _, err := cat.TypeOID(t.Name); err == nil { + if _, err := cat.TypeOID(name); err == nil { + return nil + } + var labels []core.TypeArg + for _, label := range listStrings(stmt.Vals) { + l := label + labels = append(labels, core.TypeArg{String: &l}) + } + _, err := cat.CreateTypeWithArgs(core.TypeSpec{Name: name, Typtype: "e", Category: "E"}, labels) + return err +} + +// applyCreateDomain records a domain: a type of its own that stands on its +// base, which is what it resolves through, and that may forbid NULL. +func applyCreateDomain(cat *core.Catalog, stmt *ast.CreateDomainStmt) error { + name := strings.ToLower(strings.Join(listStrings(stmt.Domainname), ".")) + if name == "" || stmt.TypeName == nil { + return fmt.Errorf("create domain: missing name or type") + } + if _, err := cat.TypeOID(name); err == nil { return nil } - _, err := cat.CreateUserType(t.Name, "E") + baseOID, err := cat.ResolveType(stmt.TypeName) + if err != nil { + return fmt.Errorf("domain %q: %w", name, err) + } + base, err := cat.LookupType(baseOID) + if err != nil { + return err + } + notNull := false + for _, item := range listItems(stmt.Constraints) { + if con, ok := item.(*ast.Constraint); ok && con.Contype == ast.ConstrTypeNotNull { + notNull = true + } + } + _, err = cat.CreateTypeWithArgs(core.TypeSpec{ + Name: name, + Typtype: "d", + Category: base.Category, + BaseOID: baseOID, + NotNull: notNull, + }, nil) return err } +// applyCompositeType records a composite type as a type whose arguments are +// its fields, labelled by name. +func applyCompositeType(cat *core.Catalog, stmt *ast.CompositeTypeStmt) error { + if stmt.TypeName == nil { + return fmt.Errorf("create type with nil name") + } + name := declaredTypeName(stmt.TypeName) + if name == "" { + return fmt.Errorf("create type with empty name") + } + if _, err := cat.TypeOID(name); err == nil { + return nil + } + var fields []core.TypeArg + for _, item := range listItems(stmt.Coldeflist) { + col, ok := item.(*ast.ColumnDef) + if !ok || col.TypeName == nil { + continue + } + t := core.ColumnTypeExpr(col) + if t == nil { + continue + } + fields = append(fields, core.TypeArg{Label: col.Colname, Type: t}) + } + _, err := cat.CreateTypeWithArgs(core.TypeSpec{Name: name, Typtype: "c", Category: "C"}, fields) + return err +} + +// applyCreateRange records a range type over its subtype, which is what a +// bound of it has. +func applyCreateRange(cat *core.Catalog, stmt *ast.CreateRangeStmt) error { + name := strings.ToLower(strings.Join(listStrings(stmt.TypeName), ".")) + if name == "" { + return fmt.Errorf("create type with empty name") + } + if _, err := cat.TypeOID(name); err == nil { + return nil + } + spec := core.TypeSpec{Name: name, Typtype: "r", Category: "R"} + for _, item := range listItems(stmt.Params) { + def, ok := item.(*ast.DefElem) + if !ok || def.Defname == nil || *def.Defname != "subtype" { + continue + } + tn, ok := def.Arg.(*ast.TypeName) + if !ok { + continue + } + oid, err := cat.ResolveType(tn) + if err != nil { + return fmt.Errorf("range %q: %w", name, err) + } + spec.ElementOID = oid + } + _, err := cat.CreateTypeWithArgs(spec, nil) + return err +} + +// declaredTypeName is the name a CREATE TYPE gives, qualified by its schema +// when it names one. +func declaredTypeName(tn *ast.TypeName) string { + t := core.TypeExprOfTypeName(tn) + if t == nil { + return "" + } + if tn.Schema != "" && !strings.Contains(t.Name, ".") { + return strings.ToLower(tn.Schema) + "." + t.Name + } + return t.Name +} + func applyCreateFunction(cat *core.Catalog, stmt *ast.CreateFunctionStmt) error { // A procedure returns nothing, so there is no result for a query to // select and nothing worth recording. @@ -421,6 +541,13 @@ func resolveOrCreateNamespace(cat *core.Catalog, schema string) (int64, error) { return cat.CreateNamespace(name) } +// typeNotNull reports whether a column of the type can never be NULL +// because the type itself says so, as a domain declared NOT NULL does. +func typeNotNull(cat *core.Catalog, typeOID int64) bool { + info, err := cat.LookupType(typeOID) + return err == nil && info.NotNull +} + // columnTypeOID interns a column's type and returns its row. func columnTypeOID(cat *core.Catalog, col *ast.ColumnDef) (int64, error) { t := core.ColumnTypeExpr(col) diff --git a/internal/core/typename.go b/internal/core/typename.go index 516c4c1ff5..81026b6944 100644 --- a/internal/core/typename.go +++ b/internal/core/typename.go @@ -74,18 +74,26 @@ func ColumnTypeExpr(col *ast.ColumnDef) *TypeExpr { return t } -// typmodArg reads one type modifier as an argument: an integer, a string, or -// a bare word, which is an identifier such as the max of nvarchar(max). +// typmodArg reads one type modifier as an argument: an integer, a quoted +// string, or a bare word, which is an identifier such as the max of +// nvarchar(max) or the day to second of an interval. A constant node holds +// a literal; a bare String node holds a word. func typmodArg(n ast.Node) (TypeArg, bool) { switch v := n.(type) { case *ast.A_Const: - return typmodArg(v.Val) + switch val := v.Val.(type) { + case *ast.String: + s := val.Str + return TypeArg{String: &s}, true + default: + return typmodArg(v.Val) + } case *ast.Integer: i := v.Ival return TypeArg{Int: &i}, true case *ast.String: - s := v.Str - return TypeArg{String: &s}, true + s := strings.ToLower(v.Str) + return TypeArg{Ident: &s}, true case *ast.ColumnRef: parts := make([]string, 0, len(listItems(v.Fields))) for _, item := range listItems(v.Fields) { diff --git a/internal/core/types.go b/internal/core/types.go index 0dfa485de4..84ae6199c2 100644 --- a/internal/core/types.go +++ b/internal/core/types.go @@ -142,11 +142,16 @@ func (c *Catalog) CreateUserType(name, category string) (int64, error) { if category == "E" { typtype = "e" } + nsOID, bare, err := c.declaredTypeNamespace(strings.ToLower(name)) + if err != nil { + return 0, fmt.Errorf("create type %q: %w", name, err) + } oid, err := c.CreateTypeSpec(TypeSpec{ - Name: name, - Typtype: typtype, - Category: category, - DialectOID: c.dialectOID, + Name: bare, + NamespaceOID: nsOID, + Typtype: typtype, + Category: category, + DialectOID: c.dialectOID, }) if err != nil { return 0, err @@ -217,6 +222,112 @@ func (c *Catalog) familyOIDByName(name string) (int64, error) { return c.q.TypeOIDByName(context.Background(), name) } +// familyOIDByQualifiedName is familyOIDByName for a name that may carry its +// namespace, as myschema.mood does: a qualified name is looked up in that +// namespace alone, a bare one in every namespace. +func (c *Catalog) familyOIDByQualifiedName(name string) (int64, error) { + ns, bare := splitQualifiedName(name) + if ns == "" { + return c.familyOIDByName(bare) + } + nsOID, err := c.NamespaceOID(ns) + if err != nil { + return 0, err + } + return c.q.TypeOIDByNameInNamespace(context.Background(), catalogdb.TypeOIDByNameInNamespaceParams{ + NamespaceOid: nsOID, + Name: bare, + }) +} + +// splitQualifiedName splits "myschema.mood" into its namespace and name. A +// name with no dot has no namespace. +func splitQualifiedName(name string) (ns, bare string) { + if i := strings.LastIndexByte(name, '.'); i > 0 { + return name[:i], name[i+1:] + } + return "", name +} + +// declaredTypeNamespace is the namespace a declared type's row goes in: the +// one its name qualifies, created if the schema has not, or the default. +func (c *Catalog) declaredTypeNamespace(name string) (int64, string, error) { + ns, bare := splitQualifiedName(name) + if ns == "" { + return 0, bare, nil + } + oid, err := c.NamespaceOID(ns) + if err != nil { + if oid, err = c.CreateNamespace(ns); err != nil { + return 0, "", err + } + } + return oid, bare, nil +} + +// CreateTypeWithArgs registers a declared type that has arguments of its own +// — a composite's fields, an enum's labels — as a family row carrying them. +// The arguments' types are interned first. +func (c *Catalog) CreateTypeWithArgs(spec TypeSpec, args []TypeArg) (int64, error) { + nsOID, bare, err := c.declaredTypeNamespace(strings.ToLower(spec.Name)) + if err != nil { + return 0, fmt.Errorf("create type %q: %w", spec.Name, err) + } + spec.Name = bare + if nsOID != 0 { + spec.NamespaceOID = nsOID + } + if spec.DialectOID == 0 { + spec.DialectOID = c.dialectOID + } + argOIDs := make([]int64, len(args)) + for i, a := range args { + if a.Type == nil { + continue + } + oid, _, err := c.internType(a.Type, func(name string) (int64, error) { + return c.CreateUserType(name, "U") + }) + if err != nil { + return 0, fmt.Errorf("create type %q: %w", spec.Name, err) + } + argOIDs[i] = oid + } + oid, err := c.CreateTypeSpec(spec) + if err != nil { + return 0, err + } + if err := c.createComparisons(oid); err != nil { + return 0, err + } + return oid, c.insertTypeArgs(oid, spec.Expr, args, argOIDs) +} + +// insertTypeArgs writes a type's argument rows. +func (c *Catalog) insertTypeArgs(oid int64, key string, args []TypeArg, argOIDs []int64) error { + ctx := context.Background() + for i, a := range args { + p := catalogdb.CreateTypeArgParams{TypeOid: oid, Ord: int64(i + 1), Label: a.Label} + switch { + case a.Type != nil: + p.ArgTypeOid = nullInt64(argOIDs[i]) + p.Nullable = boolToInt64(a.Type.Nullable) + case a.Int != nil: + p.IntValue = sql.NullInt64{Int64: *a.Int, Valid: true} + case a.Bool != nil: + p.BoolValue = sql.NullInt64{Int64: boolToInt64(*a.Bool), Valid: true} + case a.String != nil: + p.StringValue = sql.NullString{String: *a.String, Valid: true} + case a.Ident != nil: + p.Ident = sql.NullString{String: *a.Ident, Valid: true} + } + if err := c.q.CreateTypeArg(ctx, p); err != nil { + return fmt.Errorf("type %q: argument %d: %w", key, i+1, err) + } + } + return nil +} + // canonicalOID follows an alias row to the row it stands for. func (c *Catalog) canonicalOID(oid int64) (int64, error) { for i := 0; i < 16; i++ { @@ -428,8 +539,9 @@ func (c *Catalog) internType(t *TypeExpr, newFamily func(name string) (int64, er if t == nil || strings.TrimSpace(t.Name) == "" { return 0, nil, fmt.Errorf("missing type name") } + t = c.canonicalize(t) name := strings.ToLower(strings.TrimSpace(t.Name)) - familyOID, err := c.familyOIDByName(name) + familyOID, err := c.familyOIDByQualifiedName(name) if err != nil { if name == ArrayTypeName { // Every dialect has arrays, whether or not its seed lists the @@ -503,24 +615,8 @@ func (c *Catalog) internType(t *TypeExpr, newFamily func(name string) (int64, er if err != nil { return 0, nil, err } - for i, a := range canonical.Args { - p := catalogdb.CreateTypeArgParams{TypeOid: oid, Ord: int64(i + 1), Label: a.Label} - switch { - case a.Type != nil: - p.ArgTypeOid = nullInt64(argOIDs[i]) - p.Nullable = boolToInt64(a.Type.Nullable) - case a.Int != nil: - p.IntValue = sql.NullInt64{Int64: *a.Int, Valid: true} - case a.Bool != nil: - p.BoolValue = sql.NullInt64{Int64: boolToInt64(*a.Bool), Valid: true} - case a.String != nil: - p.StringValue = sql.NullString{String: *a.String, Valid: true} - case a.Ident != nil: - p.Ident = sql.NullString{String: *a.Ident, Valid: true} - } - if err := c.q.CreateTypeArg(ctx, p); err != nil { - return 0, nil, fmt.Errorf("type %q: argument %d: %w", key, i+1, err) - } + if err := c.insertTypeArgs(oid, key, canonical.Args, argOIDs); err != nil { + return 0, nil, err } return oid, canonical, nil } diff --git a/internal/endtoend/testdata/analyze_params/postgresql/stdout.json b/internal/endtoend/testdata/analyze_params/postgresql/stdout.json index faaf6f30ec..fd46e40a84 100644 --- a/internal/endtoend/testdata/analyze_params/postgresql/stdout.json +++ b/internal/endtoend/testdata/analyze_params/postgresql/stdout.json @@ -121,7 +121,12 @@ "name": "title", "type": { "name": "character varying", - "nullable": true + "nullable": true, + "args": [ + { + "int": 255 + } + ] }, "table": "posts" } diff --git a/internal/endtoend/testdata/analyze_select/postgresql/stdout.json b/internal/endtoend/testdata/analyze_select/postgresql/stdout.json index 807bcb745d..95f2d691a6 100644 --- a/internal/endtoend/testdata/analyze_select/postgresql/stdout.json +++ b/internal/endtoend/testdata/analyze_select/postgresql/stdout.json @@ -70,7 +70,12 @@ "name": "title", "type": { "name": "character varying", - "nullable": true + "nullable": true, + "args": [ + { + "int": 255 + } + ] }, "table": "posts" }, @@ -110,7 +115,12 @@ "name": "title", "type": { "name": "character varying", - "nullable": true + "nullable": true, + "args": [ + { + "int": 255 + } + ] }, "table": "posts" } @@ -122,7 +132,12 @@ "name": "title", "type": { "name": "character varying", - "nullable": true + "nullable": true, + "args": [ + { + "int": 255 + } + ] }, "table": "posts" } diff --git a/internal/endtoend/testdata/analyze_types/postgresql/exec.json b/internal/endtoend/testdata/analyze_types/postgresql/exec.json new file mode 100644 index 0000000000..b102755fb6 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/postgresql/exec.json @@ -0,0 +1,5 @@ +{ + "command": "analyze", + "args": ["--dialect", "postgresql", "--schema", "schema.sql", "query.sql"], + "contexts": ["base"] +} diff --git a/internal/endtoend/testdata/analyze_types/postgresql/query.sql b/internal/endtoend/testdata/analyze_types/postgresql/query.sql new file mode 100644 index 0000000000..b64674872e --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/postgresql/query.sql @@ -0,0 +1,21 @@ +-- name: AllTypes :many +SELECT * FROM things; + +-- name: Casts :one +SELECT + $1::numeric(10,2) AS a, + $2::int[] AS b, + price::text AS c, + $3::mood AS d, + $4::varchar(20) AS e, + $5::posint AS f, + 1::int8 AS g, + $6::int4[][] AS h, + $7::numeric(5,1) AS i, + ARRAY[1, 2] AS j, + $8::myschema.mood AS k +FROM things; + +-- name: Params :one +SELECT id FROM things +WHERE price = $1 AND ints = $2 AND m = $3 AND p = $4 AND title = $5 AND grid = $6 AND sn = $7 AND fr = $8 AND ivd = $9; diff --git a/internal/endtoend/testdata/analyze_types/postgresql/schema.sql b/internal/endtoend/testdata/analyze_types/postgresql/schema.sql new file mode 100644 index 0000000000..0cbed44721 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/postgresql/schema.sql @@ -0,0 +1,46 @@ +CREATE EXTENSION hstore; +CREATE SCHEMA myschema; +CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy'); +CREATE TYPE myschema.mood AS ENUM ('x', 'y'); +CREATE DOMAIN posint AS integer CHECK (VALUE > 0); +CREATE DOMAIN shortname AS varchar(20) NOT NULL; +CREATE TYPE point2 AS (x float8, y float8); +CREATE TYPE floatrange AS RANGE (subtype = float8); + +CREATE TABLE things ( + id bigserial PRIMARY KEY, + price numeric(10,2) NOT NULL, + amount numeric, + title varchar(255), + code character varying(10), + tag char(5), + raw bpchar, + count pg_catalog.int4, + ints int[], + grid int[][], + bounded int[3], + words text[] NOT NULL, + prices numeric(10,2)[], + ts timestamp(3), + tstz timestamptz NOT NULL, + ttz time with time zone, + iv interval, + ivd interval day to second, + iv3 interval(3), + m mood, + mm myschema.mood, + p posint, + sn shortname, + pt point2, + pts point2[], + fr floatrange, + ir int4range, + b bit(8), + vb varbit(16), + js jsonb, + u uuid, + h hstore, + moods mood[], + dp double precision, + ts2 timestamp without time zone +); diff --git a/internal/endtoend/testdata/analyze_types/postgresql/stdout.json b/internal/endtoend/testdata/analyze_types/postgresql/stdout.json new file mode 100644 index 0000000000..44b2487db8 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/postgresql/stdout.json @@ -0,0 +1,784 @@ +[ + { + "name": "AllTypes", + "cmd": ":many", + "columns": [ + { + "name": "id", + "type": { + "name": "bigint" + }, + "table": "things" + }, + { + "name": "price", + "type": { + "name": "numeric", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + }, + "table": "things" + }, + { + "name": "amount", + "type": { + "name": "numeric", + "nullable": true + }, + "table": "things" + }, + { + "name": "title", + "type": { + "name": "character varying", + "nullable": true, + "args": [ + { + "int": 255 + } + ] + }, + "table": "things" + }, + { + "name": "code", + "type": { + "name": "character varying", + "nullable": true, + "args": [ + { + "int": 10 + } + ] + }, + "table": "things" + }, + { + "name": "tag", + "type": { + "name": "character", + "nullable": true, + "args": [ + { + "int": 5 + } + ] + }, + "table": "things" + }, + { + "name": "raw", + "type": { + "name": "character", + "nullable": true + }, + "table": "things" + }, + { + "name": "count", + "type": { + "name": "integer", + "nullable": true + }, + "table": "things" + }, + { + "name": "ints", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "integer" + } + } + ] + }, + "table": "things" + }, + { + "name": "grid", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + } + ] + } + } + ] + }, + "table": "things" + }, + { + "name": "bounded", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "integer" + } + } + ] + }, + "table": "things" + }, + { + "name": "words", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "text" + } + } + ] + }, + "table": "things" + }, + { + "name": "prices", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "numeric", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + } + } + ] + }, + "table": "things" + }, + { + "name": "ts", + "type": { + "name": "timestamp without time zone", + "nullable": true, + "args": [ + { + "int": 3 + } + ] + }, + "table": "things" + }, + { + "name": "tstz", + "type": { + "name": "timestamp with time zone" + }, + "table": "things" + }, + { + "name": "ttz", + "type": { + "name": "time with time zone", + "nullable": true + }, + "table": "things" + }, + { + "name": "iv", + "type": { + "name": "interval", + "nullable": true + }, + "table": "things" + }, + { + "name": "ivd", + "type": { + "name": "interval", + "nullable": true, + "args": [ + { + "ident": "day to second" + } + ] + }, + "table": "things" + }, + { + "name": "iv3", + "type": { + "name": "interval", + "nullable": true, + "args": [ + { + "int": 3 + } + ] + }, + "table": "things" + }, + { + "name": "m", + "type": { + "name": "mood", + "nullable": true + }, + "table": "things" + }, + { + "name": "mm", + "type": { + "name": "mood", + "nullable": true + }, + "table": "things" + }, + { + "name": "p", + "type": { + "name": "posint", + "nullable": true + }, + "table": "things" + }, + { + "name": "sn", + "type": { + "name": "shortname" + }, + "table": "things" + }, + { + "name": "pt", + "type": { + "name": "point2", + "nullable": true + }, + "table": "things" + }, + { + "name": "pts", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "point2" + } + } + ] + }, + "table": "things" + }, + { + "name": "fr", + "type": { + "name": "floatrange", + "nullable": true + }, + "table": "things" + }, + { + "name": "ir", + "type": { + "name": "int4range", + "nullable": true + }, + "table": "things" + }, + { + "name": "b", + "type": { + "name": "bit", + "nullable": true, + "args": [ + { + "int": 8 + } + ] + }, + "table": "things" + }, + { + "name": "vb", + "type": { + "name": "bit varying", + "nullable": true, + "args": [ + { + "int": 16 + } + ] + }, + "table": "things" + }, + { + "name": "js", + "type": { + "name": "jsonb", + "nullable": true + }, + "table": "things" + }, + { + "name": "u", + "type": { + "name": "uuid", + "nullable": true + }, + "table": "things" + }, + { + "name": "h", + "type": { + "name": "hstore", + "nullable": true + }, + "table": "things" + }, + { + "name": "moods", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "mood" + } + } + ] + }, + "table": "things" + }, + { + "name": "dp", + "type": { + "name": "double precision", + "nullable": true + }, + "table": "things" + }, + { + "name": "ts2", + "type": { + "name": "timestamp without time zone", + "nullable": true + }, + "table": "things" + } + ], + "params": [] + }, + { + "name": "Casts", + "cmd": ":one", + "columns": [ + { + "name": "a", + "type": { + "name": "numeric", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + } + }, + { + "name": "b", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + } + ] + } + }, + { + "name": "c", + "type": { + "name": "text" + } + }, + { + "name": "d", + "type": { + "name": "mood" + } + }, + { + "name": "e", + "type": { + "name": "character varying", + "args": [ + { + "int": 20 + } + ] + } + }, + { + "name": "f", + "type": { + "name": "posint" + } + }, + { + "name": "g", + "type": { + "name": "bigint" + } + }, + { + "name": "h", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + } + ] + } + } + ] + } + }, + { + "name": "i", + "type": { + "name": "numeric", + "args": [ + { + "int": 5 + }, + { + "int": 1 + } + ] + } + }, + { + "name": "j", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + } + ] + } + }, + { + "name": "k", + "type": { + "name": "mood" + } + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "", + "type": { + "name": "numeric", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + } + } + }, + { + "number": 2, + "column": { + "name": "", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + } + ] + } + } + }, + { + "number": 3, + "column": { + "name": "", + "type": { + "name": "mood" + } + } + }, + { + "number": 4, + "column": { + "name": "", + "type": { + "name": "character varying", + "args": [ + { + "int": 20 + } + ] + } + } + }, + { + "number": 5, + "column": { + "name": "", + "type": { + "name": "posint" + } + } + }, + { + "number": 6, + "column": { + "name": "", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + } + ] + } + } + ] + } + } + }, + { + "number": 7, + "column": { + "name": "", + "type": { + "name": "numeric", + "args": [ + { + "int": 5 + }, + { + "int": 1 + } + ] + } + } + }, + { + "number": 8, + "column": { + "name": "", + "type": { + "name": "mood" + } + } + } + ] + }, + { + "name": "Params", + "cmd": ":one", + "columns": [ + { + "name": "id", + "type": { + "name": "bigint" + }, + "table": "things" + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "price", + "type": { + "name": "numeric", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + }, + "table": "things" + } + }, + { + "number": 2, + "column": { + "name": "ints", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "integer" + } + } + ] + }, + "table": "things" + } + }, + { + "number": 3, + "column": { + "name": "m", + "type": { + "name": "mood", + "nullable": true + }, + "table": "things" + } + }, + { + "number": 4, + "column": { + "name": "p", + "type": { + "name": "posint", + "nullable": true + }, + "table": "things" + } + }, + { + "number": 5, + "column": { + "name": "title", + "type": { + "name": "character varying", + "nullable": true, + "args": [ + { + "int": 255 + } + ] + }, + "table": "things" + } + }, + { + "number": 6, + "column": { + "name": "grid", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + } + ] + } + } + ] + }, + "table": "things" + } + }, + { + "number": 7, + "column": { + "name": "sn", + "type": { + "name": "shortname" + }, + "table": "things" + } + }, + { + "number": 8, + "column": { + "name": "fr", + "type": { + "name": "floatrange", + "nullable": true + }, + "table": "things" + } + }, + { + "number": 9, + "column": { + "name": "ivd", + "type": { + "name": "interval", + "nullable": true, + "args": [ + { + "ident": "day to second" + } + ] + }, + "table": "things" + } + } + ] + } +] diff --git a/internal/engine/postgresql/parse.go b/internal/engine/postgresql/parse.go index 0de54e6eb3..58f7c3d431 100644 --- a/internal/engine/postgresql/parse.go +++ b/internal/engine/postgresql/parse.go @@ -346,7 +346,7 @@ func translate(node *nodes.Node) (ast.Node, error) { item.Subtype = ast.AT_AddColumn item.Def = &ast.ColumnDef{ Colname: d.ColumnDef.Colname, - TypeName: rel.TypeName(), + TypeName: columnTypeName(rel, d.ColumnDef.TypeName), IsNotNull: isNotNull(d.ColumnDef), IsArray: isArray(d.ColumnDef.TypeName), ArrayDims: len(d.ColumnDef.TypeName.ArrayBounds), @@ -372,7 +372,7 @@ func translate(node *nodes.Node) (ast.Node, error) { item.Subtype = ast.AT_AlterColumnType item.Def = &ast.ColumnDef{ Colname: col, - TypeName: rel.TypeName(), + TypeName: columnTypeName(rel, d.ColumnDef.TypeName), IsNotNull: isNotNull(d.ColumnDef), IsArray: isArray(d.ColumnDef.TypeName), ArrayDims: len(d.ColumnDef.TypeName.ArrayBounds), @@ -457,9 +457,27 @@ func translate(node *nodes.Node) (ast.Node, error) { case *nodes.Node_CompositeTypeStmt: n := inner.CompositeTypeStmt rel := parseRelationFromRangeVar(n.Typevar) - return &ast.CompositeTypeStmt{ - TypeName: rel.TypeName(), - }, nil + stmt := &ast.CompositeTypeStmt{ + TypeName: rel.TypeName(), + Coldeflist: &ast.List{}, + } + for _, node := range n.Coldeflist { + field, ok := node.Node.(*nodes.Node_ColumnDef) + if !ok { + continue + } + rel, err := parseRelationFromNodes(field.ColumnDef.TypeName.Names) + if err != nil { + return nil, err + } + stmt.Coldeflist.Items = append(stmt.Coldeflist.Items, &ast.ColumnDef{ + Colname: field.ColumnDef.Colname, + TypeName: columnTypeName(rel, field.ColumnDef.TypeName), + IsArray: isArray(field.ColumnDef.TypeName), + ArrayDims: len(field.ColumnDef.TypeName.ArrayBounds), + }) + } + return stmt, nil case *nodes.Node_CreateStmt: n := inner.CreateStmt @@ -510,7 +528,7 @@ func translate(node *nodes.Node) (ast.Node, error) { create.Cols = append(create.Cols, &ast.ColumnDef{ Colname: item.ColumnDef.Colname, - TypeName: rel.TypeName(), + TypeName: columnTypeName(rel, item.ColumnDef.TypeName), IsNotNull: isNotNull(item.ColumnDef) || primaryKey[item.ColumnDef.Colname], IsArray: isArray(item.ColumnDef.TypeName), ArrayDims: len(item.ColumnDef.TypeName.ArrayBounds), @@ -708,3 +726,73 @@ func translate(node *nodes.Node) (ast.Node, error) { return convert(node) } } + +// columnTypeName is a column's type as the catalog needs it: the name the +// relation resolves, with the type modifiers the parser reported. Every +// modifier is an integer constant, except that an interval's first one is a +// bit mask of the fields it keeps, which is decoded into the words +// format_type prints, so that "interval day to second" carries "day to +// second" as its first argument. +func columnTypeName(rel *relation, tn *nodes.TypeName) *ast.TypeName { + out := rel.TypeName() + if tn == nil || len(tn.Typmods) == 0 { + return out + } + out.Typmods = &ast.List{} + for i, mod := range tn.Typmods { + c, ok := mod.Node.(*nodes.Node_AConst) + if !ok { + continue + } + ival, ok := c.AConst.Val.(*nodes.A_Const_Ival) + if !ok { + continue + } + if i == 0 && rel.Name == "interval" { + fields, ok := intervalFields[ival.Ival.Ival] + if !ok { + continue + } + if fields != "" { + out.Typmods.Items = append(out.Typmods.Items, &ast.String{Str: fields}) + } + continue + } + out.Typmods.Items = append(out.Typmods.Items, &ast.A_Const{Val: &ast.Integer{Ival: int64(ival.Ival.Ival)}}) + } + if len(out.Typmods.Items) == 0 { + out.Typmods = nil + } + return out +} + +// intervalFields decodes the field mask an interval typmod starts with — +// INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH) and so on, with the field +// numbers PostgreSQL's datetime.h assigns — into the words a declaration +// spells. The full range is written as nothing. +var intervalFields = func() map[int32]string { + const ( + month = 1 << 1 + year = 1 << 2 + day = 1 << 3 + hour = 1 << 10 + minute = 1 << 11 + second = 1 << 12 + ) + return map[int32]string{ + 0x7FFF: "", + year: "year", + month: "month", + day: "day", + hour: "hour", + minute: "minute", + second: "second", + year | month: "year to month", + day | hour: "day to hour", + day | hour | minute: "day to minute", + day | hour | minute | second: "day to second", + hour | minute: "hour to minute", + hour | minute | second: "hour to second", + minute | second: "minute to second", + } +}() diff --git a/internal/engine/postgresql/seed.go b/internal/engine/postgresql/seed.go index 9cf184c50d..a159563b3a 100644 --- a/internal/engine/postgresql/seed.go +++ b/internal/engine/postgresql/seed.go @@ -2,6 +2,7 @@ package postgresql import ( "embed" + "strings" "sync" "github.com/sqlc-dev/sqlc/internal/core" @@ -31,6 +32,20 @@ func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } +func init() { + core.RegisterCanonicalizer("postgresql", canonicalize) +} + +// canonicalize rewrites what pg_type spells differently from format_type: +// an array type's own name is its element's with an underscore in front, +// which is how the system catalogs' columns are seeded. +func canonicalize(t *core.TypeExpr) *core.TypeExpr { + if element, ok := strings.CutPrefix(t.Name, "_"); ok && len(t.Args) == 0 && element != "" { + return core.Array(&core.TypeExpr{Name: element, Nullable: t.Nullable}) + } + return t +} + // pgCatalogFuncs is pg_catalog's functions in the form the catalog uses. The // list runs to thousands of entries and never changes within a run, so it is // read once. diff --git a/internal/sql/ast/composite_type_stmt.go b/internal/sql/ast/composite_type_stmt.go index eab6f7f4cc..a7f27a8b54 100644 --- a/internal/sql/ast/composite_type_stmt.go +++ b/internal/sql/ast/composite_type_stmt.go @@ -4,6 +4,8 @@ type CompositeTypeStmt struct { Tag NodeTag[CompositeTypeStmt] `json:"tag"` TypeName *TypeName `json:"type_name,omitempty"` + // Coldeflist is the type's fields, each a ColumnDef. + Coldeflist *List `json:"coldeflist,omitempty"` } func (n *CompositeTypeStmt) Pos() int { diff --git a/internal/sql/ast/constr_type.go b/internal/sql/ast/constr_type.go index d84e4d8c4a..c058682672 100644 --- a/internal/sql/ast/constr_type.go +++ b/internal/sql/ast/constr_type.go @@ -5,3 +5,10 @@ type ConstrType uint func (n *ConstrType) Pos() int { return 0 } + +// The constraint kinds the analysis reads, numbered as PostgreSQL's parser +// numbers them, which is what an engine's converter records. +const ( + ConstrTypeNull ConstrType = 1 + ConstrTypeNotNull ConstrType = 2 +) From 48a9f7a804da60f85542abfbefceb7628e395d89 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 17:36:53 +0000 Subject: [PATCH 07/16] mysql: unsigned families, typmods, enum and set members, and the types MySQL reports for casts A column's UNSIGNED reaches the core as part of its family, which the seed now lists for every numeric type rather than as an alias of the signed one, so codegen picks uint64 again on the core path. The converter carries the width of tinyint(1) and bit(n), a fractional-seconds precision and the members of an enum or set, and names a cast's type as MySQL types its result: varchar for CAST AS CHAR rather than the parser's var_string. A canonicalizer fills in decimal(10,0), folds float(p) and spells boolean as tinyint(1), as MySQL does. goldeneye reads a table column's whole type from COLUMN_TYPE, in the relations seed and in the analyze check, and compares an expression by family alone, since the wire carries no more. A cast is NULL when its operand is, in every dialect. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- internal/core/analyzer/expr.go | 14 +- internal/core/seed/seed.go | 9 +- internal/core/typeexpr.go | 43 +- internal/core/typename.go | 13 + .../analyze_system_catalog/mysql/stdout.json | 34 +- .../testdata/analyze_types/mysql/exec.json | 5 + .../testdata/analyze_types/mysql/query.sql | 18 + .../testdata/analyze_types/mysql/schema.sql | 28 + .../testdata/analyze_types/mysql/stdout.json | 550 ++++++++++++++++++ internal/engine/dolphin/convert.go | 57 +- .../engine/dolphin/dialect/relations.jsonl | 152 ++--- internal/engine/dolphin/dialect/types.jsonl | 24 +- internal/engine/dolphin/seed.go | 28 + internal/goldeneye/mysql/analyze.go | 109 +++- internal/goldeneye/mysql/relations.go | 20 +- 15 files changed, 991 insertions(+), 113 deletions(-) create mode 100644 internal/endtoend/testdata/analyze_types/mysql/exec.json create mode 100644 internal/endtoend/testdata/analyze_types/mysql/query.sql create mode 100644 internal/endtoend/testdata/analyze_types/mysql/schema.sql create mode 100644 internal/endtoend/testdata/analyze_types/mysql/stdout.json diff --git a/internal/core/analyzer/expr.go b/internal/core/analyzer/expr.go index a82e3bd55a..7d9ed857ea 100644 --- a/internal/core/analyzer/expr.go +++ b/internal/core/analyzer/expr.go @@ -932,9 +932,19 @@ func (a *analyzer) typeTypeCast(c *ast.TypeCast) (exprType, error) { return exprType{}, fmt.Errorf("cast: missing target type") } t := a.lookupType(target) - // A cast is how a query says what an otherwise untyped placeholder holds. - if err := a.typeOperands(c.Arg, t); err != nil { + // A cast is how a query says what an otherwise untyped placeholder + // holds, and a placeholder so typed is not null. Anything else cast + // is NULL exactly when it was NULL before. + if pr, ok := c.Arg.(*ast.ParamRef); ok { + if err := a.typeOperands(pr, t); err != nil { + return exprType{}, err + } + return t, nil + } + arg, err := a.typeExpr(c.Arg) + if err != nil { return exprType{}, err } + t.nullable = arg.nullable return t, nil } diff --git a/internal/core/seed/seed.go b/internal/core/seed/seed.go index 5339732dac..1d6710d6e2 100644 --- a/internal/core/seed/seed.go +++ b/internal/core/seed/seed.go @@ -388,15 +388,22 @@ func Relations(fsys fs.FS, dir, schema string) ([]*catalog.Table, error) { Columns: make([]*catalog.Column, 0, len(rel.Columns)), } for _, col := range rel.Columns { + // A column's type may carry arguments, as MySQL's varchar(64) + // does; the legacy catalog holds the family and the length + // apart. + t := core.ParseTypeExpr(col.Type) column := &catalog.Column{ Name: col.Name, - Type: ast.TypeName{Name: col.Type}, + Type: ast.TypeName{Name: t.Name}, IsNotNull: col.NotNull, IsArray: col.Array, } if col.Length > 0 { length := col.Length column.Length = &length + } else if len(t.Args) > 0 && t.Args[0].Int != nil { + length := int(*t.Args[0].Int) + column.Length = &length } table.Columns = append(table.Columns, column) } diff --git a/internal/core/typeexpr.go b/internal/core/typeexpr.go index f3cf97e0ed..83cd719e84 100644 --- a/internal/core/typeexpr.go +++ b/internal/core/typeexpr.go @@ -187,14 +187,23 @@ func quotedEnd(s string) int { } // splitTypeArgs splits `Base(arg, arg)` into its base name and top-level -// arguments, leaving nested parentheses and quoted strings intact. +// arguments, leaving nested parentheses and quoted strings intact. Words +// after the closing parenthesis belong to the name, since MySQL writes +// decimal(10,2) unsigned and PostgreSQL timestamp(3) with time zone. func splitTypeArgs(t string) (string, []string) { open := strings.IndexByte(t, '(') - if open < 0 || !strings.HasSuffix(t, ")") { + if open < 0 { + return t, nil + } + close := matchingParen(t, open) + if close < 0 { return t, nil } base := strings.TrimSpace(t[:open]) - inner := t[open+1 : len(t)-1] + if rest := strings.TrimSpace(t[close+1:]); rest != "" { + base += " " + rest + } + inner := t[open+1 : close] var ( args []string depth int @@ -227,6 +236,34 @@ func splitTypeArgs(t string) (string, []string) { return base, args } +// matchingParen finds the parenthesis closing the one at open, skipping +// nested parentheses and quoted strings, or -1 when it is not closed. +func matchingParen(t string, open int) int { + depth := 0 + var quote byte + for i := open; i < len(t); i++ { + c := t[i] + switch { + case quote != 0: + if c == '\\' { + i++ + } else if c == quote { + quote = 0 + } + case c == '\'' || c == '"' || c == '`': + quote = c + case c == '(': + depth++ + case c == ')': + depth-- + if depth == 0 { + return i + } + } + } + return -1 +} + // HasNullable reports whether the expression marks nullability anywhere, // which tells whether the spelling it came from said so itself. func (t *TypeExpr) HasNullable() bool { diff --git a/internal/core/typename.go b/internal/core/typename.go index 81026b6944..d60257c5a2 100644 --- a/internal/core/typename.go +++ b/internal/core/typename.go @@ -61,6 +61,19 @@ func ColumnTypeExpr(col *ast.ColumnDef) *TypeExpr { if t == nil { return nil } + // MySQL reports an unsigned column on the definition, and the + // members of an enum or set apart from the type's name. + if col.IsUnsigned && !strings.Contains(t.Name, " unsigned") { + t.Name += " unsigned" + } + if vals := listItems(col.Vals); len(vals) > 0 && len(t.Args) == 0 { + for _, item := range vals { + if s, ok := item.(*ast.String); ok { + v := s.Str + t.Args = append(t.Args, TypeArg{String: &v}) + } + } + } if col.TypeName.Spelling != "" || listItems(col.TypeName.ArrayBounds) != nil { return t } diff --git a/internal/endtoend/testdata/analyze_system_catalog/mysql/stdout.json b/internal/endtoend/testdata/analyze_system_catalog/mysql/stdout.json index cd3116fa44..661011fd43 100644 --- a/internal/endtoend/testdata/analyze_system_catalog/mysql/stdout.json +++ b/internal/endtoend/testdata/analyze_system_catalog/mysql/stdout.json @@ -6,7 +6,12 @@ { "name": "table_name", "type": { - "name": "varchar" + "name": "varchar", + "args": [ + { + "int": 64 + } + ] }, "table": "columns" }, @@ -14,7 +19,12 @@ "name": "column_name", "type": { "name": "varchar", - "nullable": true + "nullable": true, + "args": [ + { + "int": 64 + } + ] }, "table": "columns" }, @@ -33,7 +43,12 @@ "column": { "name": "table_schema", "type": { - "name": "varchar" + "name": "varchar", + "args": [ + { + "int": 64 + } + ] }, "table": "columns" } @@ -57,7 +72,18 @@ "column": { "name": "table_type", "type": { - "name": "enum" + "name": "enum", + "args": [ + { + "string": "base table" + }, + { + "string": "view" + }, + { + "string": "system view" + } + ] }, "table": "tables" } diff --git a/internal/endtoend/testdata/analyze_types/mysql/exec.json b/internal/endtoend/testdata/analyze_types/mysql/exec.json new file mode 100644 index 0000000000..a5b24d3361 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/mysql/exec.json @@ -0,0 +1,5 @@ +{ + "command": "analyze", + "args": ["--dialect", "mysql", "--schema", "schema.sql", "query.sql"], + "contexts": ["base"] +} diff --git a/internal/endtoend/testdata/analyze_types/mysql/query.sql b/internal/endtoend/testdata/analyze_types/mysql/query.sql new file mode 100644 index 0000000000..4f945a8163 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/mysql/query.sql @@ -0,0 +1,18 @@ +-- name: AllTypes :many +SELECT * FROM things; + +-- name: Casts :one +SELECT + CAST(count AS UNSIGNED) AS a, + CAST(price AS DECIMAL(5,2)) AS b, + CAST(title AS CHAR(10)) AS c, + CAST(count AS SIGNED) AS d, + CAST(doc AS JSON) AS e, + CAST(created AS DATETIME(3)) AS f, + CAST(price AS DECIMAL) AS g, + CAST(key16 AS BINARY(8)) AS h +FROM things; + +-- name: Params :one +SELECT id FROM things +WHERE count = ? AND price = ? AND kind = ? AND flags = ? AND title = ? AND uprice = ? AND flag = ? AND created = ?; diff --git a/internal/endtoend/testdata/analyze_types/mysql/schema.sql b/internal/endtoend/testdata/analyze_types/mysql/schema.sql new file mode 100644 index 0000000000..96057f5995 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/mysql/schema.sql @@ -0,0 +1,28 @@ +CREATE TABLE things ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + flag TINYINT(1), + count INT UNSIGNED NOT NULL, + price DECIMAL(10,2) NOT NULL, + uprice DECIMAL(10,2) UNSIGNED, + plain DECIMAL, + ratio FLOAT(7,4), + f FLOAT, + title VARCHAR(255), + code CHAR(3), + kind ENUM('a','b'), + flags SET('x','y'), + doc JSON, + key16 BINARY(16), + blob255 VARBINARY(255), + created DATETIME(6), + updated TIMESTAMP(3), + y YEAR, + bits BIT(8), + body TEXT, + bin VARCHAR(10) CHARACTER SET binary, + geo GEOMETRY, + ok BOOLEAN, + d DOUBLE, + small MEDIUMINT UNSIGNED, + i INT +); diff --git a/internal/endtoend/testdata/analyze_types/mysql/stdout.json b/internal/endtoend/testdata/analyze_types/mysql/stdout.json new file mode 100644 index 0000000000..894e6cc4ec --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/mysql/stdout.json @@ -0,0 +1,550 @@ +[ + { + "name": "AllTypes", + "cmd": ":many", + "columns": [ + { + "name": "id", + "type": { + "name": "bigint unsigned" + }, + "table": "things" + }, + { + "name": "flag", + "type": { + "name": "tinyint", + "nullable": true, + "args": [ + { + "int": 1 + } + ] + }, + "table": "things" + }, + { + "name": "count", + "type": { + "name": "int unsigned" + }, + "table": "things" + }, + { + "name": "price", + "type": { + "name": "decimal", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + }, + "table": "things" + }, + { + "name": "uprice", + "type": { + "name": "decimal unsigned", + "nullable": true, + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + }, + "table": "things" + }, + { + "name": "plain", + "type": { + "name": "decimal", + "nullable": true, + "args": [ + { + "int": 10 + }, + { + "int": 0 + } + ] + }, + "table": "things" + }, + { + "name": "ratio", + "type": { + "name": "float", + "nullable": true, + "args": [ + { + "int": 7 + }, + { + "int": 4 + } + ] + }, + "table": "things" + }, + { + "name": "f", + "type": { + "name": "float", + "nullable": true + }, + "table": "things" + }, + { + "name": "title", + "type": { + "name": "varchar", + "nullable": true, + "args": [ + { + "int": 255 + } + ] + }, + "table": "things" + }, + { + "name": "code", + "type": { + "name": "char", + "nullable": true, + "args": [ + { + "int": 3 + } + ] + }, + "table": "things" + }, + { + "name": "kind", + "type": { + "name": "enum", + "nullable": true, + "args": [ + { + "string": "a" + }, + { + "string": "b" + } + ] + }, + "table": "things" + }, + { + "name": "flags", + "type": { + "name": "set", + "nullable": true, + "args": [ + { + "string": "x" + }, + { + "string": "y" + } + ] + }, + "table": "things" + }, + { + "name": "doc", + "type": { + "name": "json", + "nullable": true + }, + "table": "things" + }, + { + "name": "key16", + "type": { + "name": "binary", + "nullable": true, + "args": [ + { + "int": 16 + } + ] + }, + "table": "things" + }, + { + "name": "blob255", + "type": { + "name": "varbinary", + "nullable": true, + "args": [ + { + "int": 255 + } + ] + }, + "table": "things" + }, + { + "name": "created", + "type": { + "name": "datetime", + "nullable": true, + "args": [ + { + "int": 6 + } + ] + }, + "table": "things" + }, + { + "name": "updated", + "type": { + "name": "timestamp", + "nullable": true, + "args": [ + { + "int": 3 + } + ] + }, + "table": "things" + }, + { + "name": "y", + "type": { + "name": "year", + "nullable": true + }, + "table": "things" + }, + { + "name": "bits", + "type": { + "name": "bit", + "nullable": true, + "args": [ + { + "int": 8 + } + ] + }, + "table": "things" + }, + { + "name": "body", + "type": { + "name": "text", + "nullable": true + }, + "table": "things" + }, + { + "name": "bin", + "type": { + "name": "varbinary", + "nullable": true, + "args": [ + { + "int": 10 + } + ] + }, + "table": "things" + }, + { + "name": "geo", + "type": { + "name": "geometry", + "nullable": true + }, + "table": "things" + }, + { + "name": "ok", + "type": { + "name": "tinyint", + "nullable": true, + "args": [ + { + "int": 1 + } + ] + }, + "table": "things" + }, + { + "name": "d", + "type": { + "name": "double", + "nullable": true + }, + "table": "things" + }, + { + "name": "small", + "type": { + "name": "mediumint unsigned", + "nullable": true + }, + "table": "things" + }, + { + "name": "i", + "type": { + "name": "int", + "nullable": true + }, + "table": "things" + } + ], + "params": [] + }, + { + "name": "Casts", + "cmd": ":one", + "columns": [ + { + "name": "a", + "type": { + "name": "bigint unsigned" + } + }, + { + "name": "b", + "type": { + "name": "decimal", + "args": [ + { + "int": 5 + }, + { + "int": 2 + } + ] + } + }, + { + "name": "c", + "type": { + "name": "varchar", + "nullable": true, + "args": [ + { + "int": 10 + } + ] + } + }, + { + "name": "d", + "type": { + "name": "bigint" + } + }, + { + "name": "e", + "type": { + "name": "json", + "nullable": true + } + }, + { + "name": "f", + "type": { + "name": "datetime", + "nullable": true, + "args": [ + { + "int": 3 + } + ] + } + }, + { + "name": "g", + "type": { + "name": "decimal", + "args": [ + { + "int": 10 + }, + { + "int": 0 + } + ] + } + }, + { + "name": "h", + "type": { + "name": "varbinary", + "nullable": true, + "args": [ + { + "int": 8 + } + ] + } + } + ], + "params": [] + }, + { + "name": "Params", + "cmd": ":one", + "columns": [ + { + "name": "id", + "type": { + "name": "bigint unsigned" + }, + "table": "things" + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "count", + "type": { + "name": "int unsigned" + }, + "table": "things" + } + }, + { + "number": 2, + "column": { + "name": "price", + "type": { + "name": "decimal", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + }, + "table": "things" + } + }, + { + "number": 3, + "column": { + "name": "kind", + "type": { + "name": "enum", + "nullable": true, + "args": [ + { + "string": "a" + }, + { + "string": "b" + } + ] + }, + "table": "things" + } + }, + { + "number": 4, + "column": { + "name": "flags", + "type": { + "name": "set", + "nullable": true, + "args": [ + { + "string": "x" + }, + { + "string": "y" + } + ] + }, + "table": "things" + } + }, + { + "number": 5, + "column": { + "name": "title", + "type": { + "name": "varchar", + "nullable": true, + "args": [ + { + "int": 255 + } + ] + }, + "table": "things" + } + }, + { + "number": 6, + "column": { + "name": "uprice", + "type": { + "name": "decimal unsigned", + "nullable": true, + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + }, + "table": "things" + } + }, + { + "number": 7, + "column": { + "name": "flag", + "type": { + "name": "tinyint", + "nullable": true, + "args": [ + { + "int": 1 + } + ] + }, + "table": "things" + } + }, + { + "number": 8, + "column": { + "name": "created", + "type": { + "name": "datetime", + "nullable": true, + "args": [ + { + "int": 6 + } + ] + }, + "table": "things" + } + } + ] + } +] diff --git a/internal/engine/dolphin/convert.go b/internal/engine/dolphin/convert.go index c52fb7e2ef..e834f86f78 100644 --- a/internal/engine/dolphin/convert.go +++ b/internal/engine/dolphin/convert.go @@ -317,6 +317,24 @@ func convertColumnDef(def *pcast.ColumnDef) *ast.ColumnDef { } typeName.Typmods = &ast.List{Items: mods} } + // MySQL drops an integer's display width except the one that means + // something: tinyint(1) is what drivers and codegen read as a boolean, + // and BOOLEAN itself is spelled that way. + case mysql.TypeTiny: + if flen == 1 { + typeName.Typmods = &ast.List{Items: []ast.Node{&ast.Integer{Ival: 1}}} + } + // A bit column's width is part of its type, and is 1 when left out. + case mysql.TypeBit: + if flen >= 0 { + typeName.Typmods = &ast.List{Items: []ast.Node{&ast.Integer{Ival: int64(flen)}}} + } + // A fractional-seconds precision is part of the type; zero is the + // default and is not written. + case mysql.TypeDatetime, mysql.TypeTimestamp, mysql.TypeDuration: + if fsp := def.Tp.GetDecimal(); fsp > 0 { + typeName.Typmods = &ast.List{Items: []ast.Node{&ast.Integer{Ival: int64(fsp)}}} + } } columnDef := ast.ColumnDef{ @@ -1112,7 +1130,8 @@ func (c *cc) convertFrameClause(n *pcast.FrameClause) ast.Node { } func (c *cc) convertFuncCastExpr(n *pcast.FuncCastExpr) ast.Node { - typeName := types.TypeStr(n.Tp.GetType()) + tp := n.Tp.GetType() + typeName := types.TypeToStr(tp, n.Tp.GetCharset()) // MySQL CAST AS UNSIGNED/SIGNED uses bigint internally. // We need to preserve the signed/unsigned info for formatting. @@ -1123,10 +1142,44 @@ func (c *cc) convertFuncCastExpr(n *pcast.FuncCastExpr) ast.Node { typeName = "bigint signed" } } + // CAST(x AS CHAR) and CAST(x AS BINARY) are typed by the parser as the + // wire's var_string, which is no SQL type, or as a char or binary. + // MySQL types the result as a varchar or a varbinary — its metadata + // and a view over it both say so. + switch typeName { + case "var_string", "char": + typeName = "varchar" + if n.Tp.GetCharset() == "binary" { + typeName = "varbinary" + } + case "binary": + typeName = "varbinary" + } + + out := &ast.TypeName{Name: typeName} + flen, dec := n.Tp.GetFlen(), n.Tp.GetDecimal() + switch tp { + case mysql.TypeNewDecimal, mysql.TypeFloat, mysql.TypeDouble: + if flen >= 0 && flen != types.UnspecifiedLength { + mods := []ast.Node{&ast.Integer{Ival: int64(flen)}} + if dec > 0 && dec != types.UnspecifiedLength { + mods = append(mods, &ast.Integer{Ival: int64(dec)}) + } + out.Typmods = &ast.List{Items: mods} + } + case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeString: + if flen > 0 && flen != types.UnspecifiedLength { + out.Typmods = &ast.List{Items: []ast.Node{&ast.Integer{Ival: int64(flen)}}} + } + case mysql.TypeDatetime, mysql.TypeTimestamp, mysql.TypeDuration: + if dec > 0 && dec != types.UnspecifiedLength { + out.Typmods = &ast.List{Items: []ast.Node{&ast.Integer{Ival: int64(dec)}}} + } + } return &ast.TypeCast{ Arg: c.convert(n.Expr), - TypeName: &ast.TypeName{Name: typeName}, + TypeName: out, } } diff --git a/internal/engine/dolphin/dialect/relations.jsonl b/internal/engine/dolphin/dialect/relations.jsonl index ea3c86953f..8a0e664032 100644 --- a/internal/engine/dolphin/dialect/relations.jsonl +++ b/internal/engine/dolphin/dialect/relations.jsonl @@ -1,84 +1,84 @@ -{"catalog":"def","schema":"information_schema","name":"administrable_role_authorizations","kind":"v","columns":[{"name":"user","type":"varchar"},{"name":"host","type":"varchar"},{"name":"grantee","type":"varchar"},{"name":"grantee_host","type":"varchar"},{"name":"role_name","type":"varchar"},{"name":"role_host","type":"varchar"},{"name":"is_grantable","type":"varchar","not_null":true},{"name":"is_default","type":"varchar"},{"name":"is_mandatory","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"applicable_roles","kind":"v","columns":[{"name":"user","type":"varchar"},{"name":"host","type":"varchar"},{"name":"grantee","type":"varchar"},{"name":"grantee_host","type":"varchar"},{"name":"role_name","type":"varchar"},{"name":"role_host","type":"varchar"},{"name":"is_grantable","type":"varchar","not_null":true},{"name":"is_default","type":"varchar"},{"name":"is_mandatory","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"character_sets","kind":"v","columns":[{"name":"character_set_name","type":"varchar","not_null":true},{"name":"default_collate_name","type":"varchar","not_null":true},{"name":"description","type":"varchar","not_null":true},{"name":"maxlen","type":"int unsigned","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"check_constraints","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar","not_null":true},{"name":"constraint_schema","type":"varchar","not_null":true},{"name":"constraint_name","type":"varchar","not_null":true},{"name":"check_clause","type":"longtext","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"collations","kind":"v","columns":[{"name":"collation_name","type":"varchar","not_null":true},{"name":"character_set_name","type":"varchar","not_null":true},{"name":"id","type":"bigint unsigned","not_null":true},{"name":"is_default","type":"varchar","not_null":true},{"name":"is_compiled","type":"varchar","not_null":true},{"name":"sortlen","type":"int unsigned","not_null":true},{"name":"pad_attribute","type":"enum","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"collation_character_set_applicability","kind":"v","columns":[{"name":"collation_name","type":"varchar","not_null":true},{"name":"character_set_name","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"columns","kind":"v","columns":[{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"column_name","type":"varchar"},{"name":"ordinal_position","type":"int unsigned","not_null":true},{"name":"column_default","type":"text"},{"name":"is_nullable","type":"varchar","not_null":true},{"name":"data_type","type":"longtext"},{"name":"character_maximum_length","type":"bigint"},{"name":"character_octet_length","type":"bigint"},{"name":"numeric_precision","type":"bigint unsigned"},{"name":"numeric_scale","type":"bigint unsigned"},{"name":"datetime_precision","type":"int unsigned"},{"name":"character_set_name","type":"varchar"},{"name":"collation_name","type":"varchar"},{"name":"column_type","type":"mediumtext","not_null":true},{"name":"column_key","type":"enum","not_null":true},{"name":"extra","type":"varchar"},{"name":"privileges","type":"varchar"},{"name":"column_comment","type":"text","not_null":true},{"name":"generation_expression","type":"longtext","not_null":true},{"name":"srs_id","type":"int unsigned"}]} -{"catalog":"def","schema":"information_schema","name":"columns_extensions","kind":"v","columns":[{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"column_name","type":"varchar"},{"name":"engine_attribute","type":"json"},{"name":"secondary_engine_attribute","type":"json"}]} -{"catalog":"def","schema":"information_schema","name":"column_privileges","kind":"v","columns":[{"name":"grantee","type":"varchar","not_null":true},{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"column_name","type":"varchar","not_null":true},{"name":"privilege_type","type":"varchar","not_null":true},{"name":"is_grantable","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"column_statistics","kind":"v","columns":[{"name":"schema_name","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"column_name","type":"varchar","not_null":true},{"name":"histogram","type":"json","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"enabled_roles","kind":"v","columns":[{"name":"role_name","type":"varchar"},{"name":"role_host","type":"varchar"},{"name":"is_default","type":"varchar"},{"name":"is_mandatory","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"engines","kind":"v","columns":[{"name":"engine","type":"varchar","not_null":true},{"name":"support","type":"varchar","not_null":true},{"name":"comment","type":"varchar","not_null":true},{"name":"transactions","type":"varchar"},{"name":"xa","type":"varchar"},{"name":"savepoints","type":"varchar"}]} -{"catalog":"def","schema":"information_schema","name":"events","kind":"v","columns":[{"name":"event_catalog","type":"varchar","not_null":true},{"name":"event_schema","type":"varchar","not_null":true},{"name":"event_name","type":"varchar","not_null":true},{"name":"definer","type":"varchar","not_null":true},{"name":"time_zone","type":"varchar","not_null":true},{"name":"event_body","type":"varchar","not_null":true},{"name":"event_definition","type":"longtext","not_null":true},{"name":"event_type","type":"varchar","not_null":true},{"name":"execute_at","type":"datetime"},{"name":"interval_value","type":"varchar"},{"name":"interval_field","type":"enum"},{"name":"sql_mode","type":"set","not_null":true},{"name":"starts","type":"datetime"},{"name":"ends","type":"datetime"},{"name":"status","type":"varchar","not_null":true},{"name":"on_completion","type":"varchar","not_null":true},{"name":"created","type":"timestamp","not_null":true},{"name":"last_altered","type":"timestamp","not_null":true},{"name":"last_executed","type":"datetime"},{"name":"event_comment","type":"varchar","not_null":true},{"name":"originator","type":"int unsigned","not_null":true},{"name":"character_set_client","type":"varchar","not_null":true},{"name":"collation_connection","type":"varchar","not_null":true},{"name":"database_collation","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"files","kind":"v","columns":[{"name":"file_id","type":"bigint"},{"name":"file_name","type":"text"},{"name":"file_type","type":"varchar"},{"name":"tablespace_name","type":"varchar","not_null":true},{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varbinary"},{"name":"table_name","type":"varbinary"},{"name":"logfile_group_name","type":"varchar"},{"name":"logfile_group_number","type":"bigint"},{"name":"engine","type":"varchar","not_null":true},{"name":"fulltext_keys","type":"varbinary"},{"name":"deleted_rows","type":"varbinary"},{"name":"update_count","type":"varbinary"},{"name":"free_extents","type":"bigint"},{"name":"total_extents","type":"bigint"},{"name":"extent_size","type":"bigint"},{"name":"initial_size","type":"bigint"},{"name":"maximum_size","type":"bigint"},{"name":"autoextend_size","type":"bigint"},{"name":"creation_time","type":"varbinary"},{"name":"last_update_time","type":"varbinary"},{"name":"last_access_time","type":"varbinary"},{"name":"recover_time","type":"varbinary"},{"name":"transaction_counter","type":"varbinary"},{"name":"version","type":"bigint"},{"name":"row_format","type":"varchar"},{"name":"table_rows","type":"varbinary"},{"name":"avg_row_length","type":"varbinary"},{"name":"data_length","type":"varbinary"},{"name":"max_data_length","type":"varbinary"},{"name":"index_length","type":"varbinary"},{"name":"data_free","type":"bigint"},{"name":"create_time","type":"varbinary"},{"name":"update_time","type":"varbinary"},{"name":"check_time","type":"varbinary"},{"name":"checksum","type":"varbinary"},{"name":"status","type":"varchar"},{"name":"extra","type":"varchar"}]} -{"catalog":"def","schema":"information_schema","name":"innodb_buffer_page","kind":"v","columns":[{"name":"pool_id","type":"bigint unsigned","not_null":true},{"name":"block_id","type":"bigint unsigned","not_null":true},{"name":"space","type":"bigint unsigned","not_null":true},{"name":"page_number","type":"bigint unsigned","not_null":true},{"name":"page_type","type":"varchar"},{"name":"flush_type","type":"bigint unsigned","not_null":true},{"name":"fix_count","type":"bigint unsigned","not_null":true},{"name":"is_hashed","type":"varchar"},{"name":"newest_modification","type":"bigint unsigned","not_null":true},{"name":"oldest_modification","type":"bigint unsigned","not_null":true},{"name":"access_time","type":"bigint unsigned","not_null":true},{"name":"table_name","type":"varchar"},{"name":"index_name","type":"varchar"},{"name":"number_records","type":"bigint unsigned","not_null":true},{"name":"data_size","type":"bigint unsigned","not_null":true},{"name":"compressed_size","type":"bigint unsigned","not_null":true},{"name":"page_state","type":"varchar"},{"name":"io_fix","type":"varchar"},{"name":"is_old","type":"varchar"},{"name":"free_page_clock","type":"bigint unsigned","not_null":true},{"name":"is_stale","type":"varchar"}]} -{"catalog":"def","schema":"information_schema","name":"innodb_buffer_page_lru","kind":"v","columns":[{"name":"pool_id","type":"bigint unsigned","not_null":true},{"name":"lru_position","type":"bigint unsigned","not_null":true},{"name":"space","type":"bigint unsigned","not_null":true},{"name":"page_number","type":"bigint unsigned","not_null":true},{"name":"page_type","type":"varchar"},{"name":"flush_type","type":"bigint unsigned","not_null":true},{"name":"fix_count","type":"bigint unsigned","not_null":true},{"name":"is_hashed","type":"varchar"},{"name":"newest_modification","type":"bigint unsigned","not_null":true},{"name":"oldest_modification","type":"bigint unsigned","not_null":true},{"name":"access_time","type":"bigint unsigned","not_null":true},{"name":"table_name","type":"varchar"},{"name":"index_name","type":"varchar"},{"name":"number_records","type":"bigint unsigned","not_null":true},{"name":"data_size","type":"bigint unsigned","not_null":true},{"name":"compressed_size","type":"bigint unsigned","not_null":true},{"name":"compressed","type":"varchar"},{"name":"io_fix","type":"varchar"},{"name":"is_old","type":"varchar"},{"name":"free_page_clock","type":"bigint unsigned","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_buffer_pool_stats","kind":"v","columns":[{"name":"pool_id","type":"bigint unsigned","not_null":true},{"name":"pool_size","type":"bigint unsigned","not_null":true},{"name":"free_buffers","type":"bigint unsigned","not_null":true},{"name":"database_pages","type":"bigint unsigned","not_null":true},{"name":"old_database_pages","type":"bigint unsigned","not_null":true},{"name":"modified_database_pages","type":"bigint unsigned","not_null":true},{"name":"pending_decompress","type":"bigint unsigned","not_null":true},{"name":"pending_reads","type":"bigint unsigned","not_null":true},{"name":"pending_flush_lru","type":"bigint unsigned","not_null":true},{"name":"pending_flush_list","type":"bigint unsigned","not_null":true},{"name":"pages_made_young","type":"bigint unsigned","not_null":true},{"name":"pages_not_made_young","type":"bigint unsigned","not_null":true},{"name":"pages_made_young_rate","type":"float","not_null":true},{"name":"pages_made_not_young_rate","type":"float","not_null":true},{"name":"number_pages_read","type":"bigint unsigned","not_null":true},{"name":"number_pages_created","type":"bigint unsigned","not_null":true},{"name":"number_pages_written","type":"bigint unsigned","not_null":true},{"name":"pages_read_rate","type":"float","not_null":true},{"name":"pages_create_rate","type":"float","not_null":true},{"name":"pages_written_rate","type":"float","not_null":true},{"name":"number_pages_get","type":"bigint unsigned","not_null":true},{"name":"hit_rate","type":"bigint unsigned","not_null":true},{"name":"young_make_per_thousand_gets","type":"bigint unsigned","not_null":true},{"name":"not_young_make_per_thousand_gets","type":"bigint unsigned","not_null":true},{"name":"number_pages_read_ahead","type":"bigint unsigned","not_null":true},{"name":"number_read_ahead_evicted","type":"bigint unsigned","not_null":true},{"name":"read_ahead_rate","type":"float","not_null":true},{"name":"read_ahead_evicted_rate","type":"float","not_null":true},{"name":"lru_io_total","type":"bigint unsigned","not_null":true},{"name":"lru_io_current","type":"bigint unsigned","not_null":true},{"name":"uncompress_total","type":"bigint unsigned","not_null":true},{"name":"uncompress_current","type":"bigint unsigned","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"administrable_role_authorizations","kind":"v","columns":[{"name":"user","type":"varchar(97)"},{"name":"host","type":"varchar(256)"},{"name":"grantee","type":"varchar(97)"},{"name":"grantee_host","type":"varchar(256)"},{"name":"role_name","type":"varchar(255)"},{"name":"role_host","type":"varchar(256)"},{"name":"is_grantable","type":"varchar(3)","not_null":true},{"name":"is_default","type":"varchar(3)"},{"name":"is_mandatory","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"applicable_roles","kind":"v","columns":[{"name":"user","type":"varchar(97)"},{"name":"host","type":"varchar(256)"},{"name":"grantee","type":"varchar(97)"},{"name":"grantee_host","type":"varchar(256)"},{"name":"role_name","type":"varchar(255)"},{"name":"role_host","type":"varchar(256)"},{"name":"is_grantable","type":"varchar(3)","not_null":true},{"name":"is_default","type":"varchar(3)"},{"name":"is_mandatory","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"character_sets","kind":"v","columns":[{"name":"character_set_name","type":"varchar(64)","not_null":true},{"name":"default_collate_name","type":"varchar(64)","not_null":true},{"name":"description","type":"varchar(2048)","not_null":true},{"name":"maxlen","type":"int unsigned","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"check_constraints","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar(64)","not_null":true},{"name":"constraint_schema","type":"varchar(64)","not_null":true},{"name":"constraint_name","type":"varchar(64)","not_null":true},{"name":"check_clause","type":"longtext","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"collations","kind":"v","columns":[{"name":"collation_name","type":"varchar(64)","not_null":true},{"name":"character_set_name","type":"varchar(64)","not_null":true},{"name":"id","type":"bigint unsigned","not_null":true},{"name":"is_default","type":"varchar(3)","not_null":true},{"name":"is_compiled","type":"varchar(3)","not_null":true},{"name":"sortlen","type":"int unsigned","not_null":true},{"name":"pad_attribute","type":"enum('pad space','no pad')","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"collation_character_set_applicability","kind":"v","columns":[{"name":"collation_name","type":"varchar(64)","not_null":true},{"name":"character_set_name","type":"varchar(64)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"columns","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"column_name","type":"varchar(64)"},{"name":"ordinal_position","type":"int unsigned","not_null":true},{"name":"column_default","type":"text"},{"name":"is_nullable","type":"varchar(3)","not_null":true},{"name":"data_type","type":"longtext"},{"name":"character_maximum_length","type":"bigint"},{"name":"character_octet_length","type":"bigint"},{"name":"numeric_precision","type":"bigint unsigned"},{"name":"numeric_scale","type":"bigint unsigned"},{"name":"datetime_precision","type":"int unsigned"},{"name":"character_set_name","type":"varchar(64)"},{"name":"collation_name","type":"varchar(64)"},{"name":"column_type","type":"mediumtext","not_null":true},{"name":"column_key","type":"enum('','pri','uni','mul')","not_null":true},{"name":"extra","type":"varchar(256)"},{"name":"privileges","type":"varchar(154)"},{"name":"column_comment","type":"text","not_null":true},{"name":"generation_expression","type":"longtext","not_null":true},{"name":"srs_id","type":"int unsigned"}]} +{"catalog":"def","schema":"information_schema","name":"columns_extensions","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"column_name","type":"varchar(64)"},{"name":"engine_attribute","type":"json"},{"name":"secondary_engine_attribute","type":"json"}]} +{"catalog":"def","schema":"information_schema","name":"column_privileges","kind":"v","columns":[{"name":"grantee","type":"varchar(292)","not_null":true},{"name":"table_catalog","type":"varchar(512)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"column_name","type":"varchar(64)","not_null":true},{"name":"privilege_type","type":"varchar(64)","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"column_statistics","kind":"v","columns":[{"name":"schema_name","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"column_name","type":"varchar(64)","not_null":true},{"name":"histogram","type":"json","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"enabled_roles","kind":"v","columns":[{"name":"role_name","type":"varchar(255)"},{"name":"role_host","type":"varchar(255)"},{"name":"is_default","type":"varchar(3)"},{"name":"is_mandatory","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"engines","kind":"v","columns":[{"name":"engine","type":"varchar(64)","not_null":true},{"name":"support","type":"varchar(8)","not_null":true},{"name":"comment","type":"varchar(80)","not_null":true},{"name":"transactions","type":"varchar(3)"},{"name":"xa","type":"varchar(3)"},{"name":"savepoints","type":"varchar(3)"}]} +{"catalog":"def","schema":"information_schema","name":"events","kind":"v","columns":[{"name":"event_catalog","type":"varchar(64)","not_null":true},{"name":"event_schema","type":"varchar(64)","not_null":true},{"name":"event_name","type":"varchar(64)","not_null":true},{"name":"definer","type":"varchar(288)","not_null":true},{"name":"time_zone","type":"varchar(64)","not_null":true},{"name":"event_body","type":"varchar(3)","not_null":true},{"name":"event_definition","type":"longtext","not_null":true},{"name":"event_type","type":"varchar(9)","not_null":true},{"name":"execute_at","type":"datetime"},{"name":"interval_value","type":"varchar(256)"},{"name":"interval_field","type":"enum('year','quarter','month','day','hour','minute','week','second','microsecond','year_month','day_hour','day_minute','day_second','hour_minute','hour_second','minute_second','day_microsecond','hour_microsecond','minute_microsecond','second_microsecond')"},{"name":"sql_mode","type":"set('real_as_float','pipes_as_concat','ansi_quotes','ignore_space','not_used','only_full_group_by','no_unsigned_subtraction','no_dir_in_create','not_used_9','not_used_10','not_used_11','not_used_12','not_used_13','not_used_14','not_used_15','not_used_16','not_used_17','not_used_18','ansi','no_auto_value_on_zero','no_backslash_escapes','strict_trans_tables','strict_all_tables','no_zero_in_date','no_zero_date','allow_invalid_dates','error_for_division_by_zero','traditional','not_used_29','high_not_precedence','no_engine_substitution','pad_char_to_full_length','time_truncate_fractional','interpret_utf8_as_utf8mb4')","not_null":true},{"name":"starts","type":"datetime"},{"name":"ends","type":"datetime"},{"name":"status","type":"varchar(21)","not_null":true},{"name":"on_completion","type":"varchar(12)","not_null":true},{"name":"created","type":"timestamp","not_null":true},{"name":"last_altered","type":"timestamp","not_null":true},{"name":"last_executed","type":"datetime"},{"name":"event_comment","type":"varchar(2048)","not_null":true},{"name":"originator","type":"int unsigned","not_null":true},{"name":"character_set_client","type":"varchar(64)","not_null":true},{"name":"collation_connection","type":"varchar(64)","not_null":true},{"name":"database_collation","type":"varchar(64)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"files","kind":"v","columns":[{"name":"file_id","type":"bigint"},{"name":"file_name","type":"text"},{"name":"file_type","type":"varchar(256)"},{"name":"tablespace_name","type":"varchar(268)","not_null":true},{"name":"table_catalog","type":"varchar(0)","not_null":true},{"name":"table_schema","type":"varbinary(0)"},{"name":"table_name","type":"varbinary(0)"},{"name":"logfile_group_name","type":"varchar(256)"},{"name":"logfile_group_number","type":"bigint"},{"name":"engine","type":"varchar(64)","not_null":true},{"name":"fulltext_keys","type":"varbinary(0)"},{"name":"deleted_rows","type":"varbinary(0)"},{"name":"update_count","type":"varbinary(0)"},{"name":"free_extents","type":"bigint"},{"name":"total_extents","type":"bigint"},{"name":"extent_size","type":"bigint"},{"name":"initial_size","type":"bigint"},{"name":"maximum_size","type":"bigint"},{"name":"autoextend_size","type":"bigint"},{"name":"creation_time","type":"varbinary(0)"},{"name":"last_update_time","type":"varbinary(0)"},{"name":"last_access_time","type":"varbinary(0)"},{"name":"recover_time","type":"varbinary(0)"},{"name":"transaction_counter","type":"varbinary(0)"},{"name":"version","type":"bigint"},{"name":"row_format","type":"varchar(256)"},{"name":"table_rows","type":"varbinary(0)"},{"name":"avg_row_length","type":"varbinary(0)"},{"name":"data_length","type":"varbinary(0)"},{"name":"max_data_length","type":"varbinary(0)"},{"name":"index_length","type":"varbinary(0)"},{"name":"data_free","type":"bigint"},{"name":"create_time","type":"varbinary(0)"},{"name":"update_time","type":"varbinary(0)"},{"name":"check_time","type":"varbinary(0)"},{"name":"checksum","type":"varbinary(0)"},{"name":"status","type":"varchar(256)"},{"name":"extra","type":"varchar(256)"}]} +{"catalog":"def","schema":"information_schema","name":"innodb_buffer_page","kind":"v","columns":[{"name":"pool_id","type":"bigint unsigned","not_null":true},{"name":"block_id","type":"bigint unsigned","not_null":true},{"name":"space","type":"bigint unsigned","not_null":true},{"name":"page_number","type":"bigint unsigned","not_null":true},{"name":"page_type","type":"varchar(64)"},{"name":"flush_type","type":"bigint unsigned","not_null":true},{"name":"fix_count","type":"bigint unsigned","not_null":true},{"name":"is_hashed","type":"varchar(3)"},{"name":"newest_modification","type":"bigint unsigned","not_null":true},{"name":"oldest_modification","type":"bigint unsigned","not_null":true},{"name":"access_time","type":"bigint unsigned","not_null":true},{"name":"table_name","type":"varchar(1024)"},{"name":"index_name","type":"varchar(1024)"},{"name":"number_records","type":"bigint unsigned","not_null":true},{"name":"data_size","type":"bigint unsigned","not_null":true},{"name":"compressed_size","type":"bigint unsigned","not_null":true},{"name":"page_state","type":"varchar(64)"},{"name":"io_fix","type":"varchar(64)"},{"name":"is_old","type":"varchar(3)"},{"name":"free_page_clock","type":"bigint unsigned","not_null":true},{"name":"is_stale","type":"varchar(3)"}]} +{"catalog":"def","schema":"information_schema","name":"innodb_buffer_page_lru","kind":"v","columns":[{"name":"pool_id","type":"bigint unsigned","not_null":true},{"name":"lru_position","type":"bigint unsigned","not_null":true},{"name":"space","type":"bigint unsigned","not_null":true},{"name":"page_number","type":"bigint unsigned","not_null":true},{"name":"page_type","type":"varchar(64)"},{"name":"flush_type","type":"bigint unsigned","not_null":true},{"name":"fix_count","type":"bigint unsigned","not_null":true},{"name":"is_hashed","type":"varchar(3)"},{"name":"newest_modification","type":"bigint unsigned","not_null":true},{"name":"oldest_modification","type":"bigint unsigned","not_null":true},{"name":"access_time","type":"bigint unsigned","not_null":true},{"name":"table_name","type":"varchar(1024)"},{"name":"index_name","type":"varchar(1024)"},{"name":"number_records","type":"bigint unsigned","not_null":true},{"name":"data_size","type":"bigint unsigned","not_null":true},{"name":"compressed_size","type":"bigint unsigned","not_null":true},{"name":"compressed","type":"varchar(3)"},{"name":"io_fix","type":"varchar(64)"},{"name":"is_old","type":"varchar(3)"},{"name":"free_page_clock","type":"bigint unsigned","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_buffer_pool_stats","kind":"v","columns":[{"name":"pool_id","type":"bigint unsigned","not_null":true},{"name":"pool_size","type":"bigint unsigned","not_null":true},{"name":"free_buffers","type":"bigint unsigned","not_null":true},{"name":"database_pages","type":"bigint unsigned","not_null":true},{"name":"old_database_pages","type":"bigint unsigned","not_null":true},{"name":"modified_database_pages","type":"bigint unsigned","not_null":true},{"name":"pending_decompress","type":"bigint unsigned","not_null":true},{"name":"pending_reads","type":"bigint unsigned","not_null":true},{"name":"pending_flush_lru","type":"bigint unsigned","not_null":true},{"name":"pending_flush_list","type":"bigint unsigned","not_null":true},{"name":"pages_made_young","type":"bigint unsigned","not_null":true},{"name":"pages_not_made_young","type":"bigint unsigned","not_null":true},{"name":"pages_made_young_rate","type":"float(12,0)","not_null":true},{"name":"pages_made_not_young_rate","type":"float(12,0)","not_null":true},{"name":"number_pages_read","type":"bigint unsigned","not_null":true},{"name":"number_pages_created","type":"bigint unsigned","not_null":true},{"name":"number_pages_written","type":"bigint unsigned","not_null":true},{"name":"pages_read_rate","type":"float(12,0)","not_null":true},{"name":"pages_create_rate","type":"float(12,0)","not_null":true},{"name":"pages_written_rate","type":"float(12,0)","not_null":true},{"name":"number_pages_get","type":"bigint unsigned","not_null":true},{"name":"hit_rate","type":"bigint unsigned","not_null":true},{"name":"young_make_per_thousand_gets","type":"bigint unsigned","not_null":true},{"name":"not_young_make_per_thousand_gets","type":"bigint unsigned","not_null":true},{"name":"number_pages_read_ahead","type":"bigint unsigned","not_null":true},{"name":"number_read_ahead_evicted","type":"bigint unsigned","not_null":true},{"name":"read_ahead_rate","type":"float(12,0)","not_null":true},{"name":"read_ahead_evicted_rate","type":"float(12,0)","not_null":true},{"name":"lru_io_total","type":"bigint unsigned","not_null":true},{"name":"lru_io_current","type":"bigint unsigned","not_null":true},{"name":"uncompress_total","type":"bigint unsigned","not_null":true},{"name":"uncompress_current","type":"bigint unsigned","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"innodb_cached_indexes","kind":"v","columns":[{"name":"space_id","type":"int unsigned","not_null":true},{"name":"index_id","type":"bigint unsigned","not_null":true},{"name":"n_cached_pages","type":"bigint unsigned","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"innodb_cmp","kind":"v","columns":[{"name":"page_size","type":"int","not_null":true},{"name":"compress_ops","type":"int","not_null":true},{"name":"compress_ops_ok","type":"int","not_null":true},{"name":"compress_time","type":"int","not_null":true},{"name":"uncompress_ops","type":"int","not_null":true},{"name":"uncompress_time","type":"int","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"innodb_cmpmem","kind":"v","columns":[{"name":"page_size","type":"int","not_null":true},{"name":"buffer_pool_instance","type":"int","not_null":true},{"name":"pages_used","type":"int","not_null":true},{"name":"pages_free","type":"int","not_null":true},{"name":"relocation_ops","type":"bigint","not_null":true},{"name":"relocation_time","type":"int","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"innodb_cmpmem_reset","kind":"v","columns":[{"name":"page_size","type":"int","not_null":true},{"name":"buffer_pool_instance","type":"int","not_null":true},{"name":"pages_used","type":"int","not_null":true},{"name":"pages_free","type":"int","not_null":true},{"name":"relocation_ops","type":"bigint","not_null":true},{"name":"relocation_time","type":"int","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_cmp_per_index","kind":"v","columns":[{"name":"database_name","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"index_name","type":"varchar","not_null":true},{"name":"compress_ops","type":"int","not_null":true},{"name":"compress_ops_ok","type":"int","not_null":true},{"name":"compress_time","type":"int","not_null":true},{"name":"uncompress_ops","type":"int","not_null":true},{"name":"uncompress_time","type":"int","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_cmp_per_index_reset","kind":"v","columns":[{"name":"database_name","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"index_name","type":"varchar","not_null":true},{"name":"compress_ops","type":"int","not_null":true},{"name":"compress_ops_ok","type":"int","not_null":true},{"name":"compress_time","type":"int","not_null":true},{"name":"uncompress_ops","type":"int","not_null":true},{"name":"uncompress_time","type":"int","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_cmp_per_index","kind":"v","columns":[{"name":"database_name","type":"varchar(192)","not_null":true},{"name":"table_name","type":"varchar(192)","not_null":true},{"name":"index_name","type":"varchar(192)","not_null":true},{"name":"compress_ops","type":"int","not_null":true},{"name":"compress_ops_ok","type":"int","not_null":true},{"name":"compress_time","type":"int","not_null":true},{"name":"uncompress_ops","type":"int","not_null":true},{"name":"uncompress_time","type":"int","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_cmp_per_index_reset","kind":"v","columns":[{"name":"database_name","type":"varchar(192)","not_null":true},{"name":"table_name","type":"varchar(192)","not_null":true},{"name":"index_name","type":"varchar(192)","not_null":true},{"name":"compress_ops","type":"int","not_null":true},{"name":"compress_ops_ok","type":"int","not_null":true},{"name":"compress_time","type":"int","not_null":true},{"name":"uncompress_ops","type":"int","not_null":true},{"name":"uncompress_time","type":"int","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"innodb_cmp_reset","kind":"v","columns":[{"name":"page_size","type":"int","not_null":true},{"name":"compress_ops","type":"int","not_null":true},{"name":"compress_ops_ok","type":"int","not_null":true},{"name":"compress_time","type":"int","not_null":true},{"name":"uncompress_ops","type":"int","not_null":true},{"name":"uncompress_time","type":"int","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_columns","kind":"v","columns":[{"name":"table_id","type":"bigint unsigned","not_null":true},{"name":"name","type":"varchar","not_null":true},{"name":"pos","type":"bigint unsigned","not_null":true},{"name":"mtype","type":"int","not_null":true},{"name":"prtype","type":"int","not_null":true},{"name":"len","type":"int","not_null":true},{"name":"has_default","type":"int","not_null":true},{"name":"default_value","type":"text"}]} -{"catalog":"def","schema":"information_schema","name":"innodb_datafiles","kind":"v","columns":[{"name":"space","type":"varbinary"},{"name":"path","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_fields","kind":"v","columns":[{"name":"index_id","type":"varbinary"},{"name":"name","type":"varchar","not_null":true},{"name":"pos","type":"bigint unsigned","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_foreign","kind":"v","columns":[{"name":"id","type":"varchar"},{"name":"for_name","type":"varchar"},{"name":"ref_name","type":"varchar"},{"name":"n_cols","type":"bigint","not_null":true},{"name":"type","type":"bigint unsigned","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_foreign_cols","kind":"v","columns":[{"name":"id","type":"varchar"},{"name":"for_col_name","type":"varchar","not_null":true},{"name":"ref_col_name","type":"varchar","not_null":true},{"name":"pos","type":"int unsigned","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_columns","kind":"v","columns":[{"name":"table_id","type":"bigint unsigned","not_null":true},{"name":"name","type":"varchar(193)","not_null":true},{"name":"pos","type":"bigint unsigned","not_null":true},{"name":"mtype","type":"int","not_null":true},{"name":"prtype","type":"int","not_null":true},{"name":"len","type":"int","not_null":true},{"name":"has_default","type":"int","not_null":true},{"name":"default_value","type":"text"}]} +{"catalog":"def","schema":"information_schema","name":"innodb_datafiles","kind":"v","columns":[{"name":"space","type":"varbinary(256)"},{"name":"path","type":"varchar(512)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_fields","kind":"v","columns":[{"name":"index_id","type":"varbinary(256)"},{"name":"name","type":"varchar(64)","not_null":true},{"name":"pos","type":"bigint unsigned","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_foreign","kind":"v","columns":[{"name":"id","type":"varchar(129)"},{"name":"for_name","type":"varchar(129)"},{"name":"ref_name","type":"varchar(129)"},{"name":"n_cols","type":"bigint","not_null":true},{"name":"type","type":"bigint unsigned","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_foreign_cols","kind":"v","columns":[{"name":"id","type":"varchar(129)"},{"name":"for_col_name","type":"varchar(64)","not_null":true},{"name":"ref_col_name","type":"varchar(64)","not_null":true},{"name":"pos","type":"int unsigned","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"innodb_ft_being_deleted","kind":"v","columns":[{"name":"doc_id","type":"bigint unsigned","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_ft_config","kind":"v","columns":[{"name":"key","type":"varchar","not_null":true},{"name":"value","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_ft_default_stopword","kind":"v","columns":[{"name":"value","type":"varchar","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_ft_config","kind":"v","columns":[{"name":"key","type":"varchar(193)","not_null":true},{"name":"value","type":"varchar(193)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_ft_default_stopword","kind":"v","columns":[{"name":"value","type":"varchar(18)","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"innodb_ft_deleted","kind":"v","columns":[{"name":"doc_id","type":"bigint unsigned","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_ft_index_cache","kind":"v","columns":[{"name":"word","type":"varchar","not_null":true},{"name":"first_doc_id","type":"bigint unsigned","not_null":true},{"name":"last_doc_id","type":"bigint unsigned","not_null":true},{"name":"doc_count","type":"bigint unsigned","not_null":true},{"name":"doc_id","type":"bigint unsigned","not_null":true},{"name":"position","type":"bigint unsigned","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_ft_index_table","kind":"v","columns":[{"name":"word","type":"varchar","not_null":true},{"name":"first_doc_id","type":"bigint unsigned","not_null":true},{"name":"last_doc_id","type":"bigint unsigned","not_null":true},{"name":"doc_count","type":"bigint unsigned","not_null":true},{"name":"doc_id","type":"bigint unsigned","not_null":true},{"name":"position","type":"bigint unsigned","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_indexes","kind":"v","columns":[{"name":"index_id","type":"bigint unsigned","not_null":true},{"name":"name","type":"varchar","not_null":true},{"name":"table_id","type":"bigint unsigned","not_null":true},{"name":"type","type":"int","not_null":true},{"name":"n_fields","type":"int","not_null":true},{"name":"page_no","type":"int","not_null":true},{"name":"space","type":"int","not_null":true},{"name":"merge_threshold","type":"int","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_metrics","kind":"v","columns":[{"name":"name","type":"varchar","not_null":true},{"name":"subsystem","type":"varchar","not_null":true},{"name":"count","type":"bigint","not_null":true},{"name":"max_count","type":"bigint"},{"name":"min_count","type":"bigint"},{"name":"avg_count","type":"float"},{"name":"count_reset","type":"bigint","not_null":true},{"name":"max_count_reset","type":"bigint"},{"name":"min_count_reset","type":"bigint"},{"name":"avg_count_reset","type":"float"},{"name":"time_enabled","type":"datetime"},{"name":"time_disabled","type":"datetime"},{"name":"time_elapsed","type":"bigint"},{"name":"time_reset","type":"datetime"},{"name":"status","type":"varchar","not_null":true},{"name":"type","type":"varchar","not_null":true},{"name":"comment","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_session_temp_tablespaces","kind":"v","columns":[{"name":"id","type":"int unsigned","not_null":true},{"name":"space","type":"int unsigned","not_null":true},{"name":"path","type":"varchar","not_null":true},{"name":"size","type":"bigint unsigned","not_null":true},{"name":"state","type":"varchar","not_null":true},{"name":"purpose","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_tables","kind":"v","columns":[{"name":"table_id","type":"bigint unsigned","not_null":true},{"name":"name","type":"varchar","not_null":true},{"name":"flag","type":"int","not_null":true},{"name":"n_cols","type":"int","not_null":true},{"name":"space","type":"bigint","not_null":true},{"name":"row_format","type":"varchar"},{"name":"zip_page_size","type":"int unsigned","not_null":true},{"name":"space_type","type":"varchar"},{"name":"instant_cols","type":"int","not_null":true},{"name":"total_row_versions","type":"int","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_tablespaces","kind":"v","columns":[{"name":"space","type":"int unsigned","not_null":true},{"name":"name","type":"varchar","not_null":true},{"name":"flag","type":"int unsigned","not_null":true},{"name":"row_format","type":"varchar"},{"name":"page_size","type":"int unsigned","not_null":true},{"name":"zip_page_size","type":"int unsigned","not_null":true},{"name":"space_type","type":"varchar"},{"name":"fs_block_size","type":"int unsigned","not_null":true},{"name":"file_size","type":"bigint unsigned","not_null":true},{"name":"allocated_size","type":"bigint unsigned","not_null":true},{"name":"autoextend_size","type":"bigint unsigned","not_null":true},{"name":"server_version","type":"varchar"},{"name":"space_version","type":"int unsigned","not_null":true},{"name":"encryption","type":"varchar"},{"name":"state","type":"varchar"}]} -{"catalog":"def","schema":"information_schema","name":"innodb_tablespaces_brief","kind":"v","columns":[{"name":"space","type":"varbinary"},{"name":"name","type":"varchar","not_null":true},{"name":"path","type":"varchar","not_null":true},{"name":"flag","type":"varbinary"},{"name":"space_type","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_tablestats","kind":"v","columns":[{"name":"table_id","type":"bigint unsigned","not_null":true},{"name":"name","type":"varchar","not_null":true},{"name":"stats_initialized","type":"varchar","not_null":true},{"name":"num_rows","type":"bigint unsigned","not_null":true},{"name":"clust_index_size","type":"bigint unsigned","not_null":true},{"name":"other_index_size","type":"bigint unsigned","not_null":true},{"name":"modified_counter","type":"bigint unsigned","not_null":true},{"name":"autoinc","type":"bigint unsigned","not_null":true},{"name":"ref_count","type":"int","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_temp_table_info","kind":"v","columns":[{"name":"table_id","type":"bigint unsigned","not_null":true},{"name":"name","type":"varchar"},{"name":"n_cols","type":"int unsigned","not_null":true},{"name":"space","type":"int unsigned","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"innodb_trx","kind":"v","columns":[{"name":"trx_id","type":"bigint unsigned","not_null":true},{"name":"trx_state","type":"varchar","not_null":true},{"name":"trx_started","type":"datetime","not_null":true},{"name":"trx_requested_lock_id","type":"varchar"},{"name":"trx_wait_started","type":"datetime"},{"name":"trx_weight","type":"bigint unsigned","not_null":true},{"name":"trx_mysql_thread_id","type":"bigint unsigned","not_null":true},{"name":"trx_query","type":"varchar"},{"name":"trx_operation_state","type":"varchar"},{"name":"trx_tables_in_use","type":"bigint unsigned","not_null":true},{"name":"trx_tables_locked","type":"bigint unsigned","not_null":true},{"name":"trx_lock_structs","type":"bigint unsigned","not_null":true},{"name":"trx_lock_memory_bytes","type":"bigint unsigned","not_null":true},{"name":"trx_rows_locked","type":"bigint unsigned","not_null":true},{"name":"trx_rows_modified","type":"bigint unsigned","not_null":true},{"name":"trx_concurrency_tickets","type":"bigint unsigned","not_null":true},{"name":"trx_isolation_level","type":"varchar","not_null":true},{"name":"trx_unique_checks","type":"int","not_null":true},{"name":"trx_foreign_key_checks","type":"int","not_null":true},{"name":"trx_last_foreign_key_error","type":"varchar"},{"name":"trx_adaptive_hash_latched","type":"int","not_null":true},{"name":"trx_adaptive_hash_timeout","type":"bigint unsigned","not_null":true},{"name":"trx_is_read_only","type":"int","not_null":true},{"name":"trx_autocommit_non_locking","type":"int","not_null":true},{"name":"trx_schedule_weight","type":"bigint unsigned"}]} +{"catalog":"def","schema":"information_schema","name":"innodb_ft_index_cache","kind":"v","columns":[{"name":"word","type":"varchar(337)","not_null":true},{"name":"first_doc_id","type":"bigint unsigned","not_null":true},{"name":"last_doc_id","type":"bigint unsigned","not_null":true},{"name":"doc_count","type":"bigint unsigned","not_null":true},{"name":"doc_id","type":"bigint unsigned","not_null":true},{"name":"position","type":"bigint unsigned","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_ft_index_table","kind":"v","columns":[{"name":"word","type":"varchar(337)","not_null":true},{"name":"first_doc_id","type":"bigint unsigned","not_null":true},{"name":"last_doc_id","type":"bigint unsigned","not_null":true},{"name":"doc_count","type":"bigint unsigned","not_null":true},{"name":"doc_id","type":"bigint unsigned","not_null":true},{"name":"position","type":"bigint unsigned","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_indexes","kind":"v","columns":[{"name":"index_id","type":"bigint unsigned","not_null":true},{"name":"name","type":"varchar(193)","not_null":true},{"name":"table_id","type":"bigint unsigned","not_null":true},{"name":"type","type":"int","not_null":true},{"name":"n_fields","type":"int","not_null":true},{"name":"page_no","type":"int","not_null":true},{"name":"space","type":"int","not_null":true},{"name":"merge_threshold","type":"int","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_metrics","kind":"v","columns":[{"name":"name","type":"varchar(193)","not_null":true},{"name":"subsystem","type":"varchar(193)","not_null":true},{"name":"count","type":"bigint","not_null":true},{"name":"max_count","type":"bigint"},{"name":"min_count","type":"bigint"},{"name":"avg_count","type":"float(12,0)"},{"name":"count_reset","type":"bigint","not_null":true},{"name":"max_count_reset","type":"bigint"},{"name":"min_count_reset","type":"bigint"},{"name":"avg_count_reset","type":"float(12,0)"},{"name":"time_enabled","type":"datetime"},{"name":"time_disabled","type":"datetime"},{"name":"time_elapsed","type":"bigint"},{"name":"time_reset","type":"datetime"},{"name":"status","type":"varchar(193)","not_null":true},{"name":"type","type":"varchar(193)","not_null":true},{"name":"comment","type":"varchar(193)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_session_temp_tablespaces","kind":"v","columns":[{"name":"id","type":"int unsigned","not_null":true},{"name":"space","type":"int unsigned","not_null":true},{"name":"path","type":"varchar(4001)","not_null":true},{"name":"size","type":"bigint unsigned","not_null":true},{"name":"state","type":"varchar(192)","not_null":true},{"name":"purpose","type":"varchar(192)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_tables","kind":"v","columns":[{"name":"table_id","type":"bigint unsigned","not_null":true},{"name":"name","type":"varchar(655)","not_null":true},{"name":"flag","type":"int","not_null":true},{"name":"n_cols","type":"int","not_null":true},{"name":"space","type":"bigint","not_null":true},{"name":"row_format","type":"varchar(12)"},{"name":"zip_page_size","type":"int unsigned","not_null":true},{"name":"space_type","type":"varchar(10)"},{"name":"instant_cols","type":"int","not_null":true},{"name":"total_row_versions","type":"int","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_tablespaces","kind":"v","columns":[{"name":"space","type":"int unsigned","not_null":true},{"name":"name","type":"varchar(655)","not_null":true},{"name":"flag","type":"int unsigned","not_null":true},{"name":"row_format","type":"varchar(22)"},{"name":"page_size","type":"int unsigned","not_null":true},{"name":"zip_page_size","type":"int unsigned","not_null":true},{"name":"space_type","type":"varchar(10)"},{"name":"fs_block_size","type":"int unsigned","not_null":true},{"name":"file_size","type":"bigint unsigned","not_null":true},{"name":"allocated_size","type":"bigint unsigned","not_null":true},{"name":"autoextend_size","type":"bigint unsigned","not_null":true},{"name":"server_version","type":"varchar(10)"},{"name":"space_version","type":"int unsigned","not_null":true},{"name":"encryption","type":"varchar(1)"},{"name":"state","type":"varchar(10)"}]} +{"catalog":"def","schema":"information_schema","name":"innodb_tablespaces_brief","kind":"v","columns":[{"name":"space","type":"varbinary(256)"},{"name":"name","type":"varchar(268)","not_null":true},{"name":"path","type":"varchar(512)","not_null":true},{"name":"flag","type":"varbinary(256)"},{"name":"space_type","type":"varchar(7)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_tablestats","kind":"v","columns":[{"name":"table_id","type":"bigint unsigned","not_null":true},{"name":"name","type":"varchar(193)","not_null":true},{"name":"stats_initialized","type":"varchar(193)","not_null":true},{"name":"num_rows","type":"bigint unsigned","not_null":true},{"name":"clust_index_size","type":"bigint unsigned","not_null":true},{"name":"other_index_size","type":"bigint unsigned","not_null":true},{"name":"modified_counter","type":"bigint unsigned","not_null":true},{"name":"autoinc","type":"bigint unsigned","not_null":true},{"name":"ref_count","type":"int","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_temp_table_info","kind":"v","columns":[{"name":"table_id","type":"bigint unsigned","not_null":true},{"name":"name","type":"varchar(64)"},{"name":"n_cols","type":"int unsigned","not_null":true},{"name":"space","type":"int unsigned","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"innodb_trx","kind":"v","columns":[{"name":"trx_id","type":"bigint unsigned","not_null":true},{"name":"trx_state","type":"varchar(13)","not_null":true},{"name":"trx_started","type":"datetime","not_null":true},{"name":"trx_requested_lock_id","type":"varchar(126)"},{"name":"trx_wait_started","type":"datetime"},{"name":"trx_weight","type":"bigint unsigned","not_null":true},{"name":"trx_mysql_thread_id","type":"bigint unsigned","not_null":true},{"name":"trx_query","type":"varchar(1024)"},{"name":"trx_operation_state","type":"varchar(64)"},{"name":"trx_tables_in_use","type":"bigint unsigned","not_null":true},{"name":"trx_tables_locked","type":"bigint unsigned","not_null":true},{"name":"trx_lock_structs","type":"bigint unsigned","not_null":true},{"name":"trx_lock_memory_bytes","type":"bigint unsigned","not_null":true},{"name":"trx_rows_locked","type":"bigint unsigned","not_null":true},{"name":"trx_rows_modified","type":"bigint unsigned","not_null":true},{"name":"trx_concurrency_tickets","type":"bigint unsigned","not_null":true},{"name":"trx_isolation_level","type":"varchar(16)","not_null":true},{"name":"trx_unique_checks","type":"int","not_null":true},{"name":"trx_foreign_key_checks","type":"int","not_null":true},{"name":"trx_last_foreign_key_error","type":"varchar(256)"},{"name":"trx_adaptive_hash_latched","type":"int","not_null":true},{"name":"trx_adaptive_hash_timeout","type":"bigint unsigned","not_null":true},{"name":"trx_is_read_only","type":"int","not_null":true},{"name":"trx_autocommit_non_locking","type":"int","not_null":true},{"name":"trx_schedule_weight","type":"bigint unsigned"}]} {"catalog":"def","schema":"information_schema","name":"innodb_virtual","kind":"v","columns":[{"name":"table_id","type":"bigint unsigned","not_null":true},{"name":"pos","type":"int unsigned","not_null":true},{"name":"base_pos","type":"int unsigned","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"json_duality_views","kind":"v","columns":[{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"definer","type":"varchar"},{"name":"security_type","type":"varchar"},{"name":"json_column_name","type":"varchar","not_null":true},{"name":"root_table_catalog","type":"varchar"},{"name":"root_table_schema","type":"varchar"},{"name":"root_table_name","type":"varchar"},{"name":"allow_insert","type":"varchar"},{"name":"allow_update","type":"varchar"},{"name":"allow_delete","type":"varchar"},{"name":"read_only","type":"varchar"},{"name":"status","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"json_duality_view_columns","kind":"v","columns":[{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"referenced_table_catalog","type":"varchar"},{"name":"referenced_table_schema","type":"varchar"},{"name":"referenced_table_name","type":"varchar"},{"name":"is_root_table","type":"tinyint"},{"name":"referenced_table_id","type":"int"},{"name":"referenced_column_name","type":"varchar"},{"name":"json_key_name","type":"varchar"},{"name":"allow_insert","type":"tinyint"},{"name":"allow_update","type":"tinyint"},{"name":"allow_delete","type":"tinyint"},{"name":"read_only","type":"tinyint"}]} -{"catalog":"def","schema":"information_schema","name":"json_duality_view_links","kind":"v","columns":[{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"parent_table_catalog","type":"varchar"},{"name":"parent_table_schema","type":"varchar"},{"name":"parent_table_name","type":"varchar"},{"name":"child_table_catalog","type":"varchar"},{"name":"child_table_schema","type":"varchar"},{"name":"child_table_name","type":"varchar"},{"name":"parent_column_name","type":"varchar"},{"name":"child_column_name","type":"varchar"},{"name":"join_type","type":"varchar"},{"name":"json_key_name","type":"varchar"}]} -{"catalog":"def","schema":"information_schema","name":"json_duality_view_tables","kind":"v","columns":[{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"referenced_table_catalog","type":"varchar"},{"name":"referenced_table_schema","type":"varchar"},{"name":"referenced_table_name","type":"varchar"},{"name":"where_clause","type":"varchar"},{"name":"allow_insert","type":"tinyint"},{"name":"allow_update","type":"tinyint"},{"name":"allow_delete","type":"tinyint"},{"name":"read_only","type":"tinyint"},{"name":"is_root_table","type":"tinyint"},{"name":"referenced_table_id","type":"int"},{"name":"referenced_table_parent_id","type":"int"},{"name":"referenced_table_parent_relationship","type":"varchar"}]} -{"catalog":"def","schema":"information_schema","name":"keywords","kind":"v","columns":[{"name":"word","type":"varchar"},{"name":"reserved","type":"int"}]} -{"catalog":"def","schema":"information_schema","name":"key_column_usage","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar","not_null":true},{"name":"constraint_schema","type":"varchar","not_null":true},{"name":"constraint_name","type":"varchar"},{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"column_name","type":"varchar"},{"name":"ordinal_position","type":"int unsigned","not_null":true},{"name":"position_in_unique_constraint","type":"int unsigned"},{"name":"referenced_table_schema","type":"varchar"},{"name":"referenced_table_name","type":"varchar"},{"name":"referenced_column_name","type":"varchar"}]} -{"catalog":"def","schema":"information_schema","name":"libraries","kind":"v","columns":[{"name":"library_catalog","type":"varchar","not_null":true},{"name":"library_schema","type":"varchar","not_null":true},{"name":"library_name","type":"varchar","not_null":true},{"name":"library_definition","type":"longtext"},{"name":"language","type":"varchar","not_null":true},{"name":"created","type":"timestamp","not_null":true},{"name":"last_altered","type":"timestamp","not_null":true},{"name":"sql_mode","type":"set","not_null":true},{"name":"library_comment","type":"text","not_null":true},{"name":"creator","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"optimizer_trace","kind":"v","columns":[{"name":"query","type":"varchar","not_null":true},{"name":"trace","type":"varchar","not_null":true},{"name":"missing_bytes_beyond_max_mem_size","type":"int","not_null":true},{"name":"insufficient_privileges","type":"tinyint","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"parameters","kind":"v","columns":[{"name":"specific_catalog","type":"varchar","not_null":true},{"name":"specific_schema","type":"varchar","not_null":true},{"name":"specific_name","type":"varchar","not_null":true},{"name":"ordinal_position","type":"bigint unsigned","not_null":true},{"name":"parameter_mode","type":"varchar"},{"name":"parameter_name","type":"varchar"},{"name":"data_type","type":"longtext"},{"name":"character_maximum_length","type":"bigint"},{"name":"character_octet_length","type":"bigint"},{"name":"numeric_precision","type":"int unsigned"},{"name":"numeric_scale","type":"bigint"},{"name":"datetime_precision","type":"int unsigned"},{"name":"character_set_name","type":"varchar"},{"name":"collation_name","type":"varchar"},{"name":"dtd_identifier","type":"mediumtext","not_null":true},{"name":"routine_type","type":"enum","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"partitions","kind":"v","columns":[{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"partition_name","type":"varchar"},{"name":"subpartition_name","type":"varchar"},{"name":"partition_ordinal_position","type":"int unsigned"},{"name":"subpartition_ordinal_position","type":"int unsigned"},{"name":"secondary_load","type":"varchar"},{"name":"partition_method","type":"varchar"},{"name":"subpartition_method","type":"varchar"},{"name":"partition_expression","type":"varchar"},{"name":"subpartition_expression","type":"varchar"},{"name":"partition_description","type":"text"},{"name":"table_rows","type":"bigint unsigned"},{"name":"avg_row_length","type":"bigint unsigned"},{"name":"data_length","type":"bigint unsigned"},{"name":"max_data_length","type":"bigint unsigned"},{"name":"index_length","type":"bigint unsigned"},{"name":"data_free","type":"bigint unsigned"},{"name":"create_time","type":"timestamp","not_null":true},{"name":"update_time","type":"datetime"},{"name":"check_time","type":"datetime"},{"name":"checksum","type":"bigint"},{"name":"partition_comment","type":"text","not_null":true},{"name":"nodegroup","type":"varchar"},{"name":"tablespace_name","type":"varchar"}]} -{"catalog":"def","schema":"information_schema","name":"plugins","kind":"v","columns":[{"name":"plugin_name","type":"varchar","not_null":true},{"name":"plugin_version","type":"varchar","not_null":true},{"name":"plugin_status","type":"varchar","not_null":true},{"name":"plugin_type","type":"varchar","not_null":true},{"name":"plugin_type_version","type":"varchar","not_null":true},{"name":"plugin_library","type":"varchar"},{"name":"plugin_library_version","type":"varchar"},{"name":"plugin_author","type":"varchar"},{"name":"plugin_description","type":"varchar"},{"name":"plugin_license","type":"varchar"},{"name":"load_option","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"processlist","kind":"v","columns":[{"name":"id","type":"bigint unsigned","not_null":true},{"name":"user","type":"varchar","not_null":true},{"name":"host","type":"varchar","not_null":true},{"name":"db","type":"varchar"},{"name":"command","type":"varchar","not_null":true},{"name":"time","type":"int","not_null":true},{"name":"state","type":"varchar"},{"name":"info","type":"varchar"}]} -{"catalog":"def","schema":"information_schema","name":"profiling","kind":"v","columns":[{"name":"query_id","type":"int","not_null":true},{"name":"seq","type":"int","not_null":true},{"name":"state","type":"varchar","not_null":true},{"name":"duration","type":"decimal","not_null":true},{"name":"cpu_user","type":"decimal"},{"name":"cpu_system","type":"decimal"},{"name":"context_voluntary","type":"int"},{"name":"context_involuntary","type":"int"},{"name":"block_ops_in","type":"int"},{"name":"block_ops_out","type":"int"},{"name":"messages_sent","type":"int"},{"name":"messages_received","type":"int"},{"name":"page_faults_major","type":"int"},{"name":"page_faults_minor","type":"int"},{"name":"swaps","type":"int"},{"name":"source_function","type":"varchar"},{"name":"source_file","type":"varchar"},{"name":"source_line","type":"int"}]} -{"catalog":"def","schema":"information_schema","name":"referential_constraints","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar","not_null":true},{"name":"constraint_schema","type":"varchar","not_null":true},{"name":"constraint_name","type":"varchar"},{"name":"unique_constraint_catalog","type":"varchar","not_null":true},{"name":"unique_constraint_schema","type":"varchar","not_null":true},{"name":"unique_constraint_name","type":"varchar"},{"name":"match_option","type":"enum","not_null":true},{"name":"update_rule","type":"enum","not_null":true},{"name":"delete_rule","type":"enum","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"referenced_table_name","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"resource_groups","kind":"v","columns":[{"name":"resource_group_name","type":"varchar","not_null":true},{"name":"resource_group_type","type":"enum","not_null":true},{"name":"resource_group_enabled","type":"tinyint","not_null":true},{"name":"vcpu_ids","type":"blob"},{"name":"thread_priority","type":"int","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"role_column_grants","kind":"v","columns":[{"name":"grantor","type":"varchar"},{"name":"grantor_host","type":"varchar"},{"name":"grantee","type":"char","not_null":true},{"name":"grantee_host","type":"char","not_null":true},{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"char","not_null":true},{"name":"table_name","type":"char","not_null":true},{"name":"column_name","type":"char","not_null":true},{"name":"privilege_type","type":"set","not_null":true},{"name":"is_grantable","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"role_routine_grants","kind":"v","columns":[{"name":"grantor","type":"varchar"},{"name":"grantor_host","type":"varchar"},{"name":"grantee","type":"char","not_null":true},{"name":"grantee_host","type":"char","not_null":true},{"name":"specific_catalog","type":"varchar","not_null":true},{"name":"specific_schema","type":"char","not_null":true},{"name":"specific_name","type":"char","not_null":true},{"name":"routine_catalog","type":"varchar","not_null":true},{"name":"routine_schema","type":"char","not_null":true},{"name":"routine_name","type":"char","not_null":true},{"name":"privilege_type","type":"set","not_null":true},{"name":"is_grantable","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"role_table_grants","kind":"v","columns":[{"name":"grantor","type":"varchar"},{"name":"grantor_host","type":"varchar"},{"name":"grantee","type":"char","not_null":true},{"name":"grantee_host","type":"char","not_null":true},{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"char","not_null":true},{"name":"table_name","type":"char","not_null":true},{"name":"privilege_type","type":"set","not_null":true},{"name":"is_grantable","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"routines","kind":"v","columns":[{"name":"specific_name","type":"varchar","not_null":true},{"name":"routine_catalog","type":"varchar","not_null":true},{"name":"routine_schema","type":"varchar","not_null":true},{"name":"routine_name","type":"varchar","not_null":true},{"name":"routine_type","type":"enum","not_null":true},{"name":"data_type","type":"longtext"},{"name":"character_maximum_length","type":"bigint"},{"name":"character_octet_length","type":"bigint"},{"name":"numeric_precision","type":"int unsigned"},{"name":"numeric_scale","type":"int unsigned"},{"name":"datetime_precision","type":"int unsigned"},{"name":"character_set_name","type":"varchar"},{"name":"collation_name","type":"varchar"},{"name":"dtd_identifier","type":"longtext"},{"name":"routine_body","type":"varchar","not_null":true},{"name":"routine_definition","type":"longtext"},{"name":"external_name","type":"varbinary"},{"name":"external_language","type":"varchar","not_null":true},{"name":"parameter_style","type":"varchar","not_null":true},{"name":"is_deterministic","type":"varchar","not_null":true},{"name":"sql_data_access","type":"enum","not_null":true},{"name":"sql_path","type":"varbinary"},{"name":"security_type","type":"enum","not_null":true},{"name":"created","type":"timestamp","not_null":true},{"name":"last_altered","type":"timestamp","not_null":true},{"name":"sql_mode","type":"set","not_null":true},{"name":"routine_comment","type":"text","not_null":true},{"name":"definer","type":"varchar","not_null":true},{"name":"character_set_client","type":"varchar","not_null":true},{"name":"collation_connection","type":"varchar","not_null":true},{"name":"database_collation","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"routine_libraries","kind":"v","columns":[{"name":"routine_catalog","type":"varchar","not_null":true},{"name":"routine_schema","type":"varchar","not_null":true},{"name":"routine_name","type":"varchar","not_null":true},{"name":"routine_type","type":"enum","not_null":true},{"name":"library_catalog","type":"varchar"},{"name":"library_schema","type":"varchar"},{"name":"library_name","type":"varchar"},{"name":"library_version","type":"varchar"}]} -{"catalog":"def","schema":"information_schema","name":"schemata","kind":"v","columns":[{"name":"catalog_name","type":"varchar","not_null":true},{"name":"schema_name","type":"varchar","not_null":true},{"name":"default_character_set_name","type":"varchar","not_null":true},{"name":"default_collation_name","type":"varchar","not_null":true},{"name":"sql_path","type":"varbinary"},{"name":"default_encryption","type":"enum","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"schemata_extensions","kind":"v","columns":[{"name":"catalog_name","type":"varchar","not_null":true},{"name":"schema_name","type":"varchar","not_null":true},{"name":"options","type":"varchar"}]} -{"catalog":"def","schema":"information_schema","name":"schema_privileges","kind":"v","columns":[{"name":"grantee","type":"varchar","not_null":true},{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"privilege_type","type":"varchar","not_null":true},{"name":"is_grantable","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"statistics","kind":"v","columns":[{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"non_unique","type":"int","not_null":true},{"name":"index_schema","type":"varchar","not_null":true},{"name":"index_name","type":"varchar"},{"name":"seq_in_index","type":"int unsigned","not_null":true},{"name":"column_name","type":"varchar"},{"name":"collation","type":"varchar"},{"name":"cardinality","type":"bigint"},{"name":"sub_part","type":"bigint"},{"name":"packed","type":"varbinary"},{"name":"nullable","type":"varchar","not_null":true},{"name":"index_type","type":"varchar","not_null":true},{"name":"comment","type":"varchar","not_null":true},{"name":"index_comment","type":"varchar","not_null":true},{"name":"is_visible","type":"varchar","not_null":true},{"name":"expression","type":"longtext"}]} -{"catalog":"def","schema":"information_schema","name":"st_geometry_columns","kind":"v","columns":[{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"column_name","type":"varchar"},{"name":"srs_name","type":"varchar"},{"name":"srs_id","type":"int unsigned"},{"name":"geometry_type_name","type":"longtext"}]} -{"catalog":"def","schema":"information_schema","name":"st_spatial_reference_systems","kind":"v","columns":[{"name":"srs_name","type":"varchar","not_null":true},{"name":"srs_id","type":"int unsigned","not_null":true},{"name":"organization","type":"varchar"},{"name":"organization_coordsys_id","type":"int unsigned"},{"name":"definition","type":"varchar","not_null":true},{"name":"description","type":"varchar"}]} -{"catalog":"def","schema":"information_schema","name":"st_units_of_measure","kind":"v","columns":[{"name":"unit_name","type":"varchar"},{"name":"unit_type","type":"varchar"},{"name":"conversion_factor","type":"double"},{"name":"description","type":"varchar"}]} -{"catalog":"def","schema":"information_schema","name":"tables","kind":"v","columns":[{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"table_type","type":"enum","not_null":true},{"name":"engine","type":"varchar"},{"name":"version","type":"int"},{"name":"row_format","type":"enum"},{"name":"table_rows","type":"bigint unsigned"},{"name":"avg_row_length","type":"bigint unsigned"},{"name":"data_length","type":"bigint unsigned"},{"name":"max_data_length","type":"bigint unsigned"},{"name":"index_length","type":"bigint unsigned"},{"name":"data_free","type":"bigint unsigned"},{"name":"auto_increment","type":"bigint unsigned"},{"name":"create_time","type":"timestamp","not_null":true},{"name":"update_time","type":"datetime"},{"name":"check_time","type":"datetime"},{"name":"table_collation","type":"varchar"},{"name":"checksum","type":"bigint"},{"name":"create_options","type":"varchar"},{"name":"table_comment","type":"text"}]} -{"catalog":"def","schema":"information_schema","name":"tablespaces_extensions","kind":"v","columns":[{"name":"tablespace_name","type":"varchar","not_null":true},{"name":"engine_attribute","type":"json"}]} -{"catalog":"def","schema":"information_schema","name":"tables_extensions","kind":"v","columns":[{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"engine_attribute","type":"json"},{"name":"secondary_engine_attribute","type":"json"}]} -{"catalog":"def","schema":"information_schema","name":"table_constraints","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar","not_null":true},{"name":"constraint_schema","type":"varchar","not_null":true},{"name":"constraint_name","type":"varchar"},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"constraint_type","type":"varchar","not_null":true},{"name":"enforced","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"table_constraints_extensions","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar","not_null":true},{"name":"constraint_schema","type":"varchar","not_null":true},{"name":"constraint_name","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"engine_attribute","type":"json"},{"name":"secondary_engine_attribute","type":"json"}]} -{"catalog":"def","schema":"information_schema","name":"table_privileges","kind":"v","columns":[{"name":"grantee","type":"varchar","not_null":true},{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"privilege_type","type":"varchar","not_null":true},{"name":"is_grantable","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"triggers","kind":"v","columns":[{"name":"trigger_catalog","type":"varchar","not_null":true},{"name":"trigger_schema","type":"varchar","not_null":true},{"name":"trigger_name","type":"varchar","not_null":true},{"name":"event_manipulation","type":"enum","not_null":true},{"name":"event_object_catalog","type":"varchar","not_null":true},{"name":"event_object_schema","type":"varchar","not_null":true},{"name":"event_object_table","type":"varchar","not_null":true},{"name":"action_order","type":"int unsigned","not_null":true},{"name":"action_condition","type":"varbinary"},{"name":"action_statement","type":"longtext","not_null":true},{"name":"action_orientation","type":"varchar","not_null":true},{"name":"action_timing","type":"enum","not_null":true},{"name":"action_reference_old_table","type":"varbinary"},{"name":"action_reference_new_table","type":"varbinary"},{"name":"action_reference_old_row","type":"varchar","not_null":true},{"name":"action_reference_new_row","type":"varchar","not_null":true},{"name":"created","type":"timestamp","not_null":true},{"name":"sql_mode","type":"set","not_null":true},{"name":"definer","type":"varchar","not_null":true},{"name":"character_set_client","type":"varchar","not_null":true},{"name":"collation_connection","type":"varchar","not_null":true},{"name":"database_collation","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"user_attributes","kind":"v","columns":[{"name":"user","type":"char","not_null":true},{"name":"host","type":"char","not_null":true},{"name":"attribute","type":"longtext"}]} -{"catalog":"def","schema":"information_schema","name":"user_privileges","kind":"v","columns":[{"name":"grantee","type":"varchar","not_null":true},{"name":"table_catalog","type":"varchar","not_null":true},{"name":"privilege_type","type":"varchar","not_null":true},{"name":"is_grantable","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"views","kind":"v","columns":[{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"view_definition","type":"longtext"},{"name":"check_option","type":"enum"},{"name":"is_updatable","type":"enum"},{"name":"definer","type":"varchar"},{"name":"security_type","type":"varchar"},{"name":"character_set_client","type":"varchar","not_null":true},{"name":"collation_connection","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"view_routine_usage","kind":"v","columns":[{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true},{"name":"specific_catalog","type":"varchar","not_null":true},{"name":"specific_schema","type":"varchar","not_null":true},{"name":"specific_name","type":"varchar","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"view_table_usage","kind":"v","columns":[{"name":"view_catalog","type":"varchar","not_null":true},{"name":"view_schema","type":"varchar","not_null":true},{"name":"view_name","type":"varchar","not_null":true},{"name":"table_catalog","type":"varchar","not_null":true},{"name":"table_schema","type":"varchar","not_null":true},{"name":"table_name","type":"varchar","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"json_duality_views","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"definer","type":"varchar(288)"},{"name":"security_type","type":"varchar(7)"},{"name":"json_column_name","type":"varchar(64)","not_null":true},{"name":"root_table_catalog","type":"varchar(64)"},{"name":"root_table_schema","type":"varchar(64)"},{"name":"root_table_name","type":"varchar(64)"},{"name":"allow_insert","type":"varchar(4)"},{"name":"allow_update","type":"varchar(4)"},{"name":"allow_delete","type":"varchar(4)"},{"name":"read_only","type":"varchar(4)"},{"name":"status","type":"varchar(7)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"json_duality_view_columns","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"referenced_table_catalog","type":"varchar(64)"},{"name":"referenced_table_schema","type":"varchar(64)"},{"name":"referenced_table_name","type":"varchar(64)"},{"name":"is_root_table","type":"tinyint"},{"name":"referenced_table_id","type":"int"},{"name":"referenced_column_name","type":"varchar(64)"},{"name":"json_key_name","type":"varchar(64)"},{"name":"allow_insert","type":"tinyint"},{"name":"allow_update","type":"tinyint"},{"name":"allow_delete","type":"tinyint"},{"name":"read_only","type":"tinyint"}]} +{"catalog":"def","schema":"information_schema","name":"json_duality_view_links","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"parent_table_catalog","type":"varchar(64)"},{"name":"parent_table_schema","type":"varchar(64)"},{"name":"parent_table_name","type":"varchar(64)"},{"name":"child_table_catalog","type":"varchar(64)"},{"name":"child_table_schema","type":"varchar(64)"},{"name":"child_table_name","type":"varchar(64)"},{"name":"parent_column_name","type":"varchar(64)"},{"name":"child_column_name","type":"varchar(64)"},{"name":"join_type","type":"varchar(64)"},{"name":"json_key_name","type":"varchar(64)"}]} +{"catalog":"def","schema":"information_schema","name":"json_duality_view_tables","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"referenced_table_catalog","type":"varchar(64)"},{"name":"referenced_table_schema","type":"varchar(64)"},{"name":"referenced_table_name","type":"varchar(64)"},{"name":"where_clause","type":"varchar(64)"},{"name":"allow_insert","type":"tinyint"},{"name":"allow_update","type":"tinyint"},{"name":"allow_delete","type":"tinyint"},{"name":"read_only","type":"tinyint"},{"name":"is_root_table","type":"tinyint"},{"name":"referenced_table_id","type":"int"},{"name":"referenced_table_parent_id","type":"int"},{"name":"referenced_table_parent_relationship","type":"varchar(64)"}]} +{"catalog":"def","schema":"information_schema","name":"keywords","kind":"v","columns":[{"name":"word","type":"varchar(128)"},{"name":"reserved","type":"int"}]} +{"catalog":"def","schema":"information_schema","name":"key_column_usage","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar(64)","not_null":true},{"name":"constraint_schema","type":"varchar(64)","not_null":true},{"name":"constraint_name","type":"varchar(64)"},{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"column_name","type":"varchar(64)"},{"name":"ordinal_position","type":"int unsigned","not_null":true},{"name":"position_in_unique_constraint","type":"int unsigned"},{"name":"referenced_table_schema","type":"varchar(64)"},{"name":"referenced_table_name","type":"varchar(64)"},{"name":"referenced_column_name","type":"varchar(64)"}]} +{"catalog":"def","schema":"information_schema","name":"libraries","kind":"v","columns":[{"name":"library_catalog","type":"varchar(64)","not_null":true},{"name":"library_schema","type":"varchar(64)","not_null":true},{"name":"library_name","type":"varchar(64)","not_null":true},{"name":"library_definition","type":"longtext"},{"name":"language","type":"varchar(64)","not_null":true},{"name":"created","type":"timestamp","not_null":true},{"name":"last_altered","type":"timestamp","not_null":true},{"name":"sql_mode","type":"set('real_as_float','pipes_as_concat','ansi_quotes','ignore_space','not_used','only_full_group_by','no_unsigned_subtraction','no_dir_in_create','not_used_9','not_used_10','not_used_11','not_used_12','not_used_13','not_used_14','not_used_15','not_used_16','not_used_17','not_used_18','ansi','no_auto_value_on_zero','no_backslash_escapes','strict_trans_tables','strict_all_tables','no_zero_in_date','no_zero_date','allow_invalid_dates','error_for_division_by_zero','traditional','not_used_29','high_not_precedence','no_engine_substitution','pad_char_to_full_length','time_truncate_fractional','interpret_utf8_as_utf8mb4')","not_null":true},{"name":"library_comment","type":"text","not_null":true},{"name":"creator","type":"varchar(288)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"optimizer_trace","kind":"v","columns":[{"name":"query","type":"varchar(65535)","not_null":true},{"name":"trace","type":"varchar(65535)","not_null":true},{"name":"missing_bytes_beyond_max_mem_size","type":"int","not_null":true},{"name":"insufficient_privileges","type":"tinyint(1)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"parameters","kind":"v","columns":[{"name":"specific_catalog","type":"varchar(64)","not_null":true},{"name":"specific_schema","type":"varchar(64)","not_null":true},{"name":"specific_name","type":"varchar(64)","not_null":true},{"name":"ordinal_position","type":"bigint unsigned","not_null":true},{"name":"parameter_mode","type":"varchar(5)"},{"name":"parameter_name","type":"varchar(64)"},{"name":"data_type","type":"longtext"},{"name":"character_maximum_length","type":"bigint"},{"name":"character_octet_length","type":"bigint"},{"name":"numeric_precision","type":"int unsigned"},{"name":"numeric_scale","type":"bigint"},{"name":"datetime_precision","type":"int unsigned"},{"name":"character_set_name","type":"varchar(64)"},{"name":"collation_name","type":"varchar(64)"},{"name":"dtd_identifier","type":"mediumtext","not_null":true},{"name":"routine_type","type":"enum('function','procedure','library')","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"partitions","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"partition_name","type":"varchar(64)"},{"name":"subpartition_name","type":"varchar(64)"},{"name":"partition_ordinal_position","type":"int unsigned"},{"name":"subpartition_ordinal_position","type":"int unsigned"},{"name":"secondary_load","type":"varchar(1)"},{"name":"partition_method","type":"varchar(13)"},{"name":"subpartition_method","type":"varchar(13)"},{"name":"partition_expression","type":"varchar(2048)"},{"name":"subpartition_expression","type":"varchar(2048)"},{"name":"partition_description","type":"text"},{"name":"table_rows","type":"bigint unsigned"},{"name":"avg_row_length","type":"bigint unsigned"},{"name":"data_length","type":"bigint unsigned"},{"name":"max_data_length","type":"bigint unsigned"},{"name":"index_length","type":"bigint unsigned"},{"name":"data_free","type":"bigint unsigned"},{"name":"create_time","type":"timestamp","not_null":true},{"name":"update_time","type":"datetime"},{"name":"check_time","type":"datetime"},{"name":"checksum","type":"bigint"},{"name":"partition_comment","type":"text","not_null":true},{"name":"nodegroup","type":"varchar(256)"},{"name":"tablespace_name","type":"varchar(268)"}]} +{"catalog":"def","schema":"information_schema","name":"plugins","kind":"v","columns":[{"name":"plugin_name","type":"varchar(64)","not_null":true},{"name":"plugin_version","type":"varchar(20)","not_null":true},{"name":"plugin_status","type":"varchar(10)","not_null":true},{"name":"plugin_type","type":"varchar(80)","not_null":true},{"name":"plugin_type_version","type":"varchar(20)","not_null":true},{"name":"plugin_library","type":"varchar(64)"},{"name":"plugin_library_version","type":"varchar(20)"},{"name":"plugin_author","type":"varchar(64)"},{"name":"plugin_description","type":"varchar(65535)"},{"name":"plugin_license","type":"varchar(80)"},{"name":"load_option","type":"varchar(64)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"processlist","kind":"v","columns":[{"name":"id","type":"bigint unsigned","not_null":true},{"name":"user","type":"varchar(32)","not_null":true},{"name":"host","type":"varchar(261)","not_null":true},{"name":"db","type":"varchar(64)"},{"name":"command","type":"varchar(16)","not_null":true},{"name":"time","type":"int","not_null":true},{"name":"state","type":"varchar(64)"},{"name":"info","type":"varchar(65535)"}]} +{"catalog":"def","schema":"information_schema","name":"profiling","kind":"v","columns":[{"name":"query_id","type":"int","not_null":true},{"name":"seq","type":"int","not_null":true},{"name":"state","type":"varchar(30)","not_null":true},{"name":"duration","type":"decimal(905,0)","not_null":true},{"name":"cpu_user","type":"decimal(905,0)"},{"name":"cpu_system","type":"decimal(905,0)"},{"name":"context_voluntary","type":"int"},{"name":"context_involuntary","type":"int"},{"name":"block_ops_in","type":"int"},{"name":"block_ops_out","type":"int"},{"name":"messages_sent","type":"int"},{"name":"messages_received","type":"int"},{"name":"page_faults_major","type":"int"},{"name":"page_faults_minor","type":"int"},{"name":"swaps","type":"int"},{"name":"source_function","type":"varchar(30)"},{"name":"source_file","type":"varchar(20)"},{"name":"source_line","type":"int"}]} +{"catalog":"def","schema":"information_schema","name":"referential_constraints","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar(64)","not_null":true},{"name":"constraint_schema","type":"varchar(64)","not_null":true},{"name":"constraint_name","type":"varchar(64)"},{"name":"unique_constraint_catalog","type":"varchar(64)","not_null":true},{"name":"unique_constraint_schema","type":"varchar(64)","not_null":true},{"name":"unique_constraint_name","type":"varchar(64)"},{"name":"match_option","type":"enum('none','partial','full')","not_null":true},{"name":"update_rule","type":"enum('no action','restrict','cascade','set null','set default')","not_null":true},{"name":"delete_rule","type":"enum('no action','restrict','cascade','set null','set default')","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"referenced_table_name","type":"varchar(64)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"resource_groups","kind":"v","columns":[{"name":"resource_group_name","type":"varchar(64)","not_null":true},{"name":"resource_group_type","type":"enum('system','user')","not_null":true},{"name":"resource_group_enabled","type":"tinyint(1)","not_null":true},{"name":"vcpu_ids","type":"blob"},{"name":"thread_priority","type":"int","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"role_column_grants","kind":"v","columns":[{"name":"grantor","type":"varchar(97)"},{"name":"grantor_host","type":"varchar(256)"},{"name":"grantee","type":"char(32)","not_null":true},{"name":"grantee_host","type":"char(255)","not_null":true},{"name":"table_catalog","type":"varchar(3)","not_null":true},{"name":"table_schema","type":"char(64)","not_null":true},{"name":"table_name","type":"char(64)","not_null":true},{"name":"column_name","type":"char(64)","not_null":true},{"name":"privilege_type","type":"set('select','insert','update','references')","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"role_routine_grants","kind":"v","columns":[{"name":"grantor","type":"varchar(97)"},{"name":"grantor_host","type":"varchar(256)"},{"name":"grantee","type":"char(32)","not_null":true},{"name":"grantee_host","type":"char(255)","not_null":true},{"name":"specific_catalog","type":"varchar(3)","not_null":true},{"name":"specific_schema","type":"char(64)","not_null":true},{"name":"specific_name","type":"char(64)","not_null":true},{"name":"routine_catalog","type":"varchar(3)","not_null":true},{"name":"routine_schema","type":"char(64)","not_null":true},{"name":"routine_name","type":"char(64)","not_null":true},{"name":"privilege_type","type":"set('execute','alter routine','grant')","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"role_table_grants","kind":"v","columns":[{"name":"grantor","type":"varchar(97)"},{"name":"grantor_host","type":"varchar(256)"},{"name":"grantee","type":"char(32)","not_null":true},{"name":"grantee_host","type":"char(255)","not_null":true},{"name":"table_catalog","type":"varchar(3)","not_null":true},{"name":"table_schema","type":"char(64)","not_null":true},{"name":"table_name","type":"char(64)","not_null":true},{"name":"privilege_type","type":"set('select','insert','update','delete','create','drop','grant','references','index','alter','create view','show view','trigger')","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"routines","kind":"v","columns":[{"name":"specific_name","type":"varchar(64)","not_null":true},{"name":"routine_catalog","type":"varchar(64)","not_null":true},{"name":"routine_schema","type":"varchar(64)","not_null":true},{"name":"routine_name","type":"varchar(64)","not_null":true},{"name":"routine_type","type":"enum('function','procedure','library')","not_null":true},{"name":"data_type","type":"longtext"},{"name":"character_maximum_length","type":"bigint"},{"name":"character_octet_length","type":"bigint"},{"name":"numeric_precision","type":"int unsigned"},{"name":"numeric_scale","type":"int unsigned"},{"name":"datetime_precision","type":"int unsigned"},{"name":"character_set_name","type":"varchar(64)"},{"name":"collation_name","type":"varchar(64)"},{"name":"dtd_identifier","type":"longtext"},{"name":"routine_body","type":"varchar(8)","not_null":true},{"name":"routine_definition","type":"longtext"},{"name":"external_name","type":"varbinary(0)"},{"name":"external_language","type":"varchar(64)","not_null":true},{"name":"parameter_style","type":"varchar(3)","not_null":true},{"name":"is_deterministic","type":"varchar(3)","not_null":true},{"name":"sql_data_access","type":"enum('contains sql','no sql','reads sql data','modifies sql data')","not_null":true},{"name":"sql_path","type":"varbinary(0)"},{"name":"security_type","type":"enum('default','invoker','definer')","not_null":true},{"name":"created","type":"timestamp","not_null":true},{"name":"last_altered","type":"timestamp","not_null":true},{"name":"sql_mode","type":"set('real_as_float','pipes_as_concat','ansi_quotes','ignore_space','not_used','only_full_group_by','no_unsigned_subtraction','no_dir_in_create','not_used_9','not_used_10','not_used_11','not_used_12','not_used_13','not_used_14','not_used_15','not_used_16','not_used_17','not_used_18','ansi','no_auto_value_on_zero','no_backslash_escapes','strict_trans_tables','strict_all_tables','no_zero_in_date','no_zero_date','allow_invalid_dates','error_for_division_by_zero','traditional','not_used_29','high_not_precedence','no_engine_substitution','pad_char_to_full_length','time_truncate_fractional','interpret_utf8_as_utf8mb4')","not_null":true},{"name":"routine_comment","type":"text","not_null":true},{"name":"definer","type":"varchar(288)","not_null":true},{"name":"character_set_client","type":"varchar(64)","not_null":true},{"name":"collation_connection","type":"varchar(64)","not_null":true},{"name":"database_collation","type":"varchar(64)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"routine_libraries","kind":"v","columns":[{"name":"routine_catalog","type":"varchar(64)","not_null":true},{"name":"routine_schema","type":"varchar(64)","not_null":true},{"name":"routine_name","type":"varchar(64)","not_null":true},{"name":"routine_type","type":"enum('function','procedure','library')","not_null":true},{"name":"library_catalog","type":"varchar(64)"},{"name":"library_schema","type":"varchar(100)"},{"name":"library_name","type":"varchar(100)"},{"name":"library_version","type":"varchar(100)"}]} +{"catalog":"def","schema":"information_schema","name":"schemata","kind":"v","columns":[{"name":"catalog_name","type":"varchar(64)","not_null":true},{"name":"schema_name","type":"varchar(64)","not_null":true},{"name":"default_character_set_name","type":"varchar(64)","not_null":true},{"name":"default_collation_name","type":"varchar(64)","not_null":true},{"name":"sql_path","type":"varbinary(0)"},{"name":"default_encryption","type":"enum('no','yes')","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"schemata_extensions","kind":"v","columns":[{"name":"catalog_name","type":"varchar(64)","not_null":true},{"name":"schema_name","type":"varchar(64)","not_null":true},{"name":"options","type":"varchar(256)"}]} +{"catalog":"def","schema":"information_schema","name":"schema_privileges","kind":"v","columns":[{"name":"grantee","type":"varchar(292)","not_null":true},{"name":"table_catalog","type":"varchar(512)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"privilege_type","type":"varchar(64)","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"statistics","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"non_unique","type":"int","not_null":true},{"name":"index_schema","type":"varchar(64)","not_null":true},{"name":"index_name","type":"varchar(64)"},{"name":"seq_in_index","type":"int unsigned","not_null":true},{"name":"column_name","type":"varchar(64)"},{"name":"collation","type":"varchar(1)"},{"name":"cardinality","type":"bigint"},{"name":"sub_part","type":"bigint"},{"name":"packed","type":"varbinary(0)"},{"name":"nullable","type":"varchar(3)","not_null":true},{"name":"index_type","type":"varchar(11)","not_null":true},{"name":"comment","type":"varchar(8)","not_null":true},{"name":"index_comment","type":"varchar(2048)","not_null":true},{"name":"is_visible","type":"varchar(3)","not_null":true},{"name":"expression","type":"longtext"}]} +{"catalog":"def","schema":"information_schema","name":"st_geometry_columns","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"column_name","type":"varchar(64)"},{"name":"srs_name","type":"varchar(80)"},{"name":"srs_id","type":"int unsigned"},{"name":"geometry_type_name","type":"longtext"}]} +{"catalog":"def","schema":"information_schema","name":"st_spatial_reference_systems","kind":"v","columns":[{"name":"srs_name","type":"varchar(80)","not_null":true},{"name":"srs_id","type":"int unsigned","not_null":true},{"name":"organization","type":"varchar(256)"},{"name":"organization_coordsys_id","type":"int unsigned"},{"name":"definition","type":"varchar(4096)","not_null":true},{"name":"description","type":"varchar(2048)"}]} +{"catalog":"def","schema":"information_schema","name":"st_units_of_measure","kind":"v","columns":[{"name":"unit_name","type":"varchar(255)"},{"name":"unit_type","type":"varchar(7)"},{"name":"conversion_factor","type":"double"},{"name":"description","type":"varchar(255)"}]} +{"catalog":"def","schema":"information_schema","name":"tables","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"table_type","type":"enum('base table','view','system view')","not_null":true},{"name":"engine","type":"varchar(64)"},{"name":"version","type":"int"},{"name":"row_format","type":"enum('fixed','dynamic','compressed','redundant','compact','paged')"},{"name":"table_rows","type":"bigint unsigned"},{"name":"avg_row_length","type":"bigint unsigned"},{"name":"data_length","type":"bigint unsigned"},{"name":"max_data_length","type":"bigint unsigned"},{"name":"index_length","type":"bigint unsigned"},{"name":"data_free","type":"bigint unsigned"},{"name":"auto_increment","type":"bigint unsigned"},{"name":"create_time","type":"timestamp","not_null":true},{"name":"update_time","type":"datetime"},{"name":"check_time","type":"datetime"},{"name":"table_collation","type":"varchar(64)"},{"name":"checksum","type":"bigint"},{"name":"create_options","type":"varchar(256)"},{"name":"table_comment","type":"text"}]} +{"catalog":"def","schema":"information_schema","name":"tablespaces_extensions","kind":"v","columns":[{"name":"tablespace_name","type":"varchar(268)","not_null":true},{"name":"engine_attribute","type":"json"}]} +{"catalog":"def","schema":"information_schema","name":"tables_extensions","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"engine_attribute","type":"json"},{"name":"secondary_engine_attribute","type":"json"}]} +{"catalog":"def","schema":"information_schema","name":"table_constraints","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar(64)","not_null":true},{"name":"constraint_schema","type":"varchar(64)","not_null":true},{"name":"constraint_name","type":"varchar(64)"},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"constraint_type","type":"varchar(11)","not_null":true},{"name":"enforced","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"table_constraints_extensions","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar(64)","not_null":true},{"name":"constraint_schema","type":"varchar(64)","not_null":true},{"name":"constraint_name","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"engine_attribute","type":"json"},{"name":"secondary_engine_attribute","type":"json"}]} +{"catalog":"def","schema":"information_schema","name":"table_privileges","kind":"v","columns":[{"name":"grantee","type":"varchar(292)","not_null":true},{"name":"table_catalog","type":"varchar(512)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"privilege_type","type":"varchar(64)","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"triggers","kind":"v","columns":[{"name":"trigger_catalog","type":"varchar(64)","not_null":true},{"name":"trigger_schema","type":"varchar(64)","not_null":true},{"name":"trigger_name","type":"varchar(64)","not_null":true},{"name":"event_manipulation","type":"enum('insert','update','delete')","not_null":true},{"name":"event_object_catalog","type":"varchar(64)","not_null":true},{"name":"event_object_schema","type":"varchar(64)","not_null":true},{"name":"event_object_table","type":"varchar(64)","not_null":true},{"name":"action_order","type":"int unsigned","not_null":true},{"name":"action_condition","type":"varbinary(0)"},{"name":"action_statement","type":"longtext","not_null":true},{"name":"action_orientation","type":"varchar(3)","not_null":true},{"name":"action_timing","type":"enum('before','after')","not_null":true},{"name":"action_reference_old_table","type":"varbinary(0)"},{"name":"action_reference_new_table","type":"varbinary(0)"},{"name":"action_reference_old_row","type":"varchar(3)","not_null":true},{"name":"action_reference_new_row","type":"varchar(3)","not_null":true},{"name":"created","type":"timestamp(2)","not_null":true},{"name":"sql_mode","type":"set('real_as_float','pipes_as_concat','ansi_quotes','ignore_space','not_used','only_full_group_by','no_unsigned_subtraction','no_dir_in_create','not_used_9','not_used_10','not_used_11','not_used_12','not_used_13','not_used_14','not_used_15','not_used_16','not_used_17','not_used_18','ansi','no_auto_value_on_zero','no_backslash_escapes','strict_trans_tables','strict_all_tables','no_zero_in_date','no_zero_date','allow_invalid_dates','error_for_division_by_zero','traditional','not_used_29','high_not_precedence','no_engine_substitution','pad_char_to_full_length','time_truncate_fractional','interpret_utf8_as_utf8mb4')","not_null":true},{"name":"definer","type":"varchar(288)","not_null":true},{"name":"character_set_client","type":"varchar(64)","not_null":true},{"name":"collation_connection","type":"varchar(64)","not_null":true},{"name":"database_collation","type":"varchar(64)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"user_attributes","kind":"v","columns":[{"name":"user","type":"char(32)","not_null":true},{"name":"host","type":"char(255)","not_null":true},{"name":"attribute","type":"longtext"}]} +{"catalog":"def","schema":"information_schema","name":"user_privileges","kind":"v","columns":[{"name":"grantee","type":"varchar(292)","not_null":true},{"name":"table_catalog","type":"varchar(512)","not_null":true},{"name":"privilege_type","type":"varchar(64)","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"views","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"view_definition","type":"longtext"},{"name":"check_option","type":"enum('none','local','cascaded')"},{"name":"is_updatable","type":"enum('no','yes')"},{"name":"definer","type":"varchar(288)"},{"name":"security_type","type":"varchar(7)"},{"name":"character_set_client","type":"varchar(64)","not_null":true},{"name":"collation_connection","type":"varchar(64)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"view_routine_usage","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"specific_catalog","type":"varchar(64)","not_null":true},{"name":"specific_schema","type":"varchar(64)","not_null":true},{"name":"specific_name","type":"varchar(64)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"view_table_usage","kind":"v","columns":[{"name":"view_catalog","type":"varchar(64)","not_null":true},{"name":"view_schema","type":"varchar(64)","not_null":true},{"name":"view_name","type":"varchar(64)","not_null":true},{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true}]} diff --git a/internal/engine/dolphin/dialect/types.jsonl b/internal/engine/dolphin/dialect/types.jsonl index 9b0d0c7192..f7f3e8432f 100644 --- a/internal/engine/dolphin/dialect/types.jsonl +++ b/internal/engine/dolphin/dialect/types.jsonl @@ -1,12 +1,20 @@ {"name": "bool", "category": "B", "aliases": ["boolean"]} -{"name": "tinyint", "category": "N", "aliases": ["tinyint unsigned"]} -{"name": "smallint", "category": "N", "aliases": ["smallint unsigned"]} -{"name": "mediumint", "category": "N", "aliases": ["mediumint unsigned"]} -{"name": "int", "category": "N", "aliases": ["integer", "int unsigned"]} -{"name": "bigint", "category": "N", "aliases": ["signed", "unsigned", "bigint unsigned"]} -{"name": "float", "category": "N", "aliases": ["float unsigned"]} -{"name": "double", "category": "N", "aliases": ["double precision", "real", "double unsigned"]} -{"name": "decimal", "category": "N", "aliases": ["numeric", "dec", "fixed", "decimal unsigned"]} +{"name": "tinyint", "category": "N"} +{"name": "tinyint unsigned", "category": "N"} +{"name": "smallint", "category": "N"} +{"name": "smallint unsigned", "category": "N"} +{"name": "mediumint", "category": "N"} +{"name": "mediumint unsigned", "category": "N"} +{"name": "int", "category": "N", "aliases": ["integer"]} +{"name": "int unsigned", "category": "N", "aliases": ["integer unsigned"]} +{"name": "bigint", "category": "N", "aliases": ["signed", "bigint signed"]} +{"name": "bigint unsigned", "category": "N", "aliases": ["unsigned"]} +{"name": "float", "category": "N"} +{"name": "float unsigned", "category": "N"} +{"name": "double", "category": "N", "aliases": ["double precision", "real"]} +{"name": "double unsigned", "category": "N", "aliases": ["double precision unsigned", "real unsigned"]} +{"name": "decimal", "category": "N", "aliases": ["numeric", "dec", "fixed"]} +{"name": "decimal unsigned", "category": "N", "aliases": ["numeric unsigned", "dec unsigned", "fixed unsigned"]} {"name": "bit", "category": "N"} {"name": "char", "category": "S"} {"name": "varchar", "category": "S"} diff --git a/internal/engine/dolphin/seed.go b/internal/engine/dolphin/seed.go index ddaeffb9c0..e538879d0e 100644 --- a/internal/engine/dolphin/seed.go +++ b/internal/engine/dolphin/seed.go @@ -21,6 +21,34 @@ func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } +func init() { + core.RegisterCanonicalizer("mysql", canonicalize) +} + +// canonicalize rewrites a type the way MySQL stores it: a decimal declared +// without a precision is decimal(10,0), a float declared with one is a +// float or a double depending on it, and a boolean is a tinyint(1). +func canonicalize(t *core.TypeExpr) *core.TypeExpr { + switch { + case (t.Name == "decimal" || t.Name == "decimal unsigned") && len(t.Args) == 0: + p, s := int64(10), int64(0) + return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{{Int: &p}, {Int: &s}}} + case (t.Name == "decimal" || t.Name == "decimal unsigned") && len(t.Args) == 1 && t.Args[0].Int != nil: + s := int64(0) + return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{t.Args[0], {Int: &s}}} + case t.Name == "float" && len(t.Args) == 1 && t.Args[0].Int != nil: + name := "float" + if *t.Args[0].Int > 24 { + name = "double" + } + return &core.TypeExpr{Name: name, Nullable: t.Nullable} + case t.Name == "bool" || t.Name == "boolean": + one := int64(1) + return &core.TypeExpr{Name: "tinyint", Nullable: t.Nullable, Args: []core.TypeArg{{Int: &one}}} + } + return t +} + // stdlib is MySQL's functions in the form the catalog uses. They are embedded // in the binary and never change within a run, so they are read once. var stdlib = sync.OnceValue(func() []*catalog.Function { diff --git a/internal/goldeneye/mysql/analyze.go b/internal/goldeneye/mysql/analyze.go index b25c87c8a8..1ebd8660c6 100644 --- a/internal/goldeneye/mysql/analyze.go +++ b/internal/goldeneye/mysql/analyze.go @@ -1,9 +1,12 @@ package mysql import ( + "bytes" "context" "database/sql" + "encoding/json" "fmt" + "github.com/sqlc-dev/sqlc/internal/goldeneye/dialect" "os" "regexp" "strconv" @@ -76,13 +79,71 @@ func bind(sql string) (string, []placeholder) { return out, phs } -// column is what information_schema says about a column. +// column is what information_schema says about a column: its type as +// COLUMN_TYPE spells it, read into an expression. type column struct { name string - typ string + typ *analysis.TypeExpr nullable bool } +// typeOfColumn is a column's type as it was declared, from COLUMN_TYPE, which +// carries the arguments and the unsigned that DATA_TYPE does not: "decimal(10,2) +// unsigned" is the family "decimal unsigned" applied to 10 and 2, and +// "enum('a','b')" is enum applied to its members. A trailing word such as +// zerofill is part of the family too. +func typeOfColumn(columnType string) *analysis.TypeExpr { + s := strings.ToLower(strings.TrimSpace(columnType)) + open := strings.IndexByte(s, '(') + if open < 0 { + return &analysis.TypeExpr{Name: s} + } + close := strings.LastIndexByte(s, ')') + if close < open { + return &analysis.TypeExpr{Name: s} + } + name := strings.TrimSpace(s[:open]) + if rest := strings.TrimSpace(s[close+1:]); rest != "" { + name += " " + rest + } + t := &analysis.TypeExpr{Name: name} + for _, a := range splitArgs(s[open+1 : close]) { + a = strings.TrimSpace(a) + if strings.HasPrefix(a, "'") && strings.HasSuffix(a, "'") && len(a) >= 2 { + v := strings.ReplaceAll(a[1:len(a)-1], "''", "'") + t.Args = append(t.Args, analysis.TypeArg{String: &v}) + continue + } + if n, err := strconv.ParseInt(a, 10, 64); err == nil { + t.Args = append(t.Args, analysis.TypeArg{Int: &n}) + } + } + return t +} + +// splitArgs splits a type's argument list on the commas outside quotes. +func splitArgs(s string) []string { + var out []string + start, quoted := 0, false + for i := 0; i < len(s); i++ { + switch { + case s[i] == '\'': + quoted = !quoted + case s[i] == ',' && !quoted: + out = append(out, s[start:i]) + start = i + 1 + } + } + return append(out, s[start:]) +} + +// withNullable copies a type with its nullability set. +func withNullable(t *analysis.TypeExpr, nullable bool) *analysis.TypeExpr { + out := *t + out.Nullable = nullable + return &out +} + // relation is a table the catalog knows, by schema and name. type relation struct { schema, name string @@ -185,12 +246,48 @@ func Analyze(ctx context.Context, dsn string, c endtoend.Case) ([]byte, error) { // Check compares what MySQL reports for a case with the output the case // committed, returning a diff when they differ. +// Check compares a case's committed output with what MySQL reports. A +// column read from a table is compared in full, since information_schema +// spells its whole type; an expression's type comes from the wire, which +// carries the family and nothing of its arguments, so an expression, and +// a parameter typed by one, is compared by family alone. func Check(ctx context.Context, dsn string, c endtoend.Case) (string, error) { got, err := Analyze(ctx, dsn, c) if err != nil { return "", err } - return c.Compare(got) + want, err := os.ReadFile(c.Output) + if err != nil { + return "", err + } + var queries []analysis.Query + if err := json.Unmarshal(want, &queries); err != nil { + return "", fmt.Errorf("%s: %w", c.Output, err) + } + for i := range queries { + for j := range queries[i].Columns { + familyOnly(&queries[i].Columns[j]) + } + for j := range queries[i].Params { + familyOnly(&queries[i].Params[j].Column) + } + } + want, err = analysis.Encode(queries) + if err != nil { + return "", err + } + if bytes.Equal(want, got) { + return "", nil + } + return dialect.Diff(string(want), string(got)), nil +} + +// familyOnly drops the arguments of a column's type when the column is not +// read from a table, which is as much as the wire says about it. +func familyOnly(col *analysis.Column) { + if col.Table == "" && col.Type != nil { + col.Type.Args = nil + } } const catalogQuery = ` @@ -217,7 +314,7 @@ func readCatalog(ctx context.Context, conn *sql.Conn, db string) (map[relation][ rel := relation{schema, table} catalog[rel] = append(catalog[rel], column{ name: name, - typ: typeName(dataType, columnType), + typ: typeOfColumn(columnType), nullable: nullable == "YES", }) } @@ -313,7 +410,7 @@ func (a *analyzer) analyzeQuery(ctx context.Context, q endtoend.Query) (analysis ac.Table = rel.name } if col, ok := s.origin(top, tok, 0); ok { - ac.Type.Name = col.typ + ac.Type = withNullable(col.typ, ac.Type.Nullable) } } } @@ -582,7 +679,7 @@ func (s *statement) column(rel relation, name string) analysis.Column { if col.name == name { return analysis.Column{ Name: name, - Type: &analysis.TypeExpr{Name: col.typ, Nullable: col.nullable}, + Type: withNullable(col.typ, col.nullable), Table: rel.name, } } diff --git a/internal/goldeneye/mysql/relations.go b/internal/goldeneye/mysql/relations.go index 1dde46f89c..2eeff70439 100644 --- a/internal/goldeneye/mysql/relations.go +++ b/internal/goldeneye/mysql/relations.go @@ -34,10 +34,9 @@ ORDER BY t.TABLE_NAME, c.ORDINAL_POSITION` // 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 its data type as MySQL -// names it, with UNSIGNED kept as part of the name the way a column -// declaration spells it; the length and precision a declaration adds are -// not part of the type. +// 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 { @@ -69,13 +68,12 @@ func readRelations(ctx context.Context, conn *sql.Conn, schema string) ([]dialec return relations, rows.Err() } -// typeName spells a column's type the way a declaration does, from the -// DATA_TYPE and COLUMN_TYPE information_schema reports for it: "bigint", -// or "bigint unsigned" when the column is unsigned. +// 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')", in lower case. func typeName(dataType, columnType string) string { - name := strings.ToLower(dataType) - if strings.Contains(strings.ToLower(columnType), " unsigned") { - name += " unsigned" + if columnType == "" { + return strings.ToLower(dataType) } - return name + return strings.ToLower(columnType) } From 14c2a588f53ae8429aed39c97bfa06c7bca0e67b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 17:38:57 +0000 Subject: [PATCH 08/16] sqlite: a declared spelling stands on its affinity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A SQLite dialect setting makes each alias in types.jsonl a type of its own standing on the type it aliases, so a column declared VARCHAR(255) reports that spelling, as SQLite does, while comparing as text. A spelling neither seeded nor aliased — FOO BAR(3) — gets the affinity SQLite's rule gives it as its base, through a per-dialect hook the catalog asks when a schema declares a family it does not know. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- internal/core/hooks.go | 31 ++ internal/core/types.go | 17 +- .../testdata/analyze_types/sqlite/exec.json | 5 + .../testdata/analyze_types/sqlite/fixture.sql | 4 + .../testdata/analyze_types/sqlite/query.sql | 18 + .../testdata/analyze_types/sqlite/schema.sql | 24 ++ .../testdata/analyze_types/sqlite/stdout.json | 333 ++++++++++++++++++ internal/engine/sqlite/seed.go | 25 ++ 8 files changed, 455 insertions(+), 2 deletions(-) create mode 100644 internal/endtoend/testdata/analyze_types/sqlite/exec.json create mode 100644 internal/endtoend/testdata/analyze_types/sqlite/fixture.sql create mode 100644 internal/endtoend/testdata/analyze_types/sqlite/query.sql create mode 100644 internal/endtoend/testdata/analyze_types/sqlite/schema.sql create mode 100644 internal/endtoend/testdata/analyze_types/sqlite/stdout.json diff --git a/internal/core/hooks.go b/internal/core/hooks.go index 2b56cba68d..cb2c112e46 100644 --- a/internal/core/hooks.go +++ b/internal/core/hooks.go @@ -11,11 +11,42 @@ import "sync" // for what only code can say. type Canonicalizer func(*TypeExpr) *TypeExpr +// A UserTypeBase says what a type family the schema declared and the +// dialect did not seed stands on: SQLite gives every declared spelling one +// of five affinities by a rule over its words, so FOO BAR(3) compares as a +// numeric. It returns the base family's name and the category the new type +// takes, or an empty name for a type that stands on nothing. +type UserTypeBase func(name string) (base, category string) + var ( hooksMu sync.RWMutex canonicalizers = map[string]Canonicalizer{} + userTypeBases = map[string]UserTypeBase{} ) +// RegisterUserTypeBase installs the rule a dialect resolves an unseeded +// type family by, under the dialect's name. +func RegisterUserTypeBase(dialect string, fn UserTypeBase) { + hooksMu.Lock() + defer hooksMu.Unlock() + userTypeBases[dialect] = fn +} + +// userTypeBase applies the catalog's dialect's rule for an unseeded family. +func (c *Catalog) userTypeBase(name string) (base, category string) { + dialect := c.dialectName() + if dialect == "" { + return "", "" + } + hooksMu.RLock() + fn := userTypeBases[dialect] + hooksMu.RUnlock() + if fn == nil { + return "", "" + } + return fn(name) +} + // RegisterCanonicalizer installs the canonicalizer for a dialect, by the // name its dialect.json records. An engine registers its own at init, so // that a catalog restored from the cache — which runs no seed — finds it by diff --git a/internal/core/types.go b/internal/core/types.go index 84ae6199c2..d93373464d 100644 --- a/internal/core/types.go +++ b/internal/core/types.go @@ -146,13 +146,26 @@ func (c *Catalog) CreateUserType(name, category string) (int64, error) { if err != nil { return 0, fmt.Errorf("create type %q: %w", name, err) } - oid, err := c.CreateTypeSpec(TypeSpec{ + spec := TypeSpec{ Name: bare, NamespaceOID: nsOID, Typtype: typtype, Category: category, DialectOID: c.dialectOID, - }) + } + // A dialect may say what an unknown spelling stands on, as SQLite's + // affinity rule does; the type then resolves through that base and + // needs no operators of its own. + if category == "U" { + if base, cat := c.userTypeBase(bare); base != "" { + if baseOID, err := c.TypeOID(base); err == nil { + spec.BaseOID = baseOID + spec.Category = cat + return c.CreateTypeSpec(spec) + } + } + } + oid, err := c.CreateTypeSpec(spec) if err != nil { return 0, err } diff --git a/internal/endtoend/testdata/analyze_types/sqlite/exec.json b/internal/endtoend/testdata/analyze_types/sqlite/exec.json new file mode 100644 index 0000000000..aa77909cb2 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/sqlite/exec.json @@ -0,0 +1,5 @@ +{ + "command": "analyze", + "args": ["--dialect", "sqlite", "--schema", "schema.sql", "query.sql"], + "contexts": ["base"] +} diff --git a/internal/endtoend/testdata/analyze_types/sqlite/fixture.sql b/internal/endtoend/testdata/analyze_types/sqlite/fixture.sql new file mode 100644 index 0000000000..122acdd435 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/sqlite/fixture.sql @@ -0,0 +1,4 @@ +INSERT INTO things (id, title, code, price, flag, big, data, untyped, ratio, weird, created, n, label) +VALUES (1, 'abc', 'xy', 1.5, 1, 9000000000, x'00ff', 7, 2.5, '12', '2024-01-01 00:00:00', 3, 'ab'); +INSERT INTO strict_things (id, anything, body, amount, raw, n) VALUES (1, 'x', 'b', 1.5, x'00', 2); +INSERT INTO things (id, n) VALUES (2, 0); diff --git a/internal/endtoend/testdata/analyze_types/sqlite/query.sql b/internal/endtoend/testdata/analyze_types/sqlite/query.sql new file mode 100644 index 0000000000..315331198b --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/sqlite/query.sql @@ -0,0 +1,18 @@ +-- name: AllTypes :many +SELECT * FROM things; + +-- name: StrictTypes :many +SELECT * FROM strict_things; + +-- name: Casts :one +SELECT + CAST(title AS INTEGER) AS a, + CAST(n AS TEXT) AS b, + CAST(title AS REAL) AS d, + weird + 1 AS e, + title || 'x' AS f +FROM things; + +-- name: Params :one +SELECT id FROM things +WHERE title = ? AND price = ? AND untyped = ? AND weird = ? AND big = ? AND n = ?; diff --git a/internal/endtoend/testdata/analyze_types/sqlite/schema.sql b/internal/endtoend/testdata/analyze_types/sqlite/schema.sql new file mode 100644 index 0000000000..675a8d602a --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/sqlite/schema.sql @@ -0,0 +1,24 @@ +CREATE TABLE things ( + id INTEGER PRIMARY KEY, + title VARCHAR(255), + code VARYING CHARACTER(10), + price DECIMAL(10,5), + flag BOOLEAN, + big UNSIGNED BIG INT, + data BLOB, + untyped, + ratio DOUBLE PRECISION, + weird FOO BAR(3), + created DATETIME, + n INT NOT NULL, + label NCHAR(5) +); + +CREATE TABLE strict_things ( + id INTEGER, + anything ANY, + body TEXT, + amount REAL, + raw BLOB, + n INT +) STRICT; diff --git a/internal/endtoend/testdata/analyze_types/sqlite/stdout.json b/internal/endtoend/testdata/analyze_types/sqlite/stdout.json new file mode 100644 index 0000000000..7d992f9f73 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/sqlite/stdout.json @@ -0,0 +1,333 @@ +[ + { + "name": "AllTypes", + "cmd": ":many", + "columns": [ + { + "name": "id", + "type": { + "name": "integer" + }, + "table": "things" + }, + { + "name": "title", + "type": { + "name": "varchar", + "nullable": true, + "args": [ + { + "int": 255 + } + ] + }, + "table": "things" + }, + { + "name": "code", + "type": { + "name": "varying character", + "nullable": true, + "args": [ + { + "int": 10 + } + ] + }, + "table": "things" + }, + { + "name": "price", + "type": { + "name": "decimal", + "nullable": true, + "args": [ + { + "int": 10 + }, + { + "int": 5 + } + ] + }, + "table": "things" + }, + { + "name": "flag", + "type": { + "name": "boolean", + "nullable": true + }, + "table": "things" + }, + { + "name": "big", + "type": { + "name": "unsigned big int", + "nullable": true + }, + "table": "things" + }, + { + "name": "data", + "type": { + "name": "blob", + "nullable": true + }, + "table": "things" + }, + { + "name": "untyped", + "type": { + "name": "any", + "nullable": true + }, + "table": "things" + }, + { + "name": "ratio", + "type": { + "name": "double precision", + "nullable": true + }, + "table": "things" + }, + { + "name": "weird", + "type": { + "name": "foo bar", + "nullable": true, + "args": [ + { + "int": 3 + } + ] + }, + "table": "things" + }, + { + "name": "created", + "type": { + "name": "datetime", + "nullable": true + }, + "table": "things" + }, + { + "name": "n", + "type": { + "name": "int" + }, + "table": "things" + }, + { + "name": "label", + "type": { + "name": "nchar", + "nullable": true, + "args": [ + { + "int": 5 + } + ] + }, + "table": "things" + } + ], + "params": [] + }, + { + "name": "StrictTypes", + "cmd": ":many", + "columns": [ + { + "name": "id", + "type": { + "name": "integer", + "nullable": true + }, + "table": "strict_things" + }, + { + "name": "anything", + "type": { + "name": "any", + "nullable": true + }, + "table": "strict_things" + }, + { + "name": "body", + "type": { + "name": "text", + "nullable": true + }, + "table": "strict_things" + }, + { + "name": "amount", + "type": { + "name": "real", + "nullable": true + }, + "table": "strict_things" + }, + { + "name": "raw", + "type": { + "name": "blob", + "nullable": true + }, + "table": "strict_things" + }, + { + "name": "n", + "type": { + "name": "int", + "nullable": true + }, + "table": "strict_things" + } + ], + "params": [] + }, + { + "name": "Casts", + "cmd": ":one", + "columns": [ + { + "name": "a", + "type": { + "name": "integer", + "nullable": true + } + }, + { + "name": "b", + "type": { + "name": "text" + } + }, + { + "name": "d", + "type": { + "name": "real", + "nullable": true + } + }, + { + "name": "e", + "type": { + "name": "integer", + "nullable": true + } + }, + { + "name": "f", + "type": { + "name": "text", + "nullable": true + } + } + ], + "params": [] + }, + { + "name": "Params", + "cmd": ":one", + "columns": [ + { + "name": "id", + "type": { + "name": "integer" + }, + "table": "things" + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "title", + "type": { + "name": "varchar", + "nullable": true, + "args": [ + { + "int": 255 + } + ] + }, + "table": "things" + } + }, + { + "number": 2, + "column": { + "name": "price", + "type": { + "name": "decimal", + "nullable": true, + "args": [ + { + "int": 10 + }, + { + "int": 5 + } + ] + }, + "table": "things" + } + }, + { + "number": 3, + "column": { + "name": "untyped", + "type": { + "name": "any", + "nullable": true + }, + "table": "things" + } + }, + { + "number": 4, + "column": { + "name": "weird", + "type": { + "name": "foo bar", + "nullable": true, + "args": [ + { + "int": 3 + } + ] + }, + "table": "things" + } + }, + { + "number": 5, + "column": { + "name": "big", + "type": { + "name": "unsigned big int", + "nullable": true + }, + "table": "things" + } + }, + { + "number": 6, + "column": { + "name": "n", + "type": { + "name": "int" + }, + "table": "things" + } + } + ] + } +] diff --git a/internal/engine/sqlite/seed.go b/internal/engine/sqlite/seed.go index cc439b43b7..b115b1025a 100644 --- a/internal/engine/sqlite/seed.go +++ b/internal/engine/sqlite/seed.go @@ -2,6 +2,7 @@ package sqlite import ( "embed" + "strings" "sync" "github.com/sqlc-dev/sqlc/internal/core" @@ -21,6 +22,30 @@ func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } +func init() { + core.RegisterUserTypeBase("sqlite", affinity) +} + +// affinity is the type a declared spelling SQLite has no name for stands +// on: the affinity its rule gives it, decided by the words in it. INT +// anywhere is INTEGER; CHAR, CLOB or TEXT is TEXT; BLOB is BLOB; REAL, FLOA +// or DOUB is REAL; anything else is NUMERIC. A column with no type at all +// has BLOB affinity, but sqlc reads one as any. +func affinity(name string) (base, category string) { + upper := strings.ToUpper(name) + switch { + case strings.Contains(upper, "INT"): + return "integer", "N" + case strings.Contains(upper, "CHAR"), strings.Contains(upper, "CLOB"), strings.Contains(upper, "TEXT"): + return "text", "S" + case strings.Contains(upper, "BLOB"): + return "blob", "U" + case strings.Contains(upper, "REAL"), strings.Contains(upper, "FLOA"), strings.Contains(upper, "DOUB"): + return "real", "N" + } + return "numeric", "N" +} + // stdlib is SQLite's functions in the form the catalog uses. They are embedded // in the binary and never change within a run, so they are read once. var stdlib = sync.OnceValue(func() []*catalog.Function { From efc83d36d6832d09a19b1d1fca24bf841e7e966c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 17:48:36 +0000 Subject: [PATCH 09/16] clickhouse: casts, typed placeholders, aggregate-function idents, canonical forms and Nested columns A cast and a {name:Type} placeholder hand the core their whole spelling, so CAST(x AS Nullable(String)) reports a nullable string and a placeholder reports the type it declares, named as the query named it. A per-dialect canonicalizer stores what ClickHouse stores: Decimal32(4) as Decimal(9, 4), Enum('a', 'b') as Enum8('a' = 1, 'b' = 2), a Variant's members sorted, and the function an AggregateFunction names as a word. A result-type rule types toDecimal64(x, 4) as Decimal(18, 4) from the literal. A Nested column becomes the array columns ClickHouse stores it as, reachable as n.a, and goldeneye's rewriter binds {name:Type} like any placeholder. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- internal/core/analyzer/expr.go | 64 +++- internal/core/analyzer/projection.go | 6 + internal/core/hooks.go | 38 ++ .../analyze_types/clickhouse/fixture.sql | 2 +- .../analyze_types/clickhouse/query.sql | 16 +- .../analyze_types/clickhouse/schema.sql | 8 +- .../analyze_types/clickhouse/stdout.json | 345 ++++++++++++++++++ internal/engine/clickhouse/convert.go | 59 ++- internal/engine/clickhouse/seed.go | 88 +++++ internal/goldeneye/analysis/analysis.go | 5 +- internal/goldeneye/clickhouse/types.go | 11 +- internal/goldeneye/endtoend/query.go | 15 +- internal/sql/ast/param_ref.go | 3 + 13 files changed, 636 insertions(+), 24 deletions(-) diff --git a/internal/core/analyzer/expr.go b/internal/core/analyzer/expr.go index 7d9ed857ea..624906be26 100644 --- a/internal/core/analyzer/expr.go +++ b/internal/core/analyzer/expr.go @@ -161,6 +161,14 @@ func (a *analyzer) typeColumnRef(c *ast.ColumnRef) (exprType, error) { if err != nil { return exprType{}, err } + // A dotted name may be a column's own, as ClickHouse names the columns + // a Nested column stores as n.a. + if !ok && relation != "" { + rel, col, ok, err = a.resolveColumn("", relation+"."+column) + if err != nil { + return exprType{}, err + } + } if !ok { if relation != "" { return exprType{}, fmt.Errorf("unknown column %q.%q", relation, column) @@ -195,10 +203,10 @@ func flattenFields(fields *ast.List) []string { func (a *analyzer) typeParamRef(p *ast.ParamRef) (exprType, error) { cur, ok := a.params[p.Number] if !ok { - cur = core.Parameter{Number: p.Number} + cur = core.Parameter{Number: p.Number, Name: p.Name} a.params[p.Number] = cur } - return exprType{typeOID: cur.TypeOID, nullable: !cur.NotNull}, nil + return exprType{typeOID: cur.TypeOID, expr: cur.Type.WithNullable(false), nullable: !cur.NotNull}, nil } func (a *analyzer) inferParam(number int, t exprType) { @@ -296,11 +304,17 @@ func (a *analyzer) typeAExpr(e *ast.A_Expr) (exprType, error) { a.inferParam(pr.Number, rightT) a.nameParamAfter(pr.Number, e.Rexpr) leftT = rightT + } else if pr := castParamRef(e.Lexpr); pr != nil { + a.inferParam(pr.Number, rightT) + a.nameParamAfter(pr.Number, e.Rexpr) } if pr, ok := e.Rexpr.(*ast.ParamRef); ok && leftT.typeOID != 0 { a.inferParam(pr.Number, leftT) a.nameParamAfter(pr.Number, e.Lexpr) rightT = leftT + } else if pr := castParamRef(e.Rexpr); pr != nil { + a.inferParam(pr.Number, leftT) + a.nameParamAfter(pr.Number, e.Lexpr) } overload, err := a.resolveOperator(opName, leftT, rightT) @@ -316,6 +330,18 @@ func (a *analyzer) typeAExpr(e *ast.A_Expr) (exprType, error) { }, nil } +// castParamRef is the placeholder a cast wraps, as ClickHouse's {p:UInt64} +// is written, or nil. The cast has typed it already; what it is compared +// with still names it and says which column it stands in for. +func castParamRef(n ast.Node) *ast.ParamRef { + tc, ok := n.(*ast.TypeCast) + if !ok { + return nil + } + pr, _ := tc.Arg.(*ast.ParamRef) + return pr +} + // isNullTest reports whether an operator compares with NULL as a value // rather than propagating it, so that its result is never NULL. func isNullTest(opName string) bool { @@ -633,6 +659,11 @@ func (a *analyzer) typeNullIf(e *ast.A_Expr) (exprType, error) { // placeholder that type. func (a *analyzer) typeOperands(n ast.Node, other exprType) error { if pr, ok := n.(*ast.ParamRef); ok { + // Registering the placeholder first keeps the name its syntax gave + // it, as ClickHouse's {name:Type} does. + if _, err := a.typeParamRef(pr); err != nil { + return err + } if other.typeOID != 0 || other.expr != nil { a.inferParam(pr.Number, other) } @@ -798,6 +829,11 @@ func (a *analyzer) typeFuncCall(f *ast.FuncCall) (exprType, error) { } } ret := a.returnType(p, argTypes) + // A dialect may know the result better than the catalog does, from the + // arguments' types and literal values. + if computed := a.resultTypeHook(name, args, argTypes); computed != nil { + ret = a.lookupType(computed) + } ret.nullable = p.ReturnNullable if !p.NeverNull && anyNullable && a.cat.PropagatesNullable() { ret.nullable = true @@ -805,6 +841,22 @@ func (a *analyzer) typeFuncCall(f *ast.FuncCall) (exprType, error) { return ret, nil } +// resultTypeHook asks the dialect's result-type rule about a call, handing +// it each argument's type and, for an integer literal, its value. +func (a *analyzer) resultTypeHook(name string, args []ast.Node, argTypes []exprType) *core.TypeExpr { + ras := make([]core.ResultArg, len(args)) + for i, arg := range args { + ras[i].Type = a.exprOf(argTypes[i]) + if c, ok := arg.(*ast.A_Const); ok { + if n, ok := c.Val.(*ast.Integer); ok { + v := n.Ival + ras[i].Int = &v + } + } + } + return a.cat.ResultTypeOf(name, ras) +} + // returnType resolves a polymorphic return type — max(anyelement), or a // seed's "$2" for the type of the second argument — to the type the call was // made with. @@ -933,8 +985,10 @@ func (a *analyzer) typeTypeCast(c *ast.TypeCast) (exprType, error) { } t := a.lookupType(target) // A cast is how a query says what an otherwise untyped placeholder - // holds, and a placeholder so typed is not null. Anything else cast - // is NULL exactly when it was NULL before. + // holds, and a placeholder so typed is not null unless the type says + // otherwise, as ClickHouse's Nullable(String) does. Anything else + // cast is NULL when it was NULL before, or when the type says so. + t.nullable = target.Nullable if pr, ok := c.Arg.(*ast.ParamRef); ok { if err := a.typeOperands(pr, t); err != nil { return exprType{}, err @@ -945,6 +999,6 @@ func (a *analyzer) typeTypeCast(c *ast.TypeCast) (exprType, error) { if err != nil { return exprType{}, err } - t.nullable = arg.nullable + t.nullable = t.nullable || arg.nullable return t, nil } diff --git a/internal/core/analyzer/projection.go b/internal/core/analyzer/projection.go index 7366997d8a..17fc3cbf47 100644 --- a/internal/core/analyzer/projection.go +++ b/internal/core/analyzer/projection.go @@ -2,6 +2,7 @@ package analyzer import ( "slices" + "strings" "github.com/sqlc-dev/sqlc/internal/core" "github.com/sqlc-dev/sqlc/internal/sql/ast" @@ -49,6 +50,11 @@ func (a *analyzer) projectTarget(rt *ast.ResTarget) error { col.Type = a.typeExprOf(t) a.decorateSource(&col, t.sourceAttributeOID, t.sourceTableAlias) if rt.Name == nil || *rt.Name == "" { + // A column whose own name is dotted, as ClickHouse's n.a is, is + // reported under that name rather than its last part. + if col.Source != nil && strings.Contains(col.Source.Column, ".") { + col.Name = col.Source.Column + } a.qualifyDuplicate(&col, t.sourceTableAlias) } a.columns = append(a.columns, col) diff --git a/internal/core/hooks.go b/internal/core/hooks.go index cb2c112e46..2c0ed6b543 100644 --- a/internal/core/hooks.go +++ b/internal/core/hooks.go @@ -18,12 +18,50 @@ type Canonicalizer func(*TypeExpr) *TypeExpr // takes, or an empty name for a type that stands on nothing. type UserTypeBase func(name string) (base, category string) +// A ResultArg is one argument of a function call as a result-type rule +// sees it: its type, when known, and its value when it is an integer +// literal, which is what the scale of toDecimal64(x, 4) is. +type ResultArg struct { + Type *TypeExpr + Int *int64 +} + +// A ResultType says what a function returns when that depends on its +// arguments in a way no seed can spell: ClickHouse's toDecimal64(x, s) is +// Decimal(18, s). It returns nil to leave the answer to the catalog. +type ResultType func(name string, args []ResultArg) *TypeExpr + var ( hooksMu sync.RWMutex canonicalizers = map[string]Canonicalizer{} userTypeBases = map[string]UserTypeBase{} + resultTypes = map[string]ResultType{} ) +// RegisterResultType installs a dialect's result-type rule, under the +// dialect's name. +func RegisterResultType(dialect string, fn ResultType) { + hooksMu.Lock() + defer hooksMu.Unlock() + resultTypes[dialect] = fn +} + +// ResultTypeOf applies the catalog's dialect's result-type rule to a call, +// or returns nil when there is none or it has nothing to say. +func (c *Catalog) ResultTypeOf(name string, args []ResultArg) *TypeExpr { + dialect := c.dialectName() + if dialect == "" { + return nil + } + hooksMu.RLock() + fn := resultTypes[dialect] + hooksMu.RUnlock() + if fn == nil { + return nil + } + return fn(name, args) +} + // RegisterUserTypeBase installs the rule a dialect resolves an unseeded // type family by, under the dialect's name. func RegisterUserTypeBase(dialect string, fn UserTypeBase) { diff --git a/internal/endtoend/testdata/analyze_types/clickhouse/fixture.sql b/internal/endtoend/testdata/analyze_types/clickhouse/fixture.sql index bb958af8a2..d4353b2dc8 100644 --- a/internal/endtoend/testdata/analyze_types/clickhouse/fixture.sql +++ b/internal/endtoend/testdata/analyze_types/clickhouse/fixture.sql @@ -1 +1 @@ -INSERT INTO things VALUES (1, 'a', NULL, 1.5, ['x'], [NULL, 'y'], [[1, 2]], 'k', '2024-01-01 00:00:00', '2024-01-01 00:00:00.123', 1.25, 'active', {'k': 1}, (1.0, 2.0), (51.5, -0.1), {'a': NULL}, '127.0.0.1', '00000000-0000-0000-0000-000000000000', 'abcd', true); +INSERT INTO things VALUES (1, 'a', NULL, 1.5, ['x'], [NULL, 'y'], [[1, 2]], 'k', '2024-01-01 00:00:00', '2024-01-01 00:00:00.123', 1.25, 'active', {'k': 1}, (1.0, 2.0), (51.5, -0.1), {'a': NULL}, '127.0.0.1', '00000000-0000-0000-0000-000000000000', 'abcd', true, 1.5, 'x', 'v', [1], ['s'], 3, 7); diff --git a/internal/endtoend/testdata/analyze_types/clickhouse/query.sql b/internal/endtoend/testdata/analyze_types/clickhouse/query.sql index c55355e50d..8a4ff3e8be 100644 --- a/internal/endtoend/testdata/analyze_types/clickhouse/query.sql +++ b/internal/endtoend/testdata/analyze_types/clickhouse/query.sql @@ -2,4 +2,18 @@ SELECT * FROM things; -- name: StarColumns :many -SELECT id, name, tag, amount, tags, labels, matrix, kind, created, updated, price, status, attrs, pos, geo, scores, ip, uid, fixed, flag FROM things; +SELECT id, name, tag, amount, tags, labels, matrix, kind, created, updated, price, status, attrs, pos, geo, scores, ip, uid, fixed, flag, small, plain, either, n.a, n.b, total, whole FROM things; + +-- name: Casts :one +SELECT + CAST(id AS String) AS a, + toDecimal64(id, 4) AS b, + CAST(name AS Nullable(String)) AS c, + toDateTime64(created, 3) AS d, + CAST(tags AS Array(String)) AS e, + CAST(tag AS Nullable(String)) AS f +FROM things; + +-- name: Placeholders :many +SELECT id FROM things +WHERE id = {p1:UInt64} AND name = {p2:String} AND amount > {p3:Float64} AND tag = {p4:Nullable(String)} AND price = {p5:Decimal(10, 2)}; diff --git a/internal/endtoend/testdata/analyze_types/clickhouse/schema.sql b/internal/endtoend/testdata/analyze_types/clickhouse/schema.sql index 5f78064628..1dddddd5c9 100644 --- a/internal/endtoend/testdata/analyze_types/clickhouse/schema.sql +++ b/internal/endtoend/testdata/analyze_types/clickhouse/schema.sql @@ -18,5 +18,11 @@ CREATE TABLE things ( ip IPv4, uid UUID, fixed FixedString(4), - flag Bool + flag Bool, + small Decimal32(4), + plain Enum('x', 'y'), + either Variant(String, Int64), + n Nested(a UInt8, b String), + total SimpleAggregateFunction(sum, UInt64), + whole INT ) ENGINE = MergeTree ORDER BY id; diff --git a/internal/endtoend/testdata/analyze_types/clickhouse/stdout.json b/internal/endtoend/testdata/analyze_types/clickhouse/stdout.json index 5ef32d6b93..47068fd88d 100644 --- a/internal/endtoend/testdata/analyze_types/clickhouse/stdout.json +++ b/internal/endtoend/testdata/analyze_types/clickhouse/stdout.json @@ -262,6 +262,109 @@ "name": "bool" }, "table": "things" + }, + { + "name": "small", + "type": { + "name": "decimal", + "args": [ + { + "int": 9 + }, + { + "int": 4 + } + ] + }, + "table": "things" + }, + { + "name": "plain", + "type": { + "name": "enum8", + "args": [ + { + "label": "x", + "int": 1 + }, + { + "label": "y", + "int": 2 + } + ] + }, + "table": "things" + }, + { + "name": "either", + "type": { + "name": "variant", + "args": [ + { + "type": { + "name": "int64" + } + }, + { + "type": { + "name": "string" + } + } + ] + }, + "table": "things" + }, + { + "name": "n.a", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "uint8" + } + } + ] + }, + "table": "things" + }, + { + "name": "n.b", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "string" + } + } + ] + }, + "table": "things" + }, + { + "name": "total", + "type": { + "name": "simpleaggregatefunction", + "args": [ + { + "ident": "sum" + }, + { + "type": { + "name": "uint64" + } + } + ] + }, + "table": "things" + }, + { + "name": "whole", + "type": { + "name": "int32" + }, + "table": "things" } ], "params": [] @@ -529,8 +632,250 @@ "name": "bool" }, "table": "things" + }, + { + "name": "small", + "type": { + "name": "decimal", + "args": [ + { + "int": 9 + }, + { + "int": 4 + } + ] + }, + "table": "things" + }, + { + "name": "plain", + "type": { + "name": "enum8", + "args": [ + { + "label": "x", + "int": 1 + }, + { + "label": "y", + "int": 2 + } + ] + }, + "table": "things" + }, + { + "name": "either", + "type": { + "name": "variant", + "args": [ + { + "type": { + "name": "int64" + } + }, + { + "type": { + "name": "string" + } + } + ] + }, + "table": "things" + }, + { + "name": "n.a", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "uint8" + } + } + ] + }, + "table": "things" + }, + { + "name": "n.b", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "string" + } + } + ] + }, + "table": "things" + }, + { + "name": "total", + "type": { + "name": "simpleaggregatefunction", + "args": [ + { + "ident": "sum" + }, + { + "type": { + "name": "uint64" + } + } + ] + }, + "table": "things" + }, + { + "name": "whole", + "type": { + "name": "int32" + }, + "table": "things" + } + ], + "params": [] + }, + { + "name": "Casts", + "cmd": ":one", + "columns": [ + { + "name": "a", + "type": { + "name": "string" + } + }, + { + "name": "b", + "type": { + "name": "decimal", + "args": [ + { + "int": 18 + }, + { + "int": 4 + } + ] + } + }, + { + "name": "c", + "type": { + "name": "string", + "nullable": true + } + }, + { + "name": "d", + "type": { + "name": "datetime64", + "args": [ + { + "int": 3 + } + ] + } + }, + { + "name": "e", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "string" + } + } + ] + } + }, + { + "name": "f", + "type": { + "name": "string", + "nullable": true + } } ], "params": [] + }, + { + "name": "Placeholders", + "cmd": ":many", + "columns": [ + { + "name": "id", + "type": { + "name": "uint64" + }, + "table": "things" + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "p1", + "type": { + "name": "uint64" + }, + "table": "things" + } + }, + { + "number": 2, + "column": { + "name": "p2", + "type": { + "name": "string" + }, + "table": "things" + } + }, + { + "number": 3, + "column": { + "name": "p3", + "type": { + "name": "float64" + }, + "table": "things" + } + }, + { + "number": 4, + "column": { + "name": "p4", + "type": { + "name": "string", + "nullable": true + }, + "table": "things" + } + }, + { + "number": 5, + "column": { + "name": "p5", + "type": { + "name": "decimal", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + }, + "table": "things" + } + } + ] } ] diff --git a/internal/engine/clickhouse/convert.go b/internal/engine/clickhouse/convert.go index b65df9e966..540a2aa748 100644 --- a/internal/engine/clickhouse/convert.go +++ b/internal/engine/clickhouse/convert.go @@ -691,15 +691,23 @@ func (c *cc) convertFunctionCall(n *chast.FunctionCall) *ast.FuncCall { func (c *cc) convertParameter(n *chast.Parameter) ast.Node { c.paramCount++ - // Use the parameter name if available - name := n.Name - if name == "" { - name = strconv.Itoa(c.paramCount) - } - return &ast.ParamRef{ + ref := &ast.ParamRef{ Number: c.paramCount, + Name: n.Name, Location: pos(n), } + // A parameter written {name:Type} declares its type, which is what a + // cast of a placeholder says. + if n.Type != nil { + spelling := renderDataType(n.Type) + base, _, _ := unwrapTypeString(spelling) + return &ast.TypeCast{ + Arg: ref, + TypeName: &ast.TypeName{Name: base, Spelling: spelling}, + Location: pos(n), + } + } + return ref } func (c *cc) convertAsterisk(n *chast.Asterisk) *ast.ColumnRef { @@ -751,9 +759,11 @@ func (c *cc) convertCastExpr(n *chast.CastExpr) *ast.TypeCast { } if n.Type != nil { - tc.TypeName = &ast.TypeName{ - Name: n.Type.Name, - } + // The whole type is handed over as its spelling, arguments and + // nesting included, the way a column's is. + spelling := renderDataType(n.Type) + base, _, _ := unwrapTypeString(spelling) + tc.TypeName = &ast.TypeName{Name: base, Spelling: spelling} } return tc @@ -965,8 +975,13 @@ func (c *cc) convertCreateQuery(n *chast.CreateQuery) ast.Node { stmt.Name.Schema = identifier(n.Database) } - // Convert columns + // Convert columns. A Nested column is what ClickHouse stores as + // one array column per element, named n.a, and reports as those. for _, col := range n.Columns { + if cols, ok := c.convertNestedColumn(col); ok { + stmt.Cols = append(stmt.Cols, cols...) + continue + } colDef := c.convertColumnDeclaration(col) stmt.Cols = append(stmt.Cols, colDef) } @@ -994,6 +1009,30 @@ func (c *cc) convertCreateQuery(n *chast.CreateQuery) ast.Node { return &ast.TODO{} } +// convertNestedColumn expands a column declared Nested(a T, b U) into the +// columns n.a Array(T) and n.b Array(U), which is how ClickHouse's +// system.columns lists it and what a query selects. +func (c *cc) convertNestedColumn(n *chast.ColumnDeclaration) ([]*ast.ColumnDef, bool) { + if n.Type == nil || !strings.EqualFold(n.Type.Name, "Nested") { + return nil, false + } + var cols []*ast.ColumnDef + for _, p := range n.Type.Parameters { + pair, ok := p.(*chast.NameTypePair) + if !ok { + continue + } + spelling := "Array(" + renderDataType(pair.Type) + ")" + cols = append(cols, &ast.ColumnDef{ + Colname: identifier(n.Name) + "." + identifier(pair.Name), + TypeName: &ast.TypeName{Name: "array", Spelling: spelling}, + IsArray: true, + IsNotNull: true, + }) + } + return cols, len(cols) > 0 +} + func (c *cc) convertColumnDeclaration(n *chast.ColumnDeclaration) *ast.ColumnDef { colDef := &ast.ColumnDef{ Colname: identifier(n.Name), diff --git a/internal/engine/clickhouse/seed.go b/internal/engine/clickhouse/seed.go index b87a704e41..3aad2a8362 100644 --- a/internal/engine/clickhouse/seed.go +++ b/internal/engine/clickhouse/seed.go @@ -2,6 +2,8 @@ package clickhouse import ( "embed" + "sort" + "strings" "github.com/sqlc-dev/sqlc/internal/core" "github.com/sqlc-dev/sqlc/internal/core/seed" @@ -23,3 +25,89 @@ var dialectFS embed.FS func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } + +func init() { + core.RegisterCanonicalizer("clickhouse", canonicalize) + core.RegisterResultType("clickhouse", resultType) +} + +// decimalPrecisions is the precision each sized decimal family stands for: +// ClickHouse stores Decimal32(s) as Decimal(9, s). +var decimalPrecisions = map[string]int64{ + "decimal32": 9, + "decimal64": 18, + "decimal128": 38, + "decimal256": 76, +} + +// canonicalize rewrites a type the way ClickHouse stores and reports it: a +// sized decimal is a Decimal with that precision, an Enum is an Enum8 or +// Enum16 with its members numbered, a Variant's members are sorted, and +// the function an aggregate-function type names is a word rather than a +// type. +func canonicalize(t *core.TypeExpr) *core.TypeExpr { + switch t.Name { + case "decimal32", "decimal64", "decimal128", "decimal256": + if len(t.Args) == 1 && t.Args[0].Int != nil { + p := decimalPrecisions[t.Name] + return &core.TypeExpr{Name: "decimal", Nullable: t.Nullable, Args: []core.TypeArg{{Int: &p}, t.Args[0]}} + } + case "enum", "enum8", "enum16": + out := t.Clone() + if out.Name == "enum" { + out.Name = "enum8" + if len(out.Args) > 127 { + out.Name = "enum16" + } + } + next := int64(1) + for i := range out.Args { + a := &out.Args[i] + switch { + case a.Label != "" && a.Int != nil: + next = *a.Int + 1 + case a.String != nil: + // A bare member is numbered after the one before it. + label := *a.String + n := next + *a = core.TypeArg{Label: label, Int: &n} + next++ + } + } + return out + case "variant": + out := t.Clone() + sort.SliceStable(out.Args, func(i, j int) bool { + return out.Args[i].Type.String() < out.Args[j].Type.String() + }) + return out + case "aggregatefunction", "simpleaggregatefunction": + if len(t.Args) > 0 && t.Args[0].Type != nil && len(t.Args[0].Type.Args) == 0 { + out := t.Clone() + name := out.Args[0].Type.Name + out.Args[0] = core.TypeArg{Ident: &name} + return out + } + } + return t +} + +// resultType is what a conversion returns when that depends on an +// argument's value: toDecimal64(x, s) is Decimal(18, s) and +// toDateTime64(x, p) is DateTime64(p). +func resultType(name string, args []core.ResultArg) *core.TypeExpr { + switch strings.ToLower(name) { + case "todecimal32", "todecimal64", "todecimal128", "todecimal256": + if len(args) >= 2 && args[1].Int != nil { + p := decimalPrecisions[strings.TrimPrefix(strings.ToLower(name), "to")] + s := *args[1].Int + return &core.TypeExpr{Name: "decimal", Args: []core.TypeArg{{Int: &p}, {Int: &s}}} + } + case "todatetime64": + if len(args) >= 2 && args[1].Int != nil { + p := *args[1].Int + return &core.TypeExpr{Name: "datetime64", Args: []core.TypeArg{{Int: &p}}} + } + } + return nil +} diff --git a/internal/goldeneye/analysis/analysis.go b/internal/goldeneye/analysis/analysis.go index fe907766a3..1433f50904 100644 --- a/internal/goldeneye/analysis/analysis.go +++ b/internal/goldeneye/analysis/analysis.go @@ -40,13 +40,16 @@ type TypeExpr struct { Args []TypeArg `json:"args,omitempty"` } -// TypeArg is one argument of a TypeExpr. +// TypeArg is one argument of a TypeExpr: a type, an integer, a boolean, a +// quoted string, or an identifier — a bare word that is not a type, such as +// the function an AggregateFunction names. type TypeArg struct { Label string `json:"label,omitempty"` Type *TypeExpr `json:"type,omitempty"` Int *int64 `json:"int,omitempty"` Bool *bool `json:"bool,omitempty"` String *string `json:"string,omitempty"` + Ident *string `json:"ident,omitempty"` } // Encode prints the answer the way sqlc analyze does. diff --git a/internal/goldeneye/clickhouse/types.go b/internal/goldeneye/clickhouse/types.go index 57f6caf731..4e42a504b7 100644 --- a/internal/goldeneye/clickhouse/types.go +++ b/internal/goldeneye/clickhouse/types.go @@ -34,8 +34,8 @@ import ( // {"name": "datetime64", "args": [{"int": 3}, {"string": "UTC"}]} // // An identifier argument such as the function in AggregateFunction(uniq, -// String) is a type with no arguments. Resolving names against the catalog -// is the reader's job; the output only records what was said. +// String) is a word rather than a type. Resolving names against the +// catalog is the reader's job; the output only records what was said. // parseType turns a ClickHouse type string into its expression. func parseType(t string) *analysis.TypeExpr { @@ -53,6 +53,13 @@ func parseType(t string) *analysis.TypeExpr { for _, a := range args { expr.Args = append(expr.Args, parseArg(a)) } + // The function an aggregate-function type names is a word, not a type. + if name == "aggregatefunction" || name == "simpleaggregatefunction" { + if len(expr.Args) > 0 && expr.Args[0].Type != nil && len(expr.Args[0].Type.Args) == 0 { + fn := expr.Args[0].Type.Name + expr.Args[0] = analysis.TypeArg{Ident: &fn} + } + } return expr } diff --git a/internal/goldeneye/endtoend/query.go b/internal/goldeneye/endtoend/query.go index ea15ad89e2..70a8ae7658 100644 --- a/internal/goldeneye/endtoend/query.go +++ b/internal/goldeneye/endtoend/query.go @@ -72,10 +72,14 @@ func ParseQueries(src string) ([]Query, error) { var namedArgRe = regexp.MustCompile(`^sqlc\.(n?arg|slice)\(\s*'?([A-Za-z_][A-Za-z0-9_]*)'?\s*\)`) +// typedParamRe matches ClickHouse's {name:Type} parameter, whose type is +// the query's own business: the engine binds it as it binds any other. +var typedParamRe = regexp.MustCompile(`^\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*:\s*[^}]+\}`) + // Rewrite replaces every parameter reference in a query — ?, sqlc.arg(name), -// sqlc.narg(name) and sqlc.slice(name) — with what bind returns for it, in -// order of appearance, skipping string literals, quoted identifiers and -// comments. bind is handed the name, empty for a ?, and the word before the +// sqlc.narg(name), sqlc.slice(name) and ClickHouse's {name:Type} — with +// what bind returns for it, in order of appearance, skipping string +// literals, quoted identifiers and comments. bind is handed the name, empty for a ?, and the word before the // reference, so that a LIMIT or OFFSET can be bound differently from a // value; the second count of a LIMIT ?, ? is handed LIMIT as well. Each // engine decides what a reference becomes and how the references are @@ -120,6 +124,11 @@ func Rewrite(sql string, bind func(name, lastWord string) string) string { out.WriteString(bind(m[2], lastWord)) lastWord = afterReference(lastWord) i += len(m[0]) + case c == '{' && typedParamRe.MatchString(sql[i:]): + m := typedParamRe.FindStringSubmatch(sql[i:]) + out.WriteString(bind(m[1], lastWord)) + lastWord = afterReference(lastWord) + i += len(m[0]) case isWordByte(c): end := i for end < len(sql) && isWordByte(sql[end]) { diff --git a/internal/sql/ast/param_ref.go b/internal/sql/ast/param_ref.go index 2b7ec5c527..0936c60d30 100644 --- a/internal/sql/ast/param_ref.go +++ b/internal/sql/ast/param_ref.go @@ -8,6 +8,9 @@ type ParamRef struct { Number int `json:"number"` Location int `json:"location"` Dollar bool `json:"dollar"` + // Name is the name the query gave the placeholder, when its syntax + // has one: ClickHouse's {name:Type}. + Name string `json:"name,omitempty"` } func (n *ParamRef) Pos() int { From 4144f5d135ce918df7f2259b7b742d57948ad7bb Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 17:54:17 +0000 Subject: [PATCH 10/16] duckdb, googlesql, mssql: nested types, parameters, MAX, defaults and alias types in the core DuckDB hands the core a type's whole spelling, so a struct's fields, a map's key and value, a union's members, a fixed-size array's length and a list of lists all arrive, with a varchar's length dropped and a bare decimal filled in as decimal(18,3), as DuckDB stores them. GoogleSQL spells ARRAY, STRUCT<...>, RANGE and a parameter list in the same call form, and reads MAX as the word it is. SQL Server's converter carries a type's parameters and MAX, qualifies a user-defined type by its schema, reports CREATE TYPE ... FROM as the domain it is, and canonicalizes as sys.types does: float(24) is real, sysname is nvarchar(128), and a length or precision left out takes its default. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- .../testdata/analyze_basic/duckdb/stdout.json | 10 +- .../testdata/analyze_basic/mssql/stdout.json | 31 +- .../testdata/analyze_dml/duckdb/stdout.json | 10 +- .../testdata/analyze_dml/mssql/stdout.json | 45 +- .../analyze_params/duckdb/stdout.json | 10 +- .../testdata/analyze_params/mssql/stdout.json | 38 +- .../testdata/analyze_types/duckdb/exec.json | 5 + .../testdata/analyze_types/duckdb/query.sql | 17 + .../testdata/analyze_types/duckdb/schema.sql | 30 + .../testdata/analyze_types/duckdb/stdout.json | 686 ++++++++++++++++++ .../analyze_types/googlesql/exec.json | 5 + .../analyze_types/googlesql/query.sql | 16 + .../analyze_types/googlesql/schema.sql | 21 + .../analyze_types/googlesql/stdout.json | 482 ++++++++++++ .../testdata/analyze_types/mssql/exec.json | 5 + .../testdata/analyze_types/mssql/query.sql | 16 + .../testdata/analyze_types/mssql/schema.sql | 34 + .../testdata/analyze_types/mssql/stdout.json | 570 +++++++++++++++ internal/engine/duckdb/convert.go | 52 +- internal/engine/duckdb/seed.go | 21 + internal/engine/googlesql/convert.go | 22 +- internal/engine/googlesql/dialect/types.jsonl | 2 + internal/engine/googlesql/seed.go | 24 + internal/engine/googlesql/utils.go | 61 +- internal/engine/mssql/convert.go | 70 +- internal/engine/mssql/seed.go | 50 ++ 26 files changed, 2285 insertions(+), 48 deletions(-) create mode 100644 internal/endtoend/testdata/analyze_types/duckdb/exec.json create mode 100644 internal/endtoend/testdata/analyze_types/duckdb/query.sql create mode 100644 internal/endtoend/testdata/analyze_types/duckdb/schema.sql create mode 100644 internal/endtoend/testdata/analyze_types/duckdb/stdout.json create mode 100644 internal/endtoend/testdata/analyze_types/googlesql/exec.json create mode 100644 internal/endtoend/testdata/analyze_types/googlesql/query.sql create mode 100644 internal/endtoend/testdata/analyze_types/googlesql/schema.sql create mode 100644 internal/endtoend/testdata/analyze_types/googlesql/stdout.json create mode 100644 internal/endtoend/testdata/analyze_types/mssql/exec.json create mode 100644 internal/endtoend/testdata/analyze_types/mssql/query.sql create mode 100644 internal/endtoend/testdata/analyze_types/mssql/schema.sql create mode 100644 internal/endtoend/testdata/analyze_types/mssql/stdout.json diff --git a/internal/endtoend/testdata/analyze_basic/duckdb/stdout.json b/internal/endtoend/testdata/analyze_basic/duckdb/stdout.json index ab2cee1451..6459e87325 100644 --- a/internal/endtoend/testdata/analyze_basic/duckdb/stdout.json +++ b/internal/endtoend/testdata/analyze_basic/duckdb/stdout.json @@ -28,7 +28,15 @@ { "name": "royalties", "type": { - "name": "decimal" + "name": "decimal", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] }, "table": "authors" }, diff --git a/internal/endtoend/testdata/analyze_basic/mssql/stdout.json b/internal/endtoend/testdata/analyze_basic/mssql/stdout.json index 181660561a..818c4c26d7 100644 --- a/internal/endtoend/testdata/analyze_basic/mssql/stdout.json +++ b/internal/endtoend/testdata/analyze_basic/mssql/stdout.json @@ -13,7 +13,12 @@ { "name": "name", "type": { - "name": "nvarchar" + "name": "nvarchar", + "args": [ + { + "int": 100 + } + ] }, "table": "authors" }, @@ -21,21 +26,39 @@ "name": "bio", "type": { "name": "nvarchar", - "nullable": true + "nullable": true, + "args": [ + { + "ident": "max" + } + ] }, "table": "authors" }, { "name": "royalties", "type": { - "name": "decimal" + "name": "decimal", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] }, "table": "authors" }, { "name": "created", "type": { - "name": "datetime2" + "name": "datetime2", + "args": [ + { + "int": 7 + } + ] }, "table": "authors" } diff --git a/internal/endtoend/testdata/analyze_dml/duckdb/stdout.json b/internal/endtoend/testdata/analyze_dml/duckdb/stdout.json index c75a2a8d8b..621fdfd260 100644 --- a/internal/endtoend/testdata/analyze_dml/duckdb/stdout.json +++ b/internal/endtoend/testdata/analyze_dml/duckdb/stdout.json @@ -126,7 +126,15 @@ "name": "price", "type": { "name": "decimal", - "nullable": true + "nullable": true, + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] }, "table": "books" } diff --git a/internal/endtoend/testdata/analyze_dml/mssql/stdout.json b/internal/endtoend/testdata/analyze_dml/mssql/stdout.json index 24e2efb3e4..74f68505dd 100644 --- a/internal/endtoend/testdata/analyze_dml/mssql/stdout.json +++ b/internal/endtoend/testdata/analyze_dml/mssql/stdout.json @@ -17,7 +17,12 @@ "column": { "name": "name", "type": { - "name": "nvarchar" + "name": "nvarchar", + "args": [ + { + "int": 100 + } + ] }, "table": "authors" } @@ -28,7 +33,12 @@ "name": "bio", "type": { "name": "nvarchar", - "nullable": true + "nullable": true, + "args": [ + { + "ident": "max" + } + ] }, "table": "authors" } @@ -45,7 +55,12 @@ "column": { "name": "name", "type": { - "name": "nvarchar" + "name": "nvarchar", + "args": [ + { + "int": 100 + } + ] }, "table": "authors" } @@ -73,7 +88,15 @@ "name": "price", "type": { "name": "decimal", - "nullable": true + "nullable": true, + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] }, "table": "books" } @@ -83,7 +106,12 @@ "column": { "name": "name", "type": { - "name": "nvarchar" + "name": "nvarchar", + "args": [ + { + "int": 100 + } + ] }, "table": "authors" } @@ -100,7 +128,12 @@ "column": { "name": "name", "type": { - "name": "nvarchar" + "name": "nvarchar", + "args": [ + { + "int": 100 + } + ] }, "table": "authors" } diff --git a/internal/endtoend/testdata/analyze_params/duckdb/stdout.json b/internal/endtoend/testdata/analyze_params/duckdb/stdout.json index 617d664b7b..40037c23d5 100644 --- a/internal/endtoend/testdata/analyze_params/duckdb/stdout.json +++ b/internal/endtoend/testdata/analyze_params/duckdb/stdout.json @@ -74,7 +74,15 @@ "column": { "name": "royalties", "type": { - "name": "decimal" + "name": "decimal", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] }, "table": "authors" } diff --git a/internal/endtoend/testdata/analyze_params/mssql/stdout.json b/internal/endtoend/testdata/analyze_params/mssql/stdout.json index 1483213369..ab55aa9823 100644 --- a/internal/endtoend/testdata/analyze_params/mssql/stdout.json +++ b/internal/endtoend/testdata/analyze_params/mssql/stdout.json @@ -13,7 +13,12 @@ { "name": "name", "type": { - "name": "nvarchar" + "name": "nvarchar", + "args": [ + { + "int": 100 + } + ] }, "table": "authors" }, @@ -21,7 +26,12 @@ "name": "bio", "type": { "name": "nvarchar", - "nullable": true + "nullable": true, + "args": [ + { + "ident": "max" + } + ] }, "table": "authors" } @@ -53,7 +63,12 @@ { "name": "name", "type": { - "name": "nvarchar" + "name": "nvarchar", + "args": [ + { + "int": 100 + } + ] }, "table": "authors" } @@ -64,7 +79,12 @@ "column": { "name": "name", "type": { - "name": "nvarchar" + "name": "nvarchar", + "args": [ + { + "int": 100 + } + ] }, "table": "authors" } @@ -74,7 +94,15 @@ "column": { "name": "royalties", "type": { - "name": "decimal" + "name": "decimal", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] }, "table": "authors" } diff --git a/internal/endtoend/testdata/analyze_types/duckdb/exec.json b/internal/endtoend/testdata/analyze_types/duckdb/exec.json new file mode 100644 index 0000000000..56cb4b3fff --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/duckdb/exec.json @@ -0,0 +1,5 @@ +{ + "command": "analyze", + "args": ["--dialect", "duckdb", "--schema", "schema.sql", "query.sql"], + "contexts": ["base"] +} diff --git a/internal/endtoend/testdata/analyze_types/duckdb/query.sql b/internal/endtoend/testdata/analyze_types/duckdb/query.sql new file mode 100644 index 0000000000..e7a62ed31f --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/duckdb/query.sql @@ -0,0 +1,17 @@ +-- name: AllTypes :many +SELECT * FROM things; + +-- name: Casts :one +SELECT + $1::DECIMAL(5,2) AS a, + $2::INTEGER[] AS b, + $3::STRUCT(a INTEGER) AS c, + $4::mood AS d, + CAST($5 AS VARCHAR(5)) AS e, + $6::MAP(VARCHAR, INTEGER) AS f, + $7::INTEGER[3] AS g +FROM things; + +-- name: Params :one +SELECT id FROM things +WHERE ints = $1 AND point = $2 AND price = $3 AND m = $4 AND either = $5 AND grid = $6; diff --git a/internal/endtoend/testdata/analyze_types/duckdb/schema.sql b/internal/endtoend/testdata/analyze_types/duckdb/schema.sql new file mode 100644 index 0000000000..b04a775185 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/duckdb/schema.sql @@ -0,0 +1,30 @@ +CREATE TYPE mood AS ENUM ('sad', 'ok'); + +CREATE TABLE things ( + id INTEGER PRIMARY KEY, + ints INTEGER[], + fixed INTEGER[3], + point STRUCT(a INTEGER, b VARCHAR), + attrs MAP(VARCHAR, INTEGER), + either UNION(num INTEGER, str VARCHAR), + price DECIMAL(18,3), + title VARCHAR(10), + kind ENUM('a','b'), + m mood, + big HUGEINT, + ubig UHUGEINT, + data BLOB, + bits BIT, + uid UUID, + tstz TIMESTAMP WITH TIME ZONE, + tsns TIMESTAMP_NS, + iv INTERVAL, + doc JSON, + grid INTEGER[][], + vi VARINT, + f4 FLOAT4, + points STRUCT(a INTEGER)[], + lists MAP(VARCHAR, INTEGER[]), + body TEXT, + n NUMERIC +); diff --git a/internal/endtoend/testdata/analyze_types/duckdb/stdout.json b/internal/endtoend/testdata/analyze_types/duckdb/stdout.json new file mode 100644 index 0000000000..cf525c4744 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/duckdb/stdout.json @@ -0,0 +1,686 @@ +[ + { + "name": "AllTypes", + "cmd": ":many", + "columns": [ + { + "name": "id", + "type": { + "name": "integer" + }, + "table": "things" + }, + { + "name": "ints", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "integer" + } + } + ] + }, + "table": "things" + }, + { + "name": "fixed", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "integer" + } + }, + { + "int": 3 + } + ] + }, + "table": "things" + }, + { + "name": "point", + "type": { + "name": "struct", + "nullable": true, + "args": [ + { + "label": "a", + "type": { + "name": "integer" + } + }, + { + "label": "b", + "type": { + "name": "varchar" + } + } + ] + }, + "table": "things" + }, + { + "name": "attrs", + "type": { + "name": "map", + "nullable": true, + "args": [ + { + "type": { + "name": "varchar" + } + }, + { + "type": { + "name": "integer" + } + } + ] + }, + "table": "things" + }, + { + "name": "either", + "type": { + "name": "union", + "nullable": true, + "args": [ + { + "label": "num", + "type": { + "name": "integer" + } + }, + { + "label": "str", + "type": { + "name": "varchar" + } + } + ] + }, + "table": "things" + }, + { + "name": "price", + "type": { + "name": "decimal", + "nullable": true, + "args": [ + { + "int": 18 + }, + { + "int": 3 + } + ] + }, + "table": "things" + }, + { + "name": "title", + "type": { + "name": "varchar", + "nullable": true + }, + "table": "things" + }, + { + "name": "kind", + "type": { + "name": "enum", + "nullable": true, + "args": [ + { + "string": "a" + }, + { + "string": "b" + } + ] + }, + "table": "things" + }, + { + "name": "m", + "type": { + "name": "mood", + "nullable": true + }, + "table": "things" + }, + { + "name": "big", + "type": { + "name": "hugeint", + "nullable": true + }, + "table": "things" + }, + { + "name": "ubig", + "type": { + "name": "uhugeint", + "nullable": true + }, + "table": "things" + }, + { + "name": "data", + "type": { + "name": "blob", + "nullable": true + }, + "table": "things" + }, + { + "name": "bits", + "type": { + "name": "bit", + "nullable": true + }, + "table": "things" + }, + { + "name": "uid", + "type": { + "name": "uuid", + "nullable": true + }, + "table": "things" + }, + { + "name": "tstz", + "type": { + "name": "timestamp with time zone", + "nullable": true + }, + "table": "things" + }, + { + "name": "tsns", + "type": { + "name": "timestamp_ns", + "nullable": true + }, + "table": "things" + }, + { + "name": "iv", + "type": { + "name": "interval", + "nullable": true + }, + "table": "things" + }, + { + "name": "doc", + "type": { + "name": "varchar", + "nullable": true + }, + "table": "things" + }, + { + "name": "grid", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + } + ] + } + } + ] + }, + "table": "things" + }, + { + "name": "vi", + "type": { + "name": "bignum", + "nullable": true + }, + "table": "things" + }, + { + "name": "f4", + "type": { + "name": "float", + "nullable": true + }, + "table": "things" + }, + { + "name": "points", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "struct", + "args": [ + { + "label": "a", + "type": { + "name": "integer" + } + } + ] + } + } + ] + }, + "table": "things" + }, + { + "name": "lists", + "type": { + "name": "map", + "nullable": true, + "args": [ + { + "type": { + "name": "varchar" + } + }, + { + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + } + ] + } + } + ] + }, + "table": "things" + }, + { + "name": "body", + "type": { + "name": "varchar", + "nullable": true + }, + "table": "things" + }, + { + "name": "n", + "type": { + "name": "decimal", + "nullable": true, + "args": [ + { + "int": 18 + }, + { + "int": 3 + } + ] + }, + "table": "things" + } + ], + "params": [] + }, + { + "name": "Casts", + "cmd": ":one", + "columns": [ + { + "name": "a", + "type": { + "name": "decimal", + "args": [ + { + "int": 5 + }, + { + "int": 2 + } + ] + } + }, + { + "name": "b", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + } + ] + } + }, + { + "name": "c", + "type": { + "name": "struct", + "args": [ + { + "label": "a", + "type": { + "name": "integer" + } + } + ] + } + }, + { + "name": "d", + "type": { + "name": "mood" + } + }, + { + "name": "e", + "type": { + "name": "varchar" + } + }, + { + "name": "f", + "type": { + "name": "map", + "args": [ + { + "type": { + "name": "varchar" + } + }, + { + "type": { + "name": "integer" + } + } + ] + } + }, + { + "name": "g", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + }, + { + "int": 3 + } + ] + } + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "", + "type": { + "name": "decimal", + "args": [ + { + "int": 5 + }, + { + "int": 2 + } + ] + } + } + }, + { + "number": 2, + "column": { + "name": "", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + } + ] + } + } + }, + { + "number": 3, + "column": { + "name": "", + "type": { + "name": "struct", + "args": [ + { + "label": "a", + "type": { + "name": "integer" + } + } + ] + } + } + }, + { + "number": 4, + "column": { + "name": "", + "type": { + "name": "mood" + } + } + }, + { + "number": 5, + "column": { + "name": "", + "type": { + "name": "varchar" + } + } + }, + { + "number": 6, + "column": { + "name": "", + "type": { + "name": "map", + "args": [ + { + "type": { + "name": "varchar" + } + }, + { + "type": { + "name": "integer" + } + } + ] + } + } + }, + { + "number": 7, + "column": { + "name": "", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + }, + { + "int": 3 + } + ] + } + } + } + ] + }, + { + "name": "Params", + "cmd": ":one", + "columns": [ + { + "name": "id", + "type": { + "name": "integer" + }, + "table": "things" + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "ints", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "integer" + } + } + ] + }, + "table": "things" + } + }, + { + "number": 2, + "column": { + "name": "point", + "type": { + "name": "struct", + "nullable": true, + "args": [ + { + "label": "a", + "type": { + "name": "integer" + } + }, + { + "label": "b", + "type": { + "name": "varchar" + } + } + ] + }, + "table": "things" + } + }, + { + "number": 3, + "column": { + "name": "price", + "type": { + "name": "decimal", + "nullable": true, + "args": [ + { + "int": 18 + }, + { + "int": 3 + } + ] + }, + "table": "things" + } + }, + { + "number": 4, + "column": { + "name": "m", + "type": { + "name": "mood", + "nullable": true + }, + "table": "things" + } + }, + { + "number": 5, + "column": { + "name": "either", + "type": { + "name": "union", + "nullable": true, + "args": [ + { + "label": "num", + "type": { + "name": "integer" + } + }, + { + "label": "str", + "type": { + "name": "varchar" + } + } + ] + }, + "table": "things" + } + }, + { + "number": 6, + "column": { + "name": "grid", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "integer" + } + } + ] + } + } + ] + }, + "table": "things" + } + } + ] + } +] diff --git a/internal/endtoend/testdata/analyze_types/googlesql/exec.json b/internal/endtoend/testdata/analyze_types/googlesql/exec.json new file mode 100644 index 0000000000..a53ddddc6d --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/googlesql/exec.json @@ -0,0 +1,5 @@ +{ + "command": "analyze", + "args": ["--dialect", "googlesql", "--schema", "schema.sql", "query.sql"], + "contexts": ["base"] +} diff --git a/internal/endtoend/testdata/analyze_types/googlesql/query.sql b/internal/endtoend/testdata/analyze_types/googlesql/query.sql new file mode 100644 index 0000000000..c798e56371 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/googlesql/query.sql @@ -0,0 +1,16 @@ +-- name: AllTypes :many +SELECT * FROM things; + +-- name: Casts :one +SELECT + CAST(@a AS NUMERIC(5,2)) AS a, + CAST(@b AS ARRAY) AS b, + CAST(@c AS STRUCT) AS c, + CAST(@d AS STRING(5)) AS d, + SAFE_CAST(@e AS BIGNUMERIC) AS e, + [1, 2] AS f +FROM things; + +-- name: Params :one +SELECT id FROM things +WHERE s = @s AND ai = @ai AND n = @n AND st = @st AND smax = @smax; diff --git a/internal/endtoend/testdata/analyze_types/googlesql/schema.sql b/internal/endtoend/testdata/analyze_types/googlesql/schema.sql new file mode 100644 index 0000000000..feb846800a --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/googlesql/schema.sql @@ -0,0 +1,21 @@ +CREATE TABLE things ( + id INT64 NOT NULL, + s STRING(10), + smax STRING(MAX), + n NUMERIC(10,2), + bn BIGNUMERIC, + byt BYTES(MAX), + ai ARRAY, + as2 ARRAY, + st STRUCT, + ast ARRAY>, + ts TIMESTAMP, + d DATE, + j JSON, + g GEOGRAPHY, + iv INTERVAL, + b BOOL, + f FLOAT64, + f32 FLOAT32, + tl TOKENLIST +) PRIMARY KEY (id); diff --git a/internal/endtoend/testdata/analyze_types/googlesql/stdout.json b/internal/endtoend/testdata/analyze_types/googlesql/stdout.json new file mode 100644 index 0000000000..bae5a9082a --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/googlesql/stdout.json @@ -0,0 +1,482 @@ +[ + { + "name": "AllTypes", + "cmd": ":many", + "columns": [ + { + "name": "id", + "type": { + "name": "int64" + }, + "table": "things" + }, + { + "name": "s", + "type": { + "name": "string", + "nullable": true, + "args": [ + { + "int": 10 + } + ] + }, + "table": "things" + }, + { + "name": "smax", + "type": { + "name": "string", + "nullable": true, + "args": [ + { + "ident": "max" + } + ] + }, + "table": "things" + }, + { + "name": "n", + "type": { + "name": "numeric", + "nullable": true, + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + }, + "table": "things" + }, + { + "name": "bn", + "type": { + "name": "bignumeric", + "nullable": true + }, + "table": "things" + }, + { + "name": "byt", + "type": { + "name": "bytes", + "nullable": true, + "args": [ + { + "ident": "max" + } + ] + }, + "table": "things" + }, + { + "name": "ai", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "int64" + } + } + ] + }, + "table": "things" + }, + { + "name": "as2", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "string", + "args": [ + { + "ident": "max" + } + ] + } + } + ] + }, + "table": "things" + }, + { + "name": "st", + "type": { + "name": "struct", + "nullable": true, + "args": [ + { + "label": "a", + "type": { + "name": "int64" + } + }, + { + "label": "b", + "type": { + "name": "string" + } + } + ] + }, + "table": "things" + }, + { + "name": "ast", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "struct", + "args": [ + { + "label": "x", + "type": { + "name": "int64" + } + } + ] + } + } + ] + }, + "table": "things" + }, + { + "name": "ts", + "type": { + "name": "timestamp", + "nullable": true + }, + "table": "things" + }, + { + "name": "d", + "type": { + "name": "date", + "nullable": true + }, + "table": "things" + }, + { + "name": "j", + "type": { + "name": "json", + "nullable": true + }, + "table": "things" + }, + { + "name": "g", + "type": { + "name": "geography", + "nullable": true + }, + "table": "things" + }, + { + "name": "iv", + "type": { + "name": "interval", + "nullable": true + }, + "table": "things" + }, + { + "name": "b", + "type": { + "name": "bool", + "nullable": true + }, + "table": "things" + }, + { + "name": "f", + "type": { + "name": "float64", + "nullable": true + }, + "table": "things" + }, + { + "name": "f32", + "type": { + "name": "float32", + "nullable": true + }, + "table": "things" + }, + { + "name": "tl", + "type": { + "name": "tokenlist", + "nullable": true + }, + "table": "things" + } + ], + "params": [] + }, + { + "name": "Casts", + "cmd": ":one", + "columns": [ + { + "name": "a", + "type": { + "name": "numeric", + "args": [ + { + "int": 5 + }, + { + "int": 2 + } + ] + } + }, + { + "name": "b", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "int64" + } + } + ] + } + }, + { + "name": "c", + "type": { + "name": "struct", + "args": [ + { + "label": "x", + "type": { + "name": "int64" + } + } + ] + } + }, + { + "name": "d", + "type": { + "name": "string", + "args": [ + { + "int": 5 + } + ] + } + }, + { + "name": "e", + "type": { + "name": "bignumeric" + } + }, + { + "name": "f" + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "", + "type": { + "name": "numeric", + "args": [ + { + "int": 5 + }, + { + "int": 2 + } + ] + } + } + }, + { + "number": 2, + "column": { + "name": "", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "int64" + } + } + ] + } + } + }, + { + "number": 3, + "column": { + "name": "", + "type": { + "name": "struct", + "args": [ + { + "label": "x", + "type": { + "name": "int64" + } + } + ] + } + } + }, + { + "number": 4, + "column": { + "name": "", + "type": { + "name": "string", + "args": [ + { + "int": 5 + } + ] + } + } + }, + { + "number": 5, + "column": { + "name": "", + "type": { + "name": "bignumeric" + } + } + } + ] + }, + { + "name": "Params", + "cmd": ":one", + "columns": [ + { + "name": "id", + "type": { + "name": "int64" + }, + "table": "things" + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "s", + "type": { + "name": "string", + "nullable": true, + "args": [ + { + "int": 10 + } + ] + }, + "table": "things" + } + }, + { + "number": 2, + "column": { + "name": "ai", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "int64" + } + } + ] + }, + "table": "things" + } + }, + { + "number": 3, + "column": { + "name": "n", + "type": { + "name": "numeric", + "nullable": true, + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + }, + "table": "things" + } + }, + { + "number": 4, + "column": { + "name": "st", + "type": { + "name": "struct", + "nullable": true, + "args": [ + { + "label": "a", + "type": { + "name": "int64" + } + }, + { + "label": "b", + "type": { + "name": "string" + } + } + ] + }, + "table": "things" + } + }, + { + "number": 5, + "column": { + "name": "smax", + "type": { + "name": "string", + "nullable": true, + "args": [ + { + "ident": "max" + } + ] + }, + "table": "things" + } + } + ] + } +] diff --git a/internal/endtoend/testdata/analyze_types/mssql/exec.json b/internal/endtoend/testdata/analyze_types/mssql/exec.json new file mode 100644 index 0000000000..253b05abbf --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/mssql/exec.json @@ -0,0 +1,5 @@ +{ + "command": "analyze", + "args": ["--dialect", "mssql", "--schema", "schema.sql", "query.sql"], + "contexts": ["base"] +} diff --git a/internal/endtoend/testdata/analyze_types/mssql/query.sql b/internal/endtoend/testdata/analyze_types/mssql/query.sql new file mode 100644 index 0000000000..976b5c1070 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/mssql/query.sql @@ -0,0 +1,16 @@ +-- name: AllTypes :many +SELECT * FROM things; + +-- name: Casts :one +SELECT + CAST(@a AS DECIMAL(5,2)) AS a, + CAST(@b AS NVARCHAR(MAX)) AS b, + CAST(@c AS VARCHAR(10)) AS c, + CONVERT(DATETIME2(3), @d) AS d, + CAST(@e AS dbo.PhoneNumber) AS e, + TRY_CAST(@f AS FLOAT(24)) AS f +FROM things; + +-- name: Params :one +SELECT id FROM things +WHERE price = @price AND body = @body AND phone = @phone AND vec = @vec AND offset_at = @offset_at; diff --git a/internal/endtoend/testdata/analyze_types/mssql/schema.sql b/internal/endtoend/testdata/analyze_types/mssql/schema.sql new file mode 100644 index 0000000000..e7106319ef --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/mssql/schema.sql @@ -0,0 +1,34 @@ +CREATE TYPE dbo.PhoneNumber FROM varchar(20) NOT NULL; + +CREATE TABLE things ( + id BIGINT IDENTITY(1,1) PRIMARY KEY, + price DECIMAL(10,2) NOT NULL, + amount NUMERIC(18,4), + plain DECIMAL, + body NVARCHAR(MAX), + title VARCHAR(50), + code CHAR(10), + ncode NCHAR(5), + blob VARBINARY(MAX), + key16 BINARY(16), + created DATETIME2(3), + updated DATETIME2, + offset_at DATETIMEOFFSET(7), + tm TIME(4), + f FLOAT(24), + f53 FLOAT, + r REAL, + m MONEY, + b BIT, + u UNIQUEIDENTIFIER, + x XML, + j JSON, + sv SQL_VARIANT, + rv ROWVERSION, + g GEOGRAPHY, + h HIERARCHYID, + vec VECTOR(3), + sn sysname, + phone dbo.PhoneNumber, + bare VARCHAR +); diff --git a/internal/endtoend/testdata/analyze_types/mssql/stdout.json b/internal/endtoend/testdata/analyze_types/mssql/stdout.json new file mode 100644 index 0000000000..ec565348b5 --- /dev/null +++ b/internal/endtoend/testdata/analyze_types/mssql/stdout.json @@ -0,0 +1,570 @@ +[ + { + "name": "AllTypes", + "cmd": ":many", + "columns": [ + { + "name": "id", + "type": { + "name": "bigint" + }, + "table": "things" + }, + { + "name": "price", + "type": { + "name": "decimal", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + }, + "table": "things" + }, + { + "name": "amount", + "type": { + "name": "decimal", + "nullable": true, + "args": [ + { + "int": 18 + }, + { + "int": 4 + } + ] + }, + "table": "things" + }, + { + "name": "plain", + "type": { + "name": "decimal", + "nullable": true, + "args": [ + { + "int": 18 + }, + { + "int": 0 + } + ] + }, + "table": "things" + }, + { + "name": "body", + "type": { + "name": "nvarchar", + "nullable": true, + "args": [ + { + "ident": "max" + } + ] + }, + "table": "things" + }, + { + "name": "title", + "type": { + "name": "varchar", + "nullable": true, + "args": [ + { + "int": 50 + } + ] + }, + "table": "things" + }, + { + "name": "code", + "type": { + "name": "char", + "nullable": true, + "args": [ + { + "int": 10 + } + ] + }, + "table": "things" + }, + { + "name": "ncode", + "type": { + "name": "nchar", + "nullable": true, + "args": [ + { + "int": 5 + } + ] + }, + "table": "things" + }, + { + "name": "blob", + "type": { + "name": "varbinary", + "nullable": true, + "args": [ + { + "ident": "max" + } + ] + }, + "table": "things" + }, + { + "name": "key16", + "type": { + "name": "binary", + "nullable": true, + "args": [ + { + "int": 16 + } + ] + }, + "table": "things" + }, + { + "name": "created", + "type": { + "name": "datetime2", + "nullable": true, + "args": [ + { + "int": 3 + } + ] + }, + "table": "things" + }, + { + "name": "updated", + "type": { + "name": "datetime2", + "nullable": true, + "args": [ + { + "int": 7 + } + ] + }, + "table": "things" + }, + { + "name": "offset_at", + "type": { + "name": "datetimeoffset", + "nullable": true, + "args": [ + { + "int": 7 + } + ] + }, + "table": "things" + }, + { + "name": "tm", + "type": { + "name": "time", + "nullable": true, + "args": [ + { + "int": 4 + } + ] + }, + "table": "things" + }, + { + "name": "f", + "type": { + "name": "real", + "nullable": true + }, + "table": "things" + }, + { + "name": "f53", + "type": { + "name": "float", + "nullable": true + }, + "table": "things" + }, + { + "name": "r", + "type": { + "name": "real", + "nullable": true + }, + "table": "things" + }, + { + "name": "m", + "type": { + "name": "money", + "nullable": true + }, + "table": "things" + }, + { + "name": "b", + "type": { + "name": "bit", + "nullable": true + }, + "table": "things" + }, + { + "name": "u", + "type": { + "name": "uniqueidentifier", + "nullable": true + }, + "table": "things" + }, + { + "name": "x", + "type": { + "name": "xml", + "nullable": true + }, + "table": "things" + }, + { + "name": "j", + "type": { + "name": "json", + "nullable": true + }, + "table": "things" + }, + { + "name": "sv", + "type": { + "name": "sql_variant", + "nullable": true + }, + "table": "things" + }, + { + "name": "rv", + "type": { + "name": "rowversion", + "nullable": true + }, + "table": "things" + }, + { + "name": "g", + "type": { + "name": "geography", + "nullable": true + }, + "table": "things" + }, + { + "name": "h", + "type": { + "name": "hierarchyid", + "nullable": true + }, + "table": "things" + }, + { + "name": "vec", + "type": { + "name": "vector", + "nullable": true, + "args": [ + { + "int": 3 + } + ] + }, + "table": "things" + }, + { + "name": "sn", + "type": { + "name": "nvarchar", + "nullable": true, + "args": [ + { + "int": 128 + } + ] + }, + "table": "things" + }, + { + "name": "phone", + "type": { + "name": "phonenumber" + }, + "table": "things" + }, + { + "name": "bare", + "type": { + "name": "varchar", + "nullable": true, + "args": [ + { + "int": 1 + } + ] + }, + "table": "things" + } + ], + "params": [] + }, + { + "name": "Casts", + "cmd": ":one", + "columns": [ + { + "name": "a", + "type": { + "name": "decimal", + "args": [ + { + "int": 5 + }, + { + "int": 2 + } + ] + } + }, + { + "name": "b", + "type": { + "name": "nvarchar", + "args": [ + { + "ident": "max" + } + ] + } + }, + { + "name": "c", + "type": { + "name": "varchar", + "args": [ + { + "int": 10 + } + ] + } + }, + { + "name": "d", + "type": { + "name": "datetime2", + "args": [ + { + "int": 3 + } + ] + } + }, + { + "name": "e", + "type": { + "name": "phonenumber" + } + }, + { + "name": "f", + "type": { + "name": "real" + } + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "", + "type": { + "name": "decimal", + "args": [ + { + "int": 5 + }, + { + "int": 2 + } + ] + } + } + }, + { + "number": 2, + "column": { + "name": "", + "type": { + "name": "nvarchar", + "args": [ + { + "ident": "max" + } + ] + } + } + }, + { + "number": 3, + "column": { + "name": "", + "type": { + "name": "varchar", + "args": [ + { + "int": 10 + } + ] + } + } + }, + { + "number": 4, + "column": { + "name": "", + "type": { + "name": "datetime2", + "args": [ + { + "int": 3 + } + ] + } + } + }, + { + "number": 5, + "column": { + "name": "", + "type": { + "name": "phonenumber" + } + } + }, + { + "number": 6, + "column": { + "name": "", + "type": { + "name": "real" + } + } + } + ] + }, + { + "name": "Params", + "cmd": ":one", + "columns": [ + { + "name": "id", + "type": { + "name": "bigint" + }, + "table": "things" + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "price", + "type": { + "name": "decimal", + "args": [ + { + "int": 10 + }, + { + "int": 2 + } + ] + }, + "table": "things" + } + }, + { + "number": 2, + "column": { + "name": "body", + "type": { + "name": "nvarchar", + "nullable": true, + "args": [ + { + "ident": "max" + } + ] + }, + "table": "things" + } + }, + { + "number": 3, + "column": { + "name": "phone", + "type": { + "name": "phonenumber" + }, + "table": "things" + } + }, + { + "number": 4, + "column": { + "name": "vec", + "type": { + "name": "vector", + "nullable": true, + "args": [ + { + "int": 3 + } + ] + }, + "table": "things" + } + }, + { + "number": 5, + "column": { + "name": "offset_at", + "type": { + "name": "datetimeoffset", + "nullable": true, + "args": [ + { + "int": 7 + } + ] + }, + "table": "things" + } + } + ] + } +] diff --git a/internal/engine/duckdb/convert.go b/internal/engine/duckdb/convert.go index 03b4b01390..4a962378d5 100644 --- a/internal/engine/duckdb/convert.go +++ b/internal/engine/duckdb/convert.go @@ -812,15 +812,24 @@ func (c *cc) convertWindow(e *dw.WindowExpression) ast.Node { } // convertTypeExpression maps an unbound DuckDB type to a sqlc type name and -// the number of list/array dimensions wrapped around it. +// the number of list/array dimensions wrapped around it, which is what the +// legacy catalog reads. The whole type — its arguments, a struct's fields, +// a map's key and value, the nesting of a list of lists — goes along as +// its spelling, which is what the analysis core reads. func (c *cc) convertTypeExpression(t *dw.TypeExpression) (*ast.TypeName, int) { + typeName, dims := c.elementTypeName(t) + typeName.Spelling = renderTypeExpression(t) + return typeName, dims +} + +func (c *cc) elementTypeName(t *dw.TypeExpression) (*ast.TypeName, int) { name := identifier(t.TypeName) switch name { case "list", "array": // int[] is LIST(INTEGER); int[3] is ARRAY(INTEGER, 3). if len(t.Args) > 0 { if elem, ok := t.Args[0].(*dw.TypeExpression); ok { - typeName, dims := c.convertTypeExpression(elem) + typeName, dims := c.elementTypeName(elem) return typeName, dims + 1 } } @@ -831,6 +840,45 @@ func (c *cc) convertTypeExpression(t *dw.TypeExpression) (*ast.TypeName, int) { }, 0 } +// renderTypeExpression spells a type as a call expression the core reads: +// a list is array applied to its element, a fixed-size array carries its +// size, a struct's or union's fields are labelled, and a constant argument +// is written as it was. +func renderTypeExpression(t *dw.TypeExpression) string { + name := identifier(t.TypeName) + if t.Schema != "" { + name = schemaName(t.Schema) + "." + name + } + if name == "list" { + name = "array" + } + if len(t.Args) == 0 { + return name + } + parts := make([]string, 0, len(t.Args)) + for _, arg := range t.Args { + var part string + switch a := arg.(type) { + case *dw.TypeExpression: + part = renderTypeExpression(a) + if a.Alias != "" { + part = identifier(a.Alias) + " " + part + } + case *dw.ConstantExpression: + switch { + case a.Value.Str != "": + part = "'" + strings.ReplaceAll(a.Value.Str, "'", "''") + "'" + default: + part = strconv.FormatInt(a.Value.Int64, 10) + } + default: + continue + } + parts = append(parts, part) + } + return name + "(" + strings.Join(parts, ", ") + ")" +} + func (c *cc) convertReturning(returning []dw.Expr) *ast.List { if len(returning) == 0 { return nil diff --git a/internal/engine/duckdb/seed.go b/internal/engine/duckdb/seed.go index 852b5d217b..1d63264872 100644 --- a/internal/engine/duckdb/seed.go +++ b/internal/engine/duckdb/seed.go @@ -21,3 +21,24 @@ var dialectFS embed.FS func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } + +func init() { + core.RegisterCanonicalizer("duckdb", canonicalize) +} + +// canonicalize rewrites a type the way DuckDB stores it: a varchar's length +// is dropped, and a decimal declared without a precision is decimal(18,3). +func canonicalize(t *core.TypeExpr) *core.TypeExpr { + switch t.Name { + case "varchar": + if len(t.Args) > 0 { + return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable} + } + case "decimal": + if len(t.Args) == 0 { + p, s := int64(18), int64(3) + return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{{Int: &p}, {Int: &s}}} + } + } + return t +} diff --git a/internal/engine/googlesql/convert.go b/internal/engine/googlesql/convert.go index e94304096f..8731659222 100644 --- a/internal/engine/googlesql/convert.go +++ b/internal/engine/googlesql/convert.go @@ -452,7 +452,7 @@ func (c *cc) convertExpr(node zjast.Node) ast.Node { case *zjast.CastExpression: return &ast.TypeCast{ Arg: c.convertExpr(n.Expr), - TypeName: &ast.TypeName{Name: typeName(n.Type)}, + TypeName: spelledTypeName(typeName(n.Type)), Location: n.Pos(), } case *zjast.ExpressionSubquery: @@ -918,7 +918,7 @@ func (c *cc) convertCreateTableStatement(n *zjast.CreateTableStatement) ast.Node func (c *cc) convertColumnDefinition(n *zjast.ColumnDefinition) *ast.ColumnDef { col := &ast.ColumnDef{ Location: n.Pos(), - TypeName: &ast.TypeName{Name: columnSchemaTypeName(n.Schema)}, + TypeName: spelledTypeName(columnSchemaTypeName(n.Schema)), } if n.Name != nil { col.Colname = identifier(n.Name.Name) @@ -936,13 +936,6 @@ func (c *cc) convertColumnDefinition(n *zjast.ColumnDefinition) *ast.ColumnDef { } if simple, ok := n.Schema.(*zjast.SimpleColumnSchema); ok { - // Type parameters, e.g. STRING(10) or NUMERIC(10, 2). - if simple.TypeParameters != nil { - col.TypeName.Typmods = &ast.List{} - for _, param := range simple.TypeParameters.Parameters { - col.TypeName.Typmods.Items = append(col.TypeName.Typmods.Items, c.convertExpr(param)) - } - } if simple.DefaultExpression != nil { col.RawDefault = c.convertExpr(simple.DefaultExpression) } @@ -971,3 +964,14 @@ func (c *cc) convertTruncateStatement(n *zjast.TruncateStatement) ast.Node { Relations: &ast.List{Items: []ast.Node{parseRangeVar(n.Target)}}, } } + +// spelledTypeName is a type name the core reads from its spelling, whose +// name is the family the spelling applies: string for string(10), array +// for array(int64). +func spelledTypeName(spelling string) *ast.TypeName { + name := spelling + if i := strings.IndexByte(name, '('); i >= 0 { + name = name[:i] + } + return &ast.TypeName{Name: name, Spelling: spelling} +} diff --git a/internal/engine/googlesql/dialect/types.jsonl b/internal/engine/googlesql/dialect/types.jsonl index 08fc476b5e..c6d189461e 100644 --- a/internal/engine/googlesql/dialect/types.jsonl +++ b/internal/engine/googlesql/dialect/types.jsonl @@ -18,3 +18,5 @@ {"name": "enum", "category": "U"} {"name": "tokenlist", "category": "U"} {"name": "array", "category": "A"} +{"name": "range", "category": "U"} +{"name": "map", "category": "U"} diff --git a/internal/engine/googlesql/seed.go b/internal/engine/googlesql/seed.go index 11c85dd076..28766bf95b 100644 --- a/internal/engine/googlesql/seed.go +++ b/internal/engine/googlesql/seed.go @@ -14,3 +14,27 @@ var dialectFS embed.FS func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } + +func init() { + core.RegisterCanonicalizer("googlesql", canonicalize) +} + +// canonicalize reads the MAX of STRING(MAX) as the word it is rather than a +// type, which is the only thing a spelling cannot say for itself. +func canonicalize(t *core.TypeExpr) *core.TypeExpr { + return maxIdent(t) +} + +// maxIdent rewrites an argument that is the bare word max into an +// identifier argument. +func maxIdent(t *core.TypeExpr) *core.TypeExpr { + for i, a := range t.Args { + if a.Type != nil && a.Type.Name == "max" && len(a.Type.Args) == 0 { + out := t.Clone() + max := "max" + out.Args[i] = core.TypeArg{Label: a.Label, Ident: &max} + return out + } + } + return t +} diff --git a/internal/engine/googlesql/utils.go b/internal/engine/googlesql/utils.go index 002f217c37..e22a930b24 100644 --- a/internal/engine/googlesql/utils.go +++ b/internal/engine/googlesql/utils.go @@ -182,33 +182,78 @@ func decodeEscapes(s string) string { // typeName renders a zetajones type node (used by CAST) as a lowercased type // name. Nested types degrade to a readable representation. +// typeName spells a type the way the analysis core reads one: a family +// applied to its arguments, with an array's element, a struct's labelled +// fields, a range's subtype and a map's key and value nested, and a +// parameter list's integers and MAX written as they were. func typeName(node zjast.Node) string { switch t := node.(type) { case *zjast.SimpleType: - return strings.ToLower(strings.Join(pathParts(t.Name), ".")) + return strings.ToLower(strings.Join(pathParts(t.Name), ".")) + typeParameters(t.TypeParameters) case *zjast.ArrayType: - return "array<" + typeName(t.ElementType) + ">" + return "array(" + typeName(t.ElementType) + ")" case *zjast.StructType: - return "struct" + fields := make([]string, 0, len(t.Fields)) + for _, f := range t.Fields { + field := typeName(f.Type) + if f.Name != nil { + field = identifier(f.Name.Name) + " " + field + } + fields = append(fields, field) + } + return "struct(" + strings.Join(fields, ", ") + ")" case *zjast.RangeType: - return "range<" + typeName(t.ElementType) + ">" + return "range(" + typeName(t.ElementType) + ")" case *zjast.MapType: - return "map" + return "map(" + typeName(t.KeyType) + ", " + typeName(t.ValueType) + ")" default: return "" } } +// typeParameters spells a parameter list, STRING(10) or BYTES(MAX), as the +// arguments of a call. +func typeParameters(params *zjast.TypeParameterList) string { + if params == nil || len(params.Parameters) == 0 { + return "" + } + parts := make([]string, 0, len(params.Parameters)) + for _, p := range params.Parameters { + switch v := p.(type) { + case *zjast.IntLiteral: + parts = append(parts, v.Image) + case *zjast.MaxLiteral: + parts = append(parts, "max") + default: + continue + } + } + if len(parts) == 0 { + return "" + } + return "(" + strings.Join(parts, ", ") + ")" +} + // columnSchemaTypeName renders a CREATE TABLE column schema as a lowercased // type name. Nested types degrade to a readable representation. +// columnSchemaTypeName spells a column's type the way typeName spells a +// type. func columnSchemaTypeName(node zjast.Node) string { switch t := node.(type) { case *zjast.SimpleColumnSchema: - return strings.ToLower(strings.Join(pathParts(t.Type), ".")) + return strings.ToLower(strings.Join(pathParts(t.Type), ".")) + typeParameters(t.TypeParameters) case *zjast.ArrayColumnSchema: - return "array<" + columnSchemaTypeName(t.ElementSchema) + ">" + return "array(" + columnSchemaTypeName(t.ElementSchema) + ")" case *zjast.StructColumnSchema: - return "struct" + fields := make([]string, 0, len(t.Fields)) + for _, f := range t.Fields { + field := columnSchemaTypeName(f.Schema) + if f.Name != nil { + field = identifier(f.Name.Name) + " " + field + } + fields = append(fields, field) + } + return "struct(" + strings.Join(fields, ", ") + ")" default: return "" } diff --git a/internal/engine/mssql/convert.go b/internal/engine/mssql/convert.go index 6401b7bb88..45b3c8a498 100644 --- a/internal/engine/mssql/convert.go +++ b/internal/engine/mssql/convert.go @@ -63,11 +63,34 @@ func (c *cc) convert(node tsql.Node) ast.Node { return c.convertAlterTableAddTableElementStatement(n) case *tsql.AlterTableDropTableElementStatement: return c.convertAlterTableDropTableElementStatement(n) + case *tsql.CreateTypeUddtStatement: + return c.convertCreateTypeUddtStatement(n) default: return todo(n) } } +// convertCreateTypeUddtStatement reports CREATE TYPE name FROM base [NOT +// NULL] as the domain it is: a type standing on its base that may forbid +// NULL. +func (c *cc) convertCreateTypeUddtStatement(n *tsql.CreateTypeUddtStatement) ast.Node { + if n.Name == nil || n.Name.BaseIdentifier == nil || n.DataType == nil { + return todo(n) + } + stmt := &ast.CreateDomainStmt{ + Domainname: &ast.List{}, + TypeName: dataTypeName(n.DataType), + } + if n.Name.SchemaIdentifier != nil { + stmt.Domainname.Items = append(stmt.Domainname.Items, NewIdentifier(identifierValue(n.Name.SchemaIdentifier))) + } + stmt.Domainname.Items = append(stmt.Domainname.Items, NewIdentifier(identifierValue(n.Name.BaseIdentifier))) + if n.NullableConstraint != nil && !n.NullableConstraint.Nullable { + stmt.Constraints = &ast.List{Items: []ast.Node{&ast.Constraint{Contype: ast.ConstrTypeNotNull}}} + } + return stmt +} + func (c *cc) convertSelectStatement(n *tsql.SelectStatement) ast.Node { stmt := c.convertQueryExpression(n.QueryExpression) sel, ok := stmt.(*ast.SelectStmt) @@ -528,25 +551,25 @@ func (c *cc) convertScalarExpression(expr tsql.ScalarExpression) ast.Node { case *tsql.CastCall: return &ast.TypeCast{ Arg: c.convertScalarExpression(e.Parameter), - TypeName: &ast.TypeName{Name: dataTypeName(e.DataType)}, + TypeName: dataTypeName(e.DataType), Location: c.loc(e), } case *tsql.TryCastCall: return &ast.TypeCast{ Arg: c.convertScalarExpression(e.Parameter), - TypeName: &ast.TypeName{Name: dataTypeName(e.DataType)}, + TypeName: dataTypeName(e.DataType), Location: c.loc(e), } case *tsql.ConvertCall: return &ast.TypeCast{ Arg: c.convertScalarExpression(e.Parameter), - TypeName: &ast.TypeName{Name: dataTypeName(e.DataType)}, + TypeName: dataTypeName(e.DataType), Location: c.loc(e), } case *tsql.TryConvertCall: return &ast.TypeCast{ Arg: c.convertScalarExpression(e.Parameter), - TypeName: &ast.TypeName{Name: dataTypeName(e.DataType)}, + TypeName: dataTypeName(e.DataType), Location: c.loc(e), } case *tsql.CoalesceExpression: @@ -1037,7 +1060,7 @@ func (c *cc) convertColumnDefinition(n *tsql.ColumnDefinition, tablePrimaryKey m name := identifierValue(n.ColumnIdentifier) colDef := &ast.ColumnDef{ Colname: name, - TypeName: &ast.TypeName{Name: dataTypeName(n.DataType)}, + TypeName: dataTypeName(n.DataType), Location: c.loc(n), } @@ -1071,21 +1094,46 @@ func (c *cc) convertColumnDefinition(n *tsql.ColumnDefinition, tablePrimaryKey m // dataTypeName returns the lowercased base name of a column's declared type, // e.g. "nvarchar" for NVARCHAR(100). Length and precision arguments do not // name distinct types. -func dataTypeName(ref tsql.DataTypeReference) string { +// dataTypeName is a type as the core reads it: its name, qualified by its +// schema for a user-defined type, with its parameters as type modifiers, +// MAX among them as the word it is. +func dataTypeName(ref tsql.DataTypeReference) *ast.TypeName { switch t := ref.(type) { case *tsql.SqlDataTypeReference: + out := &ast.TypeName{Name: identifier(t.SqlDataTypeOption)} if t.Name != nil && t.Name.BaseIdentifier != nil { - return identifierValue(t.Name.BaseIdentifier) + out.Name = identifierValue(t.Name.BaseIdentifier) + } + for _, p := range t.Parameters { + switch v := p.(type) { + case *tsql.IntegerLiteral: + n, _ := strconv.ParseInt(v.Value, 10, 64) + out.Typmods = appendTypmod(out.Typmods, &ast.A_Const{Val: &ast.Integer{Ival: n}}) + case *tsql.MaxLiteral: + out.Typmods = appendTypmod(out.Typmods, &ast.String{Str: "max"}) + } } - return identifier(t.SqlDataTypeOption) + return out case *tsql.XmlDataTypeReference: - return "xml" + return &ast.TypeName{Name: "xml"} case *tsql.UserDataTypeReference: if t.Name != nil && t.Name.BaseIdentifier != nil { - return identifierValue(t.Name.BaseIdentifier) + name := identifierValue(t.Name.BaseIdentifier) + if t.Name.SchemaIdentifier != nil { + name = identifierValue(t.Name.SchemaIdentifier) + "." + name + } + return &ast.TypeName{Name: name} } } - return "" + return &ast.TypeName{} +} + +func appendTypmod(l *ast.List, n ast.Node) *ast.List { + if l == nil { + l = &ast.List{} + } + l.Items = append(l.Items, n) + return l } func (c *cc) convertDropTableStatement(n *tsql.DropTableStatement) ast.Node { diff --git a/internal/engine/mssql/seed.go b/internal/engine/mssql/seed.go index 7ce732b129..12bcbb1e3f 100644 --- a/internal/engine/mssql/seed.go +++ b/internal/engine/mssql/seed.go @@ -14,3 +14,53 @@ var dialectFS embed.FS func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } + +func init() { + core.RegisterCanonicalizer("mssql", canonicalize) +} + +// canonicalize rewrites a type the way sys.types stores it: a length or +// precision left out is filled in with SQL Server's default, float(p) is a +// real or a float by its mantissa, sysname is nvarchar(128), and the MAX +// of nvarchar(max) is the word it is rather than a type. +func canonicalize(t *core.TypeExpr) *core.TypeExpr { + for i, a := range t.Args { + if a.Type != nil && a.Type.Name == "max" && len(a.Type.Args) == 0 { + t = t.Clone() + max := "max" + t.Args[i] = core.TypeArg{Label: a.Label, Ident: &max} + } + } + switch t.Name { + case "sysname": + n := int64(128) + return &core.TypeExpr{Name: "nvarchar", Nullable: t.Nullable, Args: []core.TypeArg{{Int: &n}}} + case "float": + if len(t.Args) == 1 && t.Args[0].Int != nil { + if *t.Args[0].Int <= 24 { + return &core.TypeExpr{Name: "real", Nullable: t.Nullable} + } + return &core.TypeExpr{Name: "float", Nullable: t.Nullable} + } + case "char", "varchar", "nchar", "nvarchar", "binary", "varbinary": + if len(t.Args) == 0 { + n := int64(1) + return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{{Int: &n}}} + } + case "decimal", "numeric": + switch { + case len(t.Args) == 0: + p, s := int64(18), int64(0) + return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{{Int: &p}, {Int: &s}}} + case len(t.Args) == 1 && t.Args[0].Int != nil: + s := int64(0) + return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{t.Args[0], {Int: &s}}} + } + case "datetime2", "time", "datetimeoffset": + if len(t.Args) == 0 { + p := int64(7) + return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{{Int: &p}}} + } + } + return t +} From b0e1cf0233db28cec4b3795d7b0692e75ac6199d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 17:55:03 +0000 Subject: [PATCH 11/16] docs: describe the types analyze reports and how the design landed Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- docs/howto/analyze.md | 22 ++++++++++++++++------ internal/core/types.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/docs/howto/analyze.md b/docs/howto/analyze.md index 325acbd6f6..09784d2217 100644 --- a/docs/howto/analyze.md +++ b/docs/howto/analyze.md @@ -71,7 +71,7 @@ reports the result columns and parameters: { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "authors" }, @@ -97,7 +97,7 @@ reports the result columns and parameters: "column": { "name": "id", "type": { - "name": "bigserial" + "name": "bigint" }, "table": "authors" } @@ -109,10 +109,20 @@ reports the result columns and parameters: A column's `type` is written as a call expression: a `name` applied to `args`, each of which carries an optional `label` and exactly one of `type`, -`int`, `bool` or `string`, with `nullable` set at whatever depth it applies. -An array of text is `array` applied to `text`; a `Map(String, Nullable(UInt8))` -in ClickHouse is `map` applied to `string` and a nullable `uint8`. Names are -recorded as the engine reports them. +`int`, `bool`, `string` or `ident`, with `nullable` set at whatever depth it +applies. A `numeric(10,2)` column is `numeric` applied to `10` and `2`; an +array of text is `array` applied to `text`, and an array of arrays nests +one `array` per dimension; a `Map(String, Nullable(UInt8))` in ClickHouse is +`map` applied to `string` and a nullable `uint8`; a `STRUCT` in +GoogleSQL is `struct` applied to an `int64` labelled `a`; the `MAX` of SQL +Server's `nvarchar(max)` is the identifier `max`. + +Types are reported the way the engine itself stores and reports them rather +than the way the schema spelled them: PostgreSQL's `int` and `bigserial` are +`integer` and `bigint`, as `format_type` prints them; MySQL's `BOOLEAN` is +`tinyint(1)`; ClickHouse's `Decimal32(4)` is `decimal(9, 4)`; DuckDB's +`TEXT` is `varchar`; SQL Server's `FLOAT(24)` is `real`. SQLite, which +keeps a declared type as written, is reported as written. Pass `--ast` to also include each statement's parsed AST under an `ast` key. It has the same shape as the output of [`parse`](parse.md), with every node tagged diff --git a/internal/core/types.md b/internal/core/types.md index 0b0b27a9fb..a475a5e059 100644 --- a/internal/core/types.md +++ b/internal/core/types.md @@ -645,6 +645,48 @@ nesting, so Go codegen renders `[][]int32` as the legacy path does. DuckDB's `INTEGER[][]` and ClickHouse's `Array(Array(Int32))` produce the same rows and output, with `int32` in ClickHouse's case. +## As implemented + +The design above is implemented, engine by engine, with these departures +and details settled on the way: + +- The three per-dialect hooks are registered by an engine package at init, + by the name its `dialect.json` records, and looked up by that name, so a + catalog restored from the cache — which runs no seed — has them: + `core.RegisterCanonicalizer`, `core.RegisterUserTypeBase` (SQLite's + affinity rule, applied when the schema declares a family the seed does + not list) and `core.RegisterResultType` (ClickHouse's `toDecimal64(x, 4)` + and `toDateTime64(x, 3)`). +- SQLite's `dialect.json` says `"alias": "base"`, which makes each alias in + its `types.jsonl` a type of its own standing on the type it aliases, + rather than another spelling of it. +- An engine hands the core either a spelling (`TypeName.Spelling`, read by + `ParseTypeExpr`, which also reads words after a closing parenthesis as + part of the name, as in `decimal(10,2) unsigned`) or a name with + `Typmods` and `ArrayBounds`, where an integer constant is an integer + argument, a bare `ast.String` is an identifier and a quoted constant a + string. `ColumnDef.IsUnsigned` and `ColumnDef.Vals` add MySQL's unsigned + and enum members. `ParamRef.Name` carries the name a `{name:Type}` + placeholder gives itself. +- A cast is NULL when its operand is, or when its type says so, as + `Nullable(String)` does; a cast of a placeholder types the placeholder + and takes its name and source from what it is compared with. +- MySQL types `CAST(x AS CHAR(10))` as `varchar(10)` and `CAST(x AS + BINARY(8))` as `varbinary(8)`, which is what its metadata and a view over + the cast both report, rather than the `char(10)` the table above + proposed. `goldeneye` reads a table column's type from `COLUMN_TYPE`, in + the relations seed and in the analyze check, and compares an expression + by family alone, since the wire carries no arguments. +- SQLite reports a cast to a spelling that is not a storage class, such as + `DECIMAL(5,2)`, as that spelling, while the value's storage class is what + a run would show; the check's cases keep to storage classes. +- DuckDB's `JSON` is grouped under `varchar` by `duckdb_types()`'s logical + type, so a JSON column reports `varchar`; a named enum reports its name, + not its labels, since the canonicalizer cannot see the catalog. A + GoogleSQL array or struct constructor in a select list is still untyped. +- PostgreSQL's `relations.jsonl` still spells array columns as `pg_type` + does (`_text`), which the canonicalizer reads as `array(text)`. + ## Order of work 1. The tables and the interning entry point: `sql_type.expr`, `family_oid`, From 85b92aad15e86154e26b39eae6d2c82e4d2e0baa Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 17:58:17 +0000 Subject: [PATCH 12/16] core: a type in another namespace resolves there and is reported qualified A column declared myschema.mood resolves in that namespace rather than by its bare name, and a type outside the default namespaces is reported as myschema.mood, the way format_type prints a type off the search path. A dialect names its own default schema in dialect.json, so SQL Server's dbo and DuckDB's main report unqualified. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- internal/core/dialect.go | 18 ++++++++ internal/core/seed/seed.go | 10 +++++ internal/core/typename.go | 5 +++ internal/core/types.go | 45 +++++++++++++++++-- .../analyze_types/postgresql/stdout.json | 6 +-- .../engine/clickhouse/dialect/dialect.json | 1 + internal/engine/duckdb/dialect/dialect.json | 1 + internal/engine/mssql/dialect/dialect.json | 1 + 8 files changed, 81 insertions(+), 6 deletions(-) diff --git a/internal/core/dialect.go b/internal/core/dialect.go index bad80dedcb..e192c442c9 100644 --- a/internal/core/dialect.go +++ b/internal/core/dialect.go @@ -77,6 +77,11 @@ const FlagPropagateNullable = "functions.propagate_nullable" // e.id. const FlagQualifyDuplicateColumns = "columns.qualify_duplicates" +// FlagDefaultSchema holds the schema the dialect puts an unqualified object +// in, when that is not the catalog's own default: a type in it is reported +// without its schema. +const FlagDefaultSchema = "schema.default" + // FlagCastCategories holds the categories whose types are all implicitly // castable to one another, as the dialect's seed declared them, so that a type // arriving after the seed — an extension's, say — can join its category. @@ -190,3 +195,16 @@ func (c *Catalog) QualifiesDuplicateColumns() bool { v, _ := c.DialectFlag(c.dialectOID, FlagQualifyDuplicateColumns) return v == "true" } + +// DefaultNamespaces lists the namespaces a type is reported from without +// qualification: the catalog's default, PostgreSQL's system catalog, and +// the dialect's own default schema when it names one. +func (c *Catalog) DefaultNamespaces() []string { + out := []string{"public", "pg_catalog"} + if c.dialectOID != 0 { + if name, _ := c.DialectFlag(c.dialectOID, FlagDefaultSchema); name != "" { + out = append(out, name) + } + } + return out +} diff --git a/internal/core/seed/seed.go b/internal/core/seed/seed.go index 1d6710d6e2..b4f5d0afba 100644 --- a/internal/core/seed/seed.go +++ b/internal/core/seed/seed.go @@ -105,6 +105,11 @@ type Settings struct { // enable_fts5 compile option adds. Modules map[string]string `json:"modules,omitempty"` + // DefaultSchema names the schema a dialect puts an unqualified object + // in when it is not the catalog's own default: SQL Server's dbo, + // DuckDB's main. A type in it is reported unqualified. + DefaultSchema string `json:"default_schema,omitempty"` + // Alias says what an alias in types.jsonl is. "canonical", the default, // makes it another spelling of the type, which a column declared with // it is reported as, the way PostgreSQL reports int as integer. "base" @@ -556,6 +561,11 @@ func (b *builder) consts() error { return err } } + if b.settings.DefaultSchema != "" { + if err := b.cat.SetDialectFlag(b.dialectOID, core.FlagDefaultSchema, strings.ToLower(b.settings.DefaultSchema)); err != nil { + return err + } + } for key, name := range map[string]string{ core.FlagBoolType: b.settings.Bool, core.FlagLimitType: b.settings.Limit, diff --git a/internal/core/typename.go b/internal/core/typename.go index d60257c5a2..fd2c08272a 100644 --- a/internal/core/typename.go +++ b/internal/core/typename.go @@ -37,6 +37,11 @@ func TypeExprOfTypeName(tn *ast.TypeName) *TypeExpr { if name == "" { return nil } + // An engine that reports the schema apart from the name qualifies it + // the way a dotted name does, so the type resolves in its namespace. + if schema := strings.ToLower(tn.Schema); schema != "" && schema != "pg_catalog" && !strings.Contains(name, ".") { + name = schema + "." + name + } // A name an engine spelled with its own arguments or array suffix reads // the same way a spelling does. t := ParseTypeExpr(name) diff --git a/internal/core/types.go b/internal/core/types.go index d93373464d..50d1f7360e 100644 --- a/internal/core/types.go +++ b/internal/core/types.go @@ -5,6 +5,7 @@ import ( "database/sql" "errors" "fmt" + "slices" "strings" "sync" @@ -53,9 +54,26 @@ func (t TypeInfo) IsFamily() bool { return t.FamilyOID == 0 } // answer is good for the life of the catalog. Analysis runs concurrently on // a restored catalog, so the cache is locked. type typeCache struct { - mu sync.RWMutex - infos map[int64]TypeInfo - exprs map[int64]*TypeExpr + mu sync.RWMutex + infos map[int64]TypeInfo + exprs map[int64]*TypeExpr + namespaces map[int64]string +} + +func (c *typeCache) namespace(oid int64) (string, bool) { + c.mu.RLock() + defer c.mu.RUnlock() + name, ok := c.namespaces[oid] + return name, ok +} + +func (c *typeCache) putNamespace(oid int64, name string) { + c.mu.Lock() + defer c.mu.Unlock() + if c.namespaces == nil { + c.namespaces = map[int64]string{} + } + c.namespaces[oid] = name } func (c *typeCache) info(oid int64) (TypeInfo, bool) { @@ -425,6 +443,22 @@ func (c *Catalog) LookupType(oid int64) (TypeInfo, error) { return info, nil } +// namespaceName is the name of a namespace row, remembered once read. +func (c *Catalog) namespaceName(oid int64) (string, error) { + if name, ok := c.types.namespace(oid); ok { + return name, nil + } + namespaces, err := c.Namespaces() + if err != nil { + return "", err + } + for _, ns := range namespaces { + c.types.putNamespace(ns.OID, ns.Name) + } + name, _ := c.types.namespace(oid) + return name, nil +} + // TypeExprOf is the expression a type row stands for, read back from its // arguments: the family's name for a family, the family applied to its // arguments for an instance. The result is the caller's to change. @@ -437,6 +471,11 @@ func (c *Catalog) TypeExprOf(oid int64) (*TypeExpr, error) { return nil, err } expr := &TypeExpr{Name: info.Name} + // A type outside the default namespaces is named with its namespace, + // as format_type prints a type off the search path. + if ns, err := c.namespaceName(info.NamespaceOID); err == nil && ns != "" && !slices.Contains(c.DefaultNamespaces(), ns) { + expr.Name = ns + "." + info.Name + } if !info.IsFamily() { rows, err := c.q.TypeArgs(context.Background(), oid) if err != nil { diff --git a/internal/endtoend/testdata/analyze_types/postgresql/stdout.json b/internal/endtoend/testdata/analyze_types/postgresql/stdout.json index 44b2487db8..484e8a6b31 100644 --- a/internal/endtoend/testdata/analyze_types/postgresql/stdout.json +++ b/internal/endtoend/testdata/analyze_types/postgresql/stdout.json @@ -250,7 +250,7 @@ { "name": "mm", "type": { - "name": "mood", + "name": "myschema.mood", "nullable": true }, "table": "things" @@ -509,7 +509,7 @@ { "name": "k", "type": { - "name": "mood" + "name": "myschema.mood" } } ], @@ -624,7 +624,7 @@ "column": { "name": "", "type": { - "name": "mood" + "name": "myschema.mood" } } } diff --git a/internal/engine/clickhouse/dialect/dialect.json b/internal/engine/clickhouse/dialect/dialect.json index fa7f2c5c73..b0a1bba547 100644 --- a/internal/engine/clickhouse/dialect/dialect.json +++ b/internal/engine/clickhouse/dialect/dialect.json @@ -1,5 +1,6 @@ { "dialect": "clickhouse", + "default_schema": "default", "const": { "integer": "Int64", "float": "Float64", diff --git a/internal/engine/duckdb/dialect/dialect.json b/internal/engine/duckdb/dialect/dialect.json index e7dbb91524..126b66cebb 100644 --- a/internal/engine/duckdb/dialect/dialect.json +++ b/internal/engine/duckdb/dialect/dialect.json @@ -1,5 +1,6 @@ { "dialect": "duckdb", + "default_schema": "main", "const": { "integer": "integer", "float": "double", diff --git a/internal/engine/mssql/dialect/dialect.json b/internal/engine/mssql/dialect/dialect.json index 88d4088df1..1d601b3b8f 100644 --- a/internal/engine/mssql/dialect/dialect.json +++ b/internal/engine/mssql/dialect/dialect.json @@ -1,5 +1,6 @@ { "dialect": "mssql", + "default_schema": "dbo", "const": { "integer": "int", "float": "float", From 271b10f5c65a76a6c9128a02a55dda665b9de95e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 18:13:16 +0000 Subject: [PATCH 13/16] core: express what a dialect does to a type as data rather than registered code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The per-dialect Go hooks are gone. dialect.json now carries rewrites — an ordered list of pattern, template and bound, such as float($1) to real where $1 <= 24 or Decimal32($1) to Decimal(9, $1) — which the seed loads into sql_type_rewrite and the catalog applies before interning; the words and argument positions that are identifiers rather than types, kept as dialect flags; and SQLite's affinity rule, loaded into sql_type_affinity. A result that depends on an argument's value is a return template in functions.jsonl, kept on sql_proc and filled in from the call's literals. ClickHouse numbers an Enum's members and sorts a Variant's in its converter, where spelling a type belongs, and goldeneye writes an array column of the PostgreSQL system catalogs as its element with the array flag rather than pg_type's _text, which the legacy path now sees too. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- internal/core/analyzer/expr.go | 54 ++- internal/core/catalog.go | 11 +- internal/core/catalogdb/models.go | 16 + internal/core/catalogdb/query.sql.go | 127 ++++++- internal/core/catalogdef/query.sql | 24 +- internal/core/catalogdef/schema.sql | 33 ++ internal/core/hooks.go | 124 ------- internal/core/proc.go | 16 +- internal/core/rewrite.go | 324 ++++++++++++++++++ internal/core/seed/seed.go | 103 +++++- internal/core/types.go | 21 +- internal/core/types.md | 80 +++-- .../testdata/codegen_json/gen/codegen.json | 300 ++++++++-------- internal/engine/clickhouse/convert.go | 43 ++- .../engine/clickhouse/dialect/dialect.json | 24 +- .../engine/clickhouse/dialect/functions.jsonl | 12 +- internal/engine/clickhouse/seed.go | 88 ----- internal/engine/dolphin/dialect/dialect.json | 37 +- internal/engine/dolphin/seed.go | 28 -- internal/engine/duckdb/dialect/dialect.json | 12 +- internal/engine/duckdb/seed.go | 21 -- .../engine/googlesql/dialect/dialect.json | 3 +- internal/engine/googlesql/seed.go | 24 -- internal/engine/mssql/dialect/dialect.json | 70 +++- internal/engine/mssql/seed.go | 50 --- .../engine/postgresql/dialect/relations.jsonl | 96 +++--- internal/engine/postgresql/seed.go | 15 - internal/engine/sqlite/dialect/dialect.json | 23 +- internal/engine/sqlite/seed.go | 25 -- internal/goldeneye/postgresql/relation.go | 8 +- 30 files changed, 1145 insertions(+), 667 deletions(-) delete mode 100644 internal/core/hooks.go create mode 100644 internal/core/rewrite.go diff --git a/internal/core/analyzer/expr.go b/internal/core/analyzer/expr.go index 624906be26..c9a2de9ed6 100644 --- a/internal/core/analyzer/expr.go +++ b/internal/core/analyzer/expr.go @@ -829,9 +829,9 @@ func (a *analyzer) typeFuncCall(f *ast.FuncCall) (exprType, error) { } } ret := a.returnType(p, argTypes) - // A dialect may know the result better than the catalog does, from the - // arguments' types and literal values. - if computed := a.resultTypeHook(name, args, argTypes); computed != nil { + // A result that depends on an argument's value is spelled by the seed + // as a template over the arguments, filled in from the call. + if computed := a.returnTemplate(p, args, argTypes); computed != nil { ret = a.lookupType(computed) } ret.nullable = p.ReturnNullable @@ -841,20 +841,46 @@ func (a *analyzer) typeFuncCall(f *ast.FuncCall) (exprType, error) { return ret, nil } -// resultTypeHook asks the dialect's result-type rule about a call, handing -// it each argument's type and, for an integer literal, its value. -func (a *analyzer) resultTypeHook(name string, args []ast.Node, argTypes []exprType) *core.TypeExpr { - ras := make([]core.ResultArg, len(args)) - for i, arg := range args { - ras[i].Type = a.exprOf(argTypes[i]) - if c, ok := arg.(*ast.A_Const); ok { - if n, ok := c.Val.(*ast.Integer); ok { - v := n.Ival - ras[i].Int = &v +// returnTemplate fills a seed's return template — Decimal(18, $2) — from +// the call: a $n that stands for an integer literal takes its value, and +// one that stands for a typed argument takes its type. A template that +// cannot be filled leaves the answer to the catalog. +func (a *analyzer) returnTemplate(p core.ProcOverload, args []ast.Node, argTypes []exprType) *core.TypeExpr { + if p.ReturnTemplate == "" { + return nil + } + template := core.ParseTypeExpr(p.ReturnTemplate) + fill := func(arg core.TypeArg) (core.TypeArg, bool) { + n, ok := argIndex(arg.Type.Name) + if !ok || n >= len(args) { + return core.TypeArg{}, false + } + if c, ok := args[n].(*ast.A_Const); ok { + if lit, ok := c.Val.(*ast.Integer); ok { + v := lit.Ival + return core.TypeArg{Label: arg.Label, Int: &v}, true + } + if lit, ok := c.Val.(*ast.String); ok { + v := lit.Str + return core.TypeArg{Label: arg.Label, String: &v}, true } } + if t := a.exprOf(argTypes[n]); t != nil { + return core.TypeArg{Label: arg.Label, Type: t.WithNullable(false)}, true + } + return core.TypeArg{}, false + } + for i, arg := range template.Args { + if arg.Type == nil || !strings.HasPrefix(arg.Type.Name, "$") { + continue + } + filled, ok := fill(arg) + if !ok { + return nil + } + template.Args[i] = filled } - return a.cat.ResultTypeOf(name, ras) + return template } // returnType resolves a polymorphic return type — max(anyelement), or a diff --git a/internal/core/catalog.go b/internal/core/catalog.go index 2310d6ca03..334dcdb787 100644 --- a/internal/core/catalog.go +++ b/internal/core/catalog.go @@ -5,7 +5,6 @@ import ( "database/sql" "fmt" "runtime" - "sync" "github.com/sqlc-dev/sqlc/internal/core/catalogdb" "github.com/sqlc-dev/sqlc/internal/core/catalogdef" @@ -30,16 +29,12 @@ type Catalog struct { loadExtension func(name string) error extensions map[string]bool - // types remembers the rows and expressions looked up so far. + // types remembers the rows and expressions looked up so far, and rules + // what the dialect does to a type before storing it. types typeCache - - // dialect is the seeded dialect's name, read back once it is asked for. - dialect string - dialectNameOnce sync.Once + rules rules } -func contextBackground() context.Context { return context.Background() } - type Option func(*Catalog) error func WithSeed(fn func(*Catalog) error) Option { diff --git a/internal/core/catalogdb/models.go b/internal/core/catalogdb/models.go index c3609307df..9c103853ea 100644 --- a/internal/core/catalogdb/models.go +++ b/internal/core/catalogdb/models.go @@ -84,6 +84,7 @@ type SqlProc struct { ReturnTypeOid int64 ReturnSet int64 ReturnNullable int64 + ReturnTemplate string Strict int64 VariadicKind string } @@ -113,6 +114,13 @@ type SqlType struct { NotNull int64 } +type SqlTypeAffinity struct { + DialectOid int64 + Ord int64 + Words string + TypeOid int64 +} + type SqlTypeArg struct { TypeOid int64 Ord int64 @@ -124,3 +132,11 @@ type SqlTypeArg struct { StringValue sql.NullString Ident sql.NullString } + +type SqlTypeRewrite struct { + DialectOid int64 + Ord int64 + Pattern string + Template string + Cond string +} diff --git a/internal/core/catalogdb/query.sql.go b/internal/core/catalogdb/query.sql.go index 80954b4643..d6b51e3ec9 100644 --- a/internal/core/catalogdb/query.sql.go +++ b/internal/core/catalogdb/query.sql.go @@ -261,8 +261,8 @@ const createProc = `-- name: CreateProc :execlastid INSERT INTO sql_proc (namespace_oid, dialect_oid, name, kind, - return_type_oid, return_set, return_nullable, strict, variadic_kind) -VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + return_type_oid, return_set, return_nullable, return_template, strict, variadic_kind) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ` type CreateProcParams struct { @@ -273,6 +273,7 @@ type CreateProcParams struct { ReturnTypeOid int64 ReturnSet int64 ReturnNullable int64 + ReturnTemplate string Strict int64 VariadicKind string } @@ -287,6 +288,7 @@ func (q *Queries) CreateProc(ctx context.Context, arg CreateProcParams) (int64, arg.ReturnTypeOid, arg.ReturnSet, arg.ReturnNullable, + arg.ReturnTemplate, arg.Strict, arg.VariadicKind, ) @@ -367,6 +369,28 @@ func (q *Queries) CreateType(ctx context.Context, arg CreateTypeParams) (int64, return result.LastInsertId() } +const createTypeAffinity = `-- name: CreateTypeAffinity :exec +INSERT INTO sql_type_affinity (dialect_oid, ord, words, type_oid) +VALUES (?, ?, ?, ?) +` + +type CreateTypeAffinityParams struct { + DialectOid int64 + Ord int64 + Words string + TypeOid int64 +} + +func (q *Queries) CreateTypeAffinity(ctx context.Context, arg CreateTypeAffinityParams) error { + _, err := q.db.ExecContext(ctx, createTypeAffinity, + arg.DialectOid, + arg.Ord, + arg.Words, + arg.TypeOid, + ) + return err +} + const createTypeArg = `-- name: CreateTypeArg :exec INSERT INTO sql_type_arg (type_oid, ord, label, arg_type_oid, nullable, int_value, bool_value, string_value, ident) @@ -400,6 +424,30 @@ func (q *Queries) CreateTypeArg(ctx context.Context, arg CreateTypeArgParams) er return err } +const createTypeRewrite = `-- name: CreateTypeRewrite :exec +INSERT INTO sql_type_rewrite (dialect_oid, ord, pattern, template, cond) +VALUES (?, ?, ?, ?, ?) +` + +type CreateTypeRewriteParams struct { + DialectOid int64 + Ord int64 + Pattern string + Template string + Cond string +} + +func (q *Queries) CreateTypeRewrite(ctx context.Context, arg CreateTypeRewriteParams) error { + _, err := q.db.ExecContext(ctx, createTypeRewrite, + arg.DialectOid, + arg.Ord, + arg.Pattern, + arg.Template, + arg.Cond, + ) + return err +} + const deleteAttribute = `-- name: DeleteAttribute :exec DELETE FROM sql_attribute WHERE class_oid = ? AND name = ? ` @@ -537,7 +585,7 @@ func (q *Queries) FindOperators(ctx context.Context, arg FindOperatorsParams) ([ } const findProcsAnyNamespace = `-- name: FindProcsAnyNamespace :many -SELECT oid, name, kind, return_type_oid, return_nullable +SELECT oid, name, kind, return_type_oid, return_nullable, return_template FROM sql_proc WHERE name = ? ` @@ -548,6 +596,7 @@ type FindProcsAnyNamespaceRow struct { Kind string ReturnTypeOid int64 ReturnNullable int64 + ReturnTemplate string } func (q *Queries) FindProcsAnyNamespace(ctx context.Context, name string) ([]FindProcsAnyNamespaceRow, error) { @@ -565,6 +614,7 @@ func (q *Queries) FindProcsAnyNamespace(ctx context.Context, name string) ([]Fin &i.Kind, &i.ReturnTypeOid, &i.ReturnNullable, + &i.ReturnTemplate, ); err != nil { return nil, err } @@ -580,7 +630,7 @@ func (q *Queries) FindProcsAnyNamespace(ctx context.Context, name string) ([]Fin } const findProcsInNamespaces = `-- name: FindProcsInNamespaces :many -SELECT oid, name, kind, return_type_oid, return_nullable +SELECT oid, name, kind, return_type_oid, return_nullable, return_template FROM sql_proc WHERE name = ?1 AND namespace_oid IN (/*SLICE:namespace_oids*/?) @@ -597,6 +647,7 @@ type FindProcsInNamespacesRow struct { Kind string ReturnTypeOid int64 ReturnNullable int64 + ReturnTemplate string } func (q *Queries) FindProcsInNamespaces(ctx context.Context, arg FindProcsInNamespacesParams) ([]FindProcsInNamespacesRow, error) { @@ -625,6 +676,7 @@ func (q *Queries) FindProcsInNamespaces(ctx context.Context, arg FindProcsInName &i.Kind, &i.ReturnTypeOid, &i.ReturnNullable, + &i.ReturnTemplate, ); err != nil { return nil, err } @@ -737,6 +789,73 @@ func (q *Queries) ListTablesInNamespace(ctx context.Context, namespaceOid int64) return items, nil } +const listTypeAffinities = `-- name: ListTypeAffinities :many +SELECT words, type_oid FROM sql_type_affinity +WHERE dialect_oid = ? ORDER BY ord +` + +type ListTypeAffinitiesRow struct { + Words string + TypeOid int64 +} + +func (q *Queries) ListTypeAffinities(ctx context.Context, dialectOid int64) ([]ListTypeAffinitiesRow, error) { + rows, err := q.db.QueryContext(ctx, listTypeAffinities, dialectOid) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ListTypeAffinitiesRow + for rows.Next() { + var i ListTypeAffinitiesRow + if err := rows.Scan(&i.Words, &i.TypeOid); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listTypeRewrites = `-- name: ListTypeRewrites :many +SELECT pattern, template, cond FROM sql_type_rewrite +WHERE dialect_oid = ? ORDER BY ord +` + +type ListTypeRewritesRow struct { + Pattern string + Template string + Cond string +} + +func (q *Queries) ListTypeRewrites(ctx context.Context, dialectOid int64) ([]ListTypeRewritesRow, error) { + rows, err := q.db.QueryContext(ctx, listTypeRewrites, dialectOid) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ListTypeRewritesRow + for rows.Next() { + var i ListTypeRewritesRow + if err := rows.Scan(&i.Pattern, &i.Template, &i.Cond); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const lookupAttribute = `-- name: LookupAttribute :one SELECT ns.name AS schema_name, cls.name AS table_name, a.name AS column_name, a.num, a.decl_type, a.auto_increment, a.is_primary_key, a.is_unique, a.not_null diff --git a/internal/core/catalogdef/query.sql b/internal/core/catalogdef/query.sql index ff31099c5f..2899ae13cd 100644 --- a/internal/core/catalogdef/query.sql +++ b/internal/core/catalogdef/query.sql @@ -106,6 +106,22 @@ SELECT oid, namespace_oid, name, expr, category, typtype, preferred, FROM sql_type WHERE oid = ?; +-- name: CreateTypeRewrite :exec +INSERT INTO sql_type_rewrite (dialect_oid, ord, pattern, template, cond) +VALUES (?, ?, ?, ?, ?); + +-- name: ListTypeRewrites :many +SELECT pattern, template, cond FROM sql_type_rewrite +WHERE dialect_oid = ? ORDER BY ord; + +-- name: CreateTypeAffinity :exec +INSERT INTO sql_type_affinity (dialect_oid, ord, words, type_oid) +VALUES (?, ?, ?, ?); + +-- name: ListTypeAffinities :many +SELECT words, type_oid FROM sql_type_affinity +WHERE dialect_oid = ? ORDER BY ord; + -- =============================== sql_class ============================= -- name: CreateClass :execlastid @@ -213,8 +229,8 @@ INSERT INTO sql_constraint (class_oid, name, kind, columns) VALUES (?, ?, ?, ?); -- name: CreateProc :execlastid INSERT INTO sql_proc (namespace_oid, dialect_oid, name, kind, - return_type_oid, return_set, return_nullable, strict, variadic_kind) -VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?); + return_type_oid, return_set, return_nullable, return_template, strict, variadic_kind) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?); -- name: CreateProcArg :exec INSERT INTO sql_proc_arg (proc_oid, ord, name, type_oid, mode, has_default) @@ -226,12 +242,12 @@ WHERE proc_oid = ? AND mode IN ('i', 'b', 'v') ORDER BY ord; -- name: FindProcsAnyNamespace :many -SELECT oid, name, kind, return_type_oid, return_nullable +SELECT oid, name, kind, return_type_oid, return_nullable, return_template FROM sql_proc WHERE name = ?; -- name: FindProcsInNamespaces :many -SELECT oid, name, kind, return_type_oid, return_nullable +SELECT oid, name, kind, return_type_oid, return_nullable, return_template FROM sql_proc WHERE name = sqlc.arg(name) AND namespace_oid IN (sqlc.slice(namespace_oids)); diff --git a/internal/core/catalogdef/schema.sql b/internal/core/catalogdef/schema.sql index e086c6cc1c..26c70436e9 100644 --- a/internal/core/catalogdef/schema.sql +++ b/internal/core/catalogdef/schema.sql @@ -81,6 +81,35 @@ CREATE TABLE sql_type_arg ( PRIMARY KEY (type_oid, ord) ); +-- sql_type_rewrite: the rewrites a dialect applies to a type before it is +-- interned, in order, which are how a dialect stores what only it spells: +-- SQL Server keeps float(24) as real and a bare decimal as decimal(18,0), +-- ClickHouse keeps Decimal32(4) as Decimal(9, 4). pattern is a type +-- expression whose arguments may be $1, $2... binding whatever stands there; +-- template is the expression the match becomes, with the bindings +-- substituted; cond bounds a binding, as "$1 <= 24" does. +CREATE TABLE sql_type_rewrite ( + dialect_oid INTEGER NOT NULL REFERENCES sql_dialect(oid), + ord INTEGER NOT NULL, + pattern TEXT NOT NULL, + template TEXT NOT NULL, + cond TEXT NOT NULL DEFAULT '', + PRIMARY KEY (dialect_oid, ord) +); + +-- sql_type_affinity: the rule a dialect resolves an unseeded type family +-- by, in order: the first row one of whose words the family's name +-- contains names the type it stands on, and a row with no words is the +-- default. SQLite gives every declared spelling one of five affinities +-- this way. +CREATE TABLE sql_type_affinity ( + dialect_oid INTEGER NOT NULL REFERENCES sql_dialect(oid), + ord INTEGER NOT NULL, + words TEXT NOT NULL DEFAULT '', -- comma-separated, upper case + type_oid INTEGER NOT NULL REFERENCES sql_type(oid), + PRIMARY KEY (dialect_oid, ord) +); + -- sql_class: relations (tables, views, indexes). -- kind: 'r' = table, 'v' = view, 'i' = index, 'c' = composite type, 'f' = foreign CREATE TABLE sql_class ( @@ -136,6 +165,9 @@ CREATE TABLE sql_constraint ( -- kind: 'f' = function, 'a' = aggregate, 'w' = window, 'p' = procedure -- variadic_kind: 'n' = none, 'a' = array (VARIADIC any[]), 'v' = variadic-any -- return_set: 1 if SETOF / table-returning +-- return_template: the result as an expression over the call's arguments, +-- when it depends on their values: Decimal(18, $2) for +-- toDecimal64(x, s). Empty for a result the type says. CREATE TABLE sql_proc ( oid INTEGER PRIMARY KEY AUTOINCREMENT, namespace_oid INTEGER REFERENCES sql_namespace(oid), @@ -145,6 +177,7 @@ CREATE TABLE sql_proc ( return_type_oid INTEGER NOT NULL REFERENCES sql_type(oid), return_set INTEGER NOT NULL DEFAULT 0, return_nullable INTEGER NOT NULL DEFAULT 1, + return_template TEXT NOT NULL DEFAULT '', strict INTEGER NOT NULL DEFAULT 0, variadic_kind TEXT NOT NULL DEFAULT 'n' ); diff --git a/internal/core/hooks.go b/internal/core/hooks.go deleted file mode 100644 index 2c0ed6b543..0000000000 --- a/internal/core/hooks.go +++ /dev/null @@ -1,124 +0,0 @@ -package core - -import "sync" - -// A Canonicalizer rewrites a type expression into the form its engine -// stores and reports: ClickHouse turns Decimal32(4) into Decimal(9, 4) and -// Enum('a', 'b') into Enum8('a' = 1, 'b' = 2), SQL Server turns float(24) -// into real. It sees each expression as a whole before its arguments are -// interned, and again on each argument, so it has to be idempotent. Aliases -// and argument defaults are data in the dialect's seed; a canonicalizer is -// for what only code can say. -type Canonicalizer func(*TypeExpr) *TypeExpr - -// A UserTypeBase says what a type family the schema declared and the -// dialect did not seed stands on: SQLite gives every declared spelling one -// of five affinities by a rule over its words, so FOO BAR(3) compares as a -// numeric. It returns the base family's name and the category the new type -// takes, or an empty name for a type that stands on nothing. -type UserTypeBase func(name string) (base, category string) - -// A ResultArg is one argument of a function call as a result-type rule -// sees it: its type, when known, and its value when it is an integer -// literal, which is what the scale of toDecimal64(x, 4) is. -type ResultArg struct { - Type *TypeExpr - Int *int64 -} - -// A ResultType says what a function returns when that depends on its -// arguments in a way no seed can spell: ClickHouse's toDecimal64(x, s) is -// Decimal(18, s). It returns nil to leave the answer to the catalog. -type ResultType func(name string, args []ResultArg) *TypeExpr - -var ( - hooksMu sync.RWMutex - canonicalizers = map[string]Canonicalizer{} - userTypeBases = map[string]UserTypeBase{} - resultTypes = map[string]ResultType{} -) - -// RegisterResultType installs a dialect's result-type rule, under the -// dialect's name. -func RegisterResultType(dialect string, fn ResultType) { - hooksMu.Lock() - defer hooksMu.Unlock() - resultTypes[dialect] = fn -} - -// ResultTypeOf applies the catalog's dialect's result-type rule to a call, -// or returns nil when there is none or it has nothing to say. -func (c *Catalog) ResultTypeOf(name string, args []ResultArg) *TypeExpr { - dialect := c.dialectName() - if dialect == "" { - return nil - } - hooksMu.RLock() - fn := resultTypes[dialect] - hooksMu.RUnlock() - if fn == nil { - return nil - } - return fn(name, args) -} - -// RegisterUserTypeBase installs the rule a dialect resolves an unseeded -// type family by, under the dialect's name. -func RegisterUserTypeBase(dialect string, fn UserTypeBase) { - hooksMu.Lock() - defer hooksMu.Unlock() - userTypeBases[dialect] = fn -} - -// userTypeBase applies the catalog's dialect's rule for an unseeded family. -func (c *Catalog) userTypeBase(name string) (base, category string) { - dialect := c.dialectName() - if dialect == "" { - return "", "" - } - hooksMu.RLock() - fn := userTypeBases[dialect] - hooksMu.RUnlock() - if fn == nil { - return "", "" - } - return fn(name) -} - -// RegisterCanonicalizer installs the canonicalizer for a dialect, by the -// name its dialect.json records. An engine registers its own at init, so -// that a catalog restored from the cache — which runs no seed — finds it by -// the dialect it was seeded with. -func RegisterCanonicalizer(dialect string, fn Canonicalizer) { - hooksMu.Lock() - defer hooksMu.Unlock() - canonicalizers[dialect] = fn -} - -// canonicalize applies the catalog's dialect's canonicalizer, if any. -func (c *Catalog) canonicalize(t *TypeExpr) *TypeExpr { - name := c.dialectName() - if name == "" { - return t - } - hooksMu.RLock() - fn := canonicalizers[name] - hooksMu.RUnlock() - if fn == nil { - return t - } - return fn(t) -} - -// dialectName is the name of the dialect the catalog was seeded with. -func (c *Catalog) dialectName() string { - if c.dialectOID == 0 { - return "" - } - c.dialectNameOnce.Do(func() { - if row, err := c.q.SeededDialect(contextBackground()); err == nil { - c.dialect = row.Name - } - }) - return c.dialect -} diff --git a/internal/core/proc.go b/internal/core/proc.go index 2409a4c4af..28bf32c978 100644 --- a/internal/core/proc.go +++ b/internal/core/proc.go @@ -19,10 +19,14 @@ type ProcSpec struct { ReturnNullable bool // NeverNull marks a function whose result is never NULL even when an // argument is, in a dialect that otherwise propagates nullability. - NeverNull bool - Strict bool - VariadicKind string - Args []ProcArg + NeverNull bool + // ReturnTemplate is the result as an expression over the call's + // arguments, when it depends on their values: Decimal(18, $2) for + // toDecimal64(x, s). + ReturnTemplate string + Strict bool + VariadicKind string + Args []ProcArg } // The proc table stores nullability as one integer: 0 leaves it to the @@ -56,6 +60,7 @@ func (c *Catalog) CreateProc(p ProcSpec) (int64, error) { ReturnTypeOid: p.ReturnTypeOID, ReturnSet: boolToInt64(p.ReturnSet), ReturnNullable: returnNullable(p), + ReturnTemplate: p.ReturnTemplate, Strict: boolToInt64(p.Strict), VariadicKind: p.VariadicKind, }) @@ -99,6 +104,7 @@ type ProcOverload struct { ReturnTypeOID int64 ReturnNullable bool NeverNull bool + ReturnTemplate string ArgTypes []int64 } @@ -123,6 +129,7 @@ func (c *Catalog) FindProcs(name string, namespaceOIDs []int64) ([]ProcOverload, ReturnTypeOID: r.ReturnTypeOid, ReturnNullable: r.ReturnNullable == nullableAlways, NeverNull: r.ReturnNullable == nullableNever, + ReturnTemplate: r.ReturnTemplate, }) } } else { @@ -146,6 +153,7 @@ func (c *Catalog) FindProcs(name string, namespaceOIDs []int64) ([]ProcOverload, ReturnTypeOID: r.ReturnTypeOid, ReturnNullable: r.ReturnNullable == nullableAlways, NeverNull: r.ReturnNullable == nullableNever, + ReturnTemplate: r.ReturnTemplate, }) } } diff --git a/internal/core/rewrite.go b/internal/core/rewrite.go new file mode 100644 index 0000000000..debe834314 --- /dev/null +++ b/internal/core/rewrite.go @@ -0,0 +1,324 @@ +package core + +import ( + "context" + "fmt" + "strconv" + "strings" + "sync" + + "github.com/sqlc-dev/sqlc/internal/core/catalogdb" +) + +// A dialect describes, as data, what it does to a type before storing it. +// The seed loads three kinds of it from dialect.json: rewrites, which turn +// one expression into another (float(24) into real, a bare decimal into +// decimal(18, 0)); identifier words and positions, which say where a bare +// word is a word rather than a type (the max of nvarchar(max), the +// function of SimpleAggregateFunction(sum, UInt64)); and an affinity rule, +// which says what a family the schema declares and the seed does not list +// stands on. None of it is code, so an engine adds a dialect by writing +// files. + +// FlagIdents holds the words that are identifiers wherever they stand as a +// type argument, comma-separated. +const FlagIdents = "types.idents" + +// FlagIdentArgs holds the argument positions that are identifiers in a +// family, as "family:1,2;family:1". +const FlagIdentArgs = "types.ident_args" + +// typeRewrite is one rewrite, parsed: a pattern whose $n arguments bind +// whatever stands there, a template the bindings are substituted into, and +// a condition on a binding. +type typeRewrite struct { + pattern *TypeExpr + template *TypeExpr + cond rewriteCond +} + +// rewriteCond bounds an integer binding: "$1 <= 24". +type rewriteCond struct { + binding string + op string + value int64 +} + +// rules is what the catalog knows of its dialect's rewriting, read from the +// tables once and kept. The seed invalidates it as it adds to them. +type rules struct { + mu sync.Mutex + loaded bool + rewrites []typeRewrite + idents map[string]bool + identArgs map[string][]int +} + +func (c *Catalog) invalidateRules() { + c.rules.mu.Lock() + c.rules.loaded = false + c.rules.mu.Unlock() +} + +// loadRules reads the dialect's rewrites and identifier settings. +func (c *Catalog) loadRules() (*rules, error) { + r := &c.rules + r.mu.Lock() + defer r.mu.Unlock() + if r.loaded { + return r, nil + } + r.rewrites, r.idents, r.identArgs = nil, map[string]bool{}, map[string][]int{} + if c.dialectOID == 0 { + r.loaded = true + return r, nil + } + rows, err := c.q.ListTypeRewrites(context.Background(), c.dialectOID) + if err != nil { + return nil, fmt.Errorf("type rewrites: %w", err) + } + for _, row := range rows { + rw := typeRewrite{pattern: ParseTypeExpr(row.Pattern), template: ParseTypeExpr(row.Template)} + if row.Cond != "" { + cond, err := parseRewriteCond(row.Cond) + if err != nil { + return nil, fmt.Errorf("type rewrite %q: %w", row.Pattern, err) + } + rw.cond = cond + } + r.rewrites = append(r.rewrites, rw) + } + if idents, _ := c.DialectFlag(c.dialectOID, FlagIdents); idents != "" { + for _, w := range strings.Split(idents, ",") { + r.idents[strings.ToLower(strings.TrimSpace(w))] = true + } + } + if identArgs, _ := c.DialectFlag(c.dialectOID, FlagIdentArgs); identArgs != "" { + for _, entry := range strings.Split(identArgs, ";") { + family, positions, ok := strings.Cut(entry, ":") + if !ok { + continue + } + for _, p := range strings.Split(positions, ",") { + if n, err := strconv.Atoi(strings.TrimSpace(p)); err == nil { + family = strings.ToLower(strings.TrimSpace(family)) + r.identArgs[family] = append(r.identArgs[family], n) + } + } + } + } + r.loaded = true + return r, nil +} + +// parseRewriteCond reads "$1 <= 24". +func parseRewriteCond(s string) (rewriteCond, error) { + fields := strings.Fields(s) + if len(fields) != 3 || !strings.HasPrefix(fields[0], "$") { + return rewriteCond{}, fmt.Errorf("condition %q: want \"$n op value\"", s) + } + switch fields[1] { + case "<", "<=", "=", ">=", ">", "!=": + default: + return rewriteCond{}, fmt.Errorf("condition %q: unknown operator", s) + } + v, err := strconv.ParseInt(fields[2], 10, 64) + if err != nil { + return rewriteCond{}, fmt.Errorf("condition %q: %w", s, err) + } + return rewriteCond{binding: fields[0], op: fields[1], value: v}, nil +} + +func (cond rewriteCond) holds(bindings map[string]TypeArg) bool { + if cond.binding == "" { + return true + } + a, ok := bindings[cond.binding] + if !ok || a.Int == nil { + return false + } + switch cond.op { + case "<": + return *a.Int < cond.value + case "<=": + return *a.Int <= cond.value + case "=": + return *a.Int == cond.value + case ">=": + return *a.Int >= cond.value + case ">": + return *a.Int > cond.value + case "!=": + return *a.Int != cond.value + } + return false +} + +// AddTypeRewrite records a rewrite of the catalog's dialect, applied after +// the ones recorded before it. +func (c *Catalog) AddTypeRewrite(ord int, pattern, template, cond string) error { + err := c.q.CreateTypeRewrite(context.Background(), catalogdb.CreateTypeRewriteParams{ + DialectOid: c.dialectOID, + Ord: int64(ord), + Pattern: pattern, + Template: template, + Cond: cond, + }) + if err != nil { + return fmt.Errorf("type rewrite %q: %w", pattern, err) + } + c.invalidateRules() + return nil +} + +// AddTypeAffinity records the next step of the dialect's affinity rule: a +// family whose name contains one of words, upper-cased, stands on typeOID. +// No words is the default that ends the rule. +func (c *Catalog) AddTypeAffinity(ord int, words []string, typeOID int64) error { + err := c.q.CreateTypeAffinity(context.Background(), catalogdb.CreateTypeAffinityParams{ + DialectOid: c.dialectOID, + Ord: int64(ord), + Words: strings.ToUpper(strings.Join(words, ",")), + TypeOid: typeOID, + }) + if err != nil { + return fmt.Errorf("type affinity %d: %w", ord, err) + } + return nil +} + +// userTypeBase is the type an unseeded family stands on by the dialect's +// affinity rule, or 0 when the dialect has none or none of it matches. +func (c *Catalog) userTypeBase(name string) (int64, error) { + if c.dialectOID == 0 { + return 0, nil + } + rows, err := c.q.ListTypeAffinities(context.Background(), c.dialectOID) + if err != nil { + return 0, fmt.Errorf("type affinities: %w", err) + } + upper := strings.ToUpper(name) + for _, row := range rows { + if row.Words == "" { + return row.TypeOid, nil + } + for _, w := range strings.Split(row.Words, ",") { + if w != "" && strings.Contains(upper, w) { + return row.TypeOid, nil + } + } + } + return 0, nil +} + +// canonicalize applies the dialect's identifier settings and rewrites to +// an expression: the first rewrite whose pattern matches is applied, and +// its result is not rewritten again. +func (c *Catalog) canonicalize(t *TypeExpr) (*TypeExpr, error) { + r, err := c.loadRules() + if err != nil { + return nil, err + } + t = r.identify(t) + name := strings.ToLower(t.Name) + for _, rw := range r.rewrites { + if strings.ToLower(rw.pattern.Name) != name || len(rw.pattern.Args) != len(t.Args) { + continue + } + bindings, ok := matchArgs(rw.pattern.Args, t.Args) + if !ok || !rw.cond.holds(bindings) { + continue + } + out := substitute(rw.template, bindings) + out.Nullable = t.Nullable + return out, nil + } + return t, nil +} + +// identify turns the bare words the dialect calls identifiers into +// identifier arguments. +func (r *rules) identify(t *TypeExpr) *TypeExpr { + if len(t.Args) == 0 || (len(r.idents) == 0 && len(r.identArgs) == 0) { + return t + } + positions := r.identArgs[strings.ToLower(t.Name)] + var out *TypeExpr + for i, a := range t.Args { + if a.Type == nil || len(a.Type.Args) != 0 { + continue + } + word := strings.ToLower(a.Type.Name) + if !r.idents[word] && !containsInt(positions, i+1) { + continue + } + if out == nil { + out = t.Clone() + } + out.Args[i] = TypeArg{Label: a.Label, Ident: &word} + } + if out == nil { + return t + } + return out +} + +func containsInt(list []int, n int) bool { + for _, v := range list { + if v == n { + return true + } + } + return false +} + +// matchArgs matches a pattern's arguments against an expression's, binding +// each $n to what stands in its place and requiring a literal to be equal. +func matchArgs(pattern, args []TypeArg) (map[string]TypeArg, bool) { + bindings := map[string]TypeArg{} + for i, p := range pattern { + a := args[i] + switch { + case p.Type != nil && strings.HasPrefix(p.Type.Name, "$"): + bindings[p.Type.Name] = a + case p.Type != nil: + if a.Type == nil || !strings.EqualFold(a.Type.Name, p.Type.Name) { + return nil, false + } + case p.Int != nil: + if a.Int == nil || *a.Int != *p.Int { + return nil, false + } + case p.String != nil: + if a.String == nil || *a.String != *p.String { + return nil, false + } + case p.Ident != nil: + if a.Ident == nil || *a.Ident != *p.Ident { + return nil, false + } + default: + return nil, false + } + } + return bindings, true +} + +// substitute fills a template's $n arguments from the bindings. +func substitute(template *TypeExpr, bindings map[string]TypeArg) *TypeExpr { + out := template.Clone() + for i, a := range out.Args { + if a.Type != nil && strings.HasPrefix(a.Type.Name, "$") { + if bound, ok := bindings[a.Type.Name]; ok { + label := a.Label + out.Args[i] = bound + if label != "" { + out.Args[i].Label = label + } + } + } else if a.Type != nil { + out.Args[i].Type = substitute(a.Type, bindings) + } + } + return out +} diff --git a/internal/core/seed/seed.go b/internal/core/seed/seed.go index b4f5d0afba..69d561ad3b 100644 --- a/internal/core/seed/seed.go +++ b/internal/core/seed/seed.go @@ -31,6 +31,7 @@ import ( "io/fs" "path" "slices" + "strconv" "strings" "github.com/sqlc-dev/sqlc/internal/core" @@ -110,6 +111,24 @@ type Settings struct { // DuckDB's main. A type in it is reported unqualified. DefaultSchema string `json:"default_schema,omitempty"` + // Rewrites are what the dialect does to a type before storing it, in + // order: the first whose pattern matches applies. A pattern's $1, $2 + // bind whatever stands there, the template is what the match becomes, + // and Where bounds a binding, as "$1 <= 24" does. + Rewrites []Rewrite `json:"rewrites,omitempty"` + + // Idents are the words that are identifiers wherever they stand as a + // type argument, such as the max of nvarchar(max), and IdentArgs the + // argument positions, counted from one, that are identifiers in a + // family, such as the first of SimpleAggregateFunction(sum, UInt64). + Idents []string `json:"idents,omitempty"` + IdentArgs map[string][]int `json:"ident_args,omitempty"` + + // Affinity is the rule a type family the schema declares and the seed + // does not list stands on, in order: the first whose words the name + // contains names the base, and one with no words is the default. + Affinity []Affinity `json:"affinity,omitempty"` + // Alias says what an alias in types.jsonl is. "canonical", the default, // makes it another spelling of the type, which a column declared with // it is reported as, the way PostgreSQL reports int as integer. "base" @@ -122,6 +141,19 @@ type Settings struct { fsys fs.FS } +// Rewrite is one rewrite of a type expression. +type Rewrite struct { + From string `json:"from"` + To string `json:"to"` + Where string `json:"where,omitempty"` +} + +// Affinity is one step of the rule an unseeded family stands on. +type Affinity struct { + Contains []string `json:"contains,omitempty"` + Type string `json:"type"` +} + // Type is a type family the dialect defines. Aliases are other spellings of // it that a schema may use in a column definition; each becomes a row that // points at the type, as an alias of it or as a type standing on it, @@ -155,7 +187,8 @@ type Function struct { Kind string `json:"kind,omitempty"` Args []Arg `json:"args,omitempty"` // Returns names the result type, or "$1", "$2"... for the type of that - // argument. + // argument, or an expression over the arguments — Decimal(18, $2) — + // for a result that depends on an argument's value. Returns string `json:"returns"` Nullable bool `json:"nullable,omitempty"` // NeverNull marks a result that is never NULL even when an argument @@ -275,6 +308,9 @@ func apply(cat *core.Catalog, fsys fs.FS, settings Settings) error { if err := b.consts(); err != nil { return err } + if err := b.rules(); err != nil { + return err + } if err := b.categoryOperators(); err != nil { return err } @@ -608,6 +644,54 @@ func (b *builder) consts() error { return nil } +// rules records what the dialect does to a type before storing it: its +// rewrites, its identifier words and positions, and its affinity rule. +func (b *builder) rules() error { + s := b.settings + for i, rw := range s.Rewrites { + if rw.From == "" || rw.To == "" { + return fmt.Errorf("seed %s: rewrite %d needs from and to", s.Dialect, i+1) + } + if err := b.cat.AddTypeRewrite(i+1, rw.From, rw.To, rw.Where); err != nil { + return err + } + } + if len(s.Idents) > 0 { + if err := b.cat.SetDialectFlag(b.dialectOID, core.FlagIdents, strings.ToLower(strings.Join(s.Idents, ","))); err != nil { + return err + } + } + if len(s.IdentArgs) > 0 { + families := make([]string, 0, len(s.IdentArgs)) + for family := range s.IdentArgs { + families = append(families, family) + } + // In a fixed order, so the catalog comes out the same every time. + slices.Sort(families) + entries := make([]string, 0, len(families)) + for _, family := range families { + positions := make([]string, 0, len(s.IdentArgs[family])) + for _, p := range s.IdentArgs[family] { + positions = append(positions, strconv.Itoa(p)) + } + entries = append(entries, strings.ToLower(family)+":"+strings.Join(positions, ",")) + } + if err := b.cat.SetDialectFlag(b.dialectOID, core.FlagIdentArgs, strings.Join(entries, ";")); err != nil { + return err + } + } + for i, a := range s.Affinity { + oid, ok := b.oids[strings.ToLower(a.Type)] + if !ok { + return fmt.Errorf("seed %s: affinity names unknown type %q", s.Dialect, a.Type) + } + if err := b.cat.AddTypeAffinity(i+1, a.Contains, oid); err != nil { + return err + } + } + return nil +} + func (b *builder) categoryOperators() error { s := b.settings boolOID, ok := b.oids[strings.ToLower(s.Bool)] @@ -715,7 +799,8 @@ func (b *builder) addCast(c Cast) error { } func (b *builder) addFunction(fn Function) error { - returnOID, err := b.funcType(fn.Returns) + returns, template := returnTemplate(fn.Returns) + returnOID, err := b.funcType(returns) if err != nil { return fmt.Errorf("function %q: %w", fn.Name, err) } @@ -744,6 +829,7 @@ func (b *builder) addFunction(fn Function) error { ReturnTypeOID: returnOID, ReturnNullable: fn.Nullable, NeverNull: fn.NeverNull, + ReturnTemplate: template, Args: args, }) if err != nil { @@ -752,6 +838,19 @@ func (b *builder) addFunction(fn Function) error { return nil } +// returnTemplate splits a function's Returns into the type the catalog +// records and the template it keeps: a result spelled over the arguments, +// as Decimal(18, $2) is, records its family and keeps the whole spelling +// to fill in at each call. A bare $n, the type of that argument, is a +// pseudo-type of its own rather than a template. +func returnTemplate(returns string) (string, string) { + if !strings.Contains(returns, "$") || strings.HasPrefix(returns, "$") { + return returns, "" + } + t := core.ParseTypeExpr(returns) + return t.Name, returns +} + func (b *builder) addRelation(rel Relation) error { if rel.Name == "" { return errors.New("relation has no name") diff --git a/internal/core/types.go b/internal/core/types.go index 50d1f7360e..f163e3ad4b 100644 --- a/internal/core/types.go +++ b/internal/core/types.go @@ -175,12 +175,18 @@ func (c *Catalog) CreateUserType(name, category string) (int64, error) { // affinity rule does; the type then resolves through that base and // needs no operators of its own. if category == "U" { - if base, cat := c.userTypeBase(bare); base != "" { - if baseOID, err := c.TypeOID(base); err == nil { - spec.BaseOID = baseOID - spec.Category = cat - return c.CreateTypeSpec(spec) + baseOID, err := c.userTypeBase(bare) + if err != nil { + return 0, err + } + if baseOID != 0 { + base, err := c.LookupType(baseOID) + if err != nil { + return 0, err } + spec.BaseOID = baseOID + spec.Category = base.Category + return c.CreateTypeSpec(spec) } } oid, err := c.CreateTypeSpec(spec) @@ -591,7 +597,10 @@ func (c *Catalog) internType(t *TypeExpr, newFamily func(name string) (int64, er if t == nil || strings.TrimSpace(t.Name) == "" { return 0, nil, fmt.Errorf("missing type name") } - t = c.canonicalize(t) + t, err := c.canonicalize(t) + if err != nil { + return 0, nil, err + } name := strings.ToLower(strings.TrimSpace(t.Name)) familyOID, err := c.familyOIDByQualifiedName(name) if err != nil { diff --git a/internal/core/types.md b/internal/core/types.md index a475a5e059..5c7fa56e86 100644 --- a/internal/core/types.md +++ b/internal/core/types.md @@ -372,13 +372,15 @@ are dialect data or dialect code: `decimal(18, 3)` and `varchar(10)` is `varchar`; PostgreSQL's `int[3]` is `array(int4)`. A family in `types.jsonl` may say `"defaults": [18, 0]` or `"args": 0`. -3. **Rewrites that change the family by argument or member**, which are - code, since they are ClickHouse's enum numbering and MySQL's charset - folding: `Decimal32(s)` is `Decimal(9, s)`, `Enum('a', 'b')` is - `Enum8('a' = 1, 'b' = 2)`, `Variant(...)` sorts its members, `varchar(n) - character set binary` is `varbinary(n)`, `boolean` is `tinyint(1)`, - `float(24)` is `real`. An engine package registers a `Canonicalize(*TypeExpr)` - hook with its seed, and the catalog applies it before interning. +3. **Rewrites that change the family by argument**, which are data too: an + ordered list of pattern, template and bound in `dialect.json` — + `Decimal32($1)` to `Decimal(9, $1)`, `float($1)` to `real` where `$1 <= + 24`, `boolean` to `tinyint(1)`, `sysname` to `nvarchar(128)` — that the + seed loads into a table and the catalog applies before interning. What + only a parser can do stays in the engine's converter, which every engine + has anyway: ClickHouse numbers an Enum's members and sorts a Variant's + when it spells the type, and MySQL's parser folds `varchar(n) character + set binary` into `varbinary(n)`. The reported type is the canonical row's expression. The declared spelling is kept on the attribute in `decl_type`, for the formatter and for SQLite, @@ -462,13 +464,13 @@ catalog; PostgreSQL's catalog says `numeric + numeric` is `numeric` and its results drop the typmod. The catalog can only ever answer at the family level, and that is the baseline every dialect gets: an operator or function result is the family the overload names, with the arguments of an `$n` -result carried over from the argument it stands for. A dialect that reports -more registers a `ResultType(op, args []*TypeExpr) *TypeExpr` hook beside -its `Canonicalize` hook, and the analyzer applies it to the expression it -reports. ClickHouse needs it for its arithmetic and for value-dependent -results like `toDecimal64(x, 4)`, since its check compares whole -expressions; MySQL's check reads the wire type, which is the family with -its flags, so the baseline passes it. +result carried over from the argument it stands for. A result that depends +on an argument's value is a template in the seed — `"returns": "Decimal(18, +$2)"` for `toDecimal64(x, s)` — that the analyzer fills in from the call's +literals. ClickHouse needs it, since its check compares whole expressions; +MySQL's check reads the wire type, which is the family with its flags, so +the baseline passes it. Arithmetic promotion — `Int8 + UInt8` is `Int16` — +is not expressed yet. ### What each engine hands the core @@ -488,15 +490,15 @@ where it does not, the converter has a small change to make. | MySQL | `TINYINT(1)`, `DATETIME(6)`, `VARCHAR(255)`, `BOOLEAN` | `tinyint(1)`, `datetime(6)`, `varchar(255)`, `tinyint(1)`: the converter's `Typmods` are read, and `boolean` canonicalizes as MySQL does | | MySQL | `ENUM('a','b')`, `SET('x','y')` | `enum('a', 'b')`, `set('x', 'y')`: the converter renders `Vals` into the spelling | | MySQL | `CAST(x AS CHAR(10))` | `char(10)`: the cast converter names the SQL type, not the wire code `var_string` | -| MySQL | `VARCHAR(10) CHARACTER SET binary` | `varbinary(10)`, by the canonicalization hook; collation is not part of the type | +| MySQL | `VARCHAR(10) CHARACTER SET binary` | `varbinary(10)`, as the parser folds it; collation is not part of the type | | SQLite | any spelling | the spelling as an instance, `varchar(255)`, `foo bar(3)`, verbatim as `pragma_table_xinfo` reports it, with `base_oid` set by the affinity rule applied when the dialect resolves an unknown name; `types.jsonl`'s alias lists become the rule | | SQLite | `STRICT` tables, `ANY` | the family rows; a strict table's column names one of them or fails, as SQLite does | | SQLite | an expression | its storage class, `integer`, `real`, `text` or `blob`, which is what `typeof()` and the check report | | ClickHouse | every parametric type | the spelling, read as today, now also for casts, `{p:T}` placeholders and results | | ClickHouse | `Nullable(T)`, `LowCardinality(T)` | `T` with `nullable`; `lowcardinality(T)` with `base_oid` at `T` | -| ClickHouse | `Decimal32(4)`, `Enum('a', 'b')`, `Variant(String, Int64)`, `INT` | `decimal(9, 4)`, `enum8(a: 1, b: 2)`, `variant(int64, string)`, `int32`, by the canonicalization hook | +| ClickHouse | `Decimal32(4)`, `Enum('a', 'b')`, `Variant(String, Int64)`, `INT` | `decimal(9, 4)` by a rewrite, `enum8(a: 1, b: 2)` and `variant(int64, string)` by the converter, `int32` by an alias | | ClickHouse | `SimpleAggregateFunction(sum, UInt64)` | `simpleaggregatefunction(sum, uint64)` with `sum` an identifier argument | -| ClickHouse | `toDecimal64(x, s)`, `Int8 + UInt8` | `decimal(18, s)`, `int16`, by the result-type hook | +| ClickHouse | `toDecimal64(x, s)` | `decimal(18, s)`, by the seed's return template | | ClickHouse | `Nested(a UInt8, b String)` | a relation-shape rule, not a type: the column becomes `n.a array(uint8)` and `n.b array(string)` on load, as `system.columns` has them | | DuckDB | `STRUCT(a INTEGER, b VARCHAR)`, `MAP(K, V)`, `UNION(...)` | `struct(a: integer, b: varchar)`, `map(varchar, integer)`, `union(num: integer, str: varchar)`: the converter renders the darkwing type expression it already has instead of keeping its name | | DuckDB | `INTEGER[]`, `INTEGER[3]` | `array(integer)` and `array(integer, 3)`: a list is the cross-dialect array, a fixed size is its second argument | @@ -504,7 +506,7 @@ where it does not, the converter has a small change to make. | GoogleSQL | `ARRAY`, `STRUCT`, `RANGE` | `array(int64)`, `struct(a: int64, b: string)`, `range(date)`: the converter renders the zetajones type node in call form, or `ParseTypeExpr` accepts `<...>` | | GoogleSQL | `STRING(10)`, `STRING(MAX)`, `NUMERIC(10,2)`, `[1, 2]`, `STRUCT(1 AS x)` | the typmods are read, with `max` an identifier argument; the array and struct constructors are typed from their elements | | SQL Server | `NVARCHAR(MAX)`, `VARCHAR`, `DECIMAL` | `nvarchar(max)` with `max` an identifier argument; `varchar(1)` and `decimal(18, 0)` by the defaults `sys.types` applies | -| SQL Server | `FLOAT(24)` | `float(24)` with `canonical_oid` at `real`, by the hook | +| SQL Server | `FLOAT(24)` | `real`, by a rewrite | | SQL Server | `dbo.PhoneNumber`, `sysname` | a row in namespace `dbo`, `typtype` `d`, `base_oid` at `varchar(20)`, `not_null` set; `sysname` seeded the same way over `nvarchar(128)` | The `ident` argument is the one addition to `TypeExpr` and to @@ -538,9 +540,9 @@ casts. A function's argument and return types may be expressions, and the return type may reference an argument's value as well as its type. The category rules in `dialect.json` apply to families; an instance inherits its family's category, which is how `numeric(10, 2)` joins the numeric -casts without being seeded. An engine package may register two hooks with -its seed, `Canonicalize` and `ResultType`, for what its catalog does in -code. +casts without being seeded. `dialect.json` also carries the dialect's +rewrites, its identifier words and positions, and its affinity rule, so +that nothing about a dialect is code. `goldeneye` checks the analyze cases against what each database reports, and its answer shape is the same `TypeExpr`. ClickHouse and DuckDB report whole @@ -650,13 +652,21 @@ same rows and output, with `int32` in ClickHouse's case. The design above is implemented, engine by engine, with these departures and details settled on the way: -- The three per-dialect hooks are registered by an engine package at init, - by the name its `dialect.json` records, and looked up by that name, so a - catalog restored from the cache — which runs no seed — has them: - `core.RegisterCanonicalizer`, `core.RegisterUserTypeBase` (SQLite's - affinity rule, applied when the schema declares a family the seed does - not list) and `core.RegisterResultType` (ClickHouse's `toDecimal64(x, 4)` - and `toDateTime64(x, 3)`). +- What the note called hooks is data, so that an engine adds a dialect by + writing files and never by registering Go code. `dialect.json` carries + `rewrites`, an ordered list of pattern, template and optional bound — + `float($1)` to `real` where `$1 <= 24`, `decimal` to `decimal(18, 0)`, + `Decimal32($1)` to `Decimal(9, $1)`, `sysname` to `nvarchar(128)` — which + the seed loads into `sql_type_rewrite` and the catalog applies before + interning, first match winning; `idents` and `ident_args`, the words and + argument positions that are identifiers rather than types, kept as dialect + flags; and `affinity`, SQLite's ordered rule for a family the seed does + not list, loaded into `sql_type_affinity` and asked when a schema + declares one. A result that depends on an argument's value is a template + in `functions.jsonl` — `"returns": "Decimal(18, $2)"` — kept on + `sql_proc.return_template` and filled in from the call's literals. What + is genuinely about parsing stays in the engine's converter: ClickHouse + numbers an Enum's members and sorts a Variant's when it spells the type. - SQLite's `dialect.json` says `"alias": "base"`, which makes each alias in its `types.jsonl` a type of its own standing on the type it aliases, rather than another spelling of it. @@ -684,8 +694,8 @@ and details settled on the way: type, so a JSON column reports `varchar`; a named enum reports its name, not its labels, since the canonicalizer cannot see the catalog. A GoogleSQL array or struct constructor in a select list is still untyped. -- PostgreSQL's `relations.jsonl` still spells array columns as `pg_type` - does (`_text`), which the canonicalizer reads as `array(text)`. +- PostgreSQL's `relations.jsonl` spells an array column as its element with + the array flag, which `goldeneye` now writes from `typelem`. ## Order of work @@ -700,7 +710,7 @@ and details settled on the way: through the pointer chain. This is where ClickHouse's casts and parameters come right. 3. The engines, one at a time, each with an `analyze_types/` case - alongside ClickHouse's and each with its `Canonicalize` hook: PostgreSQL + alongside ClickHouse's and each with its rewrites: PostgreSQL typmods, dimensions, declared types and `format_type` names; MySQL unsigned, typmods, members and `boolean`, plus the `var_string` leak; DuckDB's nested types and dropped arguments; GoogleSQL's angle brackets; @@ -709,13 +719,13 @@ and details settled on the way: `ArrayDims` from the expression, and the `experiment_coreanalyzer` cases grow MySQL unsigned and boolean columns and a PostgreSQL two-dimensional array, so the core path generates what the legacy path does. -5. Result-type hooks, ClickHouse first, and the value-dependent return - types its seed needs; then MySQL's precision arithmetic once its check - reads precision from the wire. +5. Return templates for value-dependent results, ClickHouse first; then + MySQL's precision arithmetic once its check reads precision from the + wire. ## Open questions -- How far a result-type hook goes. ClickHouse's arithmetic promotion and +- How far a return template goes. ClickHouse's arithmetic promotion and `toDecimal64(x, 4)` are finite rules; `arrayMap(f, arr)` returns an array of the lambda's result, which needs the lambda typed first. - Whether DuckDB's dropped `VARCHAR(10)` length and reported anonymous enum diff --git a/internal/endtoend/testdata/codegen_json/gen/codegen.json b/internal/endtoend/testdata/codegen_json/gen/codegen.json index 1e3a217541..efcef91737 100644 --- a/internal/endtoend/testdata/codegen_json/gen/codegen.json +++ b/internal/endtoend/testdata/codegen_json/gen/codegen.json @@ -2869,7 +2869,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -2882,7 +2882,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -2908,7 +2908,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -2934,7 +2934,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -3991,7 +3991,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 64, "is_named_param": false, "is_func_call": false, "scope": "", @@ -4004,7 +4004,7 @@ "type": { "catalog": "", "schema": "", - "name": "_name" + "name": "name" }, "is_sqlc_slice": false, "embed_table": null, @@ -5669,7 +5669,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -5682,7 +5682,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -5708,7 +5708,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -6921,7 +6921,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -6934,7 +6934,7 @@ "type": { "catalog": "", "schema": "", - "name": "_int2" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -6947,7 +6947,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -6960,7 +6960,7 @@ "type": { "catalog": "", "schema": "", - "name": "_int2" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -6973,7 +6973,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -6986,7 +6986,7 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -6999,7 +6999,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -7012,7 +7012,7 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -7025,7 +7025,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -7038,7 +7038,7 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -7051,7 +7051,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -7064,7 +7064,7 @@ "type": { "catalog": "", "schema": "", - "name": "_int2" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -7077,7 +7077,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -7090,7 +7090,7 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -8251,7 +8251,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -8264,7 +8264,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -8508,7 +8508,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -8791,7 +8791,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -8804,7 +8804,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -10040,7 +10040,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -10375,7 +10375,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -10388,7 +10388,7 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -10414,7 +10414,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -10915,7 +10915,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -10928,7 +10928,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -10954,7 +10954,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -11289,7 +11289,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -11302,7 +11302,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -11328,7 +11328,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -11572,7 +11572,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -11647,7 +11647,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -11660,7 +11660,7 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -11800,7 +11800,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -11826,7 +11826,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -11930,7 +11930,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -12717,7 +12717,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -12730,7 +12730,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2vector" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -12743,7 +12743,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -12756,7 +12756,7 @@ "type": { "catalog": "", "schema": "", - "name": "oidvector" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -12769,7 +12769,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -12782,7 +12782,7 @@ "type": { "catalog": "", "schema": "", - "name": "oidvector" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -12795,7 +12795,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -12808,7 +12808,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2vector" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -13553,7 +13553,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -13566,7 +13566,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -13953,7 +13953,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -13966,7 +13966,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -14441,7 +14441,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -14454,7 +14454,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -15329,7 +15329,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -15342,7 +15342,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -16825,7 +16825,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -16838,7 +16838,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -17121,7 +17121,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17134,7 +17134,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2vector" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -17147,7 +17147,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17160,7 +17160,7 @@ "type": { "catalog": "", "schema": "", - "name": "oidvector" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -17173,7 +17173,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17186,7 +17186,7 @@ "type": { "catalog": "", "schema": "", - "name": "oidvector" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -17339,7 +17339,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 64, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17352,7 +17352,7 @@ "type": { "catalog": "", "schema": "", - "name": "_name" + "name": "name" }, "is_sqlc_slice": false, "embed_table": null, @@ -17739,7 +17739,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17752,7 +17752,7 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -17905,7 +17905,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17918,7 +17918,7 @@ "type": { "catalog": "", "schema": "", - "name": "_regtype" + "name": "regtype" }, "is_sqlc_slice": false, "embed_table": null, @@ -17931,7 +17931,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17944,7 +17944,7 @@ "type": { "catalog": "", "schema": "", - "name": "_regtype" + "name": "regtype" }, "is_sqlc_slice": false, "embed_table": null, @@ -18835,7 +18835,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -18848,7 +18848,7 @@ "type": { "catalog": "", "schema": "", - "name": "oidvector" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -18861,7 +18861,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -18874,7 +18874,7 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -18887,7 +18887,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -18900,7 +18900,7 @@ "type": { "catalog": "", "schema": "", - "name": "_char" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, @@ -18926,7 +18926,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -18965,7 +18965,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -18978,7 +18978,7 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -19082,7 +19082,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -19095,7 +19095,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -19108,7 +19108,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -20035,7 +20035,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -20048,7 +20048,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2vector" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -20149,7 +20149,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 64, "is_named_param": false, "is_func_call": false, "scope": "", @@ -20162,7 +20162,7 @@ "type": { "catalog": "", "schema": "", - "name": "_name" + "name": "name" }, "is_sqlc_slice": false, "embed_table": null, @@ -21990,7 +21990,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -23636,7 +23636,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -24010,7 +24010,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -38715,7 +38715,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -38728,7 +38728,7 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, @@ -38741,7 +38741,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -38754,7 +38754,7 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, @@ -38767,7 +38767,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -38780,7 +38780,7 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, @@ -38793,7 +38793,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -38806,7 +38806,7 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, @@ -38819,7 +38819,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -38832,7 +38832,7 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, @@ -39297,7 +39297,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -39310,7 +39310,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2vector" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -39323,7 +39323,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -39336,7 +39336,7 @@ "type": { "catalog": "", "schema": "", - "name": "_char" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, @@ -39684,7 +39684,7 @@ "type": { "catalog": "", "schema": "", - "name": "_pg_statistic" + "name": "pg_statistic" }, "is_sqlc_slice": false, "embed_table": null, @@ -39915,7 +39915,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -39928,7 +39928,7 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, @@ -40019,7 +40019,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40032,7 +40032,7 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, @@ -40045,7 +40045,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40058,7 +40058,7 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, @@ -40211,7 +40211,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 64, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40224,7 +40224,7 @@ "type": { "catalog": "", "schema": "", - "name": "_name" + "name": "name" }, "is_sqlc_slice": false, "embed_table": null, @@ -40250,7 +40250,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -40263,7 +40263,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40276,7 +40276,7 @@ "type": { "catalog": "", "schema": "", - "name": "_char" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, @@ -40380,7 +40380,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -40393,7 +40393,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40406,7 +40406,7 @@ "type": { "catalog": "", "schema": "", - "name": "_bool" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, @@ -40419,7 +40419,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 8, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40432,7 +40432,7 @@ "type": { "catalog": "", "schema": "", - "name": "_float8" + "name": "float8" }, "is_sqlc_slice": false, "embed_table": null, @@ -40445,7 +40445,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 8, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40458,7 +40458,7 @@ "type": { "catalog": "", "schema": "", - "name": "_float8" + "name": "float8" }, "is_sqlc_slice": false, "embed_table": null, @@ -40767,7 +40767,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40780,7 +40780,7 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, @@ -40871,7 +40871,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40884,7 +40884,7 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, @@ -40897,7 +40897,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40910,7 +40910,7 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, @@ -41492,7 +41492,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -42263,7 +42263,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -42276,7 +42276,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -42302,7 +42302,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -43343,7 +43343,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -43356,7 +43356,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2vector" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -46003,7 +46003,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -46016,7 +46016,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -46260,7 +46260,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -46530,7 +46530,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -46696,7 +46696,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -46906,7 +46906,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -47072,7 +47072,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -47368,7 +47368,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -47482,7 +47482,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -47622,7 +47622,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, diff --git a/internal/engine/clickhouse/convert.go b/internal/engine/clickhouse/convert.go index 540a2aa748..5a2c49be41 100644 --- a/internal/engine/clickhouse/convert.go +++ b/internal/engine/clickhouse/convert.go @@ -2,6 +2,7 @@ package clickhouse import ( "fmt" + "sort" "strconv" "strings" @@ -1069,6 +1070,10 @@ func (c *cc) convertColumnDeclaration(n *chast.ColumnDeclaration) *ast.ColumnDef return colDef } +// renderDataType spells a type the way ClickHouse stores it: an Enum's +// members are numbered and the family sized by their count, and a +// Variant's members are sorted, so that Enum('a', 'b') is Enum8('a' = 1, +// 'b' = 2) and Variant(String, Int64) is Variant(Int64, String). func renderDataType(dt *chast.DataType) string { if dt == nil { return "" @@ -1076,11 +1081,43 @@ func renderDataType(dt *chast.DataType) string { if len(dt.Parameters) == 0 { return dt.Name } + name := dt.Name parts := make([]string, 0, len(dt.Parameters)) - for _, p := range dt.Parameters { - parts = append(parts, renderTypeParam(p)) + switch strings.ToLower(name) { + case "enum", "enum8", "enum16": + if strings.EqualFold(name, "enum") { + name = "Enum8" + if len(dt.Parameters) > 127 { + name = "Enum16" + } + } + next := int64(1) + for _, p := range dt.Parameters { + switch v := p.(type) { + case *chast.BinaryExpr: + // 'a' = 3 numbers itself, and the next bare member follows it. + parts = append(parts, renderTypeParam(v)) + if lit, ok := v.Right.(*chast.Literal); ok { + if n, err := strconv.ParseInt(fmt.Sprint(lit.Value), 10, 64); err == nil { + next = n + 1 + } + } + default: + parts = append(parts, renderTypeParam(p)+" = "+strconv.FormatInt(next, 10)) + next++ + } + } + case "variant": + for _, p := range dt.Parameters { + parts = append(parts, renderTypeParam(p)) + } + sort.Strings(parts) + default: + for _, p := range dt.Parameters { + parts = append(parts, renderTypeParam(p)) + } } - return dt.Name + "(" + strings.Join(parts, ", ") + ")" + return name + "(" + strings.Join(parts, ", ") + ")" } func renderTypeParam(e chast.Expression) string { diff --git a/internal/engine/clickhouse/dialect/dialect.json b/internal/engine/clickhouse/dialect/dialect.json index b0a1bba547..51cb0b641a 100644 --- a/internal/engine/clickhouse/dialect/dialect.json +++ b/internal/engine/clickhouse/dialect/dialect.json @@ -15,5 +15,27 @@ "comparison": ["=", "==", "<>", "!=", "<", "<=", ">", ">="], "comparison_categories": "NBSDU", "arithmetic": ["+", "-", "*", "/", "%"], - "arithmetic_categories": "N" + "arithmetic_categories": "N", + "rewrites": [ + { + "from": "Decimal32($1)", + "to": "Decimal(9, $1)" + }, + { + "from": "Decimal64($1)", + "to": "Decimal(18, $1)" + }, + { + "from": "Decimal128($1)", + "to": "Decimal(38, $1)" + }, + { + "from": "Decimal256($1)", + "to": "Decimal(76, $1)" + } + ], + "ident_args": { + "AggregateFunction": [1], + "SimpleAggregateFunction": [1] + } } diff --git a/internal/engine/clickhouse/dialect/functions.jsonl b/internal/engine/clickhouse/dialect/functions.jsonl index 20898b8c9d..c0ca1406c1 100644 --- a/internal/engine/clickhouse/dialect/functions.jsonl +++ b/internal/engine/clickhouse/dialect/functions.jsonl @@ -352,12 +352,12 @@ {"name": "toString", "args": [{"type": "any"}], "returns": "String"} {"name": "toDateTime", "args": [{"type": "any"}, {"type": "any"}], "returns": "DateTime"} {"name": "toDate", "args": [{"type": "any"}, {"type": "any"}], "returns": "Date"} -{"name": "toDateTime64", "args": [{"type": "any"}, {"type": "any"}], "returns": "DateTime64"} -{"name": "toDateTime64", "args": [{"type": "any"}, {"type": "any"}, {"type": "any"}], "returns": "DateTime64"} -{"name": "toDecimal32", "args": [{"type": "any"}, {"type": "any"}], "returns": "Decimal32"} -{"name": "toDecimal64", "args": [{"type": "any"}, {"type": "any"}], "returns": "Decimal64"} -{"name": "toDecimal128", "args": [{"type": "any"}, {"type": "any"}], "returns": "Decimal128"} -{"name": "toDecimal256", "args": [{"type": "any"}, {"type": "any"}], "returns": "Decimal256"} +{"name": "toDateTime64", "args": [{"type": "any"}, {"type": "any"}], "returns": "DateTime64($2)"} +{"name": "toDateTime64", "args": [{"type": "any"}, {"type": "any"}, {"type": "any"}], "returns": "DateTime64($2, $3)"} +{"name": "toDecimal32", "args": [{"type": "any"}, {"type": "any"}], "returns": "Decimal(9, $2)"} +{"name": "toDecimal64", "args": [{"type": "any"}, {"type": "any"}], "returns": "Decimal(18, $2)"} +{"name": "toDecimal128", "args": [{"type": "any"}, {"type": "any"}], "returns": "Decimal(38, $2)"} +{"name": "toDecimal256", "args": [{"type": "any"}, {"type": "any"}], "returns": "Decimal(76, $2)"} {"name": "toFixedString", "args": [{"type": "any"}, {"type": "any"}], "returns": "FixedString"} {"name": "toStringCutToZero", "args": [{"type": "any"}], "returns": "String"} {"name": "reinterpretAsString", "args": [{"type": "any"}], "returns": "String"} diff --git a/internal/engine/clickhouse/seed.go b/internal/engine/clickhouse/seed.go index 3aad2a8362..b87a704e41 100644 --- a/internal/engine/clickhouse/seed.go +++ b/internal/engine/clickhouse/seed.go @@ -2,8 +2,6 @@ package clickhouse import ( "embed" - "sort" - "strings" "github.com/sqlc-dev/sqlc/internal/core" "github.com/sqlc-dev/sqlc/internal/core/seed" @@ -25,89 +23,3 @@ var dialectFS embed.FS func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } - -func init() { - core.RegisterCanonicalizer("clickhouse", canonicalize) - core.RegisterResultType("clickhouse", resultType) -} - -// decimalPrecisions is the precision each sized decimal family stands for: -// ClickHouse stores Decimal32(s) as Decimal(9, s). -var decimalPrecisions = map[string]int64{ - "decimal32": 9, - "decimal64": 18, - "decimal128": 38, - "decimal256": 76, -} - -// canonicalize rewrites a type the way ClickHouse stores and reports it: a -// sized decimal is a Decimal with that precision, an Enum is an Enum8 or -// Enum16 with its members numbered, a Variant's members are sorted, and -// the function an aggregate-function type names is a word rather than a -// type. -func canonicalize(t *core.TypeExpr) *core.TypeExpr { - switch t.Name { - case "decimal32", "decimal64", "decimal128", "decimal256": - if len(t.Args) == 1 && t.Args[0].Int != nil { - p := decimalPrecisions[t.Name] - return &core.TypeExpr{Name: "decimal", Nullable: t.Nullable, Args: []core.TypeArg{{Int: &p}, t.Args[0]}} - } - case "enum", "enum8", "enum16": - out := t.Clone() - if out.Name == "enum" { - out.Name = "enum8" - if len(out.Args) > 127 { - out.Name = "enum16" - } - } - next := int64(1) - for i := range out.Args { - a := &out.Args[i] - switch { - case a.Label != "" && a.Int != nil: - next = *a.Int + 1 - case a.String != nil: - // A bare member is numbered after the one before it. - label := *a.String - n := next - *a = core.TypeArg{Label: label, Int: &n} - next++ - } - } - return out - case "variant": - out := t.Clone() - sort.SliceStable(out.Args, func(i, j int) bool { - return out.Args[i].Type.String() < out.Args[j].Type.String() - }) - return out - case "aggregatefunction", "simpleaggregatefunction": - if len(t.Args) > 0 && t.Args[0].Type != nil && len(t.Args[0].Type.Args) == 0 { - out := t.Clone() - name := out.Args[0].Type.Name - out.Args[0] = core.TypeArg{Ident: &name} - return out - } - } - return t -} - -// resultType is what a conversion returns when that depends on an -// argument's value: toDecimal64(x, s) is Decimal(18, s) and -// toDateTime64(x, p) is DateTime64(p). -func resultType(name string, args []core.ResultArg) *core.TypeExpr { - switch strings.ToLower(name) { - case "todecimal32", "todecimal64", "todecimal128", "todecimal256": - if len(args) >= 2 && args[1].Int != nil { - p := decimalPrecisions[strings.TrimPrefix(strings.ToLower(name), "to")] - s := *args[1].Int - return &core.TypeExpr{Name: "decimal", Args: []core.TypeArg{{Int: &p}, {Int: &s}}} - } - case "todatetime64": - if len(args) >= 2 && args[1].Int != nil { - p := *args[1].Int - return &core.TypeExpr{Name: "datetime64", Args: []core.TypeArg{{Int: &p}}} - } - } - return nil -} diff --git a/internal/engine/dolphin/dialect/dialect.json b/internal/engine/dolphin/dialect/dialect.json index f48ee7183d..ce503b4960 100644 --- a/internal/engine/dolphin/dialect/dialect.json +++ b/internal/engine/dolphin/dialect/dialect.json @@ -11,5 +11,40 @@ "comparison_categories": "BNSDU", "arithmetic": ["+", "-", "*", "/", "%", "DIV"], "arithmetic_categories": "N", - "cast_categories": "NSD" + "cast_categories": "NSD", + "rewrites": [ + { + "from": "boolean", + "to": "tinyint(1)" + }, + { + "from": "bool", + "to": "tinyint(1)" + }, + { + "from": "decimal", + "to": "decimal(10, 0)" + }, + { + "from": "decimal($1)", + "to": "decimal($1, 0)" + }, + { + "from": "decimal unsigned", + "to": "decimal unsigned(10, 0)" + }, + { + "from": "decimal unsigned($1)", + "to": "decimal unsigned($1, 0)" + }, + { + "from": "float($1)", + "to": "float", + "where": "$1 <= 24" + }, + { + "from": "float($1)", + "to": "double" + } + ] } diff --git a/internal/engine/dolphin/seed.go b/internal/engine/dolphin/seed.go index e538879d0e..ddaeffb9c0 100644 --- a/internal/engine/dolphin/seed.go +++ b/internal/engine/dolphin/seed.go @@ -21,34 +21,6 @@ func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } -func init() { - core.RegisterCanonicalizer("mysql", canonicalize) -} - -// canonicalize rewrites a type the way MySQL stores it: a decimal declared -// without a precision is decimal(10,0), a float declared with one is a -// float or a double depending on it, and a boolean is a tinyint(1). -func canonicalize(t *core.TypeExpr) *core.TypeExpr { - switch { - case (t.Name == "decimal" || t.Name == "decimal unsigned") && len(t.Args) == 0: - p, s := int64(10), int64(0) - return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{{Int: &p}, {Int: &s}}} - case (t.Name == "decimal" || t.Name == "decimal unsigned") && len(t.Args) == 1 && t.Args[0].Int != nil: - s := int64(0) - return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{t.Args[0], {Int: &s}}} - case t.Name == "float" && len(t.Args) == 1 && t.Args[0].Int != nil: - name := "float" - if *t.Args[0].Int > 24 { - name = "double" - } - return &core.TypeExpr{Name: name, Nullable: t.Nullable} - case t.Name == "bool" || t.Name == "boolean": - one := int64(1) - return &core.TypeExpr{Name: "tinyint", Nullable: t.Nullable, Args: []core.TypeArg{{Int: &one}}} - } - return t -} - // stdlib is MySQL's functions in the form the catalog uses. They are embedded // in the binary and never change within a run, so they are read once. var stdlib = sync.OnceValue(func() []*catalog.Function { diff --git a/internal/engine/duckdb/dialect/dialect.json b/internal/engine/duckdb/dialect/dialect.json index 126b66cebb..061007b611 100644 --- a/internal/engine/duckdb/dialect/dialect.json +++ b/internal/engine/duckdb/dialect/dialect.json @@ -12,5 +12,15 @@ "comparison_categories": "BNSDU", "arithmetic": ["+", "-", "*", "/", "//", "%", "**", "^"], "arithmetic_categories": "N", - "cast_categories": "NSD" + "cast_categories": "NSD", + "rewrites": [ + { + "from": "varchar($1)", + "to": "varchar" + }, + { + "from": "decimal", + "to": "decimal(18, 3)" + } + ] } diff --git a/internal/engine/duckdb/seed.go b/internal/engine/duckdb/seed.go index 1d63264872..852b5d217b 100644 --- a/internal/engine/duckdb/seed.go +++ b/internal/engine/duckdb/seed.go @@ -21,24 +21,3 @@ var dialectFS embed.FS func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } - -func init() { - core.RegisterCanonicalizer("duckdb", canonicalize) -} - -// canonicalize rewrites a type the way DuckDB stores it: a varchar's length -// is dropped, and a decimal declared without a precision is decimal(18,3). -func canonicalize(t *core.TypeExpr) *core.TypeExpr { - switch t.Name { - case "varchar": - if len(t.Args) > 0 { - return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable} - } - case "decimal": - if len(t.Args) == 0 { - p, s := int64(18), int64(3) - return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{{Int: &p}, {Int: &s}}} - } - } - return t -} diff --git a/internal/engine/googlesql/dialect/dialect.json b/internal/engine/googlesql/dialect/dialect.json index 9fa041c005..728546d80b 100644 --- a/internal/engine/googlesql/dialect/dialect.json +++ b/internal/engine/googlesql/dialect/dialect.json @@ -10,5 +10,6 @@ "comparison": ["=", "<>", "!=", "<", "<=", ">", ">="], "comparison_categories": "BNSDTU", "arithmetic": ["+", "-", "*", "/"], - "arithmetic_categories": "N" + "arithmetic_categories": "N", + "idents": ["max"] } diff --git a/internal/engine/googlesql/seed.go b/internal/engine/googlesql/seed.go index 28766bf95b..11c85dd076 100644 --- a/internal/engine/googlesql/seed.go +++ b/internal/engine/googlesql/seed.go @@ -14,27 +14,3 @@ var dialectFS embed.FS func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } - -func init() { - core.RegisterCanonicalizer("googlesql", canonicalize) -} - -// canonicalize reads the MAX of STRING(MAX) as the word it is rather than a -// type, which is the only thing a spelling cannot say for itself. -func canonicalize(t *core.TypeExpr) *core.TypeExpr { - return maxIdent(t) -} - -// maxIdent rewrites an argument that is the bare word max into an -// identifier argument. -func maxIdent(t *core.TypeExpr) *core.TypeExpr { - for i, a := range t.Args { - if a.Type != nil && a.Type.Name == "max" && len(a.Type.Args) == 0 { - out := t.Clone() - max := "max" - out.Args[i] = core.TypeArg{Label: a.Label, Ident: &max} - return out - } - } - return t -} diff --git a/internal/engine/mssql/dialect/dialect.json b/internal/engine/mssql/dialect/dialect.json index 1d601b3b8f..30ea408744 100644 --- a/internal/engine/mssql/dialect/dialect.json +++ b/internal/engine/mssql/dialect/dialect.json @@ -12,5 +12,73 @@ "comparison_categories": "NBSD", "arithmetic": ["+", "-", "*", "/", "%"], "arithmetic_categories": "N", - "cast_categories": "NSD" + "cast_categories": "NSD", + "idents": ["max"], + "rewrites": [ + { + "from": "sysname", + "to": "nvarchar(128)" + }, + { + "from": "float($1)", + "to": "real", + "where": "$1 <= 24" + }, + { + "from": "float($1)", + "to": "float" + }, + { + "from": "char", + "to": "char(1)" + }, + { + "from": "varchar", + "to": "varchar(1)" + }, + { + "from": "nchar", + "to": "nchar(1)" + }, + { + "from": "nvarchar", + "to": "nvarchar(1)" + }, + { + "from": "binary", + "to": "binary(1)" + }, + { + "from": "varbinary", + "to": "varbinary(1)" + }, + { + "from": "decimal", + "to": "decimal(18, 0)" + }, + { + "from": "decimal($1)", + "to": "decimal($1, 0)" + }, + { + "from": "numeric", + "to": "numeric(18, 0)" + }, + { + "from": "numeric($1)", + "to": "numeric($1, 0)" + }, + { + "from": "datetime2", + "to": "datetime2(7)" + }, + { + "from": "time", + "to": "time(7)" + }, + { + "from": "datetimeoffset", + "to": "datetimeoffset(7)" + } + ] } diff --git a/internal/engine/mssql/seed.go b/internal/engine/mssql/seed.go index 12bcbb1e3f..7ce732b129 100644 --- a/internal/engine/mssql/seed.go +++ b/internal/engine/mssql/seed.go @@ -14,53 +14,3 @@ var dialectFS embed.FS func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } - -func init() { - core.RegisterCanonicalizer("mssql", canonicalize) -} - -// canonicalize rewrites a type the way sys.types stores it: a length or -// precision left out is filled in with SQL Server's default, float(p) is a -// real or a float by its mantissa, sysname is nvarchar(128), and the MAX -// of nvarchar(max) is the word it is rather than a type. -func canonicalize(t *core.TypeExpr) *core.TypeExpr { - for i, a := range t.Args { - if a.Type != nil && a.Type.Name == "max" && len(a.Type.Args) == 0 { - t = t.Clone() - max := "max" - t.Args[i] = core.TypeArg{Label: a.Label, Ident: &max} - } - } - switch t.Name { - case "sysname": - n := int64(128) - return &core.TypeExpr{Name: "nvarchar", Nullable: t.Nullable, Args: []core.TypeArg{{Int: &n}}} - case "float": - if len(t.Args) == 1 && t.Args[0].Int != nil { - if *t.Args[0].Int <= 24 { - return &core.TypeExpr{Name: "real", Nullable: t.Nullable} - } - return &core.TypeExpr{Name: "float", Nullable: t.Nullable} - } - case "char", "varchar", "nchar", "nvarchar", "binary", "varbinary": - if len(t.Args) == 0 { - n := int64(1) - return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{{Int: &n}}} - } - case "decimal", "numeric": - switch { - case len(t.Args) == 0: - p, s := int64(18), int64(0) - return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{{Int: &p}, {Int: &s}}} - case len(t.Args) == 1 && t.Args[0].Int != nil: - s := int64(0) - return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{t.Args[0], {Int: &s}}} - } - case "datetime2", "time", "datetimeoffset": - if len(t.Args) == 0 { - p := int64(7) - return &core.TypeExpr{Name: t.Name, Nullable: t.Nullable, Args: []core.TypeArg{{Int: &p}}} - } - } - return t -} diff --git a/internal/engine/postgresql/dialect/relations.jsonl b/internal/engine/postgresql/dialect/relations.jsonl index 3e2127e8a9..cef83a702a 100644 --- a/internal/engine/postgresql/dialect/relations.jsonl +++ b/internal/engine/postgresql/dialect/relations.jsonl @@ -3,71 +3,71 @@ {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_amop","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"amopfamily","type":"oid","not_null":true,"length":4},{"name":"amoplefttype","type":"oid","not_null":true,"length":4},{"name":"amoprighttype","type":"oid","not_null":true,"length":4},{"name":"amopstrategy","type":"int2","not_null":true,"length":2},{"name":"amoppurpose","type":"char","not_null":true,"length":1},{"name":"amopopr","type":"oid","not_null":true,"length":4},{"name":"amopmethod","type":"oid","not_null":true,"length":4},{"name":"amopsortfamily","type":"oid","not_null":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_amproc","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"amprocfamily","type":"oid","not_null":true,"length":4},{"name":"amproclefttype","type":"oid","not_null":true,"length":4},{"name":"amprocrighttype","type":"oid","not_null":true,"length":4},{"name":"amprocnum","type":"int2","not_null":true,"length":2},{"name":"amproc","type":"regproc","not_null":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_attrdef","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"adrelid","type":"oid","not_null":true,"length":4},{"name":"adnum","type":"int2","not_null":true,"length":2},{"name":"adbin","type":"pg_node_tree","not_null":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_attribute","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"attrelid","type":"oid","not_null":true,"length":4},{"name":"attname","type":"name","not_null":true,"length":64},{"name":"atttypid","type":"oid","not_null":true,"length":4},{"name":"attlen","type":"int2","not_null":true,"length":2},{"name":"attnum","type":"int2","not_null":true,"length":2},{"name":"attcacheoff","type":"int4","not_null":true,"length":4},{"name":"atttypmod","type":"int4","not_null":true,"length":4},{"name":"attndims","type":"int2","not_null":true,"length":2},{"name":"attbyval","type":"bool","not_null":true,"length":1},{"name":"attalign","type":"char","not_null":true,"length":1},{"name":"attstorage","type":"char","not_null":true,"length":1},{"name":"attcompression","type":"char","not_null":true,"length":1},{"name":"attnotnull","type":"bool","not_null":true,"length":1},{"name":"atthasdef","type":"bool","not_null":true,"length":1},{"name":"atthasmissing","type":"bool","not_null":true,"length":1},{"name":"attidentity","type":"char","not_null":true,"length":1},{"name":"attgenerated","type":"char","not_null":true,"length":1},{"name":"attisdropped","type":"bool","not_null":true,"length":1},{"name":"attislocal","type":"bool","not_null":true,"length":1},{"name":"attinhcount","type":"int2","not_null":true,"length":2},{"name":"attstattarget","type":"int2","not_null":true,"length":2},{"name":"attcollation","type":"oid","not_null":true,"length":4},{"name":"attacl","type":"_aclitem","array":true},{"name":"attoptions","type":"_text","array":true},{"name":"attfdwoptions","type":"_text","array":true},{"name":"attmissingval","type":"anyarray"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_attribute","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"attrelid","type":"oid","not_null":true,"length":4},{"name":"attname","type":"name","not_null":true,"length":64},{"name":"atttypid","type":"oid","not_null":true,"length":4},{"name":"attlen","type":"int2","not_null":true,"length":2},{"name":"attnum","type":"int2","not_null":true,"length":2},{"name":"attcacheoff","type":"int4","not_null":true,"length":4},{"name":"atttypmod","type":"int4","not_null":true,"length":4},{"name":"attndims","type":"int2","not_null":true,"length":2},{"name":"attbyval","type":"bool","not_null":true,"length":1},{"name":"attalign","type":"char","not_null":true,"length":1},{"name":"attstorage","type":"char","not_null":true,"length":1},{"name":"attcompression","type":"char","not_null":true,"length":1},{"name":"attnotnull","type":"bool","not_null":true,"length":1},{"name":"atthasdef","type":"bool","not_null":true,"length":1},{"name":"atthasmissing","type":"bool","not_null":true,"length":1},{"name":"attidentity","type":"char","not_null":true,"length":1},{"name":"attgenerated","type":"char","not_null":true,"length":1},{"name":"attisdropped","type":"bool","not_null":true,"length":1},{"name":"attislocal","type":"bool","not_null":true,"length":1},{"name":"attinhcount","type":"int2","not_null":true,"length":2},{"name":"attstattarget","type":"int2","not_null":true,"length":2},{"name":"attcollation","type":"oid","not_null":true,"length":4},{"name":"attacl","type":"aclitem","array":true,"length":16},{"name":"attoptions","type":"text","array":true},{"name":"attfdwoptions","type":"text","array":true},{"name":"attmissingval","type":"anyarray"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_auth_members","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"roleid","type":"oid","not_null":true,"length":4},{"name":"member","type":"oid","not_null":true,"length":4},{"name":"grantor","type":"oid","not_null":true,"length":4},{"name":"admin_option","type":"bool","not_null":true,"length":1},{"name":"inherit_option","type":"bool","not_null":true,"length":1},{"name":"set_option","type":"bool","not_null":true,"length":1}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_authid","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"rolname","type":"name","not_null":true,"length":64},{"name":"rolsuper","type":"bool","not_null":true,"length":1},{"name":"rolinherit","type":"bool","not_null":true,"length":1},{"name":"rolcreaterole","type":"bool","not_null":true,"length":1},{"name":"rolcreatedb","type":"bool","not_null":true,"length":1},{"name":"rolcanlogin","type":"bool","not_null":true,"length":1},{"name":"rolreplication","type":"bool","not_null":true,"length":1},{"name":"rolbypassrls","type":"bool","not_null":true,"length":1},{"name":"rolconnlimit","type":"int4","not_null":true,"length":4},{"name":"rolpassword","type":"text"},{"name":"rolvaliduntil","type":"timestamptz","length":8}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_available_extension_versions","columns":[{"name":"name","type":"name","length":64},{"name":"version","type":"text"},{"name":"installed","type":"bool","length":1},{"name":"superuser","type":"bool","length":1},{"name":"trusted","type":"bool","length":1},{"name":"relocatable","type":"bool","length":1},{"name":"schema","type":"name","length":64},{"name":"requires","type":"_name","array":true},{"name":"comment","type":"text"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_available_extension_versions","columns":[{"name":"name","type":"name","length":64},{"name":"version","type":"text"},{"name":"installed","type":"bool","length":1},{"name":"superuser","type":"bool","length":1},{"name":"trusted","type":"bool","length":1},{"name":"relocatable","type":"bool","length":1},{"name":"schema","type":"name","length":64},{"name":"requires","type":"name","array":true,"length":64},{"name":"comment","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_available_extensions","columns":[{"name":"name","type":"name","length":64},{"name":"default_version","type":"text"},{"name":"installed_version","type":"text"},{"name":"comment","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_backend_memory_contexts","columns":[{"name":"name","type":"text"},{"name":"ident","type":"text"},{"name":"parent","type":"text"},{"name":"level","type":"int4","length":4},{"name":"total_bytes","type":"int8","length":8},{"name":"total_nblocks","type":"int8","length":8},{"name":"free_bytes","type":"int8","length":8},{"name":"free_chunks","type":"int8","length":8},{"name":"used_bytes","type":"int8","length":8}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_cast","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"castsource","type":"oid","not_null":true,"length":4},{"name":"casttarget","type":"oid","not_null":true,"length":4},{"name":"castfunc","type":"oid","not_null":true,"length":4},{"name":"castcontext","type":"char","not_null":true,"length":1},{"name":"castmethod","type":"char","not_null":true,"length":1}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_class","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"relname","type":"name","not_null":true,"length":64},{"name":"relnamespace","type":"oid","not_null":true,"length":4},{"name":"reltype","type":"oid","not_null":true,"length":4},{"name":"reloftype","type":"oid","not_null":true,"length":4},{"name":"relowner","type":"oid","not_null":true,"length":4},{"name":"relam","type":"oid","not_null":true,"length":4},{"name":"relfilenode","type":"oid","not_null":true,"length":4},{"name":"reltablespace","type":"oid","not_null":true,"length":4},{"name":"relpages","type":"int4","not_null":true,"length":4},{"name":"reltuples","type":"float4","not_null":true,"length":4},{"name":"relallvisible","type":"int4","not_null":true,"length":4},{"name":"reltoastrelid","type":"oid","not_null":true,"length":4},{"name":"relhasindex","type":"bool","not_null":true,"length":1},{"name":"relisshared","type":"bool","not_null":true,"length":1},{"name":"relpersistence","type":"char","not_null":true,"length":1},{"name":"relkind","type":"char","not_null":true,"length":1},{"name":"relnatts","type":"int2","not_null":true,"length":2},{"name":"relchecks","type":"int2","not_null":true,"length":2},{"name":"relhasrules","type":"bool","not_null":true,"length":1},{"name":"relhastriggers","type":"bool","not_null":true,"length":1},{"name":"relhassubclass","type":"bool","not_null":true,"length":1},{"name":"relrowsecurity","type":"bool","not_null":true,"length":1},{"name":"relforcerowsecurity","type":"bool","not_null":true,"length":1},{"name":"relispopulated","type":"bool","not_null":true,"length":1},{"name":"relreplident","type":"char","not_null":true,"length":1},{"name":"relispartition","type":"bool","not_null":true,"length":1},{"name":"relrewrite","type":"oid","not_null":true,"length":4},{"name":"relfrozenxid","type":"xid","not_null":true,"length":4},{"name":"relminmxid","type":"xid","not_null":true,"length":4},{"name":"relacl","type":"_aclitem","array":true},{"name":"reloptions","type":"_text","array":true},{"name":"relpartbound","type":"pg_node_tree"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_class","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"relname","type":"name","not_null":true,"length":64},{"name":"relnamespace","type":"oid","not_null":true,"length":4},{"name":"reltype","type":"oid","not_null":true,"length":4},{"name":"reloftype","type":"oid","not_null":true,"length":4},{"name":"relowner","type":"oid","not_null":true,"length":4},{"name":"relam","type":"oid","not_null":true,"length":4},{"name":"relfilenode","type":"oid","not_null":true,"length":4},{"name":"reltablespace","type":"oid","not_null":true,"length":4},{"name":"relpages","type":"int4","not_null":true,"length":4},{"name":"reltuples","type":"float4","not_null":true,"length":4},{"name":"relallvisible","type":"int4","not_null":true,"length":4},{"name":"reltoastrelid","type":"oid","not_null":true,"length":4},{"name":"relhasindex","type":"bool","not_null":true,"length":1},{"name":"relisshared","type":"bool","not_null":true,"length":1},{"name":"relpersistence","type":"char","not_null":true,"length":1},{"name":"relkind","type":"char","not_null":true,"length":1},{"name":"relnatts","type":"int2","not_null":true,"length":2},{"name":"relchecks","type":"int2","not_null":true,"length":2},{"name":"relhasrules","type":"bool","not_null":true,"length":1},{"name":"relhastriggers","type":"bool","not_null":true,"length":1},{"name":"relhassubclass","type":"bool","not_null":true,"length":1},{"name":"relrowsecurity","type":"bool","not_null":true,"length":1},{"name":"relforcerowsecurity","type":"bool","not_null":true,"length":1},{"name":"relispopulated","type":"bool","not_null":true,"length":1},{"name":"relreplident","type":"char","not_null":true,"length":1},{"name":"relispartition","type":"bool","not_null":true,"length":1},{"name":"relrewrite","type":"oid","not_null":true,"length":4},{"name":"relfrozenxid","type":"xid","not_null":true,"length":4},{"name":"relminmxid","type":"xid","not_null":true,"length":4},{"name":"relacl","type":"aclitem","array":true,"length":16},{"name":"reloptions","type":"text","array":true},{"name":"relpartbound","type":"pg_node_tree"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_collation","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"collname","type":"name","not_null":true,"length":64},{"name":"collnamespace","type":"oid","not_null":true,"length":4},{"name":"collowner","type":"oid","not_null":true,"length":4},{"name":"collprovider","type":"char","not_null":true,"length":1},{"name":"collisdeterministic","type":"bool","not_null":true,"length":1},{"name":"collencoding","type":"int4","not_null":true,"length":4},{"name":"collcollate","type":"text"},{"name":"collctype","type":"text"},{"name":"colliculocale","type":"text"},{"name":"collicurules","type":"text"},{"name":"collversion","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_config","columns":[{"name":"name","type":"text"},{"name":"setting","type":"text"}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_constraint","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"conname","type":"name","not_null":true,"length":64},{"name":"connamespace","type":"oid","not_null":true,"length":4},{"name":"contype","type":"char","not_null":true,"length":1},{"name":"condeferrable","type":"bool","not_null":true,"length":1},{"name":"condeferred","type":"bool","not_null":true,"length":1},{"name":"convalidated","type":"bool","not_null":true,"length":1},{"name":"conrelid","type":"oid","not_null":true,"length":4},{"name":"contypid","type":"oid","not_null":true,"length":4},{"name":"conindid","type":"oid","not_null":true,"length":4},{"name":"conparentid","type":"oid","not_null":true,"length":4},{"name":"confrelid","type":"oid","not_null":true,"length":4},{"name":"confupdtype","type":"char","not_null":true,"length":1},{"name":"confdeltype","type":"char","not_null":true,"length":1},{"name":"confmatchtype","type":"char","not_null":true,"length":1},{"name":"conislocal","type":"bool","not_null":true,"length":1},{"name":"coninhcount","type":"int2","not_null":true,"length":2},{"name":"connoinherit","type":"bool","not_null":true,"length":1},{"name":"conkey","type":"_int2","array":true},{"name":"confkey","type":"_int2","array":true},{"name":"conpfeqop","type":"_oid","array":true},{"name":"conppeqop","type":"_oid","array":true},{"name":"conffeqop","type":"_oid","array":true},{"name":"confdelsetcols","type":"_int2","array":true},{"name":"conexclop","type":"_oid","array":true},{"name":"conbin","type":"pg_node_tree"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_constraint","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"conname","type":"name","not_null":true,"length":64},{"name":"connamespace","type":"oid","not_null":true,"length":4},{"name":"contype","type":"char","not_null":true,"length":1},{"name":"condeferrable","type":"bool","not_null":true,"length":1},{"name":"condeferred","type":"bool","not_null":true,"length":1},{"name":"convalidated","type":"bool","not_null":true,"length":1},{"name":"conrelid","type":"oid","not_null":true,"length":4},{"name":"contypid","type":"oid","not_null":true,"length":4},{"name":"conindid","type":"oid","not_null":true,"length":4},{"name":"conparentid","type":"oid","not_null":true,"length":4},{"name":"confrelid","type":"oid","not_null":true,"length":4},{"name":"confupdtype","type":"char","not_null":true,"length":1},{"name":"confdeltype","type":"char","not_null":true,"length":1},{"name":"confmatchtype","type":"char","not_null":true,"length":1},{"name":"conislocal","type":"bool","not_null":true,"length":1},{"name":"coninhcount","type":"int2","not_null":true,"length":2},{"name":"connoinherit","type":"bool","not_null":true,"length":1},{"name":"conkey","type":"int2","array":true,"length":2},{"name":"confkey","type":"int2","array":true,"length":2},{"name":"conpfeqop","type":"oid","array":true,"length":4},{"name":"conppeqop","type":"oid","array":true,"length":4},{"name":"conffeqop","type":"oid","array":true,"length":4},{"name":"confdelsetcols","type":"int2","array":true,"length":2},{"name":"conexclop","type":"oid","array":true,"length":4},{"name":"conbin","type":"pg_node_tree"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_conversion","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"conname","type":"name","not_null":true,"length":64},{"name":"connamespace","type":"oid","not_null":true,"length":4},{"name":"conowner","type":"oid","not_null":true,"length":4},{"name":"conforencoding","type":"int4","not_null":true,"length":4},{"name":"contoencoding","type":"int4","not_null":true,"length":4},{"name":"conproc","type":"regproc","not_null":true,"length":4},{"name":"condefault","type":"bool","not_null":true,"length":1}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_cursors","columns":[{"name":"name","type":"text"},{"name":"statement","type":"text"},{"name":"is_holdable","type":"bool","length":1},{"name":"is_binary","type":"bool","length":1},{"name":"is_scrollable","type":"bool","length":1},{"name":"creation_time","type":"timestamptz","length":8}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_database","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"datname","type":"name","not_null":true,"length":64},{"name":"datdba","type":"oid","not_null":true,"length":4},{"name":"encoding","type":"int4","not_null":true,"length":4},{"name":"datlocprovider","type":"char","not_null":true,"length":1},{"name":"datistemplate","type":"bool","not_null":true,"length":1},{"name":"datallowconn","type":"bool","not_null":true,"length":1},{"name":"datconnlimit","type":"int4","not_null":true,"length":4},{"name":"datfrozenxid","type":"xid","not_null":true,"length":4},{"name":"datminmxid","type":"xid","not_null":true,"length":4},{"name":"dattablespace","type":"oid","not_null":true,"length":4},{"name":"datcollate","type":"text","not_null":true},{"name":"datctype","type":"text","not_null":true},{"name":"daticulocale","type":"text"},{"name":"daticurules","type":"text"},{"name":"datcollversion","type":"text"},{"name":"datacl","type":"_aclitem","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_db_role_setting","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"setdatabase","type":"oid","not_null":true,"length":4},{"name":"setrole","type":"oid","not_null":true,"length":4},{"name":"setconfig","type":"_text","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_default_acl","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"defaclrole","type":"oid","not_null":true,"length":4},{"name":"defaclnamespace","type":"oid","not_null":true,"length":4},{"name":"defaclobjtype","type":"char","not_null":true,"length":1},{"name":"defaclacl","type":"_aclitem","not_null":true,"array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_database","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"datname","type":"name","not_null":true,"length":64},{"name":"datdba","type":"oid","not_null":true,"length":4},{"name":"encoding","type":"int4","not_null":true,"length":4},{"name":"datlocprovider","type":"char","not_null":true,"length":1},{"name":"datistemplate","type":"bool","not_null":true,"length":1},{"name":"datallowconn","type":"bool","not_null":true,"length":1},{"name":"datconnlimit","type":"int4","not_null":true,"length":4},{"name":"datfrozenxid","type":"xid","not_null":true,"length":4},{"name":"datminmxid","type":"xid","not_null":true,"length":4},{"name":"dattablespace","type":"oid","not_null":true,"length":4},{"name":"datcollate","type":"text","not_null":true},{"name":"datctype","type":"text","not_null":true},{"name":"daticulocale","type":"text"},{"name":"daticurules","type":"text"},{"name":"datcollversion","type":"text"},{"name":"datacl","type":"aclitem","array":true,"length":16}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_db_role_setting","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"setdatabase","type":"oid","not_null":true,"length":4},{"name":"setrole","type":"oid","not_null":true,"length":4},{"name":"setconfig","type":"text","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_default_acl","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"defaclrole","type":"oid","not_null":true,"length":4},{"name":"defaclnamespace","type":"oid","not_null":true,"length":4},{"name":"defaclobjtype","type":"char","not_null":true,"length":1},{"name":"defaclacl","type":"aclitem","not_null":true,"array":true,"length":16}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_depend","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"classid","type":"oid","not_null":true,"length":4},{"name":"objid","type":"oid","not_null":true,"length":4},{"name":"objsubid","type":"int4","not_null":true,"length":4},{"name":"refclassid","type":"oid","not_null":true,"length":4},{"name":"refobjid","type":"oid","not_null":true,"length":4},{"name":"refobjsubid","type":"int4","not_null":true,"length":4},{"name":"deptype","type":"char","not_null":true,"length":1}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_description","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"objoid","type":"oid","not_null":true,"length":4},{"name":"classoid","type":"oid","not_null":true,"length":4},{"name":"objsubid","type":"int4","not_null":true,"length":4},{"name":"description","type":"text","not_null":true}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_enum","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"enumtypid","type":"oid","not_null":true,"length":4},{"name":"enumsortorder","type":"float4","not_null":true,"length":4},{"name":"enumlabel","type":"name","not_null":true,"length":64}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_event_trigger","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"evtname","type":"name","not_null":true,"length":64},{"name":"evtevent","type":"name","not_null":true,"length":64},{"name":"evtowner","type":"oid","not_null":true,"length":4},{"name":"evtfoid","type":"oid","not_null":true,"length":4},{"name":"evtenabled","type":"char","not_null":true,"length":1},{"name":"evttags","type":"_text","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_extension","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"extname","type":"name","not_null":true,"length":64},{"name":"extowner","type":"oid","not_null":true,"length":4},{"name":"extnamespace","type":"oid","not_null":true,"length":4},{"name":"extrelocatable","type":"bool","not_null":true,"length":1},{"name":"extversion","type":"text","not_null":true},{"name":"extconfig","type":"_oid","array":true},{"name":"extcondition","type":"_text","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_event_trigger","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"evtname","type":"name","not_null":true,"length":64},{"name":"evtevent","type":"name","not_null":true,"length":64},{"name":"evtowner","type":"oid","not_null":true,"length":4},{"name":"evtfoid","type":"oid","not_null":true,"length":4},{"name":"evtenabled","type":"char","not_null":true,"length":1},{"name":"evttags","type":"text","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_extension","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"extname","type":"name","not_null":true,"length":64},{"name":"extowner","type":"oid","not_null":true,"length":4},{"name":"extnamespace","type":"oid","not_null":true,"length":4},{"name":"extrelocatable","type":"bool","not_null":true,"length":1},{"name":"extversion","type":"text","not_null":true},{"name":"extconfig","type":"oid","array":true,"length":4},{"name":"extcondition","type":"text","array":true}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_file_settings","columns":[{"name":"sourcefile","type":"text"},{"name":"sourceline","type":"int4","length":4},{"name":"seqno","type":"int4","length":4},{"name":"name","type":"text"},{"name":"setting","type":"text"},{"name":"applied","type":"bool","length":1},{"name":"error","type":"text"}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_foreign_data_wrapper","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"fdwname","type":"name","not_null":true,"length":64},{"name":"fdwowner","type":"oid","not_null":true,"length":4},{"name":"fdwhandler","type":"oid","not_null":true,"length":4},{"name":"fdwvalidator","type":"oid","not_null":true,"length":4},{"name":"fdwacl","type":"_aclitem","array":true},{"name":"fdwoptions","type":"_text","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_foreign_server","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"srvname","type":"name","not_null":true,"length":64},{"name":"srvowner","type":"oid","not_null":true,"length":4},{"name":"srvfdw","type":"oid","not_null":true,"length":4},{"name":"srvtype","type":"text"},{"name":"srvversion","type":"text"},{"name":"srvacl","type":"_aclitem","array":true},{"name":"srvoptions","type":"_text","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_foreign_table","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"ftrelid","type":"oid","not_null":true,"length":4},{"name":"ftserver","type":"oid","not_null":true,"length":4},{"name":"ftoptions","type":"_text","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_group","columns":[{"name":"groname","type":"name","length":64},{"name":"grosysid","type":"oid","length":4},{"name":"grolist","type":"_oid","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_hba_file_rules","columns":[{"name":"rule_number","type":"int4","length":4},{"name":"file_name","type":"text"},{"name":"line_number","type":"int4","length":4},{"name":"type","type":"text"},{"name":"database","type":"_text","array":true},{"name":"user_name","type":"_text","array":true},{"name":"address","type":"text"},{"name":"netmask","type":"text"},{"name":"auth_method","type":"text"},{"name":"options","type":"_text","array":true},{"name":"error","type":"text"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_foreign_data_wrapper","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"fdwname","type":"name","not_null":true,"length":64},{"name":"fdwowner","type":"oid","not_null":true,"length":4},{"name":"fdwhandler","type":"oid","not_null":true,"length":4},{"name":"fdwvalidator","type":"oid","not_null":true,"length":4},{"name":"fdwacl","type":"aclitem","array":true,"length":16},{"name":"fdwoptions","type":"text","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_foreign_server","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"srvname","type":"name","not_null":true,"length":64},{"name":"srvowner","type":"oid","not_null":true,"length":4},{"name":"srvfdw","type":"oid","not_null":true,"length":4},{"name":"srvtype","type":"text"},{"name":"srvversion","type":"text"},{"name":"srvacl","type":"aclitem","array":true,"length":16},{"name":"srvoptions","type":"text","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_foreign_table","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"ftrelid","type":"oid","not_null":true,"length":4},{"name":"ftserver","type":"oid","not_null":true,"length":4},{"name":"ftoptions","type":"text","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_group","columns":[{"name":"groname","type":"name","length":64},{"name":"grosysid","type":"oid","length":4},{"name":"grolist","type":"oid","array":true,"length":4}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_hba_file_rules","columns":[{"name":"rule_number","type":"int4","length":4},{"name":"file_name","type":"text"},{"name":"line_number","type":"int4","length":4},{"name":"type","type":"text"},{"name":"database","type":"text","array":true},{"name":"user_name","type":"text","array":true},{"name":"address","type":"text"},{"name":"netmask","type":"text"},{"name":"auth_method","type":"text"},{"name":"options","type":"text","array":true},{"name":"error","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_ident_file_mappings","columns":[{"name":"map_number","type":"int4","length":4},{"name":"file_name","type":"text"},{"name":"line_number","type":"int4","length":4},{"name":"map_name","type":"text"},{"name":"sys_name","type":"text"},{"name":"pg_username","type":"text"},{"name":"error","type":"text"}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_index","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"indexrelid","type":"oid","not_null":true,"length":4},{"name":"indrelid","type":"oid","not_null":true,"length":4},{"name":"indnatts","type":"int2","not_null":true,"length":2},{"name":"indnkeyatts","type":"int2","not_null":true,"length":2},{"name":"indisunique","type":"bool","not_null":true,"length":1},{"name":"indnullsnotdistinct","type":"bool","not_null":true,"length":1},{"name":"indisprimary","type":"bool","not_null":true,"length":1},{"name":"indisexclusion","type":"bool","not_null":true,"length":1},{"name":"indimmediate","type":"bool","not_null":true,"length":1},{"name":"indisclustered","type":"bool","not_null":true,"length":1},{"name":"indisvalid","type":"bool","not_null":true,"length":1},{"name":"indcheckxmin","type":"bool","not_null":true,"length":1},{"name":"indisready","type":"bool","not_null":true,"length":1},{"name":"indislive","type":"bool","not_null":true,"length":1},{"name":"indisreplident","type":"bool","not_null":true,"length":1},{"name":"indkey","type":"int2vector","not_null":true,"array":true},{"name":"indcollation","type":"oidvector","not_null":true,"array":true},{"name":"indclass","type":"oidvector","not_null":true,"array":true},{"name":"indoption","type":"int2vector","not_null":true,"array":true},{"name":"indexprs","type":"pg_node_tree"},{"name":"indpred","type":"pg_node_tree"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_index","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"indexrelid","type":"oid","not_null":true,"length":4},{"name":"indrelid","type":"oid","not_null":true,"length":4},{"name":"indnatts","type":"int2","not_null":true,"length":2},{"name":"indnkeyatts","type":"int2","not_null":true,"length":2},{"name":"indisunique","type":"bool","not_null":true,"length":1},{"name":"indnullsnotdistinct","type":"bool","not_null":true,"length":1},{"name":"indisprimary","type":"bool","not_null":true,"length":1},{"name":"indisexclusion","type":"bool","not_null":true,"length":1},{"name":"indimmediate","type":"bool","not_null":true,"length":1},{"name":"indisclustered","type":"bool","not_null":true,"length":1},{"name":"indisvalid","type":"bool","not_null":true,"length":1},{"name":"indcheckxmin","type":"bool","not_null":true,"length":1},{"name":"indisready","type":"bool","not_null":true,"length":1},{"name":"indislive","type":"bool","not_null":true,"length":1},{"name":"indisreplident","type":"bool","not_null":true,"length":1},{"name":"indkey","type":"int2","not_null":true,"array":true,"length":2},{"name":"indcollation","type":"oid","not_null":true,"array":true,"length":4},{"name":"indclass","type":"oid","not_null":true,"array":true,"length":4},{"name":"indoption","type":"int2","not_null":true,"array":true,"length":2},{"name":"indexprs","type":"pg_node_tree"},{"name":"indpred","type":"pg_node_tree"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_indexes","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"indexname","type":"name","length":64},{"name":"tablespace","type":"name","length":64},{"name":"indexdef","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_inherits","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"inhrelid","type":"oid","not_null":true,"length":4},{"name":"inhparent","type":"oid","not_null":true,"length":4},{"name":"inhseqno","type":"int4","not_null":true,"length":4},{"name":"inhdetachpending","type":"bool","not_null":true,"length":1}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_init_privs","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"objoid","type":"oid","not_null":true,"length":4},{"name":"classoid","type":"oid","not_null":true,"length":4},{"name":"objsubid","type":"int4","not_null":true,"length":4},{"name":"privtype","type":"char","not_null":true,"length":1},{"name":"initprivs","type":"_aclitem","not_null":true,"array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_language","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"lanname","type":"name","not_null":true,"length":64},{"name":"lanowner","type":"oid","not_null":true,"length":4},{"name":"lanispl","type":"bool","not_null":true,"length":1},{"name":"lanpltrusted","type":"bool","not_null":true,"length":1},{"name":"lanplcallfoid","type":"oid","not_null":true,"length":4},{"name":"laninline","type":"oid","not_null":true,"length":4},{"name":"lanvalidator","type":"oid","not_null":true,"length":4},{"name":"lanacl","type":"_aclitem","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_init_privs","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"objoid","type":"oid","not_null":true,"length":4},{"name":"classoid","type":"oid","not_null":true,"length":4},{"name":"objsubid","type":"int4","not_null":true,"length":4},{"name":"privtype","type":"char","not_null":true,"length":1},{"name":"initprivs","type":"aclitem","not_null":true,"array":true,"length":16}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_language","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"lanname","type":"name","not_null":true,"length":64},{"name":"lanowner","type":"oid","not_null":true,"length":4},{"name":"lanispl","type":"bool","not_null":true,"length":1},{"name":"lanpltrusted","type":"bool","not_null":true,"length":1},{"name":"lanplcallfoid","type":"oid","not_null":true,"length":4},{"name":"laninline","type":"oid","not_null":true,"length":4},{"name":"lanvalidator","type":"oid","not_null":true,"length":4},{"name":"lanacl","type":"aclitem","array":true,"length":16}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_largeobject","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"loid","type":"oid","not_null":true,"length":4},{"name":"pageno","type":"int4","not_null":true,"length":4},{"name":"data","type":"bytea","not_null":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_largeobject_metadata","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"lomowner","type":"oid","not_null":true,"length":4},{"name":"lomacl","type":"_aclitem","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_largeobject_metadata","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"lomowner","type":"oid","not_null":true,"length":4},{"name":"lomacl","type":"aclitem","array":true,"length":16}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_locks","columns":[{"name":"locktype","type":"text"},{"name":"database","type":"oid","length":4},{"name":"relation","type":"oid","length":4},{"name":"page","type":"int4","length":4},{"name":"tuple","type":"int2","length":2},{"name":"virtualxid","type":"text"},{"name":"transactionid","type":"xid","length":4},{"name":"classid","type":"oid","length":4},{"name":"objid","type":"oid","length":4},{"name":"objsubid","type":"int2","length":2},{"name":"virtualtransaction","type":"text"},{"name":"pid","type":"int4","length":4},{"name":"mode","type":"text"},{"name":"granted","type":"bool","length":1},{"name":"fastpath","type":"bool","length":1},{"name":"waitstart","type":"timestamptz","length":8}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_matviews","columns":[{"name":"schemaname","type":"name","length":64},{"name":"matviewname","type":"name","length":64},{"name":"matviewowner","type":"name","length":64},{"name":"tablespace","type":"name","length":64},{"name":"hasindexes","type":"bool","length":1},{"name":"ispopulated","type":"bool","length":1},{"name":"definition","type":"text"}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_namespace","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"nspname","type":"name","not_null":true,"length":64},{"name":"nspowner","type":"oid","not_null":true,"length":4},{"name":"nspacl","type":"_aclitem","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_namespace","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"nspname","type":"name","not_null":true,"length":64},{"name":"nspowner","type":"oid","not_null":true,"length":4},{"name":"nspacl","type":"aclitem","array":true,"length":16}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_opclass","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"opcmethod","type":"oid","not_null":true,"length":4},{"name":"opcname","type":"name","not_null":true,"length":64},{"name":"opcnamespace","type":"oid","not_null":true,"length":4},{"name":"opcowner","type":"oid","not_null":true,"length":4},{"name":"opcfamily","type":"oid","not_null":true,"length":4},{"name":"opcintype","type":"oid","not_null":true,"length":4},{"name":"opcdefault","type":"bool","not_null":true,"length":1},{"name":"opckeytype","type":"oid","not_null":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_operator","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"oprname","type":"name","not_null":true,"length":64},{"name":"oprnamespace","type":"oid","not_null":true,"length":4},{"name":"oprowner","type":"oid","not_null":true,"length":4},{"name":"oprkind","type":"char","not_null":true,"length":1},{"name":"oprcanmerge","type":"bool","not_null":true,"length":1},{"name":"oprcanhash","type":"bool","not_null":true,"length":1},{"name":"oprleft","type":"oid","not_null":true,"length":4},{"name":"oprright","type":"oid","not_null":true,"length":4},{"name":"oprresult","type":"oid","not_null":true,"length":4},{"name":"oprcom","type":"oid","not_null":true,"length":4},{"name":"oprnegate","type":"oid","not_null":true,"length":4},{"name":"oprcode","type":"regproc","not_null":true,"length":4},{"name":"oprrest","type":"regproc","not_null":true,"length":4},{"name":"oprjoin","type":"regproc","not_null":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_opfamily","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"opfmethod","type":"oid","not_null":true,"length":4},{"name":"opfname","type":"name","not_null":true,"length":64},{"name":"opfnamespace","type":"oid","not_null":true,"length":4},{"name":"opfowner","type":"oid","not_null":true,"length":4}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_parameter_acl","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"parname","type":"text","not_null":true},{"name":"paracl","type":"_aclitem","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_partitioned_table","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"partrelid","type":"oid","not_null":true,"length":4},{"name":"partstrat","type":"char","not_null":true,"length":1},{"name":"partnatts","type":"int2","not_null":true,"length":2},{"name":"partdefid","type":"oid","not_null":true,"length":4},{"name":"partattrs","type":"int2vector","not_null":true,"array":true},{"name":"partclass","type":"oidvector","not_null":true,"array":true},{"name":"partcollation","type":"oidvector","not_null":true,"array":true},{"name":"partexprs","type":"pg_node_tree"}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_policies","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"policyname","type":"name","length":64},{"name":"permissive","type":"text"},{"name":"roles","type":"_name","array":true},{"name":"cmd","type":"text"},{"name":"qual","type":"text"},{"name":"with_check","type":"text"}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_policy","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"polname","type":"name","not_null":true,"length":64},{"name":"polrelid","type":"oid","not_null":true,"length":4},{"name":"polcmd","type":"char","not_null":true,"length":1},{"name":"polpermissive","type":"bool","not_null":true,"length":1},{"name":"polroles","type":"_oid","not_null":true,"array":true},{"name":"polqual","type":"pg_node_tree"},{"name":"polwithcheck","type":"pg_node_tree"}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_prepared_statements","columns":[{"name":"name","type":"text"},{"name":"statement","type":"text"},{"name":"prepare_time","type":"timestamptz","length":8},{"name":"parameter_types","type":"_regtype","array":true},{"name":"result_types","type":"_regtype","array":true},{"name":"from_sql","type":"bool","length":1},{"name":"generic_plans","type":"int8","length":8},{"name":"custom_plans","type":"int8","length":8}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_parameter_acl","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"parname","type":"text","not_null":true},{"name":"paracl","type":"aclitem","array":true,"length":16}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_partitioned_table","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"partrelid","type":"oid","not_null":true,"length":4},{"name":"partstrat","type":"char","not_null":true,"length":1},{"name":"partnatts","type":"int2","not_null":true,"length":2},{"name":"partdefid","type":"oid","not_null":true,"length":4},{"name":"partattrs","type":"int2","not_null":true,"array":true,"length":2},{"name":"partclass","type":"oid","not_null":true,"array":true,"length":4},{"name":"partcollation","type":"oid","not_null":true,"array":true,"length":4},{"name":"partexprs","type":"pg_node_tree"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_policies","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"policyname","type":"name","length":64},{"name":"permissive","type":"text"},{"name":"roles","type":"name","array":true,"length":64},{"name":"cmd","type":"text"},{"name":"qual","type":"text"},{"name":"with_check","type":"text"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_policy","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"polname","type":"name","not_null":true,"length":64},{"name":"polrelid","type":"oid","not_null":true,"length":4},{"name":"polcmd","type":"char","not_null":true,"length":1},{"name":"polpermissive","type":"bool","not_null":true,"length":1},{"name":"polroles","type":"oid","not_null":true,"array":true,"length":4},{"name":"polqual","type":"pg_node_tree"},{"name":"polwithcheck","type":"pg_node_tree"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_prepared_statements","columns":[{"name":"name","type":"text"},{"name":"statement","type":"text"},{"name":"prepare_time","type":"timestamptz","length":8},{"name":"parameter_types","type":"regtype","array":true,"length":4},{"name":"result_types","type":"regtype","array":true,"length":4},{"name":"from_sql","type":"bool","length":1},{"name":"generic_plans","type":"int8","length":8},{"name":"custom_plans","type":"int8","length":8}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_prepared_xacts","columns":[{"name":"transaction","type":"xid","length":4},{"name":"gid","type":"text"},{"name":"prepared","type":"timestamptz","length":8},{"name":"owner","type":"name","length":64},{"name":"database","type":"name","length":64}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_proc","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"proname","type":"name","not_null":true,"length":64},{"name":"pronamespace","type":"oid","not_null":true,"length":4},{"name":"proowner","type":"oid","not_null":true,"length":4},{"name":"prolang","type":"oid","not_null":true,"length":4},{"name":"procost","type":"float4","not_null":true,"length":4},{"name":"prorows","type":"float4","not_null":true,"length":4},{"name":"provariadic","type":"oid","not_null":true,"length":4},{"name":"prosupport","type":"regproc","not_null":true,"length":4},{"name":"prokind","type":"char","not_null":true,"length":1},{"name":"prosecdef","type":"bool","not_null":true,"length":1},{"name":"proleakproof","type":"bool","not_null":true,"length":1},{"name":"proisstrict","type":"bool","not_null":true,"length":1},{"name":"proretset","type":"bool","not_null":true,"length":1},{"name":"provolatile","type":"char","not_null":true,"length":1},{"name":"proparallel","type":"char","not_null":true,"length":1},{"name":"pronargs","type":"int2","not_null":true,"length":2},{"name":"pronargdefaults","type":"int2","not_null":true,"length":2},{"name":"prorettype","type":"oid","not_null":true,"length":4},{"name":"proargtypes","type":"oidvector","not_null":true,"array":true},{"name":"proallargtypes","type":"_oid","array":true},{"name":"proargmodes","type":"_char","array":true},{"name":"proargnames","type":"_text","array":true},{"name":"proargdefaults","type":"pg_node_tree"},{"name":"protrftypes","type":"_oid","array":true},{"name":"prosrc","type":"text","not_null":true},{"name":"probin","type":"text"},{"name":"prosqlbody","type":"pg_node_tree"},{"name":"proconfig","type":"_text","array":true},{"name":"proacl","type":"_aclitem","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_proc","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"proname","type":"name","not_null":true,"length":64},{"name":"pronamespace","type":"oid","not_null":true,"length":4},{"name":"proowner","type":"oid","not_null":true,"length":4},{"name":"prolang","type":"oid","not_null":true,"length":4},{"name":"procost","type":"float4","not_null":true,"length":4},{"name":"prorows","type":"float4","not_null":true,"length":4},{"name":"provariadic","type":"oid","not_null":true,"length":4},{"name":"prosupport","type":"regproc","not_null":true,"length":4},{"name":"prokind","type":"char","not_null":true,"length":1},{"name":"prosecdef","type":"bool","not_null":true,"length":1},{"name":"proleakproof","type":"bool","not_null":true,"length":1},{"name":"proisstrict","type":"bool","not_null":true,"length":1},{"name":"proretset","type":"bool","not_null":true,"length":1},{"name":"provolatile","type":"char","not_null":true,"length":1},{"name":"proparallel","type":"char","not_null":true,"length":1},{"name":"pronargs","type":"int2","not_null":true,"length":2},{"name":"pronargdefaults","type":"int2","not_null":true,"length":2},{"name":"prorettype","type":"oid","not_null":true,"length":4},{"name":"proargtypes","type":"oid","not_null":true,"array":true,"length":4},{"name":"proallargtypes","type":"oid","array":true,"length":4},{"name":"proargmodes","type":"char","array":true,"length":1},{"name":"proargnames","type":"text","array":true},{"name":"proargdefaults","type":"pg_node_tree"},{"name":"protrftypes","type":"oid","array":true,"length":4},{"name":"prosrc","type":"text","not_null":true},{"name":"probin","type":"text"},{"name":"prosqlbody","type":"pg_node_tree"},{"name":"proconfig","type":"text","array":true},{"name":"proacl","type":"aclitem","array":true,"length":16}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_publication","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"pubname","type":"name","not_null":true,"length":64},{"name":"pubowner","type":"oid","not_null":true,"length":4},{"name":"puballtables","type":"bool","not_null":true,"length":1},{"name":"pubinsert","type":"bool","not_null":true,"length":1},{"name":"pubupdate","type":"bool","not_null":true,"length":1},{"name":"pubdelete","type":"bool","not_null":true,"length":1},{"name":"pubtruncate","type":"bool","not_null":true,"length":1},{"name":"pubviaroot","type":"bool","not_null":true,"length":1}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_publication_namespace","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"pnpubid","type":"oid","not_null":true,"length":4},{"name":"pnnspid","type":"oid","not_null":true,"length":4}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_publication_rel","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"prpubid","type":"oid","not_null":true,"length":4},{"name":"prrelid","type":"oid","not_null":true,"length":4},{"name":"prqual","type":"pg_node_tree"},{"name":"prattrs","type":"int2vector","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_publication_tables","columns":[{"name":"pubname","type":"name","length":64},{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"attnames","type":"_name","array":true},{"name":"rowfilter","type":"text"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_publication_rel","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"prpubid","type":"oid","not_null":true,"length":4},{"name":"prrelid","type":"oid","not_null":true,"length":4},{"name":"prqual","type":"pg_node_tree"},{"name":"prattrs","type":"int2","array":true,"length":2}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_publication_tables","columns":[{"name":"pubname","type":"name","length":64},{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"attnames","type":"name","array":true,"length":64},{"name":"rowfilter","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_range","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"rngtypid","type":"oid","not_null":true,"length":4},{"name":"rngsubtype","type":"oid","not_null":true,"length":4},{"name":"rngmultitypid","type":"oid","not_null":true,"length":4},{"name":"rngcollation","type":"oid","not_null":true,"length":4},{"name":"rngsubopc","type":"oid","not_null":true,"length":4},{"name":"rngcanonical","type":"regproc","not_null":true,"length":4},{"name":"rngsubdiff","type":"regproc","not_null":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_replication_origin","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"roident","type":"oid","not_null":true,"length":4},{"name":"roname","type":"text","not_null":true}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_replication_origin_status","columns":[{"name":"local_id","type":"oid","length":4},{"name":"external_id","type":"text"},{"name":"remote_lsn","type":"pg_lsn","length":8},{"name":"local_lsn","type":"pg_lsn","length":8}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_replication_slots","columns":[{"name":"slot_name","type":"name","length":64},{"name":"plugin","type":"name","length":64},{"name":"slot_type","type":"text"},{"name":"datoid","type":"oid","length":4},{"name":"database","type":"name","length":64},{"name":"temporary","type":"bool","length":1},{"name":"active","type":"bool","length":1},{"name":"active_pid","type":"int4","length":4},{"name":"xmin","type":"xid","length":4},{"name":"catalog_xmin","type":"xid","length":4},{"name":"restart_lsn","type":"pg_lsn","length":8},{"name":"confirmed_flush_lsn","type":"pg_lsn","length":8},{"name":"wal_status","type":"text"},{"name":"safe_wal_size","type":"int8","length":8},{"name":"two_phase","type":"bool","length":1},{"name":"conflicting","type":"bool","length":1}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_rewrite","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"rulename","type":"name","not_null":true,"length":64},{"name":"ev_class","type":"oid","not_null":true,"length":4},{"name":"ev_type","type":"char","not_null":true,"length":1},{"name":"ev_enabled","type":"char","not_null":true,"length":1},{"name":"is_instead","type":"bool","not_null":true,"length":1},{"name":"ev_qual","type":"pg_node_tree","not_null":true},{"name":"ev_action","type":"pg_node_tree","not_null":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_roles","columns":[{"name":"rolname","type":"name","length":64},{"name":"rolsuper","type":"bool","length":1},{"name":"rolinherit","type":"bool","length":1},{"name":"rolcreaterole","type":"bool","length":1},{"name":"rolcreatedb","type":"bool","length":1},{"name":"rolcanlogin","type":"bool","length":1},{"name":"rolreplication","type":"bool","length":1},{"name":"rolconnlimit","type":"int4","length":4},{"name":"rolpassword","type":"text"},{"name":"rolvaliduntil","type":"timestamptz","length":8},{"name":"rolbypassrls","type":"bool","length":1},{"name":"rolconfig","type":"_text","array":true},{"name":"oid","type":"oid","length":4}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_roles","columns":[{"name":"rolname","type":"name","length":64},{"name":"rolsuper","type":"bool","length":1},{"name":"rolinherit","type":"bool","length":1},{"name":"rolcreaterole","type":"bool","length":1},{"name":"rolcreatedb","type":"bool","length":1},{"name":"rolcanlogin","type":"bool","length":1},{"name":"rolreplication","type":"bool","length":1},{"name":"rolconnlimit","type":"int4","length":4},{"name":"rolpassword","type":"text"},{"name":"rolvaliduntil","type":"timestamptz","length":8},{"name":"rolbypassrls","type":"bool","length":1},{"name":"rolconfig","type":"text","array":true},{"name":"oid","type":"oid","length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_rules","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"rulename","type":"name","length":64},{"name":"definition","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_seclabel","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"objoid","type":"oid","not_null":true,"length":4},{"name":"classoid","type":"oid","not_null":true,"length":4},{"name":"objsubid","type":"int4","not_null":true,"length":4},{"name":"provider","type":"text","not_null":true},{"name":"label","type":"text","not_null":true}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_seclabels","columns":[{"name":"objoid","type":"oid","length":4},{"name":"classoid","type":"oid","length":4},{"name":"objsubid","type":"int4","length":4},{"name":"objtype","type":"text"},{"name":"objnamespace","type":"oid","length":4},{"name":"objname","type":"text"},{"name":"provider","type":"text"},{"name":"label","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_sequence","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"seqrelid","type":"oid","not_null":true,"length":4},{"name":"seqtypid","type":"oid","not_null":true,"length":4},{"name":"seqstart","type":"int8","not_null":true,"length":8},{"name":"seqincrement","type":"int8","not_null":true,"length":8},{"name":"seqmax","type":"int8","not_null":true,"length":8},{"name":"seqmin","type":"int8","not_null":true,"length":8},{"name":"seqcache","type":"int8","not_null":true,"length":8},{"name":"seqcycle","type":"bool","not_null":true,"length":1}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_sequences","columns":[{"name":"schemaname","type":"name","length":64},{"name":"sequencename","type":"name","length":64},{"name":"sequenceowner","type":"name","length":64},{"name":"data_type","type":"regtype","length":4},{"name":"start_value","type":"int8","length":8},{"name":"min_value","type":"int8","length":8},{"name":"max_value","type":"int8","length":8},{"name":"increment_by","type":"int8","length":8},{"name":"cycle","type":"bool","length":1},{"name":"cache_size","type":"int8","length":8},{"name":"last_value","type":"int8","length":8}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_settings","columns":[{"name":"name","type":"text"},{"name":"setting","type":"text"},{"name":"unit","type":"text"},{"name":"category","type":"text"},{"name":"short_desc","type":"text"},{"name":"extra_desc","type":"text"},{"name":"context","type":"text"},{"name":"vartype","type":"text"},{"name":"source","type":"text"},{"name":"min_val","type":"text"},{"name":"max_val","type":"text"},{"name":"enumvals","type":"_text","array":true},{"name":"boot_val","type":"text"},{"name":"reset_val","type":"text"},{"name":"sourcefile","type":"text"},{"name":"sourceline","type":"int4","length":4},{"name":"pending_restart","type":"bool","length":1}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_shadow","columns":[{"name":"usename","type":"name","length":64},{"name":"usesysid","type":"oid","length":4},{"name":"usecreatedb","type":"bool","length":1},{"name":"usesuper","type":"bool","length":1},{"name":"userepl","type":"bool","length":1},{"name":"usebypassrls","type":"bool","length":1},{"name":"passwd","type":"text"},{"name":"valuntil","type":"timestamptz","length":8},{"name":"useconfig","type":"_text","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_settings","columns":[{"name":"name","type":"text"},{"name":"setting","type":"text"},{"name":"unit","type":"text"},{"name":"category","type":"text"},{"name":"short_desc","type":"text"},{"name":"extra_desc","type":"text"},{"name":"context","type":"text"},{"name":"vartype","type":"text"},{"name":"source","type":"text"},{"name":"min_val","type":"text"},{"name":"max_val","type":"text"},{"name":"enumvals","type":"text","array":true},{"name":"boot_val","type":"text"},{"name":"reset_val","type":"text"},{"name":"sourcefile","type":"text"},{"name":"sourceline","type":"int4","length":4},{"name":"pending_restart","type":"bool","length":1}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_shadow","columns":[{"name":"usename","type":"name","length":64},{"name":"usesysid","type":"oid","length":4},{"name":"usecreatedb","type":"bool","length":1},{"name":"usesuper","type":"bool","length":1},{"name":"userepl","type":"bool","length":1},{"name":"usebypassrls","type":"bool","length":1},{"name":"passwd","type":"text"},{"name":"valuntil","type":"timestamptz","length":8},{"name":"useconfig","type":"text","array":true}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_shdepend","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"dbid","type":"oid","not_null":true,"length":4},{"name":"classid","type":"oid","not_null":true,"length":4},{"name":"objid","type":"oid","not_null":true,"length":4},{"name":"objsubid","type":"int4","not_null":true,"length":4},{"name":"refclassid","type":"oid","not_null":true,"length":4},{"name":"refobjid","type":"oid","not_null":true,"length":4},{"name":"deptype","type":"char","not_null":true,"length":1}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_shdescription","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"objoid","type":"oid","not_null":true,"length":4},{"name":"classoid","type":"oid","not_null":true,"length":4},{"name":"description","type":"text","not_null":true}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_shmem_allocations","columns":[{"name":"name","type":"text"},{"name":"off","type":"int8","length":8},{"name":"size","type":"int8","length":8},{"name":"allocated_size","type":"int8","length":8}]} @@ -114,35 +114,35 @@ {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statio_user_indexes","columns":[{"name":"relid","type":"oid","length":4},{"name":"indexrelid","type":"oid","length":4},{"name":"schemaname","type":"name","length":64},{"name":"relname","type":"name","length":64},{"name":"indexrelname","type":"name","length":64},{"name":"idx_blks_read","type":"int8","length":8},{"name":"idx_blks_hit","type":"int8","length":8}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statio_user_sequences","columns":[{"name":"relid","type":"oid","length":4},{"name":"schemaname","type":"name","length":64},{"name":"relname","type":"name","length":64},{"name":"blks_read","type":"int8","length":8},{"name":"blks_hit","type":"int8","length":8}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statio_user_tables","columns":[{"name":"relid","type":"oid","length":4},{"name":"schemaname","type":"name","length":64},{"name":"relname","type":"name","length":64},{"name":"heap_blks_read","type":"int8","length":8},{"name":"heap_blks_hit","type":"int8","length":8},{"name":"idx_blks_read","type":"int8","length":8},{"name":"idx_blks_hit","type":"int8","length":8},{"name":"toast_blks_read","type":"int8","length":8},{"name":"toast_blks_hit","type":"int8","length":8},{"name":"tidx_blks_read","type":"int8","length":8},{"name":"tidx_blks_hit","type":"int8","length":8}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statistic","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"starelid","type":"oid","not_null":true,"length":4},{"name":"staattnum","type":"int2","not_null":true,"length":2},{"name":"stainherit","type":"bool","not_null":true,"length":1},{"name":"stanullfrac","type":"float4","not_null":true,"length":4},{"name":"stawidth","type":"int4","not_null":true,"length":4},{"name":"stadistinct","type":"float4","not_null":true,"length":4},{"name":"stakind1","type":"int2","not_null":true,"length":2},{"name":"stakind2","type":"int2","not_null":true,"length":2},{"name":"stakind3","type":"int2","not_null":true,"length":2},{"name":"stakind4","type":"int2","not_null":true,"length":2},{"name":"stakind5","type":"int2","not_null":true,"length":2},{"name":"staop1","type":"oid","not_null":true,"length":4},{"name":"staop2","type":"oid","not_null":true,"length":4},{"name":"staop3","type":"oid","not_null":true,"length":4},{"name":"staop4","type":"oid","not_null":true,"length":4},{"name":"staop5","type":"oid","not_null":true,"length":4},{"name":"stacoll1","type":"oid","not_null":true,"length":4},{"name":"stacoll2","type":"oid","not_null":true,"length":4},{"name":"stacoll3","type":"oid","not_null":true,"length":4},{"name":"stacoll4","type":"oid","not_null":true,"length":4},{"name":"stacoll5","type":"oid","not_null":true,"length":4},{"name":"stanumbers1","type":"_float4","array":true},{"name":"stanumbers2","type":"_float4","array":true},{"name":"stanumbers3","type":"_float4","array":true},{"name":"stanumbers4","type":"_float4","array":true},{"name":"stanumbers5","type":"_float4","array":true},{"name":"stavalues1","type":"anyarray"},{"name":"stavalues2","type":"anyarray"},{"name":"stavalues3","type":"anyarray"},{"name":"stavalues4","type":"anyarray"},{"name":"stavalues5","type":"anyarray"}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statistic_ext","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"stxrelid","type":"oid","not_null":true,"length":4},{"name":"stxname","type":"name","not_null":true,"length":64},{"name":"stxnamespace","type":"oid","not_null":true,"length":4},{"name":"stxowner","type":"oid","not_null":true,"length":4},{"name":"stxstattarget","type":"int4","not_null":true,"length":4},{"name":"stxkeys","type":"int2vector","not_null":true,"array":true},{"name":"stxkind","type":"_char","not_null":true,"array":true},{"name":"stxexprs","type":"pg_node_tree"}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statistic_ext_data","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"stxoid","type":"oid","not_null":true,"length":4},{"name":"stxdinherit","type":"bool","not_null":true,"length":1},{"name":"stxdndistinct","type":"pg_ndistinct"},{"name":"stxddependencies","type":"pg_dependencies"},{"name":"stxdmcv","type":"pg_mcv_list"},{"name":"stxdexpr","type":"_pg_statistic","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_stats","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"attname","type":"name","length":64},{"name":"inherited","type":"bool","length":1},{"name":"null_frac","type":"float4","length":4},{"name":"avg_width","type":"int4","length":4},{"name":"n_distinct","type":"float4","length":4},{"name":"most_common_vals","type":"anyarray"},{"name":"most_common_freqs","type":"_float4","array":true},{"name":"histogram_bounds","type":"anyarray"},{"name":"correlation","type":"float4","length":4},{"name":"most_common_elems","type":"anyarray"},{"name":"most_common_elem_freqs","type":"_float4","array":true},{"name":"elem_count_histogram","type":"_float4","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_stats_ext","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"statistics_schemaname","type":"name","length":64},{"name":"statistics_name","type":"name","length":64},{"name":"statistics_owner","type":"name","length":64},{"name":"attnames","type":"_name","array":true},{"name":"exprs","type":"_text","array":true},{"name":"kinds","type":"_char","array":true},{"name":"inherited","type":"bool","length":1},{"name":"n_distinct","type":"pg_ndistinct"},{"name":"dependencies","type":"pg_dependencies"},{"name":"most_common_vals","type":"_text","array":true},{"name":"most_common_val_nulls","type":"_bool","array":true},{"name":"most_common_freqs","type":"_float8","array":true},{"name":"most_common_base_freqs","type":"_float8","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_stats_ext_exprs","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"statistics_schemaname","type":"name","length":64},{"name":"statistics_name","type":"name","length":64},{"name":"statistics_owner","type":"name","length":64},{"name":"expr","type":"text"},{"name":"inherited","type":"bool","length":1},{"name":"null_frac","type":"float4","length":4},{"name":"avg_width","type":"int4","length":4},{"name":"n_distinct","type":"float4","length":4},{"name":"most_common_vals","type":"anyarray"},{"name":"most_common_freqs","type":"_float4","array":true},{"name":"histogram_bounds","type":"anyarray"},{"name":"correlation","type":"float4","length":4},{"name":"most_common_elems","type":"anyarray"},{"name":"most_common_elem_freqs","type":"_float4","array":true},{"name":"elem_count_histogram","type":"_float4","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_subscription","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"subdbid","type":"oid","not_null":true,"length":4},{"name":"subskiplsn","type":"pg_lsn","not_null":true,"length":8},{"name":"subname","type":"name","not_null":true,"length":64},{"name":"subowner","type":"oid","not_null":true,"length":4},{"name":"subenabled","type":"bool","not_null":true,"length":1},{"name":"subbinary","type":"bool","not_null":true,"length":1},{"name":"substream","type":"char","not_null":true,"length":1},{"name":"subtwophasestate","type":"char","not_null":true,"length":1},{"name":"subdisableonerr","type":"bool","not_null":true,"length":1},{"name":"subpasswordrequired","type":"bool","not_null":true,"length":1},{"name":"subrunasowner","type":"bool","not_null":true,"length":1},{"name":"subconninfo","type":"text","not_null":true},{"name":"subslotname","type":"name","length":64},{"name":"subsynccommit","type":"text","not_null":true},{"name":"subpublications","type":"_text","not_null":true,"array":true},{"name":"suborigin","type":"text"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statistic","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"starelid","type":"oid","not_null":true,"length":4},{"name":"staattnum","type":"int2","not_null":true,"length":2},{"name":"stainherit","type":"bool","not_null":true,"length":1},{"name":"stanullfrac","type":"float4","not_null":true,"length":4},{"name":"stawidth","type":"int4","not_null":true,"length":4},{"name":"stadistinct","type":"float4","not_null":true,"length":4},{"name":"stakind1","type":"int2","not_null":true,"length":2},{"name":"stakind2","type":"int2","not_null":true,"length":2},{"name":"stakind3","type":"int2","not_null":true,"length":2},{"name":"stakind4","type":"int2","not_null":true,"length":2},{"name":"stakind5","type":"int2","not_null":true,"length":2},{"name":"staop1","type":"oid","not_null":true,"length":4},{"name":"staop2","type":"oid","not_null":true,"length":4},{"name":"staop3","type":"oid","not_null":true,"length":4},{"name":"staop4","type":"oid","not_null":true,"length":4},{"name":"staop5","type":"oid","not_null":true,"length":4},{"name":"stacoll1","type":"oid","not_null":true,"length":4},{"name":"stacoll2","type":"oid","not_null":true,"length":4},{"name":"stacoll3","type":"oid","not_null":true,"length":4},{"name":"stacoll4","type":"oid","not_null":true,"length":4},{"name":"stacoll5","type":"oid","not_null":true,"length":4},{"name":"stanumbers1","type":"float4","array":true,"length":4},{"name":"stanumbers2","type":"float4","array":true,"length":4},{"name":"stanumbers3","type":"float4","array":true,"length":4},{"name":"stanumbers4","type":"float4","array":true,"length":4},{"name":"stanumbers5","type":"float4","array":true,"length":4},{"name":"stavalues1","type":"anyarray"},{"name":"stavalues2","type":"anyarray"},{"name":"stavalues3","type":"anyarray"},{"name":"stavalues4","type":"anyarray"},{"name":"stavalues5","type":"anyarray"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statistic_ext","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"stxrelid","type":"oid","not_null":true,"length":4},{"name":"stxname","type":"name","not_null":true,"length":64},{"name":"stxnamespace","type":"oid","not_null":true,"length":4},{"name":"stxowner","type":"oid","not_null":true,"length":4},{"name":"stxstattarget","type":"int4","not_null":true,"length":4},{"name":"stxkeys","type":"int2","not_null":true,"array":true,"length":2},{"name":"stxkind","type":"char","not_null":true,"array":true,"length":1},{"name":"stxexprs","type":"pg_node_tree"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statistic_ext_data","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"stxoid","type":"oid","not_null":true,"length":4},{"name":"stxdinherit","type":"bool","not_null":true,"length":1},{"name":"stxdndistinct","type":"pg_ndistinct"},{"name":"stxddependencies","type":"pg_dependencies"},{"name":"stxdmcv","type":"pg_mcv_list"},{"name":"stxdexpr","type":"pg_statistic","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_stats","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"attname","type":"name","length":64},{"name":"inherited","type":"bool","length":1},{"name":"null_frac","type":"float4","length":4},{"name":"avg_width","type":"int4","length":4},{"name":"n_distinct","type":"float4","length":4},{"name":"most_common_vals","type":"anyarray"},{"name":"most_common_freqs","type":"float4","array":true,"length":4},{"name":"histogram_bounds","type":"anyarray"},{"name":"correlation","type":"float4","length":4},{"name":"most_common_elems","type":"anyarray"},{"name":"most_common_elem_freqs","type":"float4","array":true,"length":4},{"name":"elem_count_histogram","type":"float4","array":true,"length":4}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_stats_ext","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"statistics_schemaname","type":"name","length":64},{"name":"statistics_name","type":"name","length":64},{"name":"statistics_owner","type":"name","length":64},{"name":"attnames","type":"name","array":true,"length":64},{"name":"exprs","type":"text","array":true},{"name":"kinds","type":"char","array":true,"length":1},{"name":"inherited","type":"bool","length":1},{"name":"n_distinct","type":"pg_ndistinct"},{"name":"dependencies","type":"pg_dependencies"},{"name":"most_common_vals","type":"text","array":true},{"name":"most_common_val_nulls","type":"bool","array":true,"length":1},{"name":"most_common_freqs","type":"float8","array":true,"length":8},{"name":"most_common_base_freqs","type":"float8","array":true,"length":8}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_stats_ext_exprs","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"statistics_schemaname","type":"name","length":64},{"name":"statistics_name","type":"name","length":64},{"name":"statistics_owner","type":"name","length":64},{"name":"expr","type":"text"},{"name":"inherited","type":"bool","length":1},{"name":"null_frac","type":"float4","length":4},{"name":"avg_width","type":"int4","length":4},{"name":"n_distinct","type":"float4","length":4},{"name":"most_common_vals","type":"anyarray"},{"name":"most_common_freqs","type":"float4","array":true,"length":4},{"name":"histogram_bounds","type":"anyarray"},{"name":"correlation","type":"float4","length":4},{"name":"most_common_elems","type":"anyarray"},{"name":"most_common_elem_freqs","type":"float4","array":true,"length":4},{"name":"elem_count_histogram","type":"float4","array":true,"length":4}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_subscription","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"subdbid","type":"oid","not_null":true,"length":4},{"name":"subskiplsn","type":"pg_lsn","not_null":true,"length":8},{"name":"subname","type":"name","not_null":true,"length":64},{"name":"subowner","type":"oid","not_null":true,"length":4},{"name":"subenabled","type":"bool","not_null":true,"length":1},{"name":"subbinary","type":"bool","not_null":true,"length":1},{"name":"substream","type":"char","not_null":true,"length":1},{"name":"subtwophasestate","type":"char","not_null":true,"length":1},{"name":"subdisableonerr","type":"bool","not_null":true,"length":1},{"name":"subpasswordrequired","type":"bool","not_null":true,"length":1},{"name":"subrunasowner","type":"bool","not_null":true,"length":1},{"name":"subconninfo","type":"text","not_null":true},{"name":"subslotname","type":"name","length":64},{"name":"subsynccommit","type":"text","not_null":true},{"name":"subpublications","type":"text","not_null":true,"array":true},{"name":"suborigin","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_subscription_rel","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"srsubid","type":"oid","not_null":true,"length":4},{"name":"srrelid","type":"oid","not_null":true,"length":4},{"name":"srsubstate","type":"char","not_null":true,"length":1},{"name":"srsublsn","type":"pg_lsn","length":8}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_tables","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"tableowner","type":"name","length":64},{"name":"tablespace","type":"name","length":64},{"name":"hasindexes","type":"bool","length":1},{"name":"hasrules","type":"bool","length":1},{"name":"hastriggers","type":"bool","length":1},{"name":"rowsecurity","type":"bool","length":1}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_tablespace","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"spcname","type":"name","not_null":true,"length":64},{"name":"spcowner","type":"oid","not_null":true,"length":4},{"name":"spcacl","type":"_aclitem","array":true},{"name":"spcoptions","type":"_text","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_tablespace","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"spcname","type":"name","not_null":true,"length":64},{"name":"spcowner","type":"oid","not_null":true,"length":4},{"name":"spcacl","type":"aclitem","array":true,"length":16},{"name":"spcoptions","type":"text","array":true}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_timezone_abbrevs","columns":[{"name":"abbrev","type":"text"},{"name":"utc_offset","type":"interval","length":16},{"name":"is_dst","type":"bool","length":1}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_timezone_names","columns":[{"name":"name","type":"text"},{"name":"abbrev","type":"text"},{"name":"utc_offset","type":"interval","length":16},{"name":"is_dst","type":"bool","length":1}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_transform","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"trftype","type":"oid","not_null":true,"length":4},{"name":"trflang","type":"oid","not_null":true,"length":4},{"name":"trffromsql","type":"regproc","not_null":true,"length":4},{"name":"trftosql","type":"regproc","not_null":true,"length":4}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_trigger","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"tgrelid","type":"oid","not_null":true,"length":4},{"name":"tgparentid","type":"oid","not_null":true,"length":4},{"name":"tgname","type":"name","not_null":true,"length":64},{"name":"tgfoid","type":"oid","not_null":true,"length":4},{"name":"tgtype","type":"int2","not_null":true,"length":2},{"name":"tgenabled","type":"char","not_null":true,"length":1},{"name":"tgisinternal","type":"bool","not_null":true,"length":1},{"name":"tgconstrrelid","type":"oid","not_null":true,"length":4},{"name":"tgconstrindid","type":"oid","not_null":true,"length":4},{"name":"tgconstraint","type":"oid","not_null":true,"length":4},{"name":"tgdeferrable","type":"bool","not_null":true,"length":1},{"name":"tginitdeferred","type":"bool","not_null":true,"length":1},{"name":"tgnargs","type":"int2","not_null":true,"length":2},{"name":"tgattr","type":"int2vector","not_null":true,"array":true},{"name":"tgargs","type":"bytea","not_null":true},{"name":"tgqual","type":"pg_node_tree"},{"name":"tgoldtable","type":"name","length":64},{"name":"tgnewtable","type":"name","length":64}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_trigger","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"tgrelid","type":"oid","not_null":true,"length":4},{"name":"tgparentid","type":"oid","not_null":true,"length":4},{"name":"tgname","type":"name","not_null":true,"length":64},{"name":"tgfoid","type":"oid","not_null":true,"length":4},{"name":"tgtype","type":"int2","not_null":true,"length":2},{"name":"tgenabled","type":"char","not_null":true,"length":1},{"name":"tgisinternal","type":"bool","not_null":true,"length":1},{"name":"tgconstrrelid","type":"oid","not_null":true,"length":4},{"name":"tgconstrindid","type":"oid","not_null":true,"length":4},{"name":"tgconstraint","type":"oid","not_null":true,"length":4},{"name":"tgdeferrable","type":"bool","not_null":true,"length":1},{"name":"tginitdeferred","type":"bool","not_null":true,"length":1},{"name":"tgnargs","type":"int2","not_null":true,"length":2},{"name":"tgattr","type":"int2","not_null":true,"array":true,"length":2},{"name":"tgargs","type":"bytea","not_null":true},{"name":"tgqual","type":"pg_node_tree"},{"name":"tgoldtable","type":"name","length":64},{"name":"tgnewtable","type":"name","length":64}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_ts_config","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"cfgname","type":"name","not_null":true,"length":64},{"name":"cfgnamespace","type":"oid","not_null":true,"length":4},{"name":"cfgowner","type":"oid","not_null":true,"length":4},{"name":"cfgparser","type":"oid","not_null":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_ts_config_map","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"mapcfg","type":"oid","not_null":true,"length":4},{"name":"maptokentype","type":"int4","not_null":true,"length":4},{"name":"mapseqno","type":"int4","not_null":true,"length":4},{"name":"mapdict","type":"oid","not_null":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_ts_dict","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"dictname","type":"name","not_null":true,"length":64},{"name":"dictnamespace","type":"oid","not_null":true,"length":4},{"name":"dictowner","type":"oid","not_null":true,"length":4},{"name":"dicttemplate","type":"oid","not_null":true,"length":4},{"name":"dictinitoption","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_ts_parser","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"prsname","type":"name","not_null":true,"length":64},{"name":"prsnamespace","type":"oid","not_null":true,"length":4},{"name":"prsstart","type":"regproc","not_null":true,"length":4},{"name":"prstoken","type":"regproc","not_null":true,"length":4},{"name":"prsend","type":"regproc","not_null":true,"length":4},{"name":"prsheadline","type":"regproc","not_null":true,"length":4},{"name":"prslextype","type":"regproc","not_null":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_ts_template","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"tmplname","type":"name","not_null":true,"length":64},{"name":"tmplnamespace","type":"oid","not_null":true,"length":4},{"name":"tmplinit","type":"regproc","not_null":true,"length":4},{"name":"tmpllexize","type":"regproc","not_null":true,"length":4}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_type","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"typname","type":"name","not_null":true,"length":64},{"name":"typnamespace","type":"oid","not_null":true,"length":4},{"name":"typowner","type":"oid","not_null":true,"length":4},{"name":"typlen","type":"int2","not_null":true,"length":2},{"name":"typbyval","type":"bool","not_null":true,"length":1},{"name":"typtype","type":"char","not_null":true,"length":1},{"name":"typcategory","type":"char","not_null":true,"length":1},{"name":"typispreferred","type":"bool","not_null":true,"length":1},{"name":"typisdefined","type":"bool","not_null":true,"length":1},{"name":"typdelim","type":"char","not_null":true,"length":1},{"name":"typrelid","type":"oid","not_null":true,"length":4},{"name":"typsubscript","type":"regproc","not_null":true,"length":4},{"name":"typelem","type":"oid","not_null":true,"length":4},{"name":"typarray","type":"oid","not_null":true,"length":4},{"name":"typinput","type":"regproc","not_null":true,"length":4},{"name":"typoutput","type":"regproc","not_null":true,"length":4},{"name":"typreceive","type":"regproc","not_null":true,"length":4},{"name":"typsend","type":"regproc","not_null":true,"length":4},{"name":"typmodin","type":"regproc","not_null":true,"length":4},{"name":"typmodout","type":"regproc","not_null":true,"length":4},{"name":"typanalyze","type":"regproc","not_null":true,"length":4},{"name":"typalign","type":"char","not_null":true,"length":1},{"name":"typstorage","type":"char","not_null":true,"length":1},{"name":"typnotnull","type":"bool","not_null":true,"length":1},{"name":"typbasetype","type":"oid","not_null":true,"length":4},{"name":"typtypmod","type":"int4","not_null":true,"length":4},{"name":"typndims","type":"int4","not_null":true,"length":4},{"name":"typcollation","type":"oid","not_null":true,"length":4},{"name":"typdefaultbin","type":"pg_node_tree"},{"name":"typdefault","type":"text"},{"name":"typacl","type":"_aclitem","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_user","columns":[{"name":"usename","type":"name","length":64},{"name":"usesysid","type":"oid","length":4},{"name":"usecreatedb","type":"bool","length":1},{"name":"usesuper","type":"bool","length":1},{"name":"userepl","type":"bool","length":1},{"name":"usebypassrls","type":"bool","length":1},{"name":"passwd","type":"text"},{"name":"valuntil","type":"timestamptz","length":8},{"name":"useconfig","type":"_text","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_user_mapping","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"umuser","type":"oid","not_null":true,"length":4},{"name":"umserver","type":"oid","not_null":true,"length":4},{"name":"umoptions","type":"_text","array":true}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_user_mappings","columns":[{"name":"umid","type":"oid","length":4},{"name":"srvid","type":"oid","length":4},{"name":"srvname","type":"name","length":64},{"name":"umuser","type":"oid","length":4},{"name":"usename","type":"name","length":64},{"name":"umoptions","type":"_text","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_type","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"typname","type":"name","not_null":true,"length":64},{"name":"typnamespace","type":"oid","not_null":true,"length":4},{"name":"typowner","type":"oid","not_null":true,"length":4},{"name":"typlen","type":"int2","not_null":true,"length":2},{"name":"typbyval","type":"bool","not_null":true,"length":1},{"name":"typtype","type":"char","not_null":true,"length":1},{"name":"typcategory","type":"char","not_null":true,"length":1},{"name":"typispreferred","type":"bool","not_null":true,"length":1},{"name":"typisdefined","type":"bool","not_null":true,"length":1},{"name":"typdelim","type":"char","not_null":true,"length":1},{"name":"typrelid","type":"oid","not_null":true,"length":4},{"name":"typsubscript","type":"regproc","not_null":true,"length":4},{"name":"typelem","type":"oid","not_null":true,"length":4},{"name":"typarray","type":"oid","not_null":true,"length":4},{"name":"typinput","type":"regproc","not_null":true,"length":4},{"name":"typoutput","type":"regproc","not_null":true,"length":4},{"name":"typreceive","type":"regproc","not_null":true,"length":4},{"name":"typsend","type":"regproc","not_null":true,"length":4},{"name":"typmodin","type":"regproc","not_null":true,"length":4},{"name":"typmodout","type":"regproc","not_null":true,"length":4},{"name":"typanalyze","type":"regproc","not_null":true,"length":4},{"name":"typalign","type":"char","not_null":true,"length":1},{"name":"typstorage","type":"char","not_null":true,"length":1},{"name":"typnotnull","type":"bool","not_null":true,"length":1},{"name":"typbasetype","type":"oid","not_null":true,"length":4},{"name":"typtypmod","type":"int4","not_null":true,"length":4},{"name":"typndims","type":"int4","not_null":true,"length":4},{"name":"typcollation","type":"oid","not_null":true,"length":4},{"name":"typdefaultbin","type":"pg_node_tree"},{"name":"typdefault","type":"text"},{"name":"typacl","type":"aclitem","array":true,"length":16}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_user","columns":[{"name":"usename","type":"name","length":64},{"name":"usesysid","type":"oid","length":4},{"name":"usecreatedb","type":"bool","length":1},{"name":"usesuper","type":"bool","length":1},{"name":"userepl","type":"bool","length":1},{"name":"usebypassrls","type":"bool","length":1},{"name":"passwd","type":"text"},{"name":"valuntil","type":"timestamptz","length":8},{"name":"useconfig","type":"text","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_user_mapping","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"umuser","type":"oid","not_null":true,"length":4},{"name":"umserver","type":"oid","not_null":true,"length":4},{"name":"umoptions","type":"text","array":true}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_user_mappings","columns":[{"name":"umid","type":"oid","length":4},{"name":"srvid","type":"oid","length":4},{"name":"srvname","type":"name","length":64},{"name":"umuser","type":"oid","length":4},{"name":"usename","type":"name","length":64},{"name":"umoptions","type":"text","array":true}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_views","columns":[{"name":"schemaname","type":"name","length":64},{"name":"viewname","type":"name","length":64},{"name":"viewowner","type":"name","length":64},{"name":"definition","type":"text"}]} -{"catalog":"pg_catalog","schema":"information_schema","name":"_pg_foreign_data_wrappers","columns":[{"name":"oid","type":"oid","length":4},{"name":"fdwowner","type":"oid","length":4},{"name":"fdwoptions","type":"_text","array":true},{"name":"foreign_data_wrapper_catalog","type":"sql_identifier","length":64},{"name":"foreign_data_wrapper_name","type":"sql_identifier","length":64},{"name":"authorization_identifier","type":"sql_identifier","length":64},{"name":"foreign_data_wrapper_language","type":"character_data"}]} -{"catalog":"pg_catalog","schema":"information_schema","name":"_pg_foreign_servers","columns":[{"name":"oid","type":"oid","length":4},{"name":"srvoptions","type":"_text","array":true},{"name":"foreign_server_catalog","type":"sql_identifier","length":64},{"name":"foreign_server_name","type":"sql_identifier","length":64},{"name":"foreign_data_wrapper_catalog","type":"sql_identifier","length":64},{"name":"foreign_data_wrapper_name","type":"sql_identifier","length":64},{"name":"foreign_server_type","type":"character_data"},{"name":"foreign_server_version","type":"character_data"},{"name":"authorization_identifier","type":"sql_identifier","length":64}]} -{"catalog":"pg_catalog","schema":"information_schema","name":"_pg_foreign_table_columns","columns":[{"name":"nspname","type":"name","length":64},{"name":"relname","type":"name","length":64},{"name":"attname","type":"name","length":64},{"name":"attfdwoptions","type":"_text","array":true}]} -{"catalog":"pg_catalog","schema":"information_schema","name":"_pg_foreign_tables","columns":[{"name":"foreign_table_catalog","type":"sql_identifier","length":64},{"name":"foreign_table_schema","type":"sql_identifier","length":64},{"name":"foreign_table_name","type":"sql_identifier","length":64},{"name":"ftoptions","type":"_text","array":true},{"name":"foreign_server_catalog","type":"sql_identifier","length":64},{"name":"foreign_server_name","type":"sql_identifier","length":64},{"name":"authorization_identifier","type":"sql_identifier","length":64}]} -{"catalog":"pg_catalog","schema":"information_schema","name":"_pg_user_mappings","columns":[{"name":"oid","type":"oid","length":4},{"name":"umoptions","type":"_text","array":true},{"name":"umuser","type":"oid","length":4},{"name":"authorization_identifier","type":"sql_identifier","length":64},{"name":"foreign_server_catalog","type":"sql_identifier","length":64},{"name":"foreign_server_name","type":"sql_identifier","length":64},{"name":"srvowner","type":"sql_identifier","length":64}]} +{"catalog":"pg_catalog","schema":"information_schema","name":"_pg_foreign_data_wrappers","columns":[{"name":"oid","type":"oid","length":4},{"name":"fdwowner","type":"oid","length":4},{"name":"fdwoptions","type":"text","array":true},{"name":"foreign_data_wrapper_catalog","type":"sql_identifier","length":64},{"name":"foreign_data_wrapper_name","type":"sql_identifier","length":64},{"name":"authorization_identifier","type":"sql_identifier","length":64},{"name":"foreign_data_wrapper_language","type":"character_data"}]} +{"catalog":"pg_catalog","schema":"information_schema","name":"_pg_foreign_servers","columns":[{"name":"oid","type":"oid","length":4},{"name":"srvoptions","type":"text","array":true},{"name":"foreign_server_catalog","type":"sql_identifier","length":64},{"name":"foreign_server_name","type":"sql_identifier","length":64},{"name":"foreign_data_wrapper_catalog","type":"sql_identifier","length":64},{"name":"foreign_data_wrapper_name","type":"sql_identifier","length":64},{"name":"foreign_server_type","type":"character_data"},{"name":"foreign_server_version","type":"character_data"},{"name":"authorization_identifier","type":"sql_identifier","length":64}]} +{"catalog":"pg_catalog","schema":"information_schema","name":"_pg_foreign_table_columns","columns":[{"name":"nspname","type":"name","length":64},{"name":"relname","type":"name","length":64},{"name":"attname","type":"name","length":64},{"name":"attfdwoptions","type":"text","array":true}]} +{"catalog":"pg_catalog","schema":"information_schema","name":"_pg_foreign_tables","columns":[{"name":"foreign_table_catalog","type":"sql_identifier","length":64},{"name":"foreign_table_schema","type":"sql_identifier","length":64},{"name":"foreign_table_name","type":"sql_identifier","length":64},{"name":"ftoptions","type":"text","array":true},{"name":"foreign_server_catalog","type":"sql_identifier","length":64},{"name":"foreign_server_name","type":"sql_identifier","length":64},{"name":"authorization_identifier","type":"sql_identifier","length":64}]} +{"catalog":"pg_catalog","schema":"information_schema","name":"_pg_user_mappings","columns":[{"name":"oid","type":"oid","length":4},{"name":"umoptions","type":"text","array":true},{"name":"umuser","type":"oid","length":4},{"name":"authorization_identifier","type":"sql_identifier","length":64},{"name":"foreign_server_catalog","type":"sql_identifier","length":64},{"name":"foreign_server_name","type":"sql_identifier","length":64},{"name":"srvowner","type":"sql_identifier","length":64}]} {"catalog":"pg_catalog","schema":"information_schema","name":"administrable_role_authorizations","columns":[{"name":"grantee","type":"sql_identifier","length":64},{"name":"role_name","type":"sql_identifier","length":64},{"name":"is_grantable","type":"yes_or_no"}]} {"catalog":"pg_catalog","schema":"information_schema","name":"applicable_roles","columns":[{"name":"grantee","type":"sql_identifier","length":64},{"name":"role_name","type":"sql_identifier","length":64},{"name":"is_grantable","type":"yes_or_no"}]} {"catalog":"pg_catalog","schema":"information_schema","name":"attributes","columns":[{"name":"udt_catalog","type":"sql_identifier","length":64},{"name":"udt_schema","type":"sql_identifier","length":64},{"name":"udt_name","type":"sql_identifier","length":64},{"name":"attribute_name","type":"sql_identifier","length":64},{"name":"ordinal_position","type":"cardinal_number","length":4},{"name":"attribute_default","type":"character_data"},{"name":"is_nullable","type":"yes_or_no"},{"name":"data_type","type":"character_data"},{"name":"character_maximum_length","type":"cardinal_number","length":4},{"name":"character_octet_length","type":"cardinal_number","length":4},{"name":"character_set_catalog","type":"sql_identifier","length":64},{"name":"character_set_schema","type":"sql_identifier","length":64},{"name":"character_set_name","type":"sql_identifier","length":64},{"name":"collation_catalog","type":"sql_identifier","length":64},{"name":"collation_schema","type":"sql_identifier","length":64},{"name":"collation_name","type":"sql_identifier","length":64},{"name":"numeric_precision","type":"cardinal_number","length":4},{"name":"numeric_precision_radix","type":"cardinal_number","length":4},{"name":"numeric_scale","type":"cardinal_number","length":4},{"name":"datetime_precision","type":"cardinal_number","length":4},{"name":"interval_type","type":"character_data"},{"name":"interval_precision","type":"cardinal_number","length":4},{"name":"attribute_udt_catalog","type":"sql_identifier","length":64},{"name":"attribute_udt_schema","type":"sql_identifier","length":64},{"name":"attribute_udt_name","type":"sql_identifier","length":64},{"name":"scope_catalog","type":"sql_identifier","length":64},{"name":"scope_schema","type":"sql_identifier","length":64},{"name":"scope_name","type":"sql_identifier","length":64},{"name":"maximum_cardinality","type":"cardinal_number","length":4},{"name":"dtd_identifier","type":"sql_identifier","length":64},{"name":"is_derived_reference_attribute","type":"yes_or_no"}]} diff --git a/internal/engine/postgresql/seed.go b/internal/engine/postgresql/seed.go index a159563b3a..9cf184c50d 100644 --- a/internal/engine/postgresql/seed.go +++ b/internal/engine/postgresql/seed.go @@ -2,7 +2,6 @@ package postgresql import ( "embed" - "strings" "sync" "github.com/sqlc-dev/sqlc/internal/core" @@ -32,20 +31,6 @@ func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } -func init() { - core.RegisterCanonicalizer("postgresql", canonicalize) -} - -// canonicalize rewrites what pg_type spells differently from format_type: -// an array type's own name is its element's with an underscore in front, -// which is how the system catalogs' columns are seeded. -func canonicalize(t *core.TypeExpr) *core.TypeExpr { - if element, ok := strings.CutPrefix(t.Name, "_"); ok && len(t.Args) == 0 && element != "" { - return core.Array(&core.TypeExpr{Name: element, Nullable: t.Nullable}) - } - return t -} - // pgCatalogFuncs is pg_catalog's functions in the form the catalog uses. The // list runs to thousands of entries and never changes within a run, so it is // read once. diff --git a/internal/engine/sqlite/dialect/dialect.json b/internal/engine/sqlite/dialect/dialect.json index caa2e365f7..60bce0eaf3 100644 --- a/internal/engine/sqlite/dialect/dialect.json +++ b/internal/engine/sqlite/dialect/dialect.json @@ -20,5 +20,26 @@ "geopoly": "enable_geopoly", "rtree": "enable_rtree", "rtree_i32": "enable_rtree" - } + }, + "affinity": [ + { + "contains": ["INT"], + "type": "integer" + }, + { + "contains": ["CHAR", "CLOB", "TEXT"], + "type": "text" + }, + { + "contains": ["BLOB"], + "type": "blob" + }, + { + "contains": ["REAL", "FLOA", "DOUB"], + "type": "real" + }, + { + "type": "numeric" + } + ] } diff --git a/internal/engine/sqlite/seed.go b/internal/engine/sqlite/seed.go index b115b1025a..cc439b43b7 100644 --- a/internal/engine/sqlite/seed.go +++ b/internal/engine/sqlite/seed.go @@ -2,7 +2,6 @@ package sqlite import ( "embed" - "strings" "sync" "github.com/sqlc-dev/sqlc/internal/core" @@ -22,30 +21,6 @@ func Dialect() core.Option { return seed.Dialect(dialectFS, "dialect") } -func init() { - core.RegisterUserTypeBase("sqlite", affinity) -} - -// affinity is the type a declared spelling SQLite has no name for stands -// on: the affinity its rule gives it, decided by the words in it. INT -// anywhere is INTEGER; CHAR, CLOB or TEXT is TEXT; BLOB is BLOB; REAL, FLOA -// or DOUB is REAL; anything else is NUMERIC. A column with no type at all -// has BLOB affinity, but sqlc reads one as any. -func affinity(name string) (base, category string) { - upper := strings.ToUpper(name) - switch { - case strings.Contains(upper, "INT"): - return "integer", "N" - case strings.Contains(upper, "CHAR"), strings.Contains(upper, "CLOB"), strings.Contains(upper, "TEXT"): - return "text", "S" - case strings.Contains(upper, "BLOB"): - return "blob", "U" - case strings.Contains(upper, "REAL"), strings.Contains(upper, "FLOA"), strings.Contains(upper, "DOUB"): - return "real", "N" - } - return "numeric", "N" -} - // stdlib is SQLite's functions in the form the catalog uses. They are embedded // in the binary and never change within a run, so they are read once. var stdlib = sync.OnceValue(func() []*catalog.Function { diff --git a/internal/goldeneye/postgresql/relation.go b/internal/goldeneye/postgresql/relation.go index ecd20e2d1f..3a9b989e74 100644 --- a/internal/goldeneye/postgresql/relation.go +++ b/internal/goldeneye/postgresql/relation.go @@ -19,13 +19,17 @@ select relations.name as tablename, pg_attribute.attname as column_name, attnotnull as column_notnull, - column_type.typname as column_type, - nullif(column_type.typlen, -1) as column_length, + -- 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. + coalesce(element_type.typname, column_type.typname) as column_type, + nullif(coalesce(element_type.typlen, column_type.typlen), -1) as column_length, column_type.typcategory = 'A' 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 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 From c70c5d64d8b2fcf4af2e9d5ed93a0faa9efbfef0 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 18:35:14 +0000 Subject: [PATCH 14/16] core: answer the review: canonical type field, label parsing, namespaces, lookups A type's canonical rendering now travels in its own field of ast.TypeName, so DuckDB, GoogleSQL and ClickHouse hand the core struct(a: integer) and Enum8('a' = 1) while the formatter keeps printing the author's spelling; ParseTypeExpr reads a label before a colon, and before a space only when one word follows, so a field typed timestamp with time zone keeps its type. A lookup that may not write no longer probes by inserting an empty-named row. A bare type name resolves in the default namespaces only, and CREATE TYPE deduplicates within the namespace it names, so foo.mood and mood are two types. IN with a subquery types its left side once, and an operator's overloads are read once per resolution. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- internal/core/analyzer/expr.go | 29 ++++--- internal/core/catalogdb/query.sql.go | 50 +++++++++++ internal/core/catalogdef/query.sql | 5 ++ internal/core/schema/schema.go | 8 +- internal/core/typeexpr.go | 15 ++-- internal/core/typename.go | 18 ++-- internal/core/types.go | 116 +++++++++++++++++++++----- internal/core/types.md | 24 ++++-- internal/engine/clickhouse/convert.go | 60 ++++++++----- internal/engine/duckdb/convert.go | 4 +- internal/engine/googlesql/convert.go | 2 +- internal/engine/googlesql/utils.go | 4 +- internal/sql/ast/type_name.go | 5 ++ 13 files changed, 259 insertions(+), 81 deletions(-) diff --git a/internal/core/analyzer/expr.go b/internal/core/analyzer/expr.go index c9a2de9ed6..51511d3399 100644 --- a/internal/core/analyzer/expr.go +++ b/internal/core/analyzer/expr.go @@ -405,8 +405,8 @@ func (a *analyzer) typeIn(e *ast.In) (exprType, error) { if err != nil { return exprType{}, err } - if len(cols) > 0 { - if err := a.typeOperands(e.Expr, columnExprType(cols[0])); err != nil { + if pr, ok := e.Expr.(*ast.ParamRef); ok && len(cols) > 0 { + if err := a.typeOperands(pr, columnExprType(cols[0])); err != nil { return exprType{}, err } } @@ -705,24 +705,29 @@ func opNameFromList(l *ast.List) string { func (a *analyzer) resolveOperator(name string, leftT, rightT exprType) (core.OperatorOverload, error) { leftChain := a.cat.ResolutionChain(leftT.typeOID) rightChain := a.cat.ResolutionChain(rightT.typeOID) + all, err := a.cat.FindOperators(name, 0, 0) + if err != nil { + return core.OperatorOverload{}, err + } + // The operator's overloads are read once; the pairs along the two + // chains are tried against them in order, nearest first. + byOperands := make(map[[2]int64]core.OperatorOverload, len(all)) + for _, ov := range all { + key := [2]int64{ov.LeftTypeOID, ov.RightTypeOID} + if _, seen := byOperands[key]; !seen { + byOperands[key] = ov + } + } for _, l := range leftChain { for _, r := range rightChain { - candidates, err := a.cat.FindOperators(name, l, r) - if err != nil { - return core.OperatorOverload{}, err - } - if len(candidates) > 0 { - return candidates[0], nil + if ov, ok := byOperands[[2]int64{l, r}]; ok && l != 0 && r != 0 { + return ov, nil } } } leftOID := leftChain[len(leftChain)-1] rightOID := rightChain[len(rightChain)-1] - all, err := a.cat.FindOperators(name, 0, 0) - if err != nil { - return core.OperatorOverload{}, err - } for _, ov := range all { if leftOID != 0 && ov.LeftTypeOID != 0 && leftOID != ov.LeftTypeOID { ok, _ := a.cat.CastAllowed(leftOID, ov.LeftTypeOID, "i") diff --git a/internal/core/catalogdb/query.sql.go b/internal/core/catalogdb/query.sql.go index d6b51e3ec9..ba4ee026d2 100644 --- a/internal/core/catalogdb/query.sql.go +++ b/internal/core/catalogdb/query.sql.go @@ -1357,6 +1357,56 @@ func (q *Queries) TypeOIDByNameInNamespace(ctx context.Context, arg TypeOIDByNam return oid, err } +const typeOIDsByNameInNamespaces = `-- name: TypeOIDsByNameInNamespaces :many +SELECT oid, namespace_oid FROM sql_type +WHERE name = ?1 AND family_oid IS NULL + AND namespace_oid IN (/*SLICE:namespace_oids*/?) +` + +type TypeOIDsByNameInNamespacesParams struct { + Name string + NamespaceOids []int64 +} + +type TypeOIDsByNameInNamespacesRow struct { + Oid int64 + NamespaceOid int64 +} + +func (q *Queries) TypeOIDsByNameInNamespaces(ctx context.Context, arg TypeOIDsByNameInNamespacesParams) ([]TypeOIDsByNameInNamespacesRow, error) { + query := typeOIDsByNameInNamespaces + var queryParams []any + queryParams = append(queryParams, arg.Name) + if len(arg.NamespaceOids) > 0 { + for _, v := range arg.NamespaceOids { + queryParams = append(queryParams, v) + } + query = strings.Replace(query, "/*SLICE:namespace_oids*/?", strings.Repeat(",?", len(arg.NamespaceOids))[1:], 1) + } else { + query = strings.Replace(query, "/*SLICE:namespace_oids*/?", "NULL", 1) + } + rows, err := q.db.QueryContext(ctx, query, queryParams...) + if err != nil { + return nil, err + } + defer rows.Close() + var items []TypeOIDsByNameInNamespacesRow + for rows.Next() { + var i TypeOIDsByNameInNamespacesRow + if err := rows.Scan(&i.Oid, &i.NamespaceOid); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const typeOIDsInCategory = `-- name: TypeOIDsInCategory :many SELECT oid FROM sql_type WHERE dialect_oid = ? AND category = ? diff --git a/internal/core/catalogdef/query.sql b/internal/core/catalogdef/query.sql index 2899ae13cd..2c56ac5d15 100644 --- a/internal/core/catalogdef/query.sql +++ b/internal/core/catalogdef/query.sql @@ -66,6 +66,11 @@ ORDER BY ns.name LIMIT 1; +-- name: TypeOIDsByNameInNamespaces :many +SELECT oid, namespace_oid FROM sql_type +WHERE name = sqlc.arg(name) AND family_oid IS NULL + AND namespace_oid IN (sqlc.slice(namespace_oids)); + -- name: TypeOIDByNameInNamespace :one SELECT oid FROM sql_type WHERE namespace_oid = ? AND name = ? AND family_oid IS NULL; diff --git a/internal/core/schema/schema.go b/internal/core/schema/schema.go index bfdc6c1a37..e96d8dbe2a 100644 --- a/internal/core/schema/schema.go +++ b/internal/core/schema/schema.go @@ -367,7 +367,7 @@ func applyCreateEnum(cat *core.Catalog, stmt *ast.CreateEnumStmt) error { if name == "" { return fmt.Errorf("create type with empty name") } - if _, err := cat.TypeOID(name); err == nil { + if cat.TypeDeclared(name) { return nil } var labels []core.TypeArg @@ -386,7 +386,7 @@ func applyCreateDomain(cat *core.Catalog, stmt *ast.CreateDomainStmt) error { if name == "" || stmt.TypeName == nil { return fmt.Errorf("create domain: missing name or type") } - if _, err := cat.TypeOID(name); err == nil { + if cat.TypeDeclared(name) { return nil } baseOID, err := cat.ResolveType(stmt.TypeName) @@ -423,7 +423,7 @@ func applyCompositeType(cat *core.Catalog, stmt *ast.CompositeTypeStmt) error { if name == "" { return fmt.Errorf("create type with empty name") } - if _, err := cat.TypeOID(name); err == nil { + if cat.TypeDeclared(name) { return nil } var fields []core.TypeArg @@ -449,7 +449,7 @@ func applyCreateRange(cat *core.Catalog, stmt *ast.CreateRangeStmt) error { if name == "" { return fmt.Errorf("create type with empty name") } - if _, err := cat.TypeOID(name); err == nil { + if cat.TypeDeclared(name) { return nil } spec := core.TypeSpec{Name: name, Typtype: "r", Category: "R"} diff --git a/internal/core/typeexpr.go b/internal/core/typeexpr.go index 83cd719e84..59372bb1f9 100644 --- a/internal/core/typeexpr.go +++ b/internal/core/typeexpr.go @@ -156,16 +156,19 @@ func parseTypeArg(a string) TypeArg { b := strings.EqualFold(a, "true") return TypeArg{Bool: &b} } - // A label is a word before a space that comes before any parenthesis, - // as in `lat Float64` or `tags Array(String)`. + // A label is a word before a colon, as the canonical form writes it: + // `lat: Float64`. A word before a space is a label too, as ClickHouse + // writes `lat Float64`, but only when what follows is a single word, + // since `timestamp with time zone` is a name and not a label. head := a if p := strings.IndexByte(a, '('); p >= 0 { head = a[:p] } - if i := strings.IndexByte(head, ' '); i > 0 { - arg := parseTypeArg(a[i+1:]) - arg.Label = a[:i] - return arg + if i := strings.IndexByte(head, ':'); i > 0 && !strings.ContainsAny(head[:i], " '\"") { + return TypeArg{Label: strings.TrimSpace(a[:i]), Type: ParseTypeExpr(a[i+1:])} + } + if i := strings.IndexByte(head, ' '); i > 0 && !strings.Contains(strings.TrimSpace(head[i+1:]), " ") { + return TypeArg{Label: a[:i], Type: ParseTypeExpr(a[i+1:])} } return TypeArg{Type: ParseTypeExpr(a)} } diff --git a/internal/core/typename.go b/internal/core/typename.go index fd2c08272a..4c37a272ac 100644 --- a/internal/core/typename.go +++ b/internal/core/typename.go @@ -8,16 +8,20 @@ import ( ) // TypeExprOfTypeName reads the type an AST node names into an expression. -// An engine that folds the whole type into a spelling — ClickHouse's -// Array(Nullable(String)), SQLite's VARYING CHARACTER(10) — hands it over -// in Spelling and the spelling is read as written. Otherwise the name comes -// from Name or the qualifying parts of Names, the type modifiers become -// integer or string arguments, and each array bound wraps the result in an -// array. +// An engine that renders the whole type as a call expression — DuckDB's +// struct(a: integer, b: varchar) — hands it over in Canonical; one that +// folds it into the spelling the formatter prints back — ClickHouse's +// Array(Nullable(String)), SQLite's VARYING CHARACTER(10) — in Spelling. +// Otherwise the name comes from Name or the qualifying parts of Names, the +// type modifiers become integer or string arguments, and each array bound +// wraps the result in an array. func TypeExprOfTypeName(tn *ast.TypeName) *TypeExpr { if tn == nil { return nil } + if tn.Canonical != "" { + return ParseTypeExpr(tn.Canonical) + } if tn.Spelling != "" { return ParseTypeExpr(tn.Spelling) } @@ -79,7 +83,7 @@ func ColumnTypeExpr(col *ast.ColumnDef) *TypeExpr { } } } - if col.TypeName.Spelling != "" || listItems(col.TypeName.ArrayBounds) != nil { + if col.TypeName.Canonical != "" || col.TypeName.Spelling != "" || listItems(col.TypeName.ArrayBounds) != nil { return t } dims := col.ArrayDims diff --git a/internal/core/types.go b/internal/core/types.go index f163e3ad4b..8908c527fa 100644 --- a/internal/core/types.go +++ b/internal/core/types.go @@ -54,10 +54,11 @@ func (t TypeInfo) IsFamily() bool { return t.FamilyOID == 0 } // answer is good for the life of the catalog. Analysis runs concurrently on // a restored catalog, so the cache is locked. type typeCache struct { - mu sync.RWMutex - infos map[int64]TypeInfo - exprs map[int64]*TypeExpr - namespaces map[int64]string + mu sync.RWMutex + infos map[int64]TypeInfo + exprs map[int64]*TypeExpr + namespaces map[int64]string + namespaceOIDs map[string]int64 } func (c *typeCache) namespace(oid int64) (string, bool) { @@ -67,13 +68,22 @@ func (c *typeCache) namespace(oid int64) (string, bool) { return name, ok } +func (c *typeCache) namespaceOID(name string) (int64, bool) { + c.mu.RLock() + defer c.mu.RUnlock() + oid, ok := c.namespaceOIDs[name] + return oid, ok +} + func (c *typeCache) putNamespace(oid int64, name string) { c.mu.Lock() defer c.mu.Unlock() if c.namespaces == nil { c.namespaces = map[int64]string{} + c.namespaceOIDs = map[string]int64{} } c.namespaces[oid] = name + c.namespaceOIDs[name] = oid } func (c *typeCache) info(oid int64) (TypeInfo, bool) { @@ -254,20 +264,77 @@ func (c *Catalog) TypeOID(name string) (int64, error) { return c.canonicalOID(oid) } -// familyOIDByName finds the family row spelled name, alias rows included. +// familyOIDByName finds the family row a bare name refers to, alias rows +// included, in the default namespaces: the catalog's own, PostgreSQL's +// system catalog and the dialect's default schema, in that order of +// preference. A type in any other namespace is reached by qualifying it, +// as PostgreSQL reaches one off the search path. func (c *Catalog) familyOIDByName(name string) (int64, error) { - return c.q.TypeOIDByName(context.Background(), name) + nsOIDs, err := c.defaultNamespaceOIDs() + if err != nil { + return 0, err + } + rows, err := c.q.TypeOIDsByNameInNamespaces(context.Background(), catalogdb.TypeOIDsByNameInNamespacesParams{ + Name: name, + NamespaceOids: nsOIDs, + }) + if err != nil { + return 0, err + } + for _, ns := range nsOIDs { + for _, row := range rows { + if row.NamespaceOid == ns { + return row.Oid, nil + } + } + } + return 0, sql.ErrNoRows +} + +// defaultNamespaceOIDs lists the namespaces a bare type name is looked up +// in, in order of preference, skipping any the catalog does not have yet. +func (c *Catalog) defaultNamespaceOIDs() ([]int64, error) { + names := []string{"pg_catalog", "public"} + if c.dialectOID != 0 { + if name, _ := c.DialectFlag(c.dialectOID, FlagDefaultSchema); name != "" { + names = append(names, name) + } + } + out := make([]int64, 0, len(names)) + for _, name := range names { + oid, err := c.namespaceOIDByName(name) + if err != nil { + continue + } + out = append(out, oid) + } + return out, nil +} + +// namespaceOIDByName is NamespaceOID with the answer remembered, since a +// type lookup asks for the same few namespaces every time. A namespace +// created after the answer was cached is found on the next miss. +func (c *Catalog) namespaceOIDByName(name string) (int64, error) { + if oid, ok := c.types.namespaceOID(name); ok { + return oid, nil + } + oid, err := c.NamespaceOID(name) + if err != nil { + return 0, err + } + c.types.putNamespace(oid, name) + return oid, nil } // familyOIDByQualifiedName is familyOIDByName for a name that may carry its // namespace, as myschema.mood does: a qualified name is looked up in that -// namespace alone, a bare one in every namespace. +// namespace alone. func (c *Catalog) familyOIDByQualifiedName(name string) (int64, error) { ns, bare := splitQualifiedName(name) if ns == "" { return c.familyOIDByName(bare) } - nsOID, err := c.NamespaceOID(ns) + nsOID, err := c.namespaceOIDByName(ns) if err != nil { return 0, err } @@ -277,6 +344,14 @@ func (c *Catalog) familyOIDByQualifiedName(name string) (int64, error) { }) } +// TypeDeclared reports whether a schema's CREATE TYPE would redeclare a +// type: one of that name in the namespace the name qualifies, or in the +// default namespaces for a bare name. +func (c *Catalog) TypeDeclared(name string) bool { + _, err := c.familyOIDByQualifiedName(strings.ToLower(name)) + return err == nil +} + // splitQualifiedName splits "myschema.mood" into its namespace and name. A // name with no dot has no namespace. func splitQualifiedName(name string) (ns, bare string) { @@ -324,7 +399,7 @@ func (c *Catalog) CreateTypeWithArgs(spec TypeSpec, args []TypeArg) (int64, erro } oid, _, err := c.internType(a.Type, func(name string) (int64, error) { return c.CreateUserType(name, "U") - }) + }, true) if err != nil { return 0, fmt.Errorf("create type %q: %w", spec.Name, err) } @@ -525,7 +600,7 @@ func (c *Catalog) TypeExprOf(oid int64) (*TypeExpr, error) { func (c *Catalog) ResolveTypeExpr(t *TypeExpr) (int64, error) { oid, _, err := c.internType(t, func(name string) (int64, error) { return c.CreateUserType(name, "U") - }) + }, true) return oid, err } @@ -536,7 +611,7 @@ func (c *Catalog) ResolveTypeExpr(t *TypeExpr) (int64, error) { func (c *Catalog) ResolvePseudoTypeExpr(t *TypeExpr) (int64, error) { oid, _, err := c.internType(t, func(name string) (int64, error) { return c.CreateTypeSpec(TypeSpec{Name: name, Category: "U", DialectOID: c.dialectOID}) - }) + }, true) return oid, err } @@ -565,10 +640,10 @@ type TypeLookup struct { // reports false when the family is not one the catalog holds. func (c *Catalog) LookupTypeExpr(t *TypeExpr) (TypeLookup, bool) { refuse := func(string) (int64, error) { return 0, errUnknownType } - oid, canonical, err := c.internType(t, refuse) + oid, canonical, err := c.internType(t, refuse, false) if errors.Is(err, errUnknownType) && canonical != nil { // The family is known and the instance is not a row. - familyOID, _, err := c.internType(&TypeExpr{Name: canonical.Name}, refuse) + familyOID, _, err := c.internType(&TypeExpr{Name: canonical.Name}, refuse, false) if err != nil { return TypeLookup{}, false } @@ -589,11 +664,12 @@ func (c *Catalog) LookupTypeExpr(t *TypeExpr) (TypeLookup, bool) { } // internType resolves an expression to its row, creating the instance row -// when there is none and calling newFamily for a family name the catalog -// does not hold, which may refuse. Alongside the row it returns the -// expression canonicalized; when the instance is not a row and newFamily -// refuses, the canonical expression still comes back with the error. -func (c *Catalog) internType(t *TypeExpr, newFamily func(name string) (int64, error)) (int64, *TypeExpr, error) { +// when there is none and write allows it, and calling newFamily for a +// family name the catalog does not hold, which may refuse. Alongside the +// row it returns the expression canonicalized; when the instance is not a +// row and writing is not allowed, the canonical expression still comes +// back with errUnknownType. +func (c *Catalog) internType(t *TypeExpr, newFamily func(name string) (int64, error), write bool) (int64, *TypeExpr, error) { if t == nil || strings.TrimSpace(t.Name) == "" { return 0, nil, fmt.Errorf("missing type name") } @@ -637,7 +713,7 @@ func (c *Catalog) internType(t *TypeExpr, newFamily func(name string) (int64, er if a.Type == nil { continue } - oid, argExpr, err := c.internType(a.Type, newFamily) + oid, argExpr, err := c.internType(a.Type, newFamily, write) if err != nil { return 0, nil, err } @@ -656,7 +732,7 @@ func (c *Catalog) internType(t *TypeExpr, newFamily func(name string) (int64, er return 0, nil, fmt.Errorf("type %q: %w", key, err) } // A lookup that may not write stops here, canonical expression in hand. - if _, err := newFamily(""); errors.Is(err, errUnknownType) { + if !write { return 0, canonical, errUnknownType } diff --git a/internal/core/types.md b/internal/core/types.md index 5c7fa56e86..ac9b17f181 100644 --- a/internal/core/types.md +++ b/internal/core/types.md @@ -666,16 +666,26 @@ and details settled on the way: in `functions.jsonl` — `"returns": "Decimal(18, $2)"` — kept on `sql_proc.return_template` and filled in from the call's literals. What is genuinely about parsing stays in the engine's converter: ClickHouse - numbers an Enum's members and sorts a Variant's when it spells the type. + numbers an Enum's members and sorts a Variant's in the canonical + rendering it hands the core, while the spelling the formatter prints stays + the author's. +- A bare type name resolves in the default namespaces only — the catalog's + own, `pg_catalog` and the dialect's default schema — and `CREATE TYPE` + deduplicates within the namespace it names, so `foo.mood` and `mood` are + two types and a bare `mood` never binds to `foo.mood`. - SQLite's `dialect.json` says `"alias": "base"`, which makes each alias in its `types.jsonl` a type of its own standing on the type it aliases, rather than another spelling of it. -- An engine hands the core either a spelling (`TypeName.Spelling`, read by - `ParseTypeExpr`, which also reads words after a closing parenthesis as - part of the name, as in `decimal(10,2) unsigned`) or a name with - `Typmods` and `ArrayBounds`, where an integer constant is an integer - argument, a bare `ast.String` is an identifier and a quoted constant a - string. `ColumnDef.IsUnsigned` and `ColumnDef.Vals` add MySQL's unsigned +- An engine hands the core one of three things: a canonical rendering + (`TypeName.Canonical`, a call expression with fields labelled `a: integer`, + which DuckDB, GoogleSQL and ClickHouse write and the formatter never + prints), the author's spelling (`TypeName.Spelling`, which the formatter + prints back and SQLite hands over as its type), or a name with `Typmods` + and `ArrayBounds`, where an integer constant is an integer argument, a + bare `ast.String` is an identifier and a quoted constant a string. + `ParseTypeExpr` reads a label before a colon, a label before a space only + when a single word follows, and words after a closing parenthesis as part + of the name, as in `decimal(10,2) unsigned`. `ColumnDef.IsUnsigned` and `ColumnDef.Vals` add MySQL's unsigned and enum members. `ParamRef.Name` carries the name a `{name:Type}` placeholder gives itself. - A cast is NULL when its operand is, or when its type says so, as diff --git a/internal/engine/clickhouse/convert.go b/internal/engine/clickhouse/convert.go index 5a2c49be41..7ef2d7e14d 100644 --- a/internal/engine/clickhouse/convert.go +++ b/internal/engine/clickhouse/convert.go @@ -704,7 +704,7 @@ func (c *cc) convertParameter(n *chast.Parameter) ast.Node { base, _, _ := unwrapTypeString(spelling) return &ast.TypeCast{ Arg: ref, - TypeName: &ast.TypeName{Name: base, Spelling: spelling}, + TypeName: &ast.TypeName{Name: base, Spelling: spelling, Canonical: canonicalDataType(n.Type)}, Location: pos(n), } } @@ -764,7 +764,7 @@ func (c *cc) convertCastExpr(n *chast.CastExpr) *ast.TypeCast { // nesting included, the way a column's is. spelling := renderDataType(n.Type) base, _, _ := unwrapTypeString(spelling) - tc.TypeName = &ast.TypeName{Name: base, Spelling: spelling} + tc.TypeName = &ast.TypeName{Name: base, Spelling: spelling, Canonical: canonicalDataType(n.Type)} } return tc @@ -1026,7 +1026,7 @@ func (c *cc) convertNestedColumn(n *chast.ColumnDeclaration) ([]*ast.ColumnDef, spelling := "Array(" + renderDataType(pair.Type) + ")" cols = append(cols, &ast.ColumnDef{ Colname: identifier(n.Name) + "." + identifier(pair.Name), - TypeName: &ast.TypeName{Name: "array", Spelling: spelling}, + TypeName: &ast.TypeName{Name: "array", Spelling: spelling, Canonical: "Array(" + canonicalDataType(pair.Type) + ")"}, IsArray: true, IsNotNull: true, }) @@ -1045,7 +1045,7 @@ func (c *cc) convertColumnDeclaration(n *chast.ColumnDeclaration) *ast.ColumnDef base, isArray, nullable := unwrapTypeString(spelling) // The catalog resolves the base type; the full spelling, with its // arguments and nesting, is kept for the analysis to report. - colDef.TypeName = &ast.TypeName{Name: base, Spelling: spelling} + colDef.TypeName = &ast.TypeName{Name: base, Spelling: spelling, Canonical: canonicalDataType(n.Type)} colDef.IsArray = isArray if nullable { colDef.IsNotNull = false @@ -1070,11 +1070,23 @@ func (c *cc) convertColumnDeclaration(n *chast.ColumnDeclaration) *ast.ColumnDef return colDef } -// renderDataType spells a type the way ClickHouse stores it: an Enum's -// members are numbered and the family sized by their count, and a -// Variant's members are sorted, so that Enum('a', 'b') is Enum8('a' = 1, -// 'b' = 2) and Variant(String, Int64) is Variant(Int64, String). +// renderDataType spells a type as the author wrote it, which the formatter +// prints back. func renderDataType(dt *chast.DataType) string { + return renderType(dt, false) +} + +// canonicalDataType spells a type the way ClickHouse stores it, which the +// analysis core reads: an Enum's members are numbered and the family +// sized by their count, a Variant's members are sorted, and a named +// element is written label first, so that Enum('a', 'b') is Enum8('a' = +// 1, 'b' = 2), Variant(String, Int64) is Variant(Int64, String) and +// Tuple(lat Float64) is Tuple(lat: Float64). +func canonicalDataType(dt *chast.DataType) string { + return renderType(dt, true) +} + +func renderType(dt *chast.DataType, canonical bool) string { if dt == nil { return "" } @@ -1083,9 +1095,9 @@ func renderDataType(dt *chast.DataType) string { } name := dt.Name parts := make([]string, 0, len(dt.Parameters)) - switch strings.ToLower(name) { - case "enum", "enum8", "enum16": - if strings.EqualFold(name, "enum") { + switch lower := strings.ToLower(name); { + case canonical && (lower == "enum" || lower == "enum8" || lower == "enum16"): + if lower == "enum" { name = "Enum8" if len(dt.Parameters) > 127 { name = "Enum16" @@ -1096,34 +1108,38 @@ func renderDataType(dt *chast.DataType) string { switch v := p.(type) { case *chast.BinaryExpr: // 'a' = 3 numbers itself, and the next bare member follows it. - parts = append(parts, renderTypeParam(v)) + parts = append(parts, renderParam(v, canonical)) if lit, ok := v.Right.(*chast.Literal); ok { if n, err := strconv.ParseInt(fmt.Sprint(lit.Value), 10, 64); err == nil { next = n + 1 } } default: - parts = append(parts, renderTypeParam(p)+" = "+strconv.FormatInt(next, 10)) + parts = append(parts, renderParam(p, canonical)+" = "+strconv.FormatInt(next, 10)) next++ } } - case "variant": + case canonical && lower == "variant": for _, p := range dt.Parameters { - parts = append(parts, renderTypeParam(p)) + parts = append(parts, renderParam(p, canonical)) } sort.Strings(parts) default: for _, p := range dt.Parameters { - parts = append(parts, renderTypeParam(p)) + parts = append(parts, renderParam(p, canonical)) } } return name + "(" + strings.Join(parts, ", ") + ")" } func renderTypeParam(e chast.Expression) string { + return renderParam(e, false) +} + +func renderParam(e chast.Expression, canonical bool) string { switch v := e.(type) { case *chast.DataType: - return renderDataType(v) + return renderType(v, canonical) case *chast.Literal: if v.Type == chast.LiteralString { return quoteString(fmt.Sprint(v.Value)) @@ -1135,11 +1151,15 @@ func renderTypeParam(e chast.Expression) string { case *chast.Identifier: return strings.Join(v.Parts, ".") case *chast.NameTypePair: - // A named tuple or nested element: `lat Float64`. - return v.Name + " " + renderDataType(v.Type) + // A named tuple or nested element: `lat Float64`, or `lat: Float64` + // in the canonical form. + if canonical { + return v.Name + ": " + renderType(v.Type, canonical) + } + return v.Name + " " + renderType(v.Type, canonical) case *chast.BinaryExpr: // An enum member: `'active' = 1`. - return renderTypeParam(v.Left) + " " + v.Op + " " + renderTypeParam(v.Right) + return renderParam(v.Left, canonical) + " " + v.Op + " " + renderParam(v.Right, canonical) default: return "" } diff --git a/internal/engine/duckdb/convert.go b/internal/engine/duckdb/convert.go index 4a962378d5..e400138169 100644 --- a/internal/engine/duckdb/convert.go +++ b/internal/engine/duckdb/convert.go @@ -818,7 +818,7 @@ func (c *cc) convertWindow(e *dw.WindowExpression) ast.Node { // its spelling, which is what the analysis core reads. func (c *cc) convertTypeExpression(t *dw.TypeExpression) (*ast.TypeName, int) { typeName, dims := c.elementTypeName(t) - typeName.Spelling = renderTypeExpression(t) + typeName.Canonical = renderTypeExpression(t) return typeName, dims } @@ -862,7 +862,7 @@ func renderTypeExpression(t *dw.TypeExpression) string { case *dw.TypeExpression: part = renderTypeExpression(a) if a.Alias != "" { - part = identifier(a.Alias) + " " + part + part = identifier(a.Alias) + ": " + part } case *dw.ConstantExpression: switch { diff --git a/internal/engine/googlesql/convert.go b/internal/engine/googlesql/convert.go index 8731659222..cedfe7f090 100644 --- a/internal/engine/googlesql/convert.go +++ b/internal/engine/googlesql/convert.go @@ -973,5 +973,5 @@ func spelledTypeName(spelling string) *ast.TypeName { if i := strings.IndexByte(name, '('); i >= 0 { name = name[:i] } - return &ast.TypeName{Name: name, Spelling: spelling} + return &ast.TypeName{Name: name, Canonical: spelling} } diff --git a/internal/engine/googlesql/utils.go b/internal/engine/googlesql/utils.go index e22a930b24..0ee4c585d2 100644 --- a/internal/engine/googlesql/utils.go +++ b/internal/engine/googlesql/utils.go @@ -197,7 +197,7 @@ func typeName(node zjast.Node) string { for _, f := range t.Fields { field := typeName(f.Type) if f.Name != nil { - field = identifier(f.Name.Name) + " " + field + field = identifier(f.Name.Name) + ": " + field } fields = append(fields, field) } @@ -249,7 +249,7 @@ func columnSchemaTypeName(node zjast.Node) string { for _, f := range t.Fields { field := columnSchemaTypeName(f.Schema) if f.Name != nil { - field = identifier(f.Name.Name) + " " + field + field = identifier(f.Name.Name) + ": " + field } fields = append(fields, field) } diff --git a/internal/sql/ast/type_name.go b/internal/sql/ast/type_name.go index 9a557707b8..ddf7decfaa 100644 --- a/internal/sql/ast/type_name.go +++ b/internal/sql/ast/type_name.go @@ -13,6 +13,11 @@ type TypeName struct { // CHARACTER" resolves as "VARYINGCHARACTER" in the catalog), so the // formatter prints this back instead of the folded form. Spelling string `json:"spelling"` + // Canonical is the type as a call expression the analysis core reads, + // when an engine spells it differently from what the formatter prints + // back: DuckDB's STRUCT(a INTEGER, b VARCHAR) as struct(a: integer, b: + // varchar), ClickHouse's Enum('a', 'b') as Enum8('a' = 1, 'b' = 2). + Canonical string `json:"canonical,omitempty"` // From pg.TypeName Names *List `json:"names,omitempty"` From 33ee06274497eee7085b9fb35de6b9501302ee33 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 19:08:45 +0000 Subject: [PATCH 15/16] core: answer the second review: qualified keys, nested lookups, rewrites after aliases An instance row's key is built from namespace-qualified names, so array(myschema.mood) and array(mood) are two rows, and a lookup that may not write still canonicalizes an expression whose nested instance is not a row, so $1::varchar(10)[] reports array(character varying(10)). The array family is seeded for every dialect rather than created on a read-only lookup. A rewrite is tried with the alias resolved, so dec(7,2) meets a rule on decimal. A bare CREATE TYPE lands in the dialect's default schema, so SQL Server's Code and dbo.Code are one row. A family in types.jsonl may name a base, which is how MySQL's unsigned families stand on their signed ones; the legacy bridge strips " unsigned" from the data type and sets the flag. Relations() carries one array dimension for an array column. Converters: a PostgreSQL cast to interval day to second decodes the field mask as a column does; a MySQL cast to float or double carries no modifier and a decimal cast's scale left out is 0; DuckDB's main schema no longer leaves a leading dot; ClickHouse's LowCardinality(Nullable(T)) is a nullable column, read as a nullable lowcardinality(T) by the converter and by goldeneye alike. goldeneye keeps the case of a MySQL enum's members and unescapes them, and relabels only the _-prefixed PostgreSQL array types, so int2vector and oidvector stay their own; the relation seeds and the goldens they feed are regenerated. The analyze_types cases cover each fix. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- internal/compiler/catalog_core.go | 2 +- internal/core/analyzer/expr.go | 5 +- internal/core/rewrite.go | 43 +++- internal/core/seed/seed.go | 25 +- internal/core/types.go | 49 +++- internal/core/types.md | 34 ++- .../analyze_system_catalog/mysql/stdout.json | 6 +- .../analyze_types/clickhouse/stdout.json | 8 +- .../testdata/analyze_types/duckdb/query.sql | 3 +- .../testdata/analyze_types/duckdb/schema.sql | 1 + .../testdata/analyze_types/duckdb/stdout.json | 23 ++ .../testdata/analyze_types/mssql/query.sql | 3 +- .../testdata/analyze_types/mssql/schema.sql | 3 + .../testdata/analyze_types/mssql/stdout.json | 31 +++ .../testdata/analyze_types/mysql/query.sql | 6 +- .../testdata/analyze_types/mysql/schema.sql | 1 + .../testdata/analyze_types/mysql/stdout.json | 58 +++++ .../analyze_types/postgresql/query.sql | 6 +- .../analyze_types/postgresql/schema.sql | 1 + .../analyze_types/postgresql/stdout.json | 137 +++++++++++ .../testdata/codegen_json/gen/codegen.json | 224 +++++++++--------- internal/engine/clickhouse/convert.go | 18 +- internal/engine/dolphin/convert.go | 10 +- .../engine/dolphin/dialect/relations.jsonl | 32 +-- internal/engine/dolphin/dialect/types.jsonl | 16 +- internal/engine/duckdb/convert.go | 4 +- internal/engine/postgresql/convert.go | 39 ++- .../engine/postgresql/dialect/relations.jsonl | 12 +- internal/goldeneye/clickhouse/types.go | 7 + internal/goldeneye/mysql/analyze.go | 39 ++- internal/goldeneye/mysql/relations.go | 24 +- internal/goldeneye/postgresql/relation.go | 9 +- 32 files changed, 680 insertions(+), 199 deletions(-) diff --git a/internal/compiler/catalog_core.go b/internal/compiler/catalog_core.go index dd7cbaed8d..7f2e2624fe 100644 --- a/internal/compiler/catalog_core.go +++ b/internal/compiler/catalog_core.go @@ -42,7 +42,7 @@ func coreResultCatalog(c *core.Catalog) (*catalog.Catalog, error) { inner := expr.Innermost() column := &catalog.Column{ Name: col.Name, - Type: ast.TypeName{Name: inner.Name}, + Type: ast.TypeName{Name: strings.TrimSuffix(inner.Name, " unsigned")}, IsNotNull: col.NotNull, IsArray: expr.IsArray(), ArrayDims: expr.ArrayDims(), diff --git a/internal/core/analyzer/expr.go b/internal/core/analyzer/expr.go index 51511d3399..8305222c80 100644 --- a/internal/core/analyzer/expr.go +++ b/internal/core/analyzer/expr.go @@ -575,7 +575,10 @@ func (a *analyzer) typeNameOf(t exprType) (string, bool) { if e == nil { return "", false } - return e.Innermost().Name, e.IsArray() + // MySQL's unsigned families are their own types, but codegen reads + // the signed family and an unsigned flag, which the bridge derives + // from the expression. + return strings.TrimSuffix(e.Innermost().Name, " unsigned"), e.IsArray() } // columnExprType is the type a result column of a nested query has, as an diff --git a/internal/core/rewrite.go b/internal/core/rewrite.go index debe834314..352afe9bb1 100644 --- a/internal/core/rewrite.go +++ b/internal/core/rewrite.go @@ -213,13 +213,33 @@ func (c *Catalog) userTypeBase(name string) (int64, error) { // canonicalize applies the dialect's identifier settings and rewrites to // an expression: the first rewrite whose pattern matches is applied, and -// its result is not rewritten again. +// its result is not rewritten again. A rewrite names a family as the +// dialect spells it, so an expression spelled with an alias — dec(10) for +// a rule on decimal — is tried again with the alias resolved. func (c *Catalog) canonicalize(t *TypeExpr) (*TypeExpr, error) { r, err := c.loadRules() if err != nil { return nil, err } t = r.identify(t) + if len(r.rewrites) == 0 { + return t, nil + } + if out, ok := r.rewrite(t); ok { + return out, nil + } + if canonical, ok := c.canonicalFamilyName(t.Name); ok && !strings.EqualFold(canonical, t.Name) { + resolved := t.Clone() + resolved.Name = canonical + if out, ok := r.rewrite(resolved); ok { + return out, nil + } + } + return t, nil +} + +// rewrite applies the first rewrite whose pattern matches the expression. +func (r *rules) rewrite(t *TypeExpr) (*TypeExpr, bool) { name := strings.ToLower(t.Name) for _, rw := range r.rewrites { if strings.ToLower(rw.pattern.Name) != name || len(rw.pattern.Args) != len(t.Args) { @@ -231,9 +251,26 @@ func (c *Catalog) canonicalize(t *TypeExpr) (*TypeExpr, error) { } out := substitute(rw.template, bindings) out.Nullable = t.Nullable - return out, nil + return out, true } - return t, nil + return nil, false +} + +// canonicalFamilyName is the name the catalog spells a family by, with +// aliases resolved, or false when the name is not a family it holds. +func (c *Catalog) canonicalFamilyName(name string) (string, bool) { + oid, err := c.familyOIDByQualifiedName(strings.ToLower(strings.TrimSpace(name))) + if err != nil { + return "", false + } + if oid, err = c.canonicalOID(oid); err != nil { + return "", false + } + info, err := c.LookupType(oid) + if err != nil { + return "", false + } + return c.qualifiedName(info), true } // identify turns the bare words the dialect calls identifiers into diff --git a/internal/core/seed/seed.go b/internal/core/seed/seed.go index 69d561ad3b..e14f17aac8 100644 --- a/internal/core/seed/seed.go +++ b/internal/core/seed/seed.go @@ -162,6 +162,10 @@ type Type struct { Name string `json:"name"` Category string `json:"category"` Aliases []string `json:"aliases,omitempty"` + // Base names the family this one stands on, which must be listed + // before it: MySQL's bigint unsigned is a type of its own that + // resolves as a bigint where nothing takes it as itself. + Base string `json:"base,omitempty"` } // Operator is a single operator overload. @@ -305,6 +309,11 @@ func apply(cat *core.Catalog, fsys fs.FS, settings Settings) error { if err := stream(fsys, TypesFile, b.addType); err != nil { return err } + // Every dialect has arrays, whether or not its list names the family, + // and a lookup of one against the cached catalog cannot add it then. + if _, err := b.createType(core.ArrayTypeName, "A", 0); err != nil { + return err + } if err := b.consts(); err != nil { return err } @@ -439,6 +448,9 @@ func Relations(fsys fs.FS, dir, schema string) ([]*catalog.Table, error) { IsNotNull: col.NotNull, IsArray: col.Array, } + if col.Array { + column.ArrayDims = 1 + } if col.Length > 0 { length := col.Length column.Length = &length @@ -513,7 +525,15 @@ type categorized struct { } func (b *builder) addType(t Type) error { - oid, err := b.createType(t.Name, t.Category) + var baseOID int64 + if t.Base != "" { + oid, ok := b.oids[strings.ToLower(t.Base)] + if !ok { + return fmt.Errorf("type %q: base %q is not a type listed before it", t.Name, t.Base) + } + baseOID = oid + } + oid, err := b.createType(t.Name, t.Category, baseOID) if err != nil { return fmt.Errorf("type %q: %w", t.Name, err) } @@ -553,7 +573,7 @@ func (b *builder) addAlias(name string, typeOID int64, category string) error { return nil } -func (b *builder) createType(name, category string) (int64, error) { +func (b *builder) createType(name, category string, baseOID int64) (int64, error) { key := strings.ToLower(name) if oid, ok := b.oids[key]; ok { return oid, nil @@ -562,6 +582,7 @@ func (b *builder) createType(name, category string) (int64, error) { Name: key, Typtype: "b", Category: category, + BaseOID: baseOID, DialectOID: b.dialectOID, }) if err != nil { diff --git a/internal/core/types.go b/internal/core/types.go index 8908c527fa..7c482a1f2b 100644 --- a/internal/core/types.go +++ b/internal/core/types.go @@ -362,11 +362,18 @@ func splitQualifiedName(name string) (ns, bare string) { } // declaredTypeNamespace is the namespace a declared type's row goes in: the -// one its name qualifies, created if the schema has not, or the default. +// one its name qualifies, created if the schema has not, or the dialect's +// default schema — dbo, main — so that a bare CREATE TYPE and a qualified +// reference to it name one row. func (c *Catalog) declaredTypeNamespace(name string) (int64, string, error) { ns, bare := splitQualifiedName(name) if ns == "" { - return 0, bare, nil + if c.dialectOID != 0 { + ns, _ = c.DialectFlag(c.dialectOID, FlagDefaultSchema) + } + if ns == "" { + return 0, bare, nil + } } oid, err := c.NamespaceOID(ns) if err != nil { @@ -551,12 +558,7 @@ func (c *Catalog) TypeExprOf(oid int64) (*TypeExpr, error) { if err != nil { return nil, err } - expr := &TypeExpr{Name: info.Name} - // A type outside the default namespaces is named with its namespace, - // as format_type prints a type off the search path. - if ns, err := c.namespaceName(info.NamespaceOID); err == nil && ns != "" && !slices.Contains(c.DefaultNamespaces(), ns) { - expr.Name = ns + "." + info.Name - } + expr := &TypeExpr{Name: c.qualifiedName(info)} if !info.IsFamily() { rows, err := c.q.TypeArgs(context.Background(), oid) if err != nil { @@ -592,6 +594,18 @@ func (c *Catalog) TypeExprOf(oid int64) (*TypeExpr, error) { return expr.Clone(), nil } +// qualifiedName is the name a type row is known by in an expression: its +// name, qualified with its namespace when that is not one of the dialect's +// defaults, as format_type prints a type off the search path. An +// instance's key is built from these, so the array of one schema's mood is +// a row apart from the array of another's. +func (c *Catalog) qualifiedName(info TypeInfo) string { + if ns, err := c.namespaceName(info.NamespaceOID); err == nil && ns != "" && !slices.Contains(c.DefaultNamespaces(), ns) { + return ns + "." + info.Name + } + return info.Name +} + // ResolveTypeExpr interns the type an expression names and returns its row: // the family for a bare name, the instance for a family applied to // arguments, each argument type interned first. Names are canonicalized, so @@ -680,7 +694,7 @@ func (c *Catalog) internType(t *TypeExpr, newFamily func(name string) (int64, er name := strings.ToLower(strings.TrimSpace(t.Name)) familyOID, err := c.familyOIDByQualifiedName(name) if err != nil { - if name == ArrayTypeName { + if name == ArrayTypeName && write { // Every dialect has arrays, whether or not its seed lists the // family; one that does not gets it as an array type rather // than a user type. @@ -700,14 +714,17 @@ func (c *Catalog) internType(t *TypeExpr, newFamily func(name string) (int64, er return 0, nil, err } if len(t.Args) == 0 { - return familyOID, &TypeExpr{Name: family.Name}, nil + return familyOID, &TypeExpr{Name: c.qualifiedName(family)}, nil } // The instance's canonical spelling is the family applied to its // arguments as the catalog spells them, so each argument type is - // resolved first and read back. - canonical := &TypeExpr{Name: family.Name, Args: make([]TypeArg, len(t.Args))} + // resolved first and read back. An argument whose own instance is not + // a row, in a lookup that may not write, still has a canonical + // spelling, which the whole expression's is built from. + canonical := &TypeExpr{Name: c.qualifiedName(family), Args: make([]TypeArg, len(t.Args))} argOIDs := make([]int64, len(t.Args)) + unknown := false for i, a := range t.Args { canonical.Args[i] = a if a.Type == nil { @@ -715,12 +732,18 @@ func (c *Catalog) internType(t *TypeExpr, newFamily func(name string) (int64, er } oid, argExpr, err := c.internType(a.Type, newFamily, write) if err != nil { - return 0, nil, err + if !errors.Is(err, errUnknownType) || argExpr == nil { + return 0, nil, err + } + unknown = true } argOIDs[i] = oid argExpr.Nullable = a.Type.Nullable canonical.Args[i].Type = argExpr } + if unknown { + return 0, canonical, errUnknownType + } key := canonical.Key() ctx := context.Background() if oid, err := c.q.TypeOIDByExprInNamespace(ctx, catalogdb.TypeOIDByExprInNamespaceParams{ diff --git a/internal/core/types.md b/internal/core/types.md index ac9b17f181..771b09c9d5 100644 --- a/internal/core/types.md +++ b/internal/core/types.md @@ -658,7 +658,9 @@ and details settled on the way: `float($1)` to `real` where `$1 <= 24`, `decimal` to `decimal(18, 0)`, `Decimal32($1)` to `Decimal(9, $1)`, `sysname` to `nvarchar(128)` — which the seed loads into `sql_type_rewrite` and the catalog applies before - interning, first match winning; `idents` and `ident_args`, the words and + interning, first match winning, tried once with the name as spelled and + once with its alias resolved so that `dec` meets a rule on `decimal`; + `idents` and `ident_args`, the words and argument positions that are identifiers rather than types, kept as dialect flags; and `affinity`, SQLite's ordered rule for a family the seed does not list, loaded into `sql_type_affinity` and asked when a schema @@ -672,7 +674,22 @@ and details settled on the way: - A bare type name resolves in the default namespaces only — the catalog's own, `pg_catalog` and the dialect's default schema — and `CREATE TYPE` deduplicates within the namespace it names, so `foo.mood` and `mood` are - two types and a bare `mood` never binds to `foo.mood`. + two types and a bare `mood` never binds to `foo.mood`. A bare `CREATE + TYPE` lands in the dialect's default schema when it has one, so SQL + Server's `PhoneNumber` and `dbo.PhoneNumber` are one row. A type outside + the default namespaces is spelled with its namespace wherever the catalog + spells it, including inside an instance's key, so `array(foo.mood)` and + `array(mood)` are two rows. +- A lookup that may not write — the analyzer's, against the cached catalog + — still canonicalizes an expression whose instance is not a row, however + deep the missing instance sits, so `$1::varchar(10)[]` reports + `array(character varying(10))` against the `array` family. The seed + gives every dialect the `array` family for that reason, whether or not + its `types.jsonl` lists it. +- A family in `types.jsonl` may name a `base` listed before it, which is + how MySQL's unsigned families stand on their signed ones: `bigint + unsigned` is a type of its own, and resolves as a `bigint` where nothing + takes it as itself. - SQLite's `dialect.json` says `"alias": "base"`, which makes each alias in its `types.jsonl` a type of its own standing on the type it aliases, rather than another spelling of it. @@ -690,7 +707,13 @@ and details settled on the way: placeholder gives itself. - A cast is NULL when its operand is, or when its type says so, as `Nullable(String)` does; a cast of a placeholder types the placeholder - and takes its name and source from what it is compared with. + and takes its name and source from what it is compared with. A cast to + `interval day to second` decodes the field mask the parser reports the + way a column definition does. +- ClickHouse's `LowCardinality(Nullable(T))`, the only order it accepts, + is a nullable column, and the converter and `goldeneye` both read it as + `lowcardinality(T)` with the nullability on the outside, where the + column's nullability lives. - MySQL types `CAST(x AS CHAR(10))` as `varchar(10)` and `CAST(x AS BINARY(8))` as `varbinary(8)`, which is what its metadata and a view over the cast both report, rather than the `char(10)` the table above @@ -705,7 +728,10 @@ and details settled on the way: not its labels, since the canonicalizer cannot see the catalog. A GoogleSQL array or struct constructor in a select list is still untyped. - PostgreSQL's `relations.jsonl` spells an array column as its element with - the array flag, which `goldeneye` now writes from `typelem`. + the array flag, which `goldeneye` now writes from `typelem` for the + `_`-prefixed array types alone; `int2vector` and `oidvector` share the + array category but stay types of their own. MySQL's keeps the case of an + enum's members, which are values. ## Order of work diff --git a/internal/endtoend/testdata/analyze_system_catalog/mysql/stdout.json b/internal/endtoend/testdata/analyze_system_catalog/mysql/stdout.json index 661011fd43..134b5b11f8 100644 --- a/internal/endtoend/testdata/analyze_system_catalog/mysql/stdout.json +++ b/internal/endtoend/testdata/analyze_system_catalog/mysql/stdout.json @@ -75,13 +75,13 @@ "name": "enum", "args": [ { - "string": "base table" + "string": "BASE TABLE" }, { - "string": "view" + "string": "VIEW" }, { - "string": "system view" + "string": "SYSTEM VIEW" } ] }, diff --git a/internal/endtoend/testdata/analyze_types/clickhouse/stdout.json b/internal/endtoend/testdata/analyze_types/clickhouse/stdout.json index 47068fd88d..9c597de0c0 100644 --- a/internal/endtoend/testdata/analyze_types/clickhouse/stdout.json +++ b/internal/endtoend/testdata/analyze_types/clickhouse/stdout.json @@ -86,11 +86,11 @@ "name": "kind", "type": { "name": "lowcardinality", + "nullable": true, "args": [ { "type": { - "name": "string", - "nullable": true + "name": "string" } } ] @@ -456,11 +456,11 @@ "name": "kind", "type": { "name": "lowcardinality", + "nullable": true, "args": [ { "type": { - "name": "string", - "nullable": true + "name": "string" } } ] diff --git a/internal/endtoend/testdata/analyze_types/duckdb/query.sql b/internal/endtoend/testdata/analyze_types/duckdb/query.sql index e7a62ed31f..3e232d263f 100644 --- a/internal/endtoend/testdata/analyze_types/duckdb/query.sql +++ b/internal/endtoend/testdata/analyze_types/duckdb/query.sql @@ -9,7 +9,8 @@ SELECT $4::mood AS d, CAST($5 AS VARCHAR(5)) AS e, $6::MAP(VARCHAR, INTEGER) AS f, - $7::INTEGER[3] AS g + $7::INTEGER[3] AS g, + $8::main.mood AS h FROM things; -- name: Params :one diff --git a/internal/endtoend/testdata/analyze_types/duckdb/schema.sql b/internal/endtoend/testdata/analyze_types/duckdb/schema.sql index b04a775185..0a8a1b4147 100644 --- a/internal/endtoend/testdata/analyze_types/duckdb/schema.sql +++ b/internal/endtoend/testdata/analyze_types/duckdb/schema.sql @@ -11,6 +11,7 @@ CREATE TABLE things ( title VARCHAR(10), kind ENUM('a','b'), m mood, + mm main.mood, big HUGEINT, ubig UHUGEINT, data BLOB, diff --git a/internal/endtoend/testdata/analyze_types/duckdb/stdout.json b/internal/endtoend/testdata/analyze_types/duckdb/stdout.json index cf525c4744..fff8ac0228 100644 --- a/internal/endtoend/testdata/analyze_types/duckdb/stdout.json +++ b/internal/endtoend/testdata/analyze_types/duckdb/stdout.json @@ -155,6 +155,14 @@ }, "table": "things" }, + { + "name": "mm", + "type": { + "name": "mood", + "nullable": true + }, + "table": "things" + }, { "name": "big", "type": { @@ -432,6 +440,12 @@ } ] } + }, + { + "name": "h", + "type": { + "name": "mood" + } } ], "params": [ @@ -542,6 +556,15 @@ ] } } + }, + { + "number": 8, + "column": { + "name": "", + "type": { + "name": "mood" + } + } } ] }, diff --git a/internal/endtoend/testdata/analyze_types/mssql/query.sql b/internal/endtoend/testdata/analyze_types/mssql/query.sql index 976b5c1070..98d4e849db 100644 --- a/internal/endtoend/testdata/analyze_types/mssql/query.sql +++ b/internal/endtoend/testdata/analyze_types/mssql/query.sql @@ -8,7 +8,8 @@ SELECT CAST(@c AS VARCHAR(10)) AS c, CONVERT(DATETIME2(3), @d) AS d, CAST(@e AS dbo.PhoneNumber) AS e, - TRY_CAST(@f AS FLOAT(24)) AS f + TRY_CAST(@f AS FLOAT(24)) AS f, + CAST(@g AS dbo.Code) AS g FROM things; -- name: Params :one diff --git a/internal/endtoend/testdata/analyze_types/mssql/schema.sql b/internal/endtoend/testdata/analyze_types/mssql/schema.sql index e7106319ef..1e65802777 100644 --- a/internal/endtoend/testdata/analyze_types/mssql/schema.sql +++ b/internal/endtoend/testdata/analyze_types/mssql/schema.sql @@ -1,4 +1,5 @@ CREATE TYPE dbo.PhoneNumber FROM varchar(20) NOT NULL; +CREATE TYPE Code FROM char(3); CREATE TABLE things ( id BIGINT IDENTITY(1,1) PRIMARY KEY, @@ -30,5 +31,7 @@ CREATE TABLE things ( vec VECTOR(3), sn sysname, phone dbo.PhoneNumber, + code2 dbo.Code, + code3 Code, bare VARCHAR ); diff --git a/internal/endtoend/testdata/analyze_types/mssql/stdout.json b/internal/endtoend/testdata/analyze_types/mssql/stdout.json index ec565348b5..e5cad94250 100644 --- a/internal/endtoend/testdata/analyze_types/mssql/stdout.json +++ b/internal/endtoend/testdata/analyze_types/mssql/stdout.json @@ -316,6 +316,22 @@ }, "table": "things" }, + { + "name": "code2", + "type": { + "name": "code", + "nullable": true + }, + "table": "things" + }, + { + "name": "code3", + "type": { + "name": "code", + "nullable": true + }, + "table": "things" + }, { "name": "bare", "type": { @@ -394,6 +410,12 @@ "type": { "name": "real" } + }, + { + "name": "g", + "type": { + "name": "code" + } } ], "params": [ @@ -473,6 +495,15 @@ "name": "real" } } + }, + { + "number": 7, + "column": { + "name": "", + "type": { + "name": "code" + } + } } ] }, diff --git a/internal/endtoend/testdata/analyze_types/mysql/query.sql b/internal/endtoend/testdata/analyze_types/mysql/query.sql index 4f945a8163..3b3c2a4fd1 100644 --- a/internal/endtoend/testdata/analyze_types/mysql/query.sql +++ b/internal/endtoend/testdata/analyze_types/mysql/query.sql @@ -10,7 +10,11 @@ SELECT CAST(doc AS JSON) AS e, CAST(created AS DATETIME(3)) AS f, CAST(price AS DECIMAL) AS g, - CAST(key16 AS BINARY(8)) AS h + CAST(key16 AS BINARY(8)) AS h, + CAST(d AS DOUBLE) AS i, + CAST(f AS FLOAT) AS j, + CAST(price AS DEC(6,1)) AS k, + CAST(price AS DECIMAL(5)) AS l FROM things; -- name: Params :one diff --git a/internal/endtoend/testdata/analyze_types/mysql/schema.sql b/internal/endtoend/testdata/analyze_types/mysql/schema.sql index 96057f5995..880de33be2 100644 --- a/internal/endtoend/testdata/analyze_types/mysql/schema.sql +++ b/internal/endtoend/testdata/analyze_types/mysql/schema.sql @@ -5,6 +5,7 @@ CREATE TABLE things ( price DECIMAL(10,2) NOT NULL, uprice DECIMAL(10,2) UNSIGNED, plain DECIMAL, + dd DEC(7,2), ratio FLOAT(7,4), f FLOAT, title VARCHAR(255), diff --git a/internal/endtoend/testdata/analyze_types/mysql/stdout.json b/internal/endtoend/testdata/analyze_types/mysql/stdout.json index 894e6cc4ec..1e031b3570 100644 --- a/internal/endtoend/testdata/analyze_types/mysql/stdout.json +++ b/internal/endtoend/testdata/analyze_types/mysql/stdout.json @@ -77,6 +77,22 @@ }, "table": "things" }, + { + "name": "dd", + "type": { + "name": "decimal", + "nullable": true, + "args": [ + { + "int": 7 + }, + { + "int": 2 + } + ] + }, + "table": "things" + }, { "name": "ratio", "type": { @@ -395,6 +411,48 @@ } ] } + }, + { + "name": "i", + "type": { + "name": "double", + "nullable": true + } + }, + { + "name": "j", + "type": { + "name": "float", + "nullable": true + } + }, + { + "name": "k", + "type": { + "name": "decimal", + "args": [ + { + "int": 6 + }, + { + "int": 1 + } + ] + } + }, + { + "name": "l", + "type": { + "name": "decimal", + "args": [ + { + "int": 5 + }, + { + "int": 0 + } + ] + } } ], "params": [] diff --git a/internal/endtoend/testdata/analyze_types/postgresql/query.sql b/internal/endtoend/testdata/analyze_types/postgresql/query.sql index b64674872e..ad7746ed08 100644 --- a/internal/endtoend/testdata/analyze_types/postgresql/query.sql +++ b/internal/endtoend/testdata/analyze_types/postgresql/query.sql @@ -13,7 +13,11 @@ SELECT $6::int4[][] AS h, $7::numeric(5,1) AS i, ARRAY[1, 2] AS j, - $8::myschema.mood AS k + $8::myschema.mood AS k, + $9::varchar(10)[] AS l, + $10::interval day to second AS m, + $11::myschema.mood[] AS n, + $12::mood[] AS o FROM things; -- name: Params :one diff --git a/internal/endtoend/testdata/analyze_types/postgresql/schema.sql b/internal/endtoend/testdata/analyze_types/postgresql/schema.sql index 0cbed44721..0db7266d6b 100644 --- a/internal/endtoend/testdata/analyze_types/postgresql/schema.sql +++ b/internal/endtoend/testdata/analyze_types/postgresql/schema.sql @@ -29,6 +29,7 @@ CREATE TABLE things ( iv3 interval(3), m mood, mm myschema.mood, + mms myschema.mood[], p posint, sn shortname, pt point2, diff --git a/internal/endtoend/testdata/analyze_types/postgresql/stdout.json b/internal/endtoend/testdata/analyze_types/postgresql/stdout.json index 484e8a6b31..6091af1641 100644 --- a/internal/endtoend/testdata/analyze_types/postgresql/stdout.json +++ b/internal/endtoend/testdata/analyze_types/postgresql/stdout.json @@ -255,6 +255,21 @@ }, "table": "things" }, + { + "name": "mms", + "type": { + "name": "array", + "nullable": true, + "args": [ + { + "type": { + "name": "myschema.mood" + } + } + ] + }, + "table": "things" + }, { "name": "p", "type": { @@ -511,6 +526,61 @@ "type": { "name": "myschema.mood" } + }, + { + "name": "l", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "character varying", + "args": [ + { + "int": 10 + } + ] + } + } + ] + } + }, + { + "name": "m", + "type": { + "name": "interval", + "args": [ + { + "ident": "day to second" + } + ] + } + }, + { + "name": "n", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "myschema.mood" + } + } + ] + } + }, + { + "name": "o", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "mood" + } + } + ] + } } ], "params": [ @@ -627,6 +697,73 @@ "name": "myschema.mood" } } + }, + { + "number": 9, + "column": { + "name": "", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "character varying", + "args": [ + { + "int": 10 + } + ] + } + } + ] + } + } + }, + { + "number": 10, + "column": { + "name": "", + "type": { + "name": "interval", + "args": [ + { + "ident": "day to second" + } + ] + } + } + }, + { + "number": 11, + "column": { + "name": "", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "myschema.mood" + } + } + ] + } + } + }, + { + "number": 12, + "column": { + "name": "", + "type": { + "name": "array", + "args": [ + { + "type": { + "name": "mood" + } + } + ] + } + } } ] }, diff --git a/internal/endtoend/testdata/codegen_json/gen/codegen.json b/internal/endtoend/testdata/codegen_json/gen/codegen.json index efcef91737..47a6e3a39c 100644 --- a/internal/endtoend/testdata/codegen_json/gen/codegen.json +++ b/internal/endtoend/testdata/codegen_json/gen/codegen.json @@ -2888,7 +2888,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "attoptions", @@ -2914,7 +2914,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "attfdwoptions", @@ -2940,7 +2940,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "attmissingval", @@ -4010,7 +4010,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "comment", @@ -5688,7 +5688,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "reloptions", @@ -5714,7 +5714,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "relpartbound", @@ -6940,7 +6940,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "confkey", @@ -6966,7 +6966,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "conpfeqop", @@ -6992,7 +6992,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "conppeqop", @@ -7018,7 +7018,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "conffeqop", @@ -7044,7 +7044,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "confdelsetcols", @@ -7070,7 +7070,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "conexclop", @@ -7096,7 +7096,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "conbin", @@ -8270,7 +8270,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -8514,7 +8514,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -8810,7 +8810,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -10046,7 +10046,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -10394,7 +10394,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "extcondition", @@ -10420,7 +10420,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -10934,7 +10934,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "fdwoptions", @@ -10960,7 +10960,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -11308,7 +11308,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "srvoptions", @@ -11334,7 +11334,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -11578,7 +11578,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -11666,7 +11666,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -11806,7 +11806,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "user_name", @@ -11832,7 +11832,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "address", @@ -11936,7 +11936,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "error", @@ -12715,9 +12715,9 @@ { "name": "indkey", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", - "length": 2, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -12730,7 +12730,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2" + "name": "int2vector" }, "is_sqlc_slice": false, "embed_table": null, @@ -12741,9 +12741,9 @@ { "name": "indcollation", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -12756,7 +12756,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "oidvector" }, "is_sqlc_slice": false, "embed_table": null, @@ -12767,9 +12767,9 @@ { "name": "indclass", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -12782,7 +12782,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "oidvector" }, "is_sqlc_slice": false, "embed_table": null, @@ -12793,9 +12793,9 @@ { "name": "indoption", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", - "length": 2, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -12808,7 +12808,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2" + "name": "int2vector" }, "is_sqlc_slice": false, "embed_table": null, @@ -13572,7 +13572,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -13972,7 +13972,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -14460,7 +14460,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -15348,7 +15348,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -16844,7 +16844,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -17119,9 +17119,9 @@ { "name": "partattrs", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", - "length": 2, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17134,7 +17134,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2" + "name": "int2vector" }, "is_sqlc_slice": false, "embed_table": null, @@ -17145,9 +17145,9 @@ { "name": "partclass", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17160,7 +17160,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "oidvector" }, "is_sqlc_slice": false, "embed_table": null, @@ -17171,9 +17171,9 @@ { "name": "partcollation", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17186,7 +17186,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "oidvector" }, "is_sqlc_slice": false, "embed_table": null, @@ -17358,7 +17358,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "cmd", @@ -17758,7 +17758,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "polqual", @@ -17924,7 +17924,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "result_types", @@ -17950,7 +17950,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "from_sql", @@ -18833,9 +18833,9 @@ { "name": "proargtypes", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -18848,7 +18848,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "oidvector" }, "is_sqlc_slice": false, "embed_table": null, @@ -18880,7 +18880,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "proargmodes", @@ -18906,7 +18906,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "proargnames", @@ -18932,7 +18932,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "proargdefaults", @@ -18984,7 +18984,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "prosrc", @@ -19088,7 +19088,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "proacl", @@ -19114,7 +19114,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -20033,9 +20033,9 @@ { "name": "prattrs", "not_null": false, - "is_array": true, + "is_array": false, "comment": "", - "length": 2, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -20048,7 +20048,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2" + "name": "int2vector" }, "is_sqlc_slice": false, "embed_table": null, @@ -20168,7 +20168,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "rowfilter", @@ -21996,7 +21996,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "oid", @@ -23642,7 +23642,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "boot_val", @@ -24016,7 +24016,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -38734,7 +38734,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "stanumbers2", @@ -38760,7 +38760,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "stanumbers3", @@ -38786,7 +38786,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "stanumbers4", @@ -38812,7 +38812,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "stanumbers5", @@ -38838,7 +38838,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "stavalues1", @@ -39295,9 +39295,9 @@ { "name": "stxkeys", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", - "length": 2, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -39310,7 +39310,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2" + "name": "int2vector" }, "is_sqlc_slice": false, "embed_table": null, @@ -39342,7 +39342,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "stxexprs", @@ -39690,7 +39690,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -39934,7 +39934,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "histogram_bounds", @@ -40038,7 +40038,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "elem_count_histogram", @@ -40064,7 +40064,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -40230,7 +40230,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "exprs", @@ -40256,7 +40256,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "kinds", @@ -40282,7 +40282,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "inherited", @@ -40386,7 +40386,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "most_common_val_nulls", @@ -40412,7 +40412,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "most_common_freqs", @@ -40438,7 +40438,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "most_common_base_freqs", @@ -40464,7 +40464,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -40786,7 +40786,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "histogram_bounds", @@ -40890,7 +40890,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "elem_count_histogram", @@ -40916,7 +40916,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -41498,7 +41498,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "suborigin", @@ -42282,7 +42282,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "spcoptions", @@ -42308,7 +42308,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -43341,9 +43341,9 @@ { "name": "tgattr", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", - "length": 2, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -43356,7 +43356,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2" + "name": "int2vector" }, "is_sqlc_slice": false, "embed_table": null, @@ -46022,7 +46022,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -46266,7 +46266,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -46536,7 +46536,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -46702,7 +46702,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -46912,7 +46912,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "foreign_data_wrapper_catalog", @@ -47078,7 +47078,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "foreign_server_catalog", @@ -47374,7 +47374,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -47488,7 +47488,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "foreign_server_catalog", @@ -47628,7 +47628,7 @@ "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "umuser", diff --git a/internal/engine/clickhouse/convert.go b/internal/engine/clickhouse/convert.go index 7ef2d7e14d..f1505c0687 100644 --- a/internal/engine/clickhouse/convert.go +++ b/internal/engine/clickhouse/convert.go @@ -1119,6 +1119,16 @@ func renderType(dt *chast.DataType, canonical bool) string { next++ } } + case canonical && lower == "lowcardinality" && len(dt.Parameters) == 1: + // ClickHouse spells a nullable low-cardinality column as + // LowCardinality(Nullable(T)), the only order it accepts. The + // nullability is the column's, so the canonical form is + // Nullable(LowCardinality(T)), which reads as a nullable + // LowCardinality(T). + if inner, ok := dt.Parameters[0].(*chast.DataType); ok && strings.EqualFold(inner.Name, "nullable") && len(inner.Parameters) == 1 { + return "Nullable(LowCardinality(" + renderParam(inner.Parameters[0], canonical) + "))" + } + parts = append(parts, renderParam(dt.Parameters[0], canonical)) case canonical && lower == "variant": for _, p := range dt.Parameters { parts = append(parts, renderParam(p, canonical)) @@ -1175,12 +1185,10 @@ func unwrapTypeString(s string) (name string, isArray, nullable bool) { } return strings.ToLower(base), false, true case "lowcardinality": - // Only a Nullable at the top makes the column nullable: the - // analysis reports LowCardinality(Nullable(String)) with the - // nullability inside, as ClickHouse does. + // LowCardinality is an encoding of the type it wraps, and a + // column of LowCardinality(Nullable(String)) holds NULLs. if len(args) == 1 { - inner, arr, _ := unwrapTypeString(args[0]) - return inner, arr, false + return unwrapTypeString(args[0]) } return strings.ToLower(base), false, false case "array": diff --git a/internal/engine/dolphin/convert.go b/internal/engine/dolphin/convert.go index e834f86f78..4a01a68de6 100644 --- a/internal/engine/dolphin/convert.go +++ b/internal/engine/dolphin/convert.go @@ -1159,14 +1159,20 @@ func (c *cc) convertFuncCastExpr(n *pcast.FuncCastExpr) ast.Node { out := &ast.TypeName{Name: typeName} flen, dec := n.Tp.GetFlen(), n.Tp.GetDecimal() switch tp { - case mysql.TypeNewDecimal, mysql.TypeFloat, mysql.TypeDouble: + case mysql.TypeNewDecimal: + // A decimal's precision and scale are part of its type, and a + // scale left out is 0: CAST(x AS DECIMAL(5)) is a decimal(5,0). if flen >= 0 && flen != types.UnspecifiedLength { mods := []ast.Node{&ast.Integer{Ival: int64(flen)}} - if dec > 0 && dec != types.UnspecifiedLength { + if dec >= 0 && dec != types.UnspecifiedLength { mods = append(mods, &ast.Integer{Ival: int64(dec)}) } out.Typmods = &ast.List{Items: mods} } + case mysql.TypeFloat, mysql.TypeDouble: + // The parser fills in a display width for a float or double, and + // a precision written as FLOAT(p) only picks between the two; the + // result is a plain float or double. case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeString: if flen > 0 && flen != types.UnspecifiedLength { out.Typmods = &ast.List{Items: []ast.Node{&ast.Integer{Ival: int64(flen)}}} diff --git a/internal/engine/dolphin/dialect/relations.jsonl b/internal/engine/dolphin/dialect/relations.jsonl index 8a0e664032..0660804f9c 100644 --- a/internal/engine/dolphin/dialect/relations.jsonl +++ b/internal/engine/dolphin/dialect/relations.jsonl @@ -2,15 +2,15 @@ {"catalog":"def","schema":"information_schema","name":"applicable_roles","kind":"v","columns":[{"name":"user","type":"varchar(97)"},{"name":"host","type":"varchar(256)"},{"name":"grantee","type":"varchar(97)"},{"name":"grantee_host","type":"varchar(256)"},{"name":"role_name","type":"varchar(255)"},{"name":"role_host","type":"varchar(256)"},{"name":"is_grantable","type":"varchar(3)","not_null":true},{"name":"is_default","type":"varchar(3)"},{"name":"is_mandatory","type":"varchar(3)","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"character_sets","kind":"v","columns":[{"name":"character_set_name","type":"varchar(64)","not_null":true},{"name":"default_collate_name","type":"varchar(64)","not_null":true},{"name":"description","type":"varchar(2048)","not_null":true},{"name":"maxlen","type":"int unsigned","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"check_constraints","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar(64)","not_null":true},{"name":"constraint_schema","type":"varchar(64)","not_null":true},{"name":"constraint_name","type":"varchar(64)","not_null":true},{"name":"check_clause","type":"longtext","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"collations","kind":"v","columns":[{"name":"collation_name","type":"varchar(64)","not_null":true},{"name":"character_set_name","type":"varchar(64)","not_null":true},{"name":"id","type":"bigint unsigned","not_null":true},{"name":"is_default","type":"varchar(3)","not_null":true},{"name":"is_compiled","type":"varchar(3)","not_null":true},{"name":"sortlen","type":"int unsigned","not_null":true},{"name":"pad_attribute","type":"enum('pad space','no pad')","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"collations","kind":"v","columns":[{"name":"collation_name","type":"varchar(64)","not_null":true},{"name":"character_set_name","type":"varchar(64)","not_null":true},{"name":"id","type":"bigint unsigned","not_null":true},{"name":"is_default","type":"varchar(3)","not_null":true},{"name":"is_compiled","type":"varchar(3)","not_null":true},{"name":"sortlen","type":"int unsigned","not_null":true},{"name":"pad_attribute","type":"enum('PAD SPACE','NO PAD')","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"collation_character_set_applicability","kind":"v","columns":[{"name":"collation_name","type":"varchar(64)","not_null":true},{"name":"character_set_name","type":"varchar(64)","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"columns","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"column_name","type":"varchar(64)"},{"name":"ordinal_position","type":"int unsigned","not_null":true},{"name":"column_default","type":"text"},{"name":"is_nullable","type":"varchar(3)","not_null":true},{"name":"data_type","type":"longtext"},{"name":"character_maximum_length","type":"bigint"},{"name":"character_octet_length","type":"bigint"},{"name":"numeric_precision","type":"bigint unsigned"},{"name":"numeric_scale","type":"bigint unsigned"},{"name":"datetime_precision","type":"int unsigned"},{"name":"character_set_name","type":"varchar(64)"},{"name":"collation_name","type":"varchar(64)"},{"name":"column_type","type":"mediumtext","not_null":true},{"name":"column_key","type":"enum('','pri','uni','mul')","not_null":true},{"name":"extra","type":"varchar(256)"},{"name":"privileges","type":"varchar(154)"},{"name":"column_comment","type":"text","not_null":true},{"name":"generation_expression","type":"longtext","not_null":true},{"name":"srs_id","type":"int unsigned"}]} +{"catalog":"def","schema":"information_schema","name":"columns","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"column_name","type":"varchar(64)"},{"name":"ordinal_position","type":"int unsigned","not_null":true},{"name":"column_default","type":"text"},{"name":"is_nullable","type":"varchar(3)","not_null":true},{"name":"data_type","type":"longtext"},{"name":"character_maximum_length","type":"bigint"},{"name":"character_octet_length","type":"bigint"},{"name":"numeric_precision","type":"bigint unsigned"},{"name":"numeric_scale","type":"bigint unsigned"},{"name":"datetime_precision","type":"int unsigned"},{"name":"character_set_name","type":"varchar(64)"},{"name":"collation_name","type":"varchar(64)"},{"name":"column_type","type":"mediumtext","not_null":true},{"name":"column_key","type":"enum('','PRI','UNI','MUL')","not_null":true},{"name":"extra","type":"varchar(256)"},{"name":"privileges","type":"varchar(154)"},{"name":"column_comment","type":"text","not_null":true},{"name":"generation_expression","type":"longtext","not_null":true},{"name":"srs_id","type":"int unsigned"}]} {"catalog":"def","schema":"information_schema","name":"columns_extensions","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"column_name","type":"varchar(64)"},{"name":"engine_attribute","type":"json"},{"name":"secondary_engine_attribute","type":"json"}]} {"catalog":"def","schema":"information_schema","name":"column_privileges","kind":"v","columns":[{"name":"grantee","type":"varchar(292)","not_null":true},{"name":"table_catalog","type":"varchar(512)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"column_name","type":"varchar(64)","not_null":true},{"name":"privilege_type","type":"varchar(64)","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"column_statistics","kind":"v","columns":[{"name":"schema_name","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"column_name","type":"varchar(64)","not_null":true},{"name":"histogram","type":"json","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"enabled_roles","kind":"v","columns":[{"name":"role_name","type":"varchar(255)"},{"name":"role_host","type":"varchar(255)"},{"name":"is_default","type":"varchar(3)"},{"name":"is_mandatory","type":"varchar(3)","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"engines","kind":"v","columns":[{"name":"engine","type":"varchar(64)","not_null":true},{"name":"support","type":"varchar(8)","not_null":true},{"name":"comment","type":"varchar(80)","not_null":true},{"name":"transactions","type":"varchar(3)"},{"name":"xa","type":"varchar(3)"},{"name":"savepoints","type":"varchar(3)"}]} -{"catalog":"def","schema":"information_schema","name":"events","kind":"v","columns":[{"name":"event_catalog","type":"varchar(64)","not_null":true},{"name":"event_schema","type":"varchar(64)","not_null":true},{"name":"event_name","type":"varchar(64)","not_null":true},{"name":"definer","type":"varchar(288)","not_null":true},{"name":"time_zone","type":"varchar(64)","not_null":true},{"name":"event_body","type":"varchar(3)","not_null":true},{"name":"event_definition","type":"longtext","not_null":true},{"name":"event_type","type":"varchar(9)","not_null":true},{"name":"execute_at","type":"datetime"},{"name":"interval_value","type":"varchar(256)"},{"name":"interval_field","type":"enum('year','quarter','month','day','hour','minute','week','second','microsecond','year_month','day_hour','day_minute','day_second','hour_minute','hour_second','minute_second','day_microsecond','hour_microsecond','minute_microsecond','second_microsecond')"},{"name":"sql_mode","type":"set('real_as_float','pipes_as_concat','ansi_quotes','ignore_space','not_used','only_full_group_by','no_unsigned_subtraction','no_dir_in_create','not_used_9','not_used_10','not_used_11','not_used_12','not_used_13','not_used_14','not_used_15','not_used_16','not_used_17','not_used_18','ansi','no_auto_value_on_zero','no_backslash_escapes','strict_trans_tables','strict_all_tables','no_zero_in_date','no_zero_date','allow_invalid_dates','error_for_division_by_zero','traditional','not_used_29','high_not_precedence','no_engine_substitution','pad_char_to_full_length','time_truncate_fractional','interpret_utf8_as_utf8mb4')","not_null":true},{"name":"starts","type":"datetime"},{"name":"ends","type":"datetime"},{"name":"status","type":"varchar(21)","not_null":true},{"name":"on_completion","type":"varchar(12)","not_null":true},{"name":"created","type":"timestamp","not_null":true},{"name":"last_altered","type":"timestamp","not_null":true},{"name":"last_executed","type":"datetime"},{"name":"event_comment","type":"varchar(2048)","not_null":true},{"name":"originator","type":"int unsigned","not_null":true},{"name":"character_set_client","type":"varchar(64)","not_null":true},{"name":"collation_connection","type":"varchar(64)","not_null":true},{"name":"database_collation","type":"varchar(64)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"events","kind":"v","columns":[{"name":"event_catalog","type":"varchar(64)","not_null":true},{"name":"event_schema","type":"varchar(64)","not_null":true},{"name":"event_name","type":"varchar(64)","not_null":true},{"name":"definer","type":"varchar(288)","not_null":true},{"name":"time_zone","type":"varchar(64)","not_null":true},{"name":"event_body","type":"varchar(3)","not_null":true},{"name":"event_definition","type":"longtext","not_null":true},{"name":"event_type","type":"varchar(9)","not_null":true},{"name":"execute_at","type":"datetime"},{"name":"interval_value","type":"varchar(256)"},{"name":"interval_field","type":"enum('YEAR','QUARTER','MONTH','DAY','HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR','DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND','DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND','SECOND_MICROSECOND')"},{"name":"sql_mode","type":"set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','NOT_USED_9','NOT_USED_10','NOT_USED_11','NOT_USED_12','NOT_USED_13','NOT_USED_14','NOT_USED_15','NOT_USED_16','NOT_USED_17','NOT_USED_18','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','ALLOW_INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NOT_USED_29','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','TIME_TRUNCATE_FRACTIONAL','INTERPRET_UTF8_AS_UTF8MB4')","not_null":true},{"name":"starts","type":"datetime"},{"name":"ends","type":"datetime"},{"name":"status","type":"varchar(21)","not_null":true},{"name":"on_completion","type":"varchar(12)","not_null":true},{"name":"created","type":"timestamp","not_null":true},{"name":"last_altered","type":"timestamp","not_null":true},{"name":"last_executed","type":"datetime"},{"name":"event_comment","type":"varchar(2048)","not_null":true},{"name":"originator","type":"int unsigned","not_null":true},{"name":"character_set_client","type":"varchar(64)","not_null":true},{"name":"collation_connection","type":"varchar(64)","not_null":true},{"name":"database_collation","type":"varchar(64)","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"files","kind":"v","columns":[{"name":"file_id","type":"bigint"},{"name":"file_name","type":"text"},{"name":"file_type","type":"varchar(256)"},{"name":"tablespace_name","type":"varchar(268)","not_null":true},{"name":"table_catalog","type":"varchar(0)","not_null":true},{"name":"table_schema","type":"varbinary(0)"},{"name":"table_name","type":"varbinary(0)"},{"name":"logfile_group_name","type":"varchar(256)"},{"name":"logfile_group_number","type":"bigint"},{"name":"engine","type":"varchar(64)","not_null":true},{"name":"fulltext_keys","type":"varbinary(0)"},{"name":"deleted_rows","type":"varbinary(0)"},{"name":"update_count","type":"varbinary(0)"},{"name":"free_extents","type":"bigint"},{"name":"total_extents","type":"bigint"},{"name":"extent_size","type":"bigint"},{"name":"initial_size","type":"bigint"},{"name":"maximum_size","type":"bigint"},{"name":"autoextend_size","type":"bigint"},{"name":"creation_time","type":"varbinary(0)"},{"name":"last_update_time","type":"varbinary(0)"},{"name":"last_access_time","type":"varbinary(0)"},{"name":"recover_time","type":"varbinary(0)"},{"name":"transaction_counter","type":"varbinary(0)"},{"name":"version","type":"bigint"},{"name":"row_format","type":"varchar(256)"},{"name":"table_rows","type":"varbinary(0)"},{"name":"avg_row_length","type":"varbinary(0)"},{"name":"data_length","type":"varbinary(0)"},{"name":"max_data_length","type":"varbinary(0)"},{"name":"index_length","type":"varbinary(0)"},{"name":"data_free","type":"bigint"},{"name":"create_time","type":"varbinary(0)"},{"name":"update_time","type":"varbinary(0)"},{"name":"check_time","type":"varbinary(0)"},{"name":"checksum","type":"varbinary(0)"},{"name":"status","type":"varchar(256)"},{"name":"extra","type":"varchar(256)"}]} {"catalog":"def","schema":"information_schema","name":"innodb_buffer_page","kind":"v","columns":[{"name":"pool_id","type":"bigint unsigned","not_null":true},{"name":"block_id","type":"bigint unsigned","not_null":true},{"name":"space","type":"bigint unsigned","not_null":true},{"name":"page_number","type":"bigint unsigned","not_null":true},{"name":"page_type","type":"varchar(64)"},{"name":"flush_type","type":"bigint unsigned","not_null":true},{"name":"fix_count","type":"bigint unsigned","not_null":true},{"name":"is_hashed","type":"varchar(3)"},{"name":"newest_modification","type":"bigint unsigned","not_null":true},{"name":"oldest_modification","type":"bigint unsigned","not_null":true},{"name":"access_time","type":"bigint unsigned","not_null":true},{"name":"table_name","type":"varchar(1024)"},{"name":"index_name","type":"varchar(1024)"},{"name":"number_records","type":"bigint unsigned","not_null":true},{"name":"data_size","type":"bigint unsigned","not_null":true},{"name":"compressed_size","type":"bigint unsigned","not_null":true},{"name":"page_state","type":"varchar(64)"},{"name":"io_fix","type":"varchar(64)"},{"name":"is_old","type":"varchar(3)"},{"name":"free_page_clock","type":"bigint unsigned","not_null":true},{"name":"is_stale","type":"varchar(3)"}]} {"catalog":"def","schema":"information_schema","name":"innodb_buffer_page_lru","kind":"v","columns":[{"name":"pool_id","type":"bigint unsigned","not_null":true},{"name":"lru_position","type":"bigint unsigned","not_null":true},{"name":"space","type":"bigint unsigned","not_null":true},{"name":"page_number","type":"bigint unsigned","not_null":true},{"name":"page_type","type":"varchar(64)"},{"name":"flush_type","type":"bigint unsigned","not_null":true},{"name":"fix_count","type":"bigint unsigned","not_null":true},{"name":"is_hashed","type":"varchar(3)"},{"name":"newest_modification","type":"bigint unsigned","not_null":true},{"name":"oldest_modification","type":"bigint unsigned","not_null":true},{"name":"access_time","type":"bigint unsigned","not_null":true},{"name":"table_name","type":"varchar(1024)"},{"name":"index_name","type":"varchar(1024)"},{"name":"number_records","type":"bigint unsigned","not_null":true},{"name":"data_size","type":"bigint unsigned","not_null":true},{"name":"compressed_size","type":"bigint unsigned","not_null":true},{"name":"compressed","type":"varchar(3)"},{"name":"io_fix","type":"varchar(64)"},{"name":"is_old","type":"varchar(3)"},{"name":"free_page_clock","type":"bigint unsigned","not_null":true}]} @@ -49,36 +49,36 @@ {"catalog":"def","schema":"information_schema","name":"json_duality_view_tables","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"referenced_table_catalog","type":"varchar(64)"},{"name":"referenced_table_schema","type":"varchar(64)"},{"name":"referenced_table_name","type":"varchar(64)"},{"name":"where_clause","type":"varchar(64)"},{"name":"allow_insert","type":"tinyint"},{"name":"allow_update","type":"tinyint"},{"name":"allow_delete","type":"tinyint"},{"name":"read_only","type":"tinyint"},{"name":"is_root_table","type":"tinyint"},{"name":"referenced_table_id","type":"int"},{"name":"referenced_table_parent_id","type":"int"},{"name":"referenced_table_parent_relationship","type":"varchar(64)"}]} {"catalog":"def","schema":"information_schema","name":"keywords","kind":"v","columns":[{"name":"word","type":"varchar(128)"},{"name":"reserved","type":"int"}]} {"catalog":"def","schema":"information_schema","name":"key_column_usage","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar(64)","not_null":true},{"name":"constraint_schema","type":"varchar(64)","not_null":true},{"name":"constraint_name","type":"varchar(64)"},{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"column_name","type":"varchar(64)"},{"name":"ordinal_position","type":"int unsigned","not_null":true},{"name":"position_in_unique_constraint","type":"int unsigned"},{"name":"referenced_table_schema","type":"varchar(64)"},{"name":"referenced_table_name","type":"varchar(64)"},{"name":"referenced_column_name","type":"varchar(64)"}]} -{"catalog":"def","schema":"information_schema","name":"libraries","kind":"v","columns":[{"name":"library_catalog","type":"varchar(64)","not_null":true},{"name":"library_schema","type":"varchar(64)","not_null":true},{"name":"library_name","type":"varchar(64)","not_null":true},{"name":"library_definition","type":"longtext"},{"name":"language","type":"varchar(64)","not_null":true},{"name":"created","type":"timestamp","not_null":true},{"name":"last_altered","type":"timestamp","not_null":true},{"name":"sql_mode","type":"set('real_as_float','pipes_as_concat','ansi_quotes','ignore_space','not_used','only_full_group_by','no_unsigned_subtraction','no_dir_in_create','not_used_9','not_used_10','not_used_11','not_used_12','not_used_13','not_used_14','not_used_15','not_used_16','not_used_17','not_used_18','ansi','no_auto_value_on_zero','no_backslash_escapes','strict_trans_tables','strict_all_tables','no_zero_in_date','no_zero_date','allow_invalid_dates','error_for_division_by_zero','traditional','not_used_29','high_not_precedence','no_engine_substitution','pad_char_to_full_length','time_truncate_fractional','interpret_utf8_as_utf8mb4')","not_null":true},{"name":"library_comment","type":"text","not_null":true},{"name":"creator","type":"varchar(288)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"libraries","kind":"v","columns":[{"name":"library_catalog","type":"varchar(64)","not_null":true},{"name":"library_schema","type":"varchar(64)","not_null":true},{"name":"library_name","type":"varchar(64)","not_null":true},{"name":"library_definition","type":"longtext"},{"name":"language","type":"varchar(64)","not_null":true},{"name":"created","type":"timestamp","not_null":true},{"name":"last_altered","type":"timestamp","not_null":true},{"name":"sql_mode","type":"set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','NOT_USED_9','NOT_USED_10','NOT_USED_11','NOT_USED_12','NOT_USED_13','NOT_USED_14','NOT_USED_15','NOT_USED_16','NOT_USED_17','NOT_USED_18','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','ALLOW_INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NOT_USED_29','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','TIME_TRUNCATE_FRACTIONAL','INTERPRET_UTF8_AS_UTF8MB4')","not_null":true},{"name":"library_comment","type":"text","not_null":true},{"name":"creator","type":"varchar(288)","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"optimizer_trace","kind":"v","columns":[{"name":"query","type":"varchar(65535)","not_null":true},{"name":"trace","type":"varchar(65535)","not_null":true},{"name":"missing_bytes_beyond_max_mem_size","type":"int","not_null":true},{"name":"insufficient_privileges","type":"tinyint(1)","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"parameters","kind":"v","columns":[{"name":"specific_catalog","type":"varchar(64)","not_null":true},{"name":"specific_schema","type":"varchar(64)","not_null":true},{"name":"specific_name","type":"varchar(64)","not_null":true},{"name":"ordinal_position","type":"bigint unsigned","not_null":true},{"name":"parameter_mode","type":"varchar(5)"},{"name":"parameter_name","type":"varchar(64)"},{"name":"data_type","type":"longtext"},{"name":"character_maximum_length","type":"bigint"},{"name":"character_octet_length","type":"bigint"},{"name":"numeric_precision","type":"int unsigned"},{"name":"numeric_scale","type":"bigint"},{"name":"datetime_precision","type":"int unsigned"},{"name":"character_set_name","type":"varchar(64)"},{"name":"collation_name","type":"varchar(64)"},{"name":"dtd_identifier","type":"mediumtext","not_null":true},{"name":"routine_type","type":"enum('function','procedure','library')","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"parameters","kind":"v","columns":[{"name":"specific_catalog","type":"varchar(64)","not_null":true},{"name":"specific_schema","type":"varchar(64)","not_null":true},{"name":"specific_name","type":"varchar(64)","not_null":true},{"name":"ordinal_position","type":"bigint unsigned","not_null":true},{"name":"parameter_mode","type":"varchar(5)"},{"name":"parameter_name","type":"varchar(64)"},{"name":"data_type","type":"longtext"},{"name":"character_maximum_length","type":"bigint"},{"name":"character_octet_length","type":"bigint"},{"name":"numeric_precision","type":"int unsigned"},{"name":"numeric_scale","type":"bigint"},{"name":"datetime_precision","type":"int unsigned"},{"name":"character_set_name","type":"varchar(64)"},{"name":"collation_name","type":"varchar(64)"},{"name":"dtd_identifier","type":"mediumtext","not_null":true},{"name":"routine_type","type":"enum('FUNCTION','PROCEDURE','LIBRARY')","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"partitions","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"partition_name","type":"varchar(64)"},{"name":"subpartition_name","type":"varchar(64)"},{"name":"partition_ordinal_position","type":"int unsigned"},{"name":"subpartition_ordinal_position","type":"int unsigned"},{"name":"secondary_load","type":"varchar(1)"},{"name":"partition_method","type":"varchar(13)"},{"name":"subpartition_method","type":"varchar(13)"},{"name":"partition_expression","type":"varchar(2048)"},{"name":"subpartition_expression","type":"varchar(2048)"},{"name":"partition_description","type":"text"},{"name":"table_rows","type":"bigint unsigned"},{"name":"avg_row_length","type":"bigint unsigned"},{"name":"data_length","type":"bigint unsigned"},{"name":"max_data_length","type":"bigint unsigned"},{"name":"index_length","type":"bigint unsigned"},{"name":"data_free","type":"bigint unsigned"},{"name":"create_time","type":"timestamp","not_null":true},{"name":"update_time","type":"datetime"},{"name":"check_time","type":"datetime"},{"name":"checksum","type":"bigint"},{"name":"partition_comment","type":"text","not_null":true},{"name":"nodegroup","type":"varchar(256)"},{"name":"tablespace_name","type":"varchar(268)"}]} {"catalog":"def","schema":"information_schema","name":"plugins","kind":"v","columns":[{"name":"plugin_name","type":"varchar(64)","not_null":true},{"name":"plugin_version","type":"varchar(20)","not_null":true},{"name":"plugin_status","type":"varchar(10)","not_null":true},{"name":"plugin_type","type":"varchar(80)","not_null":true},{"name":"plugin_type_version","type":"varchar(20)","not_null":true},{"name":"plugin_library","type":"varchar(64)"},{"name":"plugin_library_version","type":"varchar(20)"},{"name":"plugin_author","type":"varchar(64)"},{"name":"plugin_description","type":"varchar(65535)"},{"name":"plugin_license","type":"varchar(80)"},{"name":"load_option","type":"varchar(64)","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"processlist","kind":"v","columns":[{"name":"id","type":"bigint unsigned","not_null":true},{"name":"user","type":"varchar(32)","not_null":true},{"name":"host","type":"varchar(261)","not_null":true},{"name":"db","type":"varchar(64)"},{"name":"command","type":"varchar(16)","not_null":true},{"name":"time","type":"int","not_null":true},{"name":"state","type":"varchar(64)"},{"name":"info","type":"varchar(65535)"}]} {"catalog":"def","schema":"information_schema","name":"profiling","kind":"v","columns":[{"name":"query_id","type":"int","not_null":true},{"name":"seq","type":"int","not_null":true},{"name":"state","type":"varchar(30)","not_null":true},{"name":"duration","type":"decimal(905,0)","not_null":true},{"name":"cpu_user","type":"decimal(905,0)"},{"name":"cpu_system","type":"decimal(905,0)"},{"name":"context_voluntary","type":"int"},{"name":"context_involuntary","type":"int"},{"name":"block_ops_in","type":"int"},{"name":"block_ops_out","type":"int"},{"name":"messages_sent","type":"int"},{"name":"messages_received","type":"int"},{"name":"page_faults_major","type":"int"},{"name":"page_faults_minor","type":"int"},{"name":"swaps","type":"int"},{"name":"source_function","type":"varchar(30)"},{"name":"source_file","type":"varchar(20)"},{"name":"source_line","type":"int"}]} -{"catalog":"def","schema":"information_schema","name":"referential_constraints","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar(64)","not_null":true},{"name":"constraint_schema","type":"varchar(64)","not_null":true},{"name":"constraint_name","type":"varchar(64)"},{"name":"unique_constraint_catalog","type":"varchar(64)","not_null":true},{"name":"unique_constraint_schema","type":"varchar(64)","not_null":true},{"name":"unique_constraint_name","type":"varchar(64)"},{"name":"match_option","type":"enum('none','partial','full')","not_null":true},{"name":"update_rule","type":"enum('no action','restrict','cascade','set null','set default')","not_null":true},{"name":"delete_rule","type":"enum('no action','restrict','cascade','set null','set default')","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"referenced_table_name","type":"varchar(64)","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"resource_groups","kind":"v","columns":[{"name":"resource_group_name","type":"varchar(64)","not_null":true},{"name":"resource_group_type","type":"enum('system','user')","not_null":true},{"name":"resource_group_enabled","type":"tinyint(1)","not_null":true},{"name":"vcpu_ids","type":"blob"},{"name":"thread_priority","type":"int","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"role_column_grants","kind":"v","columns":[{"name":"grantor","type":"varchar(97)"},{"name":"grantor_host","type":"varchar(256)"},{"name":"grantee","type":"char(32)","not_null":true},{"name":"grantee_host","type":"char(255)","not_null":true},{"name":"table_catalog","type":"varchar(3)","not_null":true},{"name":"table_schema","type":"char(64)","not_null":true},{"name":"table_name","type":"char(64)","not_null":true},{"name":"column_name","type":"char(64)","not_null":true},{"name":"privilege_type","type":"set('select','insert','update','references')","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"role_routine_grants","kind":"v","columns":[{"name":"grantor","type":"varchar(97)"},{"name":"grantor_host","type":"varchar(256)"},{"name":"grantee","type":"char(32)","not_null":true},{"name":"grantee_host","type":"char(255)","not_null":true},{"name":"specific_catalog","type":"varchar(3)","not_null":true},{"name":"specific_schema","type":"char(64)","not_null":true},{"name":"specific_name","type":"char(64)","not_null":true},{"name":"routine_catalog","type":"varchar(3)","not_null":true},{"name":"routine_schema","type":"char(64)","not_null":true},{"name":"routine_name","type":"char(64)","not_null":true},{"name":"privilege_type","type":"set('execute','alter routine','grant')","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"role_table_grants","kind":"v","columns":[{"name":"grantor","type":"varchar(97)"},{"name":"grantor_host","type":"varchar(256)"},{"name":"grantee","type":"char(32)","not_null":true},{"name":"grantee_host","type":"char(255)","not_null":true},{"name":"table_catalog","type":"varchar(3)","not_null":true},{"name":"table_schema","type":"char(64)","not_null":true},{"name":"table_name","type":"char(64)","not_null":true},{"name":"privilege_type","type":"set('select','insert','update','delete','create','drop','grant','references','index','alter','create view','show view','trigger')","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"routines","kind":"v","columns":[{"name":"specific_name","type":"varchar(64)","not_null":true},{"name":"routine_catalog","type":"varchar(64)","not_null":true},{"name":"routine_schema","type":"varchar(64)","not_null":true},{"name":"routine_name","type":"varchar(64)","not_null":true},{"name":"routine_type","type":"enum('function','procedure','library')","not_null":true},{"name":"data_type","type":"longtext"},{"name":"character_maximum_length","type":"bigint"},{"name":"character_octet_length","type":"bigint"},{"name":"numeric_precision","type":"int unsigned"},{"name":"numeric_scale","type":"int unsigned"},{"name":"datetime_precision","type":"int unsigned"},{"name":"character_set_name","type":"varchar(64)"},{"name":"collation_name","type":"varchar(64)"},{"name":"dtd_identifier","type":"longtext"},{"name":"routine_body","type":"varchar(8)","not_null":true},{"name":"routine_definition","type":"longtext"},{"name":"external_name","type":"varbinary(0)"},{"name":"external_language","type":"varchar(64)","not_null":true},{"name":"parameter_style","type":"varchar(3)","not_null":true},{"name":"is_deterministic","type":"varchar(3)","not_null":true},{"name":"sql_data_access","type":"enum('contains sql','no sql','reads sql data','modifies sql data')","not_null":true},{"name":"sql_path","type":"varbinary(0)"},{"name":"security_type","type":"enum('default','invoker','definer')","not_null":true},{"name":"created","type":"timestamp","not_null":true},{"name":"last_altered","type":"timestamp","not_null":true},{"name":"sql_mode","type":"set('real_as_float','pipes_as_concat','ansi_quotes','ignore_space','not_used','only_full_group_by','no_unsigned_subtraction','no_dir_in_create','not_used_9','not_used_10','not_used_11','not_used_12','not_used_13','not_used_14','not_used_15','not_used_16','not_used_17','not_used_18','ansi','no_auto_value_on_zero','no_backslash_escapes','strict_trans_tables','strict_all_tables','no_zero_in_date','no_zero_date','allow_invalid_dates','error_for_division_by_zero','traditional','not_used_29','high_not_precedence','no_engine_substitution','pad_char_to_full_length','time_truncate_fractional','interpret_utf8_as_utf8mb4')","not_null":true},{"name":"routine_comment","type":"text","not_null":true},{"name":"definer","type":"varchar(288)","not_null":true},{"name":"character_set_client","type":"varchar(64)","not_null":true},{"name":"collation_connection","type":"varchar(64)","not_null":true},{"name":"database_collation","type":"varchar(64)","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"routine_libraries","kind":"v","columns":[{"name":"routine_catalog","type":"varchar(64)","not_null":true},{"name":"routine_schema","type":"varchar(64)","not_null":true},{"name":"routine_name","type":"varchar(64)","not_null":true},{"name":"routine_type","type":"enum('function','procedure','library')","not_null":true},{"name":"library_catalog","type":"varchar(64)"},{"name":"library_schema","type":"varchar(100)"},{"name":"library_name","type":"varchar(100)"},{"name":"library_version","type":"varchar(100)"}]} -{"catalog":"def","schema":"information_schema","name":"schemata","kind":"v","columns":[{"name":"catalog_name","type":"varchar(64)","not_null":true},{"name":"schema_name","type":"varchar(64)","not_null":true},{"name":"default_character_set_name","type":"varchar(64)","not_null":true},{"name":"default_collation_name","type":"varchar(64)","not_null":true},{"name":"sql_path","type":"varbinary(0)"},{"name":"default_encryption","type":"enum('no','yes')","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"referential_constraints","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar(64)","not_null":true},{"name":"constraint_schema","type":"varchar(64)","not_null":true},{"name":"constraint_name","type":"varchar(64)"},{"name":"unique_constraint_catalog","type":"varchar(64)","not_null":true},{"name":"unique_constraint_schema","type":"varchar(64)","not_null":true},{"name":"unique_constraint_name","type":"varchar(64)"},{"name":"match_option","type":"enum('NONE','PARTIAL','FULL')","not_null":true},{"name":"update_rule","type":"enum('NO ACTION','RESTRICT','CASCADE','SET NULL','SET DEFAULT')","not_null":true},{"name":"delete_rule","type":"enum('NO ACTION','RESTRICT','CASCADE','SET NULL','SET DEFAULT')","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"referenced_table_name","type":"varchar(64)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"resource_groups","kind":"v","columns":[{"name":"resource_group_name","type":"varchar(64)","not_null":true},{"name":"resource_group_type","type":"enum('SYSTEM','USER')","not_null":true},{"name":"resource_group_enabled","type":"tinyint(1)","not_null":true},{"name":"vcpu_ids","type":"blob"},{"name":"thread_priority","type":"int","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"role_column_grants","kind":"v","columns":[{"name":"grantor","type":"varchar(97)"},{"name":"grantor_host","type":"varchar(256)"},{"name":"grantee","type":"char(32)","not_null":true},{"name":"grantee_host","type":"char(255)","not_null":true},{"name":"table_catalog","type":"varchar(3)","not_null":true},{"name":"table_schema","type":"char(64)","not_null":true},{"name":"table_name","type":"char(64)","not_null":true},{"name":"column_name","type":"char(64)","not_null":true},{"name":"privilege_type","type":"set('Select','Insert','Update','References')","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"role_routine_grants","kind":"v","columns":[{"name":"grantor","type":"varchar(97)"},{"name":"grantor_host","type":"varchar(256)"},{"name":"grantee","type":"char(32)","not_null":true},{"name":"grantee_host","type":"char(255)","not_null":true},{"name":"specific_catalog","type":"varchar(3)","not_null":true},{"name":"specific_schema","type":"char(64)","not_null":true},{"name":"specific_name","type":"char(64)","not_null":true},{"name":"routine_catalog","type":"varchar(3)","not_null":true},{"name":"routine_schema","type":"char(64)","not_null":true},{"name":"routine_name","type":"char(64)","not_null":true},{"name":"privilege_type","type":"set('Execute','Alter Routine','Grant')","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"role_table_grants","kind":"v","columns":[{"name":"grantor","type":"varchar(97)"},{"name":"grantor_host","type":"varchar(256)"},{"name":"grantee","type":"char(32)","not_null":true},{"name":"grantee_host","type":"char(255)","not_null":true},{"name":"table_catalog","type":"varchar(3)","not_null":true},{"name":"table_schema","type":"char(64)","not_null":true},{"name":"table_name","type":"char(64)","not_null":true},{"name":"privilege_type","type":"set('Select','Insert','Update','Delete','Create','Drop','Grant','References','Index','Alter','Create View','Show view','Trigger')","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"routines","kind":"v","columns":[{"name":"specific_name","type":"varchar(64)","not_null":true},{"name":"routine_catalog","type":"varchar(64)","not_null":true},{"name":"routine_schema","type":"varchar(64)","not_null":true},{"name":"routine_name","type":"varchar(64)","not_null":true},{"name":"routine_type","type":"enum('FUNCTION','PROCEDURE','LIBRARY')","not_null":true},{"name":"data_type","type":"longtext"},{"name":"character_maximum_length","type":"bigint"},{"name":"character_octet_length","type":"bigint"},{"name":"numeric_precision","type":"int unsigned"},{"name":"numeric_scale","type":"int unsigned"},{"name":"datetime_precision","type":"int unsigned"},{"name":"character_set_name","type":"varchar(64)"},{"name":"collation_name","type":"varchar(64)"},{"name":"dtd_identifier","type":"longtext"},{"name":"routine_body","type":"varchar(8)","not_null":true},{"name":"routine_definition","type":"longtext"},{"name":"external_name","type":"varbinary(0)"},{"name":"external_language","type":"varchar(64)","not_null":true},{"name":"parameter_style","type":"varchar(3)","not_null":true},{"name":"is_deterministic","type":"varchar(3)","not_null":true},{"name":"sql_data_access","type":"enum('CONTAINS SQL','NO SQL','READS SQL DATA','MODIFIES SQL DATA')","not_null":true},{"name":"sql_path","type":"varbinary(0)"},{"name":"security_type","type":"enum('DEFAULT','INVOKER','DEFINER')","not_null":true},{"name":"created","type":"timestamp","not_null":true},{"name":"last_altered","type":"timestamp","not_null":true},{"name":"sql_mode","type":"set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','NOT_USED_9','NOT_USED_10','NOT_USED_11','NOT_USED_12','NOT_USED_13','NOT_USED_14','NOT_USED_15','NOT_USED_16','NOT_USED_17','NOT_USED_18','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','ALLOW_INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NOT_USED_29','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','TIME_TRUNCATE_FRACTIONAL','INTERPRET_UTF8_AS_UTF8MB4')","not_null":true},{"name":"routine_comment","type":"text","not_null":true},{"name":"definer","type":"varchar(288)","not_null":true},{"name":"character_set_client","type":"varchar(64)","not_null":true},{"name":"collation_connection","type":"varchar(64)","not_null":true},{"name":"database_collation","type":"varchar(64)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"routine_libraries","kind":"v","columns":[{"name":"routine_catalog","type":"varchar(64)","not_null":true},{"name":"routine_schema","type":"varchar(64)","not_null":true},{"name":"routine_name","type":"varchar(64)","not_null":true},{"name":"routine_type","type":"enum('FUNCTION','PROCEDURE','LIBRARY')","not_null":true},{"name":"library_catalog","type":"varchar(64)"},{"name":"library_schema","type":"varchar(100)"},{"name":"library_name","type":"varchar(100)"},{"name":"library_version","type":"varchar(100)"}]} +{"catalog":"def","schema":"information_schema","name":"schemata","kind":"v","columns":[{"name":"catalog_name","type":"varchar(64)","not_null":true},{"name":"schema_name","type":"varchar(64)","not_null":true},{"name":"default_character_set_name","type":"varchar(64)","not_null":true},{"name":"default_collation_name","type":"varchar(64)","not_null":true},{"name":"sql_path","type":"varbinary(0)"},{"name":"default_encryption","type":"enum('NO','YES')","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"schemata_extensions","kind":"v","columns":[{"name":"catalog_name","type":"varchar(64)","not_null":true},{"name":"schema_name","type":"varchar(64)","not_null":true},{"name":"options","type":"varchar(256)"}]} {"catalog":"def","schema":"information_schema","name":"schema_privileges","kind":"v","columns":[{"name":"grantee","type":"varchar(292)","not_null":true},{"name":"table_catalog","type":"varchar(512)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"privilege_type","type":"varchar(64)","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"statistics","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"non_unique","type":"int","not_null":true},{"name":"index_schema","type":"varchar(64)","not_null":true},{"name":"index_name","type":"varchar(64)"},{"name":"seq_in_index","type":"int unsigned","not_null":true},{"name":"column_name","type":"varchar(64)"},{"name":"collation","type":"varchar(1)"},{"name":"cardinality","type":"bigint"},{"name":"sub_part","type":"bigint"},{"name":"packed","type":"varbinary(0)"},{"name":"nullable","type":"varchar(3)","not_null":true},{"name":"index_type","type":"varchar(11)","not_null":true},{"name":"comment","type":"varchar(8)","not_null":true},{"name":"index_comment","type":"varchar(2048)","not_null":true},{"name":"is_visible","type":"varchar(3)","not_null":true},{"name":"expression","type":"longtext"}]} {"catalog":"def","schema":"information_schema","name":"st_geometry_columns","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"column_name","type":"varchar(64)"},{"name":"srs_name","type":"varchar(80)"},{"name":"srs_id","type":"int unsigned"},{"name":"geometry_type_name","type":"longtext"}]} {"catalog":"def","schema":"information_schema","name":"st_spatial_reference_systems","kind":"v","columns":[{"name":"srs_name","type":"varchar(80)","not_null":true},{"name":"srs_id","type":"int unsigned","not_null":true},{"name":"organization","type":"varchar(256)"},{"name":"organization_coordsys_id","type":"int unsigned"},{"name":"definition","type":"varchar(4096)","not_null":true},{"name":"description","type":"varchar(2048)"}]} {"catalog":"def","schema":"information_schema","name":"st_units_of_measure","kind":"v","columns":[{"name":"unit_name","type":"varchar(255)"},{"name":"unit_type","type":"varchar(7)"},{"name":"conversion_factor","type":"double"},{"name":"description","type":"varchar(255)"}]} -{"catalog":"def","schema":"information_schema","name":"tables","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"table_type","type":"enum('base table','view','system view')","not_null":true},{"name":"engine","type":"varchar(64)"},{"name":"version","type":"int"},{"name":"row_format","type":"enum('fixed','dynamic','compressed','redundant','compact','paged')"},{"name":"table_rows","type":"bigint unsigned"},{"name":"avg_row_length","type":"bigint unsigned"},{"name":"data_length","type":"bigint unsigned"},{"name":"max_data_length","type":"bigint unsigned"},{"name":"index_length","type":"bigint unsigned"},{"name":"data_free","type":"bigint unsigned"},{"name":"auto_increment","type":"bigint unsigned"},{"name":"create_time","type":"timestamp","not_null":true},{"name":"update_time","type":"datetime"},{"name":"check_time","type":"datetime"},{"name":"table_collation","type":"varchar(64)"},{"name":"checksum","type":"bigint"},{"name":"create_options","type":"varchar(256)"},{"name":"table_comment","type":"text"}]} +{"catalog":"def","schema":"information_schema","name":"tables","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"table_type","type":"enum('BASE TABLE','VIEW','SYSTEM VIEW')","not_null":true},{"name":"engine","type":"varchar(64)"},{"name":"version","type":"int"},{"name":"row_format","type":"enum('Fixed','Dynamic','Compressed','Redundant','Compact','Paged')"},{"name":"table_rows","type":"bigint unsigned"},{"name":"avg_row_length","type":"bigint unsigned"},{"name":"data_length","type":"bigint unsigned"},{"name":"max_data_length","type":"bigint unsigned"},{"name":"index_length","type":"bigint unsigned"},{"name":"data_free","type":"bigint unsigned"},{"name":"auto_increment","type":"bigint unsigned"},{"name":"create_time","type":"timestamp","not_null":true},{"name":"update_time","type":"datetime"},{"name":"check_time","type":"datetime"},{"name":"table_collation","type":"varchar(64)"},{"name":"checksum","type":"bigint"},{"name":"create_options","type":"varchar(256)"},{"name":"table_comment","type":"text"}]} {"catalog":"def","schema":"information_schema","name":"tablespaces_extensions","kind":"v","columns":[{"name":"tablespace_name","type":"varchar(268)","not_null":true},{"name":"engine_attribute","type":"json"}]} {"catalog":"def","schema":"information_schema","name":"tables_extensions","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"engine_attribute","type":"json"},{"name":"secondary_engine_attribute","type":"json"}]} {"catalog":"def","schema":"information_schema","name":"table_constraints","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar(64)","not_null":true},{"name":"constraint_schema","type":"varchar(64)","not_null":true},{"name":"constraint_name","type":"varchar(64)"},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"constraint_type","type":"varchar(11)","not_null":true},{"name":"enforced","type":"varchar(3)","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"table_constraints_extensions","kind":"v","columns":[{"name":"constraint_catalog","type":"varchar(64)","not_null":true},{"name":"constraint_schema","type":"varchar(64)","not_null":true},{"name":"constraint_name","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"engine_attribute","type":"json"},{"name":"secondary_engine_attribute","type":"json"}]} {"catalog":"def","schema":"information_schema","name":"table_privileges","kind":"v","columns":[{"name":"grantee","type":"varchar(292)","not_null":true},{"name":"table_catalog","type":"varchar(512)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"privilege_type","type":"varchar(64)","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"triggers","kind":"v","columns":[{"name":"trigger_catalog","type":"varchar(64)","not_null":true},{"name":"trigger_schema","type":"varchar(64)","not_null":true},{"name":"trigger_name","type":"varchar(64)","not_null":true},{"name":"event_manipulation","type":"enum('insert','update','delete')","not_null":true},{"name":"event_object_catalog","type":"varchar(64)","not_null":true},{"name":"event_object_schema","type":"varchar(64)","not_null":true},{"name":"event_object_table","type":"varchar(64)","not_null":true},{"name":"action_order","type":"int unsigned","not_null":true},{"name":"action_condition","type":"varbinary(0)"},{"name":"action_statement","type":"longtext","not_null":true},{"name":"action_orientation","type":"varchar(3)","not_null":true},{"name":"action_timing","type":"enum('before','after')","not_null":true},{"name":"action_reference_old_table","type":"varbinary(0)"},{"name":"action_reference_new_table","type":"varbinary(0)"},{"name":"action_reference_old_row","type":"varchar(3)","not_null":true},{"name":"action_reference_new_row","type":"varchar(3)","not_null":true},{"name":"created","type":"timestamp(2)","not_null":true},{"name":"sql_mode","type":"set('real_as_float','pipes_as_concat','ansi_quotes','ignore_space','not_used','only_full_group_by','no_unsigned_subtraction','no_dir_in_create','not_used_9','not_used_10','not_used_11','not_used_12','not_used_13','not_used_14','not_used_15','not_used_16','not_used_17','not_used_18','ansi','no_auto_value_on_zero','no_backslash_escapes','strict_trans_tables','strict_all_tables','no_zero_in_date','no_zero_date','allow_invalid_dates','error_for_division_by_zero','traditional','not_used_29','high_not_precedence','no_engine_substitution','pad_char_to_full_length','time_truncate_fractional','interpret_utf8_as_utf8mb4')","not_null":true},{"name":"definer","type":"varchar(288)","not_null":true},{"name":"character_set_client","type":"varchar(64)","not_null":true},{"name":"collation_connection","type":"varchar(64)","not_null":true},{"name":"database_collation","type":"varchar(64)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"triggers","kind":"v","columns":[{"name":"trigger_catalog","type":"varchar(64)","not_null":true},{"name":"trigger_schema","type":"varchar(64)","not_null":true},{"name":"trigger_name","type":"varchar(64)","not_null":true},{"name":"event_manipulation","type":"enum('INSERT','UPDATE','DELETE')","not_null":true},{"name":"event_object_catalog","type":"varchar(64)","not_null":true},{"name":"event_object_schema","type":"varchar(64)","not_null":true},{"name":"event_object_table","type":"varchar(64)","not_null":true},{"name":"action_order","type":"int unsigned","not_null":true},{"name":"action_condition","type":"varbinary(0)"},{"name":"action_statement","type":"longtext","not_null":true},{"name":"action_orientation","type":"varchar(3)","not_null":true},{"name":"action_timing","type":"enum('BEFORE','AFTER')","not_null":true},{"name":"action_reference_old_table","type":"varbinary(0)"},{"name":"action_reference_new_table","type":"varbinary(0)"},{"name":"action_reference_old_row","type":"varchar(3)","not_null":true},{"name":"action_reference_new_row","type":"varchar(3)","not_null":true},{"name":"created","type":"timestamp(2)","not_null":true},{"name":"sql_mode","type":"set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','NOT_USED_9','NOT_USED_10','NOT_USED_11','NOT_USED_12','NOT_USED_13','NOT_USED_14','NOT_USED_15','NOT_USED_16','NOT_USED_17','NOT_USED_18','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','ALLOW_INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NOT_USED_29','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','TIME_TRUNCATE_FRACTIONAL','INTERPRET_UTF8_AS_UTF8MB4')","not_null":true},{"name":"definer","type":"varchar(288)","not_null":true},{"name":"character_set_client","type":"varchar(64)","not_null":true},{"name":"collation_connection","type":"varchar(64)","not_null":true},{"name":"database_collation","type":"varchar(64)","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"user_attributes","kind":"v","columns":[{"name":"user","type":"char(32)","not_null":true},{"name":"host","type":"char(255)","not_null":true},{"name":"attribute","type":"longtext"}]} {"catalog":"def","schema":"information_schema","name":"user_privileges","kind":"v","columns":[{"name":"grantee","type":"varchar(292)","not_null":true},{"name":"table_catalog","type":"varchar(512)","not_null":true},{"name":"privilege_type","type":"varchar(64)","not_null":true},{"name":"is_grantable","type":"varchar(3)","not_null":true}]} -{"catalog":"def","schema":"information_schema","name":"views","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"view_definition","type":"longtext"},{"name":"check_option","type":"enum('none','local','cascaded')"},{"name":"is_updatable","type":"enum('no','yes')"},{"name":"definer","type":"varchar(288)"},{"name":"security_type","type":"varchar(7)"},{"name":"character_set_client","type":"varchar(64)","not_null":true},{"name":"collation_connection","type":"varchar(64)","not_null":true}]} +{"catalog":"def","schema":"information_schema","name":"views","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"view_definition","type":"longtext"},{"name":"check_option","type":"enum('NONE','LOCAL','CASCADED')"},{"name":"is_updatable","type":"enum('NO','YES')"},{"name":"definer","type":"varchar(288)"},{"name":"security_type","type":"varchar(7)"},{"name":"character_set_client","type":"varchar(64)","not_null":true},{"name":"collation_connection","type":"varchar(64)","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"view_routine_usage","kind":"v","columns":[{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true},{"name":"specific_catalog","type":"varchar(64)","not_null":true},{"name":"specific_schema","type":"varchar(64)","not_null":true},{"name":"specific_name","type":"varchar(64)","not_null":true}]} {"catalog":"def","schema":"information_schema","name":"view_table_usage","kind":"v","columns":[{"name":"view_catalog","type":"varchar(64)","not_null":true},{"name":"view_schema","type":"varchar(64)","not_null":true},{"name":"view_name","type":"varchar(64)","not_null":true},{"name":"table_catalog","type":"varchar(64)","not_null":true},{"name":"table_schema","type":"varchar(64)","not_null":true},{"name":"table_name","type":"varchar(64)","not_null":true}]} diff --git a/internal/engine/dolphin/dialect/types.jsonl b/internal/engine/dolphin/dialect/types.jsonl index f7f3e8432f..c76d29d28a 100644 --- a/internal/engine/dolphin/dialect/types.jsonl +++ b/internal/engine/dolphin/dialect/types.jsonl @@ -1,20 +1,20 @@ {"name": "bool", "category": "B", "aliases": ["boolean"]} {"name": "tinyint", "category": "N"} -{"name": "tinyint unsigned", "category": "N"} +{"name": "tinyint unsigned", "category": "N", "base": "tinyint"} {"name": "smallint", "category": "N"} -{"name": "smallint unsigned", "category": "N"} +{"name": "smallint unsigned", "category": "N", "base": "smallint"} {"name": "mediumint", "category": "N"} -{"name": "mediumint unsigned", "category": "N"} +{"name": "mediumint unsigned", "category": "N", "base": "mediumint"} {"name": "int", "category": "N", "aliases": ["integer"]} -{"name": "int unsigned", "category": "N", "aliases": ["integer unsigned"]} +{"name": "int unsigned", "category": "N", "base": "int", "aliases": ["integer unsigned"]} {"name": "bigint", "category": "N", "aliases": ["signed", "bigint signed"]} -{"name": "bigint unsigned", "category": "N", "aliases": ["unsigned"]} +{"name": "bigint unsigned", "category": "N", "base": "bigint", "aliases": ["unsigned"]} {"name": "float", "category": "N"} -{"name": "float unsigned", "category": "N"} +{"name": "float unsigned", "category": "N", "base": "float"} {"name": "double", "category": "N", "aliases": ["double precision", "real"]} -{"name": "double unsigned", "category": "N", "aliases": ["double precision unsigned", "real unsigned"]} +{"name": "double unsigned", "category": "N", "base": "double", "aliases": ["double precision unsigned", "real unsigned"]} {"name": "decimal", "category": "N", "aliases": ["numeric", "dec", "fixed"]} -{"name": "decimal unsigned", "category": "N", "aliases": ["numeric unsigned", "dec unsigned", "fixed unsigned"]} +{"name": "decimal unsigned", "category": "N", "base": "decimal", "aliases": ["numeric unsigned", "dec unsigned", "fixed unsigned"]} {"name": "bit", "category": "N"} {"name": "char", "category": "S"} {"name": "varchar", "category": "S"} diff --git a/internal/engine/duckdb/convert.go b/internal/engine/duckdb/convert.go index e400138169..29721bb60d 100644 --- a/internal/engine/duckdb/convert.go +++ b/internal/engine/duckdb/convert.go @@ -846,8 +846,8 @@ func (c *cc) elementTypeName(t *dw.TypeExpression) (*ast.TypeName, int) { // is written as it was. func renderTypeExpression(t *dw.TypeExpression) string { name := identifier(t.TypeName) - if t.Schema != "" { - name = schemaName(t.Schema) + "." + name + if schema := schemaName(t.Schema); schema != "" { + name = schema + "." + name } if name == "list" { name = "array" diff --git a/internal/engine/postgresql/convert.go b/internal/engine/postgresql/convert.go index 6600ce2151..586ebd7291 100644 --- a/internal/engine/postgresql/convert.go +++ b/internal/engine/postgresql/convert.go @@ -2811,7 +2811,7 @@ func convertTypeName(n *pg.TypeName) *ast.TypeName { if n == nil { return nil } - return &ast.TypeName{ + out := &ast.TypeName{ Names: convertSlice(n.Names), TypeOid: ast.Oid(n.TypeOid), Setof: n.Setof, @@ -2821,6 +2821,43 @@ func convertTypeName(n *pg.TypeName) *ast.TypeName { ArrayBounds: convertSlice(n.ArrayBounds), Location: int(n.Location), } + decodeIntervalTypmods(out) + return out +} + +// decodeIntervalTypmods turns the field mask an interval's first type +// modifier carries — what the parser reports for CAST(x AS interval day to +// second) — into the words format_type prints, as a column definition's +// type does; the full range is dropped. +func decodeIntervalTypmods(tn *ast.TypeName) { + if tn.Typmods == nil || len(tn.Typmods.Items) == 0 || len(tn.Names.Items) == 0 { + return + } + last, ok := tn.Names.Items[len(tn.Names.Items)-1].(*ast.String) + if !ok || last.Str != "interval" { + return + } + c, ok := tn.Typmods.Items[0].(*ast.A_Const) + if !ok { + return + } + mask, ok := c.Val.(*ast.Integer) + if !ok { + return + } + fields, ok := intervalFields[int32(mask.Ival)] + if !ok { + return + } + rest := tn.Typmods.Items[1:] + if fields != "" { + rest = append([]ast.Node{&ast.String{Str: fields}}, rest...) + } + if len(rest) == 0 { + tn.Typmods = nil + return + } + tn.Typmods = &ast.List{Items: rest} } func convertUnlistenStmt(n *pg.UnlistenStmt) *ast.UnlistenStmt { diff --git a/internal/engine/postgresql/dialect/relations.jsonl b/internal/engine/postgresql/dialect/relations.jsonl index cef83a702a..b93fada4ac 100644 --- a/internal/engine/postgresql/dialect/relations.jsonl +++ b/internal/engine/postgresql/dialect/relations.jsonl @@ -31,7 +31,7 @@ {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_group","columns":[{"name":"groname","type":"name","length":64},{"name":"grosysid","type":"oid","length":4},{"name":"grolist","type":"oid","array":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_hba_file_rules","columns":[{"name":"rule_number","type":"int4","length":4},{"name":"file_name","type":"text"},{"name":"line_number","type":"int4","length":4},{"name":"type","type":"text"},{"name":"database","type":"text","array":true},{"name":"user_name","type":"text","array":true},{"name":"address","type":"text"},{"name":"netmask","type":"text"},{"name":"auth_method","type":"text"},{"name":"options","type":"text","array":true},{"name":"error","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_ident_file_mappings","columns":[{"name":"map_number","type":"int4","length":4},{"name":"file_name","type":"text"},{"name":"line_number","type":"int4","length":4},{"name":"map_name","type":"text"},{"name":"sys_name","type":"text"},{"name":"pg_username","type":"text"},{"name":"error","type":"text"}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_index","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"indexrelid","type":"oid","not_null":true,"length":4},{"name":"indrelid","type":"oid","not_null":true,"length":4},{"name":"indnatts","type":"int2","not_null":true,"length":2},{"name":"indnkeyatts","type":"int2","not_null":true,"length":2},{"name":"indisunique","type":"bool","not_null":true,"length":1},{"name":"indnullsnotdistinct","type":"bool","not_null":true,"length":1},{"name":"indisprimary","type":"bool","not_null":true,"length":1},{"name":"indisexclusion","type":"bool","not_null":true,"length":1},{"name":"indimmediate","type":"bool","not_null":true,"length":1},{"name":"indisclustered","type":"bool","not_null":true,"length":1},{"name":"indisvalid","type":"bool","not_null":true,"length":1},{"name":"indcheckxmin","type":"bool","not_null":true,"length":1},{"name":"indisready","type":"bool","not_null":true,"length":1},{"name":"indislive","type":"bool","not_null":true,"length":1},{"name":"indisreplident","type":"bool","not_null":true,"length":1},{"name":"indkey","type":"int2","not_null":true,"array":true,"length":2},{"name":"indcollation","type":"oid","not_null":true,"array":true,"length":4},{"name":"indclass","type":"oid","not_null":true,"array":true,"length":4},{"name":"indoption","type":"int2","not_null":true,"array":true,"length":2},{"name":"indexprs","type":"pg_node_tree"},{"name":"indpred","type":"pg_node_tree"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_index","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"indexrelid","type":"oid","not_null":true,"length":4},{"name":"indrelid","type":"oid","not_null":true,"length":4},{"name":"indnatts","type":"int2","not_null":true,"length":2},{"name":"indnkeyatts","type":"int2","not_null":true,"length":2},{"name":"indisunique","type":"bool","not_null":true,"length":1},{"name":"indnullsnotdistinct","type":"bool","not_null":true,"length":1},{"name":"indisprimary","type":"bool","not_null":true,"length":1},{"name":"indisexclusion","type":"bool","not_null":true,"length":1},{"name":"indimmediate","type":"bool","not_null":true,"length":1},{"name":"indisclustered","type":"bool","not_null":true,"length":1},{"name":"indisvalid","type":"bool","not_null":true,"length":1},{"name":"indcheckxmin","type":"bool","not_null":true,"length":1},{"name":"indisready","type":"bool","not_null":true,"length":1},{"name":"indislive","type":"bool","not_null":true,"length":1},{"name":"indisreplident","type":"bool","not_null":true,"length":1},{"name":"indkey","type":"int2vector","not_null":true},{"name":"indcollation","type":"oidvector","not_null":true},{"name":"indclass","type":"oidvector","not_null":true},{"name":"indoption","type":"int2vector","not_null":true},{"name":"indexprs","type":"pg_node_tree"},{"name":"indpred","type":"pg_node_tree"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_indexes","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"indexname","type":"name","length":64},{"name":"tablespace","type":"name","length":64},{"name":"indexdef","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_inherits","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"inhrelid","type":"oid","not_null":true,"length":4},{"name":"inhparent","type":"oid","not_null":true,"length":4},{"name":"inhseqno","type":"int4","not_null":true,"length":4},{"name":"inhdetachpending","type":"bool","not_null":true,"length":1}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_init_privs","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"objoid","type":"oid","not_null":true,"length":4},{"name":"classoid","type":"oid","not_null":true,"length":4},{"name":"objsubid","type":"int4","not_null":true,"length":4},{"name":"privtype","type":"char","not_null":true,"length":1},{"name":"initprivs","type":"aclitem","not_null":true,"array":true,"length":16}]} @@ -45,15 +45,15 @@ {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_operator","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"oprname","type":"name","not_null":true,"length":64},{"name":"oprnamespace","type":"oid","not_null":true,"length":4},{"name":"oprowner","type":"oid","not_null":true,"length":4},{"name":"oprkind","type":"char","not_null":true,"length":1},{"name":"oprcanmerge","type":"bool","not_null":true,"length":1},{"name":"oprcanhash","type":"bool","not_null":true,"length":1},{"name":"oprleft","type":"oid","not_null":true,"length":4},{"name":"oprright","type":"oid","not_null":true,"length":4},{"name":"oprresult","type":"oid","not_null":true,"length":4},{"name":"oprcom","type":"oid","not_null":true,"length":4},{"name":"oprnegate","type":"oid","not_null":true,"length":4},{"name":"oprcode","type":"regproc","not_null":true,"length":4},{"name":"oprrest","type":"regproc","not_null":true,"length":4},{"name":"oprjoin","type":"regproc","not_null":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_opfamily","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"opfmethod","type":"oid","not_null":true,"length":4},{"name":"opfname","type":"name","not_null":true,"length":64},{"name":"opfnamespace","type":"oid","not_null":true,"length":4},{"name":"opfowner","type":"oid","not_null":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_parameter_acl","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"parname","type":"text","not_null":true},{"name":"paracl","type":"aclitem","array":true,"length":16}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_partitioned_table","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"partrelid","type":"oid","not_null":true,"length":4},{"name":"partstrat","type":"char","not_null":true,"length":1},{"name":"partnatts","type":"int2","not_null":true,"length":2},{"name":"partdefid","type":"oid","not_null":true,"length":4},{"name":"partattrs","type":"int2","not_null":true,"array":true,"length":2},{"name":"partclass","type":"oid","not_null":true,"array":true,"length":4},{"name":"partcollation","type":"oid","not_null":true,"array":true,"length":4},{"name":"partexprs","type":"pg_node_tree"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_partitioned_table","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"partrelid","type":"oid","not_null":true,"length":4},{"name":"partstrat","type":"char","not_null":true,"length":1},{"name":"partnatts","type":"int2","not_null":true,"length":2},{"name":"partdefid","type":"oid","not_null":true,"length":4},{"name":"partattrs","type":"int2vector","not_null":true},{"name":"partclass","type":"oidvector","not_null":true},{"name":"partcollation","type":"oidvector","not_null":true},{"name":"partexprs","type":"pg_node_tree"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_policies","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"policyname","type":"name","length":64},{"name":"permissive","type":"text"},{"name":"roles","type":"name","array":true,"length":64},{"name":"cmd","type":"text"},{"name":"qual","type":"text"},{"name":"with_check","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_policy","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"polname","type":"name","not_null":true,"length":64},{"name":"polrelid","type":"oid","not_null":true,"length":4},{"name":"polcmd","type":"char","not_null":true,"length":1},{"name":"polpermissive","type":"bool","not_null":true,"length":1},{"name":"polroles","type":"oid","not_null":true,"array":true,"length":4},{"name":"polqual","type":"pg_node_tree"},{"name":"polwithcheck","type":"pg_node_tree"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_prepared_statements","columns":[{"name":"name","type":"text"},{"name":"statement","type":"text"},{"name":"prepare_time","type":"timestamptz","length":8},{"name":"parameter_types","type":"regtype","array":true,"length":4},{"name":"result_types","type":"regtype","array":true,"length":4},{"name":"from_sql","type":"bool","length":1},{"name":"generic_plans","type":"int8","length":8},{"name":"custom_plans","type":"int8","length":8}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_prepared_xacts","columns":[{"name":"transaction","type":"xid","length":4},{"name":"gid","type":"text"},{"name":"prepared","type":"timestamptz","length":8},{"name":"owner","type":"name","length":64},{"name":"database","type":"name","length":64}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_proc","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"proname","type":"name","not_null":true,"length":64},{"name":"pronamespace","type":"oid","not_null":true,"length":4},{"name":"proowner","type":"oid","not_null":true,"length":4},{"name":"prolang","type":"oid","not_null":true,"length":4},{"name":"procost","type":"float4","not_null":true,"length":4},{"name":"prorows","type":"float4","not_null":true,"length":4},{"name":"provariadic","type":"oid","not_null":true,"length":4},{"name":"prosupport","type":"regproc","not_null":true,"length":4},{"name":"prokind","type":"char","not_null":true,"length":1},{"name":"prosecdef","type":"bool","not_null":true,"length":1},{"name":"proleakproof","type":"bool","not_null":true,"length":1},{"name":"proisstrict","type":"bool","not_null":true,"length":1},{"name":"proretset","type":"bool","not_null":true,"length":1},{"name":"provolatile","type":"char","not_null":true,"length":1},{"name":"proparallel","type":"char","not_null":true,"length":1},{"name":"pronargs","type":"int2","not_null":true,"length":2},{"name":"pronargdefaults","type":"int2","not_null":true,"length":2},{"name":"prorettype","type":"oid","not_null":true,"length":4},{"name":"proargtypes","type":"oid","not_null":true,"array":true,"length":4},{"name":"proallargtypes","type":"oid","array":true,"length":4},{"name":"proargmodes","type":"char","array":true,"length":1},{"name":"proargnames","type":"text","array":true},{"name":"proargdefaults","type":"pg_node_tree"},{"name":"protrftypes","type":"oid","array":true,"length":4},{"name":"prosrc","type":"text","not_null":true},{"name":"probin","type":"text"},{"name":"prosqlbody","type":"pg_node_tree"},{"name":"proconfig","type":"text","array":true},{"name":"proacl","type":"aclitem","array":true,"length":16}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_proc","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"proname","type":"name","not_null":true,"length":64},{"name":"pronamespace","type":"oid","not_null":true,"length":4},{"name":"proowner","type":"oid","not_null":true,"length":4},{"name":"prolang","type":"oid","not_null":true,"length":4},{"name":"procost","type":"float4","not_null":true,"length":4},{"name":"prorows","type":"float4","not_null":true,"length":4},{"name":"provariadic","type":"oid","not_null":true,"length":4},{"name":"prosupport","type":"regproc","not_null":true,"length":4},{"name":"prokind","type":"char","not_null":true,"length":1},{"name":"prosecdef","type":"bool","not_null":true,"length":1},{"name":"proleakproof","type":"bool","not_null":true,"length":1},{"name":"proisstrict","type":"bool","not_null":true,"length":1},{"name":"proretset","type":"bool","not_null":true,"length":1},{"name":"provolatile","type":"char","not_null":true,"length":1},{"name":"proparallel","type":"char","not_null":true,"length":1},{"name":"pronargs","type":"int2","not_null":true,"length":2},{"name":"pronargdefaults","type":"int2","not_null":true,"length":2},{"name":"prorettype","type":"oid","not_null":true,"length":4},{"name":"proargtypes","type":"oidvector","not_null":true},{"name":"proallargtypes","type":"oid","array":true,"length":4},{"name":"proargmodes","type":"char","array":true,"length":1},{"name":"proargnames","type":"text","array":true},{"name":"proargdefaults","type":"pg_node_tree"},{"name":"protrftypes","type":"oid","array":true,"length":4},{"name":"prosrc","type":"text","not_null":true},{"name":"probin","type":"text"},{"name":"prosqlbody","type":"pg_node_tree"},{"name":"proconfig","type":"text","array":true},{"name":"proacl","type":"aclitem","array":true,"length":16}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_publication","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"pubname","type":"name","not_null":true,"length":64},{"name":"pubowner","type":"oid","not_null":true,"length":4},{"name":"puballtables","type":"bool","not_null":true,"length":1},{"name":"pubinsert","type":"bool","not_null":true,"length":1},{"name":"pubupdate","type":"bool","not_null":true,"length":1},{"name":"pubdelete","type":"bool","not_null":true,"length":1},{"name":"pubtruncate","type":"bool","not_null":true,"length":1},{"name":"pubviaroot","type":"bool","not_null":true,"length":1}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_publication_namespace","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"pnpubid","type":"oid","not_null":true,"length":4},{"name":"pnnspid","type":"oid","not_null":true,"length":4}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_publication_rel","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"prpubid","type":"oid","not_null":true,"length":4},{"name":"prrelid","type":"oid","not_null":true,"length":4},{"name":"prqual","type":"pg_node_tree"},{"name":"prattrs","type":"int2","array":true,"length":2}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_publication_rel","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"prpubid","type":"oid","not_null":true,"length":4},{"name":"prrelid","type":"oid","not_null":true,"length":4},{"name":"prqual","type":"pg_node_tree"},{"name":"prattrs","type":"int2vector"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_publication_tables","columns":[{"name":"pubname","type":"name","length":64},{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"attnames","type":"name","array":true,"length":64},{"name":"rowfilter","type":"text"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_range","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"rngtypid","type":"oid","not_null":true,"length":4},{"name":"rngsubtype","type":"oid","not_null":true,"length":4},{"name":"rngmultitypid","type":"oid","not_null":true,"length":4},{"name":"rngcollation","type":"oid","not_null":true,"length":4},{"name":"rngsubopc","type":"oid","not_null":true,"length":4},{"name":"rngcanonical","type":"regproc","not_null":true,"length":4},{"name":"rngsubdiff","type":"regproc","not_null":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_replication_origin","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"roident","type":"oid","not_null":true,"length":4},{"name":"roname","type":"text","not_null":true}]} @@ -115,7 +115,7 @@ {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statio_user_sequences","columns":[{"name":"relid","type":"oid","length":4},{"name":"schemaname","type":"name","length":64},{"name":"relname","type":"name","length":64},{"name":"blks_read","type":"int8","length":8},{"name":"blks_hit","type":"int8","length":8}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statio_user_tables","columns":[{"name":"relid","type":"oid","length":4},{"name":"schemaname","type":"name","length":64},{"name":"relname","type":"name","length":64},{"name":"heap_blks_read","type":"int8","length":8},{"name":"heap_blks_hit","type":"int8","length":8},{"name":"idx_blks_read","type":"int8","length":8},{"name":"idx_blks_hit","type":"int8","length":8},{"name":"toast_blks_read","type":"int8","length":8},{"name":"toast_blks_hit","type":"int8","length":8},{"name":"tidx_blks_read","type":"int8","length":8},{"name":"tidx_blks_hit","type":"int8","length":8}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statistic","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"starelid","type":"oid","not_null":true,"length":4},{"name":"staattnum","type":"int2","not_null":true,"length":2},{"name":"stainherit","type":"bool","not_null":true,"length":1},{"name":"stanullfrac","type":"float4","not_null":true,"length":4},{"name":"stawidth","type":"int4","not_null":true,"length":4},{"name":"stadistinct","type":"float4","not_null":true,"length":4},{"name":"stakind1","type":"int2","not_null":true,"length":2},{"name":"stakind2","type":"int2","not_null":true,"length":2},{"name":"stakind3","type":"int2","not_null":true,"length":2},{"name":"stakind4","type":"int2","not_null":true,"length":2},{"name":"stakind5","type":"int2","not_null":true,"length":2},{"name":"staop1","type":"oid","not_null":true,"length":4},{"name":"staop2","type":"oid","not_null":true,"length":4},{"name":"staop3","type":"oid","not_null":true,"length":4},{"name":"staop4","type":"oid","not_null":true,"length":4},{"name":"staop5","type":"oid","not_null":true,"length":4},{"name":"stacoll1","type":"oid","not_null":true,"length":4},{"name":"stacoll2","type":"oid","not_null":true,"length":4},{"name":"stacoll3","type":"oid","not_null":true,"length":4},{"name":"stacoll4","type":"oid","not_null":true,"length":4},{"name":"stacoll5","type":"oid","not_null":true,"length":4},{"name":"stanumbers1","type":"float4","array":true,"length":4},{"name":"stanumbers2","type":"float4","array":true,"length":4},{"name":"stanumbers3","type":"float4","array":true,"length":4},{"name":"stanumbers4","type":"float4","array":true,"length":4},{"name":"stanumbers5","type":"float4","array":true,"length":4},{"name":"stavalues1","type":"anyarray"},{"name":"stavalues2","type":"anyarray"},{"name":"stavalues3","type":"anyarray"},{"name":"stavalues4","type":"anyarray"},{"name":"stavalues5","type":"anyarray"}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statistic_ext","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"stxrelid","type":"oid","not_null":true,"length":4},{"name":"stxname","type":"name","not_null":true,"length":64},{"name":"stxnamespace","type":"oid","not_null":true,"length":4},{"name":"stxowner","type":"oid","not_null":true,"length":4},{"name":"stxstattarget","type":"int4","not_null":true,"length":4},{"name":"stxkeys","type":"int2","not_null":true,"array":true,"length":2},{"name":"stxkind","type":"char","not_null":true,"array":true,"length":1},{"name":"stxexprs","type":"pg_node_tree"}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statistic_ext","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"stxrelid","type":"oid","not_null":true,"length":4},{"name":"stxname","type":"name","not_null":true,"length":64},{"name":"stxnamespace","type":"oid","not_null":true,"length":4},{"name":"stxowner","type":"oid","not_null":true,"length":4},{"name":"stxstattarget","type":"int4","not_null":true,"length":4},{"name":"stxkeys","type":"int2vector","not_null":true},{"name":"stxkind","type":"char","not_null":true,"array":true,"length":1},{"name":"stxexprs","type":"pg_node_tree"}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_statistic_ext_data","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"stxoid","type":"oid","not_null":true,"length":4},{"name":"stxdinherit","type":"bool","not_null":true,"length":1},{"name":"stxdndistinct","type":"pg_ndistinct"},{"name":"stxddependencies","type":"pg_dependencies"},{"name":"stxdmcv","type":"pg_mcv_list"},{"name":"stxdexpr","type":"pg_statistic","array":true}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_stats","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"attname","type":"name","length":64},{"name":"inherited","type":"bool","length":1},{"name":"null_frac","type":"float4","length":4},{"name":"avg_width","type":"int4","length":4},{"name":"n_distinct","type":"float4","length":4},{"name":"most_common_vals","type":"anyarray"},{"name":"most_common_freqs","type":"float4","array":true,"length":4},{"name":"histogram_bounds","type":"anyarray"},{"name":"correlation","type":"float4","length":4},{"name":"most_common_elems","type":"anyarray"},{"name":"most_common_elem_freqs","type":"float4","array":true,"length":4},{"name":"elem_count_histogram","type":"float4","array":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_stats_ext","columns":[{"name":"schemaname","type":"name","length":64},{"name":"tablename","type":"name","length":64},{"name":"statistics_schemaname","type":"name","length":64},{"name":"statistics_name","type":"name","length":64},{"name":"statistics_owner","type":"name","length":64},{"name":"attnames","type":"name","array":true,"length":64},{"name":"exprs","type":"text","array":true},{"name":"kinds","type":"char","array":true,"length":1},{"name":"inherited","type":"bool","length":1},{"name":"n_distinct","type":"pg_ndistinct"},{"name":"dependencies","type":"pg_dependencies"},{"name":"most_common_vals","type":"text","array":true},{"name":"most_common_val_nulls","type":"bool","array":true,"length":1},{"name":"most_common_freqs","type":"float8","array":true,"length":8},{"name":"most_common_base_freqs","type":"float8","array":true,"length":8}]} @@ -127,7 +127,7 @@ {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_timezone_abbrevs","columns":[{"name":"abbrev","type":"text"},{"name":"utc_offset","type":"interval","length":16},{"name":"is_dst","type":"bool","length":1}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_timezone_names","columns":[{"name":"name","type":"text"},{"name":"abbrev","type":"text"},{"name":"utc_offset","type":"interval","length":16},{"name":"is_dst","type":"bool","length":1}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_transform","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"trftype","type":"oid","not_null":true,"length":4},{"name":"trflang","type":"oid","not_null":true,"length":4},{"name":"trffromsql","type":"regproc","not_null":true,"length":4},{"name":"trftosql","type":"regproc","not_null":true,"length":4}]} -{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_trigger","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"tgrelid","type":"oid","not_null":true,"length":4},{"name":"tgparentid","type":"oid","not_null":true,"length":4},{"name":"tgname","type":"name","not_null":true,"length":64},{"name":"tgfoid","type":"oid","not_null":true,"length":4},{"name":"tgtype","type":"int2","not_null":true,"length":2},{"name":"tgenabled","type":"char","not_null":true,"length":1},{"name":"tgisinternal","type":"bool","not_null":true,"length":1},{"name":"tgconstrrelid","type":"oid","not_null":true,"length":4},{"name":"tgconstrindid","type":"oid","not_null":true,"length":4},{"name":"tgconstraint","type":"oid","not_null":true,"length":4},{"name":"tgdeferrable","type":"bool","not_null":true,"length":1},{"name":"tginitdeferred","type":"bool","not_null":true,"length":1},{"name":"tgnargs","type":"int2","not_null":true,"length":2},{"name":"tgattr","type":"int2","not_null":true,"array":true,"length":2},{"name":"tgargs","type":"bytea","not_null":true},{"name":"tgqual","type":"pg_node_tree"},{"name":"tgoldtable","type":"name","length":64},{"name":"tgnewtable","type":"name","length":64}]} +{"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_trigger","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"tgrelid","type":"oid","not_null":true,"length":4},{"name":"tgparentid","type":"oid","not_null":true,"length":4},{"name":"tgname","type":"name","not_null":true,"length":64},{"name":"tgfoid","type":"oid","not_null":true,"length":4},{"name":"tgtype","type":"int2","not_null":true,"length":2},{"name":"tgenabled","type":"char","not_null":true,"length":1},{"name":"tgisinternal","type":"bool","not_null":true,"length":1},{"name":"tgconstrrelid","type":"oid","not_null":true,"length":4},{"name":"tgconstrindid","type":"oid","not_null":true,"length":4},{"name":"tgconstraint","type":"oid","not_null":true,"length":4},{"name":"tgdeferrable","type":"bool","not_null":true,"length":1},{"name":"tginitdeferred","type":"bool","not_null":true,"length":1},{"name":"tgnargs","type":"int2","not_null":true,"length":2},{"name":"tgattr","type":"int2vector","not_null":true},{"name":"tgargs","type":"bytea","not_null":true},{"name":"tgqual","type":"pg_node_tree"},{"name":"tgoldtable","type":"name","length":64},{"name":"tgnewtable","type":"name","length":64}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_ts_config","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"cfgname","type":"name","not_null":true,"length":64},{"name":"cfgnamespace","type":"oid","not_null":true,"length":4},{"name":"cfgowner","type":"oid","not_null":true,"length":4},{"name":"cfgparser","type":"oid","not_null":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_ts_config_map","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"mapcfg","type":"oid","not_null":true,"length":4},{"name":"maptokentype","type":"int4","not_null":true,"length":4},{"name":"mapseqno","type":"int4","not_null":true,"length":4},{"name":"mapdict","type":"oid","not_null":true,"length":4}]} {"catalog":"pg_catalog","schema":"pg_catalog","name":"pg_ts_dict","columns":[{"name":"tableoid","type":"oid","not_null":true,"length":4},{"name":"cmax","type":"cid","not_null":true,"length":4},{"name":"xmax","type":"xid","not_null":true,"length":4},{"name":"cmin","type":"cid","not_null":true,"length":4},{"name":"xmin","type":"xid","not_null":true,"length":4},{"name":"ctid","type":"tid","not_null":true,"length":6},{"name":"oid","type":"oid","not_null":true,"length":4},{"name":"dictname","type":"name","not_null":true,"length":64},{"name":"dictnamespace","type":"oid","not_null":true,"length":4},{"name":"dictowner","type":"oid","not_null":true,"length":4},{"name":"dicttemplate","type":"oid","not_null":true,"length":4},{"name":"dictinitoption","type":"text"}]} diff --git a/internal/goldeneye/clickhouse/types.go b/internal/goldeneye/clickhouse/types.go index 4e42a504b7..96de5124f3 100644 --- a/internal/goldeneye/clickhouse/types.go +++ b/internal/goldeneye/clickhouse/types.go @@ -53,6 +53,13 @@ func parseType(t string) *analysis.TypeExpr { for _, a := range args { expr.Args = append(expr.Args, parseArg(a)) } + // LowCardinality(Nullable(T)) is the only order ClickHouse accepts for + // a nullable low-cardinality column, and the nullability is the + // column's: it reads as a nullable LowCardinality(T). + if name == "lowcardinality" && len(expr.Args) == 1 && expr.Args[0].Type != nil && expr.Args[0].Type.Nullable { + expr.Nullable = true + expr.Args[0].Type.Nullable = false + } // The function an aggregate-function type names is a word, not a type. if name == "aggregatefunction" || name == "simpleaggregatefunction" { if len(expr.Args) > 0 && expr.Args[0].Type != nil && len(expr.Args[0].Type.Args) == 0 { diff --git a/internal/goldeneye/mysql/analyze.go b/internal/goldeneye/mysql/analyze.go index 1ebd8660c6..a2c4240817 100644 --- a/internal/goldeneye/mysql/analyze.go +++ b/internal/goldeneye/mysql/analyze.go @@ -93,24 +93,26 @@ type column struct { // "enum('a','b')" is enum applied to its members. A trailing word such as // zerofill is part of the family too. func typeOfColumn(columnType string) *analysis.TypeExpr { - s := strings.ToLower(strings.TrimSpace(columnType)) + s := strings.TrimSpace(columnType) open := strings.IndexByte(s, '(') if open < 0 { - return &analysis.TypeExpr{Name: s} + return &analysis.TypeExpr{Name: strings.ToLower(s)} } close := strings.LastIndexByte(s, ')') if close < open { - return &analysis.TypeExpr{Name: s} + return &analysis.TypeExpr{Name: strings.ToLower(s)} } - name := strings.TrimSpace(s[:open]) - if rest := strings.TrimSpace(s[close+1:]); rest != "" { + // The family is spelled in lower case; an enum's members keep theirs, + // since they are values. + name := strings.ToLower(strings.TrimSpace(s[:open])) + if rest := strings.ToLower(strings.TrimSpace(s[close+1:])); rest != "" { name += " " + rest } t := &analysis.TypeExpr{Name: name} for _, a := range splitArgs(s[open+1 : close]) { a = strings.TrimSpace(a) if strings.HasPrefix(a, "'") && strings.HasSuffix(a, "'") && len(a) >= 2 { - v := strings.ReplaceAll(a[1:len(a)-1], "''", "'") + v := unquoteMember(a[1 : len(a)-1]) t.Args = append(t.Args, analysis.TypeArg{String: &v}) continue } @@ -121,12 +123,15 @@ func typeOfColumn(columnType string) *analysis.TypeExpr { return t } -// splitArgs splits a type's argument list on the commas outside quotes. +// splitArgs splits a type's argument list on the commas outside quotes. A +// backslash inside a quoted member escapes the character after it. func splitArgs(s string) []string { var out []string start, quoted := 0, false for i := 0; i < len(s); i++ { switch { + case quoted && s[i] == '\\': + i++ case s[i] == '\'': quoted = !quoted case s[i] == ',' && !quoted: @@ -137,6 +142,26 @@ func splitArgs(s string) []string { return append(out, s[start:]) } +// unquoteMember reads the body of a quoted enum or set member as +// information_schema spells it: a quote is doubled and a backslash escapes +// the character after it. +func unquoteMember(s string) string { + var out strings.Builder + for i := 0; i < len(s); i++ { + switch { + case s[i] == '\\' && i+1 < len(s): + i++ + out.WriteByte(s[i]) + case s[i] == '\'' && i+1 < len(s) && s[i+1] == '\'': + i++ + out.WriteByte('\'') + default: + out.WriteByte(s[i]) + } + } + return out.String() +} + // withNullable copies a type with its nullability set. func withNullable(t *analysis.TypeExpr, nullable bool) *analysis.TypeExpr { out := *t diff --git a/internal/goldeneye/mysql/relations.go b/internal/goldeneye/mysql/relations.go index 2eeff70439..63e422cd9d 100644 --- a/internal/goldeneye/mysql/relations.go +++ b/internal/goldeneye/mysql/relations.go @@ -70,10 +70,30 @@ func readRelations(ctx context.Context, conn *sql.Conn, schema string) ([]dialec // 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')", in lower case. +// 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) } - return strings.ToLower(columnType) + 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() } diff --git a/internal/goldeneye/postgresql/relation.go b/internal/goldeneye/postgresql/relation.go index 3a9b989e74..7e036a0ff2 100644 --- a/internal/goldeneye/postgresql/relation.go +++ b/internal/goldeneye/postgresql/relation.go @@ -20,16 +20,19 @@ select 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. + -- 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, - column_type.typcategory = 'A' as column_isarray + 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 element_type.oid = column_type.typelem + 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 From fc7b02cf3980a62f44c910fe8e15b673d8742e93 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 19:23:29 +0000 Subject: [PATCH 16/16] endtoend: regenerate the process plugin golden for the PostgreSQL relation seed The seed now spells an array column as its element with the array flag, and int2vector and oidvector columns as their own types, which the external JSON plugin reports the same way the built-in one does. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Qryf7a1doPFuCz2eGT3bCr --- .../gen/codegen.json | 436 +++++++++--------- 1 file changed, 218 insertions(+), 218 deletions(-) diff --git a/internal/endtoend/testdata/process_plugin_sqlc_gen_json/gen/codegen.json b/internal/endtoend/testdata/process_plugin_sqlc_gen_json/gen/codegen.json index c4556eebee..4a85e7bd7e 100644 --- a/internal/endtoend/testdata/process_plugin_sqlc_gen_json/gen/codegen.json +++ b/internal/endtoend/testdata/process_plugin_sqlc_gen_json/gen/codegen.json @@ -2871,7 +2871,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -2884,13 +2884,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "attoptions", @@ -2910,13 +2910,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "attfdwoptions", @@ -2936,13 +2936,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "attmissingval", @@ -3993,7 +3993,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 64, "is_named_param": false, "is_func_call": false, "scope": "", @@ -4006,13 +4006,13 @@ "type": { "catalog": "", "schema": "", - "name": "_name" + "name": "name" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "comment", @@ -5671,7 +5671,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -5684,13 +5684,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "reloptions", @@ -5710,13 +5710,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "relpartbound", @@ -6923,7 +6923,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -6936,20 +6936,20 @@ "type": { "catalog": "", "schema": "", - "name": "_int2" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "confkey", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -6962,20 +6962,20 @@ "type": { "catalog": "", "schema": "", - "name": "_int2" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "conpfeqop", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -6988,20 +6988,20 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "conppeqop", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -7014,20 +7014,20 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "conffeqop", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -7040,20 +7040,20 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "confdelsetcols", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -7066,20 +7066,20 @@ "type": { "catalog": "", "schema": "", - "name": "_int2" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "conexclop", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -7092,13 +7092,13 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "conbin", @@ -8253,7 +8253,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -8266,13 +8266,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -8510,13 +8510,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -8793,7 +8793,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -8806,13 +8806,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -10042,13 +10042,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -10377,7 +10377,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -10390,13 +10390,13 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "extcondition", @@ -10416,13 +10416,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -10917,7 +10917,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -10930,13 +10930,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "fdwoptions", @@ -10956,13 +10956,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -11291,7 +11291,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -11304,13 +11304,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "srvoptions", @@ -11330,13 +11330,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -11574,13 +11574,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -11649,7 +11649,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -11662,13 +11662,13 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -11802,13 +11802,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "user_name", @@ -11828,13 +11828,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "address", @@ -11932,13 +11932,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "error", @@ -12717,7 +12717,7 @@ { "name": "indkey", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", "length": -1, "is_named_param": false, @@ -12743,7 +12743,7 @@ { "name": "indcollation", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", "length": -1, "is_named_param": false, @@ -12769,7 +12769,7 @@ { "name": "indclass", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", "length": -1, "is_named_param": false, @@ -12795,7 +12795,7 @@ { "name": "indoption", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", "length": -1, "is_named_param": false, @@ -13555,7 +13555,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -13568,13 +13568,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -13955,7 +13955,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -13968,13 +13968,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -14443,7 +14443,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -14456,13 +14456,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -15331,7 +15331,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -15344,13 +15344,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -16827,7 +16827,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -16840,13 +16840,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -17121,7 +17121,7 @@ { "name": "partattrs", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", "length": -1, "is_named_param": false, @@ -17147,7 +17147,7 @@ { "name": "partclass", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", "length": -1, "is_named_param": false, @@ -17173,7 +17173,7 @@ { "name": "partcollation", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", "length": -1, "is_named_param": false, @@ -17341,7 +17341,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 64, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17354,13 +17354,13 @@ "type": { "catalog": "", "schema": "", - "name": "_name" + "name": "name" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "cmd", @@ -17741,7 +17741,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17754,13 +17754,13 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "polqual", @@ -17907,7 +17907,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17920,20 +17920,20 @@ "type": { "catalog": "", "schema": "", - "name": "_regtype" + "name": "regtype" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "result_types", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -17946,13 +17946,13 @@ "type": { "catalog": "", "schema": "", - "name": "_regtype" + "name": "regtype" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "from_sql", @@ -18835,7 +18835,7 @@ { "name": "proargtypes", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", "length": -1, "is_named_param": false, @@ -18863,7 +18863,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -18876,20 +18876,20 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "proargmodes", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -18902,13 +18902,13 @@ "type": { "catalog": "", "schema": "", - "name": "_char" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "proargnames", @@ -18928,13 +18928,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "proargdefaults", @@ -18967,7 +18967,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -18980,13 +18980,13 @@ "type": { "catalog": "", "schema": "", - "name": "_oid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "prosrc", @@ -19084,20 +19084,20 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "proacl", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -19110,13 +19110,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -20035,7 +20035,7 @@ { "name": "prattrs", "not_null": false, - "is_array": true, + "is_array": false, "comment": "", "length": -1, "is_named_param": false, @@ -20151,7 +20151,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 64, "is_named_param": false, "is_func_call": false, "scope": "", @@ -20164,13 +20164,13 @@ "type": { "catalog": "", "schema": "", - "name": "_name" + "name": "name" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "rowfilter", @@ -21992,13 +21992,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "oid", @@ -23638,13 +23638,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "boot_val", @@ -24012,13 +24012,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -38717,7 +38717,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -38730,20 +38730,20 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "stanumbers2", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -38756,20 +38756,20 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "stanumbers3", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -38782,20 +38782,20 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "stanumbers4", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -38808,20 +38808,20 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "stanumbers5", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -38834,13 +38834,13 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "stavalues1", @@ -39297,7 +39297,7 @@ { "name": "stxkeys", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", "length": -1, "is_named_param": false, @@ -39325,7 +39325,7 @@ "not_null": true, "is_array": true, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -39338,13 +39338,13 @@ "type": { "catalog": "", "schema": "", - "name": "_char" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "stxexprs", @@ -39686,13 +39686,13 @@ "type": { "catalog": "", "schema": "", - "name": "_pg_statistic" + "name": "pg_statistic" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -39917,7 +39917,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -39930,13 +39930,13 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "histogram_bounds", @@ -40021,7 +40021,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40034,20 +40034,20 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "elem_count_histogram", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40060,13 +40060,13 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -40213,7 +40213,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 64, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40226,13 +40226,13 @@ "type": { "catalog": "", "schema": "", - "name": "_name" + "name": "name" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "exprs", @@ -40252,20 +40252,20 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "kinds", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40278,13 +40278,13 @@ "type": { "catalog": "", "schema": "", - "name": "_char" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "inherited", @@ -40382,20 +40382,20 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "most_common_val_nulls", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40408,20 +40408,20 @@ "type": { "catalog": "", "schema": "", - "name": "_bool" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "most_common_freqs", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 8, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40434,20 +40434,20 @@ "type": { "catalog": "", "schema": "", - "name": "_float8" + "name": "float8" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "most_common_base_freqs", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 8, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40460,13 +40460,13 @@ "type": { "catalog": "", "schema": "", - "name": "_float8" + "name": "float8" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -40769,7 +40769,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40782,13 +40782,13 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "histogram_bounds", @@ -40873,7 +40873,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40886,20 +40886,20 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "elem_count_histogram", "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -40912,13 +40912,13 @@ "type": { "catalog": "", "schema": "", - "name": "_float4" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -41494,13 +41494,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "suborigin", @@ -42265,7 +42265,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -42278,13 +42278,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "spcoptions", @@ -42304,13 +42304,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -43343,7 +43343,7 @@ { "name": "tgattr", "not_null": true, - "is_array": true, + "is_array": false, "comment": "", "length": -1, "is_named_param": false, @@ -46005,7 +46005,7 @@ "not_null": false, "is_array": true, "comment": "", - "length": -1, + "length": 16, "is_named_param": false, "is_func_call": false, "scope": "", @@ -46018,13 +46018,13 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "aclitem" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -46262,13 +46262,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -46532,13 +46532,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -46698,13 +46698,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -46908,13 +46908,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "foreign_data_wrapper_catalog", @@ -47074,13 +47074,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "foreign_server_catalog", @@ -47370,13 +47370,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 } ], "comment": "" @@ -47484,13 +47484,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "foreign_server_catalog", @@ -47624,13 +47624,13 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, - "array_dims": 0 + "array_dims": 1 }, { "name": "umuser",