From b52421341541e98ca5a8c08d8da6ed6c6033720b Mon Sep 17 00:00:00 2001 From: Joe Schafer Date: Mon, 1 Jan 2024 21:37:49 -0800 Subject: [PATCH 1/7] Upgrade to pgx v5 - For pggen row structs, implement pgx.RowScanner. - Remove the typeResolver, since we're punting type resolution to the pgx.Conn type map for now. #74 --- example/acceptance_test.go | 8 +- example/author/query.sql.go | 94 ++++-------- example/author/query.sql_test.go | 2 +- example/complex_params/query.sql.go | 6 +- example/composite/codegen_test.go | 2 +- example/composite/query.sql.go | 6 +- example/composite/query.sql_test.go | 2 +- example/custom_types/query.sql.go | 6 +- example/custom_types/query.sql_test.go | 4 +- example/device/query.sql.go | 6 +- example/device/query.sql_test.go | 2 +- example/domain/query.sql.go | 6 +- example/enums/query.sql.go | 6 +- example/enums/query.sql_test.go | 2 +- example/erp/order/customer.sql.go | 6 +- example/erp/order/customer.sql_test.go | 2 +- example/erp/order/price.sql.go | 2 +- example/function/query.sql.go | 6 +- example/go_pointer_types/query.sql.go | 6 +- .../inline_param_count/inline0/query.sql.go | 6 +- .../inline0/query.sql_test.go | 2 +- .../inline_param_count/inline1/query.sql.go | 6 +- .../inline1/query.sql_test.go | 2 +- .../inline_param_count/inline2/query.sql.go | 6 +- .../inline2/query.sql_test.go | 2 +- .../inline_param_count/inline3/query.sql.go | 6 +- .../inline3/query.sql_test.go | 2 +- example/ltree/codegen_test.go | 4 +- example/ltree/query.sql.go | 6 +- example/ltree/query.sql_test.go | 2 +- example/nested/query.sql.go | 10 +- example/pgcrypto/query.sql.go | 6 +- .../separate_out_dir/out/alpha_query.sql.0.go | 6 +- example/slices/query.sql.go | 6 +- example/syntax/query.sql.go | 6 +- example/void/query.sql.go | 6 +- generate.go | 2 +- go.mod | 7 +- go.sum | 21 ++- internal/codegen/golang/declarer_composite.go | 2 +- internal/codegen/golang/gotype/known_types.go | 142 +++++++++--------- internal/codegen/golang/gotype/types.go | 2 +- internal/codegen/golang/query.gotemplate | 31 +--- internal/codegen/golang/templater.go | 8 +- internal/codegen/golang/type_resolver_test.go | 6 +- internal/pg/column.go | 17 +-- internal/pg/column_test.go | 7 +- internal/pg/known_types.go | 4 +- internal/pg/query.sql.go | 128 +++++----------- internal/pg/type_cache.go | 19 ++- internal/pg/type_fetcher.go | 31 ++-- internal/pg/type_fetcher_test.go | 7 +- internal/pg/types.go | 89 ++++++----- internal/pgdocker/pgdocker.go | 2 +- internal/pginfer/pginfer.go | 14 +- internal/pgplan/pgplan.go | 2 +- internal/pgtest/pg_test_db.go | 2 +- 57 files changed, 346 insertions(+), 455 deletions(-) diff --git a/example/acceptance_test.go b/example/acceptance_test.go index c1e231a..5a8cb46 100644 --- a/example/acceptance_test.go +++ b/example/acceptance_test.go @@ -7,7 +7,7 @@ import ( "context" "flag" "fmt" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "github.com/jschaf/pggen/internal/errs" "github.com/jschaf/pggen/internal/pgdocker" "math/rand" @@ -57,7 +57,7 @@ func TestExamples(t *testing.T) { "--go-type", "int8=int", "--go-type", "int4=int", "--go-type", "text=string", - "--go-type", "citext=github.com/jackc/pgtype.Text", + "--go-type", "citext=github.com/jackc/pgx/v5/pgtype.Text", }, }, { @@ -179,8 +179,8 @@ func TestExamples(t *testing.T) { args: []string{ "--schema-glob", "example/ltree/schema.sql", "--query-glob", "example/ltree/query.sql", - "--go-type", "ltree=github.com/jackc/pgtype.Text", - "--go-type", "_ltree=github.com/jackc/pgtype.TextArray", + "--go-type", "ltree=github.com/jackc/pgx/v5/pgtype.Text", + "--go-type", "_ltree=github.com/jackc/pgx/v5/pgtype.TextArray", }, }, { diff --git a/example/author/query.sql.go b/example/author/query.sql.go index e654ea1..2b892be 100644 --- a/example/author/query.sql.go +++ b/example/author/query.sql.go @@ -5,9 +5,8 @@ package author import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" ) // Querier is a typesafe Go interface backed by SQL queries. @@ -48,8 +47,7 @@ type Querier interface { var _ Querier = &DBQuerier{} type DBQuerier struct { - conn genericConn // underlying Postgres transport to use - types *typeResolver // resolve types by name + conn genericConn // underlying Postgres transport to use } // genericConn is a connection like *pgx.Conn, pgx.Tx, or *pgxpool.Pool. @@ -61,36 +59,7 @@ type genericConn interface { // NewQuerier creates a DBQuerier that implements Querier. func NewQuerier(conn genericConn) *DBQuerier { - return &DBQuerier{conn: conn, types: newTypeResolver()} -} - -// typeResolver looks up the pgtype.ValueTranscoder by Postgres type name. -type typeResolver struct { - connInfo *pgtype.ConnInfo // types by Postgres type name -} - -func newTypeResolver() *typeResolver { - ci := pgtype.NewConnInfo() - return &typeResolver{connInfo: ci} -} - -// findValue find the OID, and pgtype.ValueTranscoder for a Postgres type name. -func (tr *typeResolver) findValue(name string) (uint32, pgtype.ValueTranscoder, bool) { - typ, ok := tr.connInfo.DataTypeForName(name) - if !ok { - return 0, nil, false - } - v := pgtype.NewValue(typ.Value) - return typ.OID, v.(pgtype.ValueTranscoder), true -} - -// setValue sets the value of a ValueTranscoder to a value that should always -// work and panics if it fails. -func (tr *typeResolver) setValue(vt pgtype.ValueTranscoder, val interface{}) pgtype.ValueTranscoder { - if err := vt.Set(val); err != nil { - panic(fmt.Sprintf("set ValueTranscoder %T to %+v: %s", vt, val, err)) - } - return vt + return &DBQuerier{conn: conn} } const findAuthorByIDSQL = `SELECT * FROM author WHERE author_id = $1;` @@ -102,12 +71,17 @@ type FindAuthorByIDRow struct { Suffix *string `json:"suffix"` } +func (r *FindAuthorByIDRow) scanRow(rows pgx.Rows) error { + return rows.Scan(&r.AuthorID, &r.FirstName, &r.LastName, &r.Suffix) +} + // FindAuthorByID implements Querier.FindAuthorByID. func (q *DBQuerier) FindAuthorByID(ctx context.Context, authorID int32) (FindAuthorByIDRow, error) { ctx = context.WithValue(ctx, "pggen_query_name", "FindAuthorByID") row := q.conn.QueryRow(ctx, findAuthorByIDSQL, authorID) var item FindAuthorByIDRow - if err := row.Scan(&item.AuthorID, &item.FirstName, &item.LastName, &item.Suffix); err != nil { + s := scanner[*FindAuthorByIDRow]{&item} + if err := row.Scan(s); err != nil { return item, fmt.Errorf("query FindAuthorByID: %w", err) } return item, nil @@ -122,6 +96,10 @@ type FindAuthorsRow struct { Suffix *string `json:"suffix"` } +func (r *FindAuthorsRow) scanRow(rows pgx.Rows) error { + return rows.Scan(&r.AuthorID, &r.FirstName, &r.LastName, &r.Suffix) +} + // FindAuthors implements Querier.FindAuthors. func (q *DBQuerier) FindAuthors(ctx context.Context, firstName string) ([]FindAuthorsRow, error) { ctx = context.WithValue(ctx, "pggen_query_name", "FindAuthors") @@ -131,9 +109,11 @@ func (q *DBQuerier) FindAuthors(ctx context.Context, firstName string) ([]FindAu } defer rows.Close() items := []FindAuthorsRow{} + s := scanner[*FindAuthorsRow]{} for rows.Next() { var item FindAuthorsRow - if err := rows.Scan(&item.AuthorID, &item.FirstName, &item.LastName, &item.Suffix); err != nil { + s.item = &item + if err := rows.Scan(s); err != nil { return nil, fmt.Errorf("scan FindAuthors row: %w", err) } items = append(items, item) @@ -151,6 +131,10 @@ type FindAuthorNamesRow struct { LastName *string `json:"last_name"` } +func (r *FindAuthorNamesRow) scanRow(rows pgx.Rows) error { + return rows.Scan(&r.FirstName, &r.LastName) +} + // FindAuthorNames implements Querier.FindAuthorNames. func (q *DBQuerier) FindAuthorNames(ctx context.Context, authorID int32) ([]FindAuthorNamesRow, error) { ctx = context.WithValue(ctx, "pggen_query_name", "FindAuthorNames") @@ -160,9 +144,11 @@ func (q *DBQuerier) FindAuthorNames(ctx context.Context, authorID int32) ([]Find } defer rows.Close() items := []FindAuthorNamesRow{} + s := scanner[*FindAuthorNamesRow]{} for rows.Next() { var item FindAuthorNamesRow - if err := rows.Scan(&item.FirstName, &item.LastName); err != nil { + s.item = &item + if err := rows.Scan(s); err != nil { return nil, fmt.Errorf("scan FindAuthorNames row: %w", err) } items = append(items, item) @@ -275,12 +261,17 @@ type InsertAuthorSuffixRow struct { Suffix *string `json:"suffix"` } +func (r *InsertAuthorSuffixRow) scanRow(rows pgx.Rows) error { + return rows.Scan(&r.AuthorID, &r.FirstName, &r.LastName, &r.Suffix) +} + // InsertAuthorSuffix implements Querier.InsertAuthorSuffix. func (q *DBQuerier) InsertAuthorSuffix(ctx context.Context, params InsertAuthorSuffixParams) (InsertAuthorSuffixRow, error) { ctx = context.WithValue(ctx, "pggen_query_name", "InsertAuthorSuffix") row := q.conn.QueryRow(ctx, insertAuthorSuffixSQL, params.FirstName, params.LastName, params.Suffix) var item InsertAuthorSuffixRow - if err := row.Scan(&item.AuthorID, &item.FirstName, &item.LastName, &item.Suffix); err != nil { + s := scanner[*InsertAuthorSuffixRow]{&item} + if err := row.Scan(s); err != nil { return item, fmt.Errorf("query InsertAuthorSuffix: %w", err) } return item, nil @@ -312,27 +303,8 @@ func (q *DBQuerier) ArrayAggFirstName(ctx context.Context, authorID int32) ([]st return item, nil } -// textPreferrer wraps a pgtype.ValueTranscoder and sets the preferred encoding -// format to text instead binary (the default). pggen uses the text format -// when the OID is unknownOID because the binary format requires the OID. -// Typically occurs for unregistered types. -type textPreferrer struct { - pgtype.ValueTranscoder - typeName string -} - -// PreferredParamFormat implements pgtype.ParamFormatPreferrer. -func (t textPreferrer) PreferredParamFormat() int16 { return pgtype.TextFormatCode } +type rowScanner interface{ scanRow(rows pgx.Rows) error } -func (t textPreferrer) NewTypeValue() pgtype.Value { - return textPreferrer{ValueTranscoder: pgtype.NewValue(t.ValueTranscoder).(pgtype.ValueTranscoder), typeName: t.typeName} -} - -func (t textPreferrer) TypeName() string { - return t.typeName -} +type scanner[T rowScanner] struct{ item T } -// unknownOID means we don't know the OID for a type. This is okay for decoding -// because pgx call DecodeText or DecodeBinary without requiring the OID. For -// encoding parameters, pggen uses textPreferrer if the OID is unknown. -const unknownOID = 0 +func (s scanner[T]) ScanRow(rows pgx.Rows) error { return s.item.scanRow(rows) } diff --git a/example/author/query.sql_test.go b/example/author/query.sql_test.go index 9b4f753..55521e4 100644 --- a/example/author/query.sql_test.go +++ b/example/author/query.sql_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" "testing" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "github.com/jschaf/pggen/internal/pgtest" "github.com/stretchr/testify/assert" ) diff --git a/example/complex_params/query.sql.go b/example/complex_params/query.sql.go index 4315bdc..fc0589f 100644 --- a/example/complex_params/query.sql.go +++ b/example/complex_params/query.sql.go @@ -5,9 +5,9 @@ package complex_params import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/composite/codegen_test.go b/example/composite/codegen_test.go index 68334b1..3972f02 100644 --- a/example/composite/codegen_test.go +++ b/example/composite/codegen_test.go @@ -28,7 +28,7 @@ func TestGenerate_Go_Example_Composite(t *testing.T) { "int8": "int", "int4": "int", "text": "string", - "citext": "github.com/jackc/pgtype.Text", + "citext": "github.com/jackc/pgx/v5/pgtype.Text", }, }) if err != nil { diff --git a/example/composite/query.sql.go b/example/composite/query.sql.go index c4b9b0b..a62844a 100644 --- a/example/composite/query.sql.go +++ b/example/composite/query.sql.go @@ -5,9 +5,9 @@ package composite import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/composite/query.sql_test.go b/example/composite/query.sql_test.go index 1a9d0d0..f8e8e82 100644 --- a/example/composite/query.sql_test.go +++ b/example/composite/query.sql_test.go @@ -2,7 +2,7 @@ package composite import ( "context" - "github.com/jackc/pgtype" + "github.com/jackc/pgx/v5/pgtype" "github.com/jschaf/pggen/internal/difftest" "github.com/jschaf/pggen/internal/pgtest" "github.com/jschaf/pggen/internal/ptrs" diff --git a/example/custom_types/query.sql.go b/example/custom_types/query.sql.go index 133c424..326d2b2 100644 --- a/example/custom_types/query.sql.go +++ b/example/custom_types/query.sql.go @@ -5,9 +5,9 @@ package custom_types import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" "github.com/jschaf/pggen/example/custom_types/mytype" ) diff --git a/example/custom_types/query.sql_test.go b/example/custom_types/query.sql_test.go index 1912fe9..e98d7cd 100644 --- a/example/custom_types/query.sql_test.go +++ b/example/custom_types/query.sql_test.go @@ -2,7 +2,7 @@ package custom_types import ( "context" - "github.com/jackc/pgtype" + "github.com/jackc/pgx/v5/pgtype" "github.com/jschaf/pggen/internal/pgtest" "github.com/jschaf/pggen/internal/texts" "github.com/stretchr/testify/assert" @@ -38,7 +38,7 @@ func TestQuerier_CustomMyInt(t *testing.T) { AND pn.nspname = current_schema() LIMIT 1; `)) - oidVal := pgtype.OIDValue{} + oidVal := uint32Value{} err := row.Scan(&oidVal) require.NoError(t, err) t.Logf("my_int oid: %d", oidVal.Uint) diff --git a/example/device/query.sql.go b/example/device/query.sql.go index d2b6fcd..cdad236 100644 --- a/example/device/query.sql.go +++ b/example/device/query.sql.go @@ -5,9 +5,9 @@ package device import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/device/query.sql_test.go b/example/device/query.sql_test.go index 3319f94..95171f6 100644 --- a/example/device/query.sql_test.go +++ b/example/device/query.sql_test.go @@ -2,7 +2,7 @@ package device import ( "context" - "github.com/jackc/pgtype" + "github.com/jackc/pgx/v5/pgtype" "github.com/jschaf/pggen/internal/pgtest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/example/domain/query.sql.go b/example/domain/query.sql.go index 01130f2..3d1de22 100644 --- a/example/domain/query.sql.go +++ b/example/domain/query.sql.go @@ -5,9 +5,9 @@ package domain import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/enums/query.sql.go b/example/enums/query.sql.go index cbaea2e..c72c9d9 100644 --- a/example/enums/query.sql.go +++ b/example/enums/query.sql.go @@ -5,9 +5,9 @@ package enums import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/enums/query.sql_test.go b/example/enums/query.sql_test.go index e4d87bd..e36f04a 100644 --- a/example/enums/query.sql_test.go +++ b/example/enums/query.sql_test.go @@ -2,7 +2,7 @@ package enums import ( "context" - "github.com/jackc/pgtype" + "github.com/jackc/pgx/v5/pgtype" "github.com/jschaf/pggen/internal/pgtest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/example/erp/order/customer.sql.go b/example/erp/order/customer.sql.go index ca372b2..45aca9c 100644 --- a/example/erp/order/customer.sql.go +++ b/example/erp/order/customer.sql.go @@ -5,9 +5,9 @@ package order import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/erp/order/customer.sql_test.go b/example/erp/order/customer.sql_test.go index f979954..8349839 100644 --- a/example/erp/order/customer.sql_test.go +++ b/example/erp/order/customer.sql_test.go @@ -2,7 +2,7 @@ package order import ( "context" - "github.com/jackc/pgtype" + "github.com/jackc/pgx/v5/pgtype" "github.com/jschaf/pggen/internal/pgtest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/example/erp/order/price.sql.go b/example/erp/order/price.sql.go index 2395a2c..c4c0b9a 100644 --- a/example/erp/order/price.sql.go +++ b/example/erp/order/price.sql.go @@ -5,7 +5,7 @@ package order import ( "context" "fmt" - "github.com/jackc/pgtype" + "github.com/jackc/pgx/v5/pgtype" ) const findOrdersByPriceSQL = `SELECT * FROM orders WHERE order_total > $1;` diff --git a/example/function/query.sql.go b/example/function/query.sql.go index ebfd5e7..c7ba5bd 100644 --- a/example/function/query.sql.go +++ b/example/function/query.sql.go @@ -5,9 +5,9 @@ package function import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/go_pointer_types/query.sql.go b/example/go_pointer_types/query.sql.go index fce3f65..08276e5 100644 --- a/example/go_pointer_types/query.sql.go +++ b/example/go_pointer_types/query.sql.go @@ -5,9 +5,9 @@ package go_pointer_types import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/inline_param_count/inline0/query.sql.go b/example/inline_param_count/inline0/query.sql.go index 4b4bbac..c63d4b9 100644 --- a/example/inline_param_count/inline0/query.sql.go +++ b/example/inline_param_count/inline0/query.sql.go @@ -5,9 +5,9 @@ package inline0 import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/inline_param_count/inline0/query.sql_test.go b/example/inline_param_count/inline0/query.sql_test.go index 2babf0a..a7b1bb8 100644 --- a/example/inline_param_count/inline0/query.sql_test.go +++ b/example/inline_param_count/inline0/query.sql_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" "testing" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "github.com/jschaf/pggen/internal/pgtest" "github.com/stretchr/testify/assert" ) diff --git a/example/inline_param_count/inline1/query.sql.go b/example/inline_param_count/inline1/query.sql.go index 0583f0d..2b55216 100644 --- a/example/inline_param_count/inline1/query.sql.go +++ b/example/inline_param_count/inline1/query.sql.go @@ -5,9 +5,9 @@ package inline1 import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/inline_param_count/inline1/query.sql_test.go b/example/inline_param_count/inline1/query.sql_test.go index c3bbfc9..af98925 100644 --- a/example/inline_param_count/inline1/query.sql_test.go +++ b/example/inline_param_count/inline1/query.sql_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" "testing" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "github.com/jschaf/pggen/internal/pgtest" "github.com/stretchr/testify/assert" ) diff --git a/example/inline_param_count/inline2/query.sql.go b/example/inline_param_count/inline2/query.sql.go index b5624ca..113ecb3 100644 --- a/example/inline_param_count/inline2/query.sql.go +++ b/example/inline_param_count/inline2/query.sql.go @@ -5,9 +5,9 @@ package inline2 import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/inline_param_count/inline2/query.sql_test.go b/example/inline_param_count/inline2/query.sql_test.go index b119f7f..a17347f 100644 --- a/example/inline_param_count/inline2/query.sql_test.go +++ b/example/inline_param_count/inline2/query.sql_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" "testing" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "github.com/jschaf/pggen/internal/pgtest" "github.com/stretchr/testify/assert" ) diff --git a/example/inline_param_count/inline3/query.sql.go b/example/inline_param_count/inline3/query.sql.go index 8940776..88f0c87 100644 --- a/example/inline_param_count/inline3/query.sql.go +++ b/example/inline_param_count/inline3/query.sql.go @@ -5,9 +5,9 @@ package inline3 import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/inline_param_count/inline3/query.sql_test.go b/example/inline_param_count/inline3/query.sql_test.go index 527a983..b4e9121 100644 --- a/example/inline_param_count/inline3/query.sql_test.go +++ b/example/inline_param_count/inline3/query.sql_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" "testing" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "github.com/jschaf/pggen/internal/pgtest" "github.com/stretchr/testify/assert" ) diff --git a/example/ltree/codegen_test.go b/example/ltree/codegen_test.go index f5f1fec..2a05d1f 100644 --- a/example/ltree/codegen_test.go +++ b/example/ltree/codegen_test.go @@ -23,8 +23,8 @@ func TestGenerate_Go_Example_ltree(t *testing.T) { Language: pggen.LangGo, InlineParamCount: 2, TypeOverrides: map[string]string{ - "ltree": "github.com/jackc/pgtype.Text", - "_ltree": "github.com/jackc/pgtype.TextArray", + "ltree": "github.com/jackc/pgx/v5/pgtype.Text", + "_ltree": "github.com/jackc/pgx/v5/pgtype.TextArray", }, }) if err != nil { diff --git a/example/ltree/query.sql.go b/example/ltree/query.sql.go index aee3db7..537e234 100644 --- a/example/ltree/query.sql.go +++ b/example/ltree/query.sql.go @@ -5,9 +5,9 @@ package ltree import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/ltree/query.sql_test.go b/example/ltree/query.sql_test.go index 83d0d8d..ce591bb 100644 --- a/example/ltree/query.sql_test.go +++ b/example/ltree/query.sql_test.go @@ -2,7 +2,7 @@ package ltree import ( "context" - "github.com/jackc/pgtype" + "github.com/jackc/pgx/v5/pgtype" "github.com/jschaf/pggen/internal/pgtest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/example/nested/query.sql.go b/example/nested/query.sql.go index 1ffb407..f1bc80b 100644 --- a/example/nested/query.sql.go +++ b/example/nested/query.sql.go @@ -5,9 +5,9 @@ package nested import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. @@ -57,7 +57,7 @@ type ProductImageType struct { // typeResolver looks up the pgtype.ValueTranscoder by Postgres type name. type typeResolver struct { - connInfo *pgtype.ConnInfo // types by Postgres type name + connInfo *pgtype.Codec // types by Postgres type name } func newTypeResolver() *typeResolver { @@ -148,6 +148,7 @@ func (tr *typeResolver) newDimensions() pgtype.ValueTranscoder { // newProductImageSetType creates a new pgtype.ValueTranscoder for the Postgres // composite type 'product_image_set_type'. func (tr *typeResolver) newProductImageSetType() pgtype.ValueTranscoder { + pgtype.CompositeCodec{} return tr.newCompositeValue( "product_image_set_type", compositeField{name: "name", typeName: "text", defaultVal: &pgtype.Text{}}, @@ -169,6 +170,7 @@ func (tr *typeResolver) newProductImageType() pgtype.ValueTranscoder { // newProductImageTypeArray creates a new pgtype.ValueTranscoder for the Postgres // '_product_image_type' array type. func (tr *typeResolver) newProductImageTypeArray() pgtype.ValueTranscoder { + pgtype.CompositeCodec{} return tr.newArrayValue("_product_image_type", "product_image_type", tr.newProductImageType) } diff --git a/example/pgcrypto/query.sql.go b/example/pgcrypto/query.sql.go index 90be999..ff47851 100644 --- a/example/pgcrypto/query.sql.go +++ b/example/pgcrypto/query.sql.go @@ -5,9 +5,9 @@ package pgcrypto import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/separate_out_dir/out/alpha_query.sql.0.go b/example/separate_out_dir/out/alpha_query.sql.0.go index bfebba4..f99f15d 100644 --- a/example/separate_out_dir/out/alpha_query.sql.0.go +++ b/example/separate_out_dir/out/alpha_query.sql.0.go @@ -5,9 +5,9 @@ package out import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/slices/query.sql.go b/example/slices/query.sql.go index 8ef9062..ed0fad1 100644 --- a/example/slices/query.sql.go +++ b/example/slices/query.sql.go @@ -5,9 +5,9 @@ package slices import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" "time" ) diff --git a/example/syntax/query.sql.go b/example/syntax/query.sql.go index b221822..85bf066 100644 --- a/example/syntax/query.sql.go +++ b/example/syntax/query.sql.go @@ -5,9 +5,9 @@ package syntax import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/example/void/query.sql.go b/example/void/query.sql.go index 62fb382..146b3cf 100644 --- a/example/void/query.sql.go +++ b/example/void/query.sql.go @@ -5,9 +5,9 @@ package void import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. diff --git a/generate.go b/generate.go index 94aef72..d7bf869 100644 --- a/generate.go +++ b/generate.go @@ -4,7 +4,7 @@ import ( "context" "errors" "fmt" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "github.com/jschaf/pggen/internal/ast" "github.com/jschaf/pggen/internal/codegen" "github.com/jschaf/pggen/internal/codegen/golang" diff --git a/go.mod b/go.mod index a46a9a7..db08d69 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/jackc/pgproto3/v2 v2.3.2 github.com/jackc/pgtype v1.14.0 github.com/jackc/pgx/v4 v4.18.1 + github.com/jackc/pgx/v5 v5.5.1 github.com/peterbourgon/ff/v3 v3.4.0 github.com/shopspring/decimal v1.3.1 github.com/stretchr/testify v1.8.4 @@ -26,8 +27,7 @@ require ( github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/kr/pretty v0.2.1 // indirect + github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect github.com/kr/text v0.2.0 // indirect github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect github.com/morikuni/aec v1.0.0 // indirect @@ -35,7 +35,8 @@ require ( github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.16.0 // indirect + github.com/rogpeppe/go-internal v1.6.1 // indirect + golang.org/x/crypto v0.17.0 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/sync v0.4.0 // indirect golang.org/x/sys v0.15.0 // indirect diff --git a/go.sum b/go.sum index 32ca3a7..2846269 100644 --- a/go.sum +++ b/go.sum @@ -67,8 +67,9 @@ github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= @@ -81,17 +82,22 @@ github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQ github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= +github.com/jackc/pgx/v5 v5.5.1 h1:5I9etrGkLrN+2XPCsi6XLlV5DITbSL/xBZdmAxFcXPI= +github.com/jackc/pgx/v5 v5.5.1/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -123,6 +129,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= @@ -175,8 +183,8 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -258,8 +266,9 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/codegen/golang/declarer_composite.go b/internal/codegen/golang/declarer_composite.go index 3dc1988..34625c0 100644 --- a/internal/codegen/golang/declarer_composite.go +++ b/internal/codegen/golang/declarer_composite.go @@ -263,7 +263,7 @@ func (c CompositeInitDeclarer) Declare(string) (string, error) { // of a composite type as a generic array: []interface{}. Necessary because we // can only set pgtype.CompositeType from a []interface{}. // -// Revisit after https://github.com/jackc/pgtype/pull/100 to see if we can +// Revisit after https://github.com/jackc/pgx/v5/pgtype/pull/100 to see if we can // simplify. type CompositeRawDeclarer struct { typ *gotype.CompositeType diff --git a/internal/codegen/golang/gotype/known_types.go b/internal/codegen/golang/gotype/known_types.go index 6d893f5..87ed8b4 100644 --- a/internal/codegen/golang/gotype/known_types.go +++ b/internal/codegen/golang/gotype/known_types.go @@ -1,14 +1,14 @@ package gotype import ( - "github.com/jackc/pgtype" + "github.com/jackc/pgx/v5/pgtype" "github.com/jschaf/pggen/internal/pg" "github.com/jschaf/pggen/internal/pg/pgoid" ) // FindKnownTypePgx returns the native pgx type, like pgtype.Text, if known, for // a Postgres OID. If there is no known type, returns nil. -func FindKnownTypePgx(oid pgtype.OID) (Type, bool) { +func FindKnownTypePgx(oid uint32) (Type, bool) { typ, ok := knownTypesByOID[oid] return typ.pgNative, ok } @@ -16,7 +16,7 @@ func FindKnownTypePgx(oid pgtype.OID) (Type, bool) { // FindKnownTypeNullable returns the nullable type, like *string, if known, for // a Postgres OID. Falls back to the pgNative type. If there is no known type // for the OID, returns nil. -func FindKnownTypeNullable(oid pgtype.OID) (Type, bool) { +func FindKnownTypeNullable(oid uint32) (Type, bool) { typ, ok := knownTypesByOID[oid] if !ok { return nil, false @@ -30,7 +30,7 @@ func FindKnownTypeNullable(oid pgtype.OID) (Type, bool) { // FindKnownTypeNonNullable returns the non-nullable type like string, if known, // for a Postgres OID. Falls back to the nullable type and pgNative type. If // there is no known type for the OID, returns nil. -func FindKnownTypeNonNullable(oid pgtype.OID) (Type, bool) { +func FindKnownTypeNonNullable(oid uint32) (Type, bool) { typ, ok := knownTypesByOID[oid] if !ok { return nil, false @@ -91,73 +91,73 @@ var ( // pgtype types prefixed with "pg". var ( - PgBool = MustParseKnownType("github.com/jackc/pgtype.Bool", pg.Bool) - PgQChar = MustParseKnownType("github.com/jackc/pgtype.QChar", pg.QChar) - PgName = MustParseKnownType("github.com/jackc/pgtype.Name", pg.Name) - PgInt8 = MustParseKnownType("github.com/jackc/pgtype.Int8", pg.Int8) - PgInt2 = MustParseKnownType("github.com/jackc/pgtype.Int2", pg.Int2) - PgInt4 = MustParseKnownType("github.com/jackc/pgtype.Int4", pg.Int4) - PgText = MustParseKnownType("github.com/jackc/pgtype.Text", pg.Text) - PgBytea = MustParseKnownType("github.com/jackc/pgtype.Bytea", pg.Bytea) - PgOID = MustParseKnownType("github.com/jackc/pgtype.OID", pg.OID) - PgTID = MustParseKnownType("github.com/jackc/pgtype.TID", pg.TID) - PgXID = MustParseKnownType("github.com/jackc/pgtype.XID", pg.XID) - PgCID = MustParseKnownType("github.com/jackc/pgtype.CID", pg.CID) - PgJSON = MustParseKnownType("github.com/jackc/pgtype.JSON", pg.JSON) - PgPoint = MustParseKnownType("github.com/jackc/pgtype.Point", pg.Point) - PgLseg = MustParseKnownType("github.com/jackc/pgtype.Lseg", pg.Lseg) - PgPath = MustParseKnownType("github.com/jackc/pgtype.Path", pg.Path) - PgBox = MustParseKnownType("github.com/jackc/pgtype.Box", pg.Box) - PgPolygon = MustParseKnownType("github.com/jackc/pgtype.Polygon", pg.Polygon) - PgLine = MustParseKnownType("github.com/jackc/pgtype.Line", pg.Line) - PgCIDR = MustParseKnownType("github.com/jackc/pgtype.CIDR", pg.CIDR) - PgCIDRArray = MustParseKnownType("github.com/jackc/pgtype.CIDRArray", pg.CIDRArray) - PgFloat4 = MustParseKnownType("github.com/jackc/pgtype.Float4", pg.Float4) - PgFloat8 = MustParseKnownType("github.com/jackc/pgtype.Float8", pg.Float8) - PgUnknown = MustParseKnownType("github.com/jackc/pgtype.Unknown", pg.Unknown) - PgCircle = MustParseKnownType("github.com/jackc/pgtype.Circle", pg.Circle) - PgMacaddr = MustParseKnownType("github.com/jackc/pgtype.Macaddr", pg.Macaddr) - PgInet = MustParseKnownType("github.com/jackc/pgtype.Inet", pg.Inet) - PgBoolArray = MustParseKnownType("github.com/jackc/pgtype.BoolArray", pg.BoolArray) - PgByteaArray = MustParseKnownType("github.com/jackc/pgtype.ByteaArray", pg.ByteaArray) - PgInt2Array = MustParseKnownType("github.com/jackc/pgtype.Int2Array", pg.Int2Array) - PgInt4Array = MustParseKnownType("github.com/jackc/pgtype.Int4Array", pg.Int4Array) - PgTextArray = MustParseKnownType("github.com/jackc/pgtype.TextArray", pg.TextArray) - PgBPCharArray = MustParseKnownType("github.com/jackc/pgtype.BPCharArray", pg.BPCharArray) - PgVarcharArray = MustParseKnownType("github.com/jackc/pgtype.VarcharArray", pg.VarcharArray) - PgInt8Array = MustParseKnownType("github.com/jackc/pgtype.Int8Array", pg.Int8Array) - PgFloat4Array = MustParseKnownType("github.com/jackc/pgtype.Float4Array", pg.Float4Array) - PgFloat8Array = MustParseKnownType("github.com/jackc/pgtype.Float8Array", pg.Float8Array) - PgACLItem = MustParseKnownType("github.com/jackc/pgtype.ACLItem", pg.ACLItem) - PgACLItemArray = MustParseKnownType("github.com/jackc/pgtype.ACLItemArray", pg.ACLItemArray) - PgInetArray = MustParseKnownType("github.com/jackc/pgtype.InetArray", pg.InetArray) - PgMacaddrArray = MustParseKnownType("github.com/jackc/pgtype.MacaddrArray", pg.MacaddrArray) - PgBPChar = MustParseKnownType("github.com/jackc/pgtype.BPChar", pg.BPChar) - PgVarchar = MustParseKnownType("github.com/jackc/pgtype.Varchar", pg.Varchar) - PgDate = MustParseKnownType("github.com/jackc/pgtype.Date", pg.Date) - PgTime = MustParseKnownType("github.com/jackc/pgtype.Time", pg.Time) - PgTimestamp = MustParseKnownType("github.com/jackc/pgtype.Timestamp", pg.Timestamp) - PgTimestampArray = MustParseKnownType("github.com/jackc/pgtype.TimestampArray", pg.TimestampArray) - PgDateArray = MustParseKnownType("github.com/jackc/pgtype.DateArray", pg.DateArray) - PgTimestamptz = MustParseKnownType("github.com/jackc/pgtype.Timestamptz", pg.Timestamptz) - PgTimestamptzArray = MustParseKnownType("github.com/jackc/pgtype.TimestamptzArray", pg.TimestamptzArray) - PgInterval = MustParseKnownType("github.com/jackc/pgtype.Interval", pg.Interval) - PgNumericArray = MustParseKnownType("github.com/jackc/pgtype.NumericArray", pg.NumericArray) - PgBit = MustParseKnownType("github.com/jackc/pgtype.Bit", pg.Bit) - PgVarbit = MustParseKnownType("github.com/jackc/pgtype.Varbit", pg.Varbit) + PgBool = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Bool", pg.Bool) + PgQChar = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.QChar", pg.QChar) + PgName = MustParseKnownType("string", pg.Name) + PgInt8 = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Int8", pg.Int8) + PgInt2 = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Int2", pg.Int2) + PgInt4 = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Int4", pg.Int4) + PgText = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Text", pg.Text) + PgBytea = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Bytea", pg.Bytea) + PgOID = MustParseKnownType("uint32", pg.OID) + PgTID = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.TID", pg.TID) + PgXID = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.XID", pg.XID) + PgCID = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.CID", pg.CID) + PgJSON = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.JSON", pg.JSON) + PgPoint = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Point", pg.Point) + PgLseg = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Lseg", pg.Lseg) + PgPath = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Path", pg.Path) + PgBox = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Box", pg.Box) + PgPolygon = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Polygon", pg.Polygon) + PgLine = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Line", pg.Line) + PgCIDR = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.CIDR", pg.CIDR) + PgCIDRArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.CIDRArray", pg.CIDRArray) + PgFloat4 = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Float4", pg.Float4) + PgFloat8 = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Float8", pg.Float8) + PgUnknown = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Unknown", pg.Unknown) + PgCircle = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Circle", pg.Circle) + PgMacaddr = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Macaddr", pg.Macaddr) + PgInet = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Inet", pg.Inet) + PgBoolArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.BoolArray", pg.BoolArray) + PgByteaArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.ByteaArray", pg.ByteaArray) + PgInt2Array = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Int2Array", pg.Int2Array) + PgInt4Array = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Int4Array", pg.Int4Array) + PgTextArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.TextArray", pg.TextArray) + PgBPCharArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.BPCharArray", pg.BPCharArray) + PgVarcharArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.VarcharArray", pg.VarcharArray) + PgInt8Array = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Int8Array", pg.Int8Array) + PgFloat4Array = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Float4Array", pg.Float4Array) + PgFloat8Array = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Float8Array", pg.Float8Array) + PgACLItem = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.ACLItem", pg.ACLItem) + PgACLItemArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.ACLItemArray", pg.ACLItemArray) + PgInetArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.InetArray", pg.InetArray) + PgMacaddrArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.MacaddrArray", pg.MacaddrArray) + PgBPChar = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.BPChar", pg.BPChar) + PgVarchar = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Varchar", pg.Varchar) + PgDate = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Date", pg.Date) + PgTime = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Time", pg.Time) + PgTimestamp = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Timestamp", pg.Timestamp) + PgTimestampArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.TimestampArray", pg.TimestampArray) + PgDateArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.DateArray", pg.DateArray) + PgTimestamptz = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Timestamptz", pg.Timestamptz) + PgTimestamptzArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.TimestamptzArray", pg.TimestamptzArray) + PgInterval = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Interval", pg.Interval) + PgNumericArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.NumericArray", pg.NumericArray) + PgBit = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Bit", pg.Bit) + PgVarbit = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Varbit", pg.Varbit) PgVoid = &VoidType{} - PgNumeric = MustParseKnownType("github.com/jackc/pgtype.Numeric", pg.Numeric) - PgRecord = MustParseKnownType("github.com/jackc/pgtype.Record", pg.Record) - PgUUID = MustParseKnownType("github.com/jackc/pgtype.UUID", pg.UUID) - PgUUIDArray = MustParseKnownType("github.com/jackc/pgtype.UUIDArray", pg.UUIDArray) - PgJSONB = MustParseKnownType("github.com/jackc/pgtype.JSONB", pg.JSONB) - PgJSONBArray = MustParseKnownType("github.com/jackc/pgtype.JSONBArray", pg.JSONBArray) - PgInt4range = MustParseKnownType("github.com/jackc/pgtype.Int4range", pg.Int4range) - PgNumrange = MustParseKnownType("github.com/jackc/pgtype.Numrange", pg.Numrange) - PgTsrange = MustParseKnownType("github.com/jackc/pgtype.Tsrange", pg.Tsrange) - PgTstzrange = MustParseKnownType("github.com/jackc/pgtype.Tstzrange", pg.Tstzrange) - PgDaterange = MustParseKnownType("github.com/jackc/pgtype.Daterange", pg.Daterange) - PgInt8range = MustParseKnownType("github.com/jackc/pgtype.Int8range", pg.Int8range) + PgNumeric = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Numeric", pg.Numeric) + PgRecord = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Record", pg.Record) + PgUUID = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.UUID", pg.UUID) + PgUUIDArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.UUIDArray", pg.UUIDArray) + PgJSONB = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.JSONB", pg.JSONB) + PgJSONBArray = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.JSONBArray", pg.JSONBArray) + PgInt4range = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Int4range", pg.Int4range) + PgNumrange = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Numrange", pg.Numrange) + PgTsrange = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Tsrange", pg.Tsrange) + PgTstzrange = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Tstzrange", pg.Tstzrange) + PgDaterange = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Daterange", pg.Daterange) + PgInt8range = MustParseKnownType("github.com/jackc/pgx/v5/pgtype.Int8range", pg.Int8range) ) // knownGoType is the native pgtype type, the nullable and non-nullable types @@ -177,7 +177,7 @@ var ( // "string" for a Postgres text type. type knownGoType struct{ pgNative, nullable, nonNullable Type } -var knownTypesByOID = map[pgtype.OID]knownGoType{ +var knownTypesByOID = map[uint32]knownGoType{ pgtype.BoolOID: {PgBool, Boolp, Bool}, pgtype.QCharOID: {PgQChar, nil, nil}, pgtype.NameOID: {PgName, nil, nil}, diff --git a/internal/codegen/golang/gotype/types.go b/internal/codegen/golang/gotype/types.go index 65e8fce..199d1e8 100644 --- a/internal/codegen/golang/gotype/types.go +++ b/internal/codegen/golang/gotype/types.go @@ -240,7 +240,7 @@ func ParseOpaqueType(qualType string, pgType pg.Type) (Type, error) { } // MustParseKnownType creates a gotype.Type by parsing a fully qualified Go type -// that pgx supports natively like "github.com/jackc/pgtype.Int4Array", or most +// that pgx supports natively like "github.com/jackc/pgx/v5/pgtype.Int4Array", or most // builtin types like "string" and []*int16. func MustParseKnownType(qualType string, pgType pg.Type) Type { typ, err := ParseOpaqueType(qualType, pgType) diff --git a/internal/codegen/golang/query.gotemplate b/internal/codegen/golang/query.gotemplate index 720befb..d645667 100644 --- a/internal/codegen/golang/query.gotemplate +++ b/internal/codegen/golang/query.gotemplate @@ -28,7 +28,6 @@ var _ Querier = &DBQuerier{} type DBQuerier struct { conn genericConn // underlying Postgres transport to use - types *typeResolver // resolve types by name } // genericConn is a connection like *pgx.Conn, pgx.Tx, or *pgxpool.Pool. @@ -40,7 +39,7 @@ type genericConn interface { // NewQuerier creates a DBQuerier that implements Querier. func NewQuerier(conn genericConn) *DBQuerier { - return &DBQuerier{conn: conn, types: newTypeResolver()} + return &DBQuerier{conn: conn} } {{- range .Declarers}}{{- "\n\n" -}}{{ .Declare $.PkgPath }}{{ end -}} @@ -93,33 +92,5 @@ func (q *DBQuerier) {{ $q.Name }}(ctx context.Context {{- $q.EmitParams }}) ({{ {{- end }} } {{- end -}} - -{{- if .IsLeader -}} -{{- "\n\n" -}} -// textPreferrer wraps a pgtype.ValueTranscoder and sets the preferred encoding -// format to text instead binary (the default). pggen uses the text format -// when the OID is unknownOID because the binary format requires the OID. -// Typically occurs for unregistered types. -type textPreferrer struct { - pgtype.ValueTranscoder - typeName string -} - -// PreferredParamFormat implements pgtype.ParamFormatPreferrer. -func (t textPreferrer) PreferredParamFormat() int16 { return pgtype.TextFormatCode } - -func (t textPreferrer) NewTypeValue() pgtype.Value { - return textPreferrer{ValueTranscoder: pgtype.NewValue(t.ValueTranscoder).(pgtype.ValueTranscoder), typeName: t.typeName} -} - -func (t textPreferrer) TypeName() string { - return t.typeName -} - -// unknownOID means we don't know the OID for a type. This is okay for decoding -// because pgx call DecodeText or DecodeBinary without requiring the OID. For -// encoding parameters, pggen uses textPreferrer if the OID is unknown. -const unknownOID = 0 -{{- end -}} {{- "\n" -}} {{- end -}} diff --git a/internal/codegen/golang/templater.go b/internal/codegen/golang/templater.go index 150035b..de73461 100644 --- a/internal/codegen/golang/templater.go +++ b/internal/codegen/golang/templater.go @@ -73,7 +73,7 @@ func (tm Templater) TemplateAll(files []codegen.QueryFile) ([]TemplatedFile, err pgconnIdx := -1 imports := file.Imports for i, pkg := range imports { - if pkg == "github.com/jackc/pgconn" { + if pkg == "github.com/jackc/pgx/v5/pgconn" { pgconnIdx = i break } @@ -113,10 +113,10 @@ func (tm Templater) templateFile(file codegen.QueryFile, isLeader bool) (Templat imports := NewImportSet() imports.AddPackage("context") imports.AddPackage("fmt") - imports.AddPackage("github.com/jackc/pgconn") + imports.AddPackage("github.com/jackc/pgx/v5/pgconn") if isLeader { - imports.AddPackage("github.com/jackc/pgtype") - imports.AddPackage("github.com/jackc/pgx/v4") + imports.AddPackage("github.com/jackc/pgx/v5/pgtype") + imports.AddPackage("github.com/jackc/pgx/v5") } pkgPath := "" diff --git a/internal/codegen/golang/type_resolver_test.go b/internal/codegen/golang/type_resolver_test.go index e31492e..bd37104 100644 --- a/internal/codegen/golang/type_resolver_test.go +++ b/internal/codegen/golang/type_resolver_test.go @@ -2,7 +2,7 @@ package golang import ( "github.com/google/go-cmp/cmp" - "github.com/jackc/pgtype" + "github.com/jackc/pgx/v5/pgtype" "github.com/jschaf/pggen/internal/casing" "github.com/jschaf/pggen/internal/codegen/golang/gotype" "github.com/jschaf/pggen/internal/difftest" @@ -88,7 +88,7 @@ func TestTypeResolver_Resolve(t *testing.T) { pgType: pg.BaseType{Name: "point", ID: pgtype.PointOID}, nullable: false, want: &gotype.ImportType{ - PkgPath: "github.com/jackc/pgtype", + PkgPath: "github.com/jackc/pgx/v5/pgtype", Type: &gotype.OpaqueType{ PgType: pg.BaseType{Name: "point", ID: pgtype.PointOID}, Name: "Point", @@ -100,7 +100,7 @@ func TestTypeResolver_Resolve(t *testing.T) { pgType: pg.BaseType{Name: "point", ID: pgtype.PointOID}, nullable: true, want: &gotype.ImportType{ - PkgPath: "github.com/jackc/pgtype", + PkgPath: "github.com/jackc/pgx/v5/pgtype", Type: &gotype.OpaqueType{ PgType: pg.BaseType{Name: "point", ID: pgtype.PointOID}, Name: "Point", diff --git a/internal/pg/column.go b/internal/pg/column.go index e381ec7..9215205 100644 --- a/internal/pg/column.go +++ b/internal/pg/column.go @@ -3,8 +3,7 @@ package pg import ( "context" "fmt" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "github.com/jschaf/pggen/internal/texts" "strconv" "strings" @@ -15,18 +14,18 @@ import ( // Column stores information about a column in a TableOID. // https://www.postgresql.org/docs/13/catalog-pg-attribute.html type Column struct { - Name string // pg_attribute.attname: column name - TableOID pgtype.OID // pg_attribute:attrelid: table the column belongs to - TableName string // pg_class.relname: name of table that owns the column - Number uint16 // pg_attribute.attnum: the number of column starting from 1 - Type Type // pg_attribute.atttypid: data type of the column - Null bool // pg_attribute.attnotnull: represents a not-null constraint + Name string // pg_attribute.attname: column name + TableOID uint32 // pg_attribute:attrelid: table the column belongs to + TableName string // pg_class.relname: name of table that owns the column + Number uint16 // pg_attribute.attnum: the number of column starting from 1 + Type Type // pg_attribute.atttypid: data type of the column + Null bool // pg_attribute.attnotnull: represents a not-null constraint } // ColumnKey is a composite key of a table OID and the number of the column // within the table. type ColumnKey struct { - TableOID pgtype.OID + TableOID uint32 Number uint16 // the number of column starting from 1 } diff --git a/internal/pg/column_test.go b/internal/pg/column_test.go index c437c89..5465b77 100644 --- a/internal/pg/column_test.go +++ b/internal/pg/column_test.go @@ -3,8 +3,7 @@ package pg import ( "context" "github.com/google/go-cmp/cmp" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "github.com/jschaf/pggen/internal/pgtest" "github.com/jschaf/pggen/internal/texts" "github.com/stretchr/testify/assert" @@ -74,7 +73,7 @@ func TestFetchColumns(t *testing.T) { } } -func findTableOID(t *testing.T, conn *pgx.Conn, table string) pgtype.OID { +func findTableOID(t *testing.T, conn *pgx.Conn, table string) uint32 { sql := texts.Dedent(` SELECT oid AS table_oid FROM pg_class @@ -85,7 +84,7 @@ func findTableOID(t *testing.T, conn *pgx.Conn, table string) pgtype.OID { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() row := conn.QueryRow(ctx, sql, table) - var oid pgtype.OID = 0 + var oid uint32 = 0 if err := row.Scan(&oid); err != nil && err != pgx.ErrNoRows { t.Fatal(err) } diff --git a/internal/pg/known_types.go b/internal/pg/known_types.go index 5540ad3..a78702c 100644 --- a/internal/pg/known_types.go +++ b/internal/pg/known_types.go @@ -1,7 +1,7 @@ package pg import ( - "github.com/jackc/pgtype" + "github.com/jackc/pgx/v5/pgtype" "github.com/jschaf/pggen/internal/pg/pgoid" ) @@ -81,7 +81,7 @@ var ( ) // All known Postgres types by OID. -var defaultKnownTypes = map[pgtype.OID]Type{ +var defaultKnownTypes = map[uint32]Type{ pgtype.BoolOID: Bool, pgtype.ByteaOID: Bytea, pgtype.QCharOID: QChar, diff --git a/internal/pg/query.sql.go b/internal/pg/query.sql.go index 61f49aa..af7b16a 100644 --- a/internal/pg/query.sql.go +++ b/internal/pg/query.sql.go @@ -5,9 +5,8 @@ package pg import ( "context" "fmt" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" ) // Querier is a typesafe Go interface backed by SQL queries. @@ -23,11 +22,11 @@ type Querier interface { // Recursively expands all given OIDs to all descendants through composite // types. - FindDescendantOIDs(ctx context.Context, oids []uint32) ([]pgtype.OID, error) + FindDescendantOIDs(ctx context.Context, oids []uint32) ([]uint32, error) - FindOIDByName(ctx context.Context, name string) (pgtype.OID, error) + FindOIDByName(ctx context.Context, name string) (uint32, error) - FindOIDName(ctx context.Context, oid pgtype.OID) (pgtype.Name, error) + FindOIDName(ctx context.Context, oid uint32) (string, error) FindOIDNames(ctx context.Context, oid []uint32) ([]FindOIDNamesRow, error) } @@ -35,8 +34,7 @@ type Querier interface { var _ Querier = &DBQuerier{} type DBQuerier struct { - conn genericConn // underlying Postgres transport to use - types *typeResolver // resolve types by name + conn genericConn // underlying Postgres transport to use } // genericConn is a connection like *pgx.Conn, pgx.Tx, or *pgxpool.Pool. @@ -48,36 +46,7 @@ type genericConn interface { // NewQuerier creates a DBQuerier that implements Querier. func NewQuerier(conn genericConn) *DBQuerier { - return &DBQuerier{conn: conn, types: newTypeResolver()} -} - -// typeResolver looks up the pgtype.ValueTranscoder by Postgres type name. -type typeResolver struct { - connInfo *pgtype.ConnInfo // types by Postgres type name -} - -func newTypeResolver() *typeResolver { - ci := pgtype.NewConnInfo() - return &typeResolver{connInfo: ci} -} - -// findValue find the OID, and pgtype.ValueTranscoder for a Postgres type name. -func (tr *typeResolver) findValue(name string) (uint32, pgtype.ValueTranscoder, bool) { - typ, ok := tr.connInfo.DataTypeForName(name) - if !ok { - return 0, nil, false - } - v := pgtype.NewValue(typ.Value) - return typ.OID, v.(pgtype.ValueTranscoder), true -} - -// setValue sets the value of a ValueTranscoder to a value that should always -// work and panics if it fails. -func (tr *typeResolver) setValue(vt pgtype.ValueTranscoder, val interface{}) pgtype.ValueTranscoder { - if err := vt.Set(val); err != nil { - panic(fmt.Sprintf("set ValueTranscoder %T to %+v: %s", vt, val, err)) - } - return vt + return &DBQuerier{conn: conn} } const findEnumTypesSQL = `WITH enums AS ( @@ -125,13 +94,13 @@ WHERE typ.typisdefined AND typ.oid = ANY ($1::oid[]);` type FindEnumTypesRow struct { - OID pgtype.OID `json:"oid"` - TypeName string `json:"type_name"` - ChildOIDs []int `json:"child_oids"` - Orders []float32 `json:"orders"` - Labels []string `json:"labels"` - TypeKind pgtype.QChar `json:"type_kind"` - DefaultExpr string `json:"default_expr"` + OID uint32 `json:"oid"` + TypeName string `json:"type_name"` + ChildOIDs []int `json:"child_oids"` + Orders []float32 `json:"orders"` + Labels []string `json:"labels"` + TypeKind byte `json:"type_kind"` + DefaultExpr string `json:"default_expr"` } // FindEnumTypes implements Querier.FindEnumTypes. @@ -186,10 +155,10 @@ WHERE arr_typ.typisdefined AND arr_typ.oid = ANY ($1::oid[]);` type FindArrayTypesRow struct { - OID pgtype.OID `json:"oid"` - TypeName string `json:"type_name"` - ElemOID pgtype.OID `json:"elem_oid"` - TypeKind pgtype.QChar `json:"type_kind"` + OID uint32 `json:"oid"` + TypeName string `json:"type_name"` + ElemOID uint32 `json:"elem_oid"` + TypeKind byte `json:"type_kind"` } // FindArrayTypes implements Querier.FindArrayTypes. @@ -245,14 +214,14 @@ WHERE typ.oid = ANY ($1::oid[]) AND typ.typtype = 'c';` type FindCompositeTypesRow struct { - TableTypeName string `json:"table_type_name"` - TableTypeOID pgtype.OID `json:"table_type_oid"` - TableName pgtype.Name `json:"table_name"` - ColNames []string `json:"col_names"` - ColOIDs []int `json:"col_oids"` - ColOrders []int `json:"col_orders"` - ColNotNulls pgtype.BoolArray `json:"col_not_nulls"` - ColTypeNames []string `json:"col_type_names"` + TableTypeName string `json:"table_type_name"` + TableTypeOID uint32 `json:"table_type_oid"` + TableName string `json:"table_name"` + ColNames []string `json:"col_names"` + ColOIDs []int `json:"col_oids"` + ColOrders []int `json:"col_orders"` + ColNotNulls []bool `json:"col_not_nulls"` + ColTypeNames []string `json:"col_type_names"` } // FindCompositeTypes implements Querier.FindCompositeTypes. @@ -306,16 +275,16 @@ SELECT oid FROM oid_descs;` // FindDescendantOIDs implements Querier.FindDescendantOIDs. -func (q *DBQuerier) FindDescendantOIDs(ctx context.Context, oids []uint32) ([]pgtype.OID, error) { +func (q *DBQuerier) FindDescendantOIDs(ctx context.Context, oids []uint32) ([]uint32, error) { ctx = context.WithValue(ctx, "pggen_query_name", "FindDescendantOIDs") rows, err := q.conn.Query(ctx, findDescendantOIDsSQL, oids) if err != nil { return nil, fmt.Errorf("query FindDescendantOIDs: %w", err) } defer rows.Close() - items := []pgtype.OID{} + items := []uint32{} for rows.Next() { - var item pgtype.OID + var item uint32 if err := rows.Scan(&item); err != nil { return nil, fmt.Errorf("scan FindDescendantOIDs row: %w", err) } @@ -334,10 +303,10 @@ ORDER BY oid DESC LIMIT 1;` // FindOIDByName implements Querier.FindOIDByName. -func (q *DBQuerier) FindOIDByName(ctx context.Context, name string) (pgtype.OID, error) { +func (q *DBQuerier) FindOIDByName(ctx context.Context, name string) (uint32, error) { ctx = context.WithValue(ctx, "pggen_query_name", "FindOIDByName") row := q.conn.QueryRow(ctx, findOIDByNameSQL, name) - var item pgtype.OID + var item uint32 if err := row.Scan(&item); err != nil { return item, fmt.Errorf("query FindOIDByName: %w", err) } @@ -349,10 +318,10 @@ FROM pg_type WHERE oid = $1;` // FindOIDName implements Querier.FindOIDName. -func (q *DBQuerier) FindOIDName(ctx context.Context, oid pgtype.OID) (pgtype.Name, error) { +func (q *DBQuerier) FindOIDName(ctx context.Context, oid uint32) (string, error) { ctx = context.WithValue(ctx, "pggen_query_name", "FindOIDName") row := q.conn.QueryRow(ctx, findOIDNameSQL, oid) - var item pgtype.Name + var item string if err := row.Scan(&item); err != nil { return item, fmt.Errorf("query FindOIDName: %w", err) } @@ -364,9 +333,9 @@ FROM pg_type WHERE oid = ANY ($1::oid[]);` type FindOIDNamesRow struct { - OID pgtype.OID `json:"oid"` - Name pgtype.Name `json:"name"` - Kind pgtype.QChar `json:"kind"` + OID uint32 `json:"oid"` + Name string `json:"name"` + Kind byte `json:"kind"` } // FindOIDNames implements Querier.FindOIDNames. @@ -390,28 +359,3 @@ func (q *DBQuerier) FindOIDNames(ctx context.Context, oid []uint32) ([]FindOIDNa } return items, err } - -// textPreferrer wraps a pgtype.ValueTranscoder and sets the preferred encoding -// format to text instead binary (the default). pggen uses the text format -// when the OID is unknownOID because the binary format requires the OID. -// Typically occurs for unregistered types. -type textPreferrer struct { - pgtype.ValueTranscoder - typeName string -} - -// PreferredParamFormat implements pgtype.ParamFormatPreferrer. -func (t textPreferrer) PreferredParamFormat() int16 { return pgtype.TextFormatCode } - -func (t textPreferrer) NewTypeValue() pgtype.Value { - return textPreferrer{ValueTranscoder: pgtype.NewValue(t.ValueTranscoder).(pgtype.ValueTranscoder), typeName: t.typeName} -} - -func (t textPreferrer) TypeName() string { - return t.typeName -} - -// unknownOID means we don't know the OID for a type. This is okay for decoding -// because pgx call DecodeText or DecodeBinary without requiring the OID. For -// encoding parameters, pggen uses textPreferrer if the OID is unknown. -const unknownOID = 0 diff --git a/internal/pg/type_cache.go b/internal/pg/type_cache.go index d21a060..53101a3 100644 --- a/internal/pg/type_cache.go +++ b/internal/pg/type_cache.go @@ -1,18 +1,17 @@ package pg import ( - "github.com/jackc/pgtype" "sync" ) // typeCache caches a map from a Postgres pg_type.oid to a Type. type typeCache struct { - types map[pgtype.OID]Type + types map[uint32]Type mu *sync.Mutex } func newTypeCache() *typeCache { - m := make(map[pgtype.OID]Type, len(defaultKnownTypes)) + m := make(map[uint32]Type, len(defaultKnownTypes)) for oid, typ := range defaultKnownTypes { m[oid] = typ } @@ -23,16 +22,16 @@ func newTypeCache() *typeCache { } // getOIDs returns the cached OIDS (with the type) and uncached OIDs. -func (tc *typeCache) getOIDs(oids ...uint32) (map[pgtype.OID]Type, map[pgtype.OID]struct{}) { - cachedTypes := make(map[pgtype.OID]Type, len(oids)) - uncachedTypes := make(map[pgtype.OID]struct{}, len(oids)) +func (tc *typeCache) getOIDs(oids ...uint32) (map[uint32]Type, map[uint32]struct{}) { + cachedTypes := make(map[uint32]Type, len(oids)) + uncachedTypes := make(map[uint32]struct{}, len(oids)) tc.mu.Lock() defer tc.mu.Unlock() for _, oid := range oids { - if t, ok := tc.types[pgtype.OID(oid)]; ok { - cachedTypes[pgtype.OID(oid)] = t + if t, ok := tc.types[uint32(oid)]; ok { + cachedTypes[uint32(oid)] = t } else { - uncachedTypes[pgtype.OID(oid)] = struct{}{} + uncachedTypes[uint32(oid)] = struct{}{} } } return cachedTypes, uncachedTypes @@ -40,7 +39,7 @@ func (tc *typeCache) getOIDs(oids ...uint32) (map[pgtype.OID]Type, map[pgtype.OI func (tc *typeCache) getOID(oid uint32) (Type, bool) { tc.mu.Lock() - typ, ok := tc.types[pgtype.OID(oid)] + typ, ok := tc.types[uint32(oid)] tc.mu.Unlock() return typ, ok } diff --git a/internal/pg/type_fetcher.go b/internal/pg/type_fetcher.go index c0ad0d6..8eae703 100644 --- a/internal/pg/type_fetcher.go +++ b/internal/pg/type_fetcher.go @@ -3,8 +3,7 @@ package pg import ( "context" "fmt" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "time" ) @@ -24,7 +23,7 @@ func NewTypeFetcher(conn *pgx.Conn) *TypeFetcher { // FindTypesByOIDs returns a map of a type OID to the Type description. The // returned map contains every unique OID in oids (oids may contain duplicates) // unless there's an error. -func (tf *TypeFetcher) FindTypesByOIDs(oids ...uint32) (map[pgtype.OID]Type, error) { +func (tf *TypeFetcher) FindTypesByOIDs(oids ...uint32) (map[uint32]Type, error) { if types, uncached := tf.cache.getOIDs(oids...); len(uncached) == 0 { return types, nil } @@ -96,7 +95,7 @@ func (tf *TypeFetcher) FindTypesByOIDs(oids ...uint32) (map[pgtype.OID]Type, err return types, nil } -func (tf *TypeFetcher) findEnumTypes(ctx context.Context, uncached map[pgtype.OID]struct{}) ([]EnumType, error) { +func (tf *TypeFetcher) findEnumTypes(ctx context.Context, uncached map[uint32]struct{}) ([]EnumType, error) { oids := oidKeys(uncached) rows, err := tf.querier.FindEnumTypes(ctx, oids) if err != nil { @@ -104,9 +103,9 @@ func (tf *TypeFetcher) findEnumTypes(ctx context.Context, uncached map[pgtype.OI } types := make([]EnumType, len(rows)) for i, enum := range rows { - childOIDs := make([]pgtype.OID, len(enum.ChildOIDs)) + childOIDs := make([]uint32, len(enum.ChildOIDs)) for i, oidUint32 := range enum.ChildOIDs { - childOIDs[i] = pgtype.OID(oidUint32) + childOIDs[i] = uint32(oidUint32) } types[i] = EnumType{ ID: enum.OID, @@ -119,14 +118,14 @@ func (tf *TypeFetcher) findEnumTypes(ctx context.Context, uncached map[pgtype.OI return types, nil } -func (tf *TypeFetcher) findCompositeTypes(ctx context.Context, uncached map[pgtype.OID]struct{}) ([]CompositeType, error) { +func (tf *TypeFetcher) findCompositeTypes(ctx context.Context, uncached map[uint32]struct{}) ([]CompositeType, error) { oids := oidKeys(uncached) rows, err := tf.querier.FindCompositeTypes(ctx, oids) if err != nil { return nil, fmt.Errorf("find composite types: %w", err) } // Record all composite types to fake a topological sort by repeated iteration. - allComposites := make(map[pgtype.OID]struct{}, len(rows)) + allComposites := make(map[uint32]struct{}, len(rows)) for _, row := range rows { allComposites[row.TableTypeOID] = struct{}{} } @@ -148,13 +147,13 @@ func (tf *TypeFetcher) findCompositeTypes(ctx context.Context, uncached map[pgty // We might resolve this type in a future pass like findArrayTypes. At // the end, we'll attempt to to replace the placeholder with the // resolved type. - colTypes[i] = placeholderType{ID: pgtype.OID(colOID)} + colTypes[i] = placeholderType{ID: uint32(colOID)} colNames[i] = row.ColNames[i] } } typ := CompositeType{ ID: row.TableTypeOID, - Name: row.TableName.String, + Name: row.TableName, ColumnNames: colNames, ColumnTypes: colTypes, } @@ -164,7 +163,7 @@ func (tf *TypeFetcher) findCompositeTypes(ctx context.Context, uncached map[pgty return types, nil } -func (tf *TypeFetcher) findUnknownTypes(ctx context.Context, uncached map[pgtype.OID]struct{}) ([]UnknownType, error) { +func (tf *TypeFetcher) findUnknownTypes(ctx context.Context, uncached map[uint32]struct{}) ([]UnknownType, error) { oids := oidKeys(uncached) rows, err := tf.querier.FindOIDNames(ctx, oids) if err != nil { @@ -174,14 +173,14 @@ func (tf *TypeFetcher) findUnknownTypes(ctx context.Context, uncached map[pgtype for i, row := range rows { types[i] = UnknownType{ ID: row.OID, - Name: row.Name.String, - PgKind: TypeKind(row.Kind.Int), + Name: row.Name, + PgKind: TypeKind(row.Kind), } } return types, nil } -func (tf *TypeFetcher) findArrayTypes(ctx context.Context, uncached map[pgtype.OID]struct{}) ([]ArrayType, error) { +func (tf *TypeFetcher) findArrayTypes(ctx context.Context, uncached map[uint32]struct{}) ([]ArrayType, error) { oids := oidKeys(uncached) rows, err := tf.querier.FindArrayTypes(ctx, oids) if err != nil { @@ -204,7 +203,7 @@ func (tf *TypeFetcher) findArrayTypes(ctx context.Context, uncached map[pgtype.O // resolvePlaceholderTypes resolves all placeholder types or errors if we can't // resolve a placeholderType using all known types. -func (tf *TypeFetcher) resolvePlaceholderTypes(knownTypes map[pgtype.OID]Type) error { +func (tf *TypeFetcher) resolvePlaceholderTypes(knownTypes map[uint32]Type) error { // resolveType walks down type, replacing placeholderType with a known type. var resolveType func(typ Type) (Type, error) resolveType = func(typ Type) (Type, error) { @@ -246,7 +245,7 @@ func (tf *TypeFetcher) resolvePlaceholderTypes(knownTypes map[pgtype.OID]Type) e return nil } -func oidKeys(os map[pgtype.OID]struct{}) []uint32 { +func oidKeys(os map[uint32]struct{}) []uint32 { oids := make([]uint32, 0, len(os)) for oid := range os { oids = append(oids, uint32(oid)) diff --git a/internal/pg/type_fetcher_test.go b/internal/pg/type_fetcher_test.go index 08e7456..43fc007 100644 --- a/internal/pg/type_fetcher_test.go +++ b/internal/pg/type_fetcher_test.go @@ -4,7 +4,6 @@ import ( "context" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/jackc/pgtype" "github.com/jschaf/pggen/internal/pg/pgoid" "github.com/jschaf/pggen/internal/pgtest" "github.com/jschaf/pggen/internal/texts" @@ -248,7 +247,7 @@ func TestNewTypeFetcher(t *testing.T) { } // Get the OID by name if fetchOID was a string, or just return the OID. -func findOIDVal(t *testing.T, fetchOID interface{}, querier *DBQuerier) pgtype.OID { +func findOIDVal(t *testing.T, fetchOID interface{}, querier *DBQuerier) uint32 { switch rawOID := fetchOID.(type) { case string: oid, err := querier.FindOIDByName(context.Background(), rawOID) @@ -256,10 +255,10 @@ func findOIDVal(t *testing.T, fetchOID interface{}, querier *DBQuerier) pgtype.O t.Fatalf("find oid by name %s: %s", rawOID, err) } return oid - case pgtype.OID: + case uint32: return rawOID case int: - return pgtype.OID(rawOID) + return uint32(rawOID) default: t.Fatalf("unhandled oid test value type %T: %v", rawOID, rawOID) return 0 diff --git a/internal/pg/types.go b/internal/pg/types.go index b5b07af..b662df8 100644 --- a/internal/pg/types.go +++ b/internal/pg/types.go @@ -1,15 +1,14 @@ package pg import ( - "github.com/jackc/pgtype" "github.com/jschaf/pggen/internal/pg/pgoid" "strconv" ) // Type is a Postgres type. type Type interface { - OID() pgtype.OID // pg_type.oid: row identifier - String() string // pg_type.typname: data type name + OID() uint32 // pg_type.oid: row identifier + String() string // pg_type.typname: data type name Kind() TypeKind } @@ -49,8 +48,8 @@ type ( // BaseType is a fundamental Postgres type like text and bool. // https://www.postgresql.org/docs/13/catalog-pg-type.html BaseType struct { - ID pgtype.OID // pg_type.oid: row identifier - Name string // pg_type.typname: data type name + ID uint32 // pg_type.oid: row identifier + Name string // pg_type.typname: data type name } // VoidType is an empty type. A void type doesn't appear in output, but it's @@ -60,7 +59,7 @@ type ( // ArrayType is an array type where pg_type.typelem != 0 and the name begins // with an underscore. ArrayType struct { - ID pgtype.OID // pg_type.oid: row identifier + ID uint32 // pg_type.oid: row identifier // The name of the type, like _int4. Array types in Postgres typically // begin with an underscore. From pg_type.typname. Name string @@ -69,7 +68,7 @@ type ( } EnumType struct { - ID pgtype.OID // pg_type.oid: row identifier + ID uint32 // pg_type.oid: row identifier // The name of the enum, like 'device_type' in: // CREATE TYPE device_type AS ENUM ('foo'); // From pg_type.typname. @@ -82,26 +81,26 @@ type ( // values is that they be correctly ordered and unique within each enum // type. Orders []float32 - ChildOIDs []pgtype.OID + ChildOIDs []uint32 } // DomainType is a user-create domain type. DomainType struct { - ID pgtype.OID // pg_type.oid: row identifier - Name string // pg_type.typname: data type name - IsNotNull bool // pg_type.typnotnull: domains only, not null constraint for domains - HasDefault bool // pg_type.typdefault: domains only, if there's a default value - BaseType BaseType // pg_type.typbasetype: domains only, the base type - Dimensions int // pg_type.typndims: domains on array type only, 0 otherwise, number of array dimensions + ID uint32 // pg_type.oid: row identifier + Name string // pg_type.typname: data type name + IsNotNull bool // pg_type.typnotnull: domains only, not null constraint for domains + HasDefault bool // pg_type.typdefault: domains only, if there's a default value + BaseType BaseType // pg_type.typbasetype: domains only, the base type + Dimensions int // pg_type.typndims: domains on array type only, 0 otherwise, number of array dimensions } // CompositeType is a type containing multiple columns and is represented as // a class. https://www.postgresql.org/docs/13/catalog-pg-class.html CompositeType struct { - ID pgtype.OID // pg_class.oid: row identifier - Name string // pg_class.relname: name of the composite type - ColumnNames []string // pg_attribute.attname: names of the column, in order - ColumnTypes []Type // pg_attribute JOIN pg_type: information about columns of the composite type + ID uint32 // pg_class.oid: row identifier + Name string // pg_class.relname: name of the composite type + ColumnNames []string // pg_attribute.attname: names of the column, in order + ColumnTypes []Type // pg_attribute JOIN pg_type: information about columns of the composite type } // UnknownType is a Postgres type that's not a well-known type in @@ -109,8 +108,8 @@ type ( // generator might be able to resolve this type from a user-provided mapping // like --go-type my_int=int. UnknownType struct { - ID pgtype.OID // pg_type.oid: row identifier - Name string // pg_type.typname: data type name + ID uint32 // pg_type.oid: row identifier + Name string // pg_type.typname: data type name PgKind TypeKind } @@ -120,38 +119,38 @@ type ( // requires two passes for cases like when a composite type has a child type // that's an array. placeholderType struct { - ID pgtype.OID // pg_type.oid: row identifier + ID uint32 // pg_type.oid: row identifier } ) -func (b BaseType) OID() pgtype.OID { return b.ID } -func (b BaseType) String() string { return b.Name } -func (b BaseType) Kind() TypeKind { return KindBaseType } +func (b BaseType) OID() uint32 { return b.ID } +func (b BaseType) String() string { return b.Name } +func (b BaseType) Kind() TypeKind { return KindBaseType } -func (b VoidType) OID() pgtype.OID { return pgoid.Void } -func (b VoidType) String() string { return "void" } -func (b VoidType) Kind() TypeKind { return KindPseudoType } +func (b VoidType) OID() uint32 { return pgoid.Void } +func (b VoidType) String() string { return "void" } +func (b VoidType) Kind() TypeKind { return KindPseudoType } -func (b ArrayType) OID() pgtype.OID { return b.ID } -func (b ArrayType) String() string { return b.Name } -func (b ArrayType) Kind() TypeKind { return KindBaseType } +func (b ArrayType) OID() uint32 { return b.ID } +func (b ArrayType) String() string { return b.Name } +func (b ArrayType) Kind() TypeKind { return KindBaseType } -func (e EnumType) OID() pgtype.OID { return e.ID } -func (e EnumType) String() string { return e.Name } -func (e EnumType) Kind() TypeKind { return KindEnumType } +func (e EnumType) OID() uint32 { return e.ID } +func (e EnumType) String() string { return e.Name } +func (e EnumType) Kind() TypeKind { return KindEnumType } -func (e DomainType) OID() pgtype.OID { return e.ID } -func (e DomainType) String() string { return e.Name } -func (e DomainType) Kind() TypeKind { return KindDomainType } +func (e DomainType) OID() uint32 { return e.ID } +func (e DomainType) String() string { return e.Name } +func (e DomainType) Kind() TypeKind { return KindDomainType } -func (e CompositeType) OID() pgtype.OID { return e.ID } -func (e CompositeType) String() string { return e.Name } -func (e CompositeType) Kind() TypeKind { return KindCompositeType } +func (e CompositeType) OID() uint32 { return e.ID } +func (e CompositeType) String() string { return e.Name } +func (e CompositeType) Kind() TypeKind { return KindCompositeType } -func (e UnknownType) OID() pgtype.OID { return e.ID } -func (e UnknownType) String() string { return e.Name } -func (e UnknownType) Kind() TypeKind { return e.PgKind } +func (e UnknownType) OID() uint32 { return e.ID } +func (e UnknownType) String() string { return e.Name } +func (e UnknownType) Kind() TypeKind { return e.PgKind } -func (p placeholderType) OID() pgtype.OID { return p.ID } -func (p placeholderType) String() string { return "placeholder-" + strconv.Itoa(int(p.ID)) } -func (p placeholderType) Kind() TypeKind { return kindPlaceholderType } +func (p placeholderType) OID() uint32 { return p.ID } +func (p placeholderType) String() string { return "placeholder-" + strconv.Itoa(int(p.ID)) } +func (p placeholderType) Kind() TypeKind { return kindPlaceholderType } diff --git a/internal/pgdocker/pgdocker.go b/internal/pgdocker/pgdocker.go index 1f4a1ce..0d62993 100644 --- a/internal/pgdocker/pgdocker.go +++ b/internal/pgdocker/pgdocker.go @@ -12,7 +12,7 @@ import ( "github.com/docker/docker/api/types/container" dockerClient "github.com/docker/docker/client" "github.com/docker/go-connections/nat" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "github.com/jschaf/pggen/internal/errs" "github.com/jschaf/pggen/internal/ports" "io" diff --git a/internal/pginfer/pginfer.go b/internal/pginfer/pginfer.go index 82de7f2..bcb46d7 100644 --- a/internal/pginfer/pginfer.go +++ b/internal/pginfer/pginfer.go @@ -3,13 +3,11 @@ package pginfer import ( "context" "fmt" - "github.com/jackc/pgconn" + "github.com/jackc/pgx/v5/pgconn" "strings" "time" - "github.com/jackc/pgproto3/v2" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "github.com/jschaf/pggen/internal/ast" "github.com/jschaf/pggen/internal/pg" ) @@ -157,7 +155,7 @@ func (inf *Inferrer) prepareTypes(query *ast.SourceQuery) (_a []InputParam, _ [] return nil, nil, fmt.Errorf("fetch oid types: %w", err) } for i, oid := range stmtDesc.ParamOIDs { - inputType, ok := types[pgtype.OID(oid)] + inputType, ok := types[uint32(oid)] if !ok { return nil, nil, fmt.Errorf("no postgres type name found for parameter %s with oid %d", query.ParamNames[i], oid) } @@ -187,7 +185,7 @@ func (inf *Inferrer) prepareTypes(query *ast.SourceQuery) (_a []InputParam, _ [] // Create output columns var outputColumns []OutputColumn for i, desc := range stmtDesc.Fields { - pgType, ok := outputTypes[pgtype.OID(desc.DataTypeOID)] + pgType, ok := outputTypes[uint32(desc.DataTypeOID)] if !ok { return nil, nil, fmt.Errorf("no postgrestype name found for column %s with oid %d", string(desc.Name), desc.DataTypeOID) } @@ -202,7 +200,7 @@ func (inf *Inferrer) prepareTypes(query *ast.SourceQuery) (_a []InputParam, _ [] // inferOutputNullability infers which of the output columns produced by the // query and described by descs can be null. -func (inf *Inferrer) inferOutputNullability(query *ast.SourceQuery, descs []pgproto3.FieldDescription) ([]bool, error) { +func (inf *Inferrer) inferOutputNullability(query *ast.SourceQuery, descs []pgconn.FieldDescription) ([]bool, error) { if len(descs) == 0 { return nil, nil } @@ -215,7 +213,7 @@ func (inf *Inferrer) inferOutputNullability(query *ast.SourceQuery, descs []pgpr for i, desc := range descs { if desc.TableOID > 0 { columnKeys[i] = pg.ColumnKey{ - TableOID: pgtype.OID(desc.TableOID), + TableOID: uint32(desc.TableOID), Number: desc.TableAttributeNumber, } } diff --git a/internal/pgplan/pgplan.go b/internal/pgplan/pgplan.go index 1829450..d7e69db 100644 --- a/internal/pgplan/pgplan.go +++ b/internal/pgplan/pgplan.go @@ -3,7 +3,7 @@ package pgplan import ( "context" "fmt" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "time" ) diff --git a/internal/pgtest/pg_test_db.go b/internal/pgtest/pg_test_db.go index e0905fd..62c1a98 100644 --- a/internal/pgtest/pg_test_db.go +++ b/internal/pgtest/pg_test_db.go @@ -2,7 +2,7 @@ package pgtest import ( "context" - "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v5" "math/rand" "os" "strconv" From 4bb40e2b95c380ce9d386688af28a408521121d8 Mon Sep 17 00:00:00 2001 From: Joe Schafer Date: Fri, 5 Jan 2024 15:12:55 -0800 Subject: [PATCH 2/7] experiment with codecs --- example/author/query.sql.go | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/example/author/query.sql.go b/example/author/query.sql.go index 2b892be..4622482 100644 --- a/example/author/query.sql.go +++ b/example/author/query.sql.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgtype" ) // Querier is a typesafe Go interface backed by SQL queries. @@ -80,6 +81,7 @@ func (q *DBQuerier) FindAuthorByID(ctx context.Context, authorID int32) (FindAut ctx = context.WithValue(ctx, "pggen_query_name", "FindAuthorByID") row := q.conn.QueryRow(ctx, findAuthorByIDSQL, authorID) var item FindAuthorByIDRow + s := scanner[*FindAuthorByIDRow]{&item} if err := row.Scan(s); err != nil { return item, fmt.Errorf("query FindAuthorByID: %w", err) @@ -108,13 +110,32 @@ func (q *DBQuerier) FindAuthors(ctx context.Context, firstName string) ([]FindAu return nil, fmt.Errorf("query FindAuthors: %w", err) } defer rows.Close() + + fds := rows.FieldDescriptions() + + int4Codec := pgtype.Int4Codec{} + textCodec := &pgtype.TextCodec{} + + scan0 := int4Codec.PlanScan(nil, fds[0].DataTypeOID, fds[0].Format, (*int32)(nil)) + scan1 := textCodec.PlanScan(nil, fds[1].DataTypeOID, fds[1].Format, (*string)(nil)) + scan2 := textCodec.PlanScan(nil, fds[2].DataTypeOID, fds[2].Format, (*string)(nil)) + scan3 := textCodec.PlanScan(nil, fds[2].DataTypeOID, fds[3].Format, (*string)(nil)) + items := []FindAuthorsRow{} - s := scanner[*FindAuthorsRow]{} for rows.Next() { + vals := rows.RawValues() var item FindAuthorsRow - s.item = &item - if err := rows.Scan(s); err != nil { - return nil, fmt.Errorf("scan FindAuthors row: %w", err) + if err := scan0.Scan(vals[0], &item.AuthorID); err != nil { + return nil, fmt.Errorf("scan FindAuthors row author_id column: %w", err) + } + if err := scan1.Scan(vals[1], &item.FirstName); err != nil { + return nil, fmt.Errorf("scan FindAuthors row first_name column: %w", err) + } + if err := scan2.Scan(vals[2], &item.LastName); err != nil { + return nil, fmt.Errorf("scan FindAuthors row last_name column: %w", err) + } + if err := scan3.Scan(vals[3], item.Suffix); err != nil { + return nil, fmt.Errorf("scan FindAuthors row suffix column: %w", err) } items = append(items, item) } From 500b5c6be650536cc5b4cb45a13c45002d6d04ca Mon Sep 17 00:00:00 2001 From: Joe Schafer Date: Fri, 5 Jan 2024 17:45:35 -0800 Subject: [PATCH 3/7] More experiments with codecs --- example/author/query.sql.go | 86 ++++++++++++----- example/author/query.sql_test.go | 19 ++++ go.mod | 7 -- go.sum | 159 ------------------------------- 4 files changed, 81 insertions(+), 190 deletions(-) diff --git a/example/author/query.sql.go b/example/author/query.sql.go index 4622482..283cfa9 100644 --- a/example/author/query.sql.go +++ b/example/author/query.sql.go @@ -102,6 +102,54 @@ func (r *FindAuthorsRow) scanRow(rows pgx.Rows) error { return rows.Scan(&r.AuthorID, &r.FirstName, &r.LastName, &r.Suffix) } +type scanCacheKey struct { + oid uint32 + format int16 + typeName string +} + +type scanPlan[T any] interface { + Scan([]byte, T) error +} + +var scanCache = map[string]scanPlan[any]{} + +func planScan[T any](codec pgtype.Codec, fd pgconn.FieldDescription) scanPlan[*T] { + var target *T + key := scanCacheKey{fd.DataTypeOID, fd.Format, fmt.Sprintf("%T", target)} + // TODO: synchronize + if plan, ok := scanCache[key.typeName]; ok { + return plan.(scanPlan[*T]) + } + plan := codec.PlanScan(nil, fd.DataTypeOID, fd.Format, target) + scanCache[key.typeName] = plan + return plan.(scanPlan[*T]) +} + +type ptrScanner[T any] struct { + basePlan scanPlan[T] +} + +func (s ptrScanner[T]) Scan(src []byte, dst **T) error { + if src == nil { + return nil + } + *dst = new(T) + return s.basePlan.Scan(src, *dst) +} + +func planPtrScan[T any](codec pgtype.Codec, fd pgconn.FieldDescription) scanPlan[**T] { + var target *T + key := scanCacheKey{fd.DataTypeOID, fd.Format, fmt.Sprintf("*%T", target)} + if scan, ok := scanCache[key.typeName]; ok { + return scan.(scanPlan[**T]) + } + basePlan := planScan[T](codec, fd) + var ptrPlan scanPlan[**T] = ptrScanner[T]{basePlan: basePlan.(scanPlan[T])} + scanCache[key.typeName] = ptrPlan.(scanPlan[any]) + return ptrPlan +} + // FindAuthors implements Querier.FindAuthors. func (q *DBQuerier) FindAuthors(ctx context.Context, firstName string) ([]FindAuthorsRow, error) { ctx = context.WithValue(ctx, "pggen_query_name", "FindAuthors") @@ -112,30 +160,26 @@ func (q *DBQuerier) FindAuthors(ctx context.Context, firstName string) ([]FindAu defer rows.Close() fds := rows.FieldDescriptions() - - int4Codec := pgtype.Int4Codec{} - textCodec := &pgtype.TextCodec{} - - scan0 := int4Codec.PlanScan(nil, fds[0].DataTypeOID, fds[0].Format, (*int32)(nil)) - scan1 := textCodec.PlanScan(nil, fds[1].DataTypeOID, fds[1].Format, (*string)(nil)) - scan2 := textCodec.PlanScan(nil, fds[2].DataTypeOID, fds[2].Format, (*string)(nil)) - scan3 := textCodec.PlanScan(nil, fds[2].DataTypeOID, fds[3].Format, (*string)(nil)) + var plan0 = planScan[int32](pgtype.Int4Codec{}, fds[0]) + var plan1 = planScan[string](pgtype.TextCodec{}, fds[1]) + var plan2 = plan1 + var plan3 = planPtrScan[string](pgtype.TextCodec{}, fds[3]) items := []FindAuthorsRow{} for rows.Next() { vals := rows.RawValues() var item FindAuthorsRow - if err := scan0.Scan(vals[0], &item.AuthorID); err != nil { - return nil, fmt.Errorf("scan FindAuthors row author_id column: %w", err) + if err := plan0.Scan(vals[0], &item.AuthorID); err != nil { + return nil, fmt.Errorf("scan FindAuthors.author_id column: %w", err) } - if err := scan1.Scan(vals[1], &item.FirstName); err != nil { - return nil, fmt.Errorf("scan FindAuthors row first_name column: %w", err) + if err := plan1.Scan(vals[1], &item.FirstName); err != nil { + return nil, fmt.Errorf("scan FindAuthors.first_name column: %w", err) } - if err := scan2.Scan(vals[2], &item.LastName); err != nil { - return nil, fmt.Errorf("scan FindAuthors row last_name column: %w", err) + if err := plan2.Scan(vals[2], &item.LastName); err != nil { + return nil, fmt.Errorf("scan FindAuthors.last_name column: %w", err) } - if err := scan3.Scan(vals[3], item.Suffix); err != nil { - return nil, fmt.Errorf("scan FindAuthors row suffix column: %w", err) + if err := plan3.Scan(vals[3], &item.Suffix); err != nil { + return nil, fmt.Errorf("scan FindAuthors.suffix column: %w", err) } items = append(items, item) } @@ -298,7 +342,7 @@ func (q *DBQuerier) InsertAuthorSuffix(ctx context.Context, params InsertAuthorS return item, nil } -const stringAggFirstNameSQL = `SELECT string_agg(first_name, ',') AS names FROM author WHERE author_id = $1;` +const stringAggFirstNameSQL = `SELECT string_agg(first_name, ',') AS NAMES FROM author WHERE author_id = $1;` // StringAggFirstName implements Querier.StringAggFirstName. func (q *DBQuerier) StringAggFirstName(ctx context.Context, authorID int32) (*string, error) { @@ -311,7 +355,7 @@ func (q *DBQuerier) StringAggFirstName(ctx context.Context, authorID int32) (*st return item, nil } -const arrayAggFirstNameSQL = `SELECT array_agg(first_name) AS names FROM author WHERE author_id = $1;` +const arrayAggFirstNameSQL = `SELECT array_agg(first_name) AS NAMES FROM author WHERE author_id = $1;` // ArrayAggFirstName implements Querier.ArrayAggFirstName. func (q *DBQuerier) ArrayAggFirstName(ctx context.Context, authorID int32) ([]string, error) { @@ -323,9 +367,3 @@ func (q *DBQuerier) ArrayAggFirstName(ctx context.Context, authorID int32) ([]st } return item, nil } - -type rowScanner interface{ scanRow(rows pgx.Rows) error } - -type scanner[T rowScanner] struct{ item T } - -func (s scanner[T]) ScanRow(rows pgx.Rows) error { return s.item.scanRow(rows) } diff --git a/example/author/query.sql_test.go b/example/author/query.sql_test.go index 55521e4..fc1199e 100644 --- a/example/author/query.sql_test.go +++ b/example/author/query.sql_test.go @@ -63,6 +63,25 @@ func TestNewQuerier_FindAuthors(t *testing.T) { assert.Equal(t, want, authors) }) + t.Run("FindAuthors - 1 row suffix - bill", func(t *testing.T) { + insRow, err := q.InsertAuthorSuffix(context.Background(), InsertAuthorSuffixParams{ + FirstName: "bill", + LastName: "clinton", + Suffix: "jr", + }) + authors, err := q.FindAuthors(context.Background(), "bill") + require.NoError(t, err) + want := []FindAuthorsRow{ + { + AuthorID: insRow.AuthorID, + FirstName: "bill", + LastName: "clinton", + Suffix: ptrs.String("jr"), + }, + } + assert.Equal(t, want, authors) + }) + t.Run("FindAuthors - 2 rows - george", func(t *testing.T) { authors, err := q.FindAuthors(context.Background(), "george") require.NoError(t, err) diff --git a/go.mod b/go.mod index db08d69..af5672d 100644 --- a/go.mod +++ b/go.mod @@ -7,13 +7,8 @@ require ( github.com/docker/docker v24.0.7+incompatible github.com/docker/go-connections v0.4.0 github.com/google/go-cmp v0.6.0 - github.com/jackc/pgconn v1.14.1 - github.com/jackc/pgproto3/v2 v2.3.2 - github.com/jackc/pgtype v1.14.0 - github.com/jackc/pgx/v4 v4.18.1 github.com/jackc/pgx/v5 v5.5.1 github.com/peterbourgon/ff/v3 v3.4.0 - github.com/shopspring/decimal v1.3.1 github.com/stretchr/testify v1.8.4 golang.org/x/mod v0.11.0 ) @@ -24,8 +19,6 @@ require ( github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect github.com/kr/text v0.2.0 // indirect diff --git a/go.sum b/go.sum index 2846269..5bd0bf9 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,9 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= -github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -24,95 +17,29 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= -github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= -github.com/jackc/pgconn v1.14.1 h1:smbxIaZA08n6YuxEX1sDyjV/qkbtUtkH20qLkR9MUR4= -github.com/jackc/pgconn v1.14.1/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= -github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= -github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= -github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= -github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= github.com/jackc/pgx/v5 v5.5.1 h1:5I9etrGkLrN+2XPCsi6XLlV5DITbSL/xBZdmAxFcXPI= github.com/jackc/pgx/v5 v5.5.1/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= -github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -128,139 +55,56 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -270,12 +114,9 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= From 8e58ee07478620e863ba20506fb978877d24a060 Mon Sep 17 00:00:00 2001 From: Joe Schafer Date: Wed, 24 Jan 2024 22:16:37 -0800 Subject: [PATCH 4/7] Simplify codec --- example/author/query.sql.go | 152 ++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 67 deletions(-) diff --git a/example/author/query.sql.go b/example/author/query.sql.go index 283cfa9..f84070f 100644 --- a/example/author/query.sql.go +++ b/example/author/query.sql.go @@ -8,6 +8,7 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgtype" + "sync" ) // Querier is a typesafe Go interface backed by SQL queries. @@ -79,13 +80,14 @@ func (r *FindAuthorByIDRow) scanRow(rows pgx.Rows) error { // FindAuthorByID implements Querier.FindAuthorByID. func (q *DBQuerier) FindAuthorByID(ctx context.Context, authorID int32) (FindAuthorByIDRow, error) { ctx = context.WithValue(ctx, "pggen_query_name", "FindAuthorByID") - row := q.conn.QueryRow(ctx, findAuthorByIDSQL, authorID) + //row := q.conn.QueryRow(ctx, findAuthorByIDSQL, authorID) var item FindAuthorByIDRow - s := scanner[*FindAuthorByIDRow]{&item} - if err := row.Scan(s); err != nil { - return item, fmt.Errorf("query FindAuthorByID: %w", err) - } + //s := scanner[*FindAuthorByIDRow]{&item} + //if err := row.Scan(s); err != nil { + // return item, fmt.Errorf("query FindAuthorByID: %w", err) + //} + //return item, nil return item, nil } @@ -102,54 +104,6 @@ func (r *FindAuthorsRow) scanRow(rows pgx.Rows) error { return rows.Scan(&r.AuthorID, &r.FirstName, &r.LastName, &r.Suffix) } -type scanCacheKey struct { - oid uint32 - format int16 - typeName string -} - -type scanPlan[T any] interface { - Scan([]byte, T) error -} - -var scanCache = map[string]scanPlan[any]{} - -func planScan[T any](codec pgtype.Codec, fd pgconn.FieldDescription) scanPlan[*T] { - var target *T - key := scanCacheKey{fd.DataTypeOID, fd.Format, fmt.Sprintf("%T", target)} - // TODO: synchronize - if plan, ok := scanCache[key.typeName]; ok { - return plan.(scanPlan[*T]) - } - plan := codec.PlanScan(nil, fd.DataTypeOID, fd.Format, target) - scanCache[key.typeName] = plan - return plan.(scanPlan[*T]) -} - -type ptrScanner[T any] struct { - basePlan scanPlan[T] -} - -func (s ptrScanner[T]) Scan(src []byte, dst **T) error { - if src == nil { - return nil - } - *dst = new(T) - return s.basePlan.Scan(src, *dst) -} - -func planPtrScan[T any](codec pgtype.Codec, fd pgconn.FieldDescription) scanPlan[**T] { - var target *T - key := scanCacheKey{fd.DataTypeOID, fd.Format, fmt.Sprintf("*%T", target)} - if scan, ok := scanCache[key.typeName]; ok { - return scan.(scanPlan[**T]) - } - basePlan := planScan[T](codec, fd) - var ptrPlan scanPlan[**T] = ptrScanner[T]{basePlan: basePlan.(scanPlan[T])} - scanCache[key.typeName] = ptrPlan.(scanPlan[any]) - return ptrPlan -} - // FindAuthors implements Querier.FindAuthors. func (q *DBQuerier) FindAuthors(ctx context.Context, firstName string) ([]FindAuthorsRow, error) { ctx = context.WithValue(ctx, "pggen_query_name", "FindAuthors") @@ -160,10 +114,10 @@ func (q *DBQuerier) FindAuthors(ctx context.Context, firstName string) ([]FindAu defer rows.Close() fds := rows.FieldDescriptions() - var plan0 = planScan[int32](pgtype.Int4Codec{}, fds[0]) - var plan1 = planScan[string](pgtype.TextCodec{}, fds[1]) - var plan2 = plan1 - var plan3 = planPtrScan[string](pgtype.TextCodec{}, fds[3]) + plan0 := planScan(pgtype.Int4Codec{}, fds[0], (*int32)(nil)) + plan1 := planScan(pgtype.TextCodec{}, fds[1], (*string)(nil)) + plan2 := planScan(pgtype.TextCodec{}, fds[2], (*string)(nil)) + plan3 := planPtrScan(pgtype.TextCodec{}, fds[3], (*string)(nil)) items := []FindAuthorsRow{} for rows.Next() { @@ -208,14 +162,21 @@ func (q *DBQuerier) FindAuthorNames(ctx context.Context, authorID int32) ([]Find return nil, fmt.Errorf("query FindAuthorNames: %w", err) } defer rows.Close() + + //fds := rows.FieldDescriptions() + //plan0 := planPtrScan[string](pgtype.TextCodec{}, fds[0]) + //plan1 := planPtrScan[string](pgtype.TextCodec{}, fds[1]) + items := []FindAuthorNamesRow{} - s := scanner[*FindAuthorNamesRow]{} for rows.Next() { + //vals := rows.RawValues() var item FindAuthorNamesRow - s.item = &item - if err := rows.Scan(s); err != nil { - return nil, fmt.Errorf("scan FindAuthorNames row: %w", err) - } + //if err := plan0.Scan(vals[0], &item.FirstName); err != nil { + // return nil, fmt.Errorf("scan FindAuthors.first_name column: %w", err) + //} + //if err := plan1.Scan(vals[1], &item.FirstName); err != nil { + // return nil, fmt.Errorf("scan FindAuthors.last_name column: %w", err) + //} items = append(items, item) } if err := rows.Err(); err != nil { @@ -333,13 +294,15 @@ func (r *InsertAuthorSuffixRow) scanRow(rows pgx.Rows) error { // InsertAuthorSuffix implements Querier.InsertAuthorSuffix. func (q *DBQuerier) InsertAuthorSuffix(ctx context.Context, params InsertAuthorSuffixParams) (InsertAuthorSuffixRow, error) { ctx = context.WithValue(ctx, "pggen_query_name", "InsertAuthorSuffix") - row := q.conn.QueryRow(ctx, insertAuthorSuffixSQL, params.FirstName, params.LastName, params.Suffix) + //row := q.conn.QueryRow(ctx, insertAuthorSuffixSQL, params.FirstName, params.LastName, params.Suffix) var item InsertAuthorSuffixRow - s := scanner[*InsertAuthorSuffixRow]{&item} - if err := row.Scan(s); err != nil { - return item, fmt.Errorf("query InsertAuthorSuffix: %w", err) - } return item, nil + // + //s := scanner[*InsertAuthorSuffixRow]{&item} + //if err := row.Scan(s); err != nil { + // return item, fmt.Errorf("query InsertAuthorSuffix: %w", err) + //} + //return item, nil } const stringAggFirstNameSQL = `SELECT string_agg(first_name, ',') AS NAMES FROM author WHERE author_id = $1;` @@ -367,3 +330,58 @@ func (q *DBQuerier) ArrayAggFirstName(ctx context.Context, authorID int32) ([]st } return item, nil } + +type scanCacheKey struct { + oid uint32 + format int16 + typeName string +} + +var ( + plans = make(map[scanCacheKey]pgtype.ScanPlan, 16) + plansMu sync.RWMutex +) + +func planScan(codec pgtype.Codec, fd pgconn.FieldDescription, target any) pgtype.ScanPlan { + key := scanCacheKey{fd.DataTypeOID, fd.Format, fmt.Sprintf("%T", target)} + plansMu.RLock() + plan := plans[key] + plansMu.RUnlock() + if plan != nil { + return plan + } + plan = codec.PlanScan(nil, fd.DataTypeOID, fd.Format, target) + plansMu.Lock() + plans[key] = plan + plansMu.Unlock() + return plan +} + +type ptrScanner[T any] struct { + basePlan pgtype.ScanPlan +} + +func (s ptrScanner[T]) Scan(src []byte, dst any) error { + if src == nil { + return nil + } + d := dst.(**T) + *d = new(T) + return s.basePlan.Scan(src, **d) +} + +func planPtrScan[T any](codec pgtype.Codec, fd pgconn.FieldDescription, target *T) pgtype.ScanPlan { + key := scanCacheKey{fd.DataTypeOID, fd.Format, fmt.Sprintf("*%T", target)} + plansMu.RLock() + plan := plans[key] + plansMu.RUnlock() + if plan != nil { + return plan + } + basePlan := planScan(codec, fd, target) + ptrPlan := ptrScanner[T]{basePlan} + plansMu.Lock() + plans[key] = plan + plansMu.Unlock() + return ptrPlan +} From 1b29c21c3998c76af787ed3cd089593aaaaa4f68 Mon Sep 17 00:00:00 2001 From: Joe Schafer Date: Wed, 24 Jan 2024 23:21:16 -0800 Subject: [PATCH 5/7] fix most author example tests --- example/author/query.sql.go | 217 ++++++++++++++++++++++-------------- 1 file changed, 132 insertions(+), 85 deletions(-) diff --git a/example/author/query.sql.go b/example/author/query.sql.go index f84070f..6dd2214 100644 --- a/example/author/query.sql.go +++ b/example/author/query.sql.go @@ -73,22 +73,37 @@ type FindAuthorByIDRow struct { Suffix *string `json:"suffix"` } -func (r *FindAuthorByIDRow) scanRow(rows pgx.Rows) error { - return rows.Scan(&r.AuthorID, &r.FirstName, &r.LastName, &r.Suffix) -} - // FindAuthorByID implements Querier.FindAuthorByID. func (q *DBQuerier) FindAuthorByID(ctx context.Context, authorID int32) (FindAuthorByIDRow, error) { ctx = context.WithValue(ctx, "pggen_query_name", "FindAuthorByID") - //row := q.conn.QueryRow(ctx, findAuthorByIDSQL, authorID) - var item FindAuthorByIDRow - - //s := scanner[*FindAuthorByIDRow]{&item} - //if err := row.Scan(s); err != nil { - // return item, fmt.Errorf("query FindAuthorByID: %w", err) - //} - //return item, nil - return item, nil + rows, err := q.conn.Query(ctx, findAuthorByIDSQL, authorID) + if err != nil { + return FindAuthorByIDRow{}, fmt.Errorf("query FindAuthorByID: %w", err) + } + + fds := rows.FieldDescriptions() + plan0 := planScan(pgtype.Int4Codec{}, fds[0], (*int32)(nil)) + plan1 := planScan(pgtype.TextCodec{}, fds[1], (*string)(nil)) + plan2 := planScan(pgtype.TextCodec{}, fds[2], (*string)(nil)) + plan3 := planPtrScan(pgtype.TextCodec{}, fds[3], (*string)(nil)) + + return pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) (FindAuthorByIDRow, error) { + vals := row.RawValues() + var item FindAuthorByIDRow + if err := plan0.Scan(vals[0], &item.AuthorID); err != nil { + return item, fmt.Errorf("scan FindAuthors.author_id column: %w", err) + } + if err := plan1.Scan(vals[1], &item.FirstName); err != nil { + return item, fmt.Errorf("scan FindAuthors.first_name column: %w", err) + } + if err := plan2.Scan(vals[2], &item.LastName); err != nil { + return item, fmt.Errorf("scan FindAuthors.last_name column: %w", err) + } + if err := plan3.Scan(vals[3], &item.Suffix); err != nil { + return item, fmt.Errorf("scan FindAuthors.suffix column: %w", err) + } + return item, nil + }) } const findAuthorsSQL = `SELECT * FROM author WHERE first_name = $1;` @@ -100,10 +115,6 @@ type FindAuthorsRow struct { Suffix *string `json:"suffix"` } -func (r *FindAuthorsRow) scanRow(rows pgx.Rows) error { - return rows.Scan(&r.AuthorID, &r.FirstName, &r.LastName, &r.Suffix) -} - // FindAuthors implements Querier.FindAuthors. func (q *DBQuerier) FindAuthors(ctx context.Context, firstName string) ([]FindAuthorsRow, error) { ctx = context.WithValue(ctx, "pggen_query_name", "FindAuthors") @@ -111,7 +122,6 @@ func (q *DBQuerier) FindAuthors(ctx context.Context, firstName string) ([]FindAu if err != nil { return nil, fmt.Errorf("query FindAuthors: %w", err) } - defer rows.Close() fds := rows.FieldDescriptions() plan0 := planScan(pgtype.Int4Codec{}, fds[0], (*int32)(nil)) @@ -119,28 +129,23 @@ func (q *DBQuerier) FindAuthors(ctx context.Context, firstName string) ([]FindAu plan2 := planScan(pgtype.TextCodec{}, fds[2], (*string)(nil)) plan3 := planPtrScan(pgtype.TextCodec{}, fds[3], (*string)(nil)) - items := []FindAuthorsRow{} - for rows.Next() { - vals := rows.RawValues() + return pgx.CollectRows(rows, func(row pgx.CollectableRow) (FindAuthorsRow, error) { + vals := row.RawValues() var item FindAuthorsRow if err := plan0.Scan(vals[0], &item.AuthorID); err != nil { - return nil, fmt.Errorf("scan FindAuthors.author_id column: %w", err) + return item, fmt.Errorf("scan FindAuthors.author_id column: %w", err) } if err := plan1.Scan(vals[1], &item.FirstName); err != nil { - return nil, fmt.Errorf("scan FindAuthors.first_name column: %w", err) + return item, fmt.Errorf("scan FindAuthors.first_name column: %w", err) } if err := plan2.Scan(vals[2], &item.LastName); err != nil { - return nil, fmt.Errorf("scan FindAuthors.last_name column: %w", err) + return item, fmt.Errorf("scan FindAuthors.last_name column: %w", err) } if err := plan3.Scan(vals[3], &item.Suffix); err != nil { - return nil, fmt.Errorf("scan FindAuthors.suffix column: %w", err) + return item, fmt.Errorf("scan FindAuthors.suffix column: %w", err) } - items = append(items, item) - } - if err := rows.Err(); err != nil { - return nil, fmt.Errorf("close FindAuthors rows: %w", err) - } - return items, err + return item, nil + }) } const findAuthorNamesSQL = `SELECT first_name, last_name FROM author ORDER BY author_id = $1;` @@ -161,28 +166,22 @@ func (q *DBQuerier) FindAuthorNames(ctx context.Context, authorID int32) ([]Find if err != nil { return nil, fmt.Errorf("query FindAuthorNames: %w", err) } - defer rows.Close() - //fds := rows.FieldDescriptions() - //plan0 := planPtrScan[string](pgtype.TextCodec{}, fds[0]) - //plan1 := planPtrScan[string](pgtype.TextCodec{}, fds[1]) + fds := rows.FieldDescriptions() + plan0 := planScan(pgtype.TextCodec{}, fds[0], (*string)(nil)) + plan1 := planScan(pgtype.TextCodec{}, fds[1], (*string)(nil)) - items := []FindAuthorNamesRow{} - for rows.Next() { - //vals := rows.RawValues() + return pgx.CollectRows(rows, func(row pgx.CollectableRow) (FindAuthorNamesRow, error) { + vals := row.RawValues() var item FindAuthorNamesRow - //if err := plan0.Scan(vals[0], &item.FirstName); err != nil { - // return nil, fmt.Errorf("scan FindAuthors.first_name column: %w", err) - //} - //if err := plan1.Scan(vals[1], &item.FirstName); err != nil { - // return nil, fmt.Errorf("scan FindAuthors.last_name column: %w", err) - //} - items = append(items, item) - } - if err := rows.Err(); err != nil { - return nil, fmt.Errorf("close FindAuthorNames rows: %w", err) - } - return items, err + if err := plan0.Scan(vals[0], &item.FirstName); err != nil { + return item, fmt.Errorf("scan FindAuthors.first_name column: %w", err) + } + if err := plan1.Scan(vals[1], &item.LastName); err != nil { + return item, fmt.Errorf("scan FindAuthors.last_name column: %w", err) + } + return item, nil + }) } const findFirstNamesSQL = `SELECT first_name FROM author ORDER BY author_id = $1;` @@ -194,19 +193,18 @@ func (q *DBQuerier) FindFirstNames(ctx context.Context, authorID int32) ([]*stri if err != nil { return nil, fmt.Errorf("query FindFirstNames: %w", err) } - defer rows.Close() - items := []*string{} - for rows.Next() { + + fds := rows.FieldDescriptions() + plan0 := planScan(pgtype.TextCodec{}, fds[0], (*string)(nil)) + + return pgx.CollectRows(rows, func(row pgx.CollectableRow) (*string, error) { + vals := row.RawValues() var item string - if err := rows.Scan(&item); err != nil { + if err := plan0.Scan(vals[0], &item); err != nil { return nil, fmt.Errorf("scan FindFirstNames row: %w", err) } - items = append(items, &item) - } - if err := rows.Err(); err != nil { - return nil, fmt.Errorf("close FindFirstNames rows: %w", err) - } - return items, err + return &item, nil + }) } const deleteAuthorsSQL = `DELETE FROM author WHERE first_name = 'joe';` @@ -262,12 +260,22 @@ RETURNING author_id;` // InsertAuthor implements Querier.InsertAuthor. func (q *DBQuerier) InsertAuthor(ctx context.Context, firstName string, lastName string) (int32, error) { ctx = context.WithValue(ctx, "pggen_query_name", "InsertAuthor") - row := q.conn.QueryRow(ctx, insertAuthorSQL, firstName, lastName) - var item int32 - if err := row.Scan(&item); err != nil { - return item, fmt.Errorf("query InsertAuthor: %w", err) + rows, err := q.conn.Query(ctx, insertAuthorSQL, firstName, lastName) + if err != nil { + return 0, fmt.Errorf("query InsertAuthor: %w", err) } - return item, nil + + fds := rows.FieldDescriptions() + plan0 := planScan(pgtype.Int4Codec{}, fds[0], (*int32)(nil)) + + return pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) (int32, error) { + vals := row.RawValues() + var item int32 + if err := plan0.Scan(vals[0], &item); err != nil { + return item, fmt.Errorf("scan InsertAuthor: %w", err) + } + return item, nil + }) } const insertAuthorSuffixSQL = `INSERT INTO author (first_name, last_name, suffix) @@ -294,15 +302,34 @@ func (r *InsertAuthorSuffixRow) scanRow(rows pgx.Rows) error { // InsertAuthorSuffix implements Querier.InsertAuthorSuffix. func (q *DBQuerier) InsertAuthorSuffix(ctx context.Context, params InsertAuthorSuffixParams) (InsertAuthorSuffixRow, error) { ctx = context.WithValue(ctx, "pggen_query_name", "InsertAuthorSuffix") - //row := q.conn.QueryRow(ctx, insertAuthorSuffixSQL, params.FirstName, params.LastName, params.Suffix) - var item InsertAuthorSuffixRow - return item, nil - // - //s := scanner[*InsertAuthorSuffixRow]{&item} - //if err := row.Scan(s); err != nil { - // return item, fmt.Errorf("query InsertAuthorSuffix: %w", err) - //} - //return item, nil + rows, err := q.conn.Query(ctx, insertAuthorSuffixSQL, params.FirstName, params.LastName, params.Suffix) + if err != nil { + return InsertAuthorSuffixRow{}, fmt.Errorf("query InsertAuthorSuffix: %w", err) + } + + fds := rows.FieldDescriptions() + plan0 := planScan(pgtype.Int4Codec{}, fds[0], (*int32)(nil)) + plan1 := planScan(pgtype.TextCodec{}, fds[1], (*string)(nil)) + plan2 := planScan(pgtype.TextCodec{}, fds[2], (*string)(nil)) + plan3 := planPtrScan(pgtype.TextCodec{}, fds[3], (*string)(nil)) + + return pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) (InsertAuthorSuffixRow, error) { + vals := row.RawValues() + var item InsertAuthorSuffixRow + if err := plan0.Scan(vals[0], &item.AuthorID); err != nil { + return item, fmt.Errorf("scan InsertAuthorSuffixRow.author_id column: %w", err) + } + if err := plan1.Scan(vals[1], &item.FirstName); err != nil { + return item, fmt.Errorf("scan InsertAuthorSuffixRow.first_name column: %w", err) + } + if err := plan2.Scan(vals[2], &item.LastName); err != nil { + return item, fmt.Errorf("scan InsertAuthorSuffixRow.last_name column: %w", err) + } + if err := plan3.Scan(vals[3], &item.Suffix); err != nil { + return item, fmt.Errorf("scan InsertAuthorSuffixRow.suffix column: %w", err) + } + return item, nil + }) } const stringAggFirstNameSQL = `SELECT string_agg(first_name, ',') AS NAMES FROM author WHERE author_id = $1;` @@ -310,12 +337,22 @@ const stringAggFirstNameSQL = `SELECT string_agg(first_name, ',') AS NAMES FROM // StringAggFirstName implements Querier.StringAggFirstName. func (q *DBQuerier) StringAggFirstName(ctx context.Context, authorID int32) (*string, error) { ctx = context.WithValue(ctx, "pggen_query_name", "StringAggFirstName") - row := q.conn.QueryRow(ctx, stringAggFirstNameSQL, authorID) - var item *string - if err := row.Scan(&item); err != nil { - return item, fmt.Errorf("query StringAggFirstName: %w", err) + rows, err := q.conn.Query(ctx, stringAggFirstNameSQL, authorID) + if err != nil { + return nil, fmt.Errorf("query StringAggFirstName: %w", err) } - return item, nil + + fds := rows.FieldDescriptions() + plan0 := planPtrScan(pgtype.TextCodec{}, fds[0], (*string)(nil)) + + return pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) (*string, error) { + vals := row.RawValues() + var item *string + if err := plan0.Scan(vals[0], &item); err != nil { + return item, fmt.Errorf("scan StringAggFirstName column: %w", err) + } + return item, nil + }) } const arrayAggFirstNameSQL = `SELECT array_agg(first_name) AS NAMES FROM author WHERE author_id = $1;` @@ -323,12 +360,22 @@ const arrayAggFirstNameSQL = `SELECT array_agg(first_name) AS NAMES FROM author // ArrayAggFirstName implements Querier.ArrayAggFirstName. func (q *DBQuerier) ArrayAggFirstName(ctx context.Context, authorID int32) ([]string, error) { ctx = context.WithValue(ctx, "pggen_query_name", "ArrayAggFirstName") - row := q.conn.QueryRow(ctx, arrayAggFirstNameSQL, authorID) - item := []string{} - if err := row.Scan(&item); err != nil { - return item, fmt.Errorf("query ArrayAggFirstName: %w", err) + rows, err := q.conn.Query(ctx, arrayAggFirstNameSQL, authorID) + if err != nil { + return nil, fmt.Errorf("query ArrayAggFirstName: %w", err) } - return item, nil + + fds := rows.FieldDescriptions() + plan0 := planScan(pgtype.TextCodec{}, fds[0], (*string)(nil)) + + return pgx.CollectRows(rows, func(row pgx.CollectableRow) (string, error) { + vals := row.RawValues() + var item string + if err := plan0.Scan(vals[0], &item); err != nil { + return item, fmt.Errorf("scan StringAggFirstName column: %w", err) + } + return item, nil + }) } type scanCacheKey struct { @@ -367,7 +414,7 @@ func (s ptrScanner[T]) Scan(src []byte, dst any) error { } d := dst.(**T) *d = new(T) - return s.basePlan.Scan(src, **d) + return s.basePlan.Scan(src, *d) } func planPtrScan[T any](codec pgtype.Codec, fd pgconn.FieldDescription, target *T) pgtype.ScanPlan { From 2b41dd18069be924125027b2c63c7362243ffe4a Mon Sep 17 00:00:00 2001 From: Joe Schafer Date: Thu, 25 Jan 2024 17:27:34 -0800 Subject: [PATCH 6/7] move codecs into template --- internal/codegen/golang/query.gotemplate | 112 +++++++++++++++++----- internal/codegen/golang/templated_file.go | 59 +++++++++++- 2 files changed, 141 insertions(+), 30 deletions(-) diff --git a/internal/codegen/golang/query.gotemplate b/internal/codegen/golang/query.gotemplate index d645667..852c1b8 100644 --- a/internal/codegen/golang/query.gotemplate +++ b/internal/codegen/golang/query.gotemplate @@ -42,7 +42,6 @@ func NewQuerier(conn genericConn) *DBQuerier { return &DBQuerier{conn: conn} } -{{- range .Declarers}}{{- "\n\n" -}}{{ .Declare $.PkgPath }}{{ end -}} {{- end -}} {{- range $i, $q := .Queries -}} @@ -55,34 +54,41 @@ const {{ $q.SQLVarName }} = {{ $q.EmitPreparedSQL }} func (q *DBQuerier) {{ $q.Name }}(ctx context.Context {{- $q.EmitParams }}) ({{ $q.EmitResultType }}, error) { ctx = context.WithValue(ctx, "pggen_query_name", "{{ $q.Name }}") {{- if eq $q.ResultKind ":one" }} - row := q.conn.QueryRow(ctx, {{ $q.SQLVarName }} {{- $q.EmitParamNames }}) - {{ $q.EmitResultTypeInit "item" }} - {{- $q.EmitResultDecoders }} - if err := row.Scan({{ $q.EmitRowScanArgs }}); err != nil { - return {{ $q.EmitResultExpr "item" }}, fmt.Errorf("query {{ $q.Name }}: %w", err) - } - {{- $q.EmitResultAssigns "item" }} - return {{ $q.EmitResultExpr "item" }}, nil -{{- else if eq $q.ResultKind ":many" }} - rows, err := q.conn.Query(ctx, {{ $q.SQLVarName }} {{- $q.EmitParamNames }}) + row, err := q.conn.Query(ctx, {{ $q.SQLVarName }} {{- $q.EmitParamNames }}) if err != nil { return nil, fmt.Errorf("query {{ $q.Name }}: %w", err) } - defer rows.Close() - {{ $q.EmitResultTypeInit "items" }} - {{- $q.EmitResultDecoders }} - for rows.Next() { - var item {{ $q.EmitResultElem }} - if err := rows.Scan({{- $q.EmitRowScanArgs -}}); err != nil { - return nil, fmt.Errorf("scan {{ $q.Name }} row: %w", err) - } - {{- $q.EmitResultAssigns "nil" }} - items = append(items, {{ $q.EmitResultExpr "item" }}) - } - if err := rows.Err(); err != nil { - return nil, fmt.Errorf("close {{ $q.Name }} rows: %w", err) + + fds := rows.FieldDescriptions() + {{- range $i, $col := $q.Outputs -}} + {{ $q.EmitPlanScan $i $col}} + {{- end -}} + + return pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) ({{ $q.EmitSingularResultType }}, error) { + vals := row.RawValues() + {{ $q.EmitResultTypeInit "item" }} + {{- range $i, $col := $q.Outputs -}} + {{- $q.EmitScanColumn $i $col -}} + {{- end -}} + }) +{{- else if eq $q.ResultKind ":many" }} + row, err := q.conn.Query(ctx, {{ $q.SQLVarName }} {{- $q.EmitParamNames }}) + if err != nil { + return nil, fmt.Errorf("query {{ $q.Name }}: %w", err) } - return items, err + + fds := rows.FieldDescriptions() + {{- range $i, $col := $q.Outputs -}} + {{ $q.EmitPlanScan $i $col}} + {{- end -}} + + return pgx.CollectRows(rows, func(row pgx.CollectableRow) ({{ $q.EmitSingularResultType }}, error) { + vals := row.RawValues() + {{ $q.EmitResultTypeInit "item" }} + {{- range $i, $col := $q.Outputs -}} + {{- $q.EmitScanColumn $i $col -}} + {{- end -}} + }) {{- else if eq $q.ResultKind ":exec" }} cmdTag, err := q.conn.Exec(ctx, {{ $q.SQLVarName }} {{- $q.EmitParamNames }}) if err != nil { @@ -93,4 +99,60 @@ func (q *DBQuerier) {{ $q.Name }}(ctx context.Context {{- $q.EmitParams }}) ({{ } {{- end -}} {{- "\n" -}} +{{ if .IsLeader }} +type scanCacheKey struct { + oid uint32 + format int16 + typeName string +} + +var ( + plans = make(map[scanCacheKey]pgtype.ScanPlan, 16) + plansMu sync.RWMutex +) + +func planScan(codec pgtype.Codec, fd pgconn.FieldDescription, target any) pgtype.ScanPlan { + key := scanCacheKey{fd.DataTypeOID, fd.Format, fmt.Sprintf("%T", target)} + plansMu.RLock() + plan := plans[key] + plansMu.RUnlock() + if plan != nil { + return plan + } + plan = codec.PlanScan(nil, fd.DataTypeOID, fd.Format, target) + plansMu.Lock() + plans[key] = plan + plansMu.Unlock() + return plan +} + +type ptrScanner[T any] struct { + basePlan pgtype.ScanPlan +} + +func (s ptrScanner[T]) Scan(src []byte, dst any) error { + if src == nil { + return nil + } + d := dst.(**T) + *d = new(T) + return s.basePlan.Scan(src, *d) +} + +func planPtrScan[T any](codec pgtype.Codec, fd pgconn.FieldDescription, target *T) pgtype.ScanPlan { + key := scanCacheKey{fd.DataTypeOID, fd.Format, fmt.Sprintf("*%T", target)} + plansMu.RLock() + plan := plans[key] + plansMu.RUnlock() + if plan != nil { + return plan + } + basePlan := planScan(codec, fd, target) + ptrPlan := ptrScanner[T]{basePlan} + plansMu.Lock() + plans[key] = plan + plansMu.Unlock() + return ptrPlan +} +{{- end -}} {{- end -}} diff --git a/internal/codegen/golang/templated_file.go b/internal/codegen/golang/templated_file.go index d2a5430..715f773 100644 --- a/internal/codegen/golang/templated_file.go +++ b/internal/codegen/golang/templated_file.go @@ -200,6 +200,31 @@ func (tq TemplatedQuery) isInlineParams() bool { return len(tq.Inputs) <= tq.InlineParamCount } +// EmitPlanScan emits the variable that hold the pgtype.ScanPlan for a query +// output. +func (tq TemplatedQuery) EmitPlanScan(idx int, out TemplatedColumn) (string, error) { + switch tq.ResultKind { + case ast.ResultKindExec: + return "", fmt.Errorf("cannot EmitPlanScanArgs for :exec query %s", tq.Name) + case ast.ResultKindMany, ast.ResultKindOne: + break // okay + default: + return "", fmt.Errorf("unhandled EmitPlanScanArgs type: %s", tq.ResultKind) + } + return fmt.Sprintf("planScan%d = pgtype.TODOCodec{}, fs[%d], (*%s)(nil))", idx, idx, out.Type.BaseName()), nil +} + +// EmitScanColumn emits scan call for a single TemplatedColumn. +func (tq TemplatedQuery) EmitScanColumn(idx int, out TemplatedColumn) (string, error) { + sb := &strings.Builder{} + _, _ = fmt.Fprintf(sb, "if err := plan%d.Scan(vals[%d], &item); err != nil\n", idx, idx) + sb.WriteString("\t\t\t") + _, _ = fmt.Fprintf(sb, `return item, fmt.Errorf("scan %s.%s: %%w", err)`, tq.Name, out.PgName) + sb.WriteString("\n") + sb.WriteString("\t\t}\n") + return sb.String(), nil +} + // EmitRowScanArgs emits the args to scan a single row from a pgx.Row or // pgx.Rows. func (tq TemplatedQuery) EmitRowScanArgs() (string, error) { @@ -256,9 +281,10 @@ func (tq TemplatedQuery) EmitRowScanArgs() (string, error) { return sb.String(), nil } -// EmitResultType returns the string representing the overall query result type, -// meaning the return result. -func (tq TemplatedQuery) EmitResultType() (string, error) { +// EmitSingularResultType returns the string representing a single element +// of the overall query result type, like FindAuthorsRow when the overall return +// type is []FindAuthorsRow. +func (tq TemplatedQuery) EmitSingularResultType() (string, error) { outs := removeVoidColumns(tq.Outputs) switch tq.ResultKind { case ast.ResultKindExec: @@ -268,9 +294,9 @@ func (tq TemplatedQuery) EmitResultType() (string, error) { case 0: return "pgconn.CommandTag", nil case 1: - return "[]" + outs[0].QualType, nil + return outs[0].QualType, nil default: - return "[]" + tq.Name + "Row", nil + return tq.Name + "Row", nil } case ast.ResultKindOne: switch len(outs) { @@ -281,6 +307,29 @@ func (tq TemplatedQuery) EmitResultType() (string, error) { default: return tq.Name + "Row", nil } + default: + return "", fmt.Errorf("unhandled EmitSingularResultType kind: %s", tq.ResultKind) + } +} + +// EmitResultType returns the string representing the overall query result type, +// meaning the return result. +func (tq TemplatedQuery) EmitResultType() (string, error) { + rt, err := tq.EmitSingularResultType() + if err != nil { + return "", fmt.Errorf("unhandled EmitResultType: %w", err) + } + switch tq.ResultKind { + case ast.ResultKindExec: + return rt, nil + case ast.ResultKindMany: + outs := removeVoidColumns(tq.Outputs) + if len(outs) == 0 { + return rt, nil + } + return "[]" + rt, nil + case ast.ResultKindOne: + return rt, nil default: return "", fmt.Errorf("unhandled EmitResultType kind: %s", tq.ResultKind) } From 7ca7c671954af85d8e511ec15c7d2417be335e15 Mon Sep 17 00:00:00 2001 From: Joe Schafer Date: Fri, 26 Jan 2024 10:13:40 -0800 Subject: [PATCH 7/7] update template for code --- example/author/query.sql.go | 124 ++++++------ internal/codegen/golang/query.gotemplate | 54 ++---- internal/codegen/golang/templated_file.go | 221 ++++++---------------- internal/codegen/golang/templater.go | 25 ++- internal/pg/type_cache.go | 19 +- 5 files changed, 173 insertions(+), 270 deletions(-) diff --git a/example/author/query.sql.go b/example/author/query.sql.go index 6dd2214..5af26d3 100644 --- a/example/author/query.sql.go +++ b/example/author/query.sql.go @@ -49,7 +49,7 @@ type Querier interface { var _ Querier = &DBQuerier{} type DBQuerier struct { - conn genericConn // underlying Postgres transport to use + conn genericConn } // genericConn is a connection like *pgx.Conn, pgx.Tx, or *pgxpool.Pool. @@ -80,27 +80,26 @@ func (q *DBQuerier) FindAuthorByID(ctx context.Context, authorID int32) (FindAut if err != nil { return FindAuthorByIDRow{}, fmt.Errorf("query FindAuthorByID: %w", err) } - fds := rows.FieldDescriptions() - plan0 := planScan(pgtype.Int4Codec{}, fds[0], (*int32)(nil)) + plan0 := planScan(pgtype.TextCodec{}, fds[0], (*int32)(nil)) plan1 := planScan(pgtype.TextCodec{}, fds[1], (*string)(nil)) plan2 := planScan(pgtype.TextCodec{}, fds[2], (*string)(nil)) - plan3 := planPtrScan(pgtype.TextCodec{}, fds[3], (*string)(nil)) + plan3 := planScan(pgtype.TextCodec{}, fds[3], (**string)(nil)) return pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) (FindAuthorByIDRow, error) { vals := row.RawValues() var item FindAuthorByIDRow - if err := plan0.Scan(vals[0], &item.AuthorID); err != nil { - return item, fmt.Errorf("scan FindAuthors.author_id column: %w", err) + if err := plan0.Scan(vals[0], &item); err != nil { + return item, fmt.Errorf("scan FindAuthorByID.author_id: %w", err) } - if err := plan1.Scan(vals[1], &item.FirstName); err != nil { - return item, fmt.Errorf("scan FindAuthors.first_name column: %w", err) + if err := plan1.Scan(vals[1], &item); err != nil { + return item, fmt.Errorf("scan FindAuthorByID.first_name: %w", err) } - if err := plan2.Scan(vals[2], &item.LastName); err != nil { - return item, fmt.Errorf("scan FindAuthors.last_name column: %w", err) + if err := plan2.Scan(vals[2], &item); err != nil { + return item, fmt.Errorf("scan FindAuthorByID.last_name: %w", err) } - if err := plan3.Scan(vals[3], &item.Suffix); err != nil { - return item, fmt.Errorf("scan FindAuthors.suffix column: %w", err) + if err := plan3.Scan(vals[3], &item); err != nil { + return item, fmt.Errorf("scan FindAuthorByID.suffix: %w", err) } return item, nil }) @@ -122,27 +121,26 @@ func (q *DBQuerier) FindAuthors(ctx context.Context, firstName string) ([]FindAu if err != nil { return nil, fmt.Errorf("query FindAuthors: %w", err) } - fds := rows.FieldDescriptions() - plan0 := planScan(pgtype.Int4Codec{}, fds[0], (*int32)(nil)) + plan0 := planScan(pgtype.TextCodec{}, fds[0], (*int32)(nil)) plan1 := planScan(pgtype.TextCodec{}, fds[1], (*string)(nil)) plan2 := planScan(pgtype.TextCodec{}, fds[2], (*string)(nil)) - plan3 := planPtrScan(pgtype.TextCodec{}, fds[3], (*string)(nil)) + plan3 := planScan(pgtype.TextCodec{}, fds[3], (**string)(nil)) return pgx.CollectRows(rows, func(row pgx.CollectableRow) (FindAuthorsRow, error) { vals := row.RawValues() var item FindAuthorsRow - if err := plan0.Scan(vals[0], &item.AuthorID); err != nil { - return item, fmt.Errorf("scan FindAuthors.author_id column: %w", err) + if err := plan0.Scan(vals[0], &item); err != nil { + return item, fmt.Errorf("scan FindAuthors.author_id: %w", err) } - if err := plan1.Scan(vals[1], &item.FirstName); err != nil { - return item, fmt.Errorf("scan FindAuthors.first_name column: %w", err) + if err := plan1.Scan(vals[1], &item); err != nil { + return item, fmt.Errorf("scan FindAuthors.first_name: %w", err) } - if err := plan2.Scan(vals[2], &item.LastName); err != nil { - return item, fmt.Errorf("scan FindAuthors.last_name column: %w", err) + if err := plan2.Scan(vals[2], &item); err != nil { + return item, fmt.Errorf("scan FindAuthors.last_name: %w", err) } - if err := plan3.Scan(vals[3], &item.Suffix); err != nil { - return item, fmt.Errorf("scan FindAuthors.suffix column: %w", err) + if err := plan3.Scan(vals[3], &item); err != nil { + return item, fmt.Errorf("scan FindAuthors.suffix: %w", err) } return item, nil }) @@ -155,10 +153,6 @@ type FindAuthorNamesRow struct { LastName *string `json:"last_name"` } -func (r *FindAuthorNamesRow) scanRow(rows pgx.Rows) error { - return rows.Scan(&r.FirstName, &r.LastName) -} - // FindAuthorNames implements Querier.FindAuthorNames. func (q *DBQuerier) FindAuthorNames(ctx context.Context, authorID int32) ([]FindAuthorNamesRow, error) { ctx = context.WithValue(ctx, "pggen_query_name", "FindAuthorNames") @@ -166,19 +160,18 @@ func (q *DBQuerier) FindAuthorNames(ctx context.Context, authorID int32) ([]Find if err != nil { return nil, fmt.Errorf("query FindAuthorNames: %w", err) } - fds := rows.FieldDescriptions() - plan0 := planScan(pgtype.TextCodec{}, fds[0], (*string)(nil)) - plan1 := planScan(pgtype.TextCodec{}, fds[1], (*string)(nil)) + plan0 := planScan(pgtype.TextCodec{}, fds[0], (**string)(nil)) + plan1 := planScan(pgtype.TextCodec{}, fds[1], (**string)(nil)) return pgx.CollectRows(rows, func(row pgx.CollectableRow) (FindAuthorNamesRow, error) { vals := row.RawValues() var item FindAuthorNamesRow - if err := plan0.Scan(vals[0], &item.FirstName); err != nil { - return item, fmt.Errorf("scan FindAuthors.first_name column: %w", err) + if err := plan0.Scan(vals[0], &item); err != nil { + return item, fmt.Errorf("scan FindAuthorNames.first_name: %w", err) } - if err := plan1.Scan(vals[1], &item.LastName); err != nil { - return item, fmt.Errorf("scan FindAuthors.last_name column: %w", err) + if err := plan1.Scan(vals[1], &item); err != nil { + return item, fmt.Errorf("scan FindAuthorNames.last_name: %w", err) } return item, nil }) @@ -193,17 +186,16 @@ func (q *DBQuerier) FindFirstNames(ctx context.Context, authorID int32) ([]*stri if err != nil { return nil, fmt.Errorf("query FindFirstNames: %w", err) } - fds := rows.FieldDescriptions() - plan0 := planScan(pgtype.TextCodec{}, fds[0], (*string)(nil)) + plan0 := planScan(pgtype.TextCodec{}, fds[0], (**string)(nil)) return pgx.CollectRows(rows, func(row pgx.CollectableRow) (*string, error) { vals := row.RawValues() - var item string + var item *string if err := plan0.Scan(vals[0], &item); err != nil { - return nil, fmt.Errorf("scan FindFirstNames row: %w", err) + return item, fmt.Errorf("scan FindFirstNames.first_name: %w", err) } - return &item, nil + return item, nil }) } @@ -214,7 +206,7 @@ func (q *DBQuerier) DeleteAuthors(ctx context.Context) (pgconn.CommandTag, error ctx = context.WithValue(ctx, "pggen_query_name", "DeleteAuthors") cmdTag, err := q.conn.Exec(ctx, deleteAuthorsSQL) if err != nil { - return cmdTag, fmt.Errorf("exec query DeleteAuthors: %w", err) + return pgconn.CommandTag{}, fmt.Errorf("exec query DeleteAuthors: %w", err) } return cmdTag, err } @@ -226,7 +218,7 @@ func (q *DBQuerier) DeleteAuthorsByFirstName(ctx context.Context, firstName stri ctx = context.WithValue(ctx, "pggen_query_name", "DeleteAuthorsByFirstName") cmdTag, err := q.conn.Exec(ctx, deleteAuthorsByFirstNameSQL, firstName) if err != nil { - return cmdTag, fmt.Errorf("exec query DeleteAuthorsByFirstName: %w", err) + return pgconn.CommandTag{}, fmt.Errorf("exec query DeleteAuthorsByFirstName: %w", err) } return cmdTag, err } @@ -248,7 +240,7 @@ func (q *DBQuerier) DeleteAuthorsByFullName(ctx context.Context, params DeleteAu ctx = context.WithValue(ctx, "pggen_query_name", "DeleteAuthorsByFullName") cmdTag, err := q.conn.Exec(ctx, deleteAuthorsByFullNameSQL, params.FirstName, params.LastName, params.Suffix) if err != nil { - return cmdTag, fmt.Errorf("exec query DeleteAuthorsByFullName: %w", err) + return pgconn.CommandTag{}, fmt.Errorf("exec query DeleteAuthorsByFullName: %w", err) } return cmdTag, err } @@ -264,15 +256,14 @@ func (q *DBQuerier) InsertAuthor(ctx context.Context, firstName string, lastName if err != nil { return 0, fmt.Errorf("query InsertAuthor: %w", err) } - fds := rows.FieldDescriptions() - plan0 := planScan(pgtype.Int4Codec{}, fds[0], (*int32)(nil)) + plan0 := planScan(pgtype.TextCodec{}, fds[0], (*int32)(nil)) return pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) (int32, error) { vals := row.RawValues() var item int32 if err := plan0.Scan(vals[0], &item); err != nil { - return item, fmt.Errorf("scan InsertAuthor: %w", err) + return item, fmt.Errorf("scan InsertAuthor.author_id: %w", err) } return item, nil }) @@ -295,10 +286,6 @@ type InsertAuthorSuffixRow struct { Suffix *string `json:"suffix"` } -func (r *InsertAuthorSuffixRow) scanRow(rows pgx.Rows) error { - return rows.Scan(&r.AuthorID, &r.FirstName, &r.LastName, &r.Suffix) -} - // InsertAuthorSuffix implements Querier.InsertAuthorSuffix. func (q *DBQuerier) InsertAuthorSuffix(ctx context.Context, params InsertAuthorSuffixParams) (InsertAuthorSuffixRow, error) { ctx = context.WithValue(ctx, "pggen_query_name", "InsertAuthorSuffix") @@ -306,33 +293,32 @@ func (q *DBQuerier) InsertAuthorSuffix(ctx context.Context, params InsertAuthorS if err != nil { return InsertAuthorSuffixRow{}, fmt.Errorf("query InsertAuthorSuffix: %w", err) } - fds := rows.FieldDescriptions() - plan0 := planScan(pgtype.Int4Codec{}, fds[0], (*int32)(nil)) + plan0 := planScan(pgtype.TextCodec{}, fds[0], (*int32)(nil)) plan1 := planScan(pgtype.TextCodec{}, fds[1], (*string)(nil)) plan2 := planScan(pgtype.TextCodec{}, fds[2], (*string)(nil)) - plan3 := planPtrScan(pgtype.TextCodec{}, fds[3], (*string)(nil)) + plan3 := planScan(pgtype.TextCodec{}, fds[3], (**string)(nil)) return pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) (InsertAuthorSuffixRow, error) { vals := row.RawValues() var item InsertAuthorSuffixRow - if err := plan0.Scan(vals[0], &item.AuthorID); err != nil { - return item, fmt.Errorf("scan InsertAuthorSuffixRow.author_id column: %w", err) + if err := plan0.Scan(vals[0], &item); err != nil { + return item, fmt.Errorf("scan InsertAuthorSuffix.author_id: %w", err) } - if err := plan1.Scan(vals[1], &item.FirstName); err != nil { - return item, fmt.Errorf("scan InsertAuthorSuffixRow.first_name column: %w", err) + if err := plan1.Scan(vals[1], &item); err != nil { + return item, fmt.Errorf("scan InsertAuthorSuffix.first_name: %w", err) } - if err := plan2.Scan(vals[2], &item.LastName); err != nil { - return item, fmt.Errorf("scan InsertAuthorSuffixRow.last_name column: %w", err) + if err := plan2.Scan(vals[2], &item); err != nil { + return item, fmt.Errorf("scan InsertAuthorSuffix.last_name: %w", err) } - if err := plan3.Scan(vals[3], &item.Suffix); err != nil { - return item, fmt.Errorf("scan InsertAuthorSuffixRow.suffix column: %w", err) + if err := plan3.Scan(vals[3], &item); err != nil { + return item, fmt.Errorf("scan InsertAuthorSuffix.suffix: %w", err) } return item, nil }) } -const stringAggFirstNameSQL = `SELECT string_agg(first_name, ',') AS NAMES FROM author WHERE author_id = $1;` +const stringAggFirstNameSQL = `SELECT string_agg(first_name, ',') AS names FROM author WHERE author_id = $1;` // StringAggFirstName implements Querier.StringAggFirstName. func (q *DBQuerier) StringAggFirstName(ctx context.Context, authorID int32) (*string, error) { @@ -341,21 +327,20 @@ func (q *DBQuerier) StringAggFirstName(ctx context.Context, authorID int32) (*st if err != nil { return nil, fmt.Errorf("query StringAggFirstName: %w", err) } - fds := rows.FieldDescriptions() - plan0 := planPtrScan(pgtype.TextCodec{}, fds[0], (*string)(nil)) + plan0 := planScan(pgtype.TextCodec{}, fds[0], (**string)(nil)) return pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) (*string, error) { vals := row.RawValues() var item *string if err := plan0.Scan(vals[0], &item); err != nil { - return item, fmt.Errorf("scan StringAggFirstName column: %w", err) + return item, fmt.Errorf("scan StringAggFirstName.names: %w", err) } return item, nil }) } -const arrayAggFirstNameSQL = `SELECT array_agg(first_name) AS NAMES FROM author WHERE author_id = $1;` +const arrayAggFirstNameSQL = `SELECT array_agg(first_name) AS names FROM author WHERE author_id = $1;` // ArrayAggFirstName implements Querier.ArrayAggFirstName. func (q *DBQuerier) ArrayAggFirstName(ctx context.Context, authorID int32) ([]string, error) { @@ -364,15 +349,14 @@ func (q *DBQuerier) ArrayAggFirstName(ctx context.Context, authorID int32) ([]st if err != nil { return nil, fmt.Errorf("query ArrayAggFirstName: %w", err) } - fds := rows.FieldDescriptions() - plan0 := planScan(pgtype.TextCodec{}, fds[0], (*string)(nil)) + plan0 := planScan(pgtype.TextCodec{}, fds[0], (*[]string)(nil)) - return pgx.CollectRows(rows, func(row pgx.CollectableRow) (string, error) { + return pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) ([]string, error) { vals := row.RawValues() - var item string + var item []string if err := plan0.Scan(vals[0], &item); err != nil { - return item, fmt.Errorf("scan StringAggFirstName column: %w", err) + return item, fmt.Errorf("scan ArrayAggFirstName.names: %w", err) } return item, nil }) diff --git a/internal/codegen/golang/query.gotemplate b/internal/codegen/golang/query.gotemplate index 852c1b8..d6c823f 100644 --- a/internal/codegen/golang/query.gotemplate +++ b/internal/codegen/golang/query.gotemplate @@ -27,7 +27,7 @@ type Querier interface { var _ Querier = &DBQuerier{} type DBQuerier struct { - conn genericConn // underlying Postgres transport to use + conn genericConn } // genericConn is a connection like *pgx.Conn, pgx.Tx, or *pgxpool.Pool. @@ -53,48 +53,30 @@ const {{ $q.SQLVarName }} = {{ $q.EmitPreparedSQL }} // {{ $q.Name }} implements Querier.{{ $q.Name }}. func (q *DBQuerier) {{ $q.Name }}(ctx context.Context {{- $q.EmitParams }}) ({{ $q.EmitResultType }}, error) { ctx = context.WithValue(ctx, "pggen_query_name", "{{ $q.Name }}") -{{- if eq $q.ResultKind ":one" }} - row, err := q.conn.Query(ctx, {{ $q.SQLVarName }} {{- $q.EmitParamNames }}) +{{- if eq $q.ResultKind ":exec" }} + cmdTag, err := q.conn.Exec(ctx, {{ $q.SQLVarName }} {{- $q.EmitParamNames }}) if err != nil { - return nil, fmt.Errorf("query {{ $q.Name }}: %w", err) + return {{ $q.EmitZeroResult }}, fmt.Errorf("exec query {{ $q.Name }}: %w", err) + } + return cmdTag, err +{{- else }} + rows, err := q.conn.Query(ctx, {{ $q.SQLVarName }} {{- $q.EmitParamNames }}) + if err != nil { + return {{ $q.EmitZeroResult }}, fmt.Errorf("query {{ $q.Name }}: %w", err) } - fds := rows.FieldDescriptions() - {{- range $i, $col := $q.Outputs -}} + {{- range $i, $col := $q.Outputs }} {{ $q.EmitPlanScan $i $col}} - {{- end -}} + {{- end }} - return pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) ({{ $q.EmitSingularResultType }}, error) { + return {{ $q.EmitCollectionFunc }}(rows, func(row pgx.CollectableRow) ({{ $q.EmitSingularResultType }}, error) { vals := row.RawValues() - {{ $q.EmitResultTypeInit "item" }} - {{- range $i, $col := $q.Outputs -}} - {{- $q.EmitScanColumn $i $col -}} - {{- end -}} - }) -{{- else if eq $q.ResultKind ":many" }} - row, err := q.conn.Query(ctx, {{ $q.SQLVarName }} {{- $q.EmitParamNames }}) - if err != nil { - return nil, fmt.Errorf("query {{ $q.Name }}: %w", err) - } - - fds := rows.FieldDescriptions() - {{- range $i, $col := $q.Outputs -}} - {{ $q.EmitPlanScan $i $col}} - {{- end -}} - - return pgx.CollectRows(rows, func(row pgx.CollectableRow) ({{ $q.EmitSingularResultType }}, error) { - vals := row.RawValues() - {{ $q.EmitResultTypeInit "item" }} - {{- range $i, $col := $q.Outputs -}} - {{- $q.EmitScanColumn $i $col -}} - {{- end -}} + var item {{ $q.EmitSingularResultType }} + {{- range $i, $col := $q.Outputs }} + {{ $q.EmitScanColumn $i $col }} + {{- end }} + return item, nil }) -{{- else if eq $q.ResultKind ":exec" }} - cmdTag, err := q.conn.Exec(ctx, {{ $q.SQLVarName }} {{- $q.EmitParamNames }}) - if err != nil { - return cmdTag, fmt.Errorf("exec query {{ $q.Name }}: %w", err) - } - return cmdTag, err {{- end }} } {{- end -}} diff --git a/internal/codegen/golang/templated_file.go b/internal/codegen/golang/templated_file.go index 715f773..6b59380 100644 --- a/internal/codegen/golang/templated_file.go +++ b/internal/codegen/golang/templated_file.go @@ -40,7 +40,8 @@ type TemplatedQuery struct { Doc string // doc from the source query file, formatted for Go PreparedSQL string // SQL query, ready to run with PREPARE statement Inputs []TemplatedParam // input parameters to the query - Outputs []TemplatedColumn // output columns of the query + Outputs []TemplatedColumn // non-void output columns of the query + ScanCols []TemplatedColumn // all columns of the query, including void columns InlineParamCount int // inclusive count of params that will be inlined } @@ -211,17 +212,17 @@ func (tq TemplatedQuery) EmitPlanScan(idx int, out TemplatedColumn) (string, err default: return "", fmt.Errorf("unhandled EmitPlanScanArgs type: %s", tq.ResultKind) } - return fmt.Sprintf("planScan%d = pgtype.TODOCodec{}, fs[%d], (*%s)(nil))", idx, idx, out.Type.BaseName()), nil + return fmt.Sprintf("plan%d := planScan(pgtype.TextCodec{}, fds[%d], (*%s)(nil))", idx, idx, out.Type.BaseName()), nil } // EmitScanColumn emits scan call for a single TemplatedColumn. func (tq TemplatedQuery) EmitScanColumn(idx int, out TemplatedColumn) (string, error) { sb := &strings.Builder{} - _, _ = fmt.Fprintf(sb, "if err := plan%d.Scan(vals[%d], &item); err != nil\n", idx, idx) + _, _ = fmt.Fprintf(sb, "if err := plan%d.Scan(vals[%d], &item); err != nil {\n", idx, idx) sb.WriteString("\t\t\t") _, _ = fmt.Fprintf(sb, `return item, fmt.Errorf("scan %s.%s: %%w", err)`, tq.Name, out.PgName) sb.WriteString("\n") - sb.WriteString("\t\t}\n") + sb.WriteString("\t\t}") return sb.String(), nil } @@ -237,7 +238,7 @@ func (tq TemplatedQuery) EmitRowScanArgs() (string, error) { return "", fmt.Errorf("unhandled EmitRowScanArgs type: %s", tq.ResultKind) } - hasOnlyOneNonVoid := len(removeVoidColumns(tq.Outputs)) == 1 + hasOnlyOneOutput := len(tq.Outputs) == 1 sb := strings.Builder{} sb.Grow(15 * len(tq.Outputs)) for i, out := range tq.Outputs { @@ -248,7 +249,7 @@ func (tq TemplatedQuery) EmitRowScanArgs() (string, error) { sb.WriteString(out.LowerName) sb.WriteString("Array") default: - if hasOnlyOneNonVoid { + if hasOnlyOneOutput { sb.WriteString("&item") } else { sb.WriteString("&item.") @@ -261,7 +262,7 @@ func (tq TemplatedQuery) EmitRowScanArgs() (string, error) { sb.WriteString("Row") case *gotype.EnumType, *gotype.OpaqueType: - if hasOnlyOneNonVoid { + if hasOnlyOneOutput { sb.WriteString("&item") } else { sb.WriteString("&item.") @@ -281,55 +282,45 @@ func (tq TemplatedQuery) EmitRowScanArgs() (string, error) { return sb.String(), nil } -// EmitSingularResultType returns the string representing a single element -// of the overall query result type, like FindAuthorsRow when the overall return -// type is []FindAuthorsRow. -func (tq TemplatedQuery) EmitSingularResultType() (string, error) { - outs := removeVoidColumns(tq.Outputs) +func (tq TemplatedQuery) EmitCollectionFunc() (string, error) { switch tq.ResultKind { case ast.ResultKindExec: - return "pgconn.CommandTag", nil + return "", fmt.Errorf("cannot EmitCollectionFunc for :exec query %s", tq.Name) case ast.ResultKindMany: - switch len(outs) { - case 0: - return "pgconn.CommandTag", nil - case 1: - return outs[0].QualType, nil - default: - return tq.Name + "Row", nil - } + return "pgx.CollectRows", nil case ast.ResultKindOne: - switch len(outs) { - case 0: - return "pgconn.CommandTag", nil - case 1: - return outs[0].QualType, nil - default: - return tq.Name + "Row", nil - } + return "pgx.CollectExactlyOneRow", nil default: - return "", fmt.Errorf("unhandled EmitSingularResultType kind: %s", tq.ResultKind) + return "", fmt.Errorf("unhandled EmitCollectionFunc type: %s", tq.ResultKind) + } +} + +// EmitSingularResultType returns the string representing a single element +// of the overall query result type, like FindAuthorsRow when the overall return +// type is []FindAuthorsRow. +func (tq TemplatedQuery) EmitSingularResultType() string { + if tq.ResultKind == ast.ResultKindExec { + return "pgconn.CommandTag" + } + if len(tq.Outputs) == 0 { + return "pgconn.CommandTag" + } + if len(tq.Outputs) == 1 { + return tq.Outputs[0].QualType } + return tq.Name + "Row" } // EmitResultType returns the string representing the overall query result type, // meaning the return result. func (tq TemplatedQuery) EmitResultType() (string, error) { - rt, err := tq.EmitSingularResultType() - if err != nil { - return "", fmt.Errorf("unhandled EmitResultType: %w", err) - } switch tq.ResultKind { case ast.ResultKindExec: - return rt, nil + return "pgconn.CommandTag", nil case ast.ResultKindMany: - outs := removeVoidColumns(tq.Outputs) - if len(outs) == 0 { - return rt, nil - } - return "[]" + rt, nil + return "[]" + tq.EmitSingularResultType(), nil case ast.ResultKindOne: - return rt, nil + return tq.EmitSingularResultType(), nil default: return "", fmt.Errorf("unhandled EmitResultType kind: %s", tq.ResultKind) } @@ -371,108 +362,37 @@ func (tq TemplatedQuery) EmitResultTypeInit(name string) (string, error) { } } -// EmitResultDecoders declares all initialization required for output types. -func (tq TemplatedQuery) EmitResultDecoders() (string, error) { - sb := &strings.Builder{} - const indent = "\n\t" // 1 level indent inside querier method - for _, out := range tq.Outputs { - switch typ := gotype.UnwrapNestedType(out.Type).(type) { - case *gotype.CompositeType: - sb.WriteString(indent) - sb.WriteString(out.LowerName) - sb.WriteString("Row := q.types.") - sb.WriteString(NameCompositeTranscoderFunc(typ)) - sb.WriteString("()") - case *gotype.ArrayType: - switch gotype.UnwrapNestedType(typ.Elem).(type) { - case *gotype.EnumType, *gotype.CompositeType: - // For all other array elems, a normal array works. - sb.WriteString(indent) - sb.WriteString(out.LowerName) - sb.WriteString("Array := q.types.") - sb.WriteString(NameArrayTranscoderFunc(typ)) - sb.WriteString("()") - } - default: - continue +// EmitZeroResult returns the string representing the zero value of a result. +func (tq TemplatedQuery) EmitZeroResult() (string, error) { + switch tq.ResultKind { + case ast.ResultKindExec: + return "pgconn.CommandTag{}", nil + case ast.ResultKindMany: + return "nil", nil // empty slice + case ast.ResultKindOne: + if len(tq.Outputs) > 1 { + return tq.Name + "Row{}", nil } - } - return sb.String(), nil -} - -// EmitResultAssigns writes all the assign statements after scanning the result -// from pgx. -// -// Copies pgtype.CompositeFields representing a Postgres composite type into the -// output struct. -// -// Copies pgtype.EnumArray fields into Go enum array types. -func (tq TemplatedQuery) EmitResultAssigns(zeroVal string) (string, error) { - sb := &strings.Builder{} - indent := "\n\t" - if tq.ResultKind == ast.ResultKindMany { - indent += "\t" // a :many query processes items in a for loop - } - for _, out := range tq.Outputs { - switch typ := gotype.UnwrapNestedType(out.Type).(type) { - case *gotype.CompositeType: - sb.WriteString(indent) - sb.WriteString("if err := ") - sb.WriteString(out.LowerName) - sb.WriteString("Row.AssignTo(&item") - if len(removeVoidColumns(tq.Outputs)) > 1 { - sb.WriteRune('.') - sb.WriteString(out.UpperName) - } - sb.WriteString("); err != nil {") - sb.WriteString(indent) - sb.WriteString("\treturn ") - sb.WriteString(zeroVal) - sb.WriteString(", fmt.Errorf(\"assign ") - sb.WriteString(tq.Name) - sb.WriteString(" row: %w\", err)") - sb.WriteString(indent) - sb.WriteString("}") - case *gotype.ArrayType: - switch gotype.UnwrapNestedType(typ.Elem).(type) { - case *gotype.CompositeType, *gotype.EnumType: - sb.WriteString(indent) - sb.WriteString("if err := ") - sb.WriteString(out.LowerName) - sb.WriteString("Array.AssignTo(&item") - if len(removeVoidColumns(tq.Outputs)) > 1 { - sb.WriteRune('.') - sb.WriteString(out.UpperName) - } - sb.WriteString("); err != nil {") - sb.WriteString(indent) - sb.WriteString("\treturn ") - sb.WriteString(zeroVal) - sb.WriteString(", fmt.Errorf(\"assign ") - sb.WriteString(tq.Name) - sb.WriteString(" row: %w\", err)") - sb.WriteString(indent) - sb.WriteString("}") - } + typ := tq.Outputs[0].Type.BaseName() + switch { + case strings.HasPrefix(typ, "[]"): + return "nil", nil // empty slice + case strings.HasPrefix(typ, "*"): + return "nil", nil // nil pointer } + switch typ { + case "int", "int32", "int64", "float32", "float64": + return "0", nil + case "string": + return `""`, nil + case "bool": + return "false", nil + default: + return typ + "{}", nil // won't work for type Foo int + } + default: + return "", fmt.Errorf("unhandled EmitZeroResult kind: %s", tq.ResultKind) } - return sb.String(), nil -} - -// EmitResultElem returns the string representing a single item in the overall -// query result type. For :one and :exec queries, this is the same as -// EmitResultType. For :many queries, this is the element type of the slice -// result type. -func (tq TemplatedQuery) EmitResultElem() (string, error) { - result, err := tq.EmitResultType() - if err != nil { - return "", fmt.Errorf("unhandled EmitResultElem type: %w", err) - } - // Unwrap arrays because we build the array with append. - arr := strings.TrimPrefix(result, "[]") - // Unwrap pointers because we add "&" to return the correct types. - ptr := strings.TrimPrefix(arr, "*") - return ptr, nil } // EmitResultExpr returns the string representation of a single item to return @@ -531,16 +451,15 @@ func (tq TemplatedQuery) EmitRowStruct() string { case ast.ResultKindExec: return "" case ast.ResultKindOne, ast.ResultKindMany: - outs := removeVoidColumns(tq.Outputs) - if len(outs) <= 1 { + if len(tq.Outputs) == 1 { return "" // if there's only 1 output column, return it directly } sb := &strings.Builder{} sb.WriteString("\n\ntype ") sb.WriteString(tq.Name) sb.WriteString("Row struct {\n") - maxNameLen, maxTypeLen := getLongestOutput(outs) - for _, out := range outs { + maxNameLen, maxTypeLen := getLongestOutput(tq.Outputs) + for _, out := range tq.Outputs { // Name sb.WriteString("\t") sb.WriteString(out.UpperName) @@ -560,17 +479,3 @@ func (tq TemplatedQuery) EmitRowStruct() string { panic("unhandled result type: " + tq.ResultKind) } } - -// removeVoidColumns makes a copy of cols with all VoidType columns removed. -// Useful because return types shouldn't contain the void type, but we need -// to use a nil placeholder for void types when scanning a pgx.Row. -func removeVoidColumns(cols []TemplatedColumn) []TemplatedColumn { - outs := make([]TemplatedColumn, 0, len(cols)) - for _, col := range cols { - if _, ok := col.Type.(*gotype.VoidType); ok { - continue - } - outs = append(outs, col) - } - return outs -} diff --git a/internal/codegen/golang/templater.go b/internal/codegen/golang/templater.go index de73461..4d56e40 100644 --- a/internal/codegen/golang/templater.go +++ b/internal/codegen/golang/templater.go @@ -2,6 +2,7 @@ package golang import ( "fmt" + "github.com/jschaf/pggen/internal/ast" "github.com/jschaf/pggen/internal/casing" "github.com/jschaf/pggen/internal/codegen" "github.com/jschaf/pggen/internal/codegen/golang/gotype" @@ -183,14 +184,20 @@ func (tm Templater) templateFile(file codegen.QueryFile, isLeader bool) (Templat declarers.AddAll(ds...) } + nonVoidCols := removeVoidColumns(outputs) + resultKind := query.ResultKind + if len(nonVoidCols) == 0 { + resultKind = ast.ResultKindExec + } queries = append(queries, TemplatedQuery{ Name: tm.caser.ToUpperGoIdent(query.Name), SQLVarName: tm.caser.ToLowerGoIdent(query.Name) + "SQL", - ResultKind: query.ResultKind, + ResultKind: resultKind, Doc: docs.String(), PreparedSQL: query.PreparedSQL, Inputs: inputs, - Outputs: outputs, + Outputs: nonVoidCols, + ScanCols: outputs, InlineParamCount: tm.inlineParamCount, }) } @@ -232,3 +239,17 @@ func (tm Templater) chooseLowerName(pgName string, fallback string, idx int, num } return fallback + suffix } + +// removeVoidColumns makes a copy of cols with all VoidType columns removed. +// Useful because return types shouldn't contain the void type, but we need +// to use a nil placeholder for void types when scanning a pgx.Row. +func removeVoidColumns(cols []TemplatedColumn) []TemplatedColumn { + outs := make([]TemplatedColumn, 0, len(cols)) + for _, col := range cols { + if _, ok := col.Type.(*gotype.VoidType); ok { + continue + } + outs = append(outs, col) + } + return outs +} diff --git a/internal/pg/type_cache.go b/internal/pg/type_cache.go index 53101a3..dc18549 100644 --- a/internal/pg/type_cache.go +++ b/internal/pg/type_cache.go @@ -1,6 +1,7 @@ package pg import ( + "github.com/jackc/pgx/v5/pgtype" "sync" ) @@ -21,6 +22,16 @@ func newTypeCache() *typeCache { } } +func lookup() { + !!!fixme + m := pgtype.NewMap() // !!! + m.TypeForValue() // looks up pg type for go type + m.FormatCodeForOID() + m.TypeForOID() + m.TypeForName() + +} + // getOIDs returns the cached OIDS (with the type) and uncached OIDs. func (tc *typeCache) getOIDs(oids ...uint32) (map[uint32]Type, map[uint32]struct{}) { cachedTypes := make(map[uint32]Type, len(oids)) @@ -28,10 +39,10 @@ func (tc *typeCache) getOIDs(oids ...uint32) (map[uint32]Type, map[uint32]struct tc.mu.Lock() defer tc.mu.Unlock() for _, oid := range oids { - if t, ok := tc.types[uint32(oid)]; ok { - cachedTypes[uint32(oid)] = t + if t, ok := tc.types[oid]; ok { + cachedTypes[oid] = t } else { - uncachedTypes[uint32(oid)] = struct{}{} + uncachedTypes[oid] = struct{}{} } } return cachedTypes, uncachedTypes @@ -39,7 +50,7 @@ func (tc *typeCache) getOIDs(oids ...uint32) (map[uint32]Type, map[uint32]struct func (tc *typeCache) getOID(oid uint32) (Type, bool) { tc.mu.Lock() - typ, ok := tc.types[uint32(oid)] + typ, ok := tc.types[oid] tc.mu.Unlock() return typ, ok }