@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/CHANGELOG.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.3 (2026-11-04)
|
|
4
|
+
|
|
5
|
+
- Documentation only: the README, the changelog and a code comment no longer refer to other projects or to a local setup. The module, the code and the API are the same as in 0.3.2.
|
|
6
|
+
|
|
7
|
+
## 0.3.2 (2026-11-04)
|
|
8
|
+
|
|
9
|
+
- **Renamed to `@coradb/coradb-sqlsyntaxchecker-wasm`** and published on npm as a public package of the `coradb` organization (`npm install @coradb/coradb-sqlsyntaxchecker-wasm`). Imports change accordingly; the module and API do not.
|
|
10
|
+
- **`checkSql()` and `validateSql()` use a fresh checker for each call**, made from the module compiled once, as the README recommends. `createSqlChecker()` also takes a compiled `WebAssembly.Module` (a new checker then costs about a millisecond).
|
|
11
|
+
- **Fixed: `describeError()` said "unexpected the end of the statement"**; it now says "Syntax error: unexpected end of the statement."
|
|
12
|
+
- **Fixed: a `[quoted name]` left open** was marked at the end of the text with nothing to mark; it is now marked from its bracket.
|
|
13
|
+
- Docs: README rewritten around using the package (it now includes what was in `Install-CoraDB-SQLSyntaxChecker-Wasm.md`, which is gone); `CLAUDE.md` added for working on the package.
|
|
14
|
+
- `build/generate.mjs`: a `//` comment inside a grammar action can no longer upset the brace count (the generated sources, and the module, are unchanged).
|
|
15
|
+
- **Fixed: names were not checked in longer texts, without a word.** The module kept at most 16,384 events (where the tables, columns and functions are); past that, `validate()` skipped every name check and returned `[]`. About 2,000 short statements, or an INSERT of about 16,000 rows, were enough. The buffer now grows with the text; if memory runs out, `validate()` throws instead of answering.
|
|
16
|
+
- **Fixed: a setting given as `undefined` or `null` was taken as `false`.** `{ noBackslashEscapes: undefined }` turned backslash escapes on. Such a setting now keeps the default.
|
|
17
|
+
- **Fixed: with backslash escapes on** (`noBackslashEscapes: false`), an escaped quote (`'it\'s'`) ended a string when counting an INSERT row's values or a call's arguments, which gave false "wrong number of values" reports; and an open string was marked from the wrong quote.
|
|
18
|
+
- **Fixed: a long token took time in proportion to the square of its length.** A 20 MB string literal did not finish in minutes; it now takes well under a second. (The lexer read the text 8 KB at a time and moved a long token back at every read; it now reads as much as its buffer holds.)
|
|
19
|
+
- **Fixed: a NUL character (U+0000) ended the text.** Whatever came before it was checked and the rest ignored, so `select 1` + NUL + anything was valid. It is now reported as an error at the NUL.
|
|
20
|
+
- **Fixed: a schema document with a synonym that leads back to itself** made `validate()` throw (stack overflow); columns, synonyms, serials or procedures without a `name` made it throw a TypeError. They are now let through or left out.
|
|
21
|
+
- `validate(sql, null)` no longer throws; `validate()` on long scripts is much faster (each statement's events are found once, not searched for).
|
|
22
|
+
- The module's WASI stand-ins: output, which the module never writes, is now reported as written in full (the C library retries a write that wrote nothing, which would have looped for ever), and a module that ends itself (`proc_exit`) has its instance replaced like one that traps. `builtin()` checks for memory too.
|
|
23
|
+
- `tools/coradbSchema.ts`: ENUM values and the element types of SET/MULTISET/LIST columns are read with the table's owner, so two owners' tables of the same name no longer get each other's.
|
|
24
|
+
- `build-info.json` now records the WASI SDK version (`wasiSdk`; 0.2.1's notes said it did, but it did not), and `source` is written the same way (`tesseractdb-develop\src\parser`) on every system. The build checks that `WASI_SDK_PATH` has a clang and that `CHECK_STACK_SIZE` is a sensible number.
|
|
25
|
+
- `schema-check.js`: `EV`, `nameParts` and `countItems`, which no one outside could reach, are no longer exported.
|
|
26
|
+
- Docs: README "Install" (releases, a clone); the synonyms exception to "missing means not known" in README and `index.d.ts`; comments in `check.c` and `generate.mjs`.
|
|
27
|
+
- The module was rebuilt from the same engine grammar and lexer (same SHA-256 as 0.3.1). Its exports are unchanged.
|
|
28
|
+
|
|
29
|
+
## 0.3.1 (2026-10-30)
|
|
30
|
+
|
|
31
|
+
- **This package is now the single home of the checker.** Its build (`build/generate.mjs`, `check.c`, `shim.h`, `build.mjs`) is the maintained copy.
|
|
32
|
+
- No change to the module, its exports or the JavaScript API: `coradb-sqlsyntaxchecker.wasm` is the same as in 0.3.0.
|
|
33
|
+
- **License: MIT throughout.** `package.json`, `README.md`, `NOTICE` and the install guide said Apache-2.0; they now agree with `LICENSE` (MIT).
|
|
34
|
+
|
|
35
|
+
## 0.3.0 (2026-10-30)
|
|
36
|
+
|
|
37
|
+
- **Fixed: a stray word after an expression was accepted**, e.g. `ORDER BY total DESCx` or `GROUP BY a foo`.
|
|
38
|
+
- The engine's grammar has a rule that matches an expression followed by a word only to refuse it: its action raises "invalid relational operator". The checker drops the engine's actions, so that refusal was lost.
|
|
39
|
+
- The build now finds rules whose action does nothing but raise a syntax error and makes the checker report it, at the stray word, with the engine's message. Today that is this one rule; any the engine adds are picked up the same way.
|
|
40
|
+
- `describeError` words it as `Syntax error: unexpected "DESCx" (invalid relational operator).`
|
|
41
|
+
|
|
42
|
+
## 0.2.1 (2026-10-20)
|
|
43
|
+
|
|
44
|
+
- **Fixed: memory damage that carried over between checks.**
|
|
45
|
+
- The module's C stack was the WASI SDK's default 64 KB. The engine's GLR parser overran it on some graph queries and wrote over the module's static data and heap.
|
|
46
|
+
- Later checks on the same checker then crashed with "memory access out of bounds" or returned wrong results (an error with a garbage message for valid SQL).
|
|
47
|
+
- The fix: the stack is now 4 MB and placed first in memory (`-Wl,--stack-first`), so running past it traps immediately instead of overwriting data.
|
|
48
|
+
- **Fixed: "memory exhausted" on long statements the engine parses**, e.g. a MATCH of 40 patterns. The checker now uses the engine's YYMAXDEPTH (1000000) instead of bison's default (10000).
|
|
49
|
+
- **A statement nested too deeply now throws an Error.** This covers both the module's stack and the JavaScript engine's. The checker replaces its WebAssembly instance and goes on working.
|
|
50
|
+
- **Name checks:**
|
|
51
|
+
- names created by `CREATE VERTEX TABLE` / `CREATE EDGE TABLE` in the same script are known;
|
|
52
|
+
- `DROP … IF EXISTS` is not reported when the object is missing;
|
|
53
|
+
- variables of `list_comp(x IN …)` / `reduce(acc = …, x IN …)` are not taken for columns.
|
|
54
|
+
- **README:** creating a fresh checker for every check is now the recommended way, with the reasons why.
|
|
55
|
+
- **build-info.json** records the stack size and the WASI SDK version.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ApsiswareDB
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
CoraDB-SQLSyntaxChecker-Wasm
|
|
2
|
+
|
|
3
|
+
This product includes software developed as part of the CoraDB engine:
|
|
4
|
+
Copyright 2008 Search Solution Corporation
|
|
5
|
+
Copyright 2016 CoraDB Corporation
|
|
6
|
+
CoraDB is a registered trademark of CoraDB Corporation.
|
|
7
|
+
|
|
8
|
+
coradb-sqlsyntaxchecker.wasm is compiled from the engine's SQL grammar (src/parser/
|
|
9
|
+
corasql_grammar.y) and lexer (src/parser/corasql_lexer.l), licensed under
|
|
10
|
+
the MIT License. They were modified for this package: the
|
|
11
|
+
grammar's C actions were replaced by hooks that only report where a
|
|
12
|
+
statement's names are, nothing being built from it; the built-in function
|
|
13
|
+
table and its argument checks were turned into a lookup table; and the lexer
|
|
14
|
+
was given a stand-in for the parser context it uses. See build/generate.mjs.
|
package/README-schema.md
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# The CoraDB schema document
|
|
2
|
+
|
|
3
|
+
A JSON document that describes what a CoraDB database defines: its tables,
|
|
4
|
+
views, node and edge classes, their columns, keys, indexes and comments, its
|
|
5
|
+
stored procedures and functions, serials and synonyms.
|
|
6
|
+
|
|
7
|
+
It is read from the database's catalog only. It never holds data from the
|
|
8
|
+
tables: no rows, no counts, no sample values.
|
|
9
|
+
|
|
10
|
+
It has two uses:
|
|
11
|
+
|
|
12
|
+
- **Checking SQL.** `validate(sql, { schema })` in this package looks up the
|
|
13
|
+
names a statement uses in it.
|
|
14
|
+
- **Describing the database** to a person or to an AI, for example as context
|
|
15
|
+
for writing queries. The comments and keys are there for that.
|
|
16
|
+
|
|
17
|
+
The rules of the format are also in machine-readable form: `schema.json` (JSON
|
|
18
|
+
Schema, draft 2020-12) to validate a document, and the `CoraDbSchema` types in
|
|
19
|
+
`index.d.ts` for TypeScript.
|
|
20
|
+
|
|
21
|
+
## Contents
|
|
22
|
+
|
|
23
|
+
- [Rules that apply everywhere](#rules-that-apply-everywhere)
|
|
24
|
+
- [The document](#the-document)
|
|
25
|
+
- [Objects](#objects)
|
|
26
|
+
- [Columns](#columns)
|
|
27
|
+
- [Keys and indexes](#keys-and-indexes)
|
|
28
|
+
- [Procedures and functions](#procedures-and-functions)
|
|
29
|
+
- [Serials](#serials)
|
|
30
|
+
- [Synonyms](#synonyms)
|
|
31
|
+
- [A complete example](#a-complete-example)
|
|
32
|
+
- [Making a document](#making-a-document)
|
|
33
|
+
- [How the checker uses each part](#how-the-checker-uses-each-part)
|
|
34
|
+
- [Versions](#versions)
|
|
35
|
+
|
|
36
|
+
## Rules that apply everywhere
|
|
37
|
+
|
|
38
|
+
- **Missing means "not known".** Every part except `format`, `version` and
|
|
39
|
+
`objects` is optional. A list or field that is left out means the producer
|
|
40
|
+
did not know it, never that there are none. An empty list (`[]`) means
|
|
41
|
+
"there are none". The checker never reports anything against a part that is
|
|
42
|
+
missing — with one exception: without `synonyms`, a table name is looked up
|
|
43
|
+
among the objects only.
|
|
44
|
+
- **Names are matched without regard to case**, as the engine does: `Orders`,
|
|
45
|
+
`orders` and `"ORDERS"` are the same object. Names are written as the
|
|
46
|
+
catalog spells them, without quotes.
|
|
47
|
+
- **Owners.** In CoraDB every object belongs to a user. Where an object or a
|
|
48
|
+
reference to one has an owner, it is given in `owner`, or as `owner.name` in
|
|
49
|
+
fields that point to another object (`under`, `references`, `target`). A
|
|
50
|
+
name without an owner matches that name under any owner.
|
|
51
|
+
- **Comments** are `null` when the catalog has none, and left out when the
|
|
52
|
+
producer could not read them.
|
|
53
|
+
- **Types** are written as CoraDB names them, in upper case: `INTEGER`,
|
|
54
|
+
`BIGINT`, `SMALLINT`, `NUMERIC`, `FLOAT`, `DOUBLE`, `VARCHAR`, `CHAR`,
|
|
55
|
+
`NCHAR`, `BIT`, `VARBIT`, `DATE`, `TIME`, `DATETIME`, `TIMESTAMP`, `ENUM`,
|
|
56
|
+
`SET`, `MULTISET`, `LIST`, `JSON`, `BLOB`, `CLOB`, `OBJECT`… A size is never
|
|
57
|
+
part of the type: it is in `length`, `precision` and `scale`.
|
|
58
|
+
- **Unknown fields** are allowed, so that a producer can add its own; readers
|
|
59
|
+
ignore what they do not know.
|
|
60
|
+
|
|
61
|
+
## The document
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"format": "coradb-schema",
|
|
66
|
+
"version": 1,
|
|
67
|
+
"database": "shop",
|
|
68
|
+
"objects": [],
|
|
69
|
+
"procedures": [],
|
|
70
|
+
"serials": [],
|
|
71
|
+
"synonyms": []
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
| Field | Type | Required | Meaning |
|
|
76
|
+
|---|---|---|---|
|
|
77
|
+
| `format` | `"coradb-schema"` | yes | Says what the document is. |
|
|
78
|
+
| `version` | `1` | yes | The version of this format. |
|
|
79
|
+
| `database` | string | no | The database's name, for display. |
|
|
80
|
+
| `objects` | array of [objects](#objects) | yes | Tables, views, node classes and edge classes. |
|
|
81
|
+
| `procedures` | array of [procedures](#procedures-and-functions) | no | Stored procedures and functions. |
|
|
82
|
+
| `serials` | array of [serials](#serials) | no | Serials (sequences), not counting the ones behind AUTO_INCREMENT columns. |
|
|
83
|
+
| `synonyms` | array of [synonyms](#synonyms) | no | Synonyms. |
|
|
84
|
+
|
|
85
|
+
## Objects
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"name": "orders",
|
|
90
|
+
"owner": "dba",
|
|
91
|
+
"kind": "table",
|
|
92
|
+
"comment": "One row per customer order",
|
|
93
|
+
"under": [],
|
|
94
|
+
"columns": [ … ],
|
|
95
|
+
"primaryKey": ["id"],
|
|
96
|
+
"unique": [ … ],
|
|
97
|
+
"foreignKeys": [ … ],
|
|
98
|
+
"indexes": [ … ]
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
| Field | Type | Required | Meaning |
|
|
103
|
+
|---|---|---|---|
|
|
104
|
+
| `name` | string | yes | The object's name. |
|
|
105
|
+
| `owner` | string | no | The user that owns it. |
|
|
106
|
+
| `kind` | `"table"`, `"view"`, `"node"`, `"edge"` | yes | What it is: a table, a view, a graph node class (vertex) or a graph edge class. |
|
|
107
|
+
| `comment` | string or null | no | The object's comment. |
|
|
108
|
+
| `under` | array of strings | no | The classes it inherits from (`CREATE … UNDER`), as `owner.name` or `name`. It has their columns as well as its own. |
|
|
109
|
+
| `columns` | array of [columns](#columns) | no | Its columns, in table order. Left out when they could not be read: then any column name is accepted. |
|
|
110
|
+
| `primaryKey` | array of strings | no | The columns of the primary key, in key order; `[]` when there is none. |
|
|
111
|
+
| `unique` | array | no | Unique keys: `{ "name": …, "columns": [ … ] }` each. |
|
|
112
|
+
| `foreignKeys` | array of [foreign keys](#keys-and-indexes) | no | Its foreign keys. |
|
|
113
|
+
| `indexes` | array of [indexes](#keys-and-indexes) | no | Its secondary indexes, the unique ones included; not the primary key or the foreign keys' own indexes. |
|
|
114
|
+
| `definition` | string | no | Views only: the SELECT the view is defined by. |
|
|
115
|
+
|
|
116
|
+
The partitions of a partitioned table are not listed as objects: they are the
|
|
117
|
+
table.
|
|
118
|
+
|
|
119
|
+
## Columns
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{ "name": "total", "type": "NUMERIC", "precision": 12, "scale": 2,
|
|
123
|
+
"nullable": false, "default": "0", "comment": "Including VAT, in EUR" }
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
| Field | Type | Required | Meaning |
|
|
127
|
+
|---|---|---|---|
|
|
128
|
+
| `name` | string | yes | The column's name. |
|
|
129
|
+
| `type` | string | yes | Its type, without sizes (see [Rules](#rules-that-apply-everywhere)). |
|
|
130
|
+
| `length` | integer | no | Character and bit types: the declared length. Left out when there is no limit (a VARCHAR declared without one). |
|
|
131
|
+
| `precision` | integer | no | NUMERIC: total digits. |
|
|
132
|
+
| `scale` | integer | no | NUMERIC: digits after the point. |
|
|
133
|
+
| `values` | array of strings | no | ENUM: the values it allows, in order. |
|
|
134
|
+
| `elementType` | string | no | SET, MULTISET and LIST: the type of their elements. |
|
|
135
|
+
| `nullable` | boolean | no | Whether it takes NULL. |
|
|
136
|
+
| `default` | string or null | no | The default as SQL text: `'RO'` (a string, quotes included), `0`, `SYS_DATETIME`. `null` when there is none. |
|
|
137
|
+
| `autoIncrement` | boolean | no | `true` for an AUTO_INCREMENT column. |
|
|
138
|
+
| `comment` | string or null | no | The column's comment. |
|
|
139
|
+
|
|
140
|
+
Examples of types:
|
|
141
|
+
|
|
142
|
+
| Declared as | Written as |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `INT` | `{ "type": "INTEGER" }` |
|
|
145
|
+
| `VARCHAR(100)` | `{ "type": "VARCHAR", "length": 100 }` |
|
|
146
|
+
| `VARCHAR` / `STRING` | `{ "type": "VARCHAR" }` |
|
|
147
|
+
| `CHAR(2)` | `{ "type": "CHAR", "length": 2 }` |
|
|
148
|
+
| `NUMERIC(12,2)` | `{ "type": "NUMERIC", "precision": 12, "scale": 2 }` |
|
|
149
|
+
| `ENUM('new','paid')` | `{ "type": "ENUM", "values": ["new", "paid"] }` |
|
|
150
|
+
| `SET OF VARCHAR(20)` | `{ "type": "SET", "elementType": "VARCHAR" }` |
|
|
151
|
+
|
|
152
|
+
## Keys and indexes
|
|
153
|
+
|
|
154
|
+
A foreign key:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{ "name": "fk_orders_customer", "columns": ["customer_id"],
|
|
158
|
+
"references": "dba.customers", "refColumns": ["id"],
|
|
159
|
+
"onDelete": "CASCADE", "onUpdate": "RESTRICT" }
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
| Field | Type | Required | Meaning |
|
|
163
|
+
|---|---|---|---|
|
|
164
|
+
| `name` | string | yes | The constraint's name. |
|
|
165
|
+
| `columns` | array of strings | yes | Its columns in this object, in order. |
|
|
166
|
+
| `references` | string | yes | The object it points to, as `owner.name`, or `name` when the owner is not known. Empty when the target could not be read. |
|
|
167
|
+
| `refColumns` | array of strings | yes | The columns it points to, in the same order as `columns`. |
|
|
168
|
+
| `onDelete`, `onUpdate` | string | no | `CASCADE`, `RESTRICT`, `NO ACTION` or `SET NULL`. Left out when not known. |
|
|
169
|
+
|
|
170
|
+
An index:
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{ "name": "idx_orders_status", "columns": ["status"], "unique": false, "comment": null }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
| Field | Type | Required | Meaning |
|
|
177
|
+
|---|---|---|---|
|
|
178
|
+
| `name` | string | yes | The index's name. |
|
|
179
|
+
| `columns` | array of strings | yes | Its columns, in key order. An index on an expression has none listed for that part. |
|
|
180
|
+
| `unique` | boolean | yes | Whether it is unique. |
|
|
181
|
+
| `comment` | string or null | no | The index's comment. |
|
|
182
|
+
|
|
183
|
+
## Procedures and functions
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{ "name": "calc_tax", "owner": "dba", "kind": "function", "returns": "NUMERIC",
|
|
187
|
+
"comment": "VAT for an amount, by country",
|
|
188
|
+
"params": [
|
|
189
|
+
{ "name": "amount", "type": "NUMERIC", "mode": "IN" },
|
|
190
|
+
{ "name": "country", "type": "CHAR", "mode": "IN", "comment": "ISO 3166 code" }
|
|
191
|
+
] }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
| Field | Type | Required | Meaning |
|
|
195
|
+
|---|---|---|---|
|
|
196
|
+
| `name` | string | yes | Its name. |
|
|
197
|
+
| `owner` | string | no | The user that owns it. |
|
|
198
|
+
| `kind` | `"procedure"`, `"function"` | yes | A function returns a value and is used in expressions; a procedure is run with `CALL`. |
|
|
199
|
+
| `returns` | string | no | Functions only: the type it returns. |
|
|
200
|
+
| `comment` | string or null | no | Its comment. |
|
|
201
|
+
| `params` | array | no | Its parameters, in order. Left out when not known. |
|
|
202
|
+
|
|
203
|
+
Each parameter:
|
|
204
|
+
|
|
205
|
+
| Field | Type | Required | Meaning |
|
|
206
|
+
|---|---|---|---|
|
|
207
|
+
| `name` | string | yes | The parameter's name. |
|
|
208
|
+
| `type` | string | yes | Its type. |
|
|
209
|
+
| `mode` | `"IN"`, `"OUT"`, `"INOUT"` | yes | Which way the value goes. |
|
|
210
|
+
| `comment` | string or null | no | Its comment. |
|
|
211
|
+
|
|
212
|
+
## Serials
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{ "name": "order_no", "owner": "dba", "comment": "Printed order numbers" }
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
| Field | Type | Required | Meaning |
|
|
219
|
+
|---|---|---|---|
|
|
220
|
+
| `name` | string | yes | Its name, as used in `order_no.NEXT_VALUE`. |
|
|
221
|
+
| `owner` | string | no | The user that owns it. |
|
|
222
|
+
| `comment` | string or null | no | Its comment. |
|
|
223
|
+
|
|
224
|
+
The serials the engine makes for AUTO_INCREMENT columns are not listed: the
|
|
225
|
+
columns are marked `"autoIncrement": true` instead.
|
|
226
|
+
|
|
227
|
+
## Synonyms
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{ "name": "o", "owner": "dba", "target": "dba.orders", "comment": null }
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
| Field | Type | Required | Meaning |
|
|
234
|
+
|---|---|---|---|
|
|
235
|
+
| `name` | string | yes | The synonym. |
|
|
236
|
+
| `owner` | string | no | The user that owns it. |
|
|
237
|
+
| `target` | string | yes | What it stands for, as `owner.name`. |
|
|
238
|
+
| `comment` | string or null | no | Its comment. |
|
|
239
|
+
|
|
240
|
+
## A complete example
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"format": "coradb-schema",
|
|
245
|
+
"version": 1,
|
|
246
|
+
"database": "shop",
|
|
247
|
+
"objects": [
|
|
248
|
+
{
|
|
249
|
+
"name": "customers", "owner": "dba", "kind": "table",
|
|
250
|
+
"comment": "People and companies that place orders",
|
|
251
|
+
"columns": [
|
|
252
|
+
{ "name": "id", "type": "INTEGER", "nullable": false, "default": null, "autoIncrement": true, "comment": null },
|
|
253
|
+
{ "name": "name", "type": "VARCHAR", "length": 100, "nullable": false, "default": null, "comment": "Full name" },
|
|
254
|
+
{ "name": "country", "type": "CHAR", "length": 2, "nullable": true, "default": "'RO'", "comment": "ISO 3166 code" },
|
|
255
|
+
{ "name": "kind", "type": "ENUM", "values": ["person", "company"], "nullable": false, "default": null, "comment": null }
|
|
256
|
+
],
|
|
257
|
+
"primaryKey": ["id"],
|
|
258
|
+
"unique": [ { "name": "u_customers_name", "columns": ["name"] } ],
|
|
259
|
+
"foreignKeys": [],
|
|
260
|
+
"indexes": [ { "name": "u_customers_name", "columns": ["name"], "unique": true, "comment": null } ]
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"name": "orders", "owner": "dba", "kind": "table",
|
|
264
|
+
"comment": "One row per customer order",
|
|
265
|
+
"columns": [
|
|
266
|
+
{ "name": "id", "type": "INTEGER", "nullable": false, "default": null, "autoIncrement": true, "comment": null },
|
|
267
|
+
{ "name": "customer_id", "type": "INTEGER", "nullable": false, "default": null, "comment": null },
|
|
268
|
+
{ "name": "total", "type": "NUMERIC", "precision": 12, "scale": 2, "nullable": false, "default": "0", "comment": "Including VAT, in EUR" },
|
|
269
|
+
{ "name": "created", "type": "DATETIME", "nullable": false, "default": "SYS_DATETIME", "comment": null }
|
|
270
|
+
],
|
|
271
|
+
"primaryKey": ["id"],
|
|
272
|
+
"unique": [],
|
|
273
|
+
"foreignKeys": [
|
|
274
|
+
{ "name": "fk_orders_customer", "columns": ["customer_id"], "references": "dba.customers", "refColumns": ["id"], "onDelete": "RESTRICT", "onUpdate": "RESTRICT" }
|
|
275
|
+
],
|
|
276
|
+
"indexes": [ { "name": "idx_orders_created", "columns": ["created"], "unique": false, "comment": null } ]
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"name": "big_orders", "owner": "dba", "kind": "view", "comment": "Orders over 1000 EUR",
|
|
280
|
+
"definition": "SELECT id, customer_id, total FROM orders WHERE total > 1000",
|
|
281
|
+
"columns": [
|
|
282
|
+
{ "name": "id", "type": "INTEGER", "nullable": true, "default": null, "comment": null },
|
|
283
|
+
{ "name": "customer_id", "type": "INTEGER", "nullable": true, "default": null, "comment": null },
|
|
284
|
+
{ "name": "total", "type": "NUMERIC", "precision": 12, "scale": 2, "nullable": true, "default": null, "comment": null }
|
|
285
|
+
]
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"name": "viewer", "owner": "dba", "kind": "node", "comment": "A registered viewer",
|
|
289
|
+
"columns": [ { "name": "id", "type": "INTEGER", "nullable": false }, { "name": "name", "type": "VARCHAR", "length": 100 } ],
|
|
290
|
+
"primaryKey": ["id"]
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"name": "rated", "owner": "dba", "kind": "edge", "comment": "A viewer rated a movie",
|
|
294
|
+
"columns": [ { "name": "stars", "type": "SMALLINT", "nullable": true } ]
|
|
295
|
+
}
|
|
296
|
+
],
|
|
297
|
+
"procedures": [
|
|
298
|
+
{ "name": "calc_tax", "owner": "dba", "kind": "function", "returns": "NUMERIC", "comment": "VAT for an amount, by country",
|
|
299
|
+
"params": [ { "name": "amount", "type": "NUMERIC", "mode": "IN", "comment": null },
|
|
300
|
+
{ "name": "country", "type": "CHAR", "mode": "IN", "comment": null } ] },
|
|
301
|
+
{ "name": "close_month", "owner": "dba", "kind": "procedure", "comment": null,
|
|
302
|
+
"params": [ { "name": "month", "type": "DATE", "mode": "IN", "comment": null } ] }
|
|
303
|
+
],
|
|
304
|
+
"serials": [ { "name": "order_no", "owner": "dba", "comment": "Printed order numbers" } ],
|
|
305
|
+
"synonyms": [ { "name": "o", "owner": "dba", "target": "dba.orders", "comment": null } ]
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Making a document
|
|
310
|
+
|
|
311
|
+
`tools/coradbSchema.ts` reads it from a live database. It is one
|
|
312
|
+
self-contained file, meant to be copied into an app's server. It works with
|
|
313
|
+
any driver: give it a function that runs a query and returns the rows as
|
|
314
|
+
objects keyed by lower-cased column name.
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
import { readCoraDbSchema } from './coradbSchema';
|
|
318
|
+
|
|
319
|
+
const schema = await readCoraDbSchema(async (sql) => {
|
|
320
|
+
const r = await conn.query(sql);
|
|
321
|
+
return r.rows.map((row) => Object.fromEntries(r.columns.map((c, i) => [c.toLowerCase(), row[i]])));
|
|
322
|
+
});
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
It reads the catalog views — `db_class`, `db_attribute`,
|
|
326
|
+
`db_attr_setdomain_elm`, `db_direct_super_class`, `db_index`,
|
|
327
|
+
`db_index_key`, `db_partition`, `db_vclass`, `db_serial`,
|
|
328
|
+
`db_stored_procedure`, `db_stored_procedure_args`, `db_synonym` — plus
|
|
329
|
+
`SHOW CREATE TABLE` for tables with foreign keys (where they point is only in
|
|
330
|
+
the DDL) and `SHOW COLUMNS` for tables with ENUM columns (for their values).
|
|
331
|
+
Engine builds differ in which catalog columns they have, so each read tries
|
|
332
|
+
the fullest form first and falls back to plainer ones; a part that cannot be
|
|
333
|
+
read at all is left out of the document.
|
|
334
|
+
|
|
335
|
+
It lists what the connection's user can see. System classes (the engine's own
|
|
336
|
+
catalog) are left out.
|
|
337
|
+
|
|
338
|
+
A document can also be written by hand, or by another program: only `format`,
|
|
339
|
+
`version` and `objects` with each object's `name` and `kind` are required.
|
|
340
|
+
|
|
341
|
+
## How the checker uses each part
|
|
342
|
+
|
|
343
|
+
| Part | What it lets `validate()` check | Without it |
|
|
344
|
+
|---|---|---|
|
|
345
|
+
| `objects` | That tables, views, node and edge classes exist; that a MATCH node label is a node class and an edge label an edge class. | — (required) |
|
|
346
|
+
| `columns` | That columns exist; ambiguous column names; INSERT column lists; how many values an INSERT row needs. | Any column of that object is accepted. |
|
|
347
|
+
| `under` | Inherited columns. | — |
|
|
348
|
+
| `procedures` | That called functions and procedures exist; how many arguments they take; a procedure used where a value is needed. | Function names that are not built-in are not checked. |
|
|
349
|
+
| `serials` | That `name.NEXT_VALUE` and `name.CURRENT_VALUE` name a serial. | Serials are not checked. |
|
|
350
|
+
| `synonyms` | That a synonym used as a table name stands for an existing object. | A name only a synonym would explain is reported as unknown. |
|
|
351
|
+
| comments, keys, indexes, `definition`, types | Nothing: they are there to describe the database. | — |
|
|
352
|
+
|
|
353
|
+
The built-in functions, the system catalog classes (`db_*`) and the
|
|
354
|
+
pseudo-columns (`ROWNUM`, `LEVEL`, …) belong to the engine, not to a database,
|
|
355
|
+
so they are not in the document: the checker knows them itself.
|
|
356
|
+
|
|
357
|
+
## Versions
|
|
358
|
+
|
|
359
|
+
`version` is raised only when a change would make an older reader
|
|
360
|
+
misunderstand a document: a field changing its meaning, or a new required
|
|
361
|
+
field. Adding an optional field does not change the version; readers ignore
|
|
362
|
+
fields they do not know.
|