qubu 0.0.0 → 0.3.4
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/dist/canonical-BbnqavJm.mjs +972 -0
- package/dist/codegen.d.mts +107 -0
- package/dist/codegen.mjs +1199 -0
- package/dist/column-CXMxx8Hq.mjs +118 -0
- package/dist/complete-D5Djh-zo.mjs +1788 -0
- package/dist/complete-types-BdFqUfbb.d.mts +371 -0
- package/dist/core.d.mts +33 -0
- package/dist/core.mjs +36 -0
- package/dist/ddl.d.mts +118 -0
- package/dist/ddl.mjs +1120 -0
- package/dist/dialect-b2-Z6uBF.mjs +13 -0
- package/dist/diff.d.mts +2 -0
- package/dist/diff.mjs +883 -0
- package/dist/drizzle-mysql.d.mts +24 -0
- package/dist/drizzle-mysql.mjs +72 -0
- package/dist/drizzle-postgres.d.mts +24 -0
- package/dist/drizzle-postgres.mjs +73 -0
- package/dist/drizzle-sqlite.d.mts +24 -0
- package/dist/drizzle-sqlite.mjs +82 -0
- package/dist/drizzle.d.mts +13 -0
- package/dist/drizzle.mjs +2 -0
- package/dist/errors-BGCoLe_r.mjs +14 -0
- package/dist/errors-Dxv73YJu.mjs +26 -0
- package/dist/explain-CkIK13L_.mjs +98 -0
- package/dist/index-Ds7-mhJi.d.mts +146 -0
- package/dist/index-Dug5HnLB.d.mts +204 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +1276 -0
- package/dist/introspection.d.mts +117 -0
- package/dist/introspection.mjs +4341 -0
- package/dist/json-CUZlv4HT.mjs +169 -0
- package/dist/migration.d.mts +215 -0
- package/dist/migration.mjs +1161 -0
- package/dist/mysql-DqkqXB6A.mjs +355 -0
- package/dist/mysql.d.mts +5 -0
- package/dist/mysql.mjs +50 -0
- package/dist/naming-QVCOnSj2.mjs +20 -0
- package/dist/omit-OxV58AwX.mjs +5 -0
- package/dist/on-conflict-BxnxubMb.mjs +143 -0
- package/dist/postgres-DEBBeh52.mjs +235 -0
- package/dist/postgres.d.mts +123 -0
- package/dist/postgres.mjs +52 -0
- package/dist/registry-BufIskVN.mjs +569 -0
- package/dist/relational-DCZrrNia.mjs +77 -0
- package/dist/runtime-BTr-MTlo.mjs +193 -0
- package/dist/schema.d.mts +2 -0
- package/dist/schema.mjs +5 -0
- package/dist/serialize-PF1cfH2P.mjs +556 -0
- package/dist/snapshot-CWPgzxNx.mjs +392 -0
- package/dist/snapshot.d.mts +4 -0
- package/dist/snapshot.mjs +8 -0
- package/dist/source-DUoJVXmL.mjs +68 -0
- package/dist/sqlite-BU6DBxef.mjs +320 -0
- package/dist/sqlite.d.mts +5 -0
- package/dist/sqlite.mjs +48 -0
- package/dist/standard-BTVYKh_F.mjs +12 -0
- package/dist/table-llv9tsZ8.mjs +384 -0
- package/dist/types-4Q076HKo.d.mts +3636 -0
- package/dist/types-BX0mckiU.d.mts +644 -0
- package/dist/types-Cec0xzo4.mjs +116 -0
- package/dist/types-H4vyCw8_.d.mts +45 -0
- package/dist/value-BvilP0oz.mjs +29 -0
- package/dist/vite/ambient.d.ts +449 -0
- package/dist/vite.d.mts +35 -0
- package/dist/vite.mjs +498 -0
- package/docs/config.json +21 -0
- package/docs/dialects-and-execution.md +427 -0
- package/docs/getting-started.md +96 -0
- package/docs/guides/compose-queries.md +225 -0
- package/docs/guides/drizzle.md +130 -0
- package/docs/guides/extensions/dialects.md +40 -0
- package/docs/guides/extensions/overview.md +36 -0
- package/docs/guides/extensions/sources-and-clauses.md +82 -0
- package/docs/guides/extensions/typed-expressions.md +94 -0
- package/docs/guides/extensions/unsafe-syntax.md +27 -0
- package/docs/guides/json.md +65 -0
- package/docs/guides/mutations.md +138 -0
- package/docs/guides/select/conditions.md +110 -0
- package/docs/guides/select/grouping-and-windows.md +82 -0
- package/docs/guides/select/ordering-and-pagination.md +78 -0
- package/docs/guides/select/overview.md +139 -0
- package/docs/guides/sql-templates.md +157 -0
- package/docs/guides/vite-plugin.md +92 -0
- package/docs/index.md +111 -0
- package/docs/query-model/fragments.md +109 -0
- package/docs/query-model/result-shapes.md +125 -0
- package/docs/query-model/source-scope.md +182 -0
- package/docs/reference/introspection-support.md +196 -0
- package/docs/reference/mysql-snapshot.md +55 -0
- package/docs/reference/postgres-snapshot.md +61 -0
- package/docs/reference/sqlite-snapshot.md +53 -0
- package/docs/reference/supported-surface.md +104 -0
- package/docs/schema/catalog-model.md +58 -0
- package/docs/schema/code-generation.md +139 -0
- package/docs/schema/columns-and-writes.md +130 -0
- package/docs/schema/constraints-and-indexes.md +180 -0
- package/docs/schema/ddl-emission.md +75 -0
- package/docs/schema/diff.md +75 -0
- package/docs/schema/introspection.md +214 -0
- package/docs/schema/migration-plans.md +76 -0
- package/docs/schema/snapshots.md +93 -0
- package/docs/schema/storage-and-schema-sql.md +104 -0
- package/docs/schema/tables-and-names.md +106 -0
- package/docs/sql-semantic-types.md +141 -0
- package/docs/troubleshooting.md +151 -0
- package/package.json +132 -10
- package/skills/qubu/SKILL.md +42 -0
- package/skills/qubu/agents/openai.yaml +6 -0
- package/readme.md +0 -1
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Source scope
|
|
2
|
+
|
|
3
|
+
> Keep each column tied to the table, alias, CTE, or derived source that provides it; use this page to fix scope errors and intentional correlations.
|
|
4
|
+
|
|
5
|
+
A column carries the identity of the source that provides it. Qubu checks that
|
|
6
|
+
identity when you assemble a query. The source must appear in `FROM` or a join
|
|
7
|
+
before its columns can appear in the projection, a predicate, or another
|
|
8
|
+
clause.
|
|
9
|
+
|
|
10
|
+
## Add the source that owns a column
|
|
11
|
+
|
|
12
|
+
Qubu reports a missing source when a query selects a column from a table that
|
|
13
|
+
does not appear in the query:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { from, integer, select, table, text } from 'qubu'
|
|
17
|
+
|
|
18
|
+
const users = table('users', {
|
|
19
|
+
id: integer(),
|
|
20
|
+
})
|
|
21
|
+
const posts = table('posts', {
|
|
22
|
+
id: integer(),
|
|
23
|
+
title: text(),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
select({ id: users.id }, from(posts))
|
|
27
|
+
// Type error: users is not available in this query scope
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Add the source that owns the column, or join it with a condition that refers to
|
|
31
|
+
both sources:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { eq, from, innerJoin, integer, select, table } from 'qubu'
|
|
35
|
+
|
|
36
|
+
const users = table('users', {
|
|
37
|
+
id: integer(),
|
|
38
|
+
})
|
|
39
|
+
const posts = table('posts', {
|
|
40
|
+
id: integer(),
|
|
41
|
+
authorId: integer(),
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const query = select(
|
|
45
|
+
{ userId: users.id, postId: posts.id },
|
|
46
|
+
from(users),
|
|
47
|
+
innerJoin(posts, eq(users.id, posts.authorId))
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Use the source identity you introduced
|
|
52
|
+
|
|
53
|
+
Aliases, CTEs, derived queries, and custom sources expose new source identities.
|
|
54
|
+
Use their columns after wrapping the original source:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { alias, from, integer, select, table, text } from 'qubu'
|
|
58
|
+
|
|
59
|
+
const users = table('users', {
|
|
60
|
+
id: integer(),
|
|
61
|
+
name: text(),
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const author = alias(users, 'author')
|
|
65
|
+
const query = select({ name: author.name }, from(author))
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`author.name` belongs to the `author` source. `users.name` is a different source
|
|
69
|
+
identity after aliasing, even though both columns refer to the same table.
|
|
70
|
+
|
|
71
|
+
The same rule applies to a CTE or derived query. A query's selected row becomes
|
|
72
|
+
the set of columns exposed by its new source:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { alias, from, lower, select } from 'qubu'
|
|
76
|
+
|
|
77
|
+
const names = select({ name: lower(users.name) }, from(users))
|
|
78
|
+
const namesSource = alias(names, 'names')
|
|
79
|
+
|
|
80
|
+
const query = select({ name: namesSource.name }, from(namesSource))
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Use [Compose queries](../guides/compose-queries.md) for the full CTE,
|
|
84
|
+
derived-table, scalar-subquery, and set-operation workflow.
|
|
85
|
+
|
|
86
|
+
## Produce a custom FROM source
|
|
87
|
+
|
|
88
|
+
Use `customSource()` for a table-valued function or another relation that
|
|
89
|
+
`table()` cannot describe. The producer supplies the source identity, column
|
|
90
|
+
definitions, and complete relation renderer:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { eq, from, integer, select, text, where } from 'qubu'
|
|
94
|
+
import { identifier } from 'qubu/core'
|
|
95
|
+
import { customSource } from 'qubu/schema'
|
|
96
|
+
|
|
97
|
+
const entries = customSource({
|
|
98
|
+
identity: {
|
|
99
|
+
sourceKind: 'table-function',
|
|
100
|
+
name: 'json_each',
|
|
101
|
+
alias: 'entry',
|
|
102
|
+
},
|
|
103
|
+
sourceKind: 'table-function',
|
|
104
|
+
reference: identifier('entry'),
|
|
105
|
+
columns: {
|
|
106
|
+
key: integer(),
|
|
107
|
+
value: text({ nullable: true }),
|
|
108
|
+
},
|
|
109
|
+
render(context) {
|
|
110
|
+
context.append('json_each(')
|
|
111
|
+
context.parameter('{"a":1}')
|
|
112
|
+
context.append(') AS ')
|
|
113
|
+
context.render(identifier('entry'))
|
|
114
|
+
},
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const query = select(
|
|
118
|
+
{ value: entries.value },
|
|
119
|
+
from(entries),
|
|
120
|
+
where(eq(entries.key, 7))
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`identity` is the type-level source key. `reference` is the SQL qualifier used
|
|
125
|
+
by the generated columns. The nullable `value` column stays nullable, and a
|
|
126
|
+
`leftJoin()` adds outer-join nullability to every selected column from
|
|
127
|
+
`entries`.
|
|
128
|
+
|
|
129
|
+
Bind function arguments with `context.parameter()`. The normal renderer then
|
|
130
|
+
keeps those values in placeholder order.
|
|
131
|
+
|
|
132
|
+
## Correlate an inner query
|
|
133
|
+
|
|
134
|
+
Use `correlate()` when an inner query intentionally reads a source from its
|
|
135
|
+
enclosing query. The provision changes type checking but emits no SQL:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import {
|
|
139
|
+
correlate,
|
|
140
|
+
crossJoin,
|
|
141
|
+
eq,
|
|
142
|
+
from,
|
|
143
|
+
integer,
|
|
144
|
+
lateral,
|
|
145
|
+
select,
|
|
146
|
+
table,
|
|
147
|
+
where,
|
|
148
|
+
} from 'qubu'
|
|
149
|
+
|
|
150
|
+
const users = table('users', { id: integer() })
|
|
151
|
+
const posts = table('posts', {
|
|
152
|
+
id: integer(),
|
|
153
|
+
authorId: integer(),
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
const recentPost = select(
|
|
157
|
+
{ id: posts.id },
|
|
158
|
+
from(posts),
|
|
159
|
+
correlate(users),
|
|
160
|
+
where(eq(posts.authorId, users.id))
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
const recent = lateral(recentPost, 'recent_post')
|
|
164
|
+
const query = select(
|
|
165
|
+
{ userId: users.id, postId: recent.id },
|
|
166
|
+
from(users),
|
|
167
|
+
crossJoin(recent)
|
|
168
|
+
)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The inner query consumes `posts` locally. The enclosing `users` source satisfies
|
|
172
|
+
its outer requirement. The same requirement flows through `scalar()`,
|
|
173
|
+
`exists()`, and `inQuery()`.
|
|
174
|
+
|
|
175
|
+
## Read next
|
|
176
|
+
|
|
177
|
+
- [Result shapes and cardinality](result-shapes.md) explains projection names,
|
|
178
|
+
outer-join nullability, and scalar subqueries.
|
|
179
|
+
- [Fragments and metadata](fragments.md) explains the type facts that carry
|
|
180
|
+
source requirements through custom expressions and clauses.
|
|
181
|
+
- [Extend Qubu](../guides/extensions/overview.md) shows how to publish a custom
|
|
182
|
+
source or clause.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Introspection support
|
|
2
|
+
|
|
3
|
+
> Look up which catalog facts each adapter reads, which versions it accepts, and which database features remain outside Snapshot v1.
|
|
4
|
+
|
|
5
|
+
The optional `qubu/introspection` entrypoint reads one selected database
|
|
6
|
+
namespace through a user-owned `CatalogConnection`. It returns normalized
|
|
7
|
+
catalog data and can map that data to canonical Snapshot v1 or v2. The
|
|
8
|
+
application owns the driver and connection lifecycle. Snapshot diffing,
|
|
9
|
+
migration planning, and DDL emission are separate Qubu capabilities; see the
|
|
10
|
+
[ownership map](supported-surface.md#ownership-boundary).
|
|
11
|
+
|
|
12
|
+
## Version and namespace baseline
|
|
13
|
+
|
|
14
|
+
| Adapter | Baseline | Selected namespace | Product policy |
|
|
15
|
+
| ---------- | -------- | ----------------------------- | ----------------------------------- |
|
|
16
|
+
| PostgreSQL | 12+ | one PostgreSQL schema | PostgreSQL only |
|
|
17
|
+
| SQLite | 3.37+ | one database, normally `main` | attached databases are not combined |
|
|
18
|
+
| MySQL | 8.0.16+ | one MySQL database | MariaDB requires a separate adapter |
|
|
19
|
+
|
|
20
|
+
Version and product failures are structured diagnostics. The adapters do not
|
|
21
|
+
silently downgrade to a different product's catalog rules.
|
|
22
|
+
|
|
23
|
+
## Catalog facts
|
|
24
|
+
|
|
25
|
+
| Fact | PostgreSQL | SQLite | MySQL |
|
|
26
|
+
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
27
|
+
| Tables and visible columns | `pg_class`, `pg_attribute` | `sqlite_schema`, `table_xinfo` | `TABLES`, `COLUMNS` |
|
|
28
|
+
| Native storage | `format_type` | declared type | `COLUMN_TYPE` |
|
|
29
|
+
| Defaults | `pg_get_expr` | `dflt_value` | `COLUMN_DEFAULT` |
|
|
30
|
+
| Generated columns | stored expressions | stored/virtual when CREATE SQL is recoverable | stored/virtual and `GENERATION_EXPRESSION` |
|
|
31
|
+
| Identity behavior | `attidentity` | `INTEGER PRIMARY KEY`, `AUTOINCREMENT` | `AUTO_INCREMENT` |
|
|
32
|
+
| Keys and foreign keys | `pg_constraint` | table metadata and `foreign_key_list` | `TABLE_CONSTRAINTS`, `KEY_COLUMN_USAGE`, `REFERENTIAL_CONSTRAINTS` |
|
|
33
|
+
| Checks | `pg_constraint` and decompiled definition | CREATE SQL | `CHECK_CONSTRAINTS` |
|
|
34
|
+
| Indexes | `pg_index`, access method, decompiled terms | `index_list`, `index_xinfo`, schema SQL | `STATISTICS` |
|
|
35
|
+
| Complete PostgreSQL objects | views/materialized views, sequences, enums/domains, collations, triggers, routines, partitions, row-level policies, comments/owners, and extensions | N/A | N/A |
|
|
36
|
+
| Complete SQLite objects | N/A | views and triggers when CREATE SQL is recoverable; table/view columns from `table_xinfo` | N/A |
|
|
37
|
+
| Complete MySQL objects | N/A | N/A | typed views and view columns, routines and parameters, triggers, partitions, used collations, comments, and opaque scheduled events |
|
|
38
|
+
| Unsupported MySQL families | N/A | N/A | sequences, materialized views, row-level security (RLS) policies, extensions, and ownership |
|
|
39
|
+
| Deferred or opaque objects | foreign tables and relation kinds without a safe typed mapping | virtual/shadow tables, attached-database boundaries, and unrecoverable definitions | scheduled events are opaque; unknown or other non-base table rows are deferred |
|
|
40
|
+
|
|
41
|
+
The readers preserve physical names and use database catalog identifiers only
|
|
42
|
+
as current-run join keys. The normalized catalog keeps opaque SQL and its
|
|
43
|
+
source, whether it came from a catalog value, a database decompiler, or CREATE
|
|
44
|
+
SQL.
|
|
45
|
+
|
|
46
|
+
## PostgreSQL complete catalog surface
|
|
47
|
+
|
|
48
|
+
The PostgreSQL reader also fills the complete normalized catalog used by
|
|
49
|
+
Snapshot v2. It keeps PostgreSQL OIDs in current-run physical references and
|
|
50
|
+
keeps decompiler output such as `pg_get_viewdef`, `pg_get_triggerdef`, and
|
|
51
|
+
`pg_get_functiondef` as tagged SQL data. It does not evaluate that text.
|
|
52
|
+
|
|
53
|
+
The query and normalization layout follows the catalog-oriented parts of the
|
|
54
|
+
[Drizzle PostgreSQL introspector](https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-kit/src/introspect-pg.ts),
|
|
55
|
+
while Qubu keeps the result as typed data instead of generating TypeScript
|
|
56
|
+
declarations.
|
|
57
|
+
|
|
58
|
+
Use `mapCatalogToCompleteSnapshot()` for this object set. Use
|
|
59
|
+
`mapCatalogToSnapshot()` when a caller explicitly needs the existing table-only
|
|
60
|
+
Snapshot v1.
|
|
61
|
+
|
|
62
|
+
## SQLite complete catalog surface
|
|
63
|
+
|
|
64
|
+
The SQLite reader uses fixed statements over `sqlite_schema` and the
|
|
65
|
+
table-valued PRAGMAs `database_list`, `table_list`, `table_xinfo`,
|
|
66
|
+
`index_list`, `index_xinfo`, and `foreign_key_list`. Each table-valued PRAGMA
|
|
67
|
+
receives bound namespace or object parameters through the caller's
|
|
68
|
+
`CatalogConnection`. The reader does not interpolate a database name into SQL.
|
|
69
|
+
|
|
70
|
+
SQLite views and triggers become typed complete catalog objects when their
|
|
71
|
+
CREATE SQL has a recoverable definition and target. Their SQL remains tagged
|
|
72
|
+
opaque data with a `sqlite` dialect and a catalog reference. Generated column
|
|
73
|
+
expressions, declared storage types, SQLite affinity, rowid aliases, and
|
|
74
|
+
`AUTOINCREMENT` are kept as column or identity dialect extensions. User indexes
|
|
75
|
+
retain ordered column or expression terms and partial predicates. Inline UNIQUE
|
|
76
|
+
constraints are recovered from their internal indexes with deterministic Qubu
|
|
77
|
+
names, so SQLite's `sqlite_autoindex_*` names do not become persisted logical
|
|
78
|
+
IDs.
|
|
79
|
+
|
|
80
|
+
SQLite virtual tables and shadow tables remain typed deferred objects with an
|
|
81
|
+
`unmodeled-object` diagnostic. A selected attached database can expose table
|
|
82
|
+
PRAGMA rows, but its schema SQL is outside the fixed `main` and `temp`
|
|
83
|
+
statements. Qubu marks that result as limited and keeps other attached
|
|
84
|
+
databases as opaque boundary records. It never combines attached databases into
|
|
85
|
+
the selected namespace. SQLite does not provide the PostgreSQL object families
|
|
86
|
+
such as routines, materialized views, policies, or ownership, so the reader
|
|
87
|
+
does not fabricate them.
|
|
88
|
+
|
|
89
|
+
`mapCatalogToCompleteSnapshot()` retains the typed views, triggers, deferred
|
|
90
|
+
objects, opaque boundaries, and dialect extensions in Snapshot v2. The existing
|
|
91
|
+
`mapCatalogToSnapshot()` still maps only tables and preserves Snapshot v1
|
|
92
|
+
behavior.
|
|
93
|
+
|
|
94
|
+
The query and normalization seams follow the catalog-reading portions of the
|
|
95
|
+
[Drizzle SQLite introspector](https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-kit/src/introspect-sqlite.ts).
|
|
96
|
+
Drizzle's module generates source declarations; Qubu keeps the same SQLite
|
|
97
|
+
metadata sources as normalized data and never evaluates database-provided SQL.
|
|
98
|
+
The optional [source generator](../schema/code-generation.md) consumes the
|
|
99
|
+
strict Snapshot v1 result through a separate controlled printer.
|
|
100
|
+
|
|
101
|
+
## MySQL 8 complete catalog surface
|
|
102
|
+
|
|
103
|
+
The MySQL reader accepts MySQL 8.0.16 and later within the MySQL 8 series. It
|
|
104
|
+
rejects MariaDB and older MySQL versions instead of applying MySQL catalog
|
|
105
|
+
rules to a different product or server version. It reads `INFORMATION_SCHEMA`
|
|
106
|
+
rows for one selected database and retains database-provided SQL as tagged,
|
|
107
|
+
unevaluated MySQL data.
|
|
108
|
+
|
|
109
|
+
MySQL has typed complete records for views, routines and their parameters,
|
|
110
|
+
triggers, partitions, collations used by selected tables or columns, and
|
|
111
|
+
comments. View definitions come from `INFORMATION_SCHEMA.VIEWS`; each view's
|
|
112
|
+
columns are joined back to the matching `COLUMNS` rows by physical table name,
|
|
113
|
+
so the complete Snapshot v2 cross-reference points at the view's own column
|
|
114
|
+
IDs. Missing view definitions or unresolved trigger, partition, or other
|
|
115
|
+
object references become deferred records with diagnostics.
|
|
116
|
+
|
|
117
|
+
Scheduled events are retained as `CatalogOpaqueObject` records. Their event
|
|
118
|
+
metadata and definition remain opaque, and the reader emits an
|
|
119
|
+
`unmodeled-object` warning. Snapshot v2 keeps these records in
|
|
120
|
+
`opaqueObjects`; they are not treated as typed routines, triggers, or
|
|
121
|
+
migration operations.
|
|
122
|
+
|
|
123
|
+
The MySQL reader does not expose typed sequences or materialized views. A
|
|
124
|
+
sequence-like or other non-base table row is retained as a deferred object
|
|
125
|
+
when the catalog reports one. MySQL row-level security (RLS) policies,
|
|
126
|
+
extension objects, and ownership are unsupported complete families. Definers on
|
|
127
|
+
views, routines, triggers, and events remain dialect metadata; they do not
|
|
128
|
+
become ownership records. The corresponding capability flags are false.
|
|
129
|
+
|
|
130
|
+
The query and normalization layout follows the catalog-reading portions of the
|
|
131
|
+
[Drizzle MySQL introspector](https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-kit/src/introspect-mysql.ts),
|
|
132
|
+
while Qubu keeps the result as typed data instead of generating TypeScript
|
|
133
|
+
declarations.
|
|
134
|
+
|
|
135
|
+
Use `mapCatalogToCompleteSnapshot()` to retain these typed MySQL families and
|
|
136
|
+
the opaque or deferred boundaries in Snapshot v2. Use `mapCatalogToSnapshot()`
|
|
137
|
+
when a caller explicitly needs the existing table-only Snapshot v1.
|
|
138
|
+
|
|
139
|
+
## Snapshot v1 surface
|
|
140
|
+
|
|
141
|
+
The mapper can emit these facts in canonical Snapshot v1:
|
|
142
|
+
|
|
143
|
+
- one namespace and ordinary tables;
|
|
144
|
+
- exact dialect-native column storage;
|
|
145
|
+
- nullability and unambiguous literal or opaque SQL defaults;
|
|
146
|
+
- generated columns with stored or virtual mode;
|
|
147
|
+
- identities kept separate from defaults and generated columns;
|
|
148
|
+
- primary, unique, nullable-unique, foreign-key, and check constraints;
|
|
149
|
+
- ordered column or expression index terms, predicates, included columns, and
|
|
150
|
+
representable dialect extensions;
|
|
151
|
+
- stable logical IDs with physical names preserved separately.
|
|
152
|
+
|
|
153
|
+
Strict mode returns no snapshot when a supported table fact cannot be mapped
|
|
154
|
+
soundly. Lossy mode is explicit and marks warnings in the result. A digest is
|
|
155
|
+
canonical content, not an identity or rename marker.
|
|
156
|
+
|
|
157
|
+
## Deferred and limited features
|
|
158
|
+
|
|
159
|
+
The following remain catalog facts or diagnostics rather than fabricated
|
|
160
|
+
Snapshot v1 objects:
|
|
161
|
+
|
|
162
|
+
- views and materialized views;
|
|
163
|
+
- sequences, enums, domains, routines, triggers, policies, extensions,
|
|
164
|
+
collations, comments, and partition metadata;
|
|
165
|
+
- PostgreSQL identity sequence options that have no typed Snapshot v1 field;
|
|
166
|
+
- SQLite virtual/shadow tables, attached namespaces, and unrecoverable
|
|
167
|
+
generated or expression definitions;
|
|
168
|
+
- MySQL/MariaDB differences, prefix indexes, invisible indexes, and advanced
|
|
169
|
+
functional, full-text, or spatial index semantics.
|
|
170
|
+
|
|
171
|
+
Use the complete catalog and Snapshot v2 mapper to retain supported PostgreSQL
|
|
172
|
+
and MySQL families. MySQL scheduled events stay opaque, and MySQL sequences,
|
|
173
|
+
materialized views, row-level security (RLS) policies, extension objects, and
|
|
174
|
+
ownership stay unsupported or deferred. When a row cannot be normalized safely, the reader
|
|
175
|
+
keeps a typed deferred or opaque record and emits a diagnostic instead of
|
|
176
|
+
dropping it.
|
|
177
|
+
|
|
178
|
+
## Upstream references
|
|
179
|
+
|
|
180
|
+
The dialect reader structure follows useful patterns from Drizzle Kit while
|
|
181
|
+
keeping Qubu's output data-only:
|
|
182
|
+
|
|
183
|
+
- [Drizzle PostgreSQL introspector](https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-kit/src/introspect-pg.ts)
|
|
184
|
+
- [Drizzle SQLite introspector](https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-kit/src/introspect-sqlite.ts)
|
|
185
|
+
- [Drizzle MySQL introspector](https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-kit/src/introspect-mysql.ts)
|
|
186
|
+
|
|
187
|
+
Those modules generate TypeScript declarations as part of their workflows.
|
|
188
|
+
Qubu keeps catalog normalization, Snapshot v1 mapping, and optional source
|
|
189
|
+
generation as separate pure boundaries, so diffing and planning do not depend
|
|
190
|
+
on source generation.
|
|
191
|
+
|
|
192
|
+
See [Database introspection](../schema/introspection.md) for the connection
|
|
193
|
+
adapter example, identity rules, diagnostics, and the later diff/planning
|
|
194
|
+
boundary. See the [PostgreSQL](postgres-snapshot.md),
|
|
195
|
+
[SQLite](sqlite-snapshot.md), and [MySQL](mysql-snapshot.md) snapshot pages for
|
|
196
|
+
the pure serialization adapters.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# MySQL snapshot support
|
|
2
|
+
|
|
3
|
+
> Use this matrix before selecting `mysqlSnapshotAdapter`; it records the
|
|
4
|
+
> MySQL facts Qubu v1 can encode and the combinations that need a later server
|
|
5
|
+
> version or engine policy.
|
|
6
|
+
|
|
7
|
+
Import the adapter from the optional snapshot entrypoint:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
createMysqlSchemaSnapshot,
|
|
12
|
+
tryCreateMysqlSchemaSnapshot,
|
|
13
|
+
} from 'qubu/snapshot'
|
|
14
|
+
|
|
15
|
+
const snapshot = createMysqlSchemaSnapshot(appSchema)
|
|
16
|
+
const result = tryCreateMysqlSchemaSnapshot(appSchema)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The snapshot dialect is named `mysql`, the same name used by Qubu's query
|
|
20
|
+
renderer. A dialect-tagged `unsafeSchemaSql('mysql', sql)` expression belongs
|
|
21
|
+
to this adapter.
|
|
22
|
+
|
|
23
|
+
## Support matrix
|
|
24
|
+
|
|
25
|
+
| Schema fact | MySQL v1 behavior |
|
|
26
|
+
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
27
|
+
| Portable storage | Maps `integer` to `INT`, `numeric` to `DECIMAL`, `text` to `TEXT`, `boolean` to `BOOLEAN`, `date` to `DATE`, `timestamp` to `DATETIME`, `uuid` to `CHAR(36)`, `json` to `JSON`, `bigint` to `BIGINT`, and `binary` to `VARBINARY`. |
|
|
28
|
+
| Native storage | Preserves a non-empty declaration tagged `mysql` exactly. Native declarations owned by another dialect fail. |
|
|
29
|
+
| Literals | Encodes `NULL`, finite numbers, strings, booleans, and `bigint` without query parameters. Strings use SQL quote doubling. |
|
|
30
|
+
| Defaults | Canonical literals, branded deterministic expressions, and explicit external behavior are retained. Default expressions cannot reference columns or parameters. |
|
|
31
|
+
| `ON UPDATE` | A branded, parameter-free column expression is retained as `onUpdate`. PostgreSQL and SQLite adapters diagnose this MySQL-only fact. |
|
|
32
|
+
| AUTO_INCREMENT | `identityColumn(..., { dialect: { dialect: 'mysql', autoIncrement: true } })` is retained as a column-level identity extension. The column must be non-nullable, integer-family storage, and the first term of a key. |
|
|
33
|
+
| Generated columns | Both `stored` and `virtual` modes are retained. Generated columns cannot also use `ON UPDATE` or `AUTO_INCREMENT`. |
|
|
34
|
+
| Keys and checks | Primary keys, strict unique keys, ordinary unique constraints, foreign keys, and checks retain logical and physical names, actions, and typed MySQL extension data. |
|
|
35
|
+
| Foreign keys | `MATCH SIMPLE` and standard actions except `SET DEFAULT` are supported. `MATCH FULL`, `MATCH PARTIAL`, deferrability, and `SET DEFAULT` are diagnosed. |
|
|
36
|
+
| Nullable uniqueness | `nulls: 'distinct'` is supported. `nulls: 'not-distinct'` is diagnosed because ordinary MySQL `UNIQUE` constraints allow multiple `NULL` values. |
|
|
37
|
+
| Indexes | Ordered terms, expressions, uniqueness, and candidate-key evidence are retained. Partial predicates, included columns, and `NULLS FIRST/LAST` are diagnosed. Algorithm, locking, access method, parser, and key-block options live under `dialect`. |
|
|
38
|
+
| Names | Table, column, constraint, and index names are checked against MySQL's 64-character identifier limit. Table names are database-scoped; index names are table-scoped. |
|
|
39
|
+
|
|
40
|
+
Capability checks run before common traversal. Use the non-throwing form when
|
|
41
|
+
a schema may include a MySQL engine or version-specific feature:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const result = tryCreateMysqlSchemaSnapshot(appSchema)
|
|
45
|
+
if (!result.ok) {
|
|
46
|
+
for (const issue of result.diagnostics) {
|
|
47
|
+
console.error(issue.path.join('.'), issue.code, issue.message)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The adapter does not connect to MySQL, inspect `information_schema`, or emit
|
|
53
|
+
DDL. It produces deterministic data for the strict `qubu/snapshot` decoder.
|
|
54
|
+
The package-wide [ownership map](supported-surface.md#ownership-boundary)
|
|
55
|
+
shows the separate schema and application boundaries.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# PostgreSQL snapshot support
|
|
2
|
+
|
|
3
|
+
> Use this matrix before selecting `postgresSnapshotAdapter`; it records the
|
|
4
|
+
> PostgreSQL facts Qubu v1 can encode and the cases that need a later server
|
|
5
|
+
> version policy.
|
|
6
|
+
|
|
7
|
+
Import the adapter from the optional snapshot entrypoint:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
createSchemaSnapshot,
|
|
12
|
+
createPostgresSchemaSnapshot,
|
|
13
|
+
postgresSnapshotAdapter,
|
|
14
|
+
} from 'qubu/snapshot'
|
|
15
|
+
|
|
16
|
+
const snapshot = createPostgresSchemaSnapshot(appSchema)
|
|
17
|
+
// Equivalent: createSchemaSnapshot(appSchema, { adapter: postgresSnapshotAdapter })
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The schema dialect extends Qubu's existing `postgresql` query dialect, so both
|
|
21
|
+
snapshot metadata and `unsafeSchemaSql()` use `postgresql` consistently.
|
|
22
|
+
|
|
23
|
+
## Support matrix
|
|
24
|
+
|
|
25
|
+
| Schema fact | PostgreSQL v1 behavior |
|
|
26
|
+
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
27
|
+
| Portable storage | Emits PostgreSQL declarations: `INTEGER`, `NUMERIC`, `TEXT`, `BOOLEAN`, `DATE`, `TIMESTAMP`, `UUID`, `JSONB`, `BIGINT`, and `BYTEA`. |
|
|
28
|
+
| Native storage | Preserves a non-empty declaration tagged `postgresql` exactly. Other dialect tags fail. |
|
|
29
|
+
| Literals | Encodes finite numbers, strings, booleans, `bigint`, and `NULL` without query parameters. |
|
|
30
|
+
| Defaults | Canonical literals, branded deterministic expressions, and explicit external behavior are retained. Column references in defaults fail. |
|
|
31
|
+
| Identity | `always` and `by-default` identity metadata stays separate from generated expressions. |
|
|
32
|
+
| Generated columns | Stored expressions are supported. Virtual generated columns fail with a capability diagnostic. |
|
|
33
|
+
| Keys and checks | Primary keys, strict unique keys, ordinary unique constraints, foreign keys, and checks retain names, timing, actions, and expressions. Checks cannot be deferrable. |
|
|
34
|
+
| Foreign-key match | `simple` and `full` are retained. `partial` fails because PostgreSQL does not implement it. |
|
|
35
|
+
| Nullable uniqueness | `nulls: 'distinct'` is portable. `nulls: 'not-distinct'` needs a PostgreSQL 15-or-newer policy and is rejected by v1. |
|
|
36
|
+
| Indexes | Ordered terms, expressions, predicates, included columns, uniqueness, and candidate-key evidence are retained. PostgreSQL method, concurrency, operator class, and storage-parameter extensions are encoded under `dialect`. |
|
|
37
|
+
| Names | Table, column, constraint, and index names are checked against PostgreSQL's 63-byte identifier limit. Relation names are checked for collisions across tables and indexes. |
|
|
38
|
+
|
|
39
|
+
The adapter does not connect to PostgreSQL or emit DDL. It produces
|
|
40
|
+
deterministic data for the `qubu/snapshot` decoder. The package-wide
|
|
41
|
+
[ownership map](supported-surface.md#ownership-boundary) shows the separate
|
|
42
|
+
schema and application boundaries.
|
|
43
|
+
|
|
44
|
+
## Diagnostics
|
|
45
|
+
|
|
46
|
+
Use the non-throwing form when a schema may contain a server-specific feature:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { tryCreatePostgresSchemaSnapshot } from 'qubu/snapshot'
|
|
50
|
+
|
|
51
|
+
const result = tryCreatePostgresSchemaSnapshot(appSchema)
|
|
52
|
+
if (!result.ok) {
|
|
53
|
+
for (const issue of result.diagnostics) {
|
|
54
|
+
console.error(issue.path.join('.'), issue.code, issue.message)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Capability checks run before common traversal. The common serializer still
|
|
60
|
+
owns ordering, cross-reference checks, canonical encoding, and strict snapshot
|
|
61
|
+
validation.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# SQLite snapshot support
|
|
2
|
+
|
|
3
|
+
> Use this matrix to decide which SQLite schema facts Qubu v1 can serialize and which combinations must be diagnosed before a snapshot is written.
|
|
4
|
+
|
|
5
|
+
Import the adapter from the optional snapshot entrypoint:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import {
|
|
9
|
+
createSqliteSchemaSnapshot,
|
|
10
|
+
tryCreateSqliteSchemaSnapshot,
|
|
11
|
+
} from 'qubu/snapshot'
|
|
12
|
+
|
|
13
|
+
const snapshot = createSqliteSchemaSnapshot(appSchema)
|
|
14
|
+
const result = tryCreateSqliteSchemaSnapshot(appSchema)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The snapshot dialect is `sqlite`, the same name used by Qubu's query dialect.
|
|
18
|
+
This shared name is intentional: SQLite does not need a second metadata identity
|
|
19
|
+
to distinguish it from the query renderer. A dialect-tagged
|
|
20
|
+
`unsafeSchemaSql('sqlite', sql)` expression belongs to this adapter.
|
|
21
|
+
|
|
22
|
+
## Support matrix
|
|
23
|
+
|
|
24
|
+
| Schema fact | SQLite v1 behavior |
|
|
25
|
+
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
26
|
+
| Portable storage | Maps `integer`, `boolean`, and `bigint` to `INTEGER`; `text`, `date`, `timestamp`, `uuid`, and `json` to `TEXT`; `numeric` to `NUMERIC`; and `binary` to `BLOB`. The snapshot also records SQLite's derived affinity. |
|
|
27
|
+
| Native storage | Preserves a non-empty declaration tagged `sqlite` exactly and records its affinity using SQLite's ordered declared-type rules. Other dialect tags fail. |
|
|
28
|
+
| Literals | Encodes `NULL`, finite numbers, strings, `bigint`, and booleans as parameter-free SQL. Boolean literals use `1` and `0`; strings retain SQL escaping. |
|
|
29
|
+
| Defaults | Canonical literals, branded deterministic expressions, and explicit external behavior are retained. Default expressions cannot reference columns or parameters. |
|
|
30
|
+
| Generated columns | Both `stored` and `virtual` modes are retained. Generated columns cannot be used in a SQLite `PRIMARY KEY`; expressions still pass through the shared deterministic schema-expression boundary. |
|
|
31
|
+
| Identity and rowids | Identity columns require INTEGER affinity and a single-column `PRIMARY KEY`. Set `identityColumn(..., { dialect: { dialect: 'sqlite', autoIncrement: true } })` only with an exact `INTEGER` declaration to represent SQLite `AUTOINCREMENT`. |
|
|
32
|
+
| Keys and checks | Primary keys, strict unique keys, ordinary unique constraints, foreign keys, and checks retain logical and physical names. Constraint `DEFERRABLE` timing is diagnosed except on foreign keys. |
|
|
33
|
+
| Foreign keys | Standard update/delete actions and `MATCH SIMPLE` are retained. `MATCH FULL` and `MATCH PARTIAL` are diagnosed because the v1 adapter does not claim SQLite support for them. |
|
|
34
|
+
| Nullable uniqueness | `nulls: 'distinct'` is supported. `nulls: 'not-distinct'` is diagnosed because SQLite's ordinary UNIQUE semantics distinguish NULLs. |
|
|
35
|
+
| Indexes | Ordered terms, expressions, uniqueness, predicates for partial indexes, and candidate-key evidence are retained. Included columns are diagnosed as unsupported. The typed SQLite index extension is encoded under `dialect`. |
|
|
36
|
+
| Namespaces | An optional unqualified namespace is retained; Qubu does not attach or inspect SQLite databases. |
|
|
37
|
+
|
|
38
|
+
Capability checks run before common traversal. Use the non-throwing form when a
|
|
39
|
+
schema may include a feature that depends on a SQLite version or table shape:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const result = tryCreateSqliteSchemaSnapshot(appSchema)
|
|
43
|
+
if (!result.ok) {
|
|
44
|
+
for (const issue of result.diagnostics) {
|
|
45
|
+
console.error(issue.path.join('.'), issue.code, issue.message)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The adapter does not connect to SQLite, inspect `sqlite_master`, or emit DDL.
|
|
51
|
+
It produces deterministic data for the strict `qubu/snapshot` decoder. The
|
|
52
|
+
package-wide [ownership map](supported-surface.md#ownership-boundary) shows
|
|
53
|
+
the separate schema and application boundaries.
|