json-fs-query 1.0.6 → 2.0.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/LICENSE +674 -674
- package/README.md +295 -98
- package/browser/jsonFsQuery.js +12733 -10777
- package/browser/jsonFsQuery.min.js +1 -1
- package/dist/data/introspect/create.d.ts +1 -1
- package/dist/data/introspect/create.js +35 -4
- package/dist/data/introspect/create.min.js +1 -1
- package/dist/data/introspect/definitions/field.d.ts +6 -2
- package/dist/data/introspect/definitions/field.js +7 -1
- package/dist/data/introspect/definitions/field.min.js +1 -1
- package/dist/data/introspect/index.d.ts +6 -3
- package/dist/data/introspect/index.js +10 -19
- package/dist/data/introspect/index.min.js +1 -1
- package/dist/data/introspect/policies/foreignKeyExists.js +5 -1
- package/dist/data/introspect/policies/foreignKeyExists.min.js +1 -1
- package/dist/data/introspect/policies/hasDependents.js +3 -1
- package/dist/data/introspect/policies/hasDependents.min.js +1 -1
- package/dist/data/introspect/policies/keyUnique.js +5 -1
- package/dist/data/introspect/policies/keyUnique.min.js +1 -1
- package/dist/data/introspect/policies/typeCheck.d.ts +9 -1
- package/dist/data/introspect/policies/typeCheck.js +16 -2
- package/dist/data/introspect/policies/typeCheck.min.js +1 -1
- package/dist/data/queries/createRecord.d.ts +11 -0
- package/dist/data/queries/createRecord.js +58 -0
- package/dist/data/queries/createRecord.min.js +1 -0
- package/dist/data/queries/deleteEntity.js +2 -1
- package/dist/data/queries/deleteEntity.min.js +1 -1
- package/dist/data/queries/describeRecord.d.ts +9 -0
- package/dist/data/queries/describeRecord.js +29 -0
- package/dist/data/queries/describeRecord.min.js +1 -0
- package/dist/data/queries/dropRecord.d.ts +9 -0
- package/dist/data/queries/dropRecord.js +61 -0
- package/dist/data/queries/dropRecord.min.js +1 -0
- package/dist/data/queries/insertEntity.js +11 -5
- package/dist/data/queries/insertEntity.min.js +1 -1
- package/dist/data/queries/parser.d.ts +1 -1
- package/dist/data/queries/parser.js +7 -15
- package/dist/data/queries/parser.min.js +1 -1
- package/dist/data/queries/query.d.ts +6 -0
- package/dist/data/queries/query.js +15 -6
- package/dist/data/queries/query.min.js +1 -1
- package/dist/data/queries/queryStatus.d.ts +15 -0
- package/dist/data/queries/queryStatus.js +52 -0
- package/dist/data/queries/queryStatus.min.js +1 -0
- package/dist/data/queries/quote.d.ts +11 -0
- package/dist/data/queries/quote.js +17 -0
- package/dist/data/queries/quote.min.js +1 -0
- package/dist/data/queries/showRecords.d.ts +6 -0
- package/dist/data/queries/showRecords.js +21 -0
- package/dist/data/queries/showRecords.min.js +1 -0
- package/dist/data/queries/sortBy.js +3 -0
- package/dist/data/queries/updateEntity.js +74 -18
- package/dist/data/queries/updateEntity.min.js +1 -1
- package/dist/data/queries/where.d.ts +2 -1
- package/dist/data/queries/where.js +18 -6
- package/dist/data/queries/where.min.js +1 -1
- package/dist/data/utilities/compareAs.d.ts +20 -0
- package/dist/data/utilities/compareAs.js +58 -0
- package/dist/data/utilities/compareAs.min.js +1 -0
- package/dist/data/utilities/config.d.ts +15 -0
- package/dist/data/utilities/config.js +31 -1
- package/dist/data/utilities/config.min.js +1 -1
- package/dist/data/utilities/databaseLock.d.ts +16 -0
- package/dist/data/utilities/databaseLock.js +63 -0
- package/dist/data/utilities/databaseLock.min.js +1 -0
- package/dist/data/utilities/fieldType.d.ts +16 -0
- package/dist/data/utilities/fieldType.js +23 -0
- package/dist/data/utilities/fieldType.min.js +1 -0
- package/dist/data/utilities/getConditionIndexedList.js +2 -1
- package/dist/data/utilities/getConditionIndexedList.min.js +1 -1
- package/dist/data/utilities/matchIndexedJoins.js +3 -1
- package/dist/data/utilities/matchIndexedJoins.min.js +1 -1
- package/dist/data/utilities/mergeJoin.js +5 -1
- package/dist/data/utilities/mergeJoin.min.js +1 -1
- package/dist/data/utilities/parsers/findConditions.d.ts +5 -1
- package/dist/data/utilities/parsers/findConditions.js +15 -6
- package/dist/data/utilities/parsers/findConditions.min.js +1 -1
- package/dist/data/utilities/parsers/findInsertValues.js +3 -2
- package/dist/data/utilities/parsers/findInsertValues.min.js +1 -1
- package/dist/data/utilities/parsers/findSelects.js +6 -3
- package/dist/data/utilities/parsers/findSelects.min.js +1 -1
- package/dist/data/utilities/parsers/findSortClauses.js +5 -2
- package/dist/data/utilities/parsers/findSortClauses.min.js +1 -1
- package/dist/data/utilities/parsers/findUpdateValues.js +3 -2
- package/dist/data/utilities/parsers/findUpdateValues.min.js +1 -1
- package/dist/data/utilities/parsers/maskStrings.d.ts +39 -0
- package/dist/data/utilities/parsers/maskStrings.js +123 -0
- package/dist/data/utilities/parsers/maskStrings.min.js +1 -0
- package/dist/data/utilities/parsers/parseCreate.d.ts +20 -0
- package/dist/data/utilities/parsers/parseCreate.js +330 -0
- package/dist/data/utilities/parsers/parseCreate.min.js +1 -0
- package/dist/data/utilities/parsers/parseLiteralValue.d.ts +2 -1
- package/dist/data/utilities/parsers/parseLiteralValue.js +6 -3
- package/dist/data/utilities/parsers/parseLiteralValue.min.js +1 -1
- package/dist/data/utilities/parsers/parseQuery.d.ts +3 -0
- package/dist/data/utilities/parsers/parseQuery.js +28 -2
- package/dist/data/utilities/parsers/parseQuery.min.js +1 -1
- package/dist/data/utilities/parsers/parseSchemaStatement.d.ts +13 -0
- package/dist/data/utilities/parsers/parseSchemaStatement.js +82 -0
- package/dist/data/utilities/parsers/parseSchemaStatement.min.js +1 -0
- package/dist/data/utilities/parsers/scanQuery.d.ts +52 -0
- package/dist/data/utilities/parsers/scanQuery.js +158 -0
- package/dist/data/utilities/parsers/scanQuery.min.js +1 -0
- package/dist/data/utilities/parsers/splitQueries.d.ts +6 -0
- package/dist/data/utilities/parsers/splitQueries.js +20 -2
- package/dist/data/utilities/parsers/splitQueries.min.js +1 -1
- package/dist/data/utilities/parsers/startsQuery.d.ts +5 -0
- package/dist/data/utilities/parsers/startsQuery.js +10 -0
- package/dist/data/utilities/parsers/startsQuery.min.js +1 -0
- package/dist/data/utilities/recordExists.d.ts +6 -0
- package/dist/data/utilities/recordExists.js +18 -0
- package/dist/data/utilities/recordExists.min.js +1 -0
- package/dist/data/utilities/removeFromIndex.d.ts +2 -1
- package/dist/data/utilities/removeFromIndex.js +4 -2
- package/dist/data/utilities/removeFromIndex.min.js +1 -1
- package/dist/data/utilities/runQuery.js +49 -0
- package/dist/data/utilities/runQuery.min.js +1 -1
- package/dist/data/utilities/updateIndex.d.ts +2 -1
- package/dist/data/utilities/updateIndex.js +4 -2
- package/dist/data/utilities/updateIndex.min.js +1 -1
- package/dist/data/utilities/useJoinClause.js +8 -2
- package/dist/data/utilities/useJoinClause.min.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -0
- package/dist/index.min.js +1 -1
- package/package.json +68 -55
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.unmaskStrings = exports.placeholderSource = exports.maskStrings = exports.maskArrays = exports.fromPlaceholder = exports.arraySource = void 0;
|
|
7
|
+
require("core-js/modules/es.json.stringify.js");
|
|
8
|
+
require("core-js/modules/esnext.iterator.constructor.js");
|
|
9
|
+
require("core-js/modules/esnext.iterator.map.js");
|
|
10
|
+
require("core-js/modules/esnext.iterator.some.js");
|
|
11
|
+
var _scanQuery = require("./scanQuery");
|
|
12
|
+
/**
|
|
13
|
+
* Regex source matching one placeholder from maskStrings, with the hex-encoded value as its only group. The
|
|
14
|
+
* find* parsers use it where they would otherwise match a quoted string.
|
|
15
|
+
*/
|
|
16
|
+
const placeholderSource = exports.placeholderSource = '\\u0000([0-9a-f]*)\\u0000';
|
|
17
|
+
const placeholderMatch = new RegExp(placeholderSource, 'g');
|
|
18
|
+
/**
|
|
19
|
+
* Regex source matching one placeholder from maskArrays, with the hex-encoded JSON of the array as its only group.
|
|
20
|
+
* The find* parsers use it where they would otherwise match `[...]`.
|
|
21
|
+
*/
|
|
22
|
+
const arraySource = exports.arraySource = '\\u0001([0-9a-f]*)\\u0001';
|
|
23
|
+
const arrayMatch = new RegExp(arraySource, 'g');
|
|
24
|
+
/**
|
|
25
|
+
* Encode a value as hex, four digits per UTF-16 code unit: only 0-9 and a-f, so no keyword, quote, space or
|
|
26
|
+
* bracket a parser looks for can appear in it.
|
|
27
|
+
* @param value
|
|
28
|
+
*/
|
|
29
|
+
const toHex = value => value.split('').map(char => char.charCodeAt(0).toString(16).padStart(4, '0')).join('');
|
|
30
|
+
/**
|
|
31
|
+
* Turn the hex of a placeholder back into the string's value.
|
|
32
|
+
* @param hex
|
|
33
|
+
*/
|
|
34
|
+
const fromPlaceholder = hex => (hex.match(/.{4}/g) || []).map(code => String.fromCharCode(parseInt(code, 16))).join('');
|
|
35
|
+
/**
|
|
36
|
+
* Replace every quoted string in a query with a placeholder holding its value (with the escapes applied), so that
|
|
37
|
+
* nothing inside a string (a keyword, a quote, `and`, a `]`) can be matched by the find* parsers.
|
|
38
|
+
* @param query
|
|
39
|
+
* @throws {Error} For an unclosed string, comment or bracket, or a NUL or SOH character outside a string (which
|
|
40
|
+
* could otherwise pass for a placeholder).
|
|
41
|
+
*/
|
|
42
|
+
exports.fromPlaceholder = fromPlaceholder;
|
|
43
|
+
const maskStrings = query => {
|
|
44
|
+
const {
|
|
45
|
+
strings
|
|
46
|
+
} = (0, _scanQuery.assertClosed)((0, _scanQuery.scanQuery)(query));
|
|
47
|
+
for (const [character, name] of [['\u0000', 'NUL'], ['\u0001', 'SOH']]) {
|
|
48
|
+
for (let index = query.indexOf(character); index !== -1; index = query.indexOf(character, index + 1)) {
|
|
49
|
+
if (!strings.some(({
|
|
50
|
+
start,
|
|
51
|
+
end
|
|
52
|
+
}) => start < index && index < end)) {
|
|
53
|
+
throw new Error(`Unexpected ${name} character at position ${index}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return strings.reduceRight((masked, {
|
|
58
|
+
start,
|
|
59
|
+
end,
|
|
60
|
+
value
|
|
61
|
+
}) => `${masked.slice(0, start)}\u0000${toHex(value)}\u0000${masked.slice(end)}`, query);
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Put every placeholder back the way it would be written in a query: strings quoted, arrays as JSON. For error
|
|
65
|
+
* messages, and for reading an array's text as JSON.
|
|
66
|
+
* @param text
|
|
67
|
+
* @param quote - wrap strings with JSON's double quotes (the default) or with single quotes
|
|
68
|
+
*/
|
|
69
|
+
exports.maskStrings = maskStrings;
|
|
70
|
+
const unmaskStrings = (text, quote = 'json') => text.replace(placeholderMatch, (placeholder, hex) => {
|
|
71
|
+
const value = fromPlaceholder(hex);
|
|
72
|
+
return quote === 'json' ? JSON.stringify(value) : `'${value.replace(/\\/g, '\\\\').replace(/'/g, '\\\'')}'`;
|
|
73
|
+
}).replace(arrayMatch, (placeholder, hex) => fromPlaceholder(hex));
|
|
74
|
+
/**
|
|
75
|
+
* Read one array's text (its strings already placeholders) as JSON, so it is checked once, here, with a clear error.
|
|
76
|
+
* @param text
|
|
77
|
+
*/
|
|
78
|
+
exports.unmaskStrings = unmaskStrings;
|
|
79
|
+
const arrayJson = text => {
|
|
80
|
+
try {
|
|
81
|
+
// A number too big for a double reads as Infinity, which JSON would write back as null: refuse it instead
|
|
82
|
+
return JSON.stringify(JSON.parse(unmaskStrings(text), (key, value) => {
|
|
83
|
+
if (typeof value === 'number' && !Number.isFinite(value)) {
|
|
84
|
+
throw new Error(`${value} is too large`);
|
|
85
|
+
}
|
|
86
|
+
return value;
|
|
87
|
+
}));
|
|
88
|
+
} catch (e) {
|
|
89
|
+
throw new Error(`Invalid array ${unmaskStrings(text, '\'')}: ${e.message}`);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Replace every array (outermost brackets only, so a nested array stays inside its parent) with a placeholder holding
|
|
94
|
+
* its JSON, so that the find* parsers never have to find where an array ends, and nothing inside one (a `.`, a `]`,
|
|
95
|
+
* a keyword) can be matched by them. Run it on the output of maskStrings, where no `[` or `]` is inside a string.
|
|
96
|
+
* @param masked
|
|
97
|
+
* @throws {Error} For an array which is not valid JSON once its strings are put back.
|
|
98
|
+
*/
|
|
99
|
+
const maskArrays = masked => {
|
|
100
|
+
let result = '';
|
|
101
|
+
let depth = 0;
|
|
102
|
+
let start = 0;
|
|
103
|
+
for (let index = 0; index < masked.length; index++) {
|
|
104
|
+
const char = masked[index];
|
|
105
|
+
if (char === '[') {
|
|
106
|
+
if (depth === 0) {
|
|
107
|
+
start = index;
|
|
108
|
+
}
|
|
109
|
+
depth++;
|
|
110
|
+
} else if (char === ']' && depth > 0) {
|
|
111
|
+
depth--;
|
|
112
|
+
if (depth === 0) {
|
|
113
|
+
result += `\u0001${toHex(arrayJson(masked.slice(start, index + 1)))}\u0001`;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (depth === 0) {
|
|
118
|
+
result += char;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return depth > 0 ? result + masked.slice(start) : result;
|
|
122
|
+
};
|
|
123
|
+
exports.maskArrays = maskArrays;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.unmaskStrings=exports.placeholderSource=exports.maskStrings=exports.maskArrays=exports.fromPlaceholder=exports.arraySource=void 0,require("core-js/modules/es.json.stringify.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.some.js");var _scanQuery=require("./scanQuery");const placeholderSource=exports.placeholderSource="\\u0000([0-9a-f]*)\\u0000",placeholderMatch=new RegExp(placeholderSource,"g"),arraySource=exports.arraySource="\\u0001([0-9a-f]*)\\u0001",arrayMatch=new RegExp(arraySource,"g"),toHex=r=>r.split("").map((r=>r.charCodeAt(0).toString(16).padStart(4,"0"))).join(""),fromPlaceholder=r=>(r.match(/.{4}/g)||[]).map((r=>String.fromCharCode(parseInt(r,16)))).join("");exports.fromPlaceholder=fromPlaceholder;const maskStrings=r=>{const{strings:e}=(0,_scanQuery.assertClosed)((0,_scanQuery.scanQuery)(r));for(const[s,o]of[["\0","NUL"],["","SOH"]])for(let t=r.indexOf(s);-1!==t;t=r.indexOf(s,t+1))if(!e.some((({start:r,end:e})=>r<t&&t<e)))throw new Error(`Unexpected ${o} character at position ${t}`);return e.reduceRight(((r,{start:e,end:s,value:o})=>`${r.slice(0,e)}\0${toHex(o)}\0${r.slice(s)}`),r)};exports.maskStrings=maskStrings;const unmaskStrings=(r,e="json")=>r.replace(placeholderMatch,((r,s)=>{const o=fromPlaceholder(s);return"json"===e?JSON.stringify(o):`'${o.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}'`})).replace(arrayMatch,((r,e)=>fromPlaceholder(e)));exports.unmaskStrings=unmaskStrings;const arrayJson=r=>{try{return JSON.stringify(JSON.parse(unmaskStrings(r),((r,e)=>{if("number"==typeof e&&!Number.isFinite(e))throw new Error(`${e} is too large`);return e})))}catch(e){throw new Error(`Invalid array ${unmaskStrings(r,"'")}: ${e.message}`)}},maskArrays=r=>{let e="",s=0,o=0;for(let t=0;t<r.length;t++){const a=r[t];if("["===a)0===s&&(o=t),s++;else if("]"===a&&s>0&&(s--,0===s)){e+=`${toHex(arrayJson(r.slice(o,t+1)))}`;continue}0===s&&(e+=a)}return s>0?e+r.slice(o):e};exports.maskArrays=maskArrays;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { fieldProperties } from '../../introspect/definitions/field';
|
|
2
|
+
import { keyProperties } from '../../introspect/definitions/key';
|
|
3
|
+
export type ParsedCreate = {
|
|
4
|
+
ifNotExists: boolean;
|
|
5
|
+
definition: fieldProperties[];
|
|
6
|
+
keys: keyProperties[];
|
|
7
|
+
};
|
|
8
|
+
export type ParsedCreateQuery = {
|
|
9
|
+
entity: string;
|
|
10
|
+
create: ParsedCreate;
|
|
11
|
+
};
|
|
12
|
+
export declare const createMatch: RegExp;
|
|
13
|
+
/**
|
|
14
|
+
* Parse a create statement (strings and arrays already masked):
|
|
15
|
+
* `create [if not exists] <name> (<field>, ..., <key>, ...)`. See the README's "Creating a record". Only checks
|
|
16
|
+
* what the text alone can tell; the referenced records of foreign keys are checked when it runs.
|
|
17
|
+
* @param query
|
|
18
|
+
*/
|
|
19
|
+
export declare const parseCreate: (query: string) => ParsedCreateQuery;
|
|
20
|
+
//# sourceMappingURL=parseCreate.d.ts.map
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.parseCreate = exports.createMatch = void 0;
|
|
7
|
+
require("core-js/modules/es.array.includes.js");
|
|
8
|
+
require("core-js/modules/esnext.iterator.constructor.js");
|
|
9
|
+
require("core-js/modules/esnext.iterator.filter.js");
|
|
10
|
+
require("core-js/modules/esnext.iterator.find.js");
|
|
11
|
+
require("core-js/modules/esnext.iterator.map.js");
|
|
12
|
+
require("core-js/modules/esnext.iterator.some.js");
|
|
13
|
+
require("core-js/modules/esnext.set.add-all.js");
|
|
14
|
+
require("core-js/modules/esnext.set.delete-all.js");
|
|
15
|
+
require("core-js/modules/esnext.set.difference.js");
|
|
16
|
+
require("core-js/modules/esnext.set.every.js");
|
|
17
|
+
require("core-js/modules/esnext.set.filter.js");
|
|
18
|
+
require("core-js/modules/esnext.set.find.js");
|
|
19
|
+
require("core-js/modules/esnext.set.intersection.js");
|
|
20
|
+
require("core-js/modules/esnext.set.is-disjoint-from.js");
|
|
21
|
+
require("core-js/modules/esnext.set.is-subset-of.js");
|
|
22
|
+
require("core-js/modules/esnext.set.is-superset-of.js");
|
|
23
|
+
require("core-js/modules/esnext.set.join.js");
|
|
24
|
+
require("core-js/modules/esnext.set.map.js");
|
|
25
|
+
require("core-js/modules/esnext.set.reduce.js");
|
|
26
|
+
require("core-js/modules/esnext.set.some.js");
|
|
27
|
+
require("core-js/modules/esnext.set.symmetric-difference.js");
|
|
28
|
+
require("core-js/modules/esnext.set.union.js");
|
|
29
|
+
var _typeCheck = require("../../introspect/policies/typeCheck");
|
|
30
|
+
var _maskStrings = require("./maskStrings");
|
|
31
|
+
var _startsQuery = require("./startsQuery");
|
|
32
|
+
const createMatch = exports.createMatch = /^\s*create(?![a-z0-9_-])/i;
|
|
33
|
+
const headMatch = /^\s*create\s+(if\s+not\s+exists\s+)?([a-z0-9_-]+)\s*\(/i;
|
|
34
|
+
const nameSource = '[a-z0-9_-]+';
|
|
35
|
+
const nameMatch = new RegExp(`^${nameSource}$`, 'i');
|
|
36
|
+
const fieldMatch = new RegExp(`^(${nameSource})\\s+(${nameSource})((?:\\s[\\s\\S]*)?)$`, 'i');
|
|
37
|
+
const primaryMatch = /^primary\s+key\s*\(([^()]*)\)$/i;
|
|
38
|
+
const uniqueMatch = /^unique\s*\(([^()]*)\)$/i;
|
|
39
|
+
const indexMatch = /^index\s*\(([^()]*)\)$/i;
|
|
40
|
+
const foreignMatch = new RegExp(`^foreign\\s+key\\s*\\(([^()]*)\\)\\s*references\\s+(${nameSource})\\s*\\(([^()]*)\\)$`, 'i');
|
|
41
|
+
const stringLiteral = new RegExp(`^${_maskStrings.placeholderSource}$`);
|
|
42
|
+
const arrayLiteral = new RegExp(`^${_maskStrings.arraySource}$`);
|
|
43
|
+
const lookupPrefix = {
|
|
44
|
+
primary: 'pk',
|
|
45
|
+
unique: 'unique',
|
|
46
|
+
index: 'index',
|
|
47
|
+
foreign: 'fk'
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Show a piece of the (masked) query the way it was written, for an error message.
|
|
51
|
+
* @param text
|
|
52
|
+
*/
|
|
53
|
+
const shown = text => (0, _maskStrings.unmaskStrings)(text.trim(), '\'');
|
|
54
|
+
/**
|
|
55
|
+
* The position of the `)` closing the `(` at `open`, or -1. Strings and arrays are placeholders by now, so round
|
|
56
|
+
* brackets are the only nesting left.
|
|
57
|
+
* @param text
|
|
58
|
+
* @param open
|
|
59
|
+
*/
|
|
60
|
+
const findClose = (text, open) => {
|
|
61
|
+
let depth = 0;
|
|
62
|
+
for (let i = open; i < text.length; i++) {
|
|
63
|
+
if (text[i] === '(') {
|
|
64
|
+
depth++;
|
|
65
|
+
} else if (text[i] === ')') {
|
|
66
|
+
depth--;
|
|
67
|
+
if (depth === 0) {
|
|
68
|
+
return i;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return -1;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Split the text inside the brackets on the commas that are not inside a key's own brackets.
|
|
76
|
+
* @param body
|
|
77
|
+
*/
|
|
78
|
+
const splitItems = body => {
|
|
79
|
+
const items = [];
|
|
80
|
+
let depth = 0;
|
|
81
|
+
let start = 0;
|
|
82
|
+
for (let i = 0; i < body.length; i++) {
|
|
83
|
+
if (body[i] === '(') {
|
|
84
|
+
depth++;
|
|
85
|
+
} else if (body[i] === ')') {
|
|
86
|
+
depth--;
|
|
87
|
+
} else if (body[i] === ',' && depth === 0) {
|
|
88
|
+
items.push(body.slice(start, i));
|
|
89
|
+
start = i + 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
items.push(body.slice(start));
|
|
93
|
+
return items.map(item => item.trim());
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* The names in a key's brackets: `(a, b)` gives ['a', 'b'].
|
|
97
|
+
* @param list
|
|
98
|
+
* @param item - The whole key, for the error message.
|
|
99
|
+
*/
|
|
100
|
+
const parseNames = (list, item) => {
|
|
101
|
+
const names = list.split(',').map(name => name.trim());
|
|
102
|
+
if (names.some(name => !nameMatch.test(name))) {
|
|
103
|
+
throw new Error(`Expected field names separated by commas in: ${shown(item)}`);
|
|
104
|
+
}
|
|
105
|
+
if (new Set(names).size !== names.length) {
|
|
106
|
+
throw new Error(`The same field is named twice in: ${shown(item)}`);
|
|
107
|
+
}
|
|
108
|
+
return names;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* A default's value: the same literals the rest of the query language reads.
|
|
112
|
+
* @param token
|
|
113
|
+
*/
|
|
114
|
+
const parseDefault = token => {
|
|
115
|
+
if (typeof token === 'undefined') {
|
|
116
|
+
throw new Error('Missing a value after default');
|
|
117
|
+
}
|
|
118
|
+
const string = token.match(stringLiteral);
|
|
119
|
+
if (string) {
|
|
120
|
+
return (0, _maskStrings.fromPlaceholder)(string[1]);
|
|
121
|
+
}
|
|
122
|
+
const array = token.match(arrayLiteral);
|
|
123
|
+
if (array) {
|
|
124
|
+
return JSON.parse((0, _maskStrings.fromPlaceholder)(array[1]));
|
|
125
|
+
}
|
|
126
|
+
if (/^[0-9]+$/.test(token)) {
|
|
127
|
+
return Number(token);
|
|
128
|
+
}
|
|
129
|
+
switch (token.toLowerCase()) {
|
|
130
|
+
case 'null':
|
|
131
|
+
return null;
|
|
132
|
+
case 'true':
|
|
133
|
+
return true;
|
|
134
|
+
case 'false':
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
throw new Error(`Unrecognized default value: ${shown(token)}`);
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* One field: `<name> <type> [optional] [null | not null] [auto] [default <value>]`, the modifiers in any order.
|
|
141
|
+
* @param found - The field's fieldMatch match.
|
|
142
|
+
*/
|
|
143
|
+
const parseField = found => {
|
|
144
|
+
const [item, name, typeName, rest] = found;
|
|
145
|
+
const type = typeName.toLowerCase();
|
|
146
|
+
if (!_typeCheck.fieldTypes.includes(type)) {
|
|
147
|
+
throw new Error(`Unknown type "${typeName}" for field "${name}" (expected ${_typeCheck.fieldTypes.join(', ')})`);
|
|
148
|
+
}
|
|
149
|
+
const field = {
|
|
150
|
+
name,
|
|
151
|
+
type,
|
|
152
|
+
optional: false,
|
|
153
|
+
autoGenerate: false
|
|
154
|
+
};
|
|
155
|
+
const seen = new Set();
|
|
156
|
+
const once = modifier => {
|
|
157
|
+
if (seen.has(modifier)) {
|
|
158
|
+
throw new Error(`"${modifier}" is given twice for field "${name}"`);
|
|
159
|
+
}
|
|
160
|
+
seen.add(modifier);
|
|
161
|
+
};
|
|
162
|
+
const tokens = rest.trim() === '' ? [] : rest.trim().split(/\s+/);
|
|
163
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
164
|
+
const token = tokens[i].toLowerCase();
|
|
165
|
+
if (token === 'optional') {
|
|
166
|
+
once('optional');
|
|
167
|
+
field.optional = true;
|
|
168
|
+
} else if (token === 'auto') {
|
|
169
|
+
once('auto');
|
|
170
|
+
field.autoGenerate = true;
|
|
171
|
+
} else if (token === 'null') {
|
|
172
|
+
once('null');
|
|
173
|
+
field.nullable = true;
|
|
174
|
+
} else if (token === 'not' && (tokens[i + 1] || '').toLowerCase() === 'null') {
|
|
175
|
+
once('null');
|
|
176
|
+
field.nullable = false;
|
|
177
|
+
i++;
|
|
178
|
+
} else if (token === 'default') {
|
|
179
|
+
once('default');
|
|
180
|
+
field.useDefault = true;
|
|
181
|
+
field.defaultValue = parseDefault(tokens[++i]);
|
|
182
|
+
} else {
|
|
183
|
+
throw new Error(`Unrecognized "${shown(tokens[i])}" for field "${name}" in: ${shown(item)}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (field.autoGenerate && field.useDefault) {
|
|
187
|
+
throw new Error(`Field "${name}" can't be both auto and have a default`);
|
|
188
|
+
}
|
|
189
|
+
if (field.autoGenerate && type !== 'string') {
|
|
190
|
+
throw new Error(`Field "${name}" is auto, which generates a UUID, so its type must be string`);
|
|
191
|
+
}
|
|
192
|
+
return field;
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* One key, or null when the item isn't written as a key (so it's a field).
|
|
196
|
+
* @param item
|
|
197
|
+
*/
|
|
198
|
+
const parseKey = item => {
|
|
199
|
+
let found = item.match(primaryMatch);
|
|
200
|
+
if (found) {
|
|
201
|
+
return {
|
|
202
|
+
type: 'primary',
|
|
203
|
+
fields: parseNames(found[1], item)
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
found = item.match(uniqueMatch);
|
|
207
|
+
if (found) {
|
|
208
|
+
return {
|
|
209
|
+
type: 'unique',
|
|
210
|
+
fields: parseNames(found[1], item)
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
found = item.match(indexMatch);
|
|
214
|
+
if (found) {
|
|
215
|
+
return {
|
|
216
|
+
type: 'index',
|
|
217
|
+
fields: parseNames(found[1], item)
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
found = item.match(foreignMatch);
|
|
221
|
+
if (found) {
|
|
222
|
+
const fields = parseNames(found[1], item);
|
|
223
|
+
const referenced = parseNames(found[3], item);
|
|
224
|
+
if (fields.length !== 1 || referenced.length !== 1) {
|
|
225
|
+
throw new Error(`Only single-field foreign keys are supported so far: ${shown(item)}`);
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
type: 'foreign',
|
|
229
|
+
fields,
|
|
230
|
+
references: [{
|
|
231
|
+
fields: [`${found[2]}.${referenced[0]}`],
|
|
232
|
+
lookup: `${found[2]}.json`
|
|
233
|
+
}]
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* Check the keys against the fields, then name each key's index file the way create() users do by hand:
|
|
240
|
+
* `pk_`, `unique_`, `index_` or `fk_`, then the fields joined by `_` (`primary key (a, b)` is `pk_a_b.json`).
|
|
241
|
+
* @param definition
|
|
242
|
+
* @param keys
|
|
243
|
+
*/
|
|
244
|
+
const finishKeys = (definition, keys) => {
|
|
245
|
+
const names = definition.map(field => field.name);
|
|
246
|
+
if (keys.filter(key => key.type === 'primary').length > 1) {
|
|
247
|
+
throw new Error('Only one primary key is allowed');
|
|
248
|
+
}
|
|
249
|
+
const lookups = new Set();
|
|
250
|
+
return keys.map(key => {
|
|
251
|
+
const missing = key.fields.find(field => !names.includes(field));
|
|
252
|
+
if (missing) {
|
|
253
|
+
throw new Error(`The ${key.type} key (${key.fields.join(', ')}) names "${missing}", which is not a field`);
|
|
254
|
+
}
|
|
255
|
+
const lookup = `${lookupPrefix[key.type]}_${key.fields.join('_')}.json`;
|
|
256
|
+
if (lookups.has(lookup)) {
|
|
257
|
+
throw new Error(`The same ${key.type} key on "${key.fields.join(', ')}" is given twice`);
|
|
258
|
+
}
|
|
259
|
+
lookups.add(lookup);
|
|
260
|
+
return Object.assign({
|
|
261
|
+
type: key.type,
|
|
262
|
+
fields: key.fields,
|
|
263
|
+
lookup
|
|
264
|
+
}, key.references ? {
|
|
265
|
+
references: key.references
|
|
266
|
+
} : {});
|
|
267
|
+
});
|
|
268
|
+
};
|
|
269
|
+
/**
|
|
270
|
+
* Parse a create statement (strings and arrays already masked):
|
|
271
|
+
* `create [if not exists] <name> (<field>, ..., <key>, ...)`. See the README's "Creating a record". Only checks
|
|
272
|
+
* what the text alone can tell; the referenced records of foreign keys are checked when it runs.
|
|
273
|
+
* @param query
|
|
274
|
+
*/
|
|
275
|
+
const parseCreate = query => {
|
|
276
|
+
const head = query.match(headMatch);
|
|
277
|
+
if (!head) {
|
|
278
|
+
throw new Error('Expected: create [if not exists] <name> (<fields and keys>)');
|
|
279
|
+
}
|
|
280
|
+
const entity = head[2];
|
|
281
|
+
if (entity === '__RECORDS') {
|
|
282
|
+
throw new Error('__RECORDS is reserved for the list of records');
|
|
283
|
+
}
|
|
284
|
+
const open = head[0].length - 1;
|
|
285
|
+
const close = findClose(query, open);
|
|
286
|
+
if (close === -1) {
|
|
287
|
+
// Not reached through parser(): splitQueries' scanner already rejects an unclosed bracket
|
|
288
|
+
throw new Error('Missing ) at the end of the field list');
|
|
289
|
+
}
|
|
290
|
+
const after = query.slice(close + 1).trim();
|
|
291
|
+
if (after) {
|
|
292
|
+
if (_startsQuery.startsQueryMatch.test(after)) {
|
|
293
|
+
throw new Error(`Missing ; before: ${shown(after)}`);
|
|
294
|
+
}
|
|
295
|
+
throw new Error(`Unrecognized query value: ${shown(after)}`);
|
|
296
|
+
}
|
|
297
|
+
const body = query.slice(open + 1, close);
|
|
298
|
+
const definition = [];
|
|
299
|
+
const keys = [];
|
|
300
|
+
if (body.trim()) {
|
|
301
|
+
for (const item of splitItems(body)) {
|
|
302
|
+
if (!item) {
|
|
303
|
+
throw new Error('Empty item in the field list (an extra comma?)');
|
|
304
|
+
}
|
|
305
|
+
const key = parseKey(item);
|
|
306
|
+
if (key) {
|
|
307
|
+
keys.push(key);
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
const found = item.match(fieldMatch);
|
|
311
|
+
if (!found) {
|
|
312
|
+
throw new Error(`Expected a field (<name> <type> ...) or a key in: ${shown(item)}`);
|
|
313
|
+
}
|
|
314
|
+
const field = parseField(found);
|
|
315
|
+
if (definition.some(existing => existing.name === field.name)) {
|
|
316
|
+
throw new Error(`Field "${field.name}" is given twice`);
|
|
317
|
+
}
|
|
318
|
+
definition.push(field);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
entity,
|
|
323
|
+
create: {
|
|
324
|
+
ifNotExists: Boolean(head[1]),
|
|
325
|
+
definition,
|
|
326
|
+
keys: finishKeys(definition, keys)
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
};
|
|
330
|
+
exports.parseCreate = parseCreate;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseCreate=exports.createMatch=void 0,require("core-js/modules/es.array.includes.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.filter.js"),require("core-js/modules/esnext.iterator.find.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.some.js"),require("core-js/modules/esnext.set.add-all.js"),require("core-js/modules/esnext.set.delete-all.js"),require("core-js/modules/esnext.set.difference.js"),require("core-js/modules/esnext.set.every.js"),require("core-js/modules/esnext.set.filter.js"),require("core-js/modules/esnext.set.find.js"),require("core-js/modules/esnext.set.intersection.js"),require("core-js/modules/esnext.set.is-disjoint-from.js"),require("core-js/modules/esnext.set.is-subset-of.js"),require("core-js/modules/esnext.set.is-superset-of.js"),require("core-js/modules/esnext.set.join.js"),require("core-js/modules/esnext.set.map.js"),require("core-js/modules/esnext.set.reduce.js"),require("core-js/modules/esnext.set.some.js"),require("core-js/modules/esnext.set.symmetric-difference.js"),require("core-js/modules/esnext.set.union.js");var _typeCheck=require("../../introspect/policies/typeCheck"),_maskStrings=require("./maskStrings"),_startsQuery=require("./startsQuery");const createMatch=exports.createMatch=/^\s*create(?![a-z0-9_-])/i,headMatch=/^\s*create\s+(if\s+not\s+exists\s+)?([a-z0-9_-]+)\s*\(/i,nameSource="[a-z0-9_-]+",nameMatch=new RegExp(`^${nameSource}$`,"i"),fieldMatch=new RegExp(`^(${nameSource})\\s+(${nameSource})((?:\\s[\\s\\S]*)?)$`,"i"),primaryMatch=/^primary\s+key\s*\(([^()]*)\)$/i,uniqueMatch=/^unique\s*\(([^()]*)\)$/i,indexMatch=/^index\s*\(([^()]*)\)$/i,foreignMatch=new RegExp(`^foreign\\s+key\\s*\\(([^()]*)\\)\\s*references\\s+(${nameSource})\\s*\\(([^()]*)\\)$`,"i"),stringLiteral=new RegExp(`^${_maskStrings.placeholderSource}$`),arrayLiteral=new RegExp(`^${_maskStrings.arraySource}$`),lookupPrefix={primary:"pk",unique:"unique",index:"index",foreign:"fk"},shown=e=>(0,_maskStrings.unmaskStrings)(e.trim(),"'"),findClose=(e,r)=>{let s=0;for(let t=r;t<e.length;t++)if("("===e[t])s++;else if(")"===e[t]&&(s--,0===s))return t;return-1},splitItems=e=>{const r=[];let s=0,t=0;for(let i=0;i<e.length;i++)"("===e[i]?s++:")"===e[i]?s--:","===e[i]&&0===s&&(r.push(e.slice(t,i)),t=i+1);return r.push(e.slice(t)),r.map((e=>e.trim()))},parseNames=(e,r)=>{const s=e.split(",").map((e=>e.trim()));if(s.some((e=>!nameMatch.test(e))))throw new Error(`Expected field names separated by commas in: ${shown(r)}`);if(new Set(s).size!==s.length)throw new Error(`The same field is named twice in: ${shown(r)}`);return s},parseDefault=e=>{if(void 0===e)throw new Error("Missing a value after default");const r=e.match(stringLiteral);if(r)return(0,_maskStrings.fromPlaceholder)(r[1]);const s=e.match(arrayLiteral);if(s)return JSON.parse((0,_maskStrings.fromPlaceholder)(s[1]));if(/^[0-9]+$/.test(e))return Number(e);switch(e.toLowerCase()){case"null":return null;case"true":return!0;case"false":return!1}throw new Error(`Unrecognized default value: ${shown(e)}`)},parseField=e=>{const[r,s,t,i]=e,n=t.toLowerCase();if(!_typeCheck.fieldTypes.includes(n))throw new Error(`Unknown type "${t}" for field "${s}" (expected ${_typeCheck.fieldTypes.join(", ")})`);const o={name:s,type:n,optional:!1,autoGenerate:!1},a=new Set,l=e=>{if(a.has(e))throw new Error(`"${e}" is given twice for field "${s}"`);a.add(e)},u=""===i.trim()?[]:i.trim().split(/\s+/);for(let e=0;e<u.length;e++){const t=u[e].toLowerCase();if("optional"===t)l("optional"),o.optional=!0;else if("auto"===t)l("auto"),o.autoGenerate=!0;else if("null"===t)l("null"),o.nullable=!0;else if("not"===t&&"null"===(u[e+1]||"").toLowerCase())l("null"),o.nullable=!1,e++;else{if("default"!==t)throw new Error(`Unrecognized "${shown(u[e])}" for field "${s}" in: ${shown(r)}`);l("default"),o.useDefault=!0,o.defaultValue=parseDefault(u[++e])}}if(o.autoGenerate&&o.useDefault)throw new Error(`Field "${s}" can't be both auto and have a default`);if(o.autoGenerate&&"string"!==n)throw new Error(`Field "${s}" is auto, which generates a UUID, so its type must be string`);return o},parseKey=e=>{let r=e.match(primaryMatch);if(r)return{type:"primary",fields:parseNames(r[1],e)};if(r=e.match(uniqueMatch),r)return{type:"unique",fields:parseNames(r[1],e)};if(r=e.match(indexMatch),r)return{type:"index",fields:parseNames(r[1],e)};if(r=e.match(foreignMatch),r){const s=parseNames(r[1],e),t=parseNames(r[3],e);if(1!==s.length||1!==t.length)throw new Error(`Only single-field foreign keys are supported so far: ${shown(e)}`);return{type:"foreign",fields:s,references:[{fields:[`${r[2]}.${t[0]}`],lookup:`${r[2]}.json`}]}}return null},finishKeys=(e,r)=>{const s=e.map((e=>e.name));if(r.filter((e=>"primary"===e.type)).length>1)throw new Error("Only one primary key is allowed");const t=new Set;return r.map((e=>{const r=e.fields.find((e=>!s.includes(e)));if(r)throw new Error(`The ${e.type} key (${e.fields.join(", ")}) names "${r}", which is not a field`);const i=`${lookupPrefix[e.type]}_${e.fields.join("_")}.json`;if(t.has(i))throw new Error(`The same ${e.type} key on "${e.fields.join(", ")}" is given twice`);return t.add(i),Object.assign({type:e.type,fields:e.fields,lookup:i},e.references?{references:e.references}:{})}))},parseCreate=e=>{const r=e.match(headMatch);if(!r)throw new Error("Expected: create [if not exists] <name> (<fields and keys>)");const s=r[2];if("__RECORDS"===s)throw new Error("__RECORDS is reserved for the list of records");const t=r[0].length-1,i=findClose(e,t);if(-1===i)throw new Error("Missing ) at the end of the field list");const n=e.slice(i+1).trim();if(n){if(_startsQuery.startsQueryMatch.test(n))throw new Error(`Missing ; before: ${shown(n)}`);throw new Error(`Unrecognized query value: ${shown(n)}`)}const o=e.slice(t+1,i),a=[],l=[];if(o.trim())for(const e of splitItems(o)){if(!e)throw new Error("Empty item in the field list (an extra comma?)");const r=parseKey(e);if(r){l.push(r);continue}const s=e.match(fieldMatch);if(!s)throw new Error(`Expected a field (<name> <type> ...) or a key in: ${shown(e)}`);const t=parseField(s);if(a.some((e=>e.name===t.name)))throw new Error(`Field "${t.name}" is given twice`);a.push(t)}return{entity:s,create:{ifNotExists:Boolean(r[1]),definition:a,keys:finishKeys(a,l)}}};exports.parseCreate=parseCreate;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export type LiteralValue = string | number | boolean | null | any[];
|
|
2
2
|
/**
|
|
3
3
|
* Several parsers (findConditions, findInsertValues, findUpdateValues) capture a query-string
|
|
4
|
-
* literal into one of six consecutive alternation groups depending on its shape - quoted string
|
|
4
|
+
* literal into one of six consecutive alternation groups depending on its shape - quoted string
|
|
5
|
+
* (by then a placeholder from maskStrings, holding the string's value as hex),
|
|
5
6
|
* number, array, null, true, false - since a single regex can't otherwise tell them apart. Given
|
|
6
7
|
* the match and the index of the first of those six groups (which shifts depending on how many
|
|
7
8
|
* groups come before it in the parent regex), return the literal with its real JS type.
|
|
@@ -4,9 +4,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.parseLiteralValue = void 0;
|
|
7
|
+
var _maskStrings = require("./maskStrings");
|
|
7
8
|
/**
|
|
8
9
|
* Several parsers (findConditions, findInsertValues, findUpdateValues) capture a query-string
|
|
9
|
-
* literal into one of six consecutive alternation groups depending on its shape - quoted string
|
|
10
|
+
* literal into one of six consecutive alternation groups depending on its shape - quoted string
|
|
11
|
+
* (by then a placeholder from maskStrings, holding the string's value as hex),
|
|
10
12
|
* number, array, null, true, false - since a single regex can't otherwise tell them apart. Given
|
|
11
13
|
* the match and the index of the first of those six groups (which shifts depending on how many
|
|
12
14
|
* groups come before it in the parent regex), return the literal with its real JS type.
|
|
@@ -15,13 +17,14 @@ exports.parseLiteralValue = void 0;
|
|
|
15
17
|
*/
|
|
16
18
|
const parseLiteralValue = (match, baseIndex) => {
|
|
17
19
|
if (typeof match[baseIndex] !== 'undefined') {
|
|
18
|
-
return match[baseIndex];
|
|
20
|
+
return (0, _maskStrings.fromPlaceholder)(match[baseIndex]);
|
|
19
21
|
}
|
|
20
22
|
if (typeof match[baseIndex + 1] !== 'undefined') {
|
|
21
23
|
return Number(match[baseIndex + 1]);
|
|
22
24
|
}
|
|
23
25
|
if (typeof match[baseIndex + 2] !== 'undefined') {
|
|
24
|
-
|
|
26
|
+
// An array is a placeholder from maskArrays, holding its JSON
|
|
27
|
+
return JSON.parse((0, _maskStrings.fromPlaceholder)(match[baseIndex + 2]));
|
|
25
28
|
}
|
|
26
29
|
if (typeof match[baseIndex + 3] !== 'undefined') {
|
|
27
30
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseLiteralValue=void 0;const parseLiteralValue=(e,r)=>void 0!==e[r]?e[r]:void 0!==e[r+1]?Number(e[r+1]):void 0!==e[r+2]?JSON.parse(e[r+2]):void 0!==e[r+3]?null:void 0!==e[r+4];exports.parseLiteralValue=parseLiteralValue;
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseLiteralValue=void 0;var _maskStrings=require("./maskStrings");const parseLiteralValue=(e,r)=>void 0!==e[r]?(0,_maskStrings.fromPlaceholder)(e[r]):void 0!==e[r+1]?Number(e[r+1]):void 0!==e[r+2]?JSON.parse((0,_maskStrings.fromPlaceholder)(e[r+2])):void 0!==e[r+3]?null:void 0!==e[r+4];exports.parseLiteralValue=parseLiteralValue;
|
|
@@ -10,6 +10,7 @@ import { ParsedSortClause } from './findSortClauses';
|
|
|
10
10
|
import { ParsedGroupBy } from './findGroupBy';
|
|
11
11
|
import { ParsedLimit } from './findLimit';
|
|
12
12
|
import { ParsedOffset } from './findOffset';
|
|
13
|
+
import { ParsedCreate } from './parseCreate';
|
|
13
14
|
export type ParsedQuery = {
|
|
14
15
|
command: ParsedCommand | undefined;
|
|
15
16
|
entity: ParsedEntity | undefined;
|
|
@@ -24,6 +25,8 @@ export type ParsedQuery = {
|
|
|
24
25
|
groupBy: ParsedGroupBy | undefined;
|
|
25
26
|
limit: ParsedLimit | undefined;
|
|
26
27
|
offset: ParsedOffset | undefined;
|
|
28
|
+
create: ParsedCreate | undefined;
|
|
29
|
+
ifExists: boolean | undefined;
|
|
27
30
|
};
|
|
28
31
|
/**
|
|
29
32
|
* Break a query down into its components.
|
|
@@ -17,6 +17,10 @@ var _findSortClauses = require("./findSortClauses");
|
|
|
17
17
|
var _findGroupBy = require("./findGroupBy");
|
|
18
18
|
var _findLimit = require("./findLimit");
|
|
19
19
|
var _findOffset = require("./findOffset");
|
|
20
|
+
var _maskStrings = require("./maskStrings");
|
|
21
|
+
var _parseCreate = require("./parseCreate");
|
|
22
|
+
var _parseSchemaStatement = require("./parseSchemaStatement");
|
|
23
|
+
var _startsQuery = require("./startsQuery");
|
|
20
24
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
25
|
/**
|
|
22
26
|
* Break a query down into its components.
|
|
@@ -36,15 +40,37 @@ const parseQuery = query => {
|
|
|
36
40
|
sortClauses: [],
|
|
37
41
|
groupBy: undefined,
|
|
38
42
|
limit: undefined,
|
|
39
|
-
offset: undefined
|
|
43
|
+
offset: undefined,
|
|
44
|
+
create: undefined,
|
|
45
|
+
ifExists: undefined
|
|
40
46
|
};
|
|
41
47
|
const {
|
|
42
48
|
curry,
|
|
43
49
|
pipe
|
|
44
50
|
} = _siFunciona.default;
|
|
51
|
+
// No find* step may see inside a string or an array: each one becomes a placeholder holding its value
|
|
52
|
+
query = (0, _maskStrings.maskArrays)((0, _maskStrings.maskStrings)(query));
|
|
53
|
+
// A create statement has its own shape (a bracketed list of fields and keys), so it gets its own parser
|
|
54
|
+
if (_parseCreate.createMatch.test(query)) {
|
|
55
|
+
const {
|
|
56
|
+
entity,
|
|
57
|
+
create
|
|
58
|
+
} = (0, _parseCreate.parseCreate)(query);
|
|
59
|
+
return Object.assign(parsedQuery, {
|
|
60
|
+
command: 'create',
|
|
61
|
+
entity,
|
|
62
|
+
create
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (_parseSchemaStatement.schemaStatementMatch.test(query)) {
|
|
66
|
+
return Object.assign(parsedQuery, (0, _parseSchemaStatement.parseSchemaStatement)(query));
|
|
67
|
+
}
|
|
45
68
|
query = pipe(curry(_findCommand.findCommand)(parsedQuery), curry(_findInsertValues.findInsertValues)(parsedQuery), curry(_findUpdateValues.findUpdateValues)(parsedQuery), curry(_findJoinEntity.findJoinEntity)(parsedQuery), curry(_findSelects.findSelects)(parsedQuery), curry(_findConditions.findConditions)(parsedQuery), curry(_findJoinClauses.findJoinClauses)(parsedQuery), curry(_findMergeJoins.findMergeJoins)(parsedQuery), curry(_findSortClauses.findSortClauses)(parsedQuery), curry(_findGroupBy.findGroupBy)(parsedQuery), curry(_findLimit.findLimit)(parsedQuery), curry(_findOffset.findOffset)(parsedQuery))(query);
|
|
46
69
|
if (query) {
|
|
47
|
-
|
|
70
|
+
if (_startsQuery.startsQueryMatch.test(query)) {
|
|
71
|
+
throw new Error(`Missing ; before: ${(0, _maskStrings.unmaskStrings)(query.trim(), '\'')}`);
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`Unrecognized query value: ${(0, _maskStrings.unmaskStrings)(query, '\'')}`);
|
|
48
74
|
}
|
|
49
75
|
return parsedQuery;
|
|
50
76
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseQuery=void 0;var _siFunciona=_interopRequireDefault(require("si-funciona")),_findCommand=require("./findCommand"),_findInsertValues=require("./findInsertValues"),_findUpdateValues=require("./findUpdateValues"),_findJoinEntity=require("./findJoinEntity"),_findSelects=require("./findSelects"),_findConditions=require("./findConditions"),_findJoinClauses=require("./findJoinClauses"),_findMergeJoins=require("./findMergeJoins"),_findSortClauses=require("./findSortClauses"),_findGroupBy=require("./findGroupBy"),_findLimit=require("./findLimit"),_findOffset=require("./findOffset");function _interopRequireDefault(
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseQuery=void 0;var _siFunciona=_interopRequireDefault(require("si-funciona")),_findCommand=require("./findCommand"),_findInsertValues=require("./findInsertValues"),_findUpdateValues=require("./findUpdateValues"),_findJoinEntity=require("./findJoinEntity"),_findSelects=require("./findSelects"),_findConditions=require("./findConditions"),_findJoinClauses=require("./findJoinClauses"),_findMergeJoins=require("./findMergeJoins"),_findSortClauses=require("./findSortClauses"),_findGroupBy=require("./findGroupBy"),_findLimit=require("./findLimit"),_findOffset=require("./findOffset"),_maskStrings=require("./maskStrings"),_parseCreate=require("./parseCreate"),_parseSchemaStatement=require("./parseSchemaStatement"),_startsQuery=require("./startsQuery");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const parseQuery=e=>{const i={command:void 0,entity:void 0,joinEntity:void 0,selectClauses:[],insertValues:[],updateValues:{},conditions:[],joinClauses:[],mergeJoins:[],sortClauses:[],groupBy:void 0,limit:void 0,offset:void 0,create:void 0,ifExists:void 0},{curry:n,pipe:r}=_siFunciona.default;if(e=(0,_maskStrings.maskArrays)((0,_maskStrings.maskStrings)(e)),_parseCreate.createMatch.test(e)){const{entity:n,create:r}=(0,_parseCreate.parseCreate)(e);return Object.assign(i,{command:"create",entity:n,create:r})}if(_parseSchemaStatement.schemaStatementMatch.test(e))return Object.assign(i,(0,_parseSchemaStatement.parseSchemaStatement)(e));if(e=r(n(_findCommand.findCommand)(i),n(_findInsertValues.findInsertValues)(i),n(_findUpdateValues.findUpdateValues)(i),n(_findJoinEntity.findJoinEntity)(i),n(_findSelects.findSelects)(i),n(_findConditions.findConditions)(i),n(_findJoinClauses.findJoinClauses)(i),n(_findMergeJoins.findMergeJoins)(i),n(_findSortClauses.findSortClauses)(i),n(_findGroupBy.findGroupBy)(i),n(_findLimit.findLimit)(i),n(_findOffset.findOffset)(i))(e)){if(_startsQuery.startsQueryMatch.test(e))throw new Error(`Missing ; before: ${(0,_maskStrings.unmaskStrings)(e.trim(),"'")}`);throw new Error(`Unrecognized query value: ${(0,_maskStrings.unmaskStrings)(e,"'")}`)}return i};exports.parseQuery=parseQuery;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type ParsedSchemaStatement = {
|
|
2
|
+
command: 'drop' | 'describe' | 'show';
|
|
3
|
+
entity: string;
|
|
4
|
+
ifExists: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare const schemaStatementMatch: RegExp;
|
|
7
|
+
/**
|
|
8
|
+
* Parse `drop [if exists] <name>`, `describe <name>` or `show records` (strings and arrays already masked). `show`
|
|
9
|
+
* resolves to the registry, so its entity is `__RECORDS`.
|
|
10
|
+
* @param query
|
|
11
|
+
*/
|
|
12
|
+
export declare const parseSchemaStatement: (query: string) => ParsedSchemaStatement;
|
|
13
|
+
//# sourceMappingURL=parseSchemaStatement.d.ts.map
|