@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/CHANGELOG.md +55 -0
- package/LICENSE +21 -0
- package/NOTICE +14 -0
- package/README-schema.md +362 -0
- package/README.md +318 -0
- package/build-info.json +11 -0
- package/coradb-sqlsyntaxchecker.wasm +0 -0
- package/index.d.ts +234 -0
- package/index.js +529 -0
- package/package.json +60 -0
- package/schema-check.js +715 -0
- package/schema.json +127 -0
- package/tools/coradbSchema.ts +483 -0
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`.
|
package/build-info.json
ADDED
|
@@ -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
|
+
}
|