From cc4f52e73007ec232df387153f466ca80a5af199 Mon Sep 17 00:00:00 2001 From: jennifersp Date: Tue, 7 Jul 2026 14:00:47 -0700 Subject: [PATCH 1/5] implement pg_proc table --- core/functions/collection.go | 21 +- core/procedures/collection.go | 2 +- server/functions/iterate.go | 162 +++++++++++++- server/functions/pg_get_functiondef.go | 20 +- server/tables/pgcatalog/pg_catalog_cache.go | 3 + server/tables/pgcatalog/pg_proc.go | 231 +++++++++++++++++++- testing/go/functions_test.go | 39 +++- testing/go/pgcatalog_test.go | 18 +- 8 files changed, 478 insertions(+), 18 deletions(-) diff --git a/core/functions/collection.go b/core/functions/collection.go index d3f2761146..6c7ef15600 100644 --- a/core/functions/collection.go +++ b/core/functions/collection.go @@ -377,12 +377,23 @@ func (function Function) GetID() id.Id { func (function Function) GetInnerDefinition() string { // TODO: right now we're hardcode searching for $$, which will fail for some definition strings start := strings.Index(function.Definition, "$$") - end := strings.LastIndex(function.Definition, "$$") - if start == -1 || end == -1 { - // Return the whole definition for now - return function.Definition + if start != -1 { + end := strings.LastIndex(function.Definition, "$$") + if end != -1 { + return strings.TrimSpace(function.Definition[start+2 : end]) + } } - return strings.TrimSpace(function.Definition[start+2 : end]) + + asQuote := strings.Index(strings.ToLower(function.Definition), "as '") + if asQuote != -1 { + endQuote := strings.LastIndex(function.Definition, "'") + if endQuote != -1 { + return strings.TrimSpace(function.Definition[asQuote+4 : endQuote]) + } + } + + // Return the whole definition for now + return function.Definition } // ReplaceDefinition returns a new definition with the inner portion replaced with the given string. diff --git a/core/procedures/collection.go b/core/procedures/collection.go index 4441ff9ef2..10c87f78a9 100644 --- a/core/procedures/collection.go +++ b/core/procedures/collection.go @@ -263,7 +263,7 @@ func (pgp *Collection) iterateIDs(_ context.Context, callback func(procID id.Pro } // IterateProcedures iterates over all procedures in the collection. -func (pgp *Collection) IterateProcedures(_ context.Context, callback func(f Procedure) (stop bool, err error)) error { +func (pgp *Collection) IterateProcedures(_ context.Context, callback func(p Procedure) (stop bool, err error)) error { for _, procID := range pgp.idCache { stop, err := callback(pgp.accessCache[procID]) if err != nil { diff --git a/server/functions/iterate.go b/server/functions/iterate.go index 533bc34b4f..0551628eb9 100644 --- a/server/functions/iterate.go +++ b/server/functions/iterate.go @@ -23,7 +23,9 @@ import ( "github.com/dolthub/go-mysql-server/sql" "github.com/dolthub/doltgresql/core" + "github.com/dolthub/doltgresql/core/functions" "github.com/dolthub/doltgresql/core/id" + "github.com/dolthub/doltgresql/core/procedures" "github.com/dolthub/doltgresql/core/sequences" "github.com/dolthub/doltgresql/core/typecollection" "github.com/dolthub/doltgresql/server/tables" @@ -40,8 +42,12 @@ type Callbacks struct { ColumnDefault func(ctx *sql.Context, schema ItemSchema, table ItemTable, check ItemColumnDefault) (cont bool, err error) // ForeignKey is the callback for foreign keys. ForeignKey func(ctx *sql.Context, schema ItemSchema, table ItemTable, foreignKey ItemForeignKey) (cont bool, err error) + // Function is the callback for functions. + Function func(ctx *sql.Context, schema ItemSchema, function ItemFunction) (cont bool, err error) // Index is the callback for indexes. Index func(ctx *sql.Context, schema ItemSchema, table ItemTable, index ItemIndex) (cont bool, err error) + // Procedure is the callbacks for procedures. + Procedure func(ctx *sql.Context, schema ItemSchema, function ItemProcedure) (cont bool, err error) // Schema is the callback for schemas/namespaces. Schema func(ctx *sql.Context, schema ItemSchema) (cont bool, err error) // Sequence is the callback for sequences. @@ -81,12 +87,24 @@ type ItemForeignKey struct { Item sql.ForeignKeyConstraint } +// ItemFunction contains the relevant information to pass to the Function callback. +type ItemFunction struct { + OID id.Function + Item *functions.Function +} + // ItemIndex contains the relevant information to pass to the Index callback. type ItemIndex struct { OID id.Index Item sql.Index } +// ItemProcedure contains the relevant information to pass to the Procedure callback. +type ItemProcedure struct { + OID id.Procedure + Item *procedures.Procedure +} + // ItemSchema contains the relevant information to pass to the Schema callback. type ItemSchema struct { OID id.Namespace @@ -168,7 +186,26 @@ func IterateDatabase(ctx *sql.Context, database string, callbacks Callbacks) err typeMap = typesBySchema(ctx, coll) } - if err = iterateSchemas(ctx, callbacks, schemas, sequenceMap, typeMap); err != nil { + var functionMap map[string][]*functions.Function + if callbacks.Function != nil { + coll, err := core.GetFunctionsCollectionFromContext(ctx, database) + if err != nil { + return err + } + functionMap = functionsBySchema(ctx, coll) + + } + + var procedureMap map[string][]*procedures.Procedure + if callbacks.Function != nil { + coll, err := core.GetProceduresCollectionFromContext(ctx, database) + if err != nil { + return err + } + procedureMap = proceduresBySchema(ctx, coll) + } + + if err = iterateSchemas(ctx, callbacks, schemas, sequenceMap, typeMap, functionMap, procedureMap); err != nil { return err } } @@ -185,6 +222,28 @@ func typesBySchema(ctx *sql.Context, coll *typecollection.TypeCollection) map[st return m } +// functionsBySchema returns a map of schema name to functions within that schema. +func functionsBySchema(ctx *sql.Context, coll *functions.Collection) map[string][]*functions.Function { + m := make(map[string][]*functions.Function) + _ = coll.IterateFunctions(ctx, func(f functions.Function) (stop bool, err error) { + sch := f.ID.SchemaName() + m[sch] = append(m[sch], &f) + return false, nil + }) + return m +} + +// proceduresBySchema returns a map of schema name to procedures within that schema. +func proceduresBySchema(ctx *sql.Context, coll *procedures.Collection) map[string][]*procedures.Procedure { + m := make(map[string][]*procedures.Procedure) + _ = coll.IterateProcedures(ctx, func(p procedures.Procedure) (stop bool, err error) { + sch := p.ID.SchemaName() + m[sch] = append(m[sch], &p) + return false, nil + }) + return m +} + // IterateCurrentDatabase iterates over the current database, calling each callback as the relevant items are iterated // over. This is a central function that homogenizes all iteration, since OIDs depend on a deterministic iteration over // items. This function should be expanded as we add more items to iterate over. @@ -199,6 +258,8 @@ func iterateSchemas( sortedSchemas []sql.DatabaseSchema, sequenceMap map[string][]*sequences.Sequence, typeMap map[string][]*types.DoltgresType, + functionMap map[string][]*functions.Function, + procedureMap map[string][]*procedures.Procedure, ) error { // Iterate over the sorted schemas by the iteration order for _, schemaIndex := range callbacks.schemaIterationOrder(sortedSchemas) { @@ -227,6 +288,16 @@ func iterateSchemas( return err } + err = iterateFunctions(ctx, callbacks, functionMap, schema, itemSchema) + if err != nil { + return err + } + + err = iterateProcedures(ctx, callbacks, procedureMap, schema, itemSchema) + if err != nil { + return err + } + // Check if we need to iterate over tables if callbacks.iteratesOverTables() { tableNames, err := schema.GetTableNames(ctx) @@ -446,6 +517,22 @@ func iterateForeignKeys(ctx *sql.Context, callbacks Callbacks, itemSchema ItemSc return nil } +// iterateFunctions is called by iterateSchemas to handle functions. +func iterateFunctions(ctx *sql.Context, callbacks Callbacks, functionMap map[string][]*functions.Function, schema sql.DatabaseSchema, itemSchema ItemSchema) error { + for _, f := range functionMap[schema.SchemaName()] { + itemFunction := ItemFunction{ + OID: f.ID, + Item: f, + } + if cont, err := callbacks.Function(ctx, itemSchema, itemFunction); err != nil { + return err + } else if !cont { + return nil + } + } + return nil +} + // iterateIndexes is called by iterateTables to handle indexes. func iterateIndexes(ctx *sql.Context, callbacks Callbacks, itemSchema ItemSchema, itemTable ItemTable, indexCount *int) error { if indexedTable, ok := itemTable.Item.(sql.IndexAddressable); ok { @@ -472,6 +559,22 @@ func iterateIndexes(ctx *sql.Context, callbacks Callbacks, itemSchema ItemSchema return nil } +// iterateProcedures is called by iterateSchemas to handle procedures. +func iterateProcedures(ctx *sql.Context, callbacks Callbacks, procedureMap map[string][]*procedures.Procedure, schema sql.DatabaseSchema, itemSchema ItemSchema) error { + for _, p := range procedureMap[schema.SchemaName()] { + itemProcedure := ItemProcedure{ + OID: p.ID, + Item: p, + } + if cont, err := callbacks.Procedure(ctx, itemSchema, itemProcedure); err != nil { + return err + } else if !cont { + return nil + } + } + return nil +} + // RunCallback iterates over schemas, etc. to find the item that the given oid points to. Once the item has been found, // the relevant callback is called with the item. This means that, at most, only one callback will be called. If the // item cannot be found, then no callbacks are called. @@ -512,6 +615,14 @@ func RunCallback(ctx *sql.Context, internalID id.Id, callbacks Callbacks) error if !itemSchema.OID.IsValid() { return nil } + // Check if we're looking for a function + if internalID.Section() == id.Section_Function { + return runFunction(ctx, internalID, callbacks, itemSchema) + } + // Check if we're looking for a procedure + if internalID.Section() == id.Section_Procedure { + return runProcedure(ctx, internalID, callbacks, itemSchema) + } // Check if we're looking for a sequence if internalID.Section() == id.Section_Sequence { return runSequence(ctx, internalID, callbacks, itemSchema) @@ -691,6 +802,42 @@ func runNamespace(ctx *sql.Context, internalID id.Id, callbacks Callbacks, sorte return nil } +// runFunction is called by RunCallback to handle Section_Function. +func runFunction(ctx *sql.Context, internalID id.Id, callbacks Callbacks, itemSchema ItemSchema) error { + collection, err := core.GetFunctionsCollectionFromContext(ctx, itemSchema.Item.Name()) + if err != nil { + return err + } + return collection.IterateFunctions(ctx, func(f functions.Function) (stop bool, err error) { + if f.ID.AsId() == internalID { + _, err = callbacks.Function(ctx, itemSchema, ItemFunction{ + OID: id.Function(internalID), + Item: &f, + }) + return true, err + } + return false, nil + }) +} + +// runProcedure is called by RunCallback to handle Section_Procedure. +func runProcedure(ctx *sql.Context, internalID id.Id, callbacks Callbacks, itemSchema ItemSchema) error { + collection, err := core.GetProceduresCollectionFromContext(ctx, itemSchema.Item.Name()) + if err != nil { + return err + } + return collection.IterateProcedures(ctx, func(p procedures.Procedure) (stop bool, err error) { + if p.ID.AsId() == internalID { + _, err = callbacks.Procedure(ctx, itemSchema, ItemProcedure{ + OID: id.Procedure(internalID), + Item: &p, + }) + return true, err + } + return false, nil + }) +} + // runSequence is called by RunCallback to handle Section_Sequence. func runSequence(ctx *sql.Context, internalID id.Id, callbacks Callbacks, itemSchema ItemSchema) error { collection, err := core.GetSequencesCollectionFromContext(ctx, itemSchema.Item.Name()) @@ -756,7 +903,8 @@ func runView(ctx *sql.Context, internalID id.Id, callbacks Callbacks, itemSchema // runCallbackValidation ensures that the callbacks match the given oid. func runCallbackValidation(ctx *sql.Context, internalID id.Id, callbacks Callbacks) bool { // Check that we have the relevant callback, and return early if we do not - switch internalID.Section() { + s := internalID.Section() + switch s { case id.Section_Check: if callbacks.Check == nil { return false @@ -772,6 +920,10 @@ func runCallbackValidation(ctx *sql.Context, internalID id.Id, callbacks Callbac if callbacks.ForeignKey == nil { return false } + case id.Section_Function: + if callbacks.Function == nil { + return false + } case id.Section_Index: if callbacks.Index == nil { return false @@ -780,6 +932,10 @@ func runCallbackValidation(ctx *sql.Context, internalID id.Id, callbacks Callbac if callbacks.Schema == nil { return false } + case id.Section_Procedure: + if callbacks.Procedure == nil { + return false + } case id.Section_Sequence: if callbacks.Sequence == nil { return false @@ -803,7 +959,9 @@ func (iter Callbacks) iteratesOverSchemas() bool { return iter.Check != nil || iter.ColumnDefault != nil || iter.ForeignKey != nil || + iter.Function != nil || iter.Index != nil || + iter.Procedure != nil || iter.Schema != nil || iter.Sequence != nil || iter.Table != nil || diff --git a/server/functions/pg_get_functiondef.go b/server/functions/pg_get_functiondef.go index 4afbc3db7f..c1c5e37fa6 100644 --- a/server/functions/pg_get_functiondef.go +++ b/server/functions/pg_get_functiondef.go @@ -17,6 +17,8 @@ package functions import ( "github.com/dolthub/go-mysql-server/sql" + "github.com/dolthub/doltgresql/core/id" + "github.com/dolthub/doltgresql/server/functions/framework" pgtypes "github.com/dolthub/doltgresql/server/types" ) @@ -34,7 +36,21 @@ var pg_get_functiondef_oid = framework.Function1{ IsNonDeterministic: true, Strict: true, Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) { - // TODO: real implementation - return "", nil + oidVal := val.(id.Id) + result := "" + err := RunCallback(ctx, oidVal, Callbacks{ + Function: func(ctx *sql.Context, schema ItemSchema, function ItemFunction) (cont bool, err error) { + result = function.Item.Definition + return false, nil + }, + Procedure: func(ctx *sql.Context, schema ItemSchema, procedure ItemProcedure) (cont bool, err error) { + result = procedure.Item.Definition + return false, nil + }, + }) + if err != nil { + return "", err + } + return result, nil }, } diff --git a/server/tables/pgcatalog/pg_catalog_cache.go b/server/tables/pgcatalog/pg_catalog_cache.go index 9ec62859fe..a764e947ea 100644 --- a/server/tables/pgcatalog/pg_catalog_cache.go +++ b/server/tables/pgcatalog/pg_catalog_cache.go @@ -59,6 +59,9 @@ type pgCatalogCache struct { // pg_index / pg_indexes pgIndexes *pgIndexCache + // pg_proc + procs []*pgProc + // pg_sequence / pg_sequences sequences []*pgSequence diff --git a/server/tables/pgcatalog/pg_proc.go b/server/tables/pgcatalog/pg_proc.go index 85e84c9003..e4309ff5a6 100644 --- a/server/tables/pgcatalog/pg_proc.go +++ b/server/tables/pgcatalog/pg_proc.go @@ -19,6 +19,9 @@ import ( "github.com/dolthub/go-mysql-server/sql" + "github.com/dolthub/doltgresql/core/id" + "github.com/dolthub/doltgresql/core/procedures" + "github.com/dolthub/doltgresql/server/functions" "github.com/dolthub/doltgresql/server/tables" pgtypes "github.com/dolthub/doltgresql/server/types" ) @@ -34,6 +37,27 @@ func InitPgProc() { // PgProcHandler is the handler for the pg_proc table. type PgProcHandler struct{} +// pgSequence represents a row in the pg_proc table +type pgProc struct { + oid id.Id // oid + name string // proname + schemaOid id.Id // pronamespace + variadic id.Id // provariadic + kind string // prokind + strict bool // proisstrict + retSet bool // proretset + volatile string // provolatile + nArgs int16 // pronargs + nArgDefs int16 // pronargdefaults + retTyp id.Id // prorettype + argTypes any // proargtypes + allArgTyps any // proallargtypes + argModes any // proargmodes + argNames any // proargnames + src string // prosrc + // TODO: Fill in the rest of the pg_proc columns +} + var _ tables.Handler = PgProcHandler{} // Name implements the interface tables.Handler. @@ -43,8 +67,170 @@ func (p PgProcHandler) Name() string { // RowIter implements the interface tables.Handler. func (p PgProcHandler) RowIter(ctx *sql.Context, partition sql.Partition) (sql.RowIter, error) { - // TODO: Implement pg_proc row iter - return emptyRowIter() + cache, err := getPgCatalogCache(ctx) + if err != nil { + return nil, err + } + + if cache.procs == nil { + err = cachePgProcs(ctx, cache) + if err != nil { + return nil, err + } + } + + return &pgProcRowIter{ + procs: cache.procs, + idx: 0, + }, nil +} + +// cachePgProcs caches the pg_proc data for the current database in the session. +func cachePgProcs(ctx *sql.Context, pgCatalogCache *pgCatalogCache) error { + var pprocs []*pgProc + + err := functions.IterateCurrentDatabase(ctx, functions.Callbacks{ + // TODO: add built-in functions + Function: func(ctx *sql.Context, schema functions.ItemSchema, f functions.ItemFunction) (cont bool, err error) { + variadic := id.Null + if f.Item.Variadic { + // TODO not implemented yet + variadic = id.Null + } + + nArgs := int16(len(f.Item.ParameterTypes)) + nArgDefs := int16(0) + retSet := false + if f.Item.ReturnType.IsValid() && f.Item.ReturnType.TypeName() == "record" { + retSet = true + } + + var ( + kind = "f" // a for an aggregate function, or w for a window function + argTypes, argNames any + types, names []any + ) + + hasNonEmtpyArgName := false + for i, typ := range f.Item.ParameterTypes { + if f.Item.ParameterDefaults[i] != "" { + nArgDefs += 1 + } + if f.Item.ParameterNames[i] != "" { + names = append(names, f.Item.ParameterNames[i]) + } + types = append(types, typ.AsId()) + if f.Item.ParameterNames[i] != "" { + hasNonEmtpyArgName = true + } + names = append(names, f.Item.ParameterNames[i]) + } + + if len(types) > 0 { + argTypes = types + } + if hasNonEmtpyArgName && len(names) > 0 { + argNames = names + } + + var volatile = "i" // immutable + if f.Item.IsNonDeterministic { + volatile = "v" // volatile + } + + pprocs = append(pprocs, &pgProc{ + oid: f.OID.AsId(), + name: f.Item.ID.FunctionName(), + schemaOid: schema.OID.AsId(), + variadic: variadic, + kind: kind, + strict: f.Item.Strict, + retSet: retSet, + volatile: volatile, + nArgs: nArgs, + nArgDefs: nArgDefs, + retTyp: f.Item.ReturnType.AsId(), + argTypes: argTypes, + allArgTyps: nil, + argModes: nil, + argNames: argNames, + src: f.Item.SQLDefinition, + }) + return true, nil + }, + Procedure: func(ctx *sql.Context, schema functions.ItemSchema, p functions.ItemProcedure) (cont bool, err error) { + nArgs := int16(len(p.Item.ParameterTypes)) + nArgDefs := int16(0) + + var ( + // argTypes includes only input arguments (including INOUT and VARIADIC arguments) + argTypes any + // argAllTypes includes all arguments (including OUT and INOUT arguments); + // however, if all the arguments are IN arguments, this field will be null. + argAllTypes any + argNames any + ) + + var types, allTypes, names []any + hasNonINArg := false + hasNonEmtpyArgName := false + for i, typ := range p.Item.ParameterTypes { + switch p.Item.ParameterModes[i] { + case procedures.ParameterMode_IN: + types = append(types, typ.AsId()) + case procedures.ParameterMode_VARIADIC, procedures.ParameterMode_INOUT: + types = append(types, typ.AsId()) + hasNonINArg = true + case procedures.ParameterMode_OUT: + hasNonINArg = true + } + if p.Item.ParameterDefaults[i] != "" { + nArgDefs += 1 + } + if p.Item.ParameterNames[i] != "" { + hasNonEmtpyArgName = true + } + allTypes = append(allTypes, typ.AsId()) + names = append(names, p.Item.ParameterNames[i]) + } + + if len(types) > 0 { + argTypes = types + } + if hasNonINArg && len(allTypes) > 0 { + argAllTypes = allTypes + } + if hasNonEmtpyArgName && len(names) > 0 { + argNames = names + } + + pprocs = append(pprocs, &pgProc{ + oid: p.OID.AsId(), + name: p.Item.ID.ProcedureName(), + schemaOid: schema.OID.AsId(), + variadic: id.Null, + kind: "p", + strict: false, + retSet: false, + volatile: "v", // volatile + nArgs: nArgs, + nArgDefs: nArgDefs, + retTyp: pgtypes.Void.ID.AsId(), + argTypes: argTypes, + allArgTyps: argAllTypes, + argModes: nil, + argNames: argNames, + src: p.Item.SQLDefinition, + }) + return true, nil + }, + }) + if err != nil { + return err + } + + pgCatalogCache.procs = pprocs + return nil } // PkSchema implements the interface tables.Handler. @@ -91,13 +277,52 @@ var pgProcSchema = sql.Schema{ // pgProcRowIter is the sql.RowIter for the pg_proc table. type pgProcRowIter struct { + procs []*pgProc + idx int } var _ sql.RowIter = (*pgProcRowIter)(nil) // Next implements the interface sql.RowIter. func (iter *pgProcRowIter) Next(ctx *sql.Context) (sql.Row, error) { - return nil, io.EOF + if iter.idx >= len(iter.procs) { + return nil, io.EOF + } + p := iter.procs[iter.idx] + iter.idx++ + + return sql.Row{ + p.oid, // oid + p.name, // proname + p.schemaOid, // pronamespace + id.Null, // proowner + id.Null, // prolang + float32(1), // procost + float32(0), // prorows + p.variadic, // provariadic + nil, // prosupport + p.kind, // prokind + false, // prosecdef + false, // proleakproof + p.strict, // proisstrict + p.retSet, // proretset + p.volatile, // provolatile + "u", // proparallel // TODO: default to 'unsafe' for now + p.nArgs, // pronargs + p.nArgDefs, // pronargdefaults + p.retTyp, // prorettype + p.argTypes, // proargtypes + p.allArgTyps, // proallargtypes + p.argModes, // proargmodes + p.argNames, // proargnames + nil, // proargdefaults + nil, // protrftypes + p.src, // prosrc + nil, // probin + nil, // prosqlbody + nil, // proconfig + nil, // proacl + }, nil } // Close implements the interface sql.RowIter. diff --git a/testing/go/functions_test.go b/testing/go/functions_test.go index ea061821fa..2d986c1f9a 100644 --- a/testing/go/functions_test.go +++ b/testing/go/functions_test.go @@ -2162,8 +2162,17 @@ func TestSystemCatalogInformationFunctions(t *testing.T) { }, }, { - Name: "pg_get_functiondef", - SetUpScript: []string{}, + Name: "pg_get_functiondef", + SetUpScript: []string{ + `CREATE FUNCTION alt_func1(int) RETURNS int LANGUAGE sql AS 'SELECT $1 + 1';`, + `CREATE TABLE cp_test (a int, b text);`, + `CREATE OR REPLACE PROCEDURE ptest5(a int, b text, c int default 100) + LANGUAGE SQL + AS $$ + INSERT INTO cp_test VALUES(a, b); + INSERT INTO cp_test VALUES(c, b); + $$;`, + }, Assertions: []ScriptTestAssertion{ { // TODO: not supported yet @@ -2172,6 +2181,32 @@ func TestSystemCatalogInformationFunctions(t *testing.T) { {""}, }, }, + { + Skip: true, // TODO: fails to convert oid to function id because it hasn't been cached. + Query: `SELECT pg_get_functiondef(2891346960)`, + Expected: []sql.Row{ + {"CREATE FUNCTION alt_func1(int) RETURNS int LANGUAGE sql AS 'SELECT $1 + 1'"}, + }, + }, + { + Query: `SELECT oid, proname FROM pg_catalog.pg_proc WHERE proname = 'alt_func1' OR proname = 'ptest5';`, + Expected: []sql.Row{ + {2891346960, "alt_func1"}, + {1886569565, "ptest5"}, + }, + }, + { + Query: `SELECT pg_get_functiondef(2891346960)`, + Expected: []sql.Row{ + {"CREATE FUNCTION alt_func1(int) RETURNS int LANGUAGE sql AS 'SELECT $1 + 1'"}, + }, + }, + { + Query: `SELECT pg_get_functiondef(1886569565)`, + Expected: []sql.Row{ + {"CREATE OR REPLACE PROCEDURE ptest5(a int, b text, c int default 100)\n\t\t\t\tLANGUAGE SQL\n\t\t\t\tAS $$\n\t\t\t\t\tINSERT INTO cp_test VALUES(a, b);\n\t\t\t\t\tINSERT INTO cp_test VALUES(c, b);\n\t\t\t\t$$"}, + }, + }, }, }, { diff --git a/testing/go/pgcatalog_test.go b/testing/go/pgcatalog_test.go index 948d4942d9..79d206170f 100644 --- a/testing/go/pgcatalog_test.go +++ b/testing/go/pgcatalog_test.go @@ -2475,10 +2475,22 @@ func TestPgProc(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "pg_proc", + SetUpScript: []string{ + `CREATE FUNCTION alt_func1(int) RETURNS int LANGUAGE sql AS 'SELECT $1 + 1';`, + `CREATE TABLE cp_test (a int, b text);`, + `CREATE OR REPLACE PROCEDURE ptest5(a int, b text, c int default 100) + LANGUAGE SQL + AS $$ + INSERT INTO cp_test VALUES(a, b); + INSERT INTO cp_test VALUES(c, b); + $$;`, + }, Assertions: []ScriptTestAssertion{ { - Query: `SELECT * FROM "pg_catalog"."pg_proc";`, - Expected: []sql.Row{}, + Query: `SELECT * FROM "pg_catalog"."pg_proc";`, + Expected: []sql.Row{ + {2891346960, "alt_func1", 2200, 0, 0, 1.0, 0.0, 0, nil, "f", "f", "f", "f", "f", "v", "u", 1, 0, 23, "23", nil, nil, nil, nil, nil, "SELECT $1 + 1", nil, nil, nil, nil}, + {1886569565, "ptest5", 2200, 0, 0, 1.0, 0.0, 0, nil, "p", "f", "f", "f", "f", "v", "u", 3, 1, 2278, "23 25 23", nil, nil, "{a,b,c}", nil, nil, "INSERT INTO cp_test VALUES (a, b);INSERT INTO cp_test VALUES (c, b)", nil, nil, nil, nil}}, }, { // Different cases and quoted, so it fails Query: `SELECT * FROM "PG_catalog"."pg_proc";`, @@ -2490,7 +2502,7 @@ func TestPgProc(t *testing.T) { }, { // Different cases but non-quoted, so it works Query: "SELECT proname FROM PG_catalog.pg_PROC ORDER BY proname;", - Expected: []sql.Row{}, + Expected: []sql.Row{{"alt_func1"}, {"ptest5"}}, }, }, }, From 85194d8018948e66a33bf2352966b2588f885972 Mon Sep 17 00:00:00 2001 From: jennifersp Date: Wed, 8 Jul 2026 11:00:05 -0700 Subject: [PATCH 2/5] temp fix to use OID type for regproc type to avoid conversion error --- server/tables/pgcatalog/pg_proc.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server/tables/pgcatalog/pg_proc.go b/server/tables/pgcatalog/pg_proc.go index e4309ff5a6..0b1e72fa52 100644 --- a/server/tables/pgcatalog/pg_proc.go +++ b/server/tables/pgcatalog/pg_proc.go @@ -116,9 +116,6 @@ func cachePgProcs(ctx *sql.Context, pgCatalogCache *pgCatalogCache) error { if f.Item.ParameterDefaults[i] != "" { nArgDefs += 1 } - if f.Item.ParameterNames[i] != "" { - names = append(names, f.Item.ParameterNames[i]) - } types = append(types, typ.AsId()) if f.Item.ParameterNames[i] != "" { hasNonEmtpyArgName = true @@ -251,7 +248,7 @@ var pgProcSchema = sql.Schema{ {Name: "procost", Type: pgtypes.Float32, Default: nil, Nullable: false, Source: PgProcName}, {Name: "prorows", Type: pgtypes.Float32, Default: nil, Nullable: false, Source: PgProcName}, {Name: "provariadic", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: PgProcName}, - {Name: "prosupport", Type: pgtypes.Text, Default: nil, Nullable: false, Source: PgProcName}, // TODO: type regproc + {Name: "prosupport", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: PgProcName}, // TODO: type regproc {Name: "prokind", Type: pgtypes.InternalChar, Default: nil, Nullable: false, Source: PgProcName}, {Name: "prosecdef", Type: pgtypes.Bool, Default: nil, Nullable: false, Source: PgProcName}, {Name: "proleakproof", Type: pgtypes.Bool, Default: nil, Nullable: false, Source: PgProcName}, From f4a5d7adf9976174a49fb448aa203073acf7dd9e Mon Sep 17 00:00:00 2001 From: jennifersp Date: Wed, 8 Jul 2026 13:54:20 -0700 Subject: [PATCH 3/5] temp fix to use OID type for regproc type to avoid conversion error --- server/expression/subscript.go | 3 ++- server/tables/pgcatalog/pg_am.go | 2 +- server/tables/pgcatalog/pg_type.go | 16 ++++++++-------- server/types/type.go | 2 +- testing/go/types_test.go | 10 ++++++++++ 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/server/expression/subscript.go b/server/expression/subscript.go index c927196240..622ba4c840 100755 --- a/server/expression/subscript.go +++ b/server/expression/subscript.go @@ -57,7 +57,8 @@ func (s Subscript) Type(ctx *sql.Context) sql.Type { if !ok { panic(fmt.Sprintf("unexpected type %T for subscript", s.Child.Type(ctx))) } - return dt.ArrayBaseType() + // can be either array type or vector type, so use its base type if it exists + return dt.BaseType() } // IsNullable implements the sql.Expression interface. diff --git a/server/tables/pgcatalog/pg_am.go b/server/tables/pgcatalog/pg_am.go index 2d5a8b2120..917ebd1f6a 100644 --- a/server/tables/pgcatalog/pg_am.go +++ b/server/tables/pgcatalog/pg_am.go @@ -62,7 +62,7 @@ func (p PgAmHandler) PkSchema() sql.PrimaryKeySchema { var pgAmSchema = sql.Schema{ {Name: "oid", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: PgAmName}, {Name: "amname", Type: pgtypes.Name, Default: nil, Nullable: false, Source: PgAmName}, - {Name: "amhandler", Type: pgtypes.Text, Default: nil, Nullable: false, Source: PgAmName}, // TODO: type regproc + {Name: "amhandler", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: PgAmName}, // TODO: type regproc {Name: "amtype", Type: pgtypes.InternalChar, Default: nil, Nullable: false, Source: PgAmName}, } diff --git a/server/tables/pgcatalog/pg_type.go b/server/tables/pgcatalog/pg_type.go index 1676cd5f2a..4887d4891e 100644 --- a/server/tables/pgcatalog/pg_type.go +++ b/server/tables/pgcatalog/pg_type.go @@ -303,16 +303,16 @@ var pgTypeSchema = sql.Schema{ {Name: "typisdefined", Type: pgtypes.Bool, Default: nil, Nullable: false, Source: pgTypeName}, {Name: "typdelim", Type: pgtypes.InternalChar, Default: nil, Nullable: false, Source: pgTypeName}, {Name: "typrelid", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, - {Name: "typsubscript", Type: pgtypes.Text, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc + {Name: "typsubscript", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc {Name: "typelem", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, {Name: "typarray", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, - {Name: "typinput", Type: pgtypes.Text, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc - {Name: "typoutput", Type: pgtypes.Text, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc - {Name: "typreceive", Type: pgtypes.Text, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc - {Name: "typsend", Type: pgtypes.Text, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc - {Name: "typmodin", Type: pgtypes.Text, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc - {Name: "typmodout", Type: pgtypes.Text, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc - {Name: "typanalyze", Type: pgtypes.Text, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc + {Name: "typinput", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc + {Name: "typoutput", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc + {Name: "typreceive", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc + {Name: "typsend", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc + {Name: "typmodin", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc + {Name: "typmodout", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc + {Name: "typanalyze", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc {Name: "typalign", Type: pgtypes.InternalChar, Default: nil, Nullable: false, Source: pgTypeName}, {Name: "typstorage", Type: pgtypes.InternalChar, Default: nil, Nullable: false, Source: pgTypeName}, {Name: "typnotnull", Type: pgtypes.Bool, Default: nil, Nullable: false, Source: pgTypeName}, diff --git a/server/types/type.go b/server/types/type.go index b6d5e60587..415fcc7d75 100644 --- a/server/types/type.go +++ b/server/types/type.go @@ -642,7 +642,7 @@ func (t *DoltgresType) IoOutput(ctx *sql.Context, val any) (string, error) { } // IsArrayType returns true if the type is of 'array' type. -// It can be array category with empty its array attribute NULL and element attribute NOT NULL. +// It can be array category with its array attribute NULL and element attribute NOT NULL. // Or it can be pseudo category with name 'anyarray'. func (t *DoltgresType) IsArrayType() bool { return (t.TypCategory == TypeCategory_ArrayTypes && t.Elem.ID != id.NullType && t.Array.ID == id.NullType) || diff --git a/testing/go/types_test.go b/testing/go/types_test.go index 48742d7353..32f52c62e3 100644 --- a/testing/go/types_test.go +++ b/testing/go/types_test.go @@ -2070,6 +2070,16 @@ var typesTests = []ScriptTest{ {2, "556 778 223"}, }, }, + { + Skip: true, // TODO: should convert oidvector to oid[] and subscript but on special indexing of [0:1] + Query: "select ('16 17'::oidvector)[1];", + Expected: []sql.Row{{17}}, + }, + { + Skip: true, // TODO: support cast from oidvector to oid[] + Query: "select '16 17'::oidvector::oid[];", + Expected: []sql.Row{{"[0:1]={16,17}"}}, + }, }, }, { From f9c1babb783787548eb697d69e91f9683a2aea6b Mon Sep 17 00:00:00 2001 From: jennifersp Date: Wed, 8 Jul 2026 14:57:41 -0700 Subject: [PATCH 4/5] we already have regproc type --- server/tables/pgcatalog/pg_am.go | 2 +- server/tables/pgcatalog/pg_proc.go | 2 +- server/tables/pgcatalog/pg_type.go | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/server/tables/pgcatalog/pg_am.go b/server/tables/pgcatalog/pg_am.go index 917ebd1f6a..5c4fdfd9ca 100644 --- a/server/tables/pgcatalog/pg_am.go +++ b/server/tables/pgcatalog/pg_am.go @@ -62,7 +62,7 @@ func (p PgAmHandler) PkSchema() sql.PrimaryKeySchema { var pgAmSchema = sql.Schema{ {Name: "oid", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: PgAmName}, {Name: "amname", Type: pgtypes.Name, Default: nil, Nullable: false, Source: PgAmName}, - {Name: "amhandler", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: PgAmName}, // TODO: type regproc + {Name: "amhandler", Type: pgtypes.Regproc, Default: nil, Nullable: false, Source: PgAmName}, {Name: "amtype", Type: pgtypes.InternalChar, Default: nil, Nullable: false, Source: PgAmName}, } diff --git a/server/tables/pgcatalog/pg_proc.go b/server/tables/pgcatalog/pg_proc.go index 0b1e72fa52..9a642c1140 100644 --- a/server/tables/pgcatalog/pg_proc.go +++ b/server/tables/pgcatalog/pg_proc.go @@ -248,7 +248,7 @@ var pgProcSchema = sql.Schema{ {Name: "procost", Type: pgtypes.Float32, Default: nil, Nullable: false, Source: PgProcName}, {Name: "prorows", Type: pgtypes.Float32, Default: nil, Nullable: false, Source: PgProcName}, {Name: "provariadic", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: PgProcName}, - {Name: "prosupport", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: PgProcName}, // TODO: type regproc + {Name: "prosupport", Type: pgtypes.Regproc, Default: nil, Nullable: false, Source: PgProcName}, {Name: "prokind", Type: pgtypes.InternalChar, Default: nil, Nullable: false, Source: PgProcName}, {Name: "prosecdef", Type: pgtypes.Bool, Default: nil, Nullable: false, Source: PgProcName}, {Name: "proleakproof", Type: pgtypes.Bool, Default: nil, Nullable: false, Source: PgProcName}, diff --git a/server/tables/pgcatalog/pg_type.go b/server/tables/pgcatalog/pg_type.go index 4887d4891e..1fa2b957b7 100644 --- a/server/tables/pgcatalog/pg_type.go +++ b/server/tables/pgcatalog/pg_type.go @@ -303,16 +303,16 @@ var pgTypeSchema = sql.Schema{ {Name: "typisdefined", Type: pgtypes.Bool, Default: nil, Nullable: false, Source: pgTypeName}, {Name: "typdelim", Type: pgtypes.InternalChar, Default: nil, Nullable: false, Source: pgTypeName}, {Name: "typrelid", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, - {Name: "typsubscript", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc + {Name: "typsubscript", Type: pgtypes.Regproc, Default: nil, Nullable: false, Source: pgTypeName}, {Name: "typelem", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, {Name: "typarray", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, - {Name: "typinput", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc - {Name: "typoutput", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc - {Name: "typreceive", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc - {Name: "typsend", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc - {Name: "typmodin", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc - {Name: "typmodout", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc - {Name: "typanalyze", Type: pgtypes.Oid, Default: nil, Nullable: false, Source: pgTypeName}, // TODO: type regproc + {Name: "typinput", Type: pgtypes.Regproc, Default: nil, Nullable: false, Source: pgTypeName}, + {Name: "typoutput", Type: pgtypes.Regproc, Default: nil, Nullable: false, Source: pgTypeName}, + {Name: "typreceive", Type: pgtypes.Regproc, Default: nil, Nullable: false, Source: pgTypeName}, + {Name: "typsend", Type: pgtypes.Regproc, Default: nil, Nullable: false, Source: pgTypeName}, + {Name: "typmodin", Type: pgtypes.Regproc, Default: nil, Nullable: false, Source: pgTypeName}, + {Name: "typmodout", Type: pgtypes.Regproc, Default: nil, Nullable: false, Source: pgTypeName}, + {Name: "typanalyze", Type: pgtypes.Regproc, Default: nil, Nullable: false, Source: pgTypeName}, {Name: "typalign", Type: pgtypes.InternalChar, Default: nil, Nullable: false, Source: pgTypeName}, {Name: "typstorage", Type: pgtypes.InternalChar, Default: nil, Nullable: false, Source: pgTypeName}, {Name: "typnotnull", Type: pgtypes.Bool, Default: nil, Nullable: false, Source: pgTypeName}, From 59e5914a3d1f3df22bdf00c2fb32848089fc247d Mon Sep 17 00:00:00 2001 From: jennifersp Date: Wed, 8 Jul 2026 15:53:52 -0700 Subject: [PATCH 5/5] update regproc type values --- core/id/section.go | 2 +- server/functions/regproc.go | 6 +++- server/tables/pgcatalog/pg_am.go | 16 ++++----- server/tables/pgcatalog/pg_type.go | 56 +++++++++++++++--------------- server/types/function_registry.go | 9 +++++ 5 files changed, 51 insertions(+), 38 deletions(-) diff --git a/core/id/section.go b/core/id/section.go index e994075f08..cc6de3792c 100644 --- a/core/id/section.go +++ b/core/id/section.go @@ -111,7 +111,7 @@ func (section Section) String() string { case Section_Operator: return "Operator" case Section_OperatorClass: - return "OperatorClass:" + return "OperatorClass" case Section_OperatorFamily: return "OperatorFamily" case Section_PrimaryKey: diff --git a/server/functions/regproc.go b/server/functions/regproc.go index 208c2cc47e..60b739bb02 100644 --- a/server/functions/regproc.go +++ b/server/functions/regproc.go @@ -82,7 +82,11 @@ var regprocout = framework.Function1{ if input.Section() == id.Section_OID { return input.Segment(0), nil } - return val.(id.Id).Segment(1), nil + res := val.(id.Id).Segment(1) + if res == "" { + return "-", nil + } + return res, nil }, } diff --git a/server/tables/pgcatalog/pg_am.go b/server/tables/pgcatalog/pg_am.go index 5c4fdfd9ca..c5539b87e2 100644 --- a/server/tables/pgcatalog/pg_am.go +++ b/server/tables/pgcatalog/pg_am.go @@ -98,17 +98,17 @@ func (iter *pgAmRowIter) Close(ctx *sql.Context) error { type accessMethod struct { oid id.Id name string - handler string + handler id.Id typ string } // defaultPostgresAms is the list of default access methods available in Postgres. var defaultPostgresAms = []accessMethod{ - {oid: id.NewAccessMethod("heap").AsId(), name: "heap", handler: "heap_tableam_handler", typ: "t"}, - {oid: id.NewAccessMethod("btree").AsId(), name: "btree", handler: "bthandler", typ: "i"}, - {oid: id.NewAccessMethod("hash").AsId(), name: "hash", handler: "hashhandler", typ: "i"}, - {oid: id.NewAccessMethod("gist").AsId(), name: "gist", handler: "gisthandler", typ: "i"}, - {oid: id.NewAccessMethod("gin").AsId(), name: "gin", handler: "ginhandler", typ: "i"}, - {oid: id.NewAccessMethod("spgist").AsId(), name: "spgist", handler: "spghandler", typ: "i"}, - {oid: id.NewAccessMethod("brin").AsId(), name: "brin", handler: "brinhandler", typ: "i"}, + {oid: id.NewAccessMethod("heap").AsId(), name: "heap", handler: id.NewFunction("pg_catalog", "heap_tableam_handler", pgtypes.Internal.ID).AsId(), typ: "t"}, + {oid: id.NewAccessMethod("btree").AsId(), name: "btree", handler: id.NewFunction("pg_catalog", "bthandler", pgtypes.Internal.ID).AsId(), typ: "i"}, + {oid: id.NewAccessMethod("hash").AsId(), name: "hash", handler: id.NewFunction("pg_catalog", "hashhandler", pgtypes.Internal.ID).AsId(), typ: "i"}, + {oid: id.NewAccessMethod("gist").AsId(), name: "gist", handler: id.NewFunction("pg_catalog", "gisthandler", pgtypes.Internal.ID).AsId(), typ: "i"}, + {oid: id.NewAccessMethod("gin").AsId(), name: "gin", handler: id.NewFunction("pg_catalog", "ginhandler", pgtypes.Internal.ID).AsId(), typ: "i"}, + {oid: id.NewAccessMethod("spgist").AsId(), name: "spgist", handler: id.NewFunction("pg_catalog", "spghandler", pgtypes.Internal.ID).AsId(), typ: "i"}, + {oid: id.NewAccessMethod("brin").AsId(), name: "brin", handler: id.NewFunction("pg_catalog", "brinhandler", pgtypes.Internal.ID).AsId(), typ: "i"}, } diff --git a/server/tables/pgcatalog/pg_type.go b/server/tables/pgcatalog/pg_type.go index 1fa2b957b7..a7fba7f015 100644 --- a/server/tables/pgcatalog/pg_type.go +++ b/server/tables/pgcatalog/pg_type.go @@ -381,33 +381,33 @@ func pgTypeToRow(nextType *pgType) sql.Row { nextType.name, nextType.schemaOid, id.Null, - nextType.typ.TypLength, // typlen - nextType.typ.PassedByVal, // typbyval - string(nextType.typ.TypType), // typtype - string(nextType.typ.TypCategory), // typcategory - nextType.typ.IsPreferred, // typispreferred - nextType.typ.IsDefined, // typisdefined - nextType.typ.Delimiter, // typdelim - nextType.typ.RelID, // typrelid - nextType.typ.SubscriptFuncName(), // typsubscript - nextType.typ.Elem.ID.AsId(), // typelem - nextType.typ.Array.ID.AsId(), // typarray - nextType.typ.InputFuncName(), // typinput - nextType.typ.OutputFuncName(), // typoutput - nextType.typ.ReceiveFuncName(), // typreceive - nextType.typ.SendFuncName(), // typsend - nextType.typ.ModInFuncName(), // typmodin - nextType.typ.ModOutFuncName(), // typmodout - nextType.typ.AnalyzeFuncName(), // typanalyze - string(nextType.typ.Align), // typalign - string(nextType.typ.Storage), // typstorage - nextType.typ.NotNull, // typnotnull - nextType.typ.BaseTypeType.ID.AsId(), // typbasetype - nextType.typ.TypMod, // typtypmod - nextType.typ.NDims, // typndims - nextType.typ.TypCollation.AsId(), // typcollation - nextType.typ.DefaulBin, // typdefaultbin - nextType.typ.Default, // typdefault - typAcl, // typacl + nextType.typ.TypLength, // typlen + nextType.typ.PassedByVal, // typbyval + string(nextType.typ.TypType), // typtype + string(nextType.typ.TypCategory), // typcategory + nextType.typ.IsPreferred, // typispreferred + nextType.typ.IsDefined, // typisdefined + nextType.typ.Delimiter, // typdelim + nextType.typ.RelID, // typrelid + pgtypes.FromFuncID(nextType.typ.SubscriptFunc).AsId(), // typsubscript + nextType.typ.Elem.ID.AsId(), // typelem + nextType.typ.Array.ID.AsId(), // typarray + pgtypes.FromFuncID(nextType.typ.InputFunc).AsId(), // typinput + pgtypes.FromFuncID(nextType.typ.OutputFunc).AsId(), // typoutput + pgtypes.FromFuncID(nextType.typ.ReceiveFunc).AsId(), // typreceive + pgtypes.FromFuncID(nextType.typ.SendFunc).AsId(), // typsend + pgtypes.FromFuncID(nextType.typ.ModInFunc).AsId(), // typmodin + pgtypes.FromFuncID(nextType.typ.ModOutFunc).AsId(), // typmodout + pgtypes.FromFuncID(nextType.typ.AnalyzeFunc).AsId(), // typanalyze + string(nextType.typ.Align), // typalign + string(nextType.typ.Storage), // typstorage + nextType.typ.NotNull, // typnotnull + nextType.typ.BaseTypeType.ID.AsId(), // typbasetype + nextType.typ.TypMod, // typtypmod + nextType.typ.NDims, // typndims + nextType.typ.TypCollation.AsId(), // typcollation + nextType.typ.DefaulBin, // typdefaultbin + nextType.typ.Default, // typdefault + typAcl, // typacl } } diff --git a/server/types/function_registry.go b/server/types/function_registry.go index 396d9d1957..f5d43aa4e7 100644 --- a/server/types/function_registry.go +++ b/server/types/function_registry.go @@ -159,3 +159,12 @@ func toFuncID(functionName string, params ...id.Type) uint32 { functionID := id.NewFunction("pg_catalog", functionName, params...) return globalFunctionRegistry.InternalToRegistryID(functionID) } + +// FromFuncID creates a valid function string for the given name and parameters, then registers the name with the +// global functionRegistry. The ID from the registry is returned. +func FromFuncID(u uint32) id.Function { + if u == 0 { + return id.NullFunction + } + return globalFunctionRegistry.GetInternalID(u) +}