@portll/cobolwork 0.6.0 → 0.7.0
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 +49 -18
- package/STABILITY.md +76 -0
- package/bin/cobolwork.mjs +30 -7
- package/lib/bms.mjs +21 -7
- package/lib/build.mjs +108 -53
- package/lib/capabilities.mjs +9 -10
- package/lib/cics-commands.mjs +501 -9
- package/lib/compliance.mjs +11 -0
- package/lib/consequence.mjs +13 -0
- package/lib/control-workers.mjs +161 -0
- package/lib/control.mjs +52 -3
- package/lib/dataflow.mjs +176 -125
- package/lib/db2/cursor.mjs +15 -0
- package/lib/db2/read.mjs +170 -0
- package/lib/db2/rules.mjs +77 -0
- package/lib/db2/stmt/alter.mjs +473 -0
- package/lib/db2/stmt/grant.mjs +125 -0
- package/lib/db2/stmt/index.mjs +141 -0
- package/lib/db2/stmt/misc.mjs +325 -0
- package/lib/db2/stmt/routine.mjs +564 -0
- package/lib/db2/stmt/storage.mjs +146 -0
- package/lib/db2/stmt/table.mjs +540 -0
- package/lib/db2/stmt/view.mjs +146 -0
- package/lib/diff.mjs +17 -5
- package/lib/evidence/cli.mjs +14 -3
- package/lib/evidence/record.mjs +1 -1
- package/lib/evidence/store.mjs +44 -31
- package/lib/exec-reading.mjs +51 -0
- package/lib/execution.mjs +3 -2
- package/lib/explain.mjs +2 -0
- package/lib/exploitability.mjs +11 -2
- package/lib/hlasm/asm/data.mjs +13 -1
- package/lib/hlasm/asm/sections.mjs +3 -1
- package/lib/hlasm/instr.mjs +25 -0
- package/lib/hlasm/macro/authorization.mjs +24 -4
- package/lib/hlasm/macro/datasets.mjs +30 -17
- package/lib/hlasm/macro/io.mjs +68 -45
- package/lib/hlasm/macro/le.mjs +2 -2
- package/lib/hlasm/macro/operator.mjs +32 -21
- package/lib/hlasm/macro/program.mjs +126 -84
- package/lib/hlasm/macro/recovery.mjs +10 -5
- package/lib/hlasm/macro/storage.mjs +64 -14
- package/lib/hlasm/macro/structured.mjs +1 -1
- package/lib/hlasm/model.mjs +35 -10
- package/lib/hlasm/mvs38.mjs +47 -0
- package/lib/hlasm/operands.mjs +10 -0
- package/lib/hlasm/optable.mjs +61 -0
- package/lib/hlasm/read.mjs +22 -8
- package/lib/hlasm.mjs +44 -7
- package/lib/ims/dli.mjs +37 -0
- package/lib/ims/macro/dbd.mjs +299 -0
- package/lib/ims/macro/psb.mjs +286 -0
- package/lib/ims/model.mjs +149 -0
- package/lib/ims/operands.mjs +23 -0
- package/lib/ims/read.mjs +37 -0
- package/lib/ims/rules.mjs +135 -0
- package/lib/ironwork.mjs +17 -13
- package/lib/kernel/pds-archive.mjs +256 -0
- package/lib/kernel/registry.mjs +14 -8
- package/lib/kernel/shared-pass.mjs +34 -9
- package/lib/kernel/source-tree.mjs +104 -36
- package/lib/kernel/version-key.mjs +17 -0
- package/lib/layout.mjs +26 -31
- package/lib/parser.mjs +136 -17
- package/lib/pli/cursor.mjs +15 -0
- package/lib/pli/expr.mjs +101 -0
- package/lib/pli/include.mjs +82 -0
- package/lib/pli/layout.mjs +125 -0
- package/lib/pli/lex.mjs +198 -0
- package/lib/pli/program.mjs +280 -0
- package/lib/pli/rules/based.mjs +95 -0
- package/lib/pli/rules/conditions.mjs +68 -0
- package/lib/pli/rules/entry.mjs +130 -0
- package/lib/pli/rules/index.mjs +24 -0
- package/lib/pli/rules/preprocessor.mjs +55 -0
- package/lib/pli/statements.mjs +130 -0
- package/lib/pli/stmt/alloc.mjs +45 -0
- package/lib/pli/stmt/assignment.mjs +56 -0
- package/lib/pli/stmt/call.mjs +104 -0
- package/lib/pli/stmt/conditions.mjs +94 -0
- package/lib/pli/stmt/control.mjs +219 -0
- package/lib/pli/stmt/declare.mjs +149 -0
- package/lib/pli/stmt/exec.mjs +55 -0
- package/lib/pli/stmt/io.mjs +239 -0
- package/lib/pli/stmt/misc.mjs +4 -0
- package/lib/pli/stmt/preprocessor.mjs +242 -0
- package/lib/pli/stmt/procedure.mjs +258 -0
- package/lib/pli/stmt/stream.mjs +283 -0
- package/lib/pli/storage.mjs +129 -0
- package/lib/precompile-check.mjs +124 -0
- package/lib/precompile-cics.mjs +7 -3
- package/lib/reach.mjs +11 -2
- package/lib/revision.json +1 -1
- package/lib/sarif.mjs +41 -3
- package/lib/scan.mjs +7 -1
- package/lib/sets/abend.mjs +16 -6
- package/lib/sets/cics.mjs +15 -35
- package/lib/sets/compile.mjs +41 -15
- package/lib/sets/crypto.mjs +5 -3
- package/lib/sets/ddl.mjs +36 -0
- package/lib/sets/flow.mjs +18 -1
- package/lib/sets/hidden.mjs +5 -3
- package/lib/sets/hlasm.mjs +72 -12
- package/lib/sets/ims.mjs +139 -0
- package/lib/sets/log.mjs +6 -6
- package/lib/sets/opaque.mjs +27 -7
- package/lib/sets/pli.mjs +40 -0
- package/lib/sets/recon.mjs +5 -3
- package/lib/sets/secrets.mjs +5 -3
- package/lib/sets/semantics.mjs +3 -0
- package/lib/sets/web.mjs +36 -22
- package/lib/site.mjs +10 -0
- package/lib/sources.mjs +80 -20
- package/lib/statement-cursor.mjs +67 -0
- package/lib/verify.mjs +3 -2
- package/lib/version.mjs +6 -0
- package/package.json +3 -2
- package/rules/compliance-cobit2019.json +432 -1
- package/rules/compliance-dora.json +414 -1
- package/rules/compliance-ffiec.json +414 -1
- package/rules/compliance-nist80053.json +466 -1
- package/rules/hlasm-optables.json +8024 -0
- package/schema/cobolwork-baseline.schema.json +36 -0
- package/schema/cobolwork-build-provenance.schema.json +187 -0
- package/schema/cobolwork-build.schema.json +382 -0
- package/schema/cobolwork-capabilities.schema.json +239 -0
- package/schema/cobolwork-diff.schema.json +217 -0
- package/schema/cobolwork-evidence.schema.json +161 -0
- package/schema/cobolwork-execution.schema.json +53 -0
- package/schema/cobolwork-explain.schema.json +360 -0
- package/schema/cobolwork-finding.schema.json +465 -0
- package/schema/cobolwork-flow.schema.json +465 -0
- package/schema/cobolwork-gate.schema.json +211 -0
- package/schema/cobolwork-inventory.schema.json +206 -0
- package/schema/cobolwork-parse.schema.json +105 -0
- package/schema/cobolwork-reach.schema.json +74 -0
- package/schema/cobolwork-report.schema.json +559 -0
- package/schema/cobolwork-witness.schema.json +107 -0
- package/schema/cobolwork.baseline.schema.json +101 -0
- package/schema/cobolwork.site.schema.json +116 -0
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// Db2 for z/OS CREATE TABLE, CREATE GLOBAL TEMPORARY TABLE and CREATE AUXILIARY TABLE, after the
|
|
3
|
+
// Db2 13 SQL Reference: columns with their types, nullability, defaults and constraints, the table's
|
|
4
|
+
// constraints and periods, and the clauses that place and describe the table.
|
|
5
|
+
|
|
6
|
+
import { cursor } from '../cursor.mjs';
|
|
7
|
+
|
|
8
|
+
const isName = (t) => !!t && (t.t === 'word' || t.t === 'ident');
|
|
9
|
+
|
|
10
|
+
export function readName(c) {
|
|
11
|
+
const t = c.next();
|
|
12
|
+
if (!isName(t)) { c.pos -= t ? 1 : 0; c.fail('a name'); }
|
|
13
|
+
const parts = [t.v];
|
|
14
|
+
while (c.op('.')) {
|
|
15
|
+
const p = c.next();
|
|
16
|
+
if (!isName(p)) { c.pos -= p ? 1 : 0; c.fail('a name part'); }
|
|
17
|
+
parts.push(p.v);
|
|
18
|
+
}
|
|
19
|
+
return parts;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function readInteger(c) {
|
|
23
|
+
const t = c.next();
|
|
24
|
+
if (!t || t.t !== 'num' || !/^\d+$/.test(t.v)) { c.pos -= t ? 1 : 0; c.fail('an integer'); }
|
|
25
|
+
return Number(t.v);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function readNumber(c) {
|
|
29
|
+
const sign = c.op('-') ? '-' : (c.op('+'), '');
|
|
30
|
+
const t = c.next();
|
|
31
|
+
if (!t || t.t !== 'num') { c.pos -= t ? 1 : 0; c.fail('a number'); }
|
|
32
|
+
return sign + t.v;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function readNameList(c) {
|
|
36
|
+
return c.items().map((item) => {
|
|
37
|
+
const s = cursor(item);
|
|
38
|
+
const name = readName(s);
|
|
39
|
+
if (!s.done()) s.fail("',' or ')'");
|
|
40
|
+
return name.join('.');
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function only(c, words) {
|
|
45
|
+
const t = c.expectWord(words);
|
|
46
|
+
return t.u;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// (n) or (nK), (nM), (nG): a LOB or DSSIZE length.
|
|
50
|
+
const readUnitSize = (c) => `${readInteger(c)}${only(c, ['K', 'M', 'G'])}`;
|
|
51
|
+
|
|
52
|
+
export function readSize(c) {
|
|
53
|
+
const n = readInteger(c);
|
|
54
|
+
const unit = c.word(['K', 'M', 'G']);
|
|
55
|
+
return unit ? `${n}${unit.u}` : n;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function readLength(c, withUnit = false) {
|
|
59
|
+
const s = cursor(c.group());
|
|
60
|
+
const length = withUnit ? readSize(s) : readInteger(s);
|
|
61
|
+
if (!s.done()) s.fail("')'");
|
|
62
|
+
return length;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function readCcsidAndData(c, type) {
|
|
66
|
+
for (;;) {
|
|
67
|
+
if (c.isWord('FOR') && c.isWord(['SBCS', 'MIXED', 'BIT'], 1) && c.isWord('DATA', 2)) {
|
|
68
|
+
c.next();
|
|
69
|
+
type.forData = c.next().u;
|
|
70
|
+
c.next();
|
|
71
|
+
} else if (c.isWord('CCSID') && c.peek(1) && c.peek(1).t === 'num') {
|
|
72
|
+
c.next();
|
|
73
|
+
type.ccsid = readInteger(c);
|
|
74
|
+
} else return type;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const NUMERIC = { SMALLINT: 'SMALLINT', INTEGER: 'INTEGER', INT: 'INTEGER', BIGINT: 'BIGINT', REAL: 'REAL' };
|
|
79
|
+
const NOT_A_TYPE = new Set(['NOT', 'NULL', 'DEFAULT', 'WITH', 'GENERATED', 'CONSTRAINT', 'PRIMARY', 'UNIQUE', 'REFERENCES', 'CHECK']);
|
|
80
|
+
|
|
81
|
+
export function readDataType(c) {
|
|
82
|
+
const t = c.peek();
|
|
83
|
+
if (!isName(t)) c.fail('a data type');
|
|
84
|
+
const u = t.t === 'word' ? t.u : null;
|
|
85
|
+
if (u && NUMERIC[u]) { c.next(); return { name: NUMERIC[u] }; }
|
|
86
|
+
if (u === 'DECIMAL' || u === 'DEC' || u === 'NUMERIC') {
|
|
87
|
+
c.next();
|
|
88
|
+
const type = { name: 'DECIMAL', precision: 5, scale: 0 };
|
|
89
|
+
if (c.isOp('(')) {
|
|
90
|
+
const s = cursor(c.group());
|
|
91
|
+
type.precision = readInteger(s);
|
|
92
|
+
if (s.op(',')) type.scale = readInteger(s);
|
|
93
|
+
if (!s.done()) s.fail("')'");
|
|
94
|
+
}
|
|
95
|
+
return type;
|
|
96
|
+
}
|
|
97
|
+
if (u === 'FLOAT') { c.next(); return { name: 'FLOAT', precision: c.isOp('(') ? readLength(c) : 53 }; }
|
|
98
|
+
if (u === 'DOUBLE') { c.next(); c.word('PRECISION'); return { name: 'DOUBLE' }; }
|
|
99
|
+
if (u === 'DECFLOAT') {
|
|
100
|
+
c.next();
|
|
101
|
+
const precision = c.isOp('(') ? readLength(c) : 34;
|
|
102
|
+
if (precision !== 16 && precision !== 34) c.fail('DECFLOAT(16) or DECFLOAT(34)');
|
|
103
|
+
return { name: 'DECFLOAT', precision };
|
|
104
|
+
}
|
|
105
|
+
// LONG VARCHAR and LONG VARGRAPHIC stay for compatibility; Db2 works out their length.
|
|
106
|
+
if (u === 'LONG' && c.isWord(['VARCHAR', 'VARGRAPHIC'], 1)) {
|
|
107
|
+
c.next();
|
|
108
|
+
const name = c.next().u;
|
|
109
|
+
return name === 'VARCHAR' ? readCcsidAndData(c, { name, long: true }) : { name, long: true };
|
|
110
|
+
}
|
|
111
|
+
if (u === 'CHARACTER' || u === 'CHAR' || u === 'VARCHAR' || u === 'CLOB') {
|
|
112
|
+
c.next();
|
|
113
|
+
let name = u === 'VARCHAR' ? 'VARCHAR' : u === 'CLOB' ? 'CLOB' : 'CHAR';
|
|
114
|
+
if (name === 'CHAR' && c.word('VARYING')) name = 'VARCHAR';
|
|
115
|
+
else if (name === 'CHAR' && c.isWord('LARGE') && c.isWord('OBJECT', 1)) { c.next(); c.next(); name = 'CLOB'; }
|
|
116
|
+
const type = { name };
|
|
117
|
+
if (name === 'VARCHAR') type.length = readLength(c);
|
|
118
|
+
else if (name === 'CLOB') type.length = c.isOp('(') ? readLength(c, true) : '1M';
|
|
119
|
+
else type.length = c.isOp('(') ? readLength(c) : 1;
|
|
120
|
+
return readCcsidAndData(c, type);
|
|
121
|
+
}
|
|
122
|
+
if (u === 'GRAPHIC' || u === 'VARGRAPHIC' || u === 'DBCLOB') {
|
|
123
|
+
c.next();
|
|
124
|
+
const type = { name: u };
|
|
125
|
+
if (u === 'VARGRAPHIC') type.length = readLength(c);
|
|
126
|
+
else if (u === 'DBCLOB') type.length = c.isOp('(') ? readLength(c, true) : '1M';
|
|
127
|
+
else type.length = c.isOp('(') ? readLength(c) : 1;
|
|
128
|
+
if (c.word('CCSID')) type.ccsid = readInteger(c);
|
|
129
|
+
return type;
|
|
130
|
+
}
|
|
131
|
+
if (u === 'BINARY' || u === 'VARBINARY' || u === 'BLOB') {
|
|
132
|
+
c.next();
|
|
133
|
+
let name = u;
|
|
134
|
+
if (u === 'BINARY' && c.word('VARYING')) name = 'VARBINARY';
|
|
135
|
+
else if (u === 'BINARY' && c.isWord('LARGE') && c.isWord('OBJECT', 1)) { c.next(); c.next(); name = 'BLOB'; }
|
|
136
|
+
if (name === 'VARBINARY') return { name, length: readLength(c) };
|
|
137
|
+
if (name === 'BLOB') return { name, length: c.isOp('(') ? readLength(c, true) : '1M' };
|
|
138
|
+
return { name, length: c.isOp('(') ? readLength(c) : 1 };
|
|
139
|
+
}
|
|
140
|
+
if (u === 'DATE' || u === 'TIME' || u === 'ROWID') { c.next(); return { name: u }; }
|
|
141
|
+
if (u === 'TIMESTAMP') {
|
|
142
|
+
c.next();
|
|
143
|
+
const type = { name: 'TIMESTAMP', precision: c.isOp('(') ? readLength(c) : 6, timeZone: false };
|
|
144
|
+
if (c.isWord(['WITH', 'WITHOUT']) && ((c.isWord('TIME', 1) && c.isWord('ZONE', 2)) || c.isWord('TIMEZONE', 1))) {
|
|
145
|
+
type.timeZone = c.next().u === 'WITH';
|
|
146
|
+
if (!c.word('TIMEZONE')) { c.next(); c.next(); }
|
|
147
|
+
}
|
|
148
|
+
return type;
|
|
149
|
+
}
|
|
150
|
+
if (u === 'XML') {
|
|
151
|
+
c.next();
|
|
152
|
+
return c.isOp('(') ? { name: 'XML', modifier: c.group() } : { name: 'XML' };
|
|
153
|
+
}
|
|
154
|
+
if (u && NOT_A_TYPE.has(u)) c.fail('a data type');
|
|
155
|
+
return { name: 'DISTINCT', distinct: readName(c).join('.') };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// A default is a constant, SESSION_USER or USER, CURRENT SQLID, NULL, or a distinct type's cast
|
|
159
|
+
// function applied to one of those; DEFAULT alone takes the type's own default.
|
|
160
|
+
export function readDefault(c) {
|
|
161
|
+
const t = c.peek();
|
|
162
|
+
if (!t) return { implicit: true };
|
|
163
|
+
if (t.t === 'lit') { c.next(); return { constant: t.v, prefix: t.prefix }; }
|
|
164
|
+
if (t.t === 'num' || (t.t === 'op' && (t.v === '-' || t.v === '+'))) return { constant: readNumber(c) };
|
|
165
|
+
if (c.word(['SESSION_USER', 'USER'])) return { register: 'SESSION_USER' };
|
|
166
|
+
if (c.word('NULL')) return { null: true };
|
|
167
|
+
if (c.isWord('CURRENT')) {
|
|
168
|
+
c.next();
|
|
169
|
+
c.expectWord('SQLID');
|
|
170
|
+
return { register: 'CURRENT SQLID' };
|
|
171
|
+
}
|
|
172
|
+
if (isName(t) && (c.isOp('(', 1) || (c.isOp('.', 1) && c.isOp('(', 3)))) {
|
|
173
|
+
const cast = readName(c).join('.');
|
|
174
|
+
const s = cursor(c.group());
|
|
175
|
+
const value = readDefault(s);
|
|
176
|
+
if (value.implicit || !s.done()) s.fail('a constant, SESSION_USER, USER, CURRENT SQLID or NULL');
|
|
177
|
+
return { cast, ...value };
|
|
178
|
+
}
|
|
179
|
+
return { implicit: true };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const NO_OPTION = { MINVALUE: 'minValue', MAXVALUE: 'maxValue', CYCLE: 'cycle', CACHE: 'cache', ORDER: 'order' };
|
|
183
|
+
|
|
184
|
+
// NOCACHE, NOCYCLE, NOMINVALUE, NOMAXVALUE and NOORDER are IBM's synonyms for the two-word forms.
|
|
185
|
+
function readIdentityOptions(c) {
|
|
186
|
+
const options = {};
|
|
187
|
+
if (!c.isOp('(')) return options;
|
|
188
|
+
const s = cursor(c.group());
|
|
189
|
+
while (!s.done()) {
|
|
190
|
+
if (s.word('START')) { s.expectWord('WITH'); options.startWith = readNumber(s); }
|
|
191
|
+
else if (s.word('INCREMENT')) { s.expectWord('BY'); options.incrementBy = readNumber(s); }
|
|
192
|
+
else if (s.word('MINVALUE')) options.minValue = readNumber(s);
|
|
193
|
+
else if (s.word('MAXVALUE')) options.maxValue = readNumber(s);
|
|
194
|
+
else if (s.word('CYCLE')) options.cycle = true;
|
|
195
|
+
else if (s.word('ORDER')) options.order = true;
|
|
196
|
+
else if (s.word('CACHE')) options.cache = readInteger(s);
|
|
197
|
+
else if (s.word('NO')) options[NO_OPTION[only(s, Object.keys(NO_OPTION))]] = false;
|
|
198
|
+
else if (s.isWord(Object.keys(NO_OPTION).map((w) => `NO${w}`))) options[NO_OPTION[s.next().u.slice(2)]] = false;
|
|
199
|
+
else s.fail('an identity attribute');
|
|
200
|
+
s.op(',');
|
|
201
|
+
}
|
|
202
|
+
return options;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function readGenerated(c) {
|
|
206
|
+
c.expectWord('GENERATED');
|
|
207
|
+
const when = c.word('ALWAYS') ? 'ALWAYS' : (c.expectWord('BY'), c.expectWord('DEFAULT'), 'BY DEFAULT');
|
|
208
|
+
if (c.word('FOR')) {
|
|
209
|
+
for (const w of ['EACH', 'ROW', 'ON', 'UPDATE', 'AS', 'ROW', 'CHANGE', 'TIMESTAMP']) c.expectWord(w);
|
|
210
|
+
return { when, as: 'ROW CHANGE TIMESTAMP' };
|
|
211
|
+
}
|
|
212
|
+
if (!c.word('AS')) return { when };
|
|
213
|
+
if (c.word('IDENTITY')) return { when, as: 'IDENTITY', identity: readIdentityOptions(c) };
|
|
214
|
+
if (when !== 'ALWAYS') c.fail('AS IDENTITY or FOR EACH ROW ON UPDATE');
|
|
215
|
+
if (c.word('TRANSACTION')) { c.expectWord('START'); c.expectWord('ID'); return { when, as: 'TRANSACTION START ID' }; }
|
|
216
|
+
if (c.word('ROW')) {
|
|
217
|
+
const which = only(c, ['BEGIN', 'START', 'END']);
|
|
218
|
+
return { when, as: `ROW ${which === 'START' ? 'BEGIN' : which}` };
|
|
219
|
+
}
|
|
220
|
+
return { when, as: 'EXPRESSION', expression: c.group() };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function readReferences(c) {
|
|
224
|
+
c.expectWord('REFERENCES');
|
|
225
|
+
const ref = { table: readName(c).join('.'), columns: null };
|
|
226
|
+
if (c.isOp('(')) {
|
|
227
|
+
const parts = c.items();
|
|
228
|
+
ref.columns = [];
|
|
229
|
+
for (const p of parts) {
|
|
230
|
+
const s = cursor(p);
|
|
231
|
+
if (s.word('PERIOD')) { s.expectWord('BUSINESS_TIME'); ref.period = 'BUSINESS_TIME'; }
|
|
232
|
+
else ref.columns.push(readName(s).join('.'));
|
|
233
|
+
if (!s.done()) s.fail("',' or ')'");
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
for (;;) {
|
|
237
|
+
if (c.isWord('ON') && c.isWord('DELETE', 1)) {
|
|
238
|
+
c.next();
|
|
239
|
+
c.next();
|
|
240
|
+
if (c.word('NO')) { c.expectWord('ACTION'); ref.onDelete = 'NO ACTION'; }
|
|
241
|
+
else if (c.word('SET')) { c.expectWord('NULL'); ref.onDelete = 'SET NULL'; }
|
|
242
|
+
else ref.onDelete = only(c, ['RESTRICT', 'CASCADE']);
|
|
243
|
+
} else if (c.word('ENFORCED')) ref.enforced = true;
|
|
244
|
+
else if (c.isWord('NOT') && c.isWord('ENFORCED', 1)) { c.next(); c.next(); ref.enforced = false; }
|
|
245
|
+
else if (c.isWord('ENABLE') && c.isWord('QUERY', 1)) { c.next(); c.next(); c.expectWord('OPTIMIZATION'); ref.queryOptimization = true; }
|
|
246
|
+
else return ref;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function readColumnConstraint(c) {
|
|
251
|
+
const name = c.word('CONSTRAINT') ? readName(c).join('.') : null;
|
|
252
|
+
if (c.word('PRIMARY')) { c.expectWord('KEY'); return { type: 'PRIMARY KEY', name }; }
|
|
253
|
+
if (c.word('UNIQUE')) return { type: 'UNIQUE', name };
|
|
254
|
+
if (c.isWord('REFERENCES')) return { type: 'FOREIGN KEY', name, references: readReferences(c) };
|
|
255
|
+
if (c.word('CHECK')) return { type: 'CHECK', name, condition: c.group() };
|
|
256
|
+
c.fail('PRIMARY KEY, UNIQUE, REFERENCES or CHECK');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const startsColumnConstraint = (c) => c.isWord(['CONSTRAINT', 'PRIMARY', 'UNIQUE', 'REFERENCES']) || (c.isWord('CHECK') && c.isOp('(', 1));
|
|
260
|
+
|
|
261
|
+
export function readColumnDefinition(c) {
|
|
262
|
+
const col = { name: readName(c).join('.'), type: null, notNull: false, constraints: [] };
|
|
263
|
+
if (!c.isWord('GENERATED')) col.type = readDataType(c);
|
|
264
|
+
const once = (key) => { if (key in col) c.fail(`no second ${key}`); };
|
|
265
|
+
while (!c.done()) {
|
|
266
|
+
if (c.isWord('NOT') && c.isWord('NULL', 1)) { c.next(); c.next(); col.notNull = true; }
|
|
267
|
+
else if (c.isWord('WITH') && c.isWord('DEFAULT', 1)) { once('default'); c.next(); c.next(); col.default = readDefault(c); }
|
|
268
|
+
else if (c.word('DEFAULT')) { once('default'); col.default = readDefault(c); }
|
|
269
|
+
else if (c.isWord('GENERATED')) { once('generated'); col.generated = readGenerated(c); }
|
|
270
|
+
else if (startsColumnConstraint(c)) col.constraints.push(readColumnConstraint(c));
|
|
271
|
+
else if (c.word('FIELDPROC')) {
|
|
272
|
+
col.fieldproc = { program: readName(c).join('.'), args: c.isOp('(') ? c.items() : [] };
|
|
273
|
+
} else if (c.isWord('AS') && c.isWord('SECURITY', 1)) { c.next(); c.next(); c.expectWord('LABEL'); col.securityLabel = true; }
|
|
274
|
+
else if (c.word('IMPLICITLY')) { c.expectWord('HIDDEN'); col.hidden = true; }
|
|
275
|
+
else if (c.word('INLINE')) { c.expectWord('LENGTH'); col.inlineLength = readInteger(c); }
|
|
276
|
+
else c.fail('a column attribute');
|
|
277
|
+
}
|
|
278
|
+
if (!col.type && !(col.generated && col.generated.as === 'ROW CHANGE TIMESTAMP')) c.fail('a data type');
|
|
279
|
+
return col;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function readKeyColumns(c, allowPeriod) {
|
|
283
|
+
const columns = [];
|
|
284
|
+
let period = null;
|
|
285
|
+
for (const p of c.items()) {
|
|
286
|
+
const s = cursor(p);
|
|
287
|
+
if (allowPeriod === 'unique' && s.isWord('BUSINESS_TIME') && s.isWord('WITHOUT', 1)) {
|
|
288
|
+
s.next(); s.next(); s.expectWord('OVERLAPS'); period = 'BUSINESS_TIME WITHOUT OVERLAPS';
|
|
289
|
+
} else if (allowPeriod === 'foreign' && s.word('PERIOD')) { s.expectWord('BUSINESS_TIME'); period = 'BUSINESS_TIME'; }
|
|
290
|
+
else columns.push(readName(s).join('.'));
|
|
291
|
+
if (!s.done()) s.fail("',' or ')'");
|
|
292
|
+
}
|
|
293
|
+
return period ? { columns, period } : { columns };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function readTableConstraint(c) {
|
|
297
|
+
const name = c.word('CONSTRAINT') ? readName(c).join('.') : null;
|
|
298
|
+
if (c.word('PRIMARY')) { c.expectWord('KEY'); return { type: 'PRIMARY KEY', name, ...readKeyColumns(c, 'unique') }; }
|
|
299
|
+
if (c.word('UNIQUE')) return { type: 'UNIQUE', name, ...readKeyColumns(c, 'unique') };
|
|
300
|
+
if (c.word('FOREIGN')) {
|
|
301
|
+
c.expectWord('KEY');
|
|
302
|
+
// FOREIGN KEY name (…), kept for compatibility with earlier releases, is how IBM's DSNTEJ1 names them.
|
|
303
|
+
const keyName = isName(c.peek()) && c.isOp('(', 1) ? c.next().v : null;
|
|
304
|
+
const key = readKeyColumns(c, 'foreign');
|
|
305
|
+
return { type: 'FOREIGN KEY', name: name ?? keyName, ...key, references: readReferences(c) };
|
|
306
|
+
}
|
|
307
|
+
if (c.word('CHECK')) return { type: 'CHECK', name, condition: c.group() };
|
|
308
|
+
c.fail('PRIMARY KEY, UNIQUE, FOREIGN KEY or CHECK');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function readPeriod(c) {
|
|
312
|
+
c.expectWord('PERIOD');
|
|
313
|
+
c.word('FOR');
|
|
314
|
+
const period = { name: only(c, ['SYSTEM_TIME', 'BUSINESS_TIME']) };
|
|
315
|
+
const s = cursor(c.group());
|
|
316
|
+
period.begin = readName(s).join('.');
|
|
317
|
+
s.expectOp(',');
|
|
318
|
+
period.end = readName(s).join('.');
|
|
319
|
+
if (period.name === 'BUSINESS_TIME') { const k = s.word(['EXCLUSIVE', 'INCLUSIVE']); if (k) period.endpoint = k.u; }
|
|
320
|
+
if (!s.done()) s.fail("')'");
|
|
321
|
+
return period;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const startsTableConstraint = (c) =>
|
|
325
|
+
c.isWord('CONSTRAINT') || (c.isWord('PRIMARY') && c.isWord('KEY', 1)) || (c.isWord('UNIQUE') && c.isOp('(', 1)) ||
|
|
326
|
+
(c.isWord('FOREIGN') && c.isWord('KEY', 1)) || (c.isWord('CHECK') && c.isOp('(', 1));
|
|
327
|
+
|
|
328
|
+
function readElements(c, node) {
|
|
329
|
+
for (const item of c.items()) {
|
|
330
|
+
const s = cursor(item);
|
|
331
|
+
if (!item.length) c.fail('a column definition');
|
|
332
|
+
if (s.isWord('PERIOD') && (s.isWord('FOR', 1) || s.isWord(['SYSTEM_TIME', 'BUSINESS_TIME'], 1))) node.periods.push(readPeriod(s));
|
|
333
|
+
else if (startsTableConstraint(s)) node.constraints.push(readTableConstraint(s));
|
|
334
|
+
else node.columns.push(readColumnDefinition(s));
|
|
335
|
+
if (!s.done()) s.fail("',' or ')'");
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function readCopyOptions(c, node) {
|
|
340
|
+
for (;;) {
|
|
341
|
+
const verb = c.isWord(['EXCLUDING', 'INCLUDING']) ? c.peek().u : null;
|
|
342
|
+
if (verb && c.isWord('IDENTITY', 1)) { c.next(); c.next(); if (c.word('COLUMN')) c.expectWord('ATTRIBUTES'); node.copy.identity = verb; }
|
|
343
|
+
else if (verb && c.isWord('ROW', 1)) {
|
|
344
|
+
c.next(); c.next(); c.expectWord('CHANGE'); c.expectWord('TIMESTAMP');
|
|
345
|
+
if (c.word('COLUMN')) c.expectWord('ATTRIBUTES');
|
|
346
|
+
node.copy.rowChangeTimestamp = verb;
|
|
347
|
+
} else if (verb && (c.isWord('COLUMN', 1) || c.isWord('DEFAULTS', 1))) { c.next(); c.word('COLUMN'); c.expectWord('DEFAULTS'); node.copy.defaults = verb; }
|
|
348
|
+
else if (c.isWord('USING') && c.isWord('TYPE', 1)) { c.next(); c.next(); c.expectWord('DEFAULTS'); node.copy.defaults = 'USING TYPE'; }
|
|
349
|
+
else if (verb === 'EXCLUDING' && c.isWord('XML', 1)) { c.next(); c.next(); c.expectWord('TYPE'); c.expectWord('MODIFIERS'); node.copy.xmlTypeModifiers = 'EXCLUDING'; }
|
|
350
|
+
else return;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function readRefreshOptions(c, node) {
|
|
355
|
+
const mq = node.asResult;
|
|
356
|
+
for (;;) {
|
|
357
|
+
if (c.isWord('DATA') && c.isWord('INITIALLY', 1)) { c.next(); c.next(); c.expectWord('DEFERRED'); mq.dataInitiallyDeferred = true; }
|
|
358
|
+
else if (c.isWord('REFRESH') && c.isWord('DEFERRED', 1)) { c.next(); c.next(); mq.refreshDeferred = true; }
|
|
359
|
+
else if (c.word('MAINTAINED')) { c.expectWord('BY'); mq.maintainedBy = only(c, ['SYSTEM', 'USER']); }
|
|
360
|
+
else if (c.isWord(['ENABLE', 'DISABLE']) && c.isWord('QUERY', 1)) { mq.queryOptimization = c.next().u === 'ENABLE'; c.next(); c.expectWord('OPTIMIZATION'); }
|
|
361
|
+
else return;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export function readPartitioning(c) {
|
|
366
|
+
c.expectWord('PARTITION');
|
|
367
|
+
c.expectWord('BY');
|
|
368
|
+
if (c.word('SIZE')) {
|
|
369
|
+
const p = { by: 'SIZE' };
|
|
370
|
+
if (c.word('EVERY')) { p.every = readInteger(c); c.expectWord('G'); }
|
|
371
|
+
return p;
|
|
372
|
+
}
|
|
373
|
+
c.word('RANGE');
|
|
374
|
+
const p = { by: 'RANGE', columns: [], partitions: [] };
|
|
375
|
+
for (const item of c.items()) {
|
|
376
|
+
const s = cursor(item);
|
|
377
|
+
const col = { name: readName(s).join('.') };
|
|
378
|
+
if (s.word('NULLS')) { s.expectWord('LAST'); col.nullsLast = true; }
|
|
379
|
+
const order = s.word(['ASC', 'DESC']);
|
|
380
|
+
if (order) col.order = order.u;
|
|
381
|
+
if (!s.done()) s.fail("',' or ')'");
|
|
382
|
+
p.columns.push(col);
|
|
383
|
+
}
|
|
384
|
+
for (const item of c.items()) {
|
|
385
|
+
const s = cursor(item);
|
|
386
|
+
s.expectWord(['PARTITION', 'PART']);
|
|
387
|
+
const part = { number: readInteger(s) };
|
|
388
|
+
if (!s.word('VALUES')) { s.expectWord('ENDING'); s.word('AT'); }
|
|
389
|
+
part.limits = s.items().map((v) => {
|
|
390
|
+
const vs = cursor(v);
|
|
391
|
+
const limit = vs.word(['MAXVALUE', 'MINVALUE']) ? v[0].u : vs.peek() && vs.peek().t === 'lit' ? vs.next().v : readNumber(vs);
|
|
392
|
+
if (!vs.done()) vs.fail("',' or ')'");
|
|
393
|
+
return limit;
|
|
394
|
+
});
|
|
395
|
+
if (s.word('HASH')) { s.expectWord('SPACE'); part.hashSpace = readUnitSize(s); }
|
|
396
|
+
if (s.word('INCLUSIVE')) part.inclusive = true;
|
|
397
|
+
if (!s.done()) s.fail("',' or ')'");
|
|
398
|
+
p.partitions.push(part);
|
|
399
|
+
}
|
|
400
|
+
return p;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function readOrganization(c) {
|
|
404
|
+
c.expectWord('ORGANIZE');
|
|
405
|
+
c.expectWord('BY');
|
|
406
|
+
c.expectWord('HASH');
|
|
407
|
+
c.expectWord('UNIQUE');
|
|
408
|
+
const org = { by: 'HASH', columns: readNameList(c) };
|
|
409
|
+
if (c.word('HASH')) { c.expectWord('SPACE'); org.space = readUnitSize(c); }
|
|
410
|
+
return org;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Placement and attribute clauses, in any order, each at most once.
|
|
414
|
+
function readTableOptions(c, node) {
|
|
415
|
+
const set = (key, value) => {
|
|
416
|
+
if (key in node.options) c.fail(`no second ${key}`);
|
|
417
|
+
node.options[key] = value;
|
|
418
|
+
};
|
|
419
|
+
while (!c.done()) {
|
|
420
|
+
if (c.word('IN')) {
|
|
421
|
+
if (c.word('DATABASE')) set('in', { database: readName(c).join('.') });
|
|
422
|
+
else if (c.word('ACCELERATOR')) set('in', { accelerator: readName(c).join('.') });
|
|
423
|
+
else {
|
|
424
|
+
const parts = readName(c);
|
|
425
|
+
if (parts.length > 2) c.fail('database-name.table-space-name');
|
|
426
|
+
set('in', parts.length === 2 ? { database: parts[0], tableSpace: parts[1] } : { tableSpace: parts[0] });
|
|
427
|
+
}
|
|
428
|
+
} else if (c.isWord('PARTITION') && c.isWord('BY', 1)) set('partitioning', readPartitioning(c));
|
|
429
|
+
else if (c.isWord('ORGANIZE')) set('organization', readOrganization(c));
|
|
430
|
+
else if (c.word('EDITPROC')) {
|
|
431
|
+
const editproc = { program: readName(c).join('.') };
|
|
432
|
+
if (c.isWord(['WITH', 'WITHOUT']) && c.isWord('ROW', 1)) { editproc.rowAttributes = c.next().u === 'WITH'; c.next(); c.expectWord('ATTRIBUTES'); }
|
|
433
|
+
set('editproc', editproc);
|
|
434
|
+
} else if (c.word('VALIDPROC')) set('validproc', readName(c).join('.'));
|
|
435
|
+
else if (c.word('AUDIT')) set('audit', only(c, ['NONE', 'CHANGES', 'ALL']));
|
|
436
|
+
else if (c.word('OBID')) set('obid', readInteger(c));
|
|
437
|
+
else if (c.isWord('DATA') && c.isWord('CAPTURE', 1)) { c.next(); c.next(); set('dataCapture', only(c, ['NONE', 'CHANGES'])); }
|
|
438
|
+
else if (c.isWord('WITH') && c.isWord('RESTRICT', 1)) { c.next(); c.next(); c.expectWord('ON'); c.expectWord('DROP'); set('restrictOnDrop', true); }
|
|
439
|
+
else if (c.word('CCSID')) set('ccsid', only(c, ['ASCII', 'EBCDIC', 'UNICODE']));
|
|
440
|
+
else if (c.isWord('NOT') && c.isWord('VOLATILE', 1)) { c.next(); c.next(); c.word('CARDINALITY'); set('volatile', false); }
|
|
441
|
+
else if (c.word('VOLATILE')) { c.word('CARDINALITY'); set('volatile', true); }
|
|
442
|
+
else if (c.isWord('NOT') && c.isWord('LOGGED', 1)) { c.next(); c.next(); set('logged', false); }
|
|
443
|
+
else if (c.word('LOGGED')) set('logged', true);
|
|
444
|
+
else if (c.word('COMPRESS')) {
|
|
445
|
+
const yes = only(c, ['YES', 'NO']) === 'YES';
|
|
446
|
+
const how = yes && c.word(['FIXEDLENGTH', 'HUFFMAN']);
|
|
447
|
+
set('compress', yes ? (how ? how.u : 'YES') : 'NO');
|
|
448
|
+
} else if (c.word('APPEND')) set('append', only(c, ['YES', 'NO']) === 'YES');
|
|
449
|
+
else if (c.word('DSSIZE')) { const n = readInteger(c); c.expectWord('G'); set('dssize', `${n}G`); }
|
|
450
|
+
else if (c.word('BUFFERPOOL')) set('bufferpool', readName(c).join('.'));
|
|
451
|
+
else if (c.isWord('MEMBER') && c.isWord('CLUSTER', 1)) { c.next(); c.next(); set('memberCluster', true); }
|
|
452
|
+
else if (c.word('TRACKMOD')) set('trackmod', only(c, ['YES', 'NO']) === 'YES');
|
|
453
|
+
else if (c.word('PAGENUM')) set('pagenum', only(c, ['RELATIVE', 'ABSOLUTE']));
|
|
454
|
+
else if (c.isWord('NO') && c.isWord('KEY', 1)) { c.next(); c.next(); c.expectWord('LABEL'); set('keyLabel', null); }
|
|
455
|
+
else if (c.isWord('KEY') && c.isWord('LABEL', 1)) { c.next(); c.next(); set('keyLabel', readName(c).join('.')); }
|
|
456
|
+
else c.fail('a table clause');
|
|
457
|
+
}
|
|
458
|
+
// SQLCODE -628: a table partitioned BY SIZE EVERY n G takes its partition size from there, not DSSIZE.
|
|
459
|
+
if ('dssize' in node.options && node.options.partitioning?.every !== undefined) c.fail('DSSIZE or PARTITION BY SIZE EVERY, not both');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function readBaseTable(c, kind) {
|
|
463
|
+
const node = { kind, table: 'BASE', name: readName(c).join('.'), columns: [], constraints: [], periods: [], options: {} };
|
|
464
|
+
if (c.word('LIKE')) {
|
|
465
|
+
node.like = readName(c).join('.');
|
|
466
|
+
node.copy = {};
|
|
467
|
+
readCopyOptions(c, node);
|
|
468
|
+
} else {
|
|
469
|
+
let names = null;
|
|
470
|
+
if (c.isOp('(')) {
|
|
471
|
+
const at = c.pos;
|
|
472
|
+
const group = c.group();
|
|
473
|
+
if (c.isWord('AS')) names = cursor([{ t: 'op', v: '(' }, ...group, { t: 'op', v: ')' }]);
|
|
474
|
+
else { c.pos = at; readElements(c, node); }
|
|
475
|
+
}
|
|
476
|
+
if (c.word('AS')) {
|
|
477
|
+
node.asResult = { columns: names ? readNameList(names) : null, fullselect: c.group() };
|
|
478
|
+
const withNoData = c.isWord('WITH') && c.isWord('NO', 1) && c.isWord('DATA', 2);
|
|
479
|
+
if (withNoData || (c.isWord('DEFINITION') && c.isWord('ONLY', 1))) {
|
|
480
|
+
c.pos += withNoData ? 3 : 2;
|
|
481
|
+
node.asResult.withNoData = true;
|
|
482
|
+
node.copy = {};
|
|
483
|
+
readCopyOptions(c, node);
|
|
484
|
+
} else {
|
|
485
|
+
node.table = 'MATERIALIZED QUERY';
|
|
486
|
+
readRefreshOptions(c, node);
|
|
487
|
+
if (!node.asResult.dataInitiallyDeferred || !node.asResult.refreshDeferred) c.fail('WITH NO DATA or DATA INITIALLY DEFERRED REFRESH DEFERRED');
|
|
488
|
+
}
|
|
489
|
+
} else if (!node.columns.length && !node.constraints.length) c.fail("'(', LIKE or AS");
|
|
490
|
+
}
|
|
491
|
+
readTableOptions(c, node);
|
|
492
|
+
return node;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function readTemporaryTable(c, kind) {
|
|
496
|
+
c.expectWord('GLOBAL');
|
|
497
|
+
c.expectWord('TEMPORARY');
|
|
498
|
+
c.expectWord('TABLE');
|
|
499
|
+
const node = { kind, table: 'GLOBAL TEMPORARY', name: readName(c).join('.'), columns: [] };
|
|
500
|
+
if (c.word('LIKE')) node.like = readName(c).join('.');
|
|
501
|
+
else {
|
|
502
|
+
for (const item of c.items()) {
|
|
503
|
+
const s = cursor(item);
|
|
504
|
+
const col = { name: readName(s).join('.'), type: readDataType(s), notNull: false };
|
|
505
|
+
if (s.isWord('NOT') && s.isWord('NULL', 1)) { s.next(); s.next(); col.notNull = true; }
|
|
506
|
+
if (!s.done()) s.fail("NOT NULL, ',' or ')'");
|
|
507
|
+
node.columns.push(col);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (c.word('CCSID')) node.ccsid = only(c, ['ASCII', 'EBCDIC', 'UNICODE']);
|
|
511
|
+
return node;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function readAuxiliaryTable(c, kind) {
|
|
515
|
+
c.expectWord(['AUXILIARY', 'AUX']);
|
|
516
|
+
c.expectWord('TABLE');
|
|
517
|
+
const node = { kind, table: 'AUXILIARY', name: readName(c).join('.') };
|
|
518
|
+
c.expectWord('IN');
|
|
519
|
+
const space = readName(c);
|
|
520
|
+
if (space.length > 2) c.fail('database-name.table-space-name');
|
|
521
|
+
node.in = space.length === 2 ? { database: space[0], tableSpace: space[1] } : { tableSpace: space[0] };
|
|
522
|
+
c.expectWord('STORES');
|
|
523
|
+
node.stores = readName(c).join('.');
|
|
524
|
+
if (c.word('APPEND')) node.append = only(c, ['YES', 'NO']) === 'YES';
|
|
525
|
+
c.expectWord('COLUMN');
|
|
526
|
+
node.column = readName(c).join('.');
|
|
527
|
+
if (c.word('PART')) node.part = readInteger(c);
|
|
528
|
+
return node;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export const parsers = {
|
|
532
|
+
'CREATE TABLE': (c) => {
|
|
533
|
+
c.expectWord('CREATE');
|
|
534
|
+
if (c.isWord('GLOBAL')) return readTemporaryTable(c, 'CREATE TABLE');
|
|
535
|
+
if (c.isWord(['AUXILIARY', 'AUX'])) return readAuxiliaryTable(c, 'CREATE TABLE');
|
|
536
|
+
c.word('SUMMARY');
|
|
537
|
+
c.expectWord('TABLE');
|
|
538
|
+
return readBaseTable(c, 'CREATE TABLE');
|
|
539
|
+
},
|
|
540
|
+
};
|