Skip to content

Commit b6f785f

Browse files
authored
docs: Document sqlc.* macros (#2698)
Add a new section on embedding structs
1 parent 9c9b577 commit b6f785f

File tree

4 files changed

+222
-4
lines changed

4 files changed

+222
-4
lines changed

docs/howto/embedding.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#### Embedding structs
2+
3+
Embedding allows you to reuse existing model structs in more queries, resulting
4+
in less manual serialization work. First, imagine we have the following schema
5+
with students and test scores.
6+
7+
```sql
8+
CREATE TABLE students (
9+
id bigserial PRIMARY KEY,
10+
name text NOT NULL,
11+
age integer NOT NULL
12+
);
13+
14+
CREATE TABLE test_scores (
15+
student_id bigint NOT NULL,
16+
score integer NOT NULL,
17+
grade text NOT NULL
18+
);
19+
```
20+
21+
We want to select the student record and the scores they got on a test.
22+
Here's how we'd usually do that:
23+
24+
```sql
25+
-- name: ScoreAndTests :many
26+
SELECT students.*, test_scores.*
27+
FROM students
28+
JOIN test_scores ON test_scores.student_id = students.id
29+
WHERE students.id = ?;
30+
```
31+
32+
When using Go, sqlc will produce a struct like this:
33+
34+
```go
35+
type ScoreAndTestsRow struct {
36+
ID int64
37+
Name string
38+
Age int32
39+
StudentID int64
40+
Score int32
41+
Grade string
42+
}
43+
```
44+
45+
With embedding, the struct will contain a model for both tables instead of a
46+
flattened list of columns.
47+
48+
```sql
49+
-- name: ScoreAndTests :many
50+
SELECT sqlc.embed(students), sqlc.embed(test_scores)
51+
FROM students
52+
JOIN test_scores ON test_scores.student_id = students.id
53+
WHERE students.id = ?;
54+
```
55+
56+
```
57+
type ScoreAndTestsRow struct {
58+
Student Student
59+
TestScore TestScore
60+
}
61+
```

docs/howto/insert.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ func (q *Queries) CreateAuthorAndReturnId(ctx context.Context, arg CreateAuthorA
122122

123123
## Using CopyFrom
124124

125+
### PostgreSQL
126+
125127
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:
126128

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

149-
MySQL supports a similar feature using [LOAD DATA](https://dev.mysql.com/doc/refman/8.0/en/load-data.html).
151+
The `:copyfrom` command requires either `pgx/v4` or `pgx/v5`.
152+
153+
```yaml
154+
version: "2"
155+
sql:
156+
- engine: "postgresql"
157+
queries: "query.sql"
158+
schema: "query.sql"
159+
gen:
160+
go:
161+
package: "db"
162+
sql_package: "pgx/v5"
163+
out: "db"
164+
```
165+
166+
### MySQL
167+
168+
### MySQL supports a similar feature using [LOAD DATA](https://dev.mysql.com/doc/refman/8.0/en/load-data.html).
169+
150170
151171
Errors and duplicate keys are treated as warnings and insertion will
152172
continue, even without an error for some cases. Use this in a transaction
@@ -165,4 +185,20 @@ INSERT INTO foo (a, b, c, d) VALUES (?, ?, ?, ?);
165185
func (q *Queries) InsertValues(ctx context.Context, arg []InsertValuesParams) (int64, error) {
166186
...
167187
}
188+
```
189+
190+
The `:copyfrom` command requires setting the `sql_package` and `sql_driver` options.
191+
192+
```yaml
193+
version: "2"
194+
sql:
195+
- engine: "mysql"
196+
queries: "query.sql"
197+
schema: "query.sql"
198+
gen:
199+
go:
200+
package: "db"
201+
sql_package: "database/sql"
202+
sql_driver: "github.com/go-sql-driver/mysql"
203+
out: "db"
168204
```

docs/index.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ code ever again.
5353

5454
howto/ddl.md
5555
howto/structs.md
56+
howto/embedding.md
5657

5758
howto/vet.md
5859
howto/ci-cd.md
@@ -63,13 +64,14 @@ code ever again.
6364
:caption: Reference
6465
:hidden:
6566

67+
reference/changelog.md
6668
reference/cli.md
6769
reference/config.md
6870
reference/datatypes.md
69-
reference/query-annotations.md
70-
reference/language-support.rst
7171
reference/environment-variables.md
72-
reference/changelog.md
72+
reference/language-support.rst
73+
reference/macros.md
74+
reference/query-annotations.md
7375

7476
.. toctree::
7577
:maxdepth: 2

docs/reference/macros.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Macros
2+
3+
## `sqlc.arg`
4+
5+
Attach a name to a parameter in a SQL query. This macro expands to an
6+
engine-specific parameter placeholder. The name of the parameter is noted and
7+
used during code generation.
8+
9+
```sql
10+
-- name: GetAuthorByName :one
11+
SELECT *
12+
FROM authors
13+
WHERE lower(name) = sqlc.arg(name);
14+
15+
-- >>> EXPANDS TO >>>
16+
17+
-- name: GetAuthorByName :one
18+
SELECT *
19+
FROM authors
20+
WHERE lower(name) = ?;
21+
```
22+
23+
See more examples in [Naming parameters](../howto/named_parameters).
24+
25+
## `sqlc.embed`
26+
27+
Embedding allows you to reuse existing model structs in more queries, resulting
28+
in less manual serialization work. First, imagine we have the following schema
29+
with students and test scores.
30+
31+
```sql
32+
CREATE TABLE students (
33+
id bigserial PRIMARY KEY,
34+
name text,
35+
age integer
36+
);
37+
38+
CREATE TABLE test_scores (
39+
student_id bigint,
40+
score integer,
41+
grade text
42+
);
43+
```
44+
45+
```sql
46+
-- name: GetStudentAndScore :one
47+
SELECT sqlc.embed(students), sqlc.embed(test_scores)
48+
FROM students
49+
JOIN test_scores ON test_scores.student_id = students.id
50+
WHERE students.id = $1;
51+
52+
-- >>> EXPANDS TO >>>
53+
54+
-- name: GetStudentAndScore :one
55+
SELECT students.*, test_scores.*
56+
FROM students
57+
JOIN test_scores ON test_scores.student_id = students.id
58+
WHERE students.id = $1;
59+
```
60+
61+
The Go method will return a struct with a field for the `Student` and field for
62+
the test `TestScore` instead of each column existing on the struct.
63+
64+
```go
65+
type GetStudentAndScoreRow struct {
66+
Student Student
67+
TestScore TestScore
68+
}
69+
70+
func (q *Queries) GetStudentAndScore(ctx context.Context, id int64) (GetStudentAndScoreRow, error) {
71+
// ...
72+
}
73+
```
74+
75+
See a full example in [Embedding structs](../howto/embedding).
76+
77+
## `sqlc.narg`
78+
79+
The same as `sqlc.arg`, but always marks the parameter as nullable.
80+
81+
```sql
82+
-- name: GetAuthorByName :one
83+
SELECT *
84+
FROM authors
85+
WHERE lower(name) = sqlc.narg(name);
86+
87+
-- >>> EXPANDS TO >>>
88+
89+
-- name: GetAuthorByName :one
90+
SELECT *
91+
FROM authors
92+
WHERE LOWER(name) = ?;
93+
```
94+
95+
See more examples in [Naming parameters](../howto/named_parameters).
96+
97+
## `sqlc.slice`
98+
99+
For drivers that do not support passing slices to the IN operator, the
100+
`sqlc.slice` macro generates a dynamic query at runtime with the correct
101+
number of parameters.
102+
103+
```sql
104+
/* name: SelectStudents :many */
105+
SELECT * FROM students
106+
WHERE age IN (sqlc.slice("ages"))
107+
108+
-- >>> EXPANDS TO >>>
109+
110+
/* name: SelectStudents :many */
111+
SELECT id, name, age FROM authors
112+
WHERE age IN (/*SLICE:ages*/?)
113+
```
114+
115+
Since the `/*SLICE:ages*/` placeholder is dynamically replaced on a per-query
116+
basis, this macro can't be used with prepared statements.
117+
118+
See a full example in [Passing a slice as a parameter to a
119+
query](../howto/select.html#mysql-and-sqlite).

0 commit comments

Comments
 (0)