@coradb/coradb-sqlsyntaxchecker-wasm 0.3.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/README.md ADDED
@@ -0,0 +1,318 @@
1
+ # CoraDB-SQLSyntaxChecker-Wasm
2
+
3
+ Is this valid CoraDB SQL? An offline check that uses **the engine's own
4
+ grammar and lexer** (`corasql_grammar.y`, `corasql_lexer.l`), compiled to
5
+ WebAssembly. It runs in the browser and in Node 18+, needs no server and no
6
+ database connection, and answers as the engine's parser would: accepted, or
7
+ where it stopped and why.
8
+
9
+ Given the database's schema — a JSON document read from its catalog — it also
10
+ checks the names a statement uses: tables, views, node and edge classes,
11
+ columns, functions and procedures (and how many arguments they get), serials,
12
+ and how many values each INSERT row has.
13
+
14
+ The npm package is `@coradb/coradb-sqlsyntaxchecker-wasm` (public, in the `coradb` organization). It has no dependencies.
15
+
16
+ - [Install](#install)
17
+ - [Quick start](#quick-start)
18
+ - [Checking syntax](#checking-syntax)
19
+ - [Checking names against a schema](#checking-names-against-a-schema)
20
+ - [Server settings](#server-settings)
21
+ - [Errors and problems](#errors-and-problems)
22
+ - [In the browser](#in-the-browser)
23
+ - [Without npm, or without JavaScript](#without-npm-or-without-javascript)
24
+ - [API](#api)
25
+ - [Limits](#limits)
26
+ - [Rebuilding from the engine source](#rebuilding-from-the-engine-source)
27
+
28
+ ## Install
29
+
30
+ From npm:
31
+
32
+ ```bash
33
+ npm install @coradb/coradb-sqlsyntaxchecker-wasm
34
+ ```
35
+
36
+ From a release (https://github.com/apsisware/CoraDB-SQLSyntaxChecker-Wasm/releases),
37
+ by its URL or from a copy kept with your project:
38
+
39
+ ```bash
40
+ npm install https://github.com/apsisware/CoraDB-SQLSyntaxChecker-Wasm/releases/download/v<version>/coradb-coradb-sqlsyntaxchecker-wasm-<version>.tgz
41
+ npm install ./vendor/coradb-coradb-sqlsyntaxchecker-wasm-<version>.tgz
42
+ ```
43
+
44
+ From a clone of the repository, to use it as it is while it changes:
45
+
46
+ ```bash
47
+ npm install ../CoraDB-SQLSyntaxChecker-Wasm
48
+ ```
49
+
50
+ | How | When |
51
+ |---|---|
52
+ | npm | The usual way: normal version upgrades. |
53
+ | Release URL | Any machine with internet access; the version stays fixed until the URL changes. |
54
+ | `.tgz` in the project (`vendor/`) | No network needed; commit the file with the project. |
55
+ | A clone's folder | Both projects on one machine, changes seen at once. |
56
+ | Git: `npm install git+https://github.com/apsisware/CoraDB-SQLSyntaxChecker-Wasm.git#v<version>` | Versions by tag, without the release file. |
57
+
58
+ ## Quick start
59
+
60
+ ```js
61
+ import { checkSql, validateSql, describeError } from '@coradb/coradb-sqlsyntaxchecker-wasm';
62
+
63
+ const err = await checkSql('select a, from t');
64
+ if (err) console.log(err.line, err.column, describeError(err));
65
+ // 1 11 Syntax error: unexpected "from".
66
+
67
+ const problems = await validateSql('select nmae from customers', { schema });
68
+ // [{ kind: 'name', line: 1, column: 8, offset: 7, length: 4,
69
+ // message: 'customers has no column named nmae.' }]
70
+ ```
71
+
72
+ `checkSql` and `validateSql` compile the module once and use a fresh checker
73
+ for each call.
74
+
75
+ ## Checking syntax
76
+
77
+ `check()` takes one statement, or a script of statements separated by
78
+ semicolons; the first error stops it, as it does the engine. It returns `null`
79
+ when the text is valid. A typical statement takes a fraction of a millisecond.
80
+
81
+ For many checks, compile the module once and make a checker per check (or
82
+ per request). A new checker from a compiled module costs about a millisecond:
83
+
84
+ ```js
85
+ import { readFile } from 'node:fs/promises';
86
+ import { createSqlChecker } from '@coradb/coradb-sqlsyntaxchecker-wasm';
87
+
88
+ const file = new URL(import.meta.resolve('@coradb/coradb-sqlsyntaxchecker-wasm/coradb-sqlsyntaxchecker.wasm'));
89
+ const module = await WebAssembly.compile(await readFile(file));
90
+
91
+ async function validate(sql, options) {
92
+ const checker = await createSqlChecker(module);
93
+ return checker.validate(sql, options);
94
+ }
95
+ ```
96
+
97
+ A checker can also be kept and reused (`loadSqlChecker()` returns one, made
98
+ once). A fresh one per check is recommended because a checker is a
99
+ WebAssembly instance whose memory lives on from one check to the next: a fresh
100
+ instance guarantees nothing carries over. (Up to 0.2.0 a stack too small for
101
+ some graph queries did damage that memory; that cause was fixed in 0.2.1.)
102
+
103
+ **Statements nested too deeply** (about 290 levels of parentheses, or a MATCH
104
+ of more than about 1,500 patterns) run out of stack. `check()` and
105
+ `validate()` then throw an `Error` ("The SQL checker could not check this
106
+ statement…"): the statement is neither accepted nor rejected. The checker
107
+ replaces its instance and goes on working. Deeper still (about 1,000 levels),
108
+ the parser itself refuses with "memory exhausted", as the engine does.
109
+
110
+ ## Checking names against a schema
111
+
112
+ `validate(sql, { schema })` returns every problem in the text: its first
113
+ syntax error, if it has one; otherwise each name the schema does not have.
114
+
115
+ ```js
116
+ const schema = JSON.parse(await readFile('shop-schema.json', 'utf8'));
117
+ const checker = await loadSqlChecker();
118
+ for (const p of checker.validate(sqlText, { schema })) {
119
+ console.log(`line ${p.line}, column ${p.column}: ${p.message}`);
120
+ }
121
+ ```
122
+
123
+ What it reports:
124
+
125
+ - a table, view, node or edge class that does not exist; a MATCH label of the
126
+ wrong kind (an edge used as a node);
127
+ - a column a table does not have, or one that two tables of the query have
128
+ (ambiguous);
129
+ - a table or alias in `t.column` that the query does not have;
130
+ - a function or procedure that does not exist; a built-in or stored function
131
+ given the wrong number of arguments (the built-ins' counts come from the
132
+ engine's own parser); a procedure used where a value is needed;
133
+ - a serial in `name.NEXT_VALUE` that does not exist;
134
+ - an INSERT column that does not exist, and a row with the wrong number of
135
+ values.
136
+
137
+ It lets through anything it cannot be sure of: a derived table's columns,
138
+ system catalog classes (`db_*`), names the same script creates (`CREATE TABLE
139
+ x; SELECT … FROM x`), a trigger's statements (OBJ, NEW, OLD), and any part of
140
+ the schema the document leaves out — without `procedures`, function names are
141
+ not checked; without `serials`, serials are not. The one exception: without
142
+ `synonyms`, a table name is looked up among the objects only.
143
+
144
+ Without `schema`, `validate()` checks only the syntax.
145
+
146
+ ### The schema document
147
+
148
+ **[README-schema.md](README-schema.md) documents it field by field.**
149
+ `schema.json` (JSON Schema, draft 2020-12) defines it for validators, and
150
+ `index.d.ts` has it as TypeScript types (`CoraDbSchema`). In short:
151
+
152
+ ```json
153
+ {
154
+ "format": "coradb-schema",
155
+ "version": 1,
156
+ "database": "shop",
157
+ "objects": [
158
+ { "name": "orders", "owner": "dba", "kind": "table",
159
+ "columns": [ { "name": "id", "type": "INTEGER" }, { "name": "total", "type": "NUMERIC" } ] }
160
+ ],
161
+ "procedures": [ { "name": "calc_tax", "kind": "function", "returns": "NUMERIC",
162
+ "params": [ { "name": "amount", "type": "NUMERIC", "mode": "IN" } ] } ],
163
+ "serials": [ { "name": "order_no" } ],
164
+ "synonyms": []
165
+ }
166
+ ```
167
+
168
+ Object kinds are `table`, `view`, `node` and `edge`. Names are matched
169
+ without regard to case. A missing list or field means "not known"; an empty
170
+ list means "there are none".
171
+
172
+ **Making one:** `tools/coradbSchema.ts` reads it from a live database's
173
+ catalog. Copy it into your server; it works with any driver, given a function
174
+ that runs a query and returns its rows as objects:
175
+
176
+ ```ts
177
+ import { readCoraDbSchema } from './coradbSchema';
178
+
179
+ const schema = await readCoraDbSchema(async (sql) => rowsAsObjects(await conn.query(sql)));
180
+ ```
181
+
182
+ It reads only the catalog — no table data — and can take a moment on a large
183
+ database: read it once and keep it until the schema changes. The same
184
+ document can be given to an AI as a description of the database.
185
+
186
+ ## Server settings
187
+
188
+ Three server parameters change how SQL is read. The checker uses the
189
+ engine's defaults (all on); pass the server's own when they differ:
190
+
191
+ ```js
192
+ checker.check(sql, { ansiQuotes: true, noBackslashEscapes: false, pipesAsConcat: true });
193
+ checker.validate(sql, { schema, noBackslashEscapes: false });
194
+ ```
195
+
196
+ | Option | Server parameter | Default |
197
+ |---|---|---|
198
+ | `ansiQuotes` | `ansi_quotes`: `"…"` quotes a name, not a string | `true` |
199
+ | `noBackslashEscapes` | `no_backslash_escapes`: a backslash in a string is just a backslash | `true` |
200
+ | `pipesAsConcat` | `pipes_as_concat`: `\|\|` joins strings rather than meaning OR | `true` |
201
+
202
+ A setting left out, `undefined` or `null` keeps the default.
203
+
204
+ ## Errors and problems
205
+
206
+ `check()` returns `null` or the first syntax error:
207
+
208
+ | Field | |
209
+ |---|---|
210
+ | `line`, `column` | 1-based, in characters |
211
+ | `offset`, `length` | the part of the text to mark, in characters: the token, or an open quote to the end of its line |
212
+ | `token` | the text the parser stopped at; `''` at the end of the text |
213
+ | `message` | the parser's own words, e.g. `syntax error, unexpected FROM, expecting ')'` |
214
+
215
+ `describeError(err)` puts it in words for people: `Syntax error: unexpected
216
+ "from". Expected ).`, `Syntax error: unexpected end of the statement.`, or
217
+ `This string is never closed.`
218
+
219
+ `validate()` returns a list of problems, `[]` when all is well. Each has
220
+ `kind` (`'syntax'` or `'name'`), `line`, `column`, `offset`, `length` and
221
+ `message` (already in words).
222
+
223
+ ## In the browser
224
+
225
+ `loadSqlChecker()`, `checkSql()` and `validateSql()` fetch the module from
226
+ next to `index.js`. Vite and webpack 5 copy it into the build on their own,
227
+ since it is named with `new URL('./coradb-sqlsyntaxchecker.wasm',
228
+ import.meta.url)`. To serve it from somewhere else, pass its URL or its bytes:
229
+
230
+ ```js
231
+ const checker = await loadSqlChecker('/assets/coradb-sqlsyntaxchecker.wasm');
232
+ const other = await createSqlChecker(await (await fetch(url)).arrayBuffer());
233
+ ```
234
+
235
+ With Vite, the module's URL can also be imported:
236
+ `import wasmUrl from '@coradb/coradb-sqlsyntaxchecker-wasm/coradb-sqlsyntaxchecker.wasm?url'`.
237
+ When the package is linked from a folder outside the project, add that folder
238
+ to `server.fs.allow` in `vite.config` so `npm run dev` may serve it.
239
+
240
+ The module uses WebAssembly exception handling, which every current browser
241
+ supports.
242
+
243
+ ## Without npm, or without JavaScript
244
+
245
+ **Plain web pages, Electron, any web server:** copy `index.js`,
246
+ `schema-check.js` and `coradb-sqlsyntaxchecker.wasm` into one folder of the
247
+ app and import it as a module. Ship `LICENSE` and `NOTICE` with them.
248
+
249
+ ```html
250
+ <script type="module">
251
+ import { checkSql, describeError } from './coradb-sqlsyntaxchecker/index.js';
252
+
253
+ const err = await checkSql('select a, from t');
254
+ if (err) console.log(err.line, err.column, describeError(err));
255
+ </script>
256
+ ```
257
+
258
+ **Java, .NET, Python, Go…:** nothing ready-made. Either run the `.wasm` in a
259
+ WebAssembly runtime that supports exception handling (check yours does), or
260
+ compile the generated C sources (grammar, lexer and `build/check.c`) as a
261
+ native library for JNI, P/Invoke or ctypes. The module's exports are listed
262
+ at the top of `build/check.c`.
263
+
264
+ ## API
265
+
266
+ | Export | |
267
+ |---|---|
268
+ | `checkSql(sql, settings?)` | `Promise<SqlSyntaxError \| null>`: one call, a fresh checker. |
269
+ | `validateSql(sql, options?)` | `Promise<SqlProblem[]>`: one call, a fresh checker; `options` = settings plus `schema`. |
270
+ | `loadSqlChecker(source?)` | `Promise<SqlChecker>`: the checker made once from the module next to `index.js`; with `source` (URL, path or bytes), a new one from there. |
271
+ | `createSqlChecker(bytes)` | `Promise<SqlChecker>` from the module's bytes, or from a compiled `WebAssembly.Module`. |
272
+ | `checker.check(sql, settings?)` | `SqlSyntaxError \| null`. |
273
+ | `checker.validate(sql, options?)` | `SqlProblem[]`. |
274
+ | `checker.builtin(name)` | A built-in function's allowed argument counts: bit *n* for *n* arguments, bit 30 for 30 or more; `-1` when the engine checks them some other way; `-2` when there is no such built-in. |
275
+ | `describeError(err)` | The error in words. |
276
+ | `tokenWords(name)` | A grammar token as a person would say it (`IdName` → `a name`). |
277
+ | `charOffset(text, byteOffset)` | A byte offset into the UTF-8 text, in characters. |
278
+ | `checkNames`, `createdNames` | The name checks on their own (used by `validate()`). |
279
+ | `DEFAULT_SETTINGS` | The engine's defaults. |
280
+
281
+ Also exported as files: `@coradb/coradb-sqlsyntaxchecker-wasm/coradb-sqlsyntaxchecker.wasm`,
282
+ `…/schema.json` and `…/build-info.json` (what the module was built from).
283
+ TypeScript types are in `index.d.ts`.
284
+
285
+ ## Limits
286
+
287
+ - **PL/CSQL bodies** (`CREATE PROCEDURE … AS BEGIN … END`) are not read by
288
+ this grammar: the engine compiles them separately. Leave them out of what
289
+ you check. Java procedures (`AS LANGUAGE JAVA …`) check normally.
290
+ - Data types are not checked, nor the engine's other rules after parsing
291
+ (limits, invalid combinations, permissions): those need the database. The
292
+ names are only as current as the schema document.
293
+ - A NUL character (U+0000) in the text is reported as an error where it is:
294
+ the lexer would stop reading there.
295
+ - Name checks need memory in proportion to the text. If the module runs out,
296
+ `validate()` throws rather than report nothing.
297
+
298
+ ## Rebuilding from the engine source
299
+
300
+ When the engine's grammar or lexer changes, in a clone of this repository:
301
+
302
+ ```bash
303
+ npm run build -- <path to tesseractdb-develop\src\parser>
304
+ npm test
305
+ ```
306
+
307
+ It needs bison 3.8 and flex 2.6.4 (on Windows `win_bison` and `win_flex` from
308
+ winflexbison; or set `BISON` and `FLEX`), and the WASI SDK
309
+ (https://github.com/WebAssembly/wasi-sdk/releases, 25.0) with
310
+ `WASI_SDK_PATH` set to where it was unpacked. With those versions the build
311
+ reproduces the published module byte for byte. `build-info.json` records the
312
+ engine version, the source and the SHA-256 of its grammar and lexer, the stack
313
+ size and the WASI SDK version. How the engine's sources become the module is
314
+ described at the top of `build/generate.mjs`.
315
+
316
+ ## License
317
+
318
+ MIT. See `LICENSE` and `NOTICE`.
@@ -0,0 +1,11 @@
1
+ {
2
+ "builtAt": "2026-10-30T16:46:50.511Z",
3
+ "source": "tesseractdb-develop\\src\\parser",
4
+ "engineVersion": "1.0.1",
5
+ "grammarSha256": "31de8ab1860a938b0274d287a6f3bed0fc4e80f3cdcb31f0f07a1c9f1f6cf655",
6
+ "lexerSha256": "eb85dd08d4d02e53d2776ed37563dee095da9c028a8dcfc6a2b2031932e767bc",
7
+ "tokens": 646,
8
+ "hints": 33,
9
+ "stackSize": 4194304,
10
+ "wasiSdk": "25.0"
11
+ }
Binary file
package/index.d.ts ADDED
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Server settings that change how SQL is read. Each defaults to the engine's
3
+ * own default (true); a setting left out, undefined or null keeps it.
4
+ */
5
+ export interface CheckSettings {
6
+ /** `"…"` quotes a name, not a string (ansi_quotes). */
7
+ ansiQuotes: boolean;
8
+ /** A backslash in a string is just a backslash (no_backslash_escapes). */
9
+ noBackslashEscapes: boolean;
10
+ /** `||` joins strings rather than meaning OR (pipes_as_concat). */
11
+ pipesAsConcat: boolean;
12
+ }
13
+
14
+ export declare const DEFAULT_SETTINGS: Readonly<CheckSettings>;
15
+
16
+ /** Where the parser stopped, and why. */
17
+ export interface SqlSyntaxError {
18
+ /** 1-based line in the text checked. */
19
+ line: number;
20
+ /** 1-based column, in characters. */
21
+ column: number;
22
+ /** Where the error is in the string, in characters (0-based). */
23
+ offset: number;
24
+ /** How many characters to mark from `offset`: the token, or an open quote to the end of its line. */
25
+ length: number;
26
+ /** What the parser said, e.g. `syntax error, unexpected FROM, expecting ')'`. */
27
+ message: string;
28
+ /** The text of the token it stopped at; '' at the end of the text. */
29
+ token: string;
30
+ }
31
+
32
+ /**
33
+ * Keeping one checker for many checks works, but a fresh checker per check
34
+ * (createSqlChecker with bytes read once, ~2 ms) is recommended: see README,
35
+ * "Create a fresh checker for every check".
36
+ *
37
+ * check() and validate() throw an Error when a statement nests too deeply for
38
+ * the module's stack; the checker replaces its instance and goes on working.
39
+ * validate() also throws when the module runs out of memory for a text's names
40
+ * (it never answers "all is well" for names it could not check).
41
+ *
42
+ * A text that contains a NUL character (U+0000) is refused at that character.
43
+ */
44
+ export interface SqlChecker {
45
+ /**
46
+ * null when the engine's parser accepts `sql` — one statement, or several
47
+ * separated by semicolons; otherwise the first error.
48
+ */
49
+ check(sql: string, settings?: Partial<CheckSettings>): SqlSyntaxError | null;
50
+ /**
51
+ * Every problem in `sql`: its first syntax error, if it has one; otherwise,
52
+ * given `schema`, each name the schema does not have — a table, view, node
53
+ * or edge class, a column, a function or procedure (or the wrong number of
54
+ * arguments to one), a serial — and each INSERT row with the wrong number
55
+ * of values. Without `schema`, only the syntax is checked. [] when all is well.
56
+ */
57
+ validate(sql: string, options?: ValidateOptions): SqlProblem[];
58
+ /**
59
+ * A built-in function's allowed argument counts: a bit per count (bit 30:
60
+ * 30 or more); -1 when the engine checks them some other way; -2 when there
61
+ * is no built-in function of that name.
62
+ */
63
+ builtin(name: string): number;
64
+ }
65
+
66
+ export interface ValidateOptions extends Partial<CheckSettings> {
67
+ /** The database's schema: what the names are looked up in. */
68
+ schema?: CoraDbSchema;
69
+ }
70
+
71
+ /** One problem found by validate(). */
72
+ export interface SqlProblem {
73
+ /** `syntax`: the parser stopped here. `name`: the schema does not have what is named, or a call or row does not fit. */
74
+ kind: 'syntax' | 'name';
75
+ /** 1-based. */
76
+ line: number;
77
+ /** 1-based, in characters. */
78
+ column: number;
79
+ /** In characters, 0-based. */
80
+ offset: number;
81
+ /** How many characters to mark from `offset`. */
82
+ length: number;
83
+ /** In words, e.g. `customers has no column named nmae.` */
84
+ message: string;
85
+ }
86
+
87
+ /**
88
+ * Compile a checker from the bytes of coradb-sqlsyntaxchecker.wasm, or make one
89
+ * from the module already compiled from them (`WebAssembly.compile(bytes)`):
90
+ * then a new checker costs only its instance.
91
+ */
92
+ export declare function createSqlChecker(bytes: BufferSource | WebAssembly.Module): Promise<SqlChecker>;
93
+
94
+ /**
95
+ * The checker, loaded once from the coradb-sqlsyntaxchecker.wasm next to this module (read
96
+ * from disk in Node, fetched in the browser), or from `source` when given.
97
+ */
98
+ export declare function loadSqlChecker(source?: URL | string | BufferSource): Promise<SqlChecker>;
99
+
100
+ /** Check in one call, with the module next to this one: a fresh checker each call, the module compiled once. */
101
+ export declare function checkSql(sql: string, settings?: Partial<CheckSettings>): Promise<SqlSyntaxError | null>;
102
+
103
+ /** Validate in one call, with the module next to this one: a fresh checker each call, the module compiled once. */
104
+ export declare function validateSql(sql: string, options?: ValidateOptions): Promise<SqlProblem[]>;
105
+
106
+ /** The error in words: `Syntax error: unexpected "from". Expected ).` */
107
+ export declare function describeError(err: SqlSyntaxError): string;
108
+
109
+ /** The character offset of a byte offset into the UTF-8 form of `text`. */
110
+ export declare function charOffset(text: string, byteOffset: number): number;
111
+
112
+ /** One of the grammar's token names as a person would say it (DELETE_ → DELETE, IdName → a name). */
113
+ export declare function tokenWords(name: string): string;
114
+
115
+ /** Where each name, query and call is in one statement, as the grammar reports it (character offsets). */
116
+ export interface ParseEvent {
117
+ kind: number;
118
+ start: number;
119
+ end: number;
120
+ }
121
+
122
+ /** The name checks on their own, for one statement the grammar accepted. */
123
+ export declare function checkNames(
124
+ sql: string,
125
+ events: ParseEvent[],
126
+ schema: CoraDbSchema,
127
+ ctx: {
128
+ builtin: (name: string) => number;
129
+ created?: ReadonlySet<string>;
130
+ /** no_backslash_escapes is off: a backslash in a string escapes what follows (for counting values and arguments). */
131
+ backslashEscapes?: boolean;
132
+ },
133
+ ): { start: number; end: number; message: string }[];
134
+
135
+ /** Lower-cased names a script creates, renames or changes (CREATE TABLE x, RENAME a TO b…). */
136
+ export declare function createdNames(statements: string[]): Set<string>;
137
+
138
+ // --- the schema document (see schema.json) --------------------------------------
139
+
140
+ /**
141
+ * A CoraDB database's schema, read from its catalog: objects, their columns,
142
+ * keys and comments, stored procedures and functions, serials and synonyms.
143
+ * Any part may be missing: a missing list or field means "not known", never
144
+ * "there are none" — with one exception: without `synonyms`, a table name is
145
+ * looked up among the objects only (see README-schema.md).
146
+ */
147
+ export interface CoraDbSchema {
148
+ format: 'coradb-schema';
149
+ version: 1;
150
+ database?: string;
151
+ objects: SchemaObject[];
152
+ procedures?: SchemaProcedure[];
153
+ serials?: SchemaSerial[];
154
+ synonyms?: SchemaSynonym[];
155
+ }
156
+
157
+ export type SchemaObjectKind = 'table' | 'view' | 'node' | 'edge';
158
+
159
+ export interface SchemaObject {
160
+ name: string;
161
+ owner?: string;
162
+ kind: SchemaObjectKind;
163
+ comment?: string | null;
164
+ /** Parent classes (CREATE … UNDER), as `owner.name` or `name`; their columns are inherited. */
165
+ under?: string[];
166
+ /** In table order. Missing when not known: then any column is taken. */
167
+ columns?: SchemaColumn[];
168
+ primaryKey?: string[];
169
+ unique?: { name: string; columns: string[] }[];
170
+ foreignKeys?: SchemaForeignKey[];
171
+ /** Secondary indexes, unique ones included; not the primary or foreign keys. */
172
+ indexes?: SchemaIndex[];
173
+ /** Views only: the SELECT that defines it. */
174
+ definition?: string;
175
+ }
176
+
177
+ export interface SchemaColumn {
178
+ name: string;
179
+ /** As CoraDB names it: INTEGER, VARCHAR, NUMERIC, DATETIME, ENUM, SET… */
180
+ type: string;
181
+ length?: number;
182
+ precision?: number;
183
+ scale?: number;
184
+ /** ENUM: the values it allows. */
185
+ values?: string[];
186
+ /** SET, MULTISET, LIST: the type of the elements. */
187
+ elementType?: string;
188
+ nullable?: boolean;
189
+ /** The default as SQL text, e.g. `'RO'` or `SYS_DATETIME`. */
190
+ default?: string | null;
191
+ autoIncrement?: boolean;
192
+ comment?: string | null;
193
+ }
194
+
195
+ export interface SchemaForeignKey {
196
+ name: string;
197
+ columns: string[];
198
+ /** `owner.name`, or `name` when the owner is not known. */
199
+ references: string;
200
+ refColumns: string[];
201
+ onDelete?: string;
202
+ onUpdate?: string;
203
+ }
204
+
205
+ export interface SchemaIndex {
206
+ name: string;
207
+ columns: string[];
208
+ unique: boolean;
209
+ comment?: string | null;
210
+ }
211
+
212
+ export interface SchemaProcedure {
213
+ name: string;
214
+ owner?: string;
215
+ kind: 'procedure' | 'function';
216
+ /** Functions only. */
217
+ returns?: string;
218
+ comment?: string | null;
219
+ params?: { name: string; type: string; mode: 'IN' | 'OUT' | 'INOUT'; comment?: string | null }[];
220
+ }
221
+
222
+ export interface SchemaSerial {
223
+ name: string;
224
+ owner?: string;
225
+ comment?: string | null;
226
+ }
227
+
228
+ export interface SchemaSynonym {
229
+ name: string;
230
+ owner?: string;
231
+ /** `owner.name` of what it stands for. */
232
+ target: string;
233
+ comment?: string | null;
234
+ }