Skip to content

Commit c942962

Browse files
docs: sync from coderbuzz/codex@200be78
1 parent 68ed497 commit c942962

2 files changed

Lines changed: 121 additions & 121 deletions

File tree

‎AI_KNOWLEDGE.md‎

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
<!-- docs: sync from coderbuzz/codex@b8d6f33 -->
1+
<!-- docs: sync from coderbuzz/codex@200be78 -->
22

3-
# @coderbuzz/sql — AI Expert Knowledge Reference
3+
# @coderbuzz/sql: AI Expert Knowledge Reference
44

55
**Package:** `@coderbuzz/sql` v0.1.3\
66
**Purpose:** Comprehensive reference for AI agents generating application code
@@ -54,7 +54,7 @@ import { snowflake } from "@coderbuzz/sql/snowflake";
5454
import { databricks } from "@coderbuzz/sql/databricks";
5555
```
5656

57-
### 2.2 Root package — shared helpers and types
57+
### 2.2 Root package: shared helpers and types
5858

5959
```ts
6060
import {
@@ -120,7 +120,7 @@ import * as databricksTypes from "@coderbuzz/sql/databricks-types";
120120

121121
## 3. Connecting to a Database
122122

123-
Each dialect namespace has a `connect()` factory. Always call `connect()` — do
123+
Each dialect namespace has a `connect()` factory. Always call `connect()`. Do
124124
not instantiate engine classes directly unless needed for testing.
125125

126126
```ts
@@ -227,7 +227,7 @@ const users = pg.table("users", {
227227
| `.defaultNow()` | DEFAULT NOW() in DDL |
228228

229229
Legacy uppercase aliases: `.PRIMARY()`, `.NOT_NULL()`, `.ALLOW_NULL()`,
230-
`.INDEX()`, `.DEFAULT(v)` — still work, prefer camelCase in new code.
230+
`.INDEX()`, `.DEFAULT(v)` still work. Prefer camelCase in new code.
231231

232232
### ClickHouse Table Options (third arg)
233233

@@ -306,7 +306,7 @@ for (const stmt of stmts) {
306306
DROP COLUMN (PG/MySQL/MSSQL), ALTER COLUMN (PG/MySQL/MSSQL). SQLite skips
307307
DROP/ALTER with a `console.warn`.
308308

309-
**Renaming a column.** `diff()` cannot infer a rename — `memo` disappearing and
309+
**Renaming a column.** `diff()` cannot infer a rename: `memo` disappearing and
310310
`keterangan` appearing is indistinguishable from a genuine drop-and-add, and
311311
guessing means sometimes emitting `ALTER ... RENAME` for a column that should
312312
have been dropped, keeping data that was meant to go under a name that now means
@@ -333,7 +333,7 @@ modifiers (`.notNull()`, `.index()`, …).
333333
### Pattern 1: Table-bound typed query
334334

335335
```ts
336-
// .from(engine) returns TypedSelectQuery — use .fields() for type-narrowing
336+
// .from(engine) returns TypedSelectQuery: use .fields() for type-narrowing
337337
const rows = await users.from(db)
338338
.fields("id", "email", "name") // typed: { id, email, name }[]
339339
.where({ active: true })
@@ -357,13 +357,13 @@ const rows = await db.select("u.id", "u.name", "p.title")
357357
### Fields variants in .fields()
358358

359359
```ts
360-
// 1. Plain column name — type: InferRow<S>[col]
360+
// 1. Plain column name (type: InferRow<S>[col])
361361
.fields("id", "name")
362362

363-
// 2. Column with alias — type: { userEmail: string }
363+
// 2. Column with alias (type: { userEmail: string })
364364
.fields(["email", "userEmail"])
365365

366-
// 3. Computed expression — type: { upperName: string }
366+
// 3. Computed expression (type: { upperName: string })
367367
.fields(expr<string>("UPPER(name)", "upperName"))
368368

369369
// 4. Aggregate helpers
@@ -372,18 +372,18 @@ const rows = await db.select("u.id", "u.name", "p.title")
372372

373373
### Joins
374374

375-
**Untyped** — raw strings, validated by the identifier check:
375+
**Untyped**: raw strings, validated by the identifier check:
376376

377377
```ts
378378
db.select("u.id", "p.title")
379379
.from("users u")
380380
.left_join("posts p", "p.user_id = u.id") // LEFT JOIN
381381
.inner_join("tags t", "t.post_id = p.id") // INNER JOIN
382382
.right_join("authors a", "a.id = p.author"); // RIGHT JOIN
383-
// .full_join() — NOT supported by SQLite, MySQL, ClickHouse
383+
// .full_join(): NOT supported by SQLite, MySQL, ClickHouse
384384
```
385385

386-
**Typed** — from a `SqlTable`, stated as column pairs:
386+
**Typed**: from a `SqlTable`, stated as column pairs:
387387

388388
```ts
389389
journalLines.from(db)
@@ -399,8 +399,8 @@ type JoinOn<L, R> =
399399
| ReadonlyArray<{ left: keyof L & string; right: keyof R & string }>
400400
```
401401
402-
`left` is a column of the query so far — the base table or anything already
403-
joined — `right` a column of the table being joined. Both are checked against
402+
`left` is a column of the query so far (the base table or anything already
403+
joined), and `right` a column of the table being joined. Both are checked against
404404
their schemas: a typo is a compile error, and there is no string left for
405405
anything else to end up inside. An array of pairs joins them with `AND`.
406406
@@ -415,7 +415,7 @@ anything else to end up inside. An array of pairs joins them with `AND`.
415415
416416
`order_by()` and `group_by()` on a typed query accept columns of the base table
417417
**and** of everything joined; `order_by` also takes `[column, 'ASC' | 'DESC']`. A
418-
raw string still works — the validator still runs on it — so nothing existing
418+
raw string still works (the validator still runs on it), so nothing existing
419419
breaks.
420420
421421
Why this exists: `SqlTable.from()` gave a typed query, but `TypedSelectQuery`
@@ -486,7 +486,7 @@ const { sql } = users.from(db).where({ id: 1 }).explain();
486486

487487
---
488488

489-
## 8. WHERE Conditions — Complete Reference
489+
## 8. WHERE Conditions: Complete Reference
490490

491491
### Object form (parameterised)
492492

@@ -504,7 +504,7 @@ const { sql } = users.from(db).where({ id: 1 }).explain();
504504
.where({ deleted_at: "IS NULL" }) // WHERE deleted_at IS NULL
505505
.where({ status: "IS NOT NULL" }) // WHERE status IS NOT NULL
506506

507-
// Multiple fields — joined with AND
507+
// Multiple fields: joined with AND
508508
.where({ active: true, role: "admin" })
509509
// WHERE active = ? AND role = ?
510510
```
@@ -577,12 +577,12 @@ const [row] = await users.insert(db)
577577
.returning("id", "email")
578578
.execute() as { id: number; email: string }[];
579579

580-
// Upsert — do nothing on conflict
580+
// Upsert: do nothing on conflict
581581
await db.insert_into("users", {
582582
onConflict: { type: "do_nothing", columns: ["email"] },
583583
}).values([{ email: "a@b.com", name: "Alice" }]).execute();
584584

585-
// Upsert — update on conflict
585+
// Upsert: update on conflict
586586
await db.insert_into("users", {
587587
onConflict: { type: "do_update", columns: ["email"], set: { name: "Alice Updated" } },
588588
}).values([{ email: "a@b.com", name: "Alice" }]).execute();
@@ -628,10 +628,10 @@ const batcher = db.batchInsert("events", {
628628
timeout: 2_000, // force flush after this many ms from first write
629629
maxInflight: 4, // concurrent flushes allowed before write() waits
630630
settings: { async_insert: "1" }, // engine-specific (ClickHouse)
631-
onError: (err, rows) => { /* REQUIRED — retry or dead-letter these rows */ },
631+
onError: (err, rows) => { /* REQUIRED: retry or dead-letter these rows */ },
632632
});
633633

634-
// Write rows — write() returns a promise; awaiting it applies backpressure
634+
// Write rows: write() returns a promise; awaiting it applies backpressure
635635
await batcher.write({ id: 1, val: "a" });
636636
await batcher.write([{ id: 2, val: "b" }, { id: 3, val: "c" }]);
637637

@@ -650,13 +650,13 @@ await batcher.close(); // flush + drain + seal (throws if write() called after)
650650
- `onError` is REQUIRED. The constructor throws without it. Rows leave the
651651
pending queue before the insert runs, so a failed auto-flush has no other way
652652
to be observed.
653-
- Always `await batcher.close()` at the end — never fire-and-forget.
653+
- Always `await batcher.close()` at the end: never fire-and-forget.
654654
- After `close()`, calling `write()` rejects.
655-
- Every row in a batch must have the SAME keys — a batched INSERT has one
655+
- Every row in a batch must have the SAME keys: a batched INSERT has one
656656
column list. A differing row rejects. Pass `heterogeneousRows: "union"` to
657657
insert the union of all columns with NULL for absent ones.
658658
- `await` each `write()` in a bulk import; that is what keeps memory flat.
659-
- Auto-flushes are fire-and-forget internally but tracked — `drain()` waits for
659+
- Auto-flushes are fire-and-forget internally but tracked: `drain()` waits for
660660
them.
661661

662662
---
@@ -691,14 +691,14 @@ await db.delete_from("users")
691691
.where(lt("created_at", new Date("2024-01-01")))
692692
.execute();
693693

694-
// WITHOUT .where() deletes ALL rows — guard with middleware
694+
// WITHOUT .where() deletes ALL rows: guard with middleware
695695
```
696696

697697
---
698698

699699
## 13. Raw SQL (Tagged Template)
700700

701-
Values are **always** bound parameters — never inlined into SQL text.
701+
Values are **always** bound parameters: never inlined into SQL text.
702702

703703
```ts
704704
const email = "ada@example.com";
@@ -732,7 +732,7 @@ Placeholder styles per dialect:
732732
## 14. Transactions
733733

734734
`transaction()` holds ONE connection for the whole callback. Use `tx` for every
735-
statement inside — `db` is a pool and would run the statement on a different
735+
statement inside: `db` is a pool and would run the statement on a different
736736
connection, outside the transaction.
737737

738738
```ts
@@ -756,7 +756,7 @@ await db.transaction(fn, {
756756
});
757757
```
758758

759-
`setup` is where `SET LOCAL` belongs — it is the mechanism PostgreSQL
759+
`setup` is where `SET LOCAL` belongs: it is the mechanism PostgreSQL
760760
row-level security depends on, and it is correct only inside a
761761
single-connection transaction.
762762

@@ -787,14 +787,14 @@ PostgreSQL / MySQL / Oracle only. SQLite, MSSQL and ClickHouse throw.
787787

788788
If the callback fails AND the `ROLLBACK` also fails, a
789789
`TransactionRollbackError` is thrown carrying both `cause` and `rollbackError`.
790-
The transaction's outcome is undetermined — reconcile, do not just retry.
790+
The transaction's outcome is undetermined: reconcile, do not just retry.
791791

792792
---
793793

794794
## 15. Middleware
795795

796796
```ts
797-
// Register in order — each calls next() to pass through
797+
// Register in order: each calls next() to pass through
798798
db.use(async (query, next) => {
799799
console.log("[sql]", query.sql, query.params);
800800
return next();
@@ -853,7 +853,7 @@ max<number>("score", "topScore"); // MAX(score) AS topScore
853853

854854
> **TypeScript type of exact numerics.** `decimal(p,s)`, `numeric(p,s)`,
855855
> `bigint()`, `bigserial()` and MSSQL `money()` infer as **`string`**, not
856-
> `number` — float64 cannot represent them exactly, and `pg`/`mysql2` return
856+
> `number`: float64 cannot represent them exactly, and `pg`/`mysql2` return
857857
> them as strings anyway. `integer`, `smallint`, `int`, `serial`, `float`,
858858
> `real` and `doublePrecision` remain `number`.
859859
@@ -1016,9 +1016,9 @@ const result: ClickHouseDataset = await db.select(
10161016
.group_by("tenant_id")
10171017
.execute();
10181018

1019-
result.data; // Record<string, unknown>[] — actual rows
1020-
result.meta; // { name: string; type: string }[] — column metadata
1021-
result.rows; // number — row count
1019+
result.data; // Record<string, unknown>[] : actual rows
1020+
result.meta; // { name: string; type: string }[] : column metadata
1021+
result.rows; // number : row count
10221022
result.statistics?.read_rows; // optional stats
10231023
```
10241024
@@ -1045,30 +1045,30 @@ result.statistics?.read_rows; // optional stats
10451045
**DO NOT** construct queries by string concatenation:
10461046
10471047
```ts
1048-
// WRONG — SQL injection risk
1048+
// WRONG: SQL injection risk
10491049
const rows = await db.execute(`SELECT * FROM users WHERE name = '${name}'`);
10501050

1051-
// CORRECT — use parameterised query or tagged template
1051+
// CORRECT: use parameterised query or tagged template
10521052
const rows = await db.sql`SELECT * FROM users WHERE name = ${name}`.execute();
10531053
```
10541054
10551055
**DO NOT** call `.stream()` or `.prepare()` on MySQL, MSSQL, Oracle, Snowflake,
1056-
Databricks, or ClickHouse engines — they throw.
1056+
Databricks, or ClickHouse engines: they throw.
10571057
10581058
**DO NOT** use `.returning()` on MySQL, MSSQL, Oracle, Snowflake, Databricks, or
1059-
ClickHouse — throws `"RETURNING is not supported by this dialect"`.
1059+
ClickHouse: throws `"RETURNING is not supported by this dialect"`.
10601060
1061-
**DO NOT** use `.full_join()` with SQLite, MySQL, or ClickHouse — throws at
1061+
**DO NOT** use `.full_join()` with SQLite, MySQL, or ClickHouse: throws at
10621062
compile time.
10631063
1064-
**DO NOT** call `batcher.write()` after `batcher.close()` — rejects.
1064+
**DO NOT** call `batcher.write()` after `batcher.close()`: rejects.
10651065
1066-
**DO NOT** forget `await batcher.close()` — rows may be left unwritten.
1066+
**DO NOT** forget `await batcher.close()`: rows may be left unwritten.
10671067
10681068
**DO NOT** use the pooled engine inside a transaction callback. Use `tx`:
10691069
10701070
```ts
1071-
// WRONG — this INSERT runs on a different connection, outside the transaction
1071+
// WRONG: this INSERT runs on a different connection, outside the transaction
10721072
await db.transaction(async (tx) => { await db.execute(insertSql); });
10731073

10741074
// CORRECT
@@ -1078,7 +1078,7 @@ await db.transaction(async (tx) => { await tx.execute(insertSql); });
10781078
**DO NOT** pass user input to `.order_by()`, `.group_by()`, `.select()`,
10791079
`.from()`, or a join `ON` condition. These cannot be bound parameters, so they
10801080
are interpolated. They are validated and will throw `UnsafeIdentifierError` on
1081-
anything dangerous, but that is a guard, not a licence — map a sort parameter
1081+
anything dangerous, but that is a guard, not a licence: map a sort parameter
10821082
through a fixed allow-list of column names:
10831083
10841084
```ts
@@ -1095,16 +1095,16 @@ They are typed `string` because float64 cannot hold them exactly. `Number(x)`
10951095
on a money column loses cents:
10961096
10971097
```ts
1098-
// WRONG — reintroduces the precision loss the string type exists to prevent
1098+
// WRONG: reintroduces the precision loss the string type exists to prevent
10991099
const total = rows.reduce((a, r) => a + Number(r.debit), 0);
11001100

1101-
// CORRECT — sum in SQL, or use a decimal library
1101+
// CORRECT: sum in SQL, or use a decimal library
11021102
const [{ total }] = await db.sql`SELECT SUM(debit)::text AS total FROM jurnal`.execute();
11031103
```
11041104
11051105
**DO validate incoming amounts with `decimal()` from `@coderbuzz/veta`**, not
11061106
`number()`. It takes and returns the same normalized string this package uses,
1107-
so there is no conversion at the HTTP boundary — and conversions are where
1107+
so there is no conversion at the HTTP boundary, and conversions are where
11081108
precision goes:
11091109
11101110
```ts
@@ -1121,7 +1121,7 @@ const postJournal = object({
11211121
**DO NOT** use `DELETE` or `UPDATE` without `.where()` unless you intend to
11221122
affect all rows. Add a middleware guard in production code.
11231123
1124-
**DO NOT** use `.unique()` on ClickHouse table columns — throws.
1124+
**DO NOT** use `.unique()` on ClickHouse table columns: throws.
11251125
11261126
---
11271127
@@ -1149,7 +1149,7 @@ await users.insert(db).values([{
11491149
active: true,
11501150
}]).execute();
11511151

1152-
// Read — typed result
1152+
// Read: typed result
11531153
const all = await users.from(db).fields("id", "name").where({ active: true })
11541154
.execute();
11551155

@@ -1270,7 +1270,7 @@ Version: 0.1.3
12701270
License: MIT
12711271
Type: ESM only (type: "module")
12721272
Peer deps (all optional): pg, mysql2, mssql, better-sqlite3, @db/sqlite, oracledb, snowflake-sdk, @databricks/sql
1273-
Runtime dep: @coderbuzz/veta (internal — schema coercion)
1273+
Runtime dep: @coderbuzz/veta (internal, schema coercion)
12741274
```
12751275
12761276
**Export map summary:**

0 commit comments

Comments
 (0)