Repository: sqlc-dev/sqlc
Pull request #2863 (2023-10-16 18:00:08)

docs: Update examples to use pgx/v5
I'm not sure that we should update all of these examples to use `pgx/v5`, but let me know what you think. I walked back a change to the example where we explicitly `Prepare` queries since I didn't want to deal with a custom enum issue.


Patch:
diff --git a/examples/authors/postgresql/db.go b/examples/authors/postgresql/db.go
index f5d9de730..00b0ad854 100644
--- a/examples/authors/postgresql/db.go
+++ b/examples/authors/postgresql/db.go
@@ -6,14 +6,15 @@ package authors
 
 import (
 	"context"
-	"database/sql"
+
+	"github.com/jackc/pgx/v5"
+	"github.com/jackc/pgx/v5/pgconn"
 )
 
 type DBTX interface {
-	ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
-	PrepareContext(context.Context, string) (*sql.Stmt, error)
-	QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
-	QueryRowContext(context.Context, string, ...interface{}) *sql.Row
+	Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
+	Query(context.Context, string, ...interface{}) (pgx.Rows, error)
+	QueryRow(context.Context, string, ...interface{}) pgx.Row
 }
 
 func New(db DBTX) *Queries {
@@ -24,7 +25,7 @@ type Queries struct {
 	db DBTX
 }
 
-func (q *Queries) WithTx(tx *sql.Tx) *Queries {
+func (q *Queries) WithTx(tx pgx.Tx) *Queries {
 	return &Queries{
 		db: tx,
 	}
diff --git a/examples/authors/postgresql/models.go b/examples/authors/postgresql/models.go
index ec61702f5..6961baa5f 100644
--- a/examples/authors/postgresql/models.go
+++ b/examples/authors/postgresql/models.go
@@ -5,11 +5,11 @@
 package authors
 
 import (
-	"database/sql"
+	"github.com/jackc/pgx/v5/pgtype"
 )
 
 type Author struct {
 	ID   int64
 	Name string
-	Bio  sql.NullString
+	Bio  pgtype.Text
 }
diff --git a/examples/authors/postgresql/query.sql.go b/examples/authors/postgresql/query.sql.go
index a3864f0d9..a8c3d5996 100644
--- a/examples/authors/postgresql/query.sql.go
+++ b/examples/authors/postgresql/query.sql.go
@@ -7,7 +7,8 @@ package authors
 
 import (
 	"context"
-	"database/sql"
+
+	"github.com/jackc/pgx/v5/pgtype"
 )
 
 const createAuthor = `-- name: CreateAuthor :one
@@ -21,11 +22,11 @@ RETURNING id, name, bio
 
 type CreateAuthorParams struct {
 	Name string
-	Bio  sql.NullString
+	Bio  pgtype.Text
 }
 
 func (q *Queries) CreateAuthor(ctx context.Context, arg CreateAuthorParams) (Author, error) {
-	row := q.db.QueryRowContext(ctx, createAuthor, arg.Name, arg.Bio)
+	row := q.db.QueryRow(ctx, createAuthor, arg.Name, arg.Bio)
 	var i Author
 	err := row.Scan(&i.ID, &i.Name, &i.Bio)
 	return i, err
@@ -37,7 +38,7 @@ WHERE id = $1
 `
 
 func (q *Queries) DeleteAuthor(ctx context.Context, id int64) error {
-	_, err := q.db.ExecContext(ctx, deleteAuthor, id)
+	_, err := q.db.Exec(ctx, deleteAuthor, id)
 	return err
 }
 
@@ -47,7 +48,7 @@ WHERE id = $1 LIMIT 1
 `
 
 func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) {
-	row := q.db.QueryRowContext(ctx, getAuthor, id)
+	row := q.db.QueryRow(ctx, getAuthor, id)
 	var i Author
 	err := row.Scan(&i.ID, &i.Name, &i.Bio)
 	return i, err
@@ -59,7 +60,7 @@ ORDER BY name
 `
 
 func (q *Queries) ListAuthors(ctx context.Context) ([]Author, error) {
-	rows, err := q.db.QueryContext(ctx, listAuthors)
+	rows, err := q.db.Query(ctx, listAuthors)
 	if err != nil {
 		return nil, err
 	}
@@ -72,9 +73,6 @@ func (q *Queries) ListAuthors(ctx context.Context) ([]Author, error) {
 		}
 		items = append(items, i)
 	}
-	if err := rows.Close(); err != nil {
-		return nil, err
-	}
 	if err := rows.Err(); err != nil {
 		return nil, err
 	}
diff --git a/examples/authors/sqlc.yaml b/examples/authors/sqlc.yaml
index 9ba523c0d..cc3694965 100644
--- a/examples/authors/sqlc.yaml
+++ b/examples/authors/sqlc.yaml
@@ -15,6 +15,7 @@ sql:
   gen:
     go:
       package: authors
+      sql_package: pgx/v5
       out: postgresql
 - schema: mysql/schema.sql
   queries: mysql/query.sql
@@ -45,4 +46,4 @@ rules:
   rule: "postgresql.explain.plan.total_cost > 300.0"
 - name: mysql-query-too-costly
   message: "Too costly"
-  rule: "has(mysql.explain.query_block.cost_info) && double(mysql.explain.query_block.cost_info.query_cost) > 2.0"
\ No newline at end of file
+  rule: "has(mysql.explain.query_block.cost_info) && double(mysql.explain.query_block.cost_info.query_cost) > 2.0"
diff --git a/examples/batch/postgresql/batch.go b/examples/batch/postgresql/batch.go
index aa905c88c..ca42f2a91 100644
--- a/examples/batch/postgresql/batch.go
+++ b/examples/batch/postgresql/batch.go
@@ -8,10 +8,9 @@ package batch
 import (
 	"context"
 	"errors"
-	"time"
 
-	"github.com/jackc/pgtype"
-	"github.com/jackc/pgx/v4"
+	"github.com/jackc/pgx/v5"
+	"github.com/jackc/pgx/v5/pgtype"
 )
 
 var (
@@ -114,13 +113,13 @@ type CreateBookBatchResults struct {
 }
 
 type CreateBookParams struct {
-	AuthorID  int32     `json:"author_id"`
-	Isbn      string    `json:"isbn"`
-	BookType  BookType  `json:"book_type"`
-	Title     string    `json:"title"`
-	Year      int32     `json:"year"`
-	Available time.Time `json:"available"`
-	Tags      []string  `json:"tags"`
+	AuthorID  int32              `json:"author_id"`
+	Isbn      string             `json:"isbn"`
+	BookType  BookType           `json:"book_type"`
+	Title     string             `json:"title"`
+	Year      int32              `json:"year"`
+	Available pgtype.Timestamptz `json:"available"`
+	Tags      []string           `json:"tags"`
 }
 
 func (q *Queries) CreateBook(ctx context.Context, arg []CreateBookParams) *CreateBookBatchResults {
@@ -328,10 +327,10 @@ func (q *Queries) GetBiography(ctx context.Context, authorID []int32) *GetBiogra
 	return &GetBiographyBatchResults{br, len(authorID), false}
 }
 
-func (b *GetBiographyBatchResults) QueryRow(f func(int, pgtype.JSONB, error)) {
+func (b *GetBiographyBatchResults) QueryRow(f func(int, []byte, error)) {
 	defer b.br.Close()
 	for t := 0; t < b.tot; t++ {
-		var biography pgtype.JSONB
+		var biography []byte
 		if b.closed {
 			if f != nil {
 				f(t, biography, ErrBatchAlreadyClosed)
diff --git a/examples/batch/postgresql/db.go b/examples/batch/postgresql/db.go
index c9a9445f7..330c3e0f6 100644
--- a/examples/batch/postgresql/db.go
+++ b/examples/batch/postgresql/db.go
@@ -7,8 +7,8 @@ package batch
 import (
 	"context"
 
-	"github.com/jackc/pgconn"
-	"github.com/jackc/pgx/v4"
+	"github.com/jackc/pgx/v5"
+	"github.com/jackc/pgx/v5/pgconn"
 )
 
 type DBTX interface {
diff --git a/examples/batch/postgresql/models.go b/examples/batch/postgresql/models.go
index fdb04683f..124769fad 100644
--- a/examples/batch/postgresql/models.go
+++ b/examples/batch/postgresql/models.go
@@ -7,9 +7,8 @@ package batch
 import (
 	"database/sql/driver"
 	"fmt"
-	"time"
 
-	"github.com/jackc/pgtype"
+	"github.com/jackc/pgx/v5/pgtype"
 )
 
 type BookType string
@@ -55,18 +54,18 @@ func (ns NullBookType) Value() (driver.Value, error) {
 }
 
 type Author struct {
-	AuthorID  int32        `json:"author_id"`
-	Name      string       `json:"name"`
-	Biography pgtype.JSONB `json:"biography"`
+	AuthorID  int32  `json:"author_id"`
+	Name      string `json:"name"`
+	Biography []byte `json:"biography"`
 }
 
 type Book struct {
-	BookID    int32     `json:"book_id"`
-	AuthorID  int32     `json:"author_id"`
-	Isbn      string    `json:"isbn"`
-	BookType  BookType  `json:"book_type"`
-	Title     string    `json:"title"`
-	Year      int32     `json:"year"`
-	Available time.Time `json:"available"`
-	Tags      []string  `json:"tags"`
+	BookID    int32              `json:"book_id"`
+	AuthorID  int32              `json:"author_id"`
+	Isbn      string             `json:"isbn"`
+	BookType  BookType           `json:"book_type"`
+	Title     string             `json:"title"`
+	Year      int32              `json:"year"`
+	Available pgtype.Timestamptz `json:"available"`
+	Tags      []string           `json:"tags"`
 }
diff --git a/examples/batch/postgresql/querier.go b/examples/batch/postgresql/querier.go
index b103b84ee..cca7f5871 100644
--- a/examples/batch/postgresql/querier.go
+++ b/examples/batch/postgresql/querier.go
@@ -7,7 +7,7 @@ package batch
 import (
 	"context"
 
-	"github.com/jackc/pgconn"
+	"github.com/jackc/pgx/v5/pgconn"
 )
 
 type Querier interface {
diff --git a/examples/batch/postgresql/query.sql.go b/examples/batch/postgresql/query.sql.go
index f6a6a42b5..08b6ae4bf 100644
--- a/examples/batch/postgresql/query.sql.go
+++ b/examples/batch/postgresql/query.sql.go
@@ -8,7 +8,7 @@ package batch
 import (
 	"context"
 
-	"github.com/jackc/pgconn"
+	"github.com/jackc/pgx/v5/pgconn"
 )
 
 const createAuthor = `-- name: CreateAuthor :one
diff --git a/examples/batch/sqlc.json b/examples/batch/sqlc.json
index 32124f395..5b81b4078 100644
--- a/examples/batch/sqlc.json
+++ b/examples/batch/sqlc.json
@@ -19,7 +19,7 @@
       "rules": [
         "sqlc/db-prepare"
       ],
-      "sql_package": "pgx/v4",
+      "sql_package": "pgx/v5",
       "emit_json_tags": true,
       "emit_prepared_queries": true,
       "emit_interface": true
diff --git a/examples/jets/postgresql/db.go b/examples/jets/postgresql/db.go
index bee0af264..96af127ff 100644
--- a/examples/jets/postgresql/db.go
+++ b/examples/jets/postgresql/db.go
@@ -6,14 +6,15 @@ package jets
 
 import (
 	"context"
-	"database/sql"
+
+	"github.com/jackc/pgx/v5"
+	"github.com/jackc/pgx/v5/pgconn"
 )
 
 type DBTX interface {
-	ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
-	PrepareContext(context.Context, string) (*sql.Stmt, error)
-	QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
-	QueryRowContext(context.Context, string, ...interface{}) *sql.Row
+	Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
+	Query(context.Context, string, ...interface{}) (pgx.Rows, error)
+	QueryRow(context.Context, string, ...interface{}) pgx.Row
 }
 
 func New(db DBTX) *Queries {
@@ -24,7 +25,7 @@ type Queries struct {
 	db DBTX
 }
 
-func (q *Queries) WithTx(tx *sql.Tx) *Queries {
+func (q *Queries) WithTx(tx pgx.Tx) *Queries {
 	return &Queries{
 		db: tx,
 	}
diff --git a/examples/jets/postgresql/query-building.sql.go b/examples/jets/postgresql/query-building.sql.go
index 64db2dfdf..15e4900d5 100644
--- a/examples/jets/postgresql/query-building.sql.go
+++ b/examples/jets/postgresql/query-building.sql.go
@@ -14,7 +14,7 @@ SELECT COUNT(*) FROM pilots
 `
 
 func (q *Queries) CountPilots(ctx context.Context) (int64, error) {
-	row := q.db.QueryRowContext(ctx, countPilots)
+	row := q.db.QueryRow(ctx, countPilots)
 	var count int64
 	err := row.Scan(&count)
 	return count, err
@@ -25,7 +25,7 @@ DELETE FROM pilots WHERE id = $1
 `
 
 func (q *Queries) DeletePilot(ctx context.Context, id int32) error {
-	_, err := q.db.ExecContext(ctx, deletePilot, id)
+	_, err := q.db.Exec(ctx, deletePilot, id)
 	return err
 }
 
@@ -34,7 +34,7 @@ SELECT id, name FROM pilots LIMIT 5
 `
 
 func (q *Queries) ListPilots(ctx context.Context) ([]Pilot, error) {
-	rows, err := q.db.QueryContext(ctx, listPilots)
+	rows, err := q.db.Query(ctx, listPilots)
 	if err != nil {
 		return nil, err
 	}
@@ -47,9 +47,6 @@ func (q *Queries) ListPilots(ctx context.Context) ([]Pilot, error) {
 		}
 		items = append(items, i)
 	}
-	if err := rows.Close(); err != nil {
-		return nil, err
-	}
 	if err := rows.Err(); err != nil {
 		return nil, err
 	}
diff --git a/examples/jets/sqlc.json b/examples/jets/sqlc.json
index 14e80c429..d66496675 100644
--- a/examples/jets/sqlc.json
+++ b/examples/jets/sqlc.json
@@ -10,6 +10,7 @@
       "schema": "postgresql/schema.sql",
       "queries": "postgresql/query-building.sql",
       "engine": "postgresql",
+      "sql_package": "pgx/v5",
       "database": {
         "managed": true
       },
diff --git a/examples/ondeck/sqlc.json b/examples/ondeck/sqlc.json
index 0eed50f9d..2e94b30d9 100644
--- a/examples/ondeck/sqlc.json
+++ b/examples/ondeck/sqlc.json
@@ -10,6 +10,7 @@
       "schema": "postgresql/schema",
       "queries": "postgresql/query",
       "engine": "postgresql",
+      "sql_package": "database/sql",
       "database": {
         "managed": true
       },
@@ -56,4 +57,4 @@
       "emit_interface": true
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/internal/codegen/golang/gen.go b/internal/codegen/golang/gen.go
index c5f1956e0..ea3d8384c 100644
--- a/internal/codegen/golang/gen.go
+++ b/internal/codegen/golang/gen.go
@@ -345,6 +345,9 @@ func filterUnusedStructs(enums []Enum, structs []Struct, queries []Query) ([]Enu
 			if query.Ret.IsStruct() {
 				for _, field := range query.Ret.Struct.Fields {
 					keepTypes[field.Type] = struct{}{}
+					for _, embedField := range field.EmbedFields {
+						keepTypes[embedField.Type] = struct{}{}
+					}
 				}
 			}
 		}
diff --git a/internal/compiler/output_columns.go b/internal/compiler/output_columns.go
index 6ced89ae2..0ee3b3e18 100644
--- a/internal/compiler/output_columns.go
+++ b/internal/compiler/output_columns.go
@@ -550,12 +550,15 @@ func (c *Compiler) sourceTables(qc *QueryCatalog, node ast.Node) ([]*Table, erro
 			if err != nil {
 				continue
 			}
-			table, err := qc.GetTable(&ast.TableName{
-				Catalog: fn.ReturnType.Catalog,
-				Schema:  fn.ReturnType.Schema,
-				Name:    fn.ReturnType.Name,
-			})
-			if err != nil {
+			var table *Table
+			if fn.ReturnType != nil {
+				table, err = qc.GetTable(&ast.TableName{
+					Catalog: fn.ReturnType.Catalog,
+					Schema:  fn.ReturnType.Schema,
+					Name:    fn.ReturnType.Name,
+				})
+			}
+			if table == nil || err != nil {
 				if n.Alias != nil && len(n.Alias.Colnames.Items) > 0 {
 					table = &Table{}
 					for _, colName := range n.Alias.Colnames.Items {
@@ -575,12 +578,22 @@ func (c *Compiler) sourceTables(qc *QueryCatalog, node ast.Node) ([]*Table, erro
 							Schema:  fn.Rel.Schema,
 							Name:    fn.Rel.Name,
 						},
-						Columns: []*Column{
+					}
+					if len(fn.Outs) > 0 {
+						for _, arg := range fn.Outs {
+							table.Columns = append(table.Columns, &Column{
+								Name:     arg.Name,
+								DataType: arg.Type.Name,
+							})
+						}
+					}
+					if fn.ReturnType != nil {
+						table.Columns = []*Column{
 							{
 								Name:     colName,
 								DataType: fn.ReturnType.Name,
 							},
-						},
+						}
 					}
 				}
 			}
diff --git a/internal/compiler/query.go b/internal/compiler/query.go
index df580c197..b3cf9d615 100644
--- a/internal/compiler/query.go
+++ b/internal/compiler/query.go
@@ -3,11 +3,13 @@ package compiler
 import (
 	"github.com/sqlc-dev/sqlc/internal/metadata"
 	"github.com/sqlc-dev/sqlc/internal/sql/ast"
+	"github.com/sqlc-dev/sqlc/internal/sql/catalog"
 )
 
 type Function struct {
 	Rel        *ast.FuncName
 	ReturnType *ast.TypeName
+	Outs       []*catalog.Argument
 }
 
 type Table struct {
diff --git a/internal/compiler/query_catalog.go b/internal/compiler/query_catalog.go
index 42088446d..80b59d876 100644
--- a/internal/compiler/query_catalog.go
+++ b/internal/compiler/query_catalog.go
@@ -103,6 +103,7 @@ func (qc QueryCatalog) GetFunc(rel *ast.FuncName) (*Function, error) {
 	}
 	return &Function{
 		Rel:        rel,
+		Outs:       funcs[0].OutArgs(),
 		ReturnType: funcs[0].ReturnType,
 	}, nil
 }
diff --git a/internal/sql/catalog/func.go b/internal/sql/catalog/func.go
index 7cc712492..e17077731 100644
--- a/internal/sql/catalog/func.go
+++ b/internal/sql/catalog/func.go
@@ -39,6 +39,17 @@ func (f *Function) InArgs() []*Argument {
 	return args
 }
 
+func (f *Function) OutArgs() []*Argument {
+	var args []*Argument
+	for _, a := range f.Args {
+		switch a.Mode {
+		case ast.FuncParamOut:
+			args = append(args, a)
+		}
+	}
+	return args
+}
+
 func (c *Catalog) createFunction(stmt *ast.CreateFunctionStmt) error {
 	ns := stmt.Func.Schema
 	if ns == "" {


Test patch:
diff --git a/examples/authors/postgresql/db_test.go b/examples/authors/postgresql/db_test.go
index d2df436ae..bdcd90a54 100644
--- a/examples/authors/postgresql/db_test.go
+++ b/examples/authors/postgresql/db_test.go
@@ -5,23 +5,23 @@ package authors
 
 import (
 	"context"
-	"database/sql"
 	"testing"
 
-	_ "github.com/lib/pq"
+	"github.com/jackc/pgx/v5"
+	"github.com/jackc/pgx/v5/pgtype"
 
 	"github.com/sqlc-dev/sqlc/internal/sqltest/hosted"
 )
 
 func TestAuthors(t *testing.T) {
+	ctx := context.Background()
 	uri := hosted.PostgreSQL(t, []string{"schema.sql"})
-	db, err := sql.Open("postgres", uri)
+	db, err := pgx.Connect(ctx, uri)
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer db.Close()
+	defer db.Close(ctx)
 
-	ctx := context.Background()
 	q := New(db)
 
 	// list all authors
@@ -34,7 +34,7 @@ func TestAuthors(t *testing.T) {
 	// create an author
 	insertedAuthor, err := q.CreateAuthor(ctx, CreateAuthorParams{
 		Name: "Brian Kernighan",
-		Bio:  sql.NullString{String: "Co-author of The C Programming Language and The Go Programming Language", Valid: true},
+		Bio:  pgtype.Text{String: "Co-author of The C Programming Language and The Go Programming Language", Valid: true},
 	})
 	if err != nil {
 		t.Fatal(err)
diff --git a/examples/batch/postgresql/db_test.go b/examples/batch/postgresql/db_test.go
index fd3ce4e72..c39bd0b5e 100644
--- a/examples/batch/postgresql/db_test.go
+++ b/examples/batch/postgresql/db_test.go
@@ -8,7 +8,8 @@ import (
 	"testing"
 	"time"
 
-	"github.com/jackc/pgx/v4"
+	"github.com/jackc/pgx/v5"
+	"github.com/jackc/pgx/v5/pgtype"
 	"github.com/sqlc-dev/sqlc/internal/sqltest/hosted"
 )
 
@@ -31,7 +32,7 @@ func TestBatchBooks(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	now := time.Now()
+	now := pgtype.Timestamptz{Time: time.Now(), Valid: true}
 
 	// batch insert new books
 	newBooksParams := []CreateBookParams{
@@ -114,7 +115,7 @@ func TestBatchBooks(t *testing.T) {
 	})
 
 	for _, book := range books0 {
-		t.Logf("Book %d (%s): %s available: %s\n", book.BookID, book.BookType, book.Title, book.Available.Format(time.RFC822Z))
+		t.Logf("Book %d (%s): %s available: %s\n", book.BookID, book.BookType, book.Title, book.Available.Time.Format(time.RFC822Z))
 		author, err := dq.GetAuthor(ctx, book.AuthorID)
 		if err != nil {
 			t.Fatal(err)
diff --git a/examples/booktest/postgresql/db.go b/examples/booktest/postgresql/db.go
index 301436443..34e435ebe 100644
--- a/examples/booktest/postgresql/db.go
+++ b/examples/booktest/postgresql/db.go
@@ -6,14 +6,15 @@ package booktest
 
 import (
 	"context"
-	"database/sql"
+
+	"github.com/jackc/pgx/v5"
+	"github.com/jackc/pgx/v5/pgconn"
 )
 
 type DBTX interface {
-	ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
-	PrepareContext(context.Context, string) (*sql.Stmt, error)
-	QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
-	QueryRowContext(context.Context, string, ...interface{}) *sql.Row
+	Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
+	Query(context.Context, string, ...interface{}) (pgx.Rows, error)
+	QueryRow(context.Context, string, ...interface{}) pgx.Row
 }
 
 func New(db DBTX) *Queries {
@@ -24,7 +25,7 @@ type Queries struct {
 	db DBTX
 }
 
-func (q *Queries) WithTx(tx *sql.Tx) *Queries {
+func (q *Queries) WithTx(tx pgx.Tx) *Queries {
 	return &Queries{
 		db: tx,
 	}
diff --git a/examples/booktest/postgresql/db_test.go b/examples/booktest/postgresql/db_test.go
index 9716f5684..8eeb10518 100644
--- a/examples/booktest/postgresql/db_test.go
+++ b/examples/booktest/postgresql/db_test.go
@@ -5,24 +5,24 @@ package booktest
 
 import (
 	"context"
-	"database/sql"
 	"testing"
 	"time"
 
-	_ "github.com/lib/pq"
+	"github.com/jackc/pgx/v5"
+	"github.com/jackc/pgx/v5/pgtype"
 
 	"github.com/sqlc-dev/sqlc/internal/sqltest/hosted"
 )
 
 func TestBooks(t *testing.T) {
+	ctx := context.Background()
 	uri := hosted.PostgreSQL(t, []string{"schema.sql"})
-	db, err := sql.Open("postgres", uri)
+	db, err := pgx.Connect(ctx, uri)
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer db.Close()
+	defer db.Close(ctx)
 
-	ctx := context.Background()
 	dq := New(db)
 
 	// create an author
@@ -32,7 +32,7 @@ func TestBooks(t *testing.T) {
 	}
 
 	// create transaction
-	tx, err := db.Begin()
+	tx, err := db.Begin(ctx)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -40,7 +40,7 @@ func TestBooks(t *testing.T) {
 	tq := dq.WithTx(tx)
 
 	// save first book
-	now := time.Now()
+	now := pgtype.Timestamptz{Time: time.Now(), Valid: true}
 	_, err = tq.CreateBook(ctx, CreateBookParams{
 		AuthorID:  a.AuthorID,
 		Isbn:      "1",
@@ -107,7 +107,7 @@ func TestBooks(t *testing.T) {
 	}
 
 	// tx commit
-	err = tx.Commit()
+	err = tx.Commit(ctx)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -132,7 +132,7 @@ func TestBooks(t *testing.T) {
 		t.Fatal(err)
 	}
 	for _, book := range books0 {
-		t.Logf("Book %d (%s): %s available: %s\n", book.BookID, book.BookType, book.Title, book.Available.Format(time.RFC822Z))
+		t.Logf("Book %d (%s): %s available: %s\n", book.BookID, book.BookType, book.Title, book.Available.Time.Format(time.RFC822Z))
 		author, err := dq.GetAuthor(ctx, book.AuthorID)
 		if err != nil {
 			t.Fatal(err)
diff --git a/examples/booktest/postgresql/models.go b/examples/booktest/postgresql/models.go
index 3bfe237f7..d5b27acee 100644
--- a/examples/booktest/postgresql/models.go
+++ b/examples/booktest/postgresql/models.go
@@ -7,7 +7,8 @@ package booktest
 import (
 	"database/sql/driver"
 	"fmt"
-	"time"
+
+	"github.com/jackc/pgx/v5/pgtype"
 )
 
 type BookType string
@@ -64,6 +65,6 @@ type Book struct {
 	BookType  BookType
 	Title     string
 	Year      int32
-	Available time.Time
+	Available pgtype.Timestamptz
 	Tags      []string
 }
diff --git a/examples/booktest/postgresql/query.sql.go b/examples/booktest/postgresql/query.sql.go
index 34e55ad96..9cc621bee 100644
--- a/examples/booktest/postgresql/query.sql.go
+++ b/examples/booktest/postgresql/query.sql.go
@@ -7,10 +7,8 @@ package booktest
 
 import (
 	"context"
-	"database/sql"
-	"time"
 
-	"github.com/lib/pq"
+	"github.com/jackc/pgx/v5/pgtype"
 )
 
 const booksByTags = `-- name: BooksByTags :many
@@ -28,13 +26,13 @@ WHERE tags && $1::varchar[]
 type BooksByTagsRow struct {
 	BookID int32
 	Title  string
-	Name   sql.NullString
+	Name   pgtype.Text
 	Isbn   string
 	Tags   []string
 }
 
 func (q *Queries) BooksByTags(ctx context.Context, dollar_1 []string) ([]BooksByTagsRow, error) {
-	rows, err := q.db.QueryContext(ctx, booksByTags, pq.Array(dollar_1))
+	rows, err := q.db.Query(ctx, booksByTags, dollar_1)
 	if err != nil {
 		return nil, err
 	}
@@ -47,15 +45,12 @@ func (q *Queries) BooksByTags(ctx context.Context, dollar_1 []string) ([]BooksBy
 			&i.Title,
 			&i.Name,
 			&i.Isbn,
-			pq.Array(&i.Tags),
+			&i.Tags,
 		); err != nil {
 			return nil, err
 		}
 		items = append(items, i)
 	}
-	if err := rows.Close(); err != nil {
-		return nil, err
-	}
 	if err := rows.Err(); err != nil {
 		return nil, err
 	}
@@ -73,7 +68,7 @@ type BooksByTitleYearParams struct {
 }
 
 func (q *Queries) BooksByTitleYear(ctx context.Context, arg BooksByTitleYearParams) ([]Book, error) {
-	rows, err := q.db.QueryContext(ctx, booksByTitleYear, arg.Title, arg.Year)
+	rows, err := q.db.Query(ctx, booksByTitleYear, arg.Title, arg.Year)
 	if err != nil {
 		return nil, err
 	}
@@ -89,15 +84,12 @@ func (q *Queries) BooksByTitleYear(ctx context.Context, arg BooksByTitleYearPara
 			&i.Title,
 			&i.Year,
 			&i.Available,
-			pq.Array(&i.Tags),
+			&i.Tags,
 		); err != nil {
 			return nil, err
 		}
 		items = append(items, i)
 	}
-	if err := rows.Close(); err != nil {
-		return nil, err
-	}
 	if err := rows.Err(); err != nil {
 		return nil, err
 	}
@@ -110,7 +102,7 @@ RETURNING author_id, name
 `
 
 func (q *Queries) CreateAuthor(ctx context.Context, name string) (Author, error) {
-	row := q.db.QueryRowContext(ctx, createAuthor, name)
+	row := q.db.QueryRow(ctx, createAuthor, name)
 	var i Author
 	err := row.Scan(&i.AuthorID, &i.Name)
 	return i, err
@@ -143,19 +135,19 @@ type CreateBookParams struct {
 	BookType  BookType
 	Title     string
 	Year      int32
-	Available time.Time
+	Available pgtype.Timestamptz
 	Tags      []string
 }
 
 func (q *Queries) CreateBook(ctx context.Context, arg CreateBookParams) (Book, error) {
-	row := q.db.QueryRowContext(ctx, createBook,
+	row := q.db.QueryRow(ctx, createBook,
 		arg.AuthorID,
 		arg.Isbn,
 		arg.BookType,
 		arg.Title,
 		arg.Year,
 		arg.Available,
-		pq.Array(arg.Tags),
+		arg.Tags,
 	)
 	var i Book
 	err := row.Scan(
@@ -166,7 +158,7 @@ func (q *Queries) CreateBook(ctx context.Context, arg CreateBookParams) (Book, e
 		&i.Title,
 		&i.Year,
 		&i.Available,
-		pq.Array(&i.Tags),
+		&i.Tags,
 	)
 	return i, err
 }
@@ -177,7 +169,7 @@ WHERE book_id = $1
 `
 
 func (q *Queries) DeleteBook(ctx context.Context, bookID int32) error {
-	_, err := q.db.ExecContext(ctx, deleteBook, bookID)
+	_, err := q.db.Exec(ctx, deleteBook, bookID)
 	return err
 }
 
@@ -187,7 +179,7 @@ WHERE author_id = $1
 `
 
 func (q *Queries) GetAuthor(ctx context.Context, authorID int32) (Author, error) {
-	row := q.db.QueryRowContext(ctx, getAuthor, authorID)
+	row := q.db.QueryRow(ctx, getAuthor, authorID)
 	var i Author
 	err := row.Scan(&i.AuthorID, &i.Name)
 	return i, err
@@ -199,7 +191,7 @@ WHERE book_id = $1
 `
 
 func (q *Queries) GetBook(ctx context.Context, bookID int32) (Book, error) {
-	row := q.db.QueryRowContext(ctx, getBook, bookID)
+	row := q.db.QueryRow(ctx, getBook, bookID)
 	var i Book
 	err := row.Scan(
 		&i.BookID,
@@ -209,7 +201,7 @@ func (q *Queries) GetBook(ctx context.Context, bookID int32) (Book, error) {
 		&i.Title,
 		&i.Year,
 		&i.Available,
-		pq.Array(&i.Tags),
+		&i.Tags,
 	)
 	return i, err
 }
@@ -227,7 +219,7 @@ type UpdateBookParams struct {
 }
 
 func (q *Queries) UpdateBook(ctx context.Context, arg UpdateBookParams) error {
-	_, err := q.db.ExecContext(ctx, updateBook, arg.Title, pq.Array(arg.Tags), arg.BookID)
+	_, err := q.db.Exec(ctx, updateBook, arg.Title, arg.Tags, arg.BookID)
 	return err
 }
 
@@ -245,9 +237,9 @@ type UpdateBookISBNParams struct {
 }
 
 func (q *Queries) UpdateBookISBN(ctx context.Context, arg UpdateBookISBNParams) error {
-	_, err := q.db.ExecContext(ctx, updateBookISBN,
+	_, err := q.db.Exec(ctx, updateBookISBN,
 		arg.Title,
-		pq.Array(arg.Tags),
+		arg.Tags,
 		arg.BookID,
 		arg.Isbn,
 	)
diff --git a/examples/booktest/sqlc.json b/examples/booktest/sqlc.json
index b1453a2e3..8f5ddad6e 100644
--- a/examples/booktest/sqlc.json
+++ b/examples/booktest/sqlc.json
@@ -10,6 +10,7 @@
       "schema": "postgresql/schema.sql",
       "queries": "postgresql/query.sql",
       "engine": "postgresql",
+      "sql_package": "pgx/v5",
       "database": {
         "managed": true
       },
@@ -47,4 +48,4 @@
       ]
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/internal/endtoend/testdata/func_out_param/issue.md b/internal/endtoend/testdata/func_out_param/issue.md
new file mode 100644
index 000000000..1b8779f2f
--- /dev/null
+++ b/internal/endtoend/testdata/func_out_param/issue.md
@@ -0,0 +1,1 @@
+https://github.com/sqlc-dev/sqlc/issues/1654
diff --git a/internal/endtoend/testdata/func_out_param/pgx/go/db.go b/internal/endtoend/testdata/func_out_param/pgx/go/db.go
new file mode 100644
index 000000000..8a010ccc4
--- /dev/null
+++ b/internal/endtoend/testdata/func_out_param/pgx/go/db.go
@@ -0,0 +1,32 @@
+// Code generated by sqlc. DO NOT EDIT.
+// versions:
+//   sqlc v1.22.0
+
+package querytest
+
+import (
+	"context"
+
+	"github.com/jackc/pgx/v5"
+	"github.com/jackc/pgx/v5/pgconn"
+)
+
+type DBTX interface {
+	Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
+	Query(context.Context, string, ...interface{}) (pgx.Rows, error)
+	QueryRow(context.Context, string, ...interface{}) pgx.Row
+}
+
+func New(db DBTX) *Queries {
+	return &Queries{db: db}
+}
+
+type Queries struct {
+	db DBTX
+}
+
+func (q *Queries) WithTx(tx pgx.Tx) *Queries {
+	return &Queries{
+		db: tx,
+	}
+}
diff --git a/internal/endtoend/testdata/func_out_param/pgx/go/models.go b/internal/endtoend/testdata/func_out_param/pgx/go/models.go
new file mode 100644
index 000000000..b320134bd
--- /dev/null
+++ b/internal/endtoend/testdata/func_out_param/pgx/go/models.go
@@ -0,0 +1,15 @@
+// Code generated by sqlc. DO NOT EDIT.
+// versions:
+//   sqlc v1.22.0
+
+package querytest
+
+import (
+	"github.com/jackc/pgx/v5/pgtype"
+)
+
+type Author struct {
+	ID   int64
+	Name string
+	Bio  pgtype.Text
+}
diff --git a/internal/endtoend/testdata/func_out_param/pgx/go/query.sql.go b/internal/endtoend/testdata/func_out_param/pgx/go/query.sql.go
new file mode 100644
index 000000000..b19b4a070
--- /dev/null
+++ b/internal/endtoend/testdata/func_out_param/pgx/go/query.sql.go
@@ -0,0 +1,30 @@
+// Code generated by sqlc. DO NOT EDIT.
+// versions:
+//   sqlc v1.22.0
+// source: query.sql
+
+package querytest
+
+import (
+	"context"
+
+	"github.com/jackc/pgx/v5/pgtype"
+)
+
+const createAuthor = `-- name: CreateAuthor :one
+SELECT id FROM add_author (
+  $1, $2
+)
+`
+
+type CreateAuthorParams struct {
+	Name string
+	Bio  string
+}
+
+func (q *Queries) CreateAuthor(ctx context.Context, arg CreateAuthorParams) (pgtype.Int4, error) {
+	row := q.db.QueryRow(ctx, createAuthor, arg.Name, arg.Bio)
+	var id pgtype.Int4
+	err := row.Scan(&id)
+	return id, err
+}
diff --git a/internal/endtoend/testdata/func_out_param/pgx/query.sql b/internal/endtoend/testdata/func_out_param/pgx/query.sql
new file mode 100644
index 000000000..b23c7dca4
--- /dev/null
+++ b/internal/endtoend/testdata/func_out_param/pgx/query.sql
@@ -0,0 +1,4 @@
+-- name: CreateAuthor :one
+SELECT * FROM add_author (
+  sqlc.arg(name), sqlc.arg(bio)
+);
diff --git a/internal/endtoend/testdata/func_out_param/pgx/schema.sql b/internal/endtoend/testdata/func_out_param/pgx/schema.sql
new file mode 100644
index 000000000..97c1022d1
--- /dev/null
+++ b/internal/endtoend/testdata/func_out_param/pgx/schema.sql
@@ -0,0 +1,14 @@
+-- Example queries for sqlc
+CREATE TABLE authors (
+  id   BIGSERIAL PRIMARY KEY,
+  name text      NOT NULL,
+  bio  text
+);
+
+CREATE OR REPLACE FUNCTION add_author (name text, bio text, out id int)
+AS $$
+DECLARE
+BEGIN
+  id = 123;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/internal/endtoend/testdata/func_out_param/pgx/sqlc.yaml b/internal/endtoend/testdata/func_out_param/pgx/sqlc.yaml
new file mode 100644
index 000000000..5dc63e3f9
--- /dev/null
+++ b/internal/endtoend/testdata/func_out_param/pgx/sqlc.yaml
@@ -0,0 +1,10 @@
+version: "2"
+sql:
+  - engine: "postgresql"
+    schema: "schema.sql"
+    queries: "query.sql"
+    gen:
+      go:
+        package: "querytest"
+        out: "go"
+        sql_package: "pgx/v5"
diff --git a/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/go/models.go b/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/go/models.go
index b1624997b..d918dc44c 100644
--- a/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/go/models.go
+++ b/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/go/models.go
@@ -219,6 +219,48 @@ func (ns NullQueryReturnStructEnumTableEnum) Value() (driver.Value, error) {
 	return string(ns.QueryReturnStructEnumTableEnum), nil
 }
 
+type QuerySqlcEmbedEnum string
+
+const (
+	QuerySqlcEmbedEnumM QuerySqlcEmbedEnum = "m"
+	QuerySqlcEmbedEnumN QuerySqlcEmbedEnum = "n"
+)
+
+func (e *QuerySqlcEmbedEnum) Scan(src interface{}) error {
+	switch s := src.(type) {
+	case []byte:
+		*e = QuerySqlcEmbedEnum(s)
+	case string:
+		*e = QuerySqlcEmbedEnum(s)
+	default:
+		return fmt.Errorf("unsupported scan type for QuerySqlcEmbedEnum: %T", src)
+	}
+	return nil
+}
+
+type NullQuerySqlcEmbedEnum struct {
+	QuerySqlcEmbedEnum QuerySqlcEmbedEnum
+	Valid              bool // Valid is true if QuerySqlcEmbedEnum is not NULL
+}
+
+// Scan implements the Scanner interface.
+func (ns *NullQuerySqlcEmbedEnum) Scan(value interface{}) error {
+	if value == nil {
+		ns.QuerySqlcEmbedEnum, ns.Valid = "", false
+		return nil
+	}
+	ns.Valid = true
+	return ns.QuerySqlcEmbedEnum.Scan(value)
+}
+
+// Value implements the driver Valuer interface.
+func (ns NullQuerySqlcEmbedEnum) Value() (driver.Value, error) {
+	if !ns.Valid {
+		return nil, nil
+	}
+	return string(ns.QuerySqlcEmbedEnum), nil
+}
+
 type QueryParamEnumTable struct {
 	ID    int32
 	Other QueryParamEnumTableEnum
@@ -229,3 +271,8 @@ type QueryReturnFullTable struct {
 	ID    int32
 	Value NullQueryReturnFullTableEnum
 }
+
+type QuerySqlcEmbedTable struct {
+	ID    int32
+	Value NullQuerySqlcEmbedEnum
+}
diff --git a/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/go/query.sql.go b/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/go/query.sql.go
index f65910269..91457e09a 100644
--- a/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/go/query.sql.go
+++ b/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/go/query.sql.go
@@ -90,3 +90,18 @@ func (q *Queries) query_return_struct_enum_table(ctx context.Context, id int32)
 	err := row.Scan(&i.Value, &i.Another)
 	return i, err
 }
+
+const query_sqlc_embed_table = `-- name: query_sqlc_embed_table :one
+SELECT query_sqlc_embed_table.id, query_sqlc_embed_table.value FROM query_sqlc_embed_table WHERE id = $1
+`
+
+type query_sqlc_embed_tableRow struct {
+	QuerySqlcEmbedTable QuerySqlcEmbedTable
+}
+
+func (q *Queries) query_sqlc_embed_table(ctx context.Context, id int32) (query_sqlc_embed_tableRow, error) {
+	row := q.db.QueryRowContext(ctx, query_sqlc_embed_table, id)
+	var i query_sqlc_embed_tableRow
+	err := row.Scan(&i.QuerySqlcEmbedTable.ID, &i.QuerySqlcEmbedTable.Value)
+	return i, err
+}
diff --git a/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/query.sql b/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/query.sql
index 1420d869d..599a22748 100644
--- a/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/query.sql
+++ b/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/query.sql
@@ -11,4 +11,7 @@ SELECT id FROM query_param_struct_enum_table WHERE id = $1 AND value = $2;
 SELECT value FROM query_return_enum_table WHERE id = $1;
 
 -- name: query_return_struct_enum_table :one
-SELECT value, another FROM query_return_struct_enum_table WHERE id = $1;
\ No newline at end of file
+SELECT value, another FROM query_return_struct_enum_table WHERE id = $1;
+
+-- name: query_sqlc_embed_table :one
+SELECT sqlc.embed(query_sqlc_embed_table) FROM query_sqlc_embed_table WHERE id = $1;
diff --git a/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/schema.sql b/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/schema.sql
index cc2ae14b2..486119c33 100644
--- a/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/schema.sql
+++ b/internal/endtoend/testdata/omit_unused_structs/postgresql/stdlib/schema.sql
@@ -50,4 +50,12 @@ CREATE TABLE query_return_struct_enum_table (
     id      INTEGER PRIMARY KEY,
     value   query_return_struct_enum_table_enum,
     another INTEGER
-);
\ No newline at end of file
+);
+
+CREATE TYPE query_sqlc_embed_enum AS ENUM (
+    'm', 'n'
+);
+CREATE TABLE query_sqlc_embed_table (
+    id      INTEGER PRIMARY KEY,
+    value   query_sqlc_embed_enum
+)