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.
Files changed (109) hide show
  1. package/dist/canonical-BbnqavJm.mjs +972 -0
  2. package/dist/codegen.d.mts +107 -0
  3. package/dist/codegen.mjs +1199 -0
  4. package/dist/column-CXMxx8Hq.mjs +118 -0
  5. package/dist/complete-D5Djh-zo.mjs +1788 -0
  6. package/dist/complete-types-BdFqUfbb.d.mts +371 -0
  7. package/dist/core.d.mts +33 -0
  8. package/dist/core.mjs +36 -0
  9. package/dist/ddl.d.mts +118 -0
  10. package/dist/ddl.mjs +1120 -0
  11. package/dist/dialect-b2-Z6uBF.mjs +13 -0
  12. package/dist/diff.d.mts +2 -0
  13. package/dist/diff.mjs +883 -0
  14. package/dist/drizzle-mysql.d.mts +24 -0
  15. package/dist/drizzle-mysql.mjs +72 -0
  16. package/dist/drizzle-postgres.d.mts +24 -0
  17. package/dist/drizzle-postgres.mjs +73 -0
  18. package/dist/drizzle-sqlite.d.mts +24 -0
  19. package/dist/drizzle-sqlite.mjs +82 -0
  20. package/dist/drizzle.d.mts +13 -0
  21. package/dist/drizzle.mjs +2 -0
  22. package/dist/errors-BGCoLe_r.mjs +14 -0
  23. package/dist/errors-Dxv73YJu.mjs +26 -0
  24. package/dist/explain-CkIK13L_.mjs +98 -0
  25. package/dist/index-Ds7-mhJi.d.mts +146 -0
  26. package/dist/index-Dug5HnLB.d.mts +204 -0
  27. package/dist/index.d.mts +2 -0
  28. package/dist/index.mjs +1276 -0
  29. package/dist/introspection.d.mts +117 -0
  30. package/dist/introspection.mjs +4341 -0
  31. package/dist/json-CUZlv4HT.mjs +169 -0
  32. package/dist/migration.d.mts +215 -0
  33. package/dist/migration.mjs +1161 -0
  34. package/dist/mysql-DqkqXB6A.mjs +355 -0
  35. package/dist/mysql.d.mts +5 -0
  36. package/dist/mysql.mjs +50 -0
  37. package/dist/naming-QVCOnSj2.mjs +20 -0
  38. package/dist/omit-OxV58AwX.mjs +5 -0
  39. package/dist/on-conflict-BxnxubMb.mjs +143 -0
  40. package/dist/postgres-DEBBeh52.mjs +235 -0
  41. package/dist/postgres.d.mts +123 -0
  42. package/dist/postgres.mjs +52 -0
  43. package/dist/registry-BufIskVN.mjs +569 -0
  44. package/dist/relational-DCZrrNia.mjs +77 -0
  45. package/dist/runtime-BTr-MTlo.mjs +193 -0
  46. package/dist/schema.d.mts +2 -0
  47. package/dist/schema.mjs +5 -0
  48. package/dist/serialize-PF1cfH2P.mjs +556 -0
  49. package/dist/snapshot-CWPgzxNx.mjs +392 -0
  50. package/dist/snapshot.d.mts +4 -0
  51. package/dist/snapshot.mjs +8 -0
  52. package/dist/source-DUoJVXmL.mjs +68 -0
  53. package/dist/sqlite-BU6DBxef.mjs +320 -0
  54. package/dist/sqlite.d.mts +5 -0
  55. package/dist/sqlite.mjs +48 -0
  56. package/dist/standard-BTVYKh_F.mjs +12 -0
  57. package/dist/table-llv9tsZ8.mjs +384 -0
  58. package/dist/types-4Q076HKo.d.mts +3636 -0
  59. package/dist/types-BX0mckiU.d.mts +644 -0
  60. package/dist/types-Cec0xzo4.mjs +116 -0
  61. package/dist/types-H4vyCw8_.d.mts +45 -0
  62. package/dist/value-BvilP0oz.mjs +29 -0
  63. package/dist/vite/ambient.d.ts +449 -0
  64. package/dist/vite.d.mts +35 -0
  65. package/dist/vite.mjs +498 -0
  66. package/docs/config.json +21 -0
  67. package/docs/dialects-and-execution.md +427 -0
  68. package/docs/getting-started.md +96 -0
  69. package/docs/guides/compose-queries.md +225 -0
  70. package/docs/guides/drizzle.md +130 -0
  71. package/docs/guides/extensions/dialects.md +40 -0
  72. package/docs/guides/extensions/overview.md +36 -0
  73. package/docs/guides/extensions/sources-and-clauses.md +82 -0
  74. package/docs/guides/extensions/typed-expressions.md +94 -0
  75. package/docs/guides/extensions/unsafe-syntax.md +27 -0
  76. package/docs/guides/json.md +65 -0
  77. package/docs/guides/mutations.md +138 -0
  78. package/docs/guides/select/conditions.md +110 -0
  79. package/docs/guides/select/grouping-and-windows.md +82 -0
  80. package/docs/guides/select/ordering-and-pagination.md +78 -0
  81. package/docs/guides/select/overview.md +139 -0
  82. package/docs/guides/sql-templates.md +157 -0
  83. package/docs/guides/vite-plugin.md +92 -0
  84. package/docs/index.md +111 -0
  85. package/docs/query-model/fragments.md +109 -0
  86. package/docs/query-model/result-shapes.md +125 -0
  87. package/docs/query-model/source-scope.md +182 -0
  88. package/docs/reference/introspection-support.md +196 -0
  89. package/docs/reference/mysql-snapshot.md +55 -0
  90. package/docs/reference/postgres-snapshot.md +61 -0
  91. package/docs/reference/sqlite-snapshot.md +53 -0
  92. package/docs/reference/supported-surface.md +104 -0
  93. package/docs/schema/catalog-model.md +58 -0
  94. package/docs/schema/code-generation.md +139 -0
  95. package/docs/schema/columns-and-writes.md +130 -0
  96. package/docs/schema/constraints-and-indexes.md +180 -0
  97. package/docs/schema/ddl-emission.md +75 -0
  98. package/docs/schema/diff.md +75 -0
  99. package/docs/schema/introspection.md +214 -0
  100. package/docs/schema/migration-plans.md +76 -0
  101. package/docs/schema/snapshots.md +93 -0
  102. package/docs/schema/storage-and-schema-sql.md +104 -0
  103. package/docs/schema/tables-and-names.md +106 -0
  104. package/docs/sql-semantic-types.md +141 -0
  105. package/docs/troubleshooting.md +151 -0
  106. package/package.json +132 -10
  107. package/skills/qubu/SKILL.md +42 -0
  108. package/skills/qubu/agents/openai.yaml +6 -0
  109. 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.