Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Document sqlc.* macros #2698

Merged
merged 1 commit into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions docs/howto/embedding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#### Embedding structs

Embedding allows you to reuse existing model structs in more queries, resulting
in less manual serialization work. First, imagine we have the following schema
with students and test scores.

```sql
CREATE TABLE students (
id bigserial PRIMARY KEY,
name text NOT NULL,
age integer NOT NULL
);

CREATE TABLE test_scores (
student_id bigint NOT NULL,
score integer NOT NULL,
grade text NOT NULL
);
```

We want to select the student record and the scores they got on a test.
Here's how we'd usually do that:

```sql
-- name: ScoreAndTests :many
SELECT students.*, test_scores.*
FROM students
JOIN test_scores ON test_scores.student_id = students.id
WHERE students.id = ?;
```

When using Go, sqlc will produce a struct like this:

```go
type ScoreAndTestsRow struct {
ID int64
Name string
Age int32
StudentID int64
Score int32
Grade string
}
```

With embedding, the struct will contain a model for both tables instead of a
flattened list of columns.

```sql
-- name: ScoreAndTests :many
SELECT sqlc.embed(students), sqlc.embed(test_scores)
FROM students
JOIN test_scores ON test_scores.student_id = students.id
WHERE students.id = ?;
```

```
type ScoreAndTestsRow struct {
Student Student
TestScore TestScore
}
```
38 changes: 37 additions & 1 deletion docs/howto/insert.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ func (q *Queries) CreateAuthorAndReturnId(ctx context.Context, arg CreateAuthorA

## Using CopyFrom

### PostgreSQL

PostgreSQL supports the [COPY protocol](https://www.postgresql.org/docs/current/sql-copy.html) that can insert rows a lot faster than sequential inserts. You can use this easily with sqlc:

```sql
Expand All @@ -146,7 +148,25 @@ func (q *Queries) CreateAuthors(ctx context.Context, arg []CreateAuthorsParams)
}
```

MySQL supports a similar feature using [LOAD DATA](https://dev.mysql.com/doc/refman/8.0/en/load-data.html).
The `:copyfrom` command requires either `pgx/v4` or `pgx/v5`.

```yaml
version: "2"
sql:
- engine: "postgresql"
queries: "query.sql"
schema: "query.sql"
gen:
go:
package: "db"
sql_package: "pgx/v5"
out: "db"
```

### MySQL

### MySQL supports a similar feature using [LOAD DATA](https://dev.mysql.com/doc/refman/8.0/en/load-data.html).


Errors and duplicate keys are treated as warnings and insertion will
continue, even without an error for some cases. Use this in a transaction
Expand All @@ -165,4 +185,20 @@ INSERT INTO foo (a, b, c, d) VALUES (?, ?, ?, ?);
func (q *Queries) InsertValues(ctx context.Context, arg []InsertValuesParams) (int64, error) {
...
}
```

The `:copyfrom` command requires setting the `sql_package` and `sql_driver` options.

```yaml
version: "2"
sql:
- engine: "mysql"
queries: "query.sql"
schema: "query.sql"
gen:
go:
package: "db"
sql_package: "database/sql"
sql_driver: "github.com/go-sql-driver/mysql"
out: "db"
```
8 changes: 5 additions & 3 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ code ever again.

howto/ddl.md
howto/structs.md
howto/embedding.md

howto/vet.md
howto/ci-cd.md
Expand All @@ -63,13 +64,14 @@ code ever again.
:caption: Reference
:hidden:

reference/changelog.md
reference/cli.md
reference/config.md
reference/datatypes.md
reference/query-annotations.md
reference/language-support.rst
reference/environment-variables.md
reference/changelog.md
reference/language-support.rst
reference/macros.md
reference/query-annotations.md

.. toctree::
:maxdepth: 2
Expand Down
119 changes: 119 additions & 0 deletions docs/reference/macros.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Macros

## `sqlc.arg`

Attach a name to a parameter in a SQL query. This macro expands to an
engine-specific parameter placeholder. The name of the parameter is noted and
used during code generation.

```sql
-- name: GetAuthorByName :one
SELECT *
FROM authors
WHERE lower(name) = sqlc.arg(name);

-- >>> EXPANDS TO >>>

-- name: GetAuthorByName :one
SELECT *
FROM authors
WHERE lower(name) = ?;
```

See more examples in [Naming parameters](../howto/named_parameters).

## `sqlc.embed`

Embedding allows you to reuse existing model structs in more queries, resulting
in less manual serialization work. First, imagine we have the following schema
with students and test scores.

```sql
CREATE TABLE students (
id bigserial PRIMARY KEY,
name text,
age integer
);

CREATE TABLE test_scores (
student_id bigint,
score integer,
grade text
);
```

```sql
-- name: GetStudentAndScore :one
SELECT sqlc.embed(students), sqlc.embed(test_scores)
FROM students
JOIN test_scores ON test_scores.student_id = students.id
WHERE students.id = $1;

-- >>> EXPANDS TO >>>

-- name: GetStudentAndScore :one
SELECT students.*, test_scores.*
FROM students
JOIN test_scores ON test_scores.student_id = students.id
WHERE students.id = $1;
```

The Go method will return a struct with a field for the `Student` and field for
the test `TestScore` instead of each column existing on the struct.

```go
type GetStudentAndScoreRow struct {
Student Student
TestScore TestScore
}

func (q *Queries) GetStudentAndScore(ctx context.Context, id int64) (GetStudentAndScoreRow, error) {
// ...
}
```

See a full example in [Embedding structs](../howto/embedding).

## `sqlc.narg`

The same as `sqlc.arg`, but always marks the parameter as nullable.

```sql
-- name: GetAuthorByName :one
SELECT *
FROM authors
WHERE lower(name) = sqlc.narg(name);

-- >>> EXPANDS TO >>>

-- name: GetAuthorByName :one
SELECT *
FROM authors
WHERE LOWER(name) = ?;
```

See more examples in [Naming parameters](../howto/named_parameters).

## `sqlc.slice`

For drivers that do not support passing slices to the IN operator, the
`sqlc.slice` macro generates a dynamic query at runtime with the correct
number of parameters.

```sql
/* name: SelectStudents :many */
SELECT * FROM students
WHERE age IN (sqlc.slice("ages"))

-- >>> EXPANDS TO >>>

/* name: SelectStudents :many */
SELECT id, name, age FROM authors
WHERE age IN (/*SLICE:ages*/?)
```

Since the `/*SLICE:ages*/` placeholder is dynamically replaced on a per-query
basis, this macro can't be used with prepared statements.

See a full example in [Passing a slice as a parameter to a
query](../howto/select.html#mysql-and-sqlite).
Loading