Skip to content

Commit 1d96ed1

Browse files
committed
update docs about the :batch feature
1 parent f5d897a commit 1d96ed1

File tree

4 files changed

+119
-23
lines changed

4 files changed

+119
-23
lines changed

website/public/benchmark-postgres-insert.html

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,49 @@
11
<div class="benchmark-report">
22
<style>
3+
.benchmark-report, .benchmark-report * {
4+
color-scheme: light only;
5+
}
36
.benchmark-report {
47
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
5-
color: #111827;
8+
color: #111827 !important;
9+
background: #ffffff !important;
610
line-height: 1.6;
11+
padding: 2rem;
12+
border-radius: 8px;
713
}
814
.benchmark-report .container {
915
max-width: 1100px;
1016
margin: 0;
1117
background: transparent;
1218
padding: 0;
1319
}
14-
h1 { font-size: 2rem; margin-bottom: 0.5rem; color: #111827; }
15-
.subtitle { color: #6b7280; margin-bottom: 2rem; }
16-
.chart-section { margin-top: 2rem; padding-top: 1.5rem; border-top: 2px solid #e5e7eb; }
17-
.chart-section h2 { font-size: 1.25rem; margin-bottom: 0.75rem; }
18-
.chart-container { max-width: 900px; margin: 0 auto 2rem; }
19-
.batch-section { margin-top: 2rem; padding-top: 1.5rem; border-top: 2px solid #e5e7eb; }
20-
.batch-section h2 { font-size: 1.25rem; margin-bottom: 0.75rem; }
21-
.results-table { width: 100%; border-collapse: collapse; margin-bottom: 1rem; }
22-
.results-table th, .results-table td {
20+
.benchmark-report h1 { font-size: 2rem; margin-bottom: 0.5rem; color: #111827 !important; }
21+
.benchmark-report h2 { color: #111827 !important; }
22+
.benchmark-report h3 { color: #374151 !important; }
23+
.benchmark-report p { color: #111827 !important; }
24+
.benchmark-report .subtitle { color: #6b7280 !important; margin-bottom: 2rem; }
25+
.benchmark-report .chart-section { margin-top: 2rem; padding-top: 1.5rem; border-top: 2px solid #e5e7eb; }
26+
.benchmark-report .chart-section h2 { font-size: 1.25rem; margin-bottom: 0.75rem; }
27+
.benchmark-report .chart-container { max-width: 900px; margin: 0 auto 2rem; }
28+
.benchmark-report .batch-section { margin-top: 2rem; padding-top: 1.5rem; border-top: 2px solid #e5e7eb; }
29+
.benchmark-report .batch-section h2 { font-size: 1.25rem; margin-bottom: 0.75rem; }
30+
.benchmark-report .results-table { width: 100%; border-collapse: collapse; margin-bottom: 1rem; }
31+
.benchmark-report .results-table th, .benchmark-report .results-table td {
2332
padding: 0.5rem 0.75rem; text-align: left;
2433
border-bottom: 1px solid #e5e7eb; font-size: 0.875rem;
34+
color: #111827 !important;
2535
}
26-
.results-table th { background: #f9fafb; font-weight: 600; color: #374151; }
27-
.results-table td.number { text-align: right; font-variant-numeric: tabular-nums; }
28-
.winner { font-weight: 600; color: #10b981; }
29-
.system-info { margin-top: 2rem; padding-top: 2rem; border-top: 2px solid #e5e7eb; }
30-
.system-info h3 { font-size: 1.125rem; margin-bottom: 0.75rem; color: #374151; }
31-
.system-table { width: 100%; border-collapse: collapse; }
32-
.system-table td {
36+
.benchmark-report .results-table th { background: #f9fafb; font-weight: 600; color: #374151 !important; }
37+
.benchmark-report .results-table td.number { text-align: right; font-variant-numeric: tabular-nums; }
38+
.benchmark-report .winner td { font-weight: 600; color: #10b981 !important; }
39+
.benchmark-report .system-info { margin-top: 2rem; padding-top: 2rem; border-top: 2px solid #e5e7eb; }
40+
.benchmark-report .system-info h3 { font-size: 1.125rem; margin-bottom: 0.75rem; color: #374151 !important; }
41+
.benchmark-report .system-table { width: 100%; border-collapse: collapse; }
42+
.benchmark-report .system-table td {
3343
padding: 0.375rem 0.5rem; border-bottom: 1px solid #e5e7eb; font-size: 0.875rem;
44+
color: #111827 !important;
3445
}
35-
.system-table td:first-child { font-weight: 600; color: #6b7280; width: 160px; }
46+
.benchmark-report .system-table td:first-child { font-weight: 600; color: #6b7280 !important; width: 160px; }
3647
</style>
3748
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
3849
<div class="container">

website/src/components/BenchmarkReport.astro

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,11 @@ const styles = styleMatch ? styleMatch[1] : "";
4545
<style>
4646
.benchmark-report-wrapper {
4747
margin: 2rem 0;
48-
border: 1px solid #e5e7eb;
49-
border-radius: 8px;
5048
overflow: hidden;
51-
background: white;
5249
}
53-
50+
5451
.benchmark-report-wrapper :global(.container) {
5552
margin: 0;
56-
padding: 2rem;
5753
box-shadow: none;
5854
}
5955
</style>

website/src/content/docs/generators/java-jdbc.mdx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,47 @@ The generator creates a single Java file with:
187187

188188
All types are nullable (using wrapper classes).
189189

190+
## Batch EXEC (`:batch`)
191+
192+
Add the `:batch` modifier to any `EXEC` to also generate a JDBC batch method. The single-row method is kept alongside it, so callers can pick per use case. Works with any JDBC database (SQLite, DuckDB, PostgreSQL, ...).
193+
194+
```sql
195+
-- EXEC insert_user :batch
196+
@set id = 'u1'
197+
@set name = 'Alice'
198+
@set email = 'alice@example.com'
199+
INSERT INTO users (id, name, email) VALUES (${id}, ${name}, ${email});
200+
```
201+
202+
Multi-parameter EXECs generate a params record so call sites stay readable:
203+
204+
```java
205+
public record InsertUserParams(String id, String name, String email) {}
206+
207+
public int insertUser(String id, String name, String email) throws SQLException { ... }
208+
209+
public int[] insertUserBatch(Iterable<InsertUserParams> params) throws SQLException { ... }
210+
```
211+
212+
Single-parameter EXECs skip the record:
213+
214+
```sql
215+
-- EXEC delete_user :batch
216+
@set id = 'u1'
217+
DELETE FROM users WHERE id = ${id};
218+
```
219+
220+
```java
221+
public int[] deleteUserBatch(Iterable<String> params) throws SQLException { ... }
222+
```
223+
224+
Parameters are bound with typed JDBC setters (`setInt`, `setString`, `setLong`, ...) based on the inferred Java type, falling back to `setObject` for types without a typed setter (`LocalDate`, `OffsetDateTime`, `BigDecimal`, `UUID`, list/array types). Typed setters skip the driver's per-row type inspection, which is noticeable on large batches.
225+
226+
**When to use:**
227+
- `:batch` works for INSERT, UPDATE, and DELETE on any JDBC database.
228+
- For PostgreSQL INSERTs, add `?reWriteBatchedInserts=true` to your JDBC URL for ~2.5× throughput — the driver automatically rewrites batches into multi-VALUES statements.
229+
- For the fastest PostgreSQL INSERTs, use [`TABLE ... :appender`](#bulk-inserts-postgresql) (COPY BINARY) instead — typically 5–20× faster than `:batch`. See the [PostgreSQL insert benchmark](/blog/java-postgres-insert-benchmark/) for the numbers.
230+
190231
## PostgreSQL
191232

192233
SQG supports PostgreSQL via the `java/postgres` generator. PostgreSQL-specific features include:

website/src/content/docs/guides/sql-syntax.mdx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ UPDATE users SET active = false WHERE id = ${id};
152152
- Database-specific result type (e.g., `RunResult` for SQLite)
153153
- Typically includes `changes` count and `lastInsertRowid`
154154

155+
Add `:batch` to additionally generate a JDBC batch method — see the [`:batch`](#batch) modifier below.
156+
155157
### TABLE (Bulk Inserts)
156158

157159
Generate high-performance bulk insert code for DuckDB and PostgreSQL tables. Provides significantly faster inserts than individual INSERT statements.
@@ -245,6 +247,52 @@ SELECT id FROM users;
245247
getUserIds(): (number | null)[]
246248
```
247249

250+
### :batch
251+
252+
Applies to `EXEC` statements only. Generates an additional `<name>Batch(...)` method that uses JDBC's `addBatch()` / `executeBatch()` to send many rows in one round trip. The regular single-row method is still generated alongside it.
253+
254+
**Java only** — ignored by the TypeScript and Python generators.
255+
256+
```sql
257+
-- EXEC insert_user :batch
258+
@set id = 'u1'
259+
@set name = 'Alice'
260+
@set email = 'alice@example.com'
261+
INSERT INTO users (id, name, email) VALUES (${id}, ${name}, ${email});
262+
```
263+
264+
For queries with multiple parameters, SQG generates a params record to keep call sites readable:
265+
266+
```java
267+
// Generated
268+
public record InsertUserParams(String id, String name, String email) {}
269+
270+
public int[] insertUserBatch(Iterable<InsertUserParams> params) throws SQLException { ... }
271+
```
272+
273+
```java
274+
queries.insertUserBatch(List.of(
275+
new MyApp.InsertUserParams("u1", "Alice", "alice@example.com"),
276+
new MyApp.InsertUserParams("u2", "Bob", "bob@example.com")
277+
));
278+
```
279+
280+
Single-parameter EXECs skip the record wrapper:
281+
282+
```sql
283+
-- EXEC delete_user :batch
284+
@set id = 'u1'
285+
DELETE FROM users WHERE id = ${id};
286+
```
287+
288+
```java
289+
queries.deleteUserBatch(List.of("u1", "u2", "u3"));
290+
```
291+
292+
**When to use which:**
293+
- `:batch` — general-purpose; works for INSERT, UPDATE, and DELETE on any JDBC database. For PostgreSQL, add `?reWriteBatchedInserts=true` to the JDBC URL for ~2.5× throughput on batched INSERTs (see the [insert benchmark](/blog/java-postgres-insert-benchmark/)).
294+
- `TABLE ... :appender` — INSERT-only, DuckDB and PostgreSQL only, typically 5–20× faster than `:batch` because it bypasses SQL parsing entirely (COPY BINARY / DuckDB appender API).
295+
248296
### Combining Modifiers
249297

250298
Modifiers can be combined:

0 commit comments

Comments
 (0)