snapshot of https://github.com/sqlc-dev/sqlc.git (main, 2026-09-16; blobs > 131072B omitted)
Some checks failed
kotlin / test (push) Has been skipped
python / test (push) Has been skipped
typescript / test (push) Has been skipped
go / build darwin/amd64 (push) Has been cancelled
go / build linux/amd64 (push) Has been cancelled
go / build windows/amd64 (push) Has been cancelled
go / build darwin/arm64 (push) Has been cancelled
go / build linux/arm64 (push) Has been cancelled
go / build windows/arm64 (push) Has been cancelled
go / test (push) Has been cancelled
go / vuln_check (push) Has been cancelled
Some checks failed
kotlin / test (push) Has been skipped
python / test (push) Has been skipped
typescript / test (push) Has been skipped
go / build darwin/amd64 (push) Has been cancelled
go / build linux/amd64 (push) Has been cancelled
go / build windows/amd64 (push) Has been cancelled
go / build darwin/arm64 (push) Has been cancelled
go / build linux/arm64 (push) Has been cancelled
go / build windows/arm64 (push) Has been cancelled
go / test (push) Has been cancelled
go / vuln_check (push) Has been cancelled
This commit is contained in:
31
examples/authors/mysql/db.go
Normal file
31
examples/authors/mysql/db.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package authors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
ExecContext(context.Context, string, ...any) (sql.Result, error)
|
||||
PrepareContext(context.Context, string) (*sql.Stmt, error)
|
||||
QueryContext(context.Context, string, ...any) (*sql.Rows, error)
|
||||
QueryRowContext(context.Context, string, ...any) *sql.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
}
|
||||
}
|
||||
53
examples/authors/mysql/db_test.go
Normal file
53
examples/authors/mysql/db_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
//go:build examples
|
||||
|
||||
package authors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/sqltest/local"
|
||||
)
|
||||
|
||||
func TestAuthors(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
uri := local.MySQL(t, []string{"schema.sql"})
|
||||
sdb, err := sql.Open("mysql", uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer sdb.Close()
|
||||
|
||||
db := New(sdb)
|
||||
|
||||
// list all authors
|
||||
authors, err := db.ListAuthors(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(authors)
|
||||
|
||||
// create an author
|
||||
result, err := db.CreateAuthor(ctx, CreateAuthorParams{
|
||||
Name: "Brian Kernighan",
|
||||
Bio: sql.NullString{String: "Co-author of The C Programming Language and The Go Programming Language", Valid: true},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
authorID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(authorID)
|
||||
|
||||
// get the author we just inserted
|
||||
fetchedAuthor, err := db.GetAuthor(ctx, authorID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(fetchedAuthor)
|
||||
}
|
||||
15
examples/authors/mysql/models.go
Normal file
15
examples/authors/mysql/models.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package authors
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type Author struct {
|
||||
ID int64
|
||||
Name string
|
||||
Bio sql.NullString
|
||||
}
|
||||
18
examples/authors/mysql/query.sql
Normal file
18
examples/authors/mysql/query.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
/* name: GetAuthor :one */
|
||||
SELECT * FROM authors
|
||||
WHERE id = ? LIMIT 1;
|
||||
|
||||
/* name: ListAuthors :many */
|
||||
SELECT * FROM authors
|
||||
ORDER BY name;
|
||||
|
||||
/* name: CreateAuthor :execresult */
|
||||
INSERT INTO authors (
|
||||
name, bio
|
||||
) VALUES (
|
||||
?, ?
|
||||
);
|
||||
|
||||
/* name: DeleteAuthor :exec */
|
||||
DELETE FROM authors
|
||||
WHERE id = ?;
|
||||
78
examples/authors/mysql/query.sql.go
Normal file
78
examples/authors/mysql/query.sql.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: query.sql
|
||||
|
||||
package authors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
const createAuthor = `-- name: CreateAuthor :execresult
|
||||
INSERT INTO authors (
|
||||
name, bio
|
||||
) VALUES (
|
||||
?, ?
|
||||
)
|
||||
`
|
||||
|
||||
type CreateAuthorParams struct {
|
||||
Name string
|
||||
Bio sql.NullString
|
||||
}
|
||||
|
||||
func (q *Queries) CreateAuthor(ctx context.Context, arg CreateAuthorParams) (sql.Result, error) {
|
||||
return q.db.ExecContext(ctx, createAuthor, arg.Name, arg.Bio)
|
||||
}
|
||||
|
||||
const deleteAuthor = `-- name: DeleteAuthor :exec
|
||||
DELETE FROM authors
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteAuthor(ctx context.Context, id int64) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteAuthor, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAuthor = `-- name: GetAuthor :one
|
||||
SELECT id, name, bio FROM authors
|
||||
WHERE id = ? LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) {
|
||||
row := q.db.QueryRowContext(ctx, getAuthor, id)
|
||||
var i Author
|
||||
err := row.Scan(&i.ID, &i.Name, &i.Bio)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listAuthors = `-- name: ListAuthors :many
|
||||
SELECT id, name, bio FROM authors
|
||||
ORDER BY name
|
||||
`
|
||||
|
||||
func (q *Queries) ListAuthors(ctx context.Context) ([]Author, error) {
|
||||
rows, err := q.db.QueryContext(ctx, listAuthors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Author
|
||||
for rows.Next() {
|
||||
var i Author
|
||||
if err := rows.Scan(&i.ID, &i.Name, &i.Bio); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
5
examples/authors/mysql/schema.sql
Normal file
5
examples/authors/mysql/schema.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
CREATE TABLE authors (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
name text NOT NULL,
|
||||
bio text
|
||||
);
|
||||
32
examples/authors/postgresql/db.go
Normal file
32
examples/authors/postgresql/db.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package authors
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
Exec(context.Context, string, ...any) (pgconn.CommandTag, error)
|
||||
Query(context.Context, string, ...any) (pgx.Rows, error)
|
||||
QueryRow(context.Context, string, ...any) 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,
|
||||
}
|
||||
}
|
||||
49
examples/authors/postgresql/db_test.go
Normal file
49
examples/authors/postgresql/db_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
//go:build examples
|
||||
|
||||
package authors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/sqltest/local"
|
||||
)
|
||||
|
||||
func TestAuthors(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
uri := local.PostgreSQL(t, []string{"schema.sql"})
|
||||
db, err := pgx.Connect(ctx, uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close(ctx)
|
||||
|
||||
q := New(db)
|
||||
|
||||
// list all authors
|
||||
authors, err := q.ListAuthors(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(authors)
|
||||
|
||||
// create an author
|
||||
insertedAuthor, err := q.CreateAuthor(ctx, CreateAuthorParams{
|
||||
Name: "Brian Kernighan",
|
||||
Bio: pgtype.Text{String: "Co-author of The C Programming Language and The Go Programming Language", Valid: true},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(insertedAuthor)
|
||||
|
||||
// get the author we just inserted
|
||||
fetchedAuthor, err := q.GetAuthor(ctx, insertedAuthor.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(fetchedAuthor)
|
||||
}
|
||||
15
examples/authors/postgresql/models.go
Normal file
15
examples/authors/postgresql/models.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package authors
|
||||
|
||||
import (
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type Author struct {
|
||||
ID int64
|
||||
Name string
|
||||
Bio pgtype.Text
|
||||
}
|
||||
19
examples/authors/postgresql/query.sql
Normal file
19
examples/authors/postgresql/query.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- name: GetAuthor :one
|
||||
SELECT * FROM authors
|
||||
WHERE id = $1 LIMIT 1;
|
||||
|
||||
-- name: ListAuthors :many
|
||||
SELECT * FROM authors
|
||||
ORDER BY name;
|
||||
|
||||
-- name: CreateAuthor :one
|
||||
INSERT INTO authors (
|
||||
name, bio
|
||||
) VALUES (
|
||||
$1, $2
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteAuthor :exec
|
||||
DELETE FROM authors
|
||||
WHERE id = $1;
|
||||
80
examples/authors/postgresql/query.sql.go
Normal file
80
examples/authors/postgresql/query.sql.go
Normal file
@@ -0,0 +1,80 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: query.sql
|
||||
|
||||
package authors
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const createAuthor = `-- name: CreateAuthor :one
|
||||
INSERT INTO authors (
|
||||
name, bio
|
||||
) VALUES (
|
||||
$1, $2
|
||||
)
|
||||
RETURNING id, name, bio
|
||||
`
|
||||
|
||||
type CreateAuthorParams struct {
|
||||
Name string
|
||||
Bio pgtype.Text
|
||||
}
|
||||
|
||||
func (q *Queries) CreateAuthor(ctx context.Context, arg CreateAuthorParams) (Author, error) {
|
||||
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
|
||||
}
|
||||
|
||||
const deleteAuthor = `-- name: DeleteAuthor :exec
|
||||
DELETE FROM authors
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteAuthor(ctx context.Context, id int64) error {
|
||||
_, err := q.db.Exec(ctx, deleteAuthor, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAuthor = `-- name: GetAuthor :one
|
||||
SELECT id, name, bio FROM authors
|
||||
WHERE id = $1 LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) {
|
||||
row := q.db.QueryRow(ctx, getAuthor, id)
|
||||
var i Author
|
||||
err := row.Scan(&i.ID, &i.Name, &i.Bio)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listAuthors = `-- name: ListAuthors :many
|
||||
SELECT id, name, bio FROM authors
|
||||
ORDER BY name
|
||||
`
|
||||
|
||||
func (q *Queries) ListAuthors(ctx context.Context) ([]Author, error) {
|
||||
rows, err := q.db.Query(ctx, listAuthors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Author
|
||||
for rows.Next() {
|
||||
var i Author
|
||||
if err := rows.Scan(&i.ID, &i.Name, &i.Bio); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
5
examples/authors/postgresql/schema.sql
Normal file
5
examples/authors/postgresql/schema.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
CREATE TABLE authors (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name text NOT NULL,
|
||||
bio text
|
||||
);
|
||||
52
examples/authors/sqlc.yaml
Normal file
52
examples/authors/sqlc.yaml
Normal file
@@ -0,0 +1,52 @@
|
||||
version: '2'
|
||||
cloud:
|
||||
project: "01HAQMMECEYQYKFJN8MP16QC41"
|
||||
sql:
|
||||
- name: postgresql
|
||||
schema: postgresql/schema.sql
|
||||
queries: postgresql/query.sql
|
||||
engine: postgresql
|
||||
database:
|
||||
uri: "${VET_TEST_EXAMPLES_POSTGRES_AUTHORS}"
|
||||
analyzer:
|
||||
database: false
|
||||
rules:
|
||||
- sqlc/db-prepare
|
||||
- postgresql-query-too-costly
|
||||
gen:
|
||||
go:
|
||||
package: authors
|
||||
sql_package: pgx/v5
|
||||
out: postgresql
|
||||
- name: mysql
|
||||
schema: mysql/schema.sql
|
||||
queries: mysql/query.sql
|
||||
engine: mysql
|
||||
database:
|
||||
uri: "${VET_TEST_EXAMPLES_MYSQL_AUTHORS}"
|
||||
rules:
|
||||
- sqlc/db-prepare
|
||||
# - mysql-query-too-costly
|
||||
gen:
|
||||
go:
|
||||
package: authors
|
||||
out: mysql
|
||||
- name: sqlite
|
||||
schema: sqlite/schema.sql
|
||||
queries: sqlite/query.sql
|
||||
engine: sqlite
|
||||
database:
|
||||
uri: file:authors?mode=memory&cache=shared
|
||||
rules:
|
||||
- sqlc/db-prepare
|
||||
gen:
|
||||
go:
|
||||
package: authors
|
||||
out: sqlite
|
||||
rules:
|
||||
- name: postgresql-query-too-costly
|
||||
message: "Too costly"
|
||||
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"
|
||||
31
examples/authors/sqlite/db.go
Normal file
31
examples/authors/sqlite/db.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package authors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
ExecContext(context.Context, string, ...any) (sql.Result, error)
|
||||
PrepareContext(context.Context, string) (*sql.Stmt, error)
|
||||
QueryContext(context.Context, string, ...any) (*sql.Rows, error)
|
||||
QueryRowContext(context.Context, string, ...any) *sql.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
}
|
||||
}
|
||||
48
examples/authors/sqlite/db_test.go
Normal file
48
examples/authors/sqlite/db_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
//go:build examples
|
||||
|
||||
package authors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/sqltest"
|
||||
)
|
||||
|
||||
func TestAuthors(t *testing.T) {
|
||||
sdb, cleanup := sqltest.SQLite(t, []string{"schema.sql"})
|
||||
defer sdb.Close()
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
db := New(sdb)
|
||||
|
||||
// list all authors
|
||||
authors, err := db.ListAuthors(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(authors)
|
||||
|
||||
// create an author
|
||||
result, err := db.CreateAuthor(ctx, CreateAuthorParams{
|
||||
Name: "Brian Kernighan",
|
||||
Bio: sql.NullString{String: "Co-author of The C Programming Language and The Go Programming Language", Valid: true},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
authorID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(authorID)
|
||||
|
||||
// get the author we just inserted
|
||||
fetchedAuthor, err := db.GetAuthor(ctx, authorID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(fetchedAuthor)
|
||||
}
|
||||
15
examples/authors/sqlite/models.go
Normal file
15
examples/authors/sqlite/models.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package authors
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type Author struct {
|
||||
ID int64
|
||||
Name string
|
||||
Bio sql.NullString
|
||||
}
|
||||
18
examples/authors/sqlite/query.sql
Normal file
18
examples/authors/sqlite/query.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
/* name: GetAuthor :one */
|
||||
SELECT * FROM authors
|
||||
WHERE id = ? LIMIT 1;
|
||||
|
||||
/* name: ListAuthors :many */
|
||||
SELECT * FROM authors
|
||||
ORDER BY name;
|
||||
|
||||
/* name: CreateAuthor :execresult */
|
||||
INSERT INTO authors (
|
||||
name, bio
|
||||
) VALUES (
|
||||
?, ?
|
||||
);
|
||||
|
||||
/* name: DeleteAuthor :exec */
|
||||
DELETE FROM authors
|
||||
WHERE id = ?;
|
||||
78
examples/authors/sqlite/query.sql.go
Normal file
78
examples/authors/sqlite/query.sql.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: query.sql
|
||||
|
||||
package authors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
const createAuthor = `-- name: CreateAuthor :execresult
|
||||
INSERT INTO authors (
|
||||
name, bio
|
||||
) VALUES (
|
||||
?, ?
|
||||
)
|
||||
`
|
||||
|
||||
type CreateAuthorParams struct {
|
||||
Name string
|
||||
Bio sql.NullString
|
||||
}
|
||||
|
||||
func (q *Queries) CreateAuthor(ctx context.Context, arg CreateAuthorParams) (sql.Result, error) {
|
||||
return q.db.ExecContext(ctx, createAuthor, arg.Name, arg.Bio)
|
||||
}
|
||||
|
||||
const deleteAuthor = `-- name: DeleteAuthor :exec
|
||||
DELETE FROM authors
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteAuthor(ctx context.Context, id int64) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteAuthor, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAuthor = `-- name: GetAuthor :one
|
||||
SELECT id, name, bio FROM authors
|
||||
WHERE id = ? LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) {
|
||||
row := q.db.QueryRowContext(ctx, getAuthor, id)
|
||||
var i Author
|
||||
err := row.Scan(&i.ID, &i.Name, &i.Bio)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listAuthors = `-- name: ListAuthors :many
|
||||
SELECT id, name, bio FROM authors
|
||||
ORDER BY name
|
||||
`
|
||||
|
||||
func (q *Queries) ListAuthors(ctx context.Context) ([]Author, error) {
|
||||
rows, err := q.db.QueryContext(ctx, listAuthors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Author
|
||||
for rows.Next() {
|
||||
var i Author
|
||||
if err := rows.Scan(&i.ID, &i.Name, &i.Bio); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
5
examples/authors/sqlite/schema.sql
Normal file
5
examples/authors/sqlite/schema.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
CREATE TABLE authors (
|
||||
id integer PRIMARY KEY AUTOINCREMENT,
|
||||
name text NOT NULL,
|
||||
bio text
|
||||
);
|
||||
404
examples/batch/postgresql/batch.go
Normal file
404
examples/batch/postgresql/batch.go
Normal file
@@ -0,0 +1,404 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: batch.go
|
||||
|
||||
package batch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBatchAlreadyClosed = errors.New("batch already closed")
|
||||
)
|
||||
|
||||
const booksByYear = `-- name: BooksByYear :batchmany
|
||||
SELECT book_id, author_id, isbn, book_type, title, year, available, tags FROM books
|
||||
WHERE year = $1
|
||||
`
|
||||
|
||||
type BooksByYearBatchResults struct {
|
||||
br pgx.BatchResults
|
||||
tot int
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (q *Queries) BooksByYear(ctx context.Context, year []int32) *BooksByYearBatchResults {
|
||||
batch := &pgx.Batch{}
|
||||
for _, a := range year {
|
||||
vals := []any{
|
||||
a,
|
||||
}
|
||||
batch.Queue(booksByYear, vals...)
|
||||
}
|
||||
br := q.db.SendBatch(ctx, batch)
|
||||
return &BooksByYearBatchResults{br, len(year), false}
|
||||
}
|
||||
|
||||
func (b *BooksByYearBatchResults) Query(f func(int, []Book, error)) {
|
||||
defer b.br.Close()
|
||||
for t := 0; t < b.tot; t++ {
|
||||
var items []Book
|
||||
if b.closed {
|
||||
if f != nil {
|
||||
f(t, items, ErrBatchAlreadyClosed)
|
||||
}
|
||||
continue
|
||||
}
|
||||
err := func() error {
|
||||
rows, err := b.br.Query()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var i Book
|
||||
if err := rows.Scan(
|
||||
&i.BookID,
|
||||
&i.AuthorID,
|
||||
&i.Isbn,
|
||||
&i.BookType,
|
||||
&i.Title,
|
||||
&i.Year,
|
||||
&i.Available,
|
||||
&i.Tags,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
return rows.Err()
|
||||
}()
|
||||
if f != nil {
|
||||
f(t, items, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BooksByYearBatchResults) Close() error {
|
||||
b.closed = true
|
||||
return b.br.Close()
|
||||
}
|
||||
|
||||
const createBook = `-- name: CreateBook :batchone
|
||||
INSERT INTO books (
|
||||
author_id,
|
||||
isbn,
|
||||
book_type,
|
||||
title,
|
||||
year,
|
||||
available,
|
||||
tags
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7
|
||||
)
|
||||
RETURNING book_id, author_id, isbn, book_type, title, year, available, tags
|
||||
`
|
||||
|
||||
type CreateBookBatchResults struct {
|
||||
br pgx.BatchResults
|
||||
tot int
|
||||
closed bool
|
||||
}
|
||||
|
||||
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 pgtype.Timestamptz `json:"available"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateBook(ctx context.Context, arg []CreateBookParams) *CreateBookBatchResults {
|
||||
batch := &pgx.Batch{}
|
||||
for _, a := range arg {
|
||||
vals := []any{
|
||||
a.AuthorID,
|
||||
a.Isbn,
|
||||
a.BookType,
|
||||
a.Title,
|
||||
a.Year,
|
||||
a.Available,
|
||||
a.Tags,
|
||||
}
|
||||
batch.Queue(createBook, vals...)
|
||||
}
|
||||
br := q.db.SendBatch(ctx, batch)
|
||||
return &CreateBookBatchResults{br, len(arg), false}
|
||||
}
|
||||
|
||||
func (b *CreateBookBatchResults) QueryRow(f func(int, Book, error)) {
|
||||
defer b.br.Close()
|
||||
for t := 0; t < b.tot; t++ {
|
||||
var i Book
|
||||
if b.closed {
|
||||
if f != nil {
|
||||
f(t, i, ErrBatchAlreadyClosed)
|
||||
}
|
||||
continue
|
||||
}
|
||||
row := b.br.QueryRow()
|
||||
err := row.Scan(
|
||||
&i.BookID,
|
||||
&i.AuthorID,
|
||||
&i.Isbn,
|
||||
&i.BookType,
|
||||
&i.Title,
|
||||
&i.Year,
|
||||
&i.Available,
|
||||
&i.Tags,
|
||||
)
|
||||
if f != nil {
|
||||
f(t, i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *CreateBookBatchResults) Close() error {
|
||||
b.closed = true
|
||||
return b.br.Close()
|
||||
}
|
||||
|
||||
const deleteBook = `-- name: DeleteBook :batchexec
|
||||
DELETE FROM books
|
||||
WHERE book_id = $1
|
||||
`
|
||||
|
||||
type DeleteBookBatchResults struct {
|
||||
br pgx.BatchResults
|
||||
tot int
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteBook(ctx context.Context, bookID []int32) *DeleteBookBatchResults {
|
||||
batch := &pgx.Batch{}
|
||||
for _, a := range bookID {
|
||||
vals := []any{
|
||||
a,
|
||||
}
|
||||
batch.Queue(deleteBook, vals...)
|
||||
}
|
||||
br := q.db.SendBatch(ctx, batch)
|
||||
return &DeleteBookBatchResults{br, len(bookID), false}
|
||||
}
|
||||
|
||||
func (b *DeleteBookBatchResults) Exec(f func(int, error)) {
|
||||
defer b.br.Close()
|
||||
for t := 0; t < b.tot; t++ {
|
||||
if b.closed {
|
||||
if f != nil {
|
||||
f(t, ErrBatchAlreadyClosed)
|
||||
}
|
||||
continue
|
||||
}
|
||||
_, err := b.br.Exec()
|
||||
if f != nil {
|
||||
f(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *DeleteBookBatchResults) Close() error {
|
||||
b.closed = true
|
||||
return b.br.Close()
|
||||
}
|
||||
|
||||
const deleteBookNamedFunc = `-- name: DeleteBookNamedFunc :batchexec
|
||||
DELETE FROM books
|
||||
WHERE book_id = $1
|
||||
`
|
||||
|
||||
type DeleteBookNamedFuncBatchResults struct {
|
||||
br pgx.BatchResults
|
||||
tot int
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteBookNamedFunc(ctx context.Context, bookID []int32) *DeleteBookNamedFuncBatchResults {
|
||||
batch := &pgx.Batch{}
|
||||
for _, a := range bookID {
|
||||
vals := []any{
|
||||
a,
|
||||
}
|
||||
batch.Queue(deleteBookNamedFunc, vals...)
|
||||
}
|
||||
br := q.db.SendBatch(ctx, batch)
|
||||
return &DeleteBookNamedFuncBatchResults{br, len(bookID), false}
|
||||
}
|
||||
|
||||
func (b *DeleteBookNamedFuncBatchResults) Exec(f func(int, error)) {
|
||||
defer b.br.Close()
|
||||
for t := 0; t < b.tot; t++ {
|
||||
if b.closed {
|
||||
if f != nil {
|
||||
f(t, ErrBatchAlreadyClosed)
|
||||
}
|
||||
continue
|
||||
}
|
||||
_, err := b.br.Exec()
|
||||
if f != nil {
|
||||
f(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *DeleteBookNamedFuncBatchResults) Close() error {
|
||||
b.closed = true
|
||||
return b.br.Close()
|
||||
}
|
||||
|
||||
const deleteBookNamedSign = `-- name: DeleteBookNamedSign :batchexec
|
||||
DELETE FROM books
|
||||
WHERE book_id = $1
|
||||
`
|
||||
|
||||
type DeleteBookNamedSignBatchResults struct {
|
||||
br pgx.BatchResults
|
||||
tot int
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteBookNamedSign(ctx context.Context, bookID []int32) *DeleteBookNamedSignBatchResults {
|
||||
batch := &pgx.Batch{}
|
||||
for _, a := range bookID {
|
||||
vals := []any{
|
||||
a,
|
||||
}
|
||||
batch.Queue(deleteBookNamedSign, vals...)
|
||||
}
|
||||
br := q.db.SendBatch(ctx, batch)
|
||||
return &DeleteBookNamedSignBatchResults{br, len(bookID), false}
|
||||
}
|
||||
|
||||
func (b *DeleteBookNamedSignBatchResults) Exec(f func(int, error)) {
|
||||
defer b.br.Close()
|
||||
for t := 0; t < b.tot; t++ {
|
||||
if b.closed {
|
||||
if f != nil {
|
||||
f(t, ErrBatchAlreadyClosed)
|
||||
}
|
||||
continue
|
||||
}
|
||||
_, err := b.br.Exec()
|
||||
if f != nil {
|
||||
f(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *DeleteBookNamedSignBatchResults) Close() error {
|
||||
b.closed = true
|
||||
return b.br.Close()
|
||||
}
|
||||
|
||||
const getBiography = `-- name: GetBiography :batchone
|
||||
SELECT biography FROM authors
|
||||
WHERE author_id = $1
|
||||
`
|
||||
|
||||
type GetBiographyBatchResults struct {
|
||||
br pgx.BatchResults
|
||||
tot int
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (q *Queries) GetBiography(ctx context.Context, authorID []int32) *GetBiographyBatchResults {
|
||||
batch := &pgx.Batch{}
|
||||
for _, a := range authorID {
|
||||
vals := []any{
|
||||
a,
|
||||
}
|
||||
batch.Queue(getBiography, vals...)
|
||||
}
|
||||
br := q.db.SendBatch(ctx, batch)
|
||||
return &GetBiographyBatchResults{br, len(authorID), false}
|
||||
}
|
||||
|
||||
func (b *GetBiographyBatchResults) QueryRow(f func(int, []byte, error)) {
|
||||
defer b.br.Close()
|
||||
for t := 0; t < b.tot; t++ {
|
||||
var biography []byte
|
||||
if b.closed {
|
||||
if f != nil {
|
||||
f(t, biography, ErrBatchAlreadyClosed)
|
||||
}
|
||||
continue
|
||||
}
|
||||
row := b.br.QueryRow()
|
||||
err := row.Scan(&biography)
|
||||
if f != nil {
|
||||
f(t, biography, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *GetBiographyBatchResults) Close() error {
|
||||
b.closed = true
|
||||
return b.br.Close()
|
||||
}
|
||||
|
||||
const updateBook = `-- name: UpdateBook :batchexec
|
||||
UPDATE books
|
||||
SET title = $1, tags = $2
|
||||
WHERE book_id = $3
|
||||
`
|
||||
|
||||
type UpdateBookBatchResults struct {
|
||||
br pgx.BatchResults
|
||||
tot int
|
||||
closed bool
|
||||
}
|
||||
|
||||
type UpdateBookParams struct {
|
||||
Title string `json:"title"`
|
||||
Tags []string `json:"tags"`
|
||||
BookID int32 `json:"book_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateBook(ctx context.Context, arg []UpdateBookParams) *UpdateBookBatchResults {
|
||||
batch := &pgx.Batch{}
|
||||
for _, a := range arg {
|
||||
vals := []any{
|
||||
a.Title,
|
||||
a.Tags,
|
||||
a.BookID,
|
||||
}
|
||||
batch.Queue(updateBook, vals...)
|
||||
}
|
||||
br := q.db.SendBatch(ctx, batch)
|
||||
return &UpdateBookBatchResults{br, len(arg), false}
|
||||
}
|
||||
|
||||
func (b *UpdateBookBatchResults) Exec(f func(int, error)) {
|
||||
defer b.br.Close()
|
||||
for t := 0; t < b.tot; t++ {
|
||||
if b.closed {
|
||||
if f != nil {
|
||||
f(t, ErrBatchAlreadyClosed)
|
||||
}
|
||||
continue
|
||||
}
|
||||
_, err := b.br.Exec()
|
||||
if f != nil {
|
||||
f(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *UpdateBookBatchResults) Close() error {
|
||||
b.closed = true
|
||||
return b.br.Close()
|
||||
}
|
||||
33
examples/batch/postgresql/db.go
Normal file
33
examples/batch/postgresql/db.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package batch
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
Exec(context.Context, string, ...any) (pgconn.CommandTag, error)
|
||||
Query(context.Context, string, ...any) (pgx.Rows, error)
|
||||
QueryRow(context.Context, string, ...any) pgx.Row
|
||||
SendBatch(context.Context, *pgx.Batch) pgx.BatchResults
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
153
examples/batch/postgresql/db_test.go
Normal file
153
examples/batch/postgresql/db_test.go
Normal file
@@ -0,0 +1,153 @@
|
||||
//go:build examples
|
||||
|
||||
package batch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/sqltest/local"
|
||||
)
|
||||
|
||||
func TestBatchBooks(t *testing.T) {
|
||||
uri := local.PostgreSQL(t, []string{"schema.sql"})
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
db, err := pgx.Connect(ctx, uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close(ctx)
|
||||
|
||||
dq := New(db)
|
||||
|
||||
// create an author
|
||||
a, err := dq.CreateAuthor(ctx, "Unknown Master")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
now := pgtype.Timestamptz{Time: time.Now(), Valid: true}
|
||||
|
||||
// batch insert new books
|
||||
newBooksParams := []CreateBookParams{
|
||||
{
|
||||
AuthorID: a.AuthorID,
|
||||
Isbn: "1",
|
||||
Title: "my book title",
|
||||
BookType: BookTypeFICTION,
|
||||
Year: 2016,
|
||||
Available: now,
|
||||
Tags: []string{},
|
||||
},
|
||||
{
|
||||
AuthorID: a.AuthorID,
|
||||
Isbn: "2",
|
||||
Title: "the second book",
|
||||
BookType: BookTypeFICTION,
|
||||
Year: 2016,
|
||||
Available: now,
|
||||
Tags: []string{"cool", "unique"},
|
||||
},
|
||||
{
|
||||
AuthorID: a.AuthorID,
|
||||
Isbn: "3",
|
||||
Title: "the third book",
|
||||
BookType: BookTypeFICTION,
|
||||
Year: 2001,
|
||||
Available: now,
|
||||
Tags: []string{"cool"},
|
||||
},
|
||||
{
|
||||
AuthorID: a.AuthorID,
|
||||
Isbn: "4",
|
||||
Title: "4th place finisher",
|
||||
BookType: BookTypeNONFICTION,
|
||||
Year: 2011,
|
||||
Available: now,
|
||||
Tags: []string{"other"},
|
||||
},
|
||||
}
|
||||
newBooks := make([]Book, len(newBooksParams))
|
||||
var cnt int
|
||||
dq.CreateBook(ctx, newBooksParams).QueryRow(func(i int, b Book, err error) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed inserting book (%s): %s", b.Title, err)
|
||||
}
|
||||
newBooks[i] = b
|
||||
cnt = i
|
||||
})
|
||||
// first i was 0, so add 1
|
||||
cnt++
|
||||
numBooksExpected := len(newBooks)
|
||||
if cnt != numBooksExpected {
|
||||
t.Fatalf("expected to insert %d books; got %d", numBooksExpected, cnt)
|
||||
}
|
||||
|
||||
// batch update the title and tags
|
||||
updateBooksParams := []UpdateBookParams{
|
||||
{
|
||||
BookID: newBooks[1].BookID,
|
||||
Title: "changed second title",
|
||||
Tags: []string{"cool", "disastor"},
|
||||
},
|
||||
}
|
||||
dq.UpdateBook(ctx, updateBooksParams).Exec(func(i int, err error) {
|
||||
if err != nil {
|
||||
t.Fatalf("error updating book %d: %s", updateBooksParams[i].BookID, err)
|
||||
}
|
||||
})
|
||||
|
||||
// batch many to retrieve books by year
|
||||
selectBooksByTitleYearParams := []int32{2001, 2016}
|
||||
var books0 []Book
|
||||
dq.BooksByYear(ctx, selectBooksByTitleYearParams).Query(func(i int, books []Book, err error) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("num books for %d: %d", selectBooksByTitleYearParams[i], len(books))
|
||||
books0 = append(books0, books...)
|
||||
})
|
||||
|
||||
for _, book := range books0 {
|
||||
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)
|
||||
}
|
||||
t.Logf("Book %d author: %s\n", book.BookID, author.Name)
|
||||
}
|
||||
|
||||
// batch delete books
|
||||
deleteBooksParams := make([]int32, len(newBooks))
|
||||
for i, book := range newBooks {
|
||||
deleteBooksParams[i] = book.BookID
|
||||
}
|
||||
batchDelete := dq.DeleteBook(ctx, deleteBooksParams)
|
||||
numDeletesProcessed := 0
|
||||
wantNumDeletesProcessed := 2
|
||||
batchDelete.Exec(func(i int, err error) {
|
||||
if err != nil && err.Error() != "batch already closed" {
|
||||
t.Fatalf("error deleting book %d: %s", deleteBooksParams[i], err)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
numDeletesProcessed++
|
||||
}
|
||||
|
||||
if i == wantNumDeletesProcessed-1 {
|
||||
// close batch operation before processing all errors from delete operation
|
||||
if err := batchDelete.Close(); err != nil {
|
||||
t.Fatalf("failed to close batch operation: %s", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
if numDeletesProcessed != wantNumDeletesProcessed {
|
||||
t.Fatalf("expected Close to short-circuit record processing (expected %d; got %d)", wantNumDeletesProcessed, numDeletesProcessed)
|
||||
}
|
||||
}
|
||||
71
examples/batch/postgresql/models.go
Normal file
71
examples/batch/postgresql/models.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package batch
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type BookType string
|
||||
|
||||
const (
|
||||
BookTypeFICTION BookType = "FICTION"
|
||||
BookTypeNONFICTION BookType = "NONFICTION"
|
||||
)
|
||||
|
||||
func (e *BookType) Scan(src any) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = BookType(s)
|
||||
case string:
|
||||
*e = BookType(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for BookType: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullBookType struct {
|
||||
BookType BookType `json:"book_type"`
|
||||
Valid bool `json:"valid"` // Valid is true if BookType is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullBookType) Scan(value any) error {
|
||||
if value == nil {
|
||||
ns.BookType, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.BookType.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullBookType) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.BookType), nil
|
||||
}
|
||||
|
||||
type Author struct {
|
||||
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 pgtype.Timestamptz `json:"available"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
26
examples/batch/postgresql/querier.go
Normal file
26
examples/batch/postgresql/querier.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package batch
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
type Querier interface {
|
||||
BooksByYear(ctx context.Context, year []int32) *BooksByYearBatchResults
|
||||
CreateAuthor(ctx context.Context, name string) (Author, error)
|
||||
CreateBook(ctx context.Context, arg []CreateBookParams) *CreateBookBatchResults
|
||||
DeleteBook(ctx context.Context, bookID []int32) *DeleteBookBatchResults
|
||||
DeleteBookExecResult(ctx context.Context, bookID int32) (pgconn.CommandTag, error)
|
||||
DeleteBookNamedFunc(ctx context.Context, bookID []int32) *DeleteBookNamedFuncBatchResults
|
||||
DeleteBookNamedSign(ctx context.Context, bookID []int32) *DeleteBookNamedSignBatchResults
|
||||
GetAuthor(ctx context.Context, authorID int32) (Author, error)
|
||||
GetBiography(ctx context.Context, authorID []int32) *GetBiographyBatchResults
|
||||
UpdateBook(ctx context.Context, arg []UpdateBookParams) *UpdateBookBatchResults
|
||||
}
|
||||
|
||||
var _ Querier = (*Queries)(nil)
|
||||
56
examples/batch/postgresql/query.sql
Normal file
56
examples/batch/postgresql/query.sql
Normal file
@@ -0,0 +1,56 @@
|
||||
-- name: GetAuthor :one
|
||||
SELECT * FROM authors
|
||||
WHERE author_id = $1;
|
||||
|
||||
-- name: DeleteBookExecResult :execresult
|
||||
DELETE FROM books
|
||||
WHERE book_id = $1;
|
||||
|
||||
-- name: DeleteBook :batchexec
|
||||
DELETE FROM books
|
||||
WHERE book_id = $1;
|
||||
|
||||
-- name: DeleteBookNamedFunc :batchexec
|
||||
DELETE FROM books
|
||||
WHERE book_id = sqlc.arg (book_id);
|
||||
|
||||
-- name: DeleteBookNamedSign :batchexec
|
||||
DELETE FROM books
|
||||
WHERE book_id = @book_id;
|
||||
|
||||
-- name: BooksByYear :batchmany
|
||||
SELECT * FROM books
|
||||
WHERE year = $1;
|
||||
|
||||
-- name: CreateAuthor :one
|
||||
INSERT INTO authors (name) VALUES ($1)
|
||||
RETURNING *;
|
||||
|
||||
-- name: CreateBook :batchone
|
||||
INSERT INTO books (
|
||||
author_id,
|
||||
isbn,
|
||||
book_type,
|
||||
title,
|
||||
year,
|
||||
available,
|
||||
tags
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateBook :batchexec
|
||||
UPDATE books
|
||||
SET title = $1, tags = $2
|
||||
WHERE book_id = $3;
|
||||
|
||||
-- name: GetBiography :batchone
|
||||
SELECT biography FROM authors
|
||||
WHERE author_id = $1;
|
||||
45
examples/batch/postgresql/query.sql.go
Normal file
45
examples/batch/postgresql/query.sql.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: query.sql
|
||||
|
||||
package batch
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
const createAuthor = `-- name: CreateAuthor :one
|
||||
INSERT INTO authors (name) VALUES ($1)
|
||||
RETURNING author_id, name, biography
|
||||
`
|
||||
|
||||
func (q *Queries) CreateAuthor(ctx context.Context, name string) (Author, error) {
|
||||
row := q.db.QueryRow(ctx, createAuthor, name)
|
||||
var i Author
|
||||
err := row.Scan(&i.AuthorID, &i.Name, &i.Biography)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteBookExecResult = `-- name: DeleteBookExecResult :execresult
|
||||
DELETE FROM books
|
||||
WHERE book_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteBookExecResult(ctx context.Context, bookID int32) (pgconn.CommandTag, error) {
|
||||
return q.db.Exec(ctx, deleteBookExecResult, bookID)
|
||||
}
|
||||
|
||||
const getAuthor = `-- name: GetAuthor :one
|
||||
SELECT author_id, name, biography FROM authors
|
||||
WHERE author_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetAuthor(ctx context.Context, authorID int32) (Author, error) {
|
||||
row := q.db.QueryRow(ctx, getAuthor, authorID)
|
||||
var i Author
|
||||
err := row.Scan(&i.AuthorID, &i.Name, &i.Biography)
|
||||
return i, err
|
||||
}
|
||||
21
examples/batch/postgresql/schema.sql
Normal file
21
examples/batch/postgresql/schema.sql
Normal file
@@ -0,0 +1,21 @@
|
||||
CREATE TABLE authors (
|
||||
author_id SERIAL PRIMARY KEY,
|
||||
name text NOT NULL DEFAULT '',
|
||||
biography JSONB
|
||||
);
|
||||
|
||||
CREATE TYPE book_type AS ENUM (
|
||||
'FICTION',
|
||||
'NONFICTION'
|
||||
);
|
||||
|
||||
CREATE TABLE books (
|
||||
book_id SERIAL PRIMARY KEY,
|
||||
author_id integer NOT NULL REFERENCES authors(author_id),
|
||||
isbn text NOT NULL DEFAULT '' UNIQUE,
|
||||
book_type book_type NOT NULL DEFAULT 'FICTION',
|
||||
title text NOT NULL DEFAULT '',
|
||||
year integer NOT NULL DEFAULT 2000,
|
||||
available timestamp with time zone NOT NULL DEFAULT 'NOW()',
|
||||
tags varchar[] NOT NULL DEFAULT '{}'
|
||||
);
|
||||
28
examples/batch/sqlc.json
Normal file
28
examples/batch/sqlc.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"version": "1",
|
||||
"cloud": {
|
||||
"project": "01HAQMMECEYQYKFJN8MP16QC41"
|
||||
},
|
||||
"packages": [
|
||||
{
|
||||
"path": "postgresql",
|
||||
"name": "batch",
|
||||
"schema": "postgresql/schema.sql",
|
||||
"queries": "postgresql/query.sql",
|
||||
"engine": "postgresql",
|
||||
"database": {
|
||||
"uri": "${VET_TEST_EXAMPLES_POSTGRES_BATCH}"
|
||||
},
|
||||
"analyzer": {
|
||||
"database": false
|
||||
},
|
||||
"rules": [
|
||||
"sqlc/db-prepare"
|
||||
],
|
||||
"sql_package": "pgx/v5",
|
||||
"emit_json_tags": true,
|
||||
"emit_prepared_queries": true,
|
||||
"emit_interface": true
|
||||
}
|
||||
]
|
||||
}
|
||||
31
examples/booktest/mysql/db.go
Normal file
31
examples/booktest/mysql/db.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package booktest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
ExecContext(context.Context, string, ...any) (sql.Result, error)
|
||||
PrepareContext(context.Context, string) (*sql.Stmt, error)
|
||||
QueryContext(context.Context, string, ...any) (*sql.Rows, error)
|
||||
QueryRowContext(context.Context, string, ...any) *sql.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
}
|
||||
}
|
||||
174
examples/booktest/mysql/db_test.go
Normal file
174
examples/booktest/mysql/db_test.go
Normal file
@@ -0,0 +1,174 @@
|
||||
//go:build examples
|
||||
|
||||
package booktest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/sqltest/local"
|
||||
)
|
||||
|
||||
func TestBooks(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
uri := local.MySQL(t, []string{"schema.sql"})
|
||||
db, err := sql.Open("mysql", uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
dq := New(db)
|
||||
|
||||
// create an author
|
||||
result, err := dq.CreateAuthor(ctx, "Unknown Master")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
authorID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// create transaction
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tq := dq.WithTx(tx)
|
||||
|
||||
// save first book
|
||||
now := time.Now()
|
||||
_, err = tq.CreateBook(ctx, CreateBookParams{
|
||||
AuthorID: int32(authorID),
|
||||
Isbn: "1",
|
||||
Title: "my book title",
|
||||
BookType: BooksBookTypeFICTION,
|
||||
Yr: 2016,
|
||||
Available: now,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// save second book
|
||||
result, err = tq.CreateBook(ctx, CreateBookParams{
|
||||
AuthorID: int32(authorID),
|
||||
Isbn: "2",
|
||||
Title: "the second book",
|
||||
BookType: BooksBookTypeFICTION,
|
||||
Yr: 2016,
|
||||
Available: now,
|
||||
Tags: "cool,unique",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bookOneID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// update the title and tags
|
||||
err = tq.UpdateBook(ctx, UpdateBookParams{
|
||||
BookID: int32(bookOneID),
|
||||
Title: "changed second title",
|
||||
Tags: "cool,disastor",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// save third book
|
||||
_, err = tq.CreateBook(ctx, CreateBookParams{
|
||||
AuthorID: int32(authorID),
|
||||
Isbn: "3",
|
||||
Title: "the third book",
|
||||
BookType: BooksBookTypeFICTION,
|
||||
Yr: 2001,
|
||||
Available: now,
|
||||
Tags: "cool",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// save fourth book
|
||||
result, err = tq.CreateBook(ctx, CreateBookParams{
|
||||
AuthorID: int32(authorID),
|
||||
Isbn: "4",
|
||||
Title: "4th place finisher",
|
||||
BookType: BooksBookTypeNONFICTION,
|
||||
Yr: 2011,
|
||||
Available: now,
|
||||
Tags: "other",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bookThreeID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// tx commit
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// upsert, changing ISBN and title
|
||||
err = dq.UpdateBookISBN(ctx, UpdateBookISBNParams{
|
||||
BookID: int32(bookThreeID),
|
||||
Isbn: "NEW ISBN",
|
||||
Title: "never ever gonna finish, a quatrain",
|
||||
Tags: "someother",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// retrieve first book
|
||||
books0, err := dq.BooksByTitleYear(ctx, BooksByTitleYearParams{
|
||||
Title: "my book title",
|
||||
Yr: 2016,
|
||||
})
|
||||
if err != nil {
|
||||
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))
|
||||
author, err := dq.GetAuthor(ctx, book.AuthorID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("Book %d author: %s\n", book.BookID, author.Name)
|
||||
}
|
||||
|
||||
// find a book with either "cool" or "other" tag
|
||||
t.Logf("---------\nTag search results:\n")
|
||||
res, err := dq.BooksByTags(ctx, "cool")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, ab := range res {
|
||||
t.Logf("Book %d: '%s', Author: '%s', ISBN: '%s' Tags: '%v'\n", ab.BookID, ab.Title, ab.Name.String, ab.Isbn, ab.Tags)
|
||||
}
|
||||
|
||||
// TODO: call say_hello(varchar)
|
||||
|
||||
// get book 4 and delete
|
||||
b5, err := dq.GetBook(ctx, int32(bookThreeID))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := dq.DeleteBook(ctx, b5.BookID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
69
examples/booktest/mysql/models.go
Normal file
69
examples/booktest/mysql/models.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package booktest
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BooksBookType string
|
||||
|
||||
const (
|
||||
BooksBookTypeFICTION BooksBookType = "FICTION"
|
||||
BooksBookTypeNONFICTION BooksBookType = "NONFICTION"
|
||||
)
|
||||
|
||||
func (e *BooksBookType) Scan(src any) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = BooksBookType(s)
|
||||
case string:
|
||||
*e = BooksBookType(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for BooksBookType: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullBooksBookType struct {
|
||||
BooksBookType BooksBookType
|
||||
Valid bool // Valid is true if BooksBookType is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullBooksBookType) Scan(value any) error {
|
||||
if value == nil {
|
||||
ns.BooksBookType, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.BooksBookType.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullBooksBookType) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.BooksBookType), nil
|
||||
}
|
||||
|
||||
type Author struct {
|
||||
AuthorID int32
|
||||
Name string
|
||||
}
|
||||
|
||||
type Book struct {
|
||||
BookID int32
|
||||
AuthorID int32
|
||||
Isbn string
|
||||
BookType BooksBookType
|
||||
Title string
|
||||
Yr int32
|
||||
Available time.Time
|
||||
Tags string
|
||||
}
|
||||
63
examples/booktest/mysql/query.sql
Normal file
63
examples/booktest/mysql/query.sql
Normal file
@@ -0,0 +1,63 @@
|
||||
/* name: GetAuthor :one */
|
||||
SELECT * FROM authors
|
||||
WHERE author_id = ?;
|
||||
|
||||
/* name: GetBook :one */
|
||||
SELECT * FROM books
|
||||
WHERE book_id = ?;
|
||||
|
||||
/* name: DeleteBook :exec */
|
||||
DELETE FROM books
|
||||
WHERE book_id = ?;
|
||||
|
||||
/* name: BooksByTitleYear :many */
|
||||
SELECT * FROM books
|
||||
WHERE title = ? AND yr = ?;
|
||||
|
||||
/* name: BooksByTags :many */
|
||||
SELECT
|
||||
book_id,
|
||||
title,
|
||||
name,
|
||||
isbn,
|
||||
tags
|
||||
FROM books
|
||||
LEFT JOIN authors ON books.author_id = authors.author_id
|
||||
WHERE tags = ?;
|
||||
|
||||
/* name: CreateAuthor :execresult */
|
||||
INSERT INTO authors (name) VALUES (?);
|
||||
|
||||
/* name: CreateBook :execresult */
|
||||
INSERT INTO books (
|
||||
author_id,
|
||||
isbn,
|
||||
book_type,
|
||||
title,
|
||||
yr,
|
||||
available,
|
||||
tags
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
);
|
||||
|
||||
/* name: UpdateBook :exec */
|
||||
UPDATE books
|
||||
SET title = ?, tags = ?
|
||||
WHERE book_id = ?;
|
||||
|
||||
/* name: UpdateBookISBN :exec */
|
||||
UPDATE books
|
||||
SET title = ?, tags = ?, isbn = ?
|
||||
WHERE book_id = ?;
|
||||
|
||||
/* name: DeleteAuthorBeforeYear :exec */
|
||||
DELETE FROM books
|
||||
WHERE yr < ? AND author_id = ?;
|
||||
-- WHERE yr < sqlc.arg(min_publish_year) AND author_id = ?;
|
||||
251
examples/booktest/mysql/query.sql.go
Normal file
251
examples/booktest/mysql/query.sql.go
Normal file
@@ -0,0 +1,251 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: query.sql
|
||||
|
||||
package booktest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
const booksByTags = `-- name: BooksByTags :many
|
||||
SELECT
|
||||
book_id,
|
||||
title,
|
||||
name,
|
||||
isbn,
|
||||
tags
|
||||
FROM books
|
||||
LEFT JOIN authors ON books.author_id = authors.author_id
|
||||
WHERE tags = ?
|
||||
`
|
||||
|
||||
type BooksByTagsRow struct {
|
||||
BookID int32
|
||||
Title string
|
||||
Name sql.NullString
|
||||
Isbn string
|
||||
Tags string
|
||||
}
|
||||
|
||||
func (q *Queries) BooksByTags(ctx context.Context, tags string) ([]BooksByTagsRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, booksByTags, tags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []BooksByTagsRow
|
||||
for rows.Next() {
|
||||
var i BooksByTagsRow
|
||||
if err := rows.Scan(
|
||||
&i.BookID,
|
||||
&i.Title,
|
||||
&i.Name,
|
||||
&i.Isbn,
|
||||
&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
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const booksByTitleYear = `-- name: BooksByTitleYear :many
|
||||
SELECT book_id, author_id, isbn, book_type, title, yr, available, tags FROM books
|
||||
WHERE title = ? AND yr = ?
|
||||
`
|
||||
|
||||
type BooksByTitleYearParams struct {
|
||||
Title string
|
||||
Yr int32
|
||||
}
|
||||
|
||||
func (q *Queries) BooksByTitleYear(ctx context.Context, arg BooksByTitleYearParams) ([]Book, error) {
|
||||
rows, err := q.db.QueryContext(ctx, booksByTitleYear, arg.Title, arg.Yr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Book
|
||||
for rows.Next() {
|
||||
var i Book
|
||||
if err := rows.Scan(
|
||||
&i.BookID,
|
||||
&i.AuthorID,
|
||||
&i.Isbn,
|
||||
&i.BookType,
|
||||
&i.Title,
|
||||
&i.Yr,
|
||||
&i.Available,
|
||||
&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
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const createAuthor = `-- name: CreateAuthor :execresult
|
||||
INSERT INTO authors (name) VALUES (?)
|
||||
`
|
||||
|
||||
func (q *Queries) CreateAuthor(ctx context.Context, name string) (sql.Result, error) {
|
||||
return q.db.ExecContext(ctx, createAuthor, name)
|
||||
}
|
||||
|
||||
const createBook = `-- name: CreateBook :execresult
|
||||
INSERT INTO books (
|
||||
author_id,
|
||||
isbn,
|
||||
book_type,
|
||||
title,
|
||||
yr,
|
||||
available,
|
||||
tags
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
)
|
||||
`
|
||||
|
||||
type CreateBookParams struct {
|
||||
AuthorID int32
|
||||
Isbn string
|
||||
BookType BooksBookType
|
||||
Title string
|
||||
Yr int32
|
||||
Available time.Time
|
||||
Tags string
|
||||
}
|
||||
|
||||
func (q *Queries) CreateBook(ctx context.Context, arg CreateBookParams) (sql.Result, error) {
|
||||
return q.db.ExecContext(ctx, createBook,
|
||||
arg.AuthorID,
|
||||
arg.Isbn,
|
||||
arg.BookType,
|
||||
arg.Title,
|
||||
arg.Yr,
|
||||
arg.Available,
|
||||
arg.Tags,
|
||||
)
|
||||
}
|
||||
|
||||
const deleteAuthorBeforeYear = `-- name: DeleteAuthorBeforeYear :exec
|
||||
DELETE FROM books
|
||||
WHERE yr < ? AND author_id = ?
|
||||
`
|
||||
|
||||
type DeleteAuthorBeforeYearParams struct {
|
||||
Yr int32
|
||||
AuthorID int32
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteAuthorBeforeYear(ctx context.Context, arg DeleteAuthorBeforeYearParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteAuthorBeforeYear, arg.Yr, arg.AuthorID)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteBook = `-- name: DeleteBook :exec
|
||||
DELETE FROM books
|
||||
WHERE book_id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteBook(ctx context.Context, bookID int32) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteBook, bookID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAuthor = `-- name: GetAuthor :one
|
||||
SELECT author_id, name FROM authors
|
||||
WHERE author_id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) GetAuthor(ctx context.Context, authorID int32) (Author, error) {
|
||||
row := q.db.QueryRowContext(ctx, getAuthor, authorID)
|
||||
var i Author
|
||||
err := row.Scan(&i.AuthorID, &i.Name)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getBook = `-- name: GetBook :one
|
||||
SELECT book_id, author_id, isbn, book_type, title, yr, available, tags FROM books
|
||||
WHERE book_id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) GetBook(ctx context.Context, bookID int32) (Book, error) {
|
||||
row := q.db.QueryRowContext(ctx, getBook, bookID)
|
||||
var i Book
|
||||
err := row.Scan(
|
||||
&i.BookID,
|
||||
&i.AuthorID,
|
||||
&i.Isbn,
|
||||
&i.BookType,
|
||||
&i.Title,
|
||||
&i.Yr,
|
||||
&i.Available,
|
||||
&i.Tags,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateBook = `-- name: UpdateBook :exec
|
||||
UPDATE books
|
||||
SET title = ?, tags = ?
|
||||
WHERE book_id = ?
|
||||
`
|
||||
|
||||
type UpdateBookParams struct {
|
||||
Title string
|
||||
Tags string
|
||||
BookID int32
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateBook(ctx context.Context, arg UpdateBookParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateBook, arg.Title, arg.Tags, arg.BookID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateBookISBN = `-- name: UpdateBookISBN :exec
|
||||
UPDATE books
|
||||
SET title = ?, tags = ?, isbn = ?
|
||||
WHERE book_id = ?
|
||||
`
|
||||
|
||||
type UpdateBookISBNParams struct {
|
||||
Title string
|
||||
Tags string
|
||||
Isbn string
|
||||
BookID int32
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateBookISBN(ctx context.Context, arg UpdateBookISBNParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateBookISBN,
|
||||
arg.Title,
|
||||
arg.Tags,
|
||||
arg.Isbn,
|
||||
arg.BookID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
26
examples/booktest/mysql/schema.sql
Normal file
26
examples/booktest/mysql/schema.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
CREATE TABLE authors (
|
||||
author_id integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
name text NOT NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE INDEX authors_name_idx ON authors(name(255));
|
||||
|
||||
CREATE TABLE books (
|
||||
book_id integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
author_id integer NOT NULL,
|
||||
isbn varchar(255) NOT NULL DEFAULT '' UNIQUE,
|
||||
book_type ENUM('FICTION', 'NONFICTION') NOT NULL DEFAULT 'FICTION',
|
||||
title text NOT NULL,
|
||||
yr integer NOT NULL DEFAULT 2000,
|
||||
available datetime NOT NULL DEFAULT NOW(),
|
||||
tags text NOT NULL
|
||||
-- CONSTRAINT FOREIGN KEY (author_id) REFERENCES authors(author_id)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE INDEX books_title_idx ON books(title(255), yr);
|
||||
|
||||
/*
|
||||
CREATE FUNCTION say_hello(s text) RETURNS text
|
||||
DETERMINISTIC
|
||||
RETURN CONCAT('hello ', s);
|
||||
*/
|
||||
32
examples/booktest/postgresql/db.go
Normal file
32
examples/booktest/postgresql/db.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package booktest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
Exec(context.Context, string, ...any) (pgconn.CommandTag, error)
|
||||
Query(context.Context, string, ...any) (pgx.Rows, error)
|
||||
QueryRow(context.Context, string, ...any) 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,
|
||||
}
|
||||
}
|
||||
174
examples/booktest/postgresql/db_test.go
Normal file
174
examples/booktest/postgresql/db_test.go
Normal file
@@ -0,0 +1,174 @@
|
||||
//go:build examples
|
||||
|
||||
package booktest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/sqltest/local"
|
||||
)
|
||||
|
||||
func TestBooks(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
uri := local.PostgreSQL(t, []string{"schema.sql"})
|
||||
db, err := pgx.Connect(ctx, uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close(ctx)
|
||||
|
||||
dq := New(db)
|
||||
|
||||
// create an author
|
||||
a, err := dq.CreateAuthor(ctx, "Unknown Master")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// create transaction
|
||||
tx, err := db.Begin(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tq := dq.WithTx(tx)
|
||||
|
||||
// save first book
|
||||
now := pgtype.Timestamptz{Time: time.Now(), Valid: true}
|
||||
_, err = tq.CreateBook(ctx, CreateBookParams{
|
||||
AuthorID: a.AuthorID,
|
||||
Isbn: "1",
|
||||
Title: "my book title",
|
||||
BookType: BookTypeFICTION,
|
||||
Year: 2016,
|
||||
Available: now,
|
||||
Tags: []string{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// save second book
|
||||
b1, err := tq.CreateBook(ctx, CreateBookParams{
|
||||
AuthorID: a.AuthorID,
|
||||
Isbn: "2",
|
||||
Title: "the second book",
|
||||
BookType: BookTypeFICTION,
|
||||
Year: 2016,
|
||||
Available: now,
|
||||
Tags: []string{"cool", "unique"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// update the title and tags
|
||||
err = tq.UpdateBook(ctx, UpdateBookParams{
|
||||
BookID: b1.BookID,
|
||||
Title: "changed second title",
|
||||
Tags: []string{"cool", "disastor"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// save third book
|
||||
_, err = tq.CreateBook(ctx, CreateBookParams{
|
||||
AuthorID: a.AuthorID,
|
||||
Isbn: "3",
|
||||
Title: "the third book",
|
||||
BookType: BookTypeFICTION,
|
||||
Year: 2001,
|
||||
Available: now,
|
||||
Tags: []string{"cool"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// save fourth book
|
||||
b3, err := tq.CreateBook(ctx, CreateBookParams{
|
||||
AuthorID: a.AuthorID,
|
||||
Isbn: "4",
|
||||
Title: "4th place finisher",
|
||||
BookType: BookTypeNONFICTION,
|
||||
Year: 2011,
|
||||
Available: now,
|
||||
Tags: []string{"other"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// tx commit
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// upsert, changing ISBN and title
|
||||
err = dq.UpdateBookISBN(ctx, UpdateBookISBNParams{
|
||||
BookID: b3.BookID,
|
||||
Isbn: "NEW ISBN",
|
||||
Title: "never ever gonna finish, a quatrain",
|
||||
Tags: []string{"someother"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// retrieve first book
|
||||
books0, err := dq.BooksByTitleYear(ctx, BooksByTitleYearParams{
|
||||
Title: "my book title",
|
||||
Year: 2016,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, book := range books0 {
|
||||
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)
|
||||
}
|
||||
t.Logf("Book %d author: %s\n", book.BookID, author.Name)
|
||||
}
|
||||
|
||||
// find a book with either "cool" or "other" tag
|
||||
t.Logf("---------\nTag search results:\n")
|
||||
res, err := dq.BooksByTags(ctx, []string{"cool", "other", "someother"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, ab := range res {
|
||||
t.Logf("Book %d: '%s', Author: '%s', ISBN: '%s' Tags: '%v'\n", ab.BookID, ab.Title, ab.Name.String, ab.Isbn, ab.Tags)
|
||||
}
|
||||
|
||||
// call function
|
||||
pgText, err := dq.SayHello(ctx, "world")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
str, err := pgText.Value()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if str != "hello world" {
|
||||
t.Fatal("expected function result to be \"hello world\". actual:", str)
|
||||
}
|
||||
|
||||
// get book 4 and delete
|
||||
b5, err := dq.GetBook(ctx, b3.BookID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := dq.DeleteBook(ctx, b5.BookID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
70
examples/booktest/postgresql/models.go
Normal file
70
examples/booktest/postgresql/models.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package booktest
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type BookType string
|
||||
|
||||
const (
|
||||
BookTypeFICTION BookType = "FICTION"
|
||||
BookTypeNONFICTION BookType = "NONFICTION"
|
||||
)
|
||||
|
||||
func (e *BookType) Scan(src any) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = BookType(s)
|
||||
case string:
|
||||
*e = BookType(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for BookType: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullBookType struct {
|
||||
BookType BookType
|
||||
Valid bool // Valid is true if BookType is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullBookType) Scan(value any) error {
|
||||
if value == nil {
|
||||
ns.BookType, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.BookType.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullBookType) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.BookType), nil
|
||||
}
|
||||
|
||||
type Author struct {
|
||||
AuthorID int32
|
||||
Name string
|
||||
}
|
||||
|
||||
type Book struct {
|
||||
BookID int32
|
||||
AuthorID int32
|
||||
Isbn string
|
||||
BookType BookType
|
||||
Title string
|
||||
Year int32
|
||||
Available pgtype.Timestamptz
|
||||
Tags []string
|
||||
}
|
||||
63
examples/booktest/postgresql/query.sql
Normal file
63
examples/booktest/postgresql/query.sql
Normal file
@@ -0,0 +1,63 @@
|
||||
-- name: GetAuthor :one
|
||||
SELECT * FROM authors
|
||||
WHERE author_id = $1;
|
||||
|
||||
-- name: GetBook :one
|
||||
SELECT * FROM books
|
||||
WHERE book_id = $1;
|
||||
|
||||
-- name: DeleteBook :exec
|
||||
DELETE FROM books
|
||||
WHERE book_id = $1;
|
||||
|
||||
-- name: BooksByTitleYear :many
|
||||
SELECT * FROM books
|
||||
WHERE title = $1 AND year = $2;
|
||||
|
||||
-- name: BooksByTags :many
|
||||
SELECT
|
||||
book_id,
|
||||
title,
|
||||
name,
|
||||
isbn,
|
||||
tags
|
||||
FROM books
|
||||
LEFT JOIN authors ON books.author_id = authors.author_id
|
||||
WHERE tags && $1::varchar[];
|
||||
|
||||
-- name: CreateAuthor :one
|
||||
INSERT INTO authors (name) VALUES ($1)
|
||||
RETURNING *;
|
||||
|
||||
-- name: CreateBook :one
|
||||
INSERT INTO books (
|
||||
author_id,
|
||||
isbn,
|
||||
book_type,
|
||||
title,
|
||||
year,
|
||||
available,
|
||||
tags
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateBook :exec
|
||||
UPDATE books
|
||||
SET title = $1, tags = $2
|
||||
WHERE book_id = $3;
|
||||
|
||||
-- name: UpdateBookISBN :exec
|
||||
UPDATE books
|
||||
SET title = $1, tags = $2, isbn = $4
|
||||
WHERE book_id = $3;
|
||||
|
||||
-- name: SayHello :one
|
||||
select * from say_hello($1);
|
||||
258
examples/booktest/postgresql/query.sql.go
Normal file
258
examples/booktest/postgresql/query.sql.go
Normal file
@@ -0,0 +1,258 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: query.sql
|
||||
|
||||
package booktest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const booksByTags = `-- name: BooksByTags :many
|
||||
SELECT
|
||||
book_id,
|
||||
title,
|
||||
name,
|
||||
isbn,
|
||||
tags
|
||||
FROM books
|
||||
LEFT JOIN authors ON books.author_id = authors.author_id
|
||||
WHERE tags && $1::varchar[]
|
||||
`
|
||||
|
||||
type BooksByTagsRow struct {
|
||||
BookID int32
|
||||
Title string
|
||||
Name pgtype.Text
|
||||
Isbn string
|
||||
Tags []string
|
||||
}
|
||||
|
||||
func (q *Queries) BooksByTags(ctx context.Context, dollar_1 []string) ([]BooksByTagsRow, error) {
|
||||
rows, err := q.db.Query(ctx, booksByTags, dollar_1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []BooksByTagsRow
|
||||
for rows.Next() {
|
||||
var i BooksByTagsRow
|
||||
if err := rows.Scan(
|
||||
&i.BookID,
|
||||
&i.Title,
|
||||
&i.Name,
|
||||
&i.Isbn,
|
||||
&i.Tags,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const booksByTitleYear = `-- name: BooksByTitleYear :many
|
||||
SELECT book_id, author_id, isbn, book_type, title, year, available, tags FROM books
|
||||
WHERE title = $1 AND year = $2
|
||||
`
|
||||
|
||||
type BooksByTitleYearParams struct {
|
||||
Title string
|
||||
Year int32
|
||||
}
|
||||
|
||||
func (q *Queries) BooksByTitleYear(ctx context.Context, arg BooksByTitleYearParams) ([]Book, error) {
|
||||
rows, err := q.db.Query(ctx, booksByTitleYear, arg.Title, arg.Year)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Book
|
||||
for rows.Next() {
|
||||
var i Book
|
||||
if err := rows.Scan(
|
||||
&i.BookID,
|
||||
&i.AuthorID,
|
||||
&i.Isbn,
|
||||
&i.BookType,
|
||||
&i.Title,
|
||||
&i.Year,
|
||||
&i.Available,
|
||||
&i.Tags,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const createAuthor = `-- name: CreateAuthor :one
|
||||
INSERT INTO authors (name) VALUES ($1)
|
||||
RETURNING author_id, name
|
||||
`
|
||||
|
||||
func (q *Queries) CreateAuthor(ctx context.Context, name string) (Author, error) {
|
||||
row := q.db.QueryRow(ctx, createAuthor, name)
|
||||
var i Author
|
||||
err := row.Scan(&i.AuthorID, &i.Name)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createBook = `-- name: CreateBook :one
|
||||
INSERT INTO books (
|
||||
author_id,
|
||||
isbn,
|
||||
book_type,
|
||||
title,
|
||||
year,
|
||||
available,
|
||||
tags
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7
|
||||
)
|
||||
RETURNING book_id, author_id, isbn, book_type, title, year, available, tags
|
||||
`
|
||||
|
||||
type CreateBookParams struct {
|
||||
AuthorID int32
|
||||
Isbn string
|
||||
BookType BookType
|
||||
Title string
|
||||
Year int32
|
||||
Available pgtype.Timestamptz
|
||||
Tags []string
|
||||
}
|
||||
|
||||
func (q *Queries) CreateBook(ctx context.Context, arg CreateBookParams) (Book, error) {
|
||||
row := q.db.QueryRow(ctx, createBook,
|
||||
arg.AuthorID,
|
||||
arg.Isbn,
|
||||
arg.BookType,
|
||||
arg.Title,
|
||||
arg.Year,
|
||||
arg.Available,
|
||||
arg.Tags,
|
||||
)
|
||||
var i Book
|
||||
err := row.Scan(
|
||||
&i.BookID,
|
||||
&i.AuthorID,
|
||||
&i.Isbn,
|
||||
&i.BookType,
|
||||
&i.Title,
|
||||
&i.Year,
|
||||
&i.Available,
|
||||
&i.Tags,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteBook = `-- name: DeleteBook :exec
|
||||
DELETE FROM books
|
||||
WHERE book_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteBook(ctx context.Context, bookID int32) error {
|
||||
_, err := q.db.Exec(ctx, deleteBook, bookID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAuthor = `-- name: GetAuthor :one
|
||||
SELECT author_id, name FROM authors
|
||||
WHERE author_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetAuthor(ctx context.Context, authorID int32) (Author, error) {
|
||||
row := q.db.QueryRow(ctx, getAuthor, authorID)
|
||||
var i Author
|
||||
err := row.Scan(&i.AuthorID, &i.Name)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getBook = `-- name: GetBook :one
|
||||
SELECT book_id, author_id, isbn, book_type, title, year, available, tags FROM books
|
||||
WHERE book_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetBook(ctx context.Context, bookID int32) (Book, error) {
|
||||
row := q.db.QueryRow(ctx, getBook, bookID)
|
||||
var i Book
|
||||
err := row.Scan(
|
||||
&i.BookID,
|
||||
&i.AuthorID,
|
||||
&i.Isbn,
|
||||
&i.BookType,
|
||||
&i.Title,
|
||||
&i.Year,
|
||||
&i.Available,
|
||||
&i.Tags,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const sayHello = `-- name: SayHello :one
|
||||
select say_hello from say_hello($1)
|
||||
`
|
||||
|
||||
func (q *Queries) SayHello(ctx context.Context, s string) (pgtype.Text, error) {
|
||||
row := q.db.QueryRow(ctx, sayHello, s)
|
||||
var say_hello pgtype.Text
|
||||
err := row.Scan(&say_hello)
|
||||
return say_hello, err
|
||||
}
|
||||
|
||||
const updateBook = `-- name: UpdateBook :exec
|
||||
UPDATE books
|
||||
SET title = $1, tags = $2
|
||||
WHERE book_id = $3
|
||||
`
|
||||
|
||||
type UpdateBookParams struct {
|
||||
Title string
|
||||
Tags []string
|
||||
BookID int32
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateBook(ctx context.Context, arg UpdateBookParams) error {
|
||||
_, err := q.db.Exec(ctx, updateBook, arg.Title, arg.Tags, arg.BookID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateBookISBN = `-- name: UpdateBookISBN :exec
|
||||
UPDATE books
|
||||
SET title = $1, tags = $2, isbn = $4
|
||||
WHERE book_id = $3
|
||||
`
|
||||
|
||||
type UpdateBookISBNParams struct {
|
||||
Title string
|
||||
Tags []string
|
||||
BookID int32
|
||||
Isbn string
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateBookISBN(ctx context.Context, arg UpdateBookISBNParams) error {
|
||||
_, err := q.db.Exec(ctx, updateBookISBN,
|
||||
arg.Title,
|
||||
arg.Tags,
|
||||
arg.BookID,
|
||||
arg.Isbn,
|
||||
)
|
||||
return err
|
||||
}
|
||||
32
examples/booktest/postgresql/schema.sql
Normal file
32
examples/booktest/postgresql/schema.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
CREATE TABLE authors (
|
||||
author_id SERIAL PRIMARY KEY,
|
||||
name text NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE INDEX authors_name_idx ON authors(name);
|
||||
|
||||
CREATE TYPE book_type AS ENUM (
|
||||
'FICTION',
|
||||
'NONFICTION'
|
||||
);
|
||||
|
||||
CREATE TABLE books (
|
||||
book_id SERIAL PRIMARY KEY,
|
||||
author_id integer NOT NULL REFERENCES authors(author_id),
|
||||
isbn text NOT NULL DEFAULT '' UNIQUE,
|
||||
book_type book_type NOT NULL DEFAULT 'FICTION',
|
||||
title text NOT NULL DEFAULT '',
|
||||
year integer NOT NULL DEFAULT 2000,
|
||||
available timestamp with time zone NOT NULL DEFAULT 'NOW()',
|
||||
tags varchar[] NOT NULL DEFAULT '{}'
|
||||
);
|
||||
|
||||
CREATE INDEX books_title_idx ON books(title, year);
|
||||
|
||||
CREATE FUNCTION say_hello(s text) RETURNS text AS $$
|
||||
BEGIN
|
||||
RETURN CONCAT('hello ', s);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE INDEX books_title_lower_idx ON books(title);
|
||||
51
examples/booktest/sqlc.json
Normal file
51
examples/booktest/sqlc.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"version": "1",
|
||||
"cloud": {
|
||||
"project": "01HAQMMECEYQYKFJN8MP16QC41"
|
||||
},
|
||||
"packages": [
|
||||
{
|
||||
"name": "booktest",
|
||||
"path": "postgresql",
|
||||
"schema": "postgresql/schema.sql",
|
||||
"queries": "postgresql/query.sql",
|
||||
"engine": "postgresql",
|
||||
"sql_package": "pgx/v5",
|
||||
"database": {
|
||||
"uri": "${VET_TEST_EXAMPLES_POSTGRES_BOOKTEST}"
|
||||
},
|
||||
"analyzer": {
|
||||
"database": false
|
||||
},
|
||||
"rules": [
|
||||
"sqlc/db-prepare"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "booktest",
|
||||
"path": "mysql",
|
||||
"schema": "mysql/schema.sql",
|
||||
"queries": "mysql/query.sql",
|
||||
"engine": "mysql",
|
||||
"database": {
|
||||
"uri": "${VET_TEST_EXAMPLES_MYSQL_BOOKTEST}"
|
||||
},
|
||||
"rules": [
|
||||
"sqlc/db-prepare"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "booktest",
|
||||
"path": "sqlite",
|
||||
"schema": "sqlite/schema.sql",
|
||||
"queries": "sqlite/query.sql",
|
||||
"engine": "sqlite",
|
||||
"database": {
|
||||
"uri": "file:booktest?mode=memory&cache=shared"
|
||||
},
|
||||
"rules": [
|
||||
"sqlc/db-prepare"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
31
examples/booktest/sqlite/db.go
Normal file
31
examples/booktest/sqlite/db.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package booktest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
ExecContext(context.Context, string, ...any) (sql.Result, error)
|
||||
PrepareContext(context.Context, string) (*sql.Stmt, error)
|
||||
QueryContext(context.Context, string, ...any) (*sql.Rows, error)
|
||||
QueryRowContext(context.Context, string, ...any) *sql.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
}
|
||||
}
|
||||
163
examples/booktest/sqlite/db_test.go
Normal file
163
examples/booktest/sqlite/db_test.go
Normal file
@@ -0,0 +1,163 @@
|
||||
//go:build examples
|
||||
|
||||
package booktest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/sqltest"
|
||||
)
|
||||
|
||||
// TODO: Enum is not yet supported
|
||||
const (
|
||||
BooksBookTypeFICTION string = "FICTION"
|
||||
BooksBookTypeNONFICTION string = "NONFICTION"
|
||||
)
|
||||
|
||||
func TestBooks(t *testing.T) {
|
||||
db, cleanup := sqltest.SQLite(t, []string{"schema.sql"})
|
||||
defer db.Close()
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
dq := New(db)
|
||||
|
||||
// create an author
|
||||
a, err := dq.CreateAuthor(ctx, "Unknown Master")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// create transaction
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tq := dq.WithTx(tx)
|
||||
|
||||
// save first book
|
||||
now := time.Now()
|
||||
_, err = tq.CreateBook(ctx, CreateBookParams{
|
||||
AuthorID: a.AuthorID,
|
||||
Isbn: "1",
|
||||
Title: "my book title",
|
||||
BookType: BooksBookTypeFICTION,
|
||||
Yr: 2016,
|
||||
Available: now,
|
||||
Tag: "",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// save second book
|
||||
b1, err := tq.CreateBook(ctx, CreateBookParams{
|
||||
AuthorID: a.AuthorID,
|
||||
Isbn: "2",
|
||||
Title: "the second book",
|
||||
BookType: BooksBookTypeFICTION,
|
||||
Yr: 2016,
|
||||
Available: now,
|
||||
Tag: "unique",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// update the title and tags
|
||||
err = tq.UpdateBook(ctx, UpdateBookParams{
|
||||
BookID: b1.BookID,
|
||||
Title: "changed second title",
|
||||
Tag: "disastor",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// save third book
|
||||
_, err = tq.CreateBook(ctx, CreateBookParams{
|
||||
AuthorID: a.AuthorID,
|
||||
Isbn: "3",
|
||||
Title: "the third book",
|
||||
BookType: BooksBookTypeFICTION,
|
||||
Yr: 2001,
|
||||
Available: now,
|
||||
Tag: "cool",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// save fourth book
|
||||
b3, err := tq.CreateBook(ctx, CreateBookParams{
|
||||
AuthorID: a.AuthorID,
|
||||
Isbn: "4",
|
||||
Title: "4th place finisher",
|
||||
BookType: BooksBookTypeFICTION,
|
||||
Yr: 2011,
|
||||
Available: now,
|
||||
Tag: "other",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// tx commit
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// upsert, changing ISBN and title
|
||||
err = dq.UpdateBookISBN(ctx, UpdateBookISBNParams{
|
||||
BookID: b3.BookID,
|
||||
Isbn: "NEW ISBN",
|
||||
Title: "never ever gonna finish, a quatrain",
|
||||
Tag: "someother",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// retrieve first book
|
||||
books0, err := dq.BooksByTitleYear(ctx, BooksByTitleYearParams{
|
||||
Title: "my book title",
|
||||
Yr: 2016,
|
||||
})
|
||||
if err != nil {
|
||||
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))
|
||||
author, err := dq.GetAuthor(ctx, book.AuthorID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("Book %d author: %s\n", book.BookID, author.Name)
|
||||
}
|
||||
|
||||
// find a book with either "cool" or "other" or "someother" tag
|
||||
t.Logf("---------\nTag search results:\n")
|
||||
res, err := dq.BooksByTags(ctx, []string{"cool", "other", "someother"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, ab := range res {
|
||||
t.Logf("Book %d: '%s', Author: '%s', ISBN: '%s' Tag: '%v'\n", ab.BookID, ab.Title, ab.Name.String, ab.Isbn, ab.Tag)
|
||||
}
|
||||
|
||||
// TODO: call say_hello(varchar)
|
||||
|
||||
// get book 4 and delete
|
||||
b5, err := dq.GetBook(ctx, b3.BookID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := dq.DeleteBook(ctx, b5.BookID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
25
examples/booktest/sqlite/models.go
Normal file
25
examples/booktest/sqlite/models.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package booktest
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Author struct {
|
||||
AuthorID int64
|
||||
Name string
|
||||
}
|
||||
|
||||
type Book struct {
|
||||
BookID int64
|
||||
AuthorID int64
|
||||
Isbn string
|
||||
BookType string
|
||||
Title string
|
||||
Yr int64
|
||||
Available time.Time
|
||||
Tag string
|
||||
}
|
||||
64
examples/booktest/sqlite/query.sql
Normal file
64
examples/booktest/sqlite/query.sql
Normal file
@@ -0,0 +1,64 @@
|
||||
/* name: GetAuthor :one */
|
||||
SELECT * FROM authors
|
||||
WHERE author_id = ?;
|
||||
|
||||
/* name: GetBook :one */
|
||||
SELECT * FROM books
|
||||
WHERE book_id = ?;
|
||||
|
||||
/* name: DeleteBook :exec */
|
||||
DELETE FROM books
|
||||
WHERE book_id = ?;
|
||||
|
||||
/* name: BooksByTitleYear :many */
|
||||
SELECT * FROM books
|
||||
WHERE title = ? AND yr = ?;
|
||||
|
||||
/* name: BooksByTags :many */
|
||||
SELECT
|
||||
book_id,
|
||||
title,
|
||||
name,
|
||||
isbn,
|
||||
tag
|
||||
FROM books
|
||||
LEFT JOIN authors ON books.author_id = authors.author_id
|
||||
WHERE tag IN (sqlc.slice(tags));
|
||||
|
||||
/* name: CreateAuthor :one */
|
||||
INSERT INTO authors (name) VALUES (?)
|
||||
RETURNING *;
|
||||
|
||||
/* name: CreateBook :one */
|
||||
INSERT INTO books (
|
||||
author_id,
|
||||
isbn,
|
||||
book_type,
|
||||
title,
|
||||
yr,
|
||||
available,
|
||||
tag
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
/* name: UpdateBook :exec */
|
||||
UPDATE books
|
||||
SET title = ?1, tag = ?2
|
||||
WHERE book_id = ?3;
|
||||
|
||||
/* name: UpdateBookISBN :exec */
|
||||
UPDATE books
|
||||
SET title = ?1, tag = ?2, isbn = ?4
|
||||
WHERE book_id = ?3;
|
||||
|
||||
/* name: DeleteAuthorBeforeYear :exec */
|
||||
DELETE FROM books
|
||||
WHERE yr < ? AND author_id = ?;
|
||||
279
examples/booktest/sqlite/query.sql.go
Normal file
279
examples/booktest/sqlite/query.sql.go
Normal file
@@ -0,0 +1,279 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: query.sql
|
||||
|
||||
package booktest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const booksByTags = `-- name: BooksByTags :many
|
||||
SELECT
|
||||
book_id,
|
||||
title,
|
||||
name,
|
||||
isbn,
|
||||
tag
|
||||
FROM books
|
||||
LEFT JOIN authors ON books.author_id = authors.author_id
|
||||
WHERE tag IN (/*SLICE:tags*/?)
|
||||
`
|
||||
|
||||
type BooksByTagsRow struct {
|
||||
BookID int64
|
||||
Title string
|
||||
Name sql.NullString
|
||||
Isbn string
|
||||
Tag string
|
||||
}
|
||||
|
||||
func (q *Queries) BooksByTags(ctx context.Context, tags []string) ([]BooksByTagsRow, error) {
|
||||
query := booksByTags
|
||||
var queryParams []any
|
||||
if len(tags) > 0 {
|
||||
for _, v := range tags {
|
||||
queryParams = append(queryParams, v)
|
||||
}
|
||||
query = strings.Replace(query, "/*SLICE:tags*/?", strings.Repeat(",?", len(tags))[1:], 1)
|
||||
} else {
|
||||
query = strings.Replace(query, "/*SLICE:tags*/?", "NULL", 1)
|
||||
}
|
||||
rows, err := q.db.QueryContext(ctx, query, queryParams...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []BooksByTagsRow
|
||||
for rows.Next() {
|
||||
var i BooksByTagsRow
|
||||
if err := rows.Scan(
|
||||
&i.BookID,
|
||||
&i.Title,
|
||||
&i.Name,
|
||||
&i.Isbn,
|
||||
&i.Tag,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const booksByTitleYear = `-- name: BooksByTitleYear :many
|
||||
SELECT book_id, author_id, isbn, book_type, title, yr, available, tag FROM books
|
||||
WHERE title = ? AND yr = ?
|
||||
`
|
||||
|
||||
type BooksByTitleYearParams struct {
|
||||
Title string
|
||||
Yr int64
|
||||
}
|
||||
|
||||
func (q *Queries) BooksByTitleYear(ctx context.Context, arg BooksByTitleYearParams) ([]Book, error) {
|
||||
rows, err := q.db.QueryContext(ctx, booksByTitleYear, arg.Title, arg.Yr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Book
|
||||
for rows.Next() {
|
||||
var i Book
|
||||
if err := rows.Scan(
|
||||
&i.BookID,
|
||||
&i.AuthorID,
|
||||
&i.Isbn,
|
||||
&i.BookType,
|
||||
&i.Title,
|
||||
&i.Yr,
|
||||
&i.Available,
|
||||
&i.Tag,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const createAuthor = `-- name: CreateAuthor :one
|
||||
INSERT INTO authors (name) VALUES (?)
|
||||
RETURNING author_id, name
|
||||
`
|
||||
|
||||
func (q *Queries) CreateAuthor(ctx context.Context, name string) (Author, error) {
|
||||
row := q.db.QueryRowContext(ctx, createAuthor, name)
|
||||
var i Author
|
||||
err := row.Scan(&i.AuthorID, &i.Name)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createBook = `-- name: CreateBook :one
|
||||
INSERT INTO books (
|
||||
author_id,
|
||||
isbn,
|
||||
book_type,
|
||||
title,
|
||||
yr,
|
||||
available,
|
||||
tag
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
)
|
||||
RETURNING book_id, author_id, isbn, book_type, title, yr, available, tag
|
||||
`
|
||||
|
||||
type CreateBookParams struct {
|
||||
AuthorID int64
|
||||
Isbn string
|
||||
BookType string
|
||||
Title string
|
||||
Yr int64
|
||||
Available time.Time
|
||||
Tag string
|
||||
}
|
||||
|
||||
func (q *Queries) CreateBook(ctx context.Context, arg CreateBookParams) (Book, error) {
|
||||
row := q.db.QueryRowContext(ctx, createBook,
|
||||
arg.AuthorID,
|
||||
arg.Isbn,
|
||||
arg.BookType,
|
||||
arg.Title,
|
||||
arg.Yr,
|
||||
arg.Available,
|
||||
arg.Tag,
|
||||
)
|
||||
var i Book
|
||||
err := row.Scan(
|
||||
&i.BookID,
|
||||
&i.AuthorID,
|
||||
&i.Isbn,
|
||||
&i.BookType,
|
||||
&i.Title,
|
||||
&i.Yr,
|
||||
&i.Available,
|
||||
&i.Tag,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteAuthorBeforeYear = `-- name: DeleteAuthorBeforeYear :exec
|
||||
DELETE FROM books
|
||||
WHERE yr < ? AND author_id = ?
|
||||
`
|
||||
|
||||
type DeleteAuthorBeforeYearParams struct {
|
||||
Yr int64
|
||||
AuthorID int64
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteAuthorBeforeYear(ctx context.Context, arg DeleteAuthorBeforeYearParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteAuthorBeforeYear, arg.Yr, arg.AuthorID)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteBook = `-- name: DeleteBook :exec
|
||||
DELETE FROM books
|
||||
WHERE book_id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteBook(ctx context.Context, bookID int64) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteBook, bookID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAuthor = `-- name: GetAuthor :one
|
||||
SELECT author_id, name FROM authors
|
||||
WHERE author_id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) GetAuthor(ctx context.Context, authorID int64) (Author, error) {
|
||||
row := q.db.QueryRowContext(ctx, getAuthor, authorID)
|
||||
var i Author
|
||||
err := row.Scan(&i.AuthorID, &i.Name)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getBook = `-- name: GetBook :one
|
||||
SELECT book_id, author_id, isbn, book_type, title, yr, available, tag FROM books
|
||||
WHERE book_id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) GetBook(ctx context.Context, bookID int64) (Book, error) {
|
||||
row := q.db.QueryRowContext(ctx, getBook, bookID)
|
||||
var i Book
|
||||
err := row.Scan(
|
||||
&i.BookID,
|
||||
&i.AuthorID,
|
||||
&i.Isbn,
|
||||
&i.BookType,
|
||||
&i.Title,
|
||||
&i.Yr,
|
||||
&i.Available,
|
||||
&i.Tag,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateBook = `-- name: UpdateBook :exec
|
||||
UPDATE books
|
||||
SET title = ?1, tag = ?2
|
||||
WHERE book_id = ?3
|
||||
`
|
||||
|
||||
type UpdateBookParams struct {
|
||||
Title string
|
||||
Tag string
|
||||
BookID int64
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateBook(ctx context.Context, arg UpdateBookParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateBook, arg.Title, arg.Tag, arg.BookID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateBookISBN = `-- name: UpdateBookISBN :exec
|
||||
UPDATE books
|
||||
SET title = ?1, tag = ?2, isbn = ?4
|
||||
WHERE book_id = ?3
|
||||
`
|
||||
|
||||
type UpdateBookISBNParams struct {
|
||||
Title string
|
||||
Tag string
|
||||
BookID int64
|
||||
Isbn string
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateBookISBN(ctx context.Context, arg UpdateBookISBNParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateBookISBN,
|
||||
arg.Title,
|
||||
arg.Tag,
|
||||
arg.BookID,
|
||||
arg.Isbn,
|
||||
)
|
||||
return err
|
||||
}
|
||||
20
examples/booktest/sqlite/schema.sql
Normal file
20
examples/booktest/sqlite/schema.sql
Normal file
@@ -0,0 +1,20 @@
|
||||
CREATE TABLE authors (
|
||||
author_id integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
name text NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX authors_name_idx ON authors(name);
|
||||
|
||||
CREATE TABLE books (
|
||||
book_id integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
author_id integer NOT NULL,
|
||||
isbn varchar(255) NOT NULL DEFAULT '' UNIQUE,
|
||||
book_type text NOT NULL DEFAULT 'FICTION',
|
||||
title text NOT NULL,
|
||||
yr integer NOT NULL DEFAULT 2000,
|
||||
available datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
tag text NOT NULL,
|
||||
CHECK (book_type = 'FICTION' OR book_type = 'NONFICTION')
|
||||
);
|
||||
|
||||
CREATE INDEX books_title_idx ON books(title, yr);
|
||||
3
examples/jets/README.md
Normal file
3
examples/jets/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
This database schema and query selection is taken from the
|
||||
[SQLBoiler](https://github.com/volatiletech/sqlboiler#features--examples)
|
||||
README.
|
||||
32
examples/jets/postgresql/db.go
Normal file
32
examples/jets/postgresql/db.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package jets
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
Exec(context.Context, string, ...any) (pgconn.CommandTag, error)
|
||||
Query(context.Context, string, ...any) (pgx.Rows, error)
|
||||
QueryRow(context.Context, string, ...any) 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,
|
||||
}
|
||||
}
|
||||
28
examples/jets/postgresql/models.go
Normal file
28
examples/jets/postgresql/models.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package jets
|
||||
|
||||
type Jet struct {
|
||||
ID int32
|
||||
PilotID int32
|
||||
Age int32
|
||||
Name string
|
||||
Color string
|
||||
}
|
||||
|
||||
type Language struct {
|
||||
ID int32
|
||||
Language string
|
||||
}
|
||||
|
||||
type Pilot struct {
|
||||
ID int32
|
||||
Name string
|
||||
}
|
||||
|
||||
type PilotLanguage struct {
|
||||
PilotID int32
|
||||
LanguageID int32
|
||||
}
|
||||
8
examples/jets/postgresql/query-building.sql
Normal file
8
examples/jets/postgresql/query-building.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
-- name: CountPilots :one
|
||||
SELECT COUNT(*) FROM pilots;
|
||||
|
||||
-- name: ListPilots :many
|
||||
SELECT * FROM pilots LIMIT 5;
|
||||
|
||||
-- name: DeletePilot :exec
|
||||
DELETE FROM pilots WHERE id = $1;
|
||||
54
examples/jets/postgresql/query-building.sql.go
Normal file
54
examples/jets/postgresql/query-building.sql.go
Normal file
@@ -0,0 +1,54 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: query-building.sql
|
||||
|
||||
package jets
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const countPilots = `-- name: CountPilots :one
|
||||
SELECT COUNT(*) FROM pilots
|
||||
`
|
||||
|
||||
func (q *Queries) CountPilots(ctx context.Context) (int64, error) {
|
||||
row := q.db.QueryRow(ctx, countPilots)
|
||||
var count int64
|
||||
err := row.Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
|
||||
const deletePilot = `-- name: DeletePilot :exec
|
||||
DELETE FROM pilots WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeletePilot(ctx context.Context, id int32) error {
|
||||
_, err := q.db.Exec(ctx, deletePilot, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const listPilots = `-- name: ListPilots :many
|
||||
SELECT id, name FROM pilots LIMIT 5
|
||||
`
|
||||
|
||||
func (q *Queries) ListPilots(ctx context.Context) ([]Pilot, error) {
|
||||
rows, err := q.db.Query(ctx, listPilots)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Pilot
|
||||
for rows.Next() {
|
||||
var i Pilot
|
||||
if err := rows.Scan(&i.ID, &i.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
35
examples/jets/postgresql/schema.sql
Normal file
35
examples/jets/postgresql/schema.sql
Normal file
@@ -0,0 +1,35 @@
|
||||
CREATE TABLE pilots (
|
||||
id integer NOT NULL,
|
||||
name text NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE pilots ADD CONSTRAINT pilot_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE TABLE jets (
|
||||
id integer NOT NULL,
|
||||
pilot_id integer NOT NULL,
|
||||
age integer NOT NULL,
|
||||
name text NOT NULL,
|
||||
color text NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE jets ADD CONSTRAINT jet_pkey PRIMARY KEY (id);
|
||||
ALTER TABLE jets ADD CONSTRAINT jet_pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id);
|
||||
|
||||
CREATE TABLE languages (
|
||||
id integer NOT NULL,
|
||||
language text NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE languages ADD CONSTRAINT language_pkey PRIMARY KEY (id);
|
||||
|
||||
-- Join table
|
||||
CREATE TABLE pilot_languages (
|
||||
pilot_id integer NOT NULL,
|
||||
language_id integer NOT NULL
|
||||
);
|
||||
|
||||
-- Composite primary key
|
||||
ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_pkey PRIMARY KEY (pilot_id, language_id);
|
||||
ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id);
|
||||
ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_languages_fkey FOREIGN KEY (language_id) REFERENCES languages(id);
|
||||
25
examples/jets/sqlc.json
Normal file
25
examples/jets/sqlc.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"version": "1",
|
||||
"cloud": {
|
||||
"project": "01HAQMMECEYQYKFJN8MP16QC41"
|
||||
},
|
||||
"packages": [
|
||||
{
|
||||
"path": "postgresql",
|
||||
"name": "jets",
|
||||
"schema": "postgresql/schema.sql",
|
||||
"queries": "postgresql/query-building.sql",
|
||||
"engine": "postgresql",
|
||||
"sql_package": "pgx/v5",
|
||||
"database": {
|
||||
"uri": "${VET_TEST_EXAMPLES_POSTGRES_JETS}"
|
||||
},
|
||||
"analyzer": {
|
||||
"database": false
|
||||
},
|
||||
"rules": [
|
||||
"sqlc/db-prepare"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
88
examples/ondeck/mysql/city.sql.go
Normal file
88
examples/ondeck/mysql/city.sql.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: city.sql
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createCity = `-- name: CreateCity :exec
|
||||
INSERT INTO city (
|
||||
name,
|
||||
slug
|
||||
) VALUES (
|
||||
?,
|
||||
?
|
||||
)
|
||||
`
|
||||
|
||||
type CreateCityParams struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateCity(ctx context.Context, arg CreateCityParams) error {
|
||||
_, err := q.exec(ctx, q.createCityStmt, createCity, arg.Name, arg.Slug)
|
||||
return err
|
||||
}
|
||||
|
||||
const getCity = `-- name: GetCity :one
|
||||
SELECT slug, name
|
||||
FROM city
|
||||
WHERE slug = ?
|
||||
`
|
||||
|
||||
func (q *Queries) GetCity(ctx context.Context, slug string) (City, error) {
|
||||
row := q.queryRow(ctx, q.getCityStmt, getCity, slug)
|
||||
var i City
|
||||
err := row.Scan(&i.Slug, &i.Name)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listCities = `-- name: ListCities :many
|
||||
SELECT slug, name
|
||||
FROM city
|
||||
ORDER BY name
|
||||
`
|
||||
|
||||
func (q *Queries) ListCities(ctx context.Context) ([]City, error) {
|
||||
rows, err := q.query(ctx, q.listCitiesStmt, listCities)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []City
|
||||
for rows.Next() {
|
||||
var i City
|
||||
if err := rows.Scan(&i.Slug, &i.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateCityName = `-- name: UpdateCityName :exec
|
||||
UPDATE city
|
||||
SET name = ?
|
||||
WHERE slug = ?
|
||||
`
|
||||
|
||||
type UpdateCityNameParams struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCityName(ctx context.Context, arg UpdateCityNameParams) error {
|
||||
_, err := q.exec(ctx, q.updateCityNameStmt, updateCityName, arg.Name, arg.Slug)
|
||||
return err
|
||||
}
|
||||
178
examples/ondeck/mysql/db.go
Normal file
178
examples/ondeck/mysql/db.go
Normal file
@@ -0,0 +1,178 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
ExecContext(context.Context, string, ...any) (sql.Result, error)
|
||||
PrepareContext(context.Context, string) (*sql.Stmt, error)
|
||||
QueryContext(context.Context, string, ...any) (*sql.Rows, error)
|
||||
QueryRowContext(context.Context, string, ...any) *sql.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
|
||||
q := Queries{db: db}
|
||||
var err error
|
||||
if q.createCityStmt, err = db.PrepareContext(ctx, createCity); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query CreateCity: %w", err)
|
||||
}
|
||||
if q.createVenueStmt, err = db.PrepareContext(ctx, createVenue); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query CreateVenue: %w", err)
|
||||
}
|
||||
if q.deleteVenueStmt, err = db.PrepareContext(ctx, deleteVenue); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query DeleteVenue: %w", err)
|
||||
}
|
||||
if q.getCityStmt, err = db.PrepareContext(ctx, getCity); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query GetCity: %w", err)
|
||||
}
|
||||
if q.getVenueStmt, err = db.PrepareContext(ctx, getVenue); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query GetVenue: %w", err)
|
||||
}
|
||||
if q.listCitiesStmt, err = db.PrepareContext(ctx, listCities); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query ListCities: %w", err)
|
||||
}
|
||||
if q.listVenuesStmt, err = db.PrepareContext(ctx, listVenues); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query ListVenues: %w", err)
|
||||
}
|
||||
if q.updateCityNameStmt, err = db.PrepareContext(ctx, updateCityName); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query UpdateCityName: %w", err)
|
||||
}
|
||||
if q.updateVenueNameStmt, err = db.PrepareContext(ctx, updateVenueName); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query UpdateVenueName: %w", err)
|
||||
}
|
||||
if q.venueCountByCityStmt, err = db.PrepareContext(ctx, venueCountByCity); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query VenueCountByCity: %w", err)
|
||||
}
|
||||
return &q, nil
|
||||
}
|
||||
|
||||
func (q *Queries) Close() error {
|
||||
var err error
|
||||
if q.createCityStmt != nil {
|
||||
if cerr := q.createCityStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing createCityStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.createVenueStmt != nil {
|
||||
if cerr := q.createVenueStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing createVenueStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.deleteVenueStmt != nil {
|
||||
if cerr := q.deleteVenueStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing deleteVenueStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.getCityStmt != nil {
|
||||
if cerr := q.getCityStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing getCityStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.getVenueStmt != nil {
|
||||
if cerr := q.getVenueStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing getVenueStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.listCitiesStmt != nil {
|
||||
if cerr := q.listCitiesStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing listCitiesStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.listVenuesStmt != nil {
|
||||
if cerr := q.listVenuesStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing listVenuesStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.updateCityNameStmt != nil {
|
||||
if cerr := q.updateCityNameStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing updateCityNameStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.updateVenueNameStmt != nil {
|
||||
if cerr := q.updateVenueNameStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing updateVenueNameStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.venueCountByCityStmt != nil {
|
||||
if cerr := q.venueCountByCityStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing venueCountByCityStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (q *Queries) exec(ctx context.Context, stmt *sql.Stmt, query string, args ...any) (sql.Result, error) {
|
||||
switch {
|
||||
case stmt != nil && q.tx != nil:
|
||||
return q.tx.StmtContext(ctx, stmt).ExecContext(ctx, args...)
|
||||
case stmt != nil:
|
||||
return stmt.ExecContext(ctx, args...)
|
||||
default:
|
||||
return q.db.ExecContext(ctx, query, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Queries) query(ctx context.Context, stmt *sql.Stmt, query string, args ...any) (*sql.Rows, error) {
|
||||
switch {
|
||||
case stmt != nil && q.tx != nil:
|
||||
return q.tx.StmtContext(ctx, stmt).QueryContext(ctx, args...)
|
||||
case stmt != nil:
|
||||
return stmt.QueryContext(ctx, args...)
|
||||
default:
|
||||
return q.db.QueryContext(ctx, query, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Queries) queryRow(ctx context.Context, stmt *sql.Stmt, query string, args ...any) *sql.Row {
|
||||
switch {
|
||||
case stmt != nil && q.tx != nil:
|
||||
return q.tx.StmtContext(ctx, stmt).QueryRowContext(ctx, args...)
|
||||
case stmt != nil:
|
||||
return stmt.QueryRowContext(ctx, args...)
|
||||
default:
|
||||
return q.db.QueryRowContext(ctx, query, args...)
|
||||
}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
tx *sql.Tx
|
||||
createCityStmt *sql.Stmt
|
||||
createVenueStmt *sql.Stmt
|
||||
deleteVenueStmt *sql.Stmt
|
||||
getCityStmt *sql.Stmt
|
||||
getVenueStmt *sql.Stmt
|
||||
listCitiesStmt *sql.Stmt
|
||||
listVenuesStmt *sql.Stmt
|
||||
updateCityNameStmt *sql.Stmt
|
||||
updateVenueNameStmt *sql.Stmt
|
||||
venueCountByCityStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
tx: tx,
|
||||
createCityStmt: q.createCityStmt,
|
||||
createVenueStmt: q.createVenueStmt,
|
||||
deleteVenueStmt: q.deleteVenueStmt,
|
||||
getCityStmt: q.getCityStmt,
|
||||
getVenueStmt: q.getVenueStmt,
|
||||
listCitiesStmt: q.listCitiesStmt,
|
||||
listVenuesStmt: q.listVenuesStmt,
|
||||
updateCityNameStmt: q.updateCityNameStmt,
|
||||
updateVenueNameStmt: q.updateVenueNameStmt,
|
||||
venueCountByCityStmt: q.venueCountByCityStmt,
|
||||
}
|
||||
}
|
||||
172
examples/ondeck/mysql/db_test.go
Normal file
172
examples/ondeck/mysql/db_test.go
Normal file
@@ -0,0 +1,172 @@
|
||||
//go:build examples
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/sqltest/local"
|
||||
)
|
||||
|
||||
func join(vals ...string) sql.NullString {
|
||||
if len(vals) == 0 {
|
||||
return sql.NullString{}
|
||||
}
|
||||
return sql.NullString{
|
||||
Valid: true,
|
||||
String: strings.Join(vals, ","),
|
||||
}
|
||||
}
|
||||
|
||||
func runOnDeckQueries(t *testing.T, q *Queries) {
|
||||
ctx := context.Background()
|
||||
|
||||
err := q.CreateCity(ctx, CreateCityParams{
|
||||
Slug: "san-francisco",
|
||||
Name: "San Francisco",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
city, err := q.GetCity(ctx, "san-francisco")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
venueResult, err := q.CreateVenue(ctx, CreateVenueParams{
|
||||
Slug: "the-fillmore",
|
||||
Name: "The Fillmore",
|
||||
City: city.Slug,
|
||||
SpotifyPlaylist: "spotify:uri",
|
||||
Status: VenueStatusOpen,
|
||||
Statuses: join(string(VenueStatusOpen), string(VenueStatusClosed)),
|
||||
Tags: join("rock", "punk"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
venueID, err := venueResult.LastInsertId()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
venue, err := q.GetVenue(ctx, GetVenueParams{
|
||||
Slug: "the-fillmore",
|
||||
City: city.Slug,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(venue.ID, uint64(venueID)); diff != "" {
|
||||
t.Errorf("venue ID mismatch:\n%s", diff)
|
||||
}
|
||||
|
||||
{
|
||||
actual, err := q.VenueCountByCity(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if diff := cmp.Diff(actual, []VenueCountByCityRow{
|
||||
{city.Slug, int64(1)},
|
||||
}); diff != "" {
|
||||
t.Errorf("venue count mismatch:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
actual, err := q.ListCities(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if diff := cmp.Diff(actual, []City{city}); diff != "" {
|
||||
t.Errorf("list city mismatch:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
actual, err := q.ListVenues(ctx, city.Slug)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if diff := cmp.Diff(actual, []Venue{venue}); diff != "" {
|
||||
t.Errorf("list venue mismatch:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
err := q.UpdateCityName(ctx, UpdateCityNameParams{
|
||||
Slug: city.Slug,
|
||||
Name: "SF",
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
expected := "Fillmore"
|
||||
err := q.UpdateVenueName(ctx, UpdateVenueNameParams{
|
||||
Slug: venue.Slug,
|
||||
Name: expected,
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fresh, err := q.GetVenue(ctx, GetVenueParams{
|
||||
Slug: venue.Slug,
|
||||
City: city.Slug,
|
||||
})
|
||||
if diff := cmp.Diff(expected, fresh.Name); diff != "" {
|
||||
t.Errorf("update venue mismatch:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
err := q.DeleteVenue(ctx, DeleteVenueParams{
|
||||
Slug: venue.Slug,
|
||||
Slug_2: venue.Slug,
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepared(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
uri := local.MySQL(t, []string{"schema"})
|
||||
db, err := sql.Open("mysql", uri)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %s", uri, err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
q, err := Prepare(context.Background(), db)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
runOnDeckQueries(t, q)
|
||||
}
|
||||
|
||||
func TestQueries(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
uri := local.MySQL(t, []string{"schema"})
|
||||
db, err := sql.Open("mysql", uri)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %s", uri, err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
runOnDeckQueries(t, New(db))
|
||||
}
|
||||
75
examples/ondeck/mysql/models.go
Normal file
75
examples/ondeck/mysql/models.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type VenueStatus string
|
||||
|
||||
const (
|
||||
VenueStatusOpen VenueStatus = "open"
|
||||
VenueStatusClosed VenueStatus = "closed"
|
||||
)
|
||||
|
||||
func (e *VenueStatus) Scan(src any) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = VenueStatus(s)
|
||||
case string:
|
||||
*e = VenueStatus(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for VenueStatus: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullVenueStatus struct {
|
||||
VenueStatus VenueStatus `json:"venue_status"`
|
||||
Valid bool `json:"valid"` // Valid is true if VenueStatus is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullVenueStatus) Scan(value any) error {
|
||||
if value == nil {
|
||||
ns.VenueStatus, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.VenueStatus.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullVenueStatus) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.VenueStatus), nil
|
||||
}
|
||||
|
||||
type City struct {
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// Venues are places where muisc happens
|
||||
type Venue struct {
|
||||
ID uint64 `json:"id"`
|
||||
// Venues can be either open or closed
|
||||
Status VenueStatus `json:"status"`
|
||||
Statuses sql.NullString `json:"statuses"`
|
||||
// This value appears in public URLs
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
City string `json:"city"`
|
||||
SpotifyPlaylist string `json:"spotify_playlist"`
|
||||
SongkickID sql.NullString `json:"songkick_id"`
|
||||
Tags sql.NullString `json:"tags"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
25
examples/ondeck/mysql/querier.go
Normal file
25
examples/ondeck/mysql/querier.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type Querier interface {
|
||||
CreateCity(ctx context.Context, arg CreateCityParams) error
|
||||
CreateVenue(ctx context.Context, arg CreateVenueParams) (sql.Result, error)
|
||||
DeleteVenue(ctx context.Context, arg DeleteVenueParams) error
|
||||
GetCity(ctx context.Context, slug string) (City, error)
|
||||
GetVenue(ctx context.Context, arg GetVenueParams) (Venue, error)
|
||||
ListCities(ctx context.Context) ([]City, error)
|
||||
ListVenues(ctx context.Context, city string) ([]Venue, error)
|
||||
UpdateCityName(ctx context.Context, arg UpdateCityNameParams) error
|
||||
UpdateVenueName(ctx context.Context, arg UpdateVenueNameParams) error
|
||||
VenueCountByCity(ctx context.Context) ([]VenueCountByCityRow, error)
|
||||
}
|
||||
|
||||
var _ Querier = (*Queries)(nil)
|
||||
23
examples/ondeck/mysql/query/city.sql
Normal file
23
examples/ondeck/mysql/query/city.sql
Normal file
@@ -0,0 +1,23 @@
|
||||
/* name: ListCities :many */
|
||||
SELECT *
|
||||
FROM city
|
||||
ORDER BY name;
|
||||
|
||||
/* name: GetCity :one */
|
||||
SELECT *
|
||||
FROM city
|
||||
WHERE slug = ?;
|
||||
|
||||
/* name: CreateCity :exec */
|
||||
INSERT INTO city (
|
||||
name,
|
||||
slug
|
||||
) VALUES (
|
||||
?,
|
||||
?
|
||||
);
|
||||
|
||||
/* name: UpdateCityName :exec */
|
||||
UPDATE city
|
||||
SET name = ?
|
||||
WHERE slug = ?;
|
||||
48
examples/ondeck/mysql/query/venue.sql
Normal file
48
examples/ondeck/mysql/query/venue.sql
Normal file
@@ -0,0 +1,48 @@
|
||||
/* name: ListVenues :many */
|
||||
SELECT *
|
||||
FROM venue
|
||||
WHERE city = ?
|
||||
ORDER BY name;
|
||||
|
||||
/* name: DeleteVenue :exec */
|
||||
DELETE FROM venue
|
||||
WHERE slug = ? AND slug = ?;
|
||||
|
||||
/* name: GetVenue :one */
|
||||
SELECT *
|
||||
FROM venue
|
||||
WHERE slug = ? AND city = ?;
|
||||
|
||||
/* name: CreateVenue :execresult */
|
||||
INSERT INTO venue (
|
||||
slug,
|
||||
name,
|
||||
city,
|
||||
created_at,
|
||||
spotify_playlist,
|
||||
status,
|
||||
statuses,
|
||||
tags
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
NOW(),
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
);
|
||||
|
||||
/* name: UpdateVenueName :exec */
|
||||
UPDATE venue
|
||||
SET name = ?
|
||||
WHERE slug = ?;
|
||||
|
||||
/* name: VenueCountByCity :many */
|
||||
SELECT
|
||||
city,
|
||||
count(*)
|
||||
FROM venue
|
||||
GROUP BY 1
|
||||
ORDER BY 1;
|
||||
4
examples/ondeck/mysql/schema/0001_city.sql
Normal file
4
examples/ondeck/mysql/schema/0001_city.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
CREATE TABLE city (
|
||||
slug varchar(255) PRIMARY KEY,
|
||||
name text NOT NULL
|
||||
);
|
||||
12
examples/ondeck/mysql/schema/0002_venue.sql
Normal file
12
examples/ondeck/mysql/schema/0002_venue.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE venues (
|
||||
id SERIAL primary key,
|
||||
dropped text,
|
||||
status ENUM('open', 'closed') not null COMMENT 'Venues can be either open or closed',
|
||||
statuses text, -- status[],
|
||||
slug text not null COMMENT 'This value appears in public URLs',
|
||||
name varchar(255) not null,
|
||||
city varchar(255) not null references city(slug),
|
||||
spotify_playlist varchar(255) not null,
|
||||
songkick_id text,
|
||||
tags text -- text[]
|
||||
) COMMENT='Venues are places where muisc happens';
|
||||
3
examples/ondeck/mysql/schema/0003_add_column.sql
Normal file
3
examples/ondeck/mysql/schema/0003_add_column.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE venues RENAME TO venue;
|
||||
ALTER TABLE venue ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT NOW();
|
||||
ALTER TABLE venue DROP COLUMN dropped;
|
||||
193
examples/ondeck/mysql/venue.sql.go
Normal file
193
examples/ondeck/mysql/venue.sql.go
Normal file
@@ -0,0 +1,193 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: venue.sql
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
const createVenue = `-- name: CreateVenue :execresult
|
||||
INSERT INTO venue (
|
||||
slug,
|
||||
name,
|
||||
city,
|
||||
created_at,
|
||||
spotify_playlist,
|
||||
status,
|
||||
statuses,
|
||||
tags
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
NOW(),
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
)
|
||||
`
|
||||
|
||||
type CreateVenueParams struct {
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
City string `json:"city"`
|
||||
SpotifyPlaylist string `json:"spotify_playlist"`
|
||||
Status VenueStatus `json:"status"`
|
||||
Statuses sql.NullString `json:"statuses"`
|
||||
Tags sql.NullString `json:"tags"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateVenue(ctx context.Context, arg CreateVenueParams) (sql.Result, error) {
|
||||
return q.exec(ctx, q.createVenueStmt, createVenue,
|
||||
arg.Slug,
|
||||
arg.Name,
|
||||
arg.City,
|
||||
arg.SpotifyPlaylist,
|
||||
arg.Status,
|
||||
arg.Statuses,
|
||||
arg.Tags,
|
||||
)
|
||||
}
|
||||
|
||||
const deleteVenue = `-- name: DeleteVenue :exec
|
||||
DELETE FROM venue
|
||||
WHERE slug = ? AND slug = ?
|
||||
`
|
||||
|
||||
type DeleteVenueParams struct {
|
||||
Slug string `json:"slug"`
|
||||
Slug_2 string `json:"slug_2"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteVenue(ctx context.Context, arg DeleteVenueParams) error {
|
||||
_, err := q.exec(ctx, q.deleteVenueStmt, deleteVenue, arg.Slug, arg.Slug_2)
|
||||
return err
|
||||
}
|
||||
|
||||
const getVenue = `-- name: GetVenue :one
|
||||
SELECT id, status, statuses, slug, name, city, spotify_playlist, songkick_id, tags, created_at
|
||||
FROM venue
|
||||
WHERE slug = ? AND city = ?
|
||||
`
|
||||
|
||||
type GetVenueParams struct {
|
||||
Slug string `json:"slug"`
|
||||
City string `json:"city"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetVenue(ctx context.Context, arg GetVenueParams) (Venue, error) {
|
||||
row := q.queryRow(ctx, q.getVenueStmt, getVenue, arg.Slug, arg.City)
|
||||
var i Venue
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Status,
|
||||
&i.Statuses,
|
||||
&i.Slug,
|
||||
&i.Name,
|
||||
&i.City,
|
||||
&i.SpotifyPlaylist,
|
||||
&i.SongkickID,
|
||||
&i.Tags,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listVenues = `-- name: ListVenues :many
|
||||
SELECT id, status, statuses, slug, name, city, spotify_playlist, songkick_id, tags, created_at
|
||||
FROM venue
|
||||
WHERE city = ?
|
||||
ORDER BY name
|
||||
`
|
||||
|
||||
func (q *Queries) ListVenues(ctx context.Context, city string) ([]Venue, error) {
|
||||
rows, err := q.query(ctx, q.listVenuesStmt, listVenues, city)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Venue
|
||||
for rows.Next() {
|
||||
var i Venue
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Status,
|
||||
&i.Statuses,
|
||||
&i.Slug,
|
||||
&i.Name,
|
||||
&i.City,
|
||||
&i.SpotifyPlaylist,
|
||||
&i.SongkickID,
|
||||
&i.Tags,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateVenueName = `-- name: UpdateVenueName :exec
|
||||
UPDATE venue
|
||||
SET name = ?
|
||||
WHERE slug = ?
|
||||
`
|
||||
|
||||
type UpdateVenueNameParams struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateVenueName(ctx context.Context, arg UpdateVenueNameParams) error {
|
||||
_, err := q.exec(ctx, q.updateVenueNameStmt, updateVenueName, arg.Name, arg.Slug)
|
||||
return err
|
||||
}
|
||||
|
||||
const venueCountByCity = `-- name: VenueCountByCity :many
|
||||
SELECT
|
||||
city,
|
||||
count(*)
|
||||
FROM venue
|
||||
GROUP BY 1
|
||||
ORDER BY 1
|
||||
`
|
||||
|
||||
type VenueCountByCityRow struct {
|
||||
City string `json:"city"`
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
func (q *Queries) VenueCountByCity(ctx context.Context) ([]VenueCountByCityRow, error) {
|
||||
rows, err := q.query(ctx, q.venueCountByCityStmt, venueCountByCity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []VenueCountByCityRow
|
||||
for rows.Next() {
|
||||
var i VenueCountByCityRow
|
||||
if err := rows.Scan(&i.City, &i.Count); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
95
examples/ondeck/postgresql/city.sql.go
Normal file
95
examples/ondeck/postgresql/city.sql.go
Normal file
@@ -0,0 +1,95 @@
|
||||
//go:build examples
|
||||
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: city.sql
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createCity = `-- name: CreateCity :one
|
||||
INSERT INTO city (
|
||||
name,
|
||||
slug
|
||||
) VALUES (
|
||||
$1,
|
||||
$2
|
||||
) RETURNING slug, name
|
||||
`
|
||||
|
||||
type CreateCityParams struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
}
|
||||
|
||||
// Create a new city. The slug must be unique.
|
||||
// This is the second line of the comment
|
||||
// This is the third line
|
||||
func (q *Queries) CreateCity(ctx context.Context, arg CreateCityParams) (City, error) {
|
||||
row := q.queryRow(ctx, q.createCityStmt, createCity, arg.Name, arg.Slug)
|
||||
var i City
|
||||
err := row.Scan(&i.Slug, &i.Name)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getCity = `-- name: GetCity :one
|
||||
SELECT slug, name
|
||||
FROM city
|
||||
WHERE slug = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetCity(ctx context.Context, slug string) (City, error) {
|
||||
row := q.queryRow(ctx, q.getCityStmt, getCity, slug)
|
||||
var i City
|
||||
err := row.Scan(&i.Slug, &i.Name)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listCities = `-- name: ListCities :many
|
||||
SELECT slug, name
|
||||
FROM city
|
||||
ORDER BY name
|
||||
`
|
||||
|
||||
func (q *Queries) ListCities(ctx context.Context) ([]City, error) {
|
||||
rows, err := q.query(ctx, q.listCitiesStmt, listCities)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []City
|
||||
for rows.Next() {
|
||||
var i City
|
||||
if err := rows.Scan(&i.Slug, &i.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateCityName = `-- name: UpdateCityName :exec
|
||||
UPDATE city
|
||||
SET name = $2
|
||||
WHERE slug = $1
|
||||
`
|
||||
|
||||
type UpdateCityNameParams struct {
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCityName(ctx context.Context, arg UpdateCityNameParams) error {
|
||||
_, err := q.exec(ctx, q.updateCityNameStmt, updateCityName, arg.Slug, arg.Name)
|
||||
return err
|
||||
}
|
||||
180
examples/ondeck/postgresql/db.go
Normal file
180
examples/ondeck/postgresql/db.go
Normal file
@@ -0,0 +1,180 @@
|
||||
//go:build examples
|
||||
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
ExecContext(context.Context, string, ...any) (sql.Result, error)
|
||||
PrepareContext(context.Context, string) (*sql.Stmt, error)
|
||||
QueryContext(context.Context, string, ...any) (*sql.Rows, error)
|
||||
QueryRowContext(context.Context, string, ...any) *sql.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
|
||||
q := Queries{db: db}
|
||||
var err error
|
||||
if q.createCityStmt, err = db.PrepareContext(ctx, createCity); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query CreateCity: %w", err)
|
||||
}
|
||||
if q.createVenueStmt, err = db.PrepareContext(ctx, createVenue); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query CreateVenue: %w", err)
|
||||
}
|
||||
if q.deleteVenueStmt, err = db.PrepareContext(ctx, deleteVenue); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query DeleteVenue: %w", err)
|
||||
}
|
||||
if q.getCityStmt, err = db.PrepareContext(ctx, getCity); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query GetCity: %w", err)
|
||||
}
|
||||
if q.getVenueStmt, err = db.PrepareContext(ctx, getVenue); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query GetVenue: %w", err)
|
||||
}
|
||||
if q.listCitiesStmt, err = db.PrepareContext(ctx, listCities); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query ListCities: %w", err)
|
||||
}
|
||||
if q.listVenuesStmt, err = db.PrepareContext(ctx, listVenues); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query ListVenues: %w", err)
|
||||
}
|
||||
if q.updateCityNameStmt, err = db.PrepareContext(ctx, updateCityName); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query UpdateCityName: %w", err)
|
||||
}
|
||||
if q.updateVenueNameStmt, err = db.PrepareContext(ctx, updateVenueName); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query UpdateVenueName: %w", err)
|
||||
}
|
||||
if q.venueCountByCityStmt, err = db.PrepareContext(ctx, venueCountByCity); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query VenueCountByCity: %w", err)
|
||||
}
|
||||
return &q, nil
|
||||
}
|
||||
|
||||
func (q *Queries) Close() error {
|
||||
var err error
|
||||
if q.createCityStmt != nil {
|
||||
if cerr := q.createCityStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing createCityStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.createVenueStmt != nil {
|
||||
if cerr := q.createVenueStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing createVenueStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.deleteVenueStmt != nil {
|
||||
if cerr := q.deleteVenueStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing deleteVenueStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.getCityStmt != nil {
|
||||
if cerr := q.getCityStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing getCityStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.getVenueStmt != nil {
|
||||
if cerr := q.getVenueStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing getVenueStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.listCitiesStmt != nil {
|
||||
if cerr := q.listCitiesStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing listCitiesStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.listVenuesStmt != nil {
|
||||
if cerr := q.listVenuesStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing listVenuesStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.updateCityNameStmt != nil {
|
||||
if cerr := q.updateCityNameStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing updateCityNameStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.updateVenueNameStmt != nil {
|
||||
if cerr := q.updateVenueNameStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing updateVenueNameStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.venueCountByCityStmt != nil {
|
||||
if cerr := q.venueCountByCityStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing venueCountByCityStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (q *Queries) exec(ctx context.Context, stmt *sql.Stmt, query string, args ...any) (sql.Result, error) {
|
||||
switch {
|
||||
case stmt != nil && q.tx != nil:
|
||||
return q.tx.StmtContext(ctx, stmt).ExecContext(ctx, args...)
|
||||
case stmt != nil:
|
||||
return stmt.ExecContext(ctx, args...)
|
||||
default:
|
||||
return q.db.ExecContext(ctx, query, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Queries) query(ctx context.Context, stmt *sql.Stmt, query string, args ...any) (*sql.Rows, error) {
|
||||
switch {
|
||||
case stmt != nil && q.tx != nil:
|
||||
return q.tx.StmtContext(ctx, stmt).QueryContext(ctx, args...)
|
||||
case stmt != nil:
|
||||
return stmt.QueryContext(ctx, args...)
|
||||
default:
|
||||
return q.db.QueryContext(ctx, query, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Queries) queryRow(ctx context.Context, stmt *sql.Stmt, query string, args ...any) *sql.Row {
|
||||
switch {
|
||||
case stmt != nil && q.tx != nil:
|
||||
return q.tx.StmtContext(ctx, stmt).QueryRowContext(ctx, args...)
|
||||
case stmt != nil:
|
||||
return stmt.QueryRowContext(ctx, args...)
|
||||
default:
|
||||
return q.db.QueryRowContext(ctx, query, args...)
|
||||
}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
tx *sql.Tx
|
||||
createCityStmt *sql.Stmt
|
||||
createVenueStmt *sql.Stmt
|
||||
deleteVenueStmt *sql.Stmt
|
||||
getCityStmt *sql.Stmt
|
||||
getVenueStmt *sql.Stmt
|
||||
listCitiesStmt *sql.Stmt
|
||||
listVenuesStmt *sql.Stmt
|
||||
updateCityNameStmt *sql.Stmt
|
||||
updateVenueNameStmt *sql.Stmt
|
||||
venueCountByCityStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
tx: tx,
|
||||
createCityStmt: q.createCityStmt,
|
||||
createVenueStmt: q.createVenueStmt,
|
||||
deleteVenueStmt: q.deleteVenueStmt,
|
||||
getCityStmt: q.getCityStmt,
|
||||
getVenueStmt: q.getVenueStmt,
|
||||
listCitiesStmt: q.listCitiesStmt,
|
||||
listVenuesStmt: q.listVenuesStmt,
|
||||
updateCityNameStmt: q.updateCityNameStmt,
|
||||
updateVenueNameStmt: q.updateVenueNameStmt,
|
||||
venueCountByCityStmt: q.venueCountByCityStmt,
|
||||
}
|
||||
}
|
||||
154
examples/ondeck/postgresql/db_test.go
Normal file
154
examples/ondeck/postgresql/db_test.go
Normal file
@@ -0,0 +1,154 @@
|
||||
//go:build examples
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
_ "github.com/lib/pq"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/sqltest/local"
|
||||
)
|
||||
|
||||
func runOnDeckQueries(t *testing.T, q *Queries) {
|
||||
ctx := context.Background()
|
||||
|
||||
city, err := q.CreateCity(ctx, CreateCityParams{
|
||||
Slug: "san-francisco",
|
||||
Name: "San Francisco",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
venueID, err := q.CreateVenue(ctx, CreateVenueParams{
|
||||
Slug: "the-fillmore",
|
||||
Name: "The Fillmore",
|
||||
City: city.Slug,
|
||||
SpotifyPlaylist: "spotify:uri",
|
||||
Status: StatusOpen,
|
||||
Statuses: []Status{StatusOpen, StatusClosed},
|
||||
Tags: []string{"rock", "punk"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
venue, err := q.GetVenue(ctx, GetVenueParams{
|
||||
Slug: "the-fillmore",
|
||||
City: city.Slug,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(venue.ID, venueID); diff != "" {
|
||||
t.Errorf("venue ID mismatch:\n%s", diff)
|
||||
}
|
||||
|
||||
{
|
||||
actual, err := q.GetCity(ctx, city.Slug)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if diff := cmp.Diff(actual, city); diff != "" {
|
||||
t.Errorf("get city mismatch:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
actual, err := q.VenueCountByCity(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if diff := cmp.Diff(actual, []VenueCountByCityRow{
|
||||
{city.Slug, 1},
|
||||
}); diff != "" {
|
||||
t.Errorf("venue county mismatch:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
actual, err := q.ListCities(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if diff := cmp.Diff(actual, []City{city}); diff != "" {
|
||||
t.Errorf("list city mismatch:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
actual, err := q.ListVenues(ctx, city.Slug)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if diff := cmp.Diff(actual, []Venue{venue}); diff != "" {
|
||||
t.Errorf("list venue mismatch:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
err := q.UpdateCityName(ctx, UpdateCityNameParams{
|
||||
Slug: city.Slug,
|
||||
Name: "SF",
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
id, err := q.UpdateVenueName(ctx, UpdateVenueNameParams{
|
||||
Slug: venue.Slug,
|
||||
Name: "Fillmore",
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if diff := cmp.Diff(id, venue.ID); diff != "" {
|
||||
t.Errorf("update venue mismatch:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
err := q.DeleteVenue(ctx, venue.Slug)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepared(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
uri := local.PostgreSQL(t, []string{"schema"})
|
||||
db, err := sql.Open("postgres", uri)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %s", uri, err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
q, err := Prepare(context.Background(), db)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
runOnDeckQueries(t, q)
|
||||
}
|
||||
|
||||
func TestQueries(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
uri := local.PostgreSQL(t, []string{"schema"})
|
||||
db, err := sql.Open("postgres", uri)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %s", uri, err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
runOnDeckQueries(t, New(db))
|
||||
}
|
||||
77
examples/ondeck/postgresql/models.go
Normal file
77
examples/ondeck/postgresql/models.go
Normal file
@@ -0,0 +1,77 @@
|
||||
//go:build examples
|
||||
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Venues can be either open or closed
|
||||
type Status string
|
||||
|
||||
const (
|
||||
StatusOpen Status = "op!en"
|
||||
StatusClosed Status = "clo@sed"
|
||||
)
|
||||
|
||||
func (e *Status) Scan(src any) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = Status(s)
|
||||
case string:
|
||||
*e = Status(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for Status: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullStatus struct {
|
||||
Status Status `json:"status"`
|
||||
Valid bool `json:"valid"` // Valid is true if Status is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullStatus) Scan(value any) error {
|
||||
if value == nil {
|
||||
ns.Status, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.Status.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullStatus) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.Status), nil
|
||||
}
|
||||
|
||||
type City struct {
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// Venues are places where muisc happens
|
||||
type Venue struct {
|
||||
ID int32 `json:"id"`
|
||||
Status Status `json:"status"`
|
||||
Statuses []Status `json:"statuses"`
|
||||
// This value appears in public URLs
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
City string `json:"city"`
|
||||
SpotifyPlaylist string `json:"spotify_playlist"`
|
||||
SongkickID sql.NullString `json:"songkick_id"`
|
||||
Tags []string `json:"tags"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
29
examples/ondeck/postgresql/querier.go
Normal file
29
examples/ondeck/postgresql/querier.go
Normal file
@@ -0,0 +1,29 @@
|
||||
//go:build examples
|
||||
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type Querier interface {
|
||||
// Create a new city. The slug must be unique.
|
||||
// This is the second line of the comment
|
||||
// This is the third line
|
||||
CreateCity(ctx context.Context, arg CreateCityParams) (City, error)
|
||||
CreateVenue(ctx context.Context, arg CreateVenueParams) (int32, error)
|
||||
DeleteVenue(ctx context.Context, slug string) error
|
||||
GetCity(ctx context.Context, slug string) (City, error)
|
||||
GetVenue(ctx context.Context, arg GetVenueParams) (Venue, error)
|
||||
ListCities(ctx context.Context) ([]City, error)
|
||||
ListVenues(ctx context.Context, city string) ([]Venue, error)
|
||||
UpdateCityName(ctx context.Context, arg UpdateCityNameParams) error
|
||||
UpdateVenueName(ctx context.Context, arg UpdateVenueNameParams) (int32, error)
|
||||
VenueCountByCity(ctx context.Context) ([]VenueCountByCityRow, error)
|
||||
}
|
||||
|
||||
var _ Querier = (*Queries)(nil)
|
||||
26
examples/ondeck/postgresql/query/city.sql
Normal file
26
examples/ondeck/postgresql/query/city.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
-- name: ListCities :many
|
||||
SELECT *
|
||||
FROM city
|
||||
ORDER BY name;
|
||||
|
||||
-- name: GetCity :one
|
||||
SELECT *
|
||||
FROM city
|
||||
WHERE slug = $1;
|
||||
|
||||
-- name: CreateCity :one
|
||||
-- Create a new city. The slug must be unique.
|
||||
-- This is the second line of the comment
|
||||
-- This is the third line
|
||||
INSERT INTO city (
|
||||
name,
|
||||
slug
|
||||
) VALUES (
|
||||
$1,
|
||||
$2
|
||||
) RETURNING *;
|
||||
|
||||
-- name: UpdateCityName :exec
|
||||
UPDATE city
|
||||
SET name = $2
|
||||
WHERE slug = $1;
|
||||
49
examples/ondeck/postgresql/query/venue.sql
Normal file
49
examples/ondeck/postgresql/query/venue.sql
Normal file
@@ -0,0 +1,49 @@
|
||||
-- name: ListVenues :many
|
||||
SELECT *
|
||||
FROM venue
|
||||
WHERE city = $1
|
||||
ORDER BY name;
|
||||
|
||||
-- name: DeleteVenue :exec
|
||||
DELETE FROM venue
|
||||
WHERE slug = $1 AND slug = $1;
|
||||
|
||||
-- name: GetVenue :one
|
||||
SELECT *
|
||||
FROM venue
|
||||
WHERE slug = $1 AND city = $2;
|
||||
|
||||
-- name: CreateVenue :one
|
||||
INSERT INTO venue (
|
||||
slug,
|
||||
name,
|
||||
city,
|
||||
created_at,
|
||||
spotify_playlist,
|
||||
status,
|
||||
statuses,
|
||||
tags
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
NOW(),
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7
|
||||
) RETURNING id;
|
||||
|
||||
-- name: UpdateVenueName :one
|
||||
UPDATE venue
|
||||
SET name = $2
|
||||
WHERE slug = $1
|
||||
RETURNING id;
|
||||
|
||||
-- name: VenueCountByCity :many
|
||||
SELECT
|
||||
city,
|
||||
count(*)
|
||||
FROM venue
|
||||
GROUP BY 1
|
||||
ORDER BY 1;
|
||||
4
examples/ondeck/postgresql/schema/0001_city.sql
Normal file
4
examples/ondeck/postgresql/schema/0001_city.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
CREATE TABLE city (
|
||||
slug text PRIMARY KEY,
|
||||
name text NOT NULL
|
||||
)
|
||||
18
examples/ondeck/postgresql/schema/0002_venue.sql
Normal file
18
examples/ondeck/postgresql/schema/0002_venue.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
CREATE TYPE status AS ENUM ('op!en', 'clo@sed');
|
||||
COMMENT ON TYPE status IS 'Venues can be either open or closed';
|
||||
|
||||
CREATE TABLE venues (
|
||||
id SERIAL primary key,
|
||||
dropped text,
|
||||
status status not null,
|
||||
statuses status[],
|
||||
slug text not null,
|
||||
name varchar(255) not null,
|
||||
city text not null references city(slug),
|
||||
spotify_playlist varchar not null,
|
||||
songkick_id text,
|
||||
tags text[]
|
||||
);
|
||||
COMMENT ON TABLE venues IS 'Venues are places where muisc happens';
|
||||
COMMENT ON COLUMN venues.slug IS 'This value appears in public URLs';
|
||||
|
||||
3
examples/ondeck/postgresql/schema/0003_add_column.sql
Normal file
3
examples/ondeck/postgresql/schema/0003_add_column.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE venues RENAME TO venue;
|
||||
ALTER TABLE venue ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT NOW();
|
||||
ALTER TABLE venue DROP COLUMN dropped;
|
||||
197
examples/ondeck/postgresql/venue.sql.go
Normal file
197
examples/ondeck/postgresql/venue.sql.go
Normal file
@@ -0,0 +1,197 @@
|
||||
//go:build examples
|
||||
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: venue.sql
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
const createVenue = `-- name: CreateVenue :one
|
||||
INSERT INTO venue (
|
||||
slug,
|
||||
name,
|
||||
city,
|
||||
created_at,
|
||||
spotify_playlist,
|
||||
status,
|
||||
statuses,
|
||||
tags
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
NOW(),
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7
|
||||
) RETURNING id
|
||||
`
|
||||
|
||||
type CreateVenueParams struct {
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
City string `json:"city"`
|
||||
SpotifyPlaylist string `json:"spotify_playlist"`
|
||||
Status Status `json:"status"`
|
||||
Statuses []Status `json:"statuses"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateVenue(ctx context.Context, arg CreateVenueParams) (int32, error) {
|
||||
row := q.queryRow(ctx, q.createVenueStmt, createVenue,
|
||||
arg.Slug,
|
||||
arg.Name,
|
||||
arg.City,
|
||||
arg.SpotifyPlaylist,
|
||||
arg.Status,
|
||||
pq.Array(arg.Statuses),
|
||||
pq.Array(arg.Tags),
|
||||
)
|
||||
var id int32
|
||||
err := row.Scan(&id)
|
||||
return id, err
|
||||
}
|
||||
|
||||
const deleteVenue = `-- name: DeleteVenue :exec
|
||||
DELETE FROM venue
|
||||
WHERE slug = $1 AND slug = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteVenue(ctx context.Context, slug string) error {
|
||||
_, err := q.exec(ctx, q.deleteVenueStmt, deleteVenue, slug)
|
||||
return err
|
||||
}
|
||||
|
||||
const getVenue = `-- name: GetVenue :one
|
||||
SELECT id, status, statuses, slug, name, city, spotify_playlist, songkick_id, tags, created_at
|
||||
FROM venue
|
||||
WHERE slug = $1 AND city = $2
|
||||
`
|
||||
|
||||
type GetVenueParams struct {
|
||||
Slug string `json:"slug"`
|
||||
City string `json:"city"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetVenue(ctx context.Context, arg GetVenueParams) (Venue, error) {
|
||||
row := q.queryRow(ctx, q.getVenueStmt, getVenue, arg.Slug, arg.City)
|
||||
var i Venue
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Status,
|
||||
pq.Array(&i.Statuses),
|
||||
&i.Slug,
|
||||
&i.Name,
|
||||
&i.City,
|
||||
&i.SpotifyPlaylist,
|
||||
&i.SongkickID,
|
||||
pq.Array(&i.Tags),
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listVenues = `-- name: ListVenues :many
|
||||
SELECT id, status, statuses, slug, name, city, spotify_playlist, songkick_id, tags, created_at
|
||||
FROM venue
|
||||
WHERE city = $1
|
||||
ORDER BY name
|
||||
`
|
||||
|
||||
func (q *Queries) ListVenues(ctx context.Context, city string) ([]Venue, error) {
|
||||
rows, err := q.query(ctx, q.listVenuesStmt, listVenues, city)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Venue
|
||||
for rows.Next() {
|
||||
var i Venue
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Status,
|
||||
pq.Array(&i.Statuses),
|
||||
&i.Slug,
|
||||
&i.Name,
|
||||
&i.City,
|
||||
&i.SpotifyPlaylist,
|
||||
&i.SongkickID,
|
||||
pq.Array(&i.Tags),
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateVenueName = `-- name: UpdateVenueName :one
|
||||
UPDATE venue
|
||||
SET name = $2
|
||||
WHERE slug = $1
|
||||
RETURNING id
|
||||
`
|
||||
|
||||
type UpdateVenueNameParams struct {
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateVenueName(ctx context.Context, arg UpdateVenueNameParams) (int32, error) {
|
||||
row := q.queryRow(ctx, q.updateVenueNameStmt, updateVenueName, arg.Slug, arg.Name)
|
||||
var id int32
|
||||
err := row.Scan(&id)
|
||||
return id, err
|
||||
}
|
||||
|
||||
const venueCountByCity = `-- name: VenueCountByCity :many
|
||||
SELECT
|
||||
city,
|
||||
count(*)
|
||||
FROM venue
|
||||
GROUP BY 1
|
||||
ORDER BY 1
|
||||
`
|
||||
|
||||
type VenueCountByCityRow struct {
|
||||
City string `json:"city"`
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
func (q *Queries) VenueCountByCity(ctx context.Context) ([]VenueCountByCityRow, error) {
|
||||
rows, err := q.query(ctx, q.venueCountByCityStmt, venueCountByCity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []VenueCountByCityRow
|
||||
for rows.Next() {
|
||||
var i VenueCountByCityRow
|
||||
if err := rows.Scan(&i.City, &i.Count); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
61
examples/ondeck/sqlc.json
Normal file
61
examples/ondeck/sqlc.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"version": "1",
|
||||
"cloud": {
|
||||
"project": "01HAQMMECEYQYKFJN8MP16QC41"
|
||||
},
|
||||
"packages": [
|
||||
{
|
||||
"path": "postgresql",
|
||||
"name": "ondeck",
|
||||
"schema": "postgresql/schema",
|
||||
"queries": "postgresql/query",
|
||||
"engine": "postgresql",
|
||||
"sql_package": "database/sql",
|
||||
"database": {
|
||||
"uri": "${VET_TEST_EXAMPLES_POSTGRES_ONDECK}"
|
||||
},
|
||||
"analyzer": {
|
||||
"database": false
|
||||
},
|
||||
"rules": [
|
||||
"sqlc/db-prepare"
|
||||
],
|
||||
"build_tags": "examples",
|
||||
"emit_json_tags": true,
|
||||
"emit_prepared_queries": true,
|
||||
"emit_interface": true
|
||||
},
|
||||
{
|
||||
"path": "mysql",
|
||||
"name": "ondeck",
|
||||
"schema": "mysql/schema",
|
||||
"queries": "mysql/query",
|
||||
"engine": "mysql",
|
||||
"database": {
|
||||
"uri": "${VET_TEST_EXAMPLES_MYSQL_ONDECK}"
|
||||
},
|
||||
"rules": [
|
||||
"sqlc/db-prepare"
|
||||
],
|
||||
"emit_json_tags": true,
|
||||
"emit_prepared_queries": true,
|
||||
"emit_interface": true
|
||||
},
|
||||
{
|
||||
"path": "sqlite",
|
||||
"name": "ondeck",
|
||||
"schema": "sqlite/schema",
|
||||
"queries": "sqlite/query",
|
||||
"engine": "sqlite",
|
||||
"database": {
|
||||
"uri": "file:ondeck?mode=memory&cache=shared"
|
||||
},
|
||||
"rules": [
|
||||
"sqlc/db-prepare"
|
||||
],
|
||||
"emit_json_tags": true,
|
||||
"emit_prepared_queries": true,
|
||||
"emit_interface": true
|
||||
}
|
||||
]
|
||||
}
|
||||
88
examples/ondeck/sqlite/city.sql.go
Normal file
88
examples/ondeck/sqlite/city.sql.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: city.sql
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createCity = `-- name: CreateCity :exec
|
||||
INSERT INTO city (
|
||||
name,
|
||||
slug
|
||||
) VALUES (
|
||||
?,
|
||||
?
|
||||
)
|
||||
`
|
||||
|
||||
type CreateCityParams struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateCity(ctx context.Context, arg CreateCityParams) error {
|
||||
_, err := q.exec(ctx, q.createCityStmt, createCity, arg.Name, arg.Slug)
|
||||
return err
|
||||
}
|
||||
|
||||
const getCity = `-- name: GetCity :one
|
||||
SELECT slug, name
|
||||
FROM city
|
||||
WHERE slug = ?
|
||||
`
|
||||
|
||||
func (q *Queries) GetCity(ctx context.Context, slug string) (City, error) {
|
||||
row := q.queryRow(ctx, q.getCityStmt, getCity, slug)
|
||||
var i City
|
||||
err := row.Scan(&i.Slug, &i.Name)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listCities = `-- name: ListCities :many
|
||||
SELECT slug, name
|
||||
FROM city
|
||||
ORDER BY name
|
||||
`
|
||||
|
||||
func (q *Queries) ListCities(ctx context.Context) ([]City, error) {
|
||||
rows, err := q.query(ctx, q.listCitiesStmt, listCities)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []City
|
||||
for rows.Next() {
|
||||
var i City
|
||||
if err := rows.Scan(&i.Slug, &i.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateCityName = `-- name: UpdateCityName :exec
|
||||
UPDATE city
|
||||
SET name = ?
|
||||
WHERE slug = ?
|
||||
`
|
||||
|
||||
type UpdateCityNameParams struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCityName(ctx context.Context, arg UpdateCityNameParams) error {
|
||||
_, err := q.exec(ctx, q.updateCityNameStmt, updateCityName, arg.Name, arg.Slug)
|
||||
return err
|
||||
}
|
||||
178
examples/ondeck/sqlite/db.go
Normal file
178
examples/ondeck/sqlite/db.go
Normal file
@@ -0,0 +1,178 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
ExecContext(context.Context, string, ...any) (sql.Result, error)
|
||||
PrepareContext(context.Context, string) (*sql.Stmt, error)
|
||||
QueryContext(context.Context, string, ...any) (*sql.Rows, error)
|
||||
QueryRowContext(context.Context, string, ...any) *sql.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
|
||||
q := Queries{db: db}
|
||||
var err error
|
||||
if q.createCityStmt, err = db.PrepareContext(ctx, createCity); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query CreateCity: %w", err)
|
||||
}
|
||||
if q.createVenueStmt, err = db.PrepareContext(ctx, createVenue); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query CreateVenue: %w", err)
|
||||
}
|
||||
if q.deleteVenueStmt, err = db.PrepareContext(ctx, deleteVenue); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query DeleteVenue: %w", err)
|
||||
}
|
||||
if q.getCityStmt, err = db.PrepareContext(ctx, getCity); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query GetCity: %w", err)
|
||||
}
|
||||
if q.getVenueStmt, err = db.PrepareContext(ctx, getVenue); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query GetVenue: %w", err)
|
||||
}
|
||||
if q.listCitiesStmt, err = db.PrepareContext(ctx, listCities); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query ListCities: %w", err)
|
||||
}
|
||||
if q.listVenuesStmt, err = db.PrepareContext(ctx, listVenues); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query ListVenues: %w", err)
|
||||
}
|
||||
if q.updateCityNameStmt, err = db.PrepareContext(ctx, updateCityName); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query UpdateCityName: %w", err)
|
||||
}
|
||||
if q.updateVenueNameStmt, err = db.PrepareContext(ctx, updateVenueName); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query UpdateVenueName: %w", err)
|
||||
}
|
||||
if q.venueCountByCityStmt, err = db.PrepareContext(ctx, venueCountByCity); err != nil {
|
||||
return nil, fmt.Errorf("error preparing query VenueCountByCity: %w", err)
|
||||
}
|
||||
return &q, nil
|
||||
}
|
||||
|
||||
func (q *Queries) Close() error {
|
||||
var err error
|
||||
if q.createCityStmt != nil {
|
||||
if cerr := q.createCityStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing createCityStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.createVenueStmt != nil {
|
||||
if cerr := q.createVenueStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing createVenueStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.deleteVenueStmt != nil {
|
||||
if cerr := q.deleteVenueStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing deleteVenueStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.getCityStmt != nil {
|
||||
if cerr := q.getCityStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing getCityStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.getVenueStmt != nil {
|
||||
if cerr := q.getVenueStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing getVenueStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.listCitiesStmt != nil {
|
||||
if cerr := q.listCitiesStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing listCitiesStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.listVenuesStmt != nil {
|
||||
if cerr := q.listVenuesStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing listVenuesStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.updateCityNameStmt != nil {
|
||||
if cerr := q.updateCityNameStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing updateCityNameStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.updateVenueNameStmt != nil {
|
||||
if cerr := q.updateVenueNameStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing updateVenueNameStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
if q.venueCountByCityStmt != nil {
|
||||
if cerr := q.venueCountByCityStmt.Close(); cerr != nil {
|
||||
err = fmt.Errorf("error closing venueCountByCityStmt: %w", cerr)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (q *Queries) exec(ctx context.Context, stmt *sql.Stmt, query string, args ...any) (sql.Result, error) {
|
||||
switch {
|
||||
case stmt != nil && q.tx != nil:
|
||||
return q.tx.StmtContext(ctx, stmt).ExecContext(ctx, args...)
|
||||
case stmt != nil:
|
||||
return stmt.ExecContext(ctx, args...)
|
||||
default:
|
||||
return q.db.ExecContext(ctx, query, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Queries) query(ctx context.Context, stmt *sql.Stmt, query string, args ...any) (*sql.Rows, error) {
|
||||
switch {
|
||||
case stmt != nil && q.tx != nil:
|
||||
return q.tx.StmtContext(ctx, stmt).QueryContext(ctx, args...)
|
||||
case stmt != nil:
|
||||
return stmt.QueryContext(ctx, args...)
|
||||
default:
|
||||
return q.db.QueryContext(ctx, query, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Queries) queryRow(ctx context.Context, stmt *sql.Stmt, query string, args ...any) *sql.Row {
|
||||
switch {
|
||||
case stmt != nil && q.tx != nil:
|
||||
return q.tx.StmtContext(ctx, stmt).QueryRowContext(ctx, args...)
|
||||
case stmt != nil:
|
||||
return stmt.QueryRowContext(ctx, args...)
|
||||
default:
|
||||
return q.db.QueryRowContext(ctx, query, args...)
|
||||
}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
tx *sql.Tx
|
||||
createCityStmt *sql.Stmt
|
||||
createVenueStmt *sql.Stmt
|
||||
deleteVenueStmt *sql.Stmt
|
||||
getCityStmt *sql.Stmt
|
||||
getVenueStmt *sql.Stmt
|
||||
listCitiesStmt *sql.Stmt
|
||||
listVenuesStmt *sql.Stmt
|
||||
updateCityNameStmt *sql.Stmt
|
||||
updateVenueNameStmt *sql.Stmt
|
||||
venueCountByCityStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
tx: tx,
|
||||
createCityStmt: q.createCityStmt,
|
||||
createVenueStmt: q.createVenueStmt,
|
||||
deleteVenueStmt: q.deleteVenueStmt,
|
||||
getCityStmt: q.getCityStmt,
|
||||
getVenueStmt: q.getVenueStmt,
|
||||
listCitiesStmt: q.listCitiesStmt,
|
||||
listVenuesStmt: q.listVenuesStmt,
|
||||
updateCityNameStmt: q.updateCityNameStmt,
|
||||
updateVenueNameStmt: q.updateVenueNameStmt,
|
||||
venueCountByCityStmt: q.venueCountByCityStmt,
|
||||
}
|
||||
}
|
||||
171
examples/ondeck/sqlite/db_test.go
Normal file
171
examples/ondeck/sqlite/db_test.go
Normal file
@@ -0,0 +1,171 @@
|
||||
//go:build examples
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/sqltest"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
// TODO: Enum is not yet supported
|
||||
const (
|
||||
VenuesStatusOpen string = "open"
|
||||
VenuesStatusClosed string = "closed"
|
||||
)
|
||||
|
||||
func join(vals ...string) sql.NullString {
|
||||
if len(vals) == 0 {
|
||||
return sql.NullString{}
|
||||
}
|
||||
return sql.NullString{
|
||||
Valid: true,
|
||||
String: strings.Join(vals, ","),
|
||||
}
|
||||
}
|
||||
|
||||
func runOnDeckQueries(t *testing.T, q *Queries) {
|
||||
ctx := context.Background()
|
||||
|
||||
err := q.CreateCity(ctx, CreateCityParams{
|
||||
Slug: "san-francisco",
|
||||
Name: "San Francisco",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
city, err := q.GetCity(ctx, "san-francisco")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
venueResult, err := q.CreateVenue(ctx, CreateVenueParams{
|
||||
Slug: "the-fillmore",
|
||||
Name: "The Fillmore",
|
||||
City: city.Slug,
|
||||
SpotifyPlaylist: "spotify:uri",
|
||||
Status: VenuesStatusOpen,
|
||||
Statuses: join(string(VenuesStatusOpen), string(VenuesStatusClosed)),
|
||||
Tags: join("rock", "punk"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
venueID, err := venueResult.LastInsertId()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
venue, err := q.GetVenue(ctx, GetVenueParams{
|
||||
Slug: "the-fillmore",
|
||||
City: city.Slug,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(venue.ID, venueID); diff != "" {
|
||||
t.Errorf("venue ID mismatch:\n%s", diff)
|
||||
}
|
||||
|
||||
{
|
||||
actual, err := q.VenueCountByCity(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if diff := cmp.Diff(actual, []VenueCountByCityRow{
|
||||
{city.Slug, int64(1)},
|
||||
}); diff != "" {
|
||||
t.Errorf("venue count mismatch:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
actual, err := q.ListCities(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if diff := cmp.Diff(actual, []City{city}); diff != "" {
|
||||
t.Errorf("list city mismatch:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
actual, err := q.ListVenues(ctx, city.Slug)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if diff := cmp.Diff(actual, []Venue{venue}); diff != "" {
|
||||
t.Errorf("list venue mismatch:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
err := q.UpdateCityName(ctx, UpdateCityNameParams{
|
||||
Slug: city.Slug,
|
||||
Name: "SF",
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
expected := "Fillmore"
|
||||
err := q.UpdateVenueName(ctx, UpdateVenueNameParams{
|
||||
Slug: venue.Slug,
|
||||
Name: expected,
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fresh, err := q.GetVenue(ctx, GetVenueParams{
|
||||
Slug: venue.Slug,
|
||||
City: city.Slug,
|
||||
})
|
||||
if diff := cmp.Diff(expected, fresh.Name); diff != "" {
|
||||
t.Errorf("update venue mismatch:\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
err := q.DeleteVenue(ctx, DeleteVenueParams{
|
||||
Slug: venue.Slug,
|
||||
Slug_2: venue.Slug,
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepared(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
sdb, cleanup := sqltest.SQLite(t, []string{"schema"})
|
||||
defer sdb.Close()
|
||||
defer cleanup()
|
||||
|
||||
q, err := Prepare(context.Background(), sdb)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
runOnDeckQueries(t, q)
|
||||
}
|
||||
|
||||
func TestQueries(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
sdb, cleanup := sqltest.SQLite(t, []string{"schema"})
|
||||
defer sdb.Close()
|
||||
defer cleanup()
|
||||
|
||||
runOnDeckQueries(t, New(sdb))
|
||||
}
|
||||
28
examples/ondeck/sqlite/models.go
Normal file
28
examples/ondeck/sqlite/models.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
type City struct {
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type Venue struct {
|
||||
ID int64 `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Statuses sql.NullString `json:"statuses"`
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
City string `json:"city"`
|
||||
SpotifyPlaylist string `json:"spotify_playlist"`
|
||||
SongkickID sql.NullString `json:"songkick_id"`
|
||||
Tags sql.NullString `json:"tags"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
25
examples/ondeck/sqlite/querier.go
Normal file
25
examples/ondeck/sqlite/querier.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type Querier interface {
|
||||
CreateCity(ctx context.Context, arg CreateCityParams) error
|
||||
CreateVenue(ctx context.Context, arg CreateVenueParams) (sql.Result, error)
|
||||
DeleteVenue(ctx context.Context, arg DeleteVenueParams) error
|
||||
GetCity(ctx context.Context, slug string) (City, error)
|
||||
GetVenue(ctx context.Context, arg GetVenueParams) (Venue, error)
|
||||
ListCities(ctx context.Context) ([]City, error)
|
||||
ListVenues(ctx context.Context, city string) ([]Venue, error)
|
||||
UpdateCityName(ctx context.Context, arg UpdateCityNameParams) error
|
||||
UpdateVenueName(ctx context.Context, arg UpdateVenueNameParams) error
|
||||
VenueCountByCity(ctx context.Context) ([]VenueCountByCityRow, error)
|
||||
}
|
||||
|
||||
var _ Querier = (*Queries)(nil)
|
||||
23
examples/ondeck/sqlite/query/city.sql
Normal file
23
examples/ondeck/sqlite/query/city.sql
Normal file
@@ -0,0 +1,23 @@
|
||||
/* name: ListCities :many */
|
||||
SELECT *
|
||||
FROM city
|
||||
ORDER BY name;
|
||||
|
||||
/* name: GetCity :one */
|
||||
SELECT *
|
||||
FROM city
|
||||
WHERE slug = ?;
|
||||
|
||||
/* name: CreateCity :exec */
|
||||
INSERT INTO city (
|
||||
name,
|
||||
slug
|
||||
) VALUES (
|
||||
?,
|
||||
?
|
||||
);
|
||||
|
||||
/* name: UpdateCityName :exec */
|
||||
UPDATE city
|
||||
SET name = ?
|
||||
WHERE slug = ?;
|
||||
48
examples/ondeck/sqlite/query/venue.sql
Normal file
48
examples/ondeck/sqlite/query/venue.sql
Normal file
@@ -0,0 +1,48 @@
|
||||
/* name: ListVenues :many */
|
||||
SELECT *
|
||||
FROM venue
|
||||
WHERE city = ?
|
||||
ORDER BY name;
|
||||
|
||||
/* name: DeleteVenue :exec */
|
||||
DELETE FROM venue
|
||||
WHERE slug = ? AND slug = ?;
|
||||
|
||||
/* name: GetVenue :one */
|
||||
SELECT *
|
||||
FROM venue
|
||||
WHERE slug = ? AND city = ?;
|
||||
|
||||
/* name: CreateVenue :execresult */
|
||||
INSERT INTO venue (
|
||||
slug,
|
||||
name,
|
||||
city,
|
||||
created_at,
|
||||
spotify_playlist,
|
||||
status,
|
||||
statuses,
|
||||
tags
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
CURRENT_TIMESTAMP,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
);
|
||||
|
||||
/* name: UpdateVenueName :exec */
|
||||
UPDATE venue
|
||||
SET name = ?
|
||||
WHERE slug = ?;
|
||||
|
||||
/* name: VenueCountByCity :many */
|
||||
SELECT
|
||||
city,
|
||||
count(*)
|
||||
FROM venue
|
||||
GROUP BY 1
|
||||
ORDER BY 1;
|
||||
4
examples/ondeck/sqlite/schema/0001_city.sql
Normal file
4
examples/ondeck/sqlite/schema/0001_city.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
CREATE TABLE city (
|
||||
slug varchar(255) PRIMARY KEY,
|
||||
name text NOT NULL
|
||||
)
|
||||
13
examples/ondeck/sqlite/schema/0002_venue.sql
Normal file
13
examples/ondeck/sqlite/schema/0002_venue.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE venues (
|
||||
id integer primary key AUTOINCREMENT,
|
||||
dropped text,
|
||||
status text not null,
|
||||
statuses text, -- status[]
|
||||
slug text not null,
|
||||
name varchar(255) not null,
|
||||
city text not null references city(slug),
|
||||
spotify_playlist varchar(255) not null,
|
||||
songkick_id text,
|
||||
tags text, -- tags[]
|
||||
CHECK (status = 'open' OR status = 'closed')
|
||||
);
|
||||
3
examples/ondeck/sqlite/schema/0003_add_column.sql
Normal file
3
examples/ondeck/sqlite/schema/0003_add_column.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE venues RENAME TO venue;
|
||||
ALTER TABLE venue ADD COLUMN created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
ALTER TABLE venue DROP COLUMN dropped;
|
||||
193
examples/ondeck/sqlite/venue.sql.go
Normal file
193
examples/ondeck/sqlite/venue.sql.go
Normal file
@@ -0,0 +1,193 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: venue.sql
|
||||
|
||||
package ondeck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
const createVenue = `-- name: CreateVenue :execresult
|
||||
INSERT INTO venue (
|
||||
slug,
|
||||
name,
|
||||
city,
|
||||
created_at,
|
||||
spotify_playlist,
|
||||
status,
|
||||
statuses,
|
||||
tags
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
CURRENT_TIMESTAMP,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
)
|
||||
`
|
||||
|
||||
type CreateVenueParams struct {
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
City string `json:"city"`
|
||||
SpotifyPlaylist string `json:"spotify_playlist"`
|
||||
Status string `json:"status"`
|
||||
Statuses sql.NullString `json:"statuses"`
|
||||
Tags sql.NullString `json:"tags"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateVenue(ctx context.Context, arg CreateVenueParams) (sql.Result, error) {
|
||||
return q.exec(ctx, q.createVenueStmt, createVenue,
|
||||
arg.Slug,
|
||||
arg.Name,
|
||||
arg.City,
|
||||
arg.SpotifyPlaylist,
|
||||
arg.Status,
|
||||
arg.Statuses,
|
||||
arg.Tags,
|
||||
)
|
||||
}
|
||||
|
||||
const deleteVenue = `-- name: DeleteVenue :exec
|
||||
DELETE FROM venue
|
||||
WHERE slug = ? AND slug = ?
|
||||
`
|
||||
|
||||
type DeleteVenueParams struct {
|
||||
Slug string `json:"slug"`
|
||||
Slug_2 string `json:"slug_2"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteVenue(ctx context.Context, arg DeleteVenueParams) error {
|
||||
_, err := q.exec(ctx, q.deleteVenueStmt, deleteVenue, arg.Slug, arg.Slug_2)
|
||||
return err
|
||||
}
|
||||
|
||||
const getVenue = `-- name: GetVenue :one
|
||||
SELECT id, status, statuses, slug, name, city, spotify_playlist, songkick_id, tags, created_at
|
||||
FROM venue
|
||||
WHERE slug = ? AND city = ?
|
||||
`
|
||||
|
||||
type GetVenueParams struct {
|
||||
Slug string `json:"slug"`
|
||||
City string `json:"city"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetVenue(ctx context.Context, arg GetVenueParams) (Venue, error) {
|
||||
row := q.queryRow(ctx, q.getVenueStmt, getVenue, arg.Slug, arg.City)
|
||||
var i Venue
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Status,
|
||||
&i.Statuses,
|
||||
&i.Slug,
|
||||
&i.Name,
|
||||
&i.City,
|
||||
&i.SpotifyPlaylist,
|
||||
&i.SongkickID,
|
||||
&i.Tags,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listVenues = `-- name: ListVenues :many
|
||||
SELECT id, status, statuses, slug, name, city, spotify_playlist, songkick_id, tags, created_at
|
||||
FROM venue
|
||||
WHERE city = ?
|
||||
ORDER BY name
|
||||
`
|
||||
|
||||
func (q *Queries) ListVenues(ctx context.Context, city string) ([]Venue, error) {
|
||||
rows, err := q.query(ctx, q.listVenuesStmt, listVenues, city)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Venue
|
||||
for rows.Next() {
|
||||
var i Venue
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Status,
|
||||
&i.Statuses,
|
||||
&i.Slug,
|
||||
&i.Name,
|
||||
&i.City,
|
||||
&i.SpotifyPlaylist,
|
||||
&i.SongkickID,
|
||||
&i.Tags,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateVenueName = `-- name: UpdateVenueName :exec
|
||||
UPDATE venue
|
||||
SET name = ?
|
||||
WHERE slug = ?
|
||||
`
|
||||
|
||||
type UpdateVenueNameParams struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateVenueName(ctx context.Context, arg UpdateVenueNameParams) error {
|
||||
_, err := q.exec(ctx, q.updateVenueNameStmt, updateVenueName, arg.Name, arg.Slug)
|
||||
return err
|
||||
}
|
||||
|
||||
const venueCountByCity = `-- name: VenueCountByCity :many
|
||||
SELECT
|
||||
city,
|
||||
count(*)
|
||||
FROM venue
|
||||
GROUP BY 1
|
||||
ORDER BY 1
|
||||
`
|
||||
|
||||
type VenueCountByCityRow struct {
|
||||
City string `json:"city"`
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
func (q *Queries) VenueCountByCity(ctx context.Context) ([]VenueCountByCityRow, error) {
|
||||
rows, err := q.query(ctx, q.venueCountByCityStmt, venueCountByCity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []VenueCountByCityRow
|
||||
for rows.Next() {
|
||||
var i VenueCountByCityRow
|
||||
if err := rows.Scan(&i.City, &i.Count); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
Reference in New Issue
Block a user