You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a function returns a TABLE type, you get an "empty list" error or something like that. I'm sorry I saw this earlier today and don't have the exact output, but the repro below should work.
sqlc generate fails due to an error.
Relevant log output
No response
Database schema
CREATETABLEaccounts (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMAR KEY,
username TEXTNOT NULL UNIQUE,
password TEXTNOT NULL
);
-- this is a useless and horrifying function cause we don't hash-- the password, this is just to repro the bug in sqlcCREATE OR REPLACEFUNCTIONregister_account(
_username TEXT,
_password VARCHAR(70)
)
RETURNS TABLE (
account_id INTEGER
)
AS $$
BEGININSERT INTO accounts (username, password)
VALUES (
_username,
_password
)
RETURNING id INTO account_id;
RETURN NEXT;
END;
$$ LANGUAGE plpgsql;
The return type for this turns into pg_catalog.record, and the arguments have multiple parameters for each col type of mode == Table. I'm super new to this codebase, but my guess is you need to build a model for each possible function TABLE return type and then resolve to that.
Here's a rough diff I have so far that I think will generate model structs for function returns (lots of opinions in here I didn't know the answer to), but maybe helpful to get you on your way:
diff --git a/internal/codegen/golang/result.go b/internal/codegen/golang/result.go
index b213826..b0b9a52 100644
--- a/internal/codegen/golang/result.go+++ b/internal/codegen/golang/result.go@@ -10,6 +10,7 @@ import (
"github.com/kyleconroy/sqlc/internal/config"
"github.com/kyleconroy/sqlc/internal/core"
"github.com/kyleconroy/sqlc/internal/inflection"
+ "github.com/kyleconroy/sqlc/internal/sql/ast"
"github.com/kyleconroy/sqlc/internal/sql/catalog"
)
@@ -95,6 +96,61 @@ func buildStructs(r *compiler.Result, settings config.CombinedSettings) []Struct
}
structs = append(structs, s)
}
++ // Functions that return TABLE types need to have structs created.+ for _, f := range schema.Funcs {+ var cols []*catalog.Argument+ for _, arg := range f.Args {+ if arg.Mode == ast.FuncParamTable {+ cols = append(cols, arg)+ }+ }++ // If empty, this isn't a table type return argument.+ if len(cols) == 0 {+ continue+ }++ var tableName string+ if schema.Name == r.Catalog.DefaultSchema {+ tableName = f.Name+ } else {+ tableName = schema.Name + "_" + f.Name+ }+ structName := tableName+ if !settings.Go.EmitExactTableNames {+ structName = inflection.Singular(structName)+ }+ s := Struct{+ Table: core.FQN{Schema: schema.Name, Rel: f.Name},+ Name: StructName(structName, settings),+ Comment: f.Comment,+ }+ for _, column := range cols {+ tags := map[string]string{}+ if settings.Go.EmitDBTags {+ tags["db:"] = column.Name+ }+ if settings.Go.EmitJSONTags {+ tags["json:"] = JSONTagName(column.Name, settings)+ }+ s.Fields = append(s.Fields, Field{+ Name: StructName(column.Name, settings),+ Type: goType(r, compiler.ConvertColumn(&ast.TableName{+ Catalog: column.Type.Catalog,+ Schema: column.Type.Schema,+ Name: column.Type.Name,+ }, &catalog.Column{+ Name: column.Name,+ Type: *column.Type,+ IsNotNull: false, // func args are always nullable+ IsArray: false, // parser can't currently detect this?+ }), settings),+ Tags: tags,+ })+ }+ structs = append(structs, s)+ }
}
if len(structs) > 0 {
sort.Slice(structs, func(i, j int) bool { return structs[i].Name < structs[j].Name })
"Anonymous" tables with multiple fields still return interface{}: playground
Just changing it to:
CREATETABLEtemp_table (f1 INTEGER, f2 TEXT); -- 1. Add temporary tableCREATEFUNCTIONregister_account()
RETURNS temp_table -- 2. Use it as return typeAS $$
BEGIN
RETURN (5, "lol");
END;
$$ LANGUAGE plpgsql;
DROPTABLE temp_table; -- 3. Optionally drop it
works but it's such a crutch and I don't understand why can't the "anonymous" table just be interpreted as a table with the name equal to the name of the function or something like that. Is the problem in potential naming conflicts? Is there a relevant issue? I can't find one
Activity
mitchellh commentedon Dec 1, 2021
Okay, some helpful progress, dumping the catalog.
The return type for this turns into
pg_catalog.record, and the arguments have multiple parameters for each col type ofmode == Table. I'm super new to this codebase, but my guess is you need to build a model for each possible functionTABLEreturn type and then resolve to that.mitchellh commentedon Dec 1, 2021
Here's a rough diff I have so far that I think will generate model structs for function returns (lots of opinions in here I didn't know the answer to), but maybe helpful to get you on your way:
Fix #1322
Fix #1322
kyleconroy commentedon Oct 24, 2023
This is fixed in v1.23.0 by enabling the database-backed query analyzer. We added a test case for this issue so it won’t break in the future.
You can play around with the working example on the playground
Esensats commentedon Apr 9, 2025
"Anonymous" tables with multiple fields still return
interface{}: playgroundJust changing it to:
works but it's such a crutch and I don't understand why can't the "anonymous" table just be interpreted as a table with the name equal to the name of the function or something like that. Is the problem in potential naming conflicts? Is there a relevant issue? I can't find one
Edit: found a less crutchy solution
test(analyzer): Add more database analyzer test cases (sqlc-dev#2854)