@kequtech/tenon-orm 0.4.0 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -2
- package/README.md +93 -51
- package/dist/check.js +4 -2
- package/dist/cli.js +97 -40
- package/dist/config/init.d.ts +2 -0
- package/dist/config/init.js +34 -0
- package/dist/config/load.d.ts +7 -0
- package/dist/config/load.js +159 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.js +1 -0
- package/dist/connection.d.ts +5 -0
- package/dist/describe.js +3 -1
- package/dist/generation/output.js +0 -2
- package/dist/index.d.ts +2 -1
- package/dist/inspection/cli.d.ts +3 -4
- package/dist/inspection/cli.js +3 -13
- package/dist/inspection/connection.js +23 -19
- package/dist/inspection/options.d.ts +5 -7
- package/dist/inspection/options.js +49 -22
- package/dist/inspection/types.d.ts +3 -2
- package/dist/migrations/history.d.ts +1 -1
- package/dist/migrations/history.js +3 -2
- package/dist/validation.d.ts +2 -0
- package/dist/validation.js +6 -0
- package/docs/adoption-guide.md +94 -153
- package/docs/agent-guide.md +93 -176
- package/docs/configuration.md +146 -0
- package/docs/connections.md +187 -0
- package/docs/human-guide.md +150 -179
- package/docs/migrations.md +140 -0
- package/docs/mutations.md +104 -67
- package/docs/query-checking.md +122 -120
- package/docs/query-inspection.md +83 -76
- package/docs/reference.md +234 -474
- package/docs/schema-types.md +266 -0
- package/docs/sql.md +212 -0
- package/package.json +1 -1
package/docs/query-inspection.md
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Inspecting and testing queries
|
|
2
2
|
|
|
3
|
-
`describeQuery` asks PostgreSQL for a
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
`describeQuery` asks PostgreSQL for a finished statement's parameter and result metadata. It does
|
|
4
|
+
not bind values or execute the statement. Use it to inspect SQL and to support database tests. To
|
|
5
|
+
compare TypeScript source annotations instead, use [query checking](query-checking.md).
|
|
6
6
|
|
|
7
7
|
## Inspect completed SQL
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
|
+
// scripts/inspect-query.ts
|
|
10
11
|
import { describeQuery, sql } from '@kequtech/tenon-orm';
|
|
12
|
+
import config from '../tenon.config.ts';
|
|
11
13
|
|
|
12
|
-
const connection =
|
|
13
|
-
if (!connection) throw new Error('DATABASE_URL is required');
|
|
14
|
+
const connection = config.connection;
|
|
14
15
|
|
|
15
16
|
const query = sql<{ id: number; total: string }>`
|
|
16
17
|
SELECT ${42}::integer AS id, count(*) AS total FROM pg_catalog.pg_class
|
|
@@ -18,16 +19,19 @@ const query = sql<{ id: number; total: string }>`
|
|
|
18
19
|
const description = await describeQuery({ connection, query });
|
|
19
20
|
```
|
|
20
21
|
|
|
21
|
-
`query` accepts SQL text or an object
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
take locks.
|
|
22
|
+
Pass the same completed query to inspection and execution. `query` accepts SQL text or an object
|
|
23
|
+
with `text`, including a Tenon `SqlQuery`. Raw SQL uses `$1` placeholders; templates use `${value}`.
|
|
24
|
+
Inspection checks neither `query.values` nor its `sql<Row>` annotation.
|
|
25
|
+
|
|
26
|
+
Cast standalone parameters so PostgreSQL can infer their types without values. Alternatively pass
|
|
27
|
+
`query: { text, parameterTypes: [oid, ...] }` for explicit Describe hints. PostgreSQL still parses
|
|
28
|
+
and analyzes the statement, which can take locks.
|
|
28
29
|
|
|
29
30
|
## Session and timeouts
|
|
30
31
|
|
|
32
|
+
Inspection opens and closes its own short-lived session. `connection` accepts a PostgreSQL URI or pgclear pool-option values;
|
|
33
|
+
it does not reuse a runtime pool or transaction:
|
|
34
|
+
|
|
31
35
|
```ts
|
|
32
36
|
const description = await describeQuery({
|
|
33
37
|
connection,
|
|
@@ -37,48 +41,51 @@ const description = await describeQuery({
|
|
|
37
41
|
});
|
|
38
42
|
```
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
Match runtime settings. Optional `session.role` uses SET ROLE when permitted; it does not load that
|
|
45
|
+
role's login defaults. Omitted settings use the inspection connection's defaults. Explicit undefined
|
|
46
|
+
behaves like omission. Unknown keys and invalid settings reject.
|
|
43
47
|
|
|
44
48
|
`context` reports login/effective roles, configured/resolved search paths, timezone, database, and
|
|
45
|
-
effective statement/lock timeouts.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
| `
|
|
49
|
+
effective statement/lock timeouts. Mode is `separate-session`, read mapping profile `pgclear`.
|
|
50
|
+
Temporary tables, uncommitted changes, and transaction-local state are not inherited. PG environment
|
|
51
|
+
variables, pgpass, service files, and `.env` are not loaded.
|
|
52
|
+
|
|
53
|
+
| Timeout | Default | Scope |
|
|
54
|
+
| ------------- | -------- | ----------------------------- |
|
|
55
|
+
| `connectMs` | 3,000 ms | Connection and authentication |
|
|
56
|
+
| `statementMs` | 5,000 ms | Each server statement |
|
|
57
|
+
| `lockMs` | 1,000 ms | Lock wait |
|
|
58
|
+
|
|
59
|
+
Overrides are positive integer milliseconds up to 2,147,483,647. These are operation bounds, not a
|
|
60
|
+
single overall deadline. URI settings cannot disable statement/lock bounds. pg-specific
|
|
61
|
+
`query_timeout` and `options` URI parameters are unsupported; use the options above.
|
|
62
|
+
|
|
63
|
+
## Read the report
|
|
64
|
+
|
|
65
|
+
| Field | Meaning |
|
|
66
|
+
| ----------------------- | ----------------------------------------------------------------------------------------- |
|
|
67
|
+
| `parameters` | OIDs, qualified PostgreSQL names, and read representations; these are not input contracts |
|
|
68
|
+
| `columns` | Ordered names, PostgreSQL types, read representations, and available source metadata |
|
|
69
|
+
| `columns[].nullability` | Always `"unknown"`; source `notNull` is informational |
|
|
70
|
+
| `rowType` | Conservative `DescribedRow` declaration, or null for no columns/duplicate names |
|
|
71
|
+
| `issues` | Findings with stable `code` and descriptive messages |
|
|
72
|
+
| `context` | Effective inspection-session settings |
|
|
73
|
+
|
|
74
|
+
Known read types in `rowType` include null; unknown already includes it. A source column's NOT NULL
|
|
75
|
+
constraint cannot prove result nullability through outer joins, expressions, or grouping. JSON
|
|
76
|
+
members remain unknown.
|
|
77
|
+
|
|
78
|
+
| Finding code | Meaning |
|
|
79
|
+
| --------------------- | ----------------------------------------------------- |
|
|
80
|
+
| `nullability-unknown` | Result nullability is unresolved |
|
|
81
|
+
| `shape-unknown` | Listed columns have unknown shapes/read types |
|
|
82
|
+
| `unmapped-type` | A custom decoder has no known representation |
|
|
83
|
+
| `duplicate-column` | Repeated result names would fail object-row execution |
|
|
77
84
|
|
|
78
85
|
## Custom codecs
|
|
79
86
|
|
|
80
|
-
|
|
81
|
-
|
|
87
|
+
Programmatic inspection inherits `connection.codecs` when connection-option values are supplied.
|
|
88
|
+
With a URI, pass the runtime registry as `codecs`; omission uses defaults:
|
|
82
89
|
|
|
83
90
|
```ts
|
|
84
91
|
import { builtin, defaultCodecs } from '@kequtech/tenon-orm';
|
|
@@ -87,21 +94,21 @@ const codecs = defaultCodecs.with(builtin.jsonbText);
|
|
|
87
94
|
const description = await describeQuery({ connection, query, codecs });
|
|
88
95
|
```
|
|
89
96
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
not
|
|
98
|
-
|
|
97
|
+
That registry reports JSONB output as string. Scalar overrides affect derived built-in arrays;
|
|
98
|
+
explicit array overrides win. Custom `text.typescript` expressions appear in the conservative row
|
|
99
|
+
declaration and are shared with generation/source checking. Without known metadata, a custom
|
|
100
|
+
decoder remains unknown even for a familiar scalar/enum/domain OID. Catalog base types do not
|
|
101
|
+
replace the wire OID's decoder contract.
|
|
102
|
+
|
|
103
|
+
Inspection's own catalog/context queries use default decoding. Reported parameter read types are
|
|
104
|
+
not encoding rules: exact text can be a valid int8 input while its output also uses text by default.
|
|
105
|
+
See [schema mappings and codecs](schema-types.md#custom-codecs).
|
|
99
106
|
|
|
100
107
|
## Test the same statement
|
|
101
108
|
|
|
102
|
-
For
|
|
103
|
-
|
|
104
|
-
`{ id: 2, email: 'lin@example.test', active: false }
|
|
109
|
+
Use a dedicated test database. For `public.users` from [Getting started](human-guide.md), assume a
|
|
110
|
+
fixture contains `{ id: 1, email: 'ada@example.test', active: true }` and
|
|
111
|
+
`{ id: 2, email: 'lin@example.test', active: false }`. Here `db` and `connection` select that database:
|
|
105
112
|
|
|
106
113
|
```ts
|
|
107
114
|
import assert from 'node:assert/strict';
|
|
@@ -133,15 +140,15 @@ for (const activeOnly of [false, true]) {
|
|
|
133
140
|
}
|
|
134
141
|
```
|
|
135
142
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
rows
|
|
139
|
-
|
|
140
|
-
|
|
143
|
+
Describe establishes metadata; execution tests establish behavior. Describe may succeed even when
|
|
144
|
+
bound values are invalid, constraints would reject a write, or the role lacks execution permission.
|
|
145
|
+
Test actual rows, write effects, authorization, JSON validation, and rollback. Exercise meaningful
|
|
146
|
+
composition variants and keep unexpected findings visible. Do not test query behavior by matching
|
|
147
|
+
words in documentation or SQL strings.
|
|
141
148
|
|
|
142
149
|
## CLI
|
|
143
150
|
|
|
144
|
-
Save SQL
|
|
151
|
+
Save raw SQL in `query.sql`:
|
|
145
152
|
|
|
146
153
|
```sql
|
|
147
154
|
SELECT $1::integer AS id, count(*) AS total FROM pg_catalog.pg_class;
|
|
@@ -152,12 +159,12 @@ npx tenon describe query.sql
|
|
|
152
159
|
npx tenon describe query.sql --search-path app,public --time-zone UTC
|
|
153
160
|
```
|
|
154
161
|
|
|
155
|
-
The CLI
|
|
156
|
-
`--
|
|
157
|
-
`--
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
connection, timeout, or PostgreSQL analysis failed. The inspected statement is never executed.
|
|
162
|
+
The CLI requires [shared configuration](configuration.md) and prints JSON using its connection
|
|
163
|
+
and codec registry. Pass `--config path` to select a specific module. Session flags are `--role`,
|
|
164
|
+
`--search-path`, and `--time-zone`; timeout flags are `--connect-timeout-ms`,
|
|
165
|
+
`--statement-timeout-ms`, and `--lock-timeout-ms`. Unsupported flags, including `--namespace` and
|
|
166
|
+
`--check`, reject.
|
|
161
167
|
|
|
162
|
-
|
|
163
|
-
|
|
168
|
+
Exit 0 means inspection succeeded, even with unresolved findings. Input, connection, timeout, or
|
|
169
|
+
PostgreSQL analysis failures exit 1. The inspected statement is never executed. Generated-file
|
|
170
|
+
freshness uses `tenon generate --check`, a separate operation.
|