rawsql-ts 0.1.0-beta.4 → 0.1.0-beta.6
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 +143 -199
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/models/BinarySelectQuery.js +140 -0
- package/dist/models/BinarySelectQuery.js.map +1 -0
- package/dist/models/Clause.js +318 -0
- package/dist/models/Clause.js.map +1 -0
- package/dist/models/KeywordTrie.js +52 -0
- package/dist/models/KeywordTrie.js.map +1 -0
- package/dist/models/Lexeme.js +21 -0
- package/dist/models/Lexeme.js.map +1 -0
- package/dist/models/SelectQuery.js +10 -0
- package/dist/models/SelectQuery.js.map +1 -0
- package/dist/models/SimpleSelectQuery.js +290 -0
- package/dist/models/SimpleSelectQuery.js.map +1 -0
- package/dist/models/SqlComponent.js +27 -0
- package/dist/models/SqlComponent.js.map +1 -0
- package/dist/models/ValueComponent.js +250 -0
- package/dist/models/ValueComponent.js.map +1 -0
- package/dist/models/ValuesQuery.js +16 -0
- package/dist/models/ValuesQuery.js.map +1 -0
- package/dist/parsers/CommandExpressionParser.js +124 -0
- package/dist/parsers/CommandExpressionParser.js.map +1 -0
- package/dist/parsers/CommonTableParser.js +60 -0
- package/dist/parsers/CommonTableParser.js.map +1 -0
- package/dist/parsers/ForClauseParser.js +56 -0
- package/dist/parsers/ForClauseParser.js.map +1 -0
- package/dist/parsers/FromClauseParser.js +45 -0
- package/dist/parsers/FromClauseParser.js.map +1 -0
- package/dist/parsers/FunctionExpressionParser.js +178 -0
- package/dist/parsers/FunctionExpressionParser.js.map +1 -0
- package/dist/parsers/GroupByParser.js +56 -0
- package/dist/parsers/GroupByParser.js.map +1 -0
- package/dist/parsers/HavingParser.js +34 -0
- package/dist/parsers/HavingParser.js.map +1 -0
- package/dist/parsers/IdentifierParser.js +39 -0
- package/dist/parsers/IdentifierParser.js.map +1 -0
- package/dist/parsers/JoinClauseParser.js +105 -0
- package/dist/parsers/JoinClauseParser.js.map +1 -0
- package/dist/parsers/KeywordParser.js +91 -0
- package/dist/parsers/KeywordParser.js.map +1 -0
- package/dist/parsers/LimitClauseParser.js +48 -0
- package/dist/parsers/LimitClauseParser.js.map +1 -0
- package/dist/parsers/LiteralParser.js +38 -0
- package/dist/parsers/LiteralParser.js.map +1 -0
- package/dist/parsers/OrderByClauseParser.js +75 -0
- package/dist/parsers/OrderByClauseParser.js.map +1 -0
- package/dist/parsers/OverExpressionParser.js +44 -0
- package/dist/parsers/OverExpressionParser.js.map +1 -0
- package/dist/parsers/ParameterExpressionParser.js +15 -0
- package/dist/parsers/ParameterExpressionParser.js.map +1 -0
- package/dist/parsers/ParenExpressionParser.js +33 -0
- package/dist/parsers/ParenExpressionParser.js.map +1 -0
- package/dist/parsers/PartitionByParser.js +51 -0
- package/dist/parsers/PartitionByParser.js.map +1 -0
- package/dist/parsers/SelectClauseParser.js +82 -0
- package/dist/parsers/SelectClauseParser.js.map +1 -0
- package/dist/parsers/SelectQueryParser.js +151 -0
- package/dist/parsers/SelectQueryParser.js.map +1 -0
- package/dist/parsers/SourceAliasExpressionParser.js +48 -0
- package/dist/parsers/SourceAliasExpressionParser.js.map +1 -0
- package/dist/parsers/SourceExpressionParser.js +34 -0
- package/dist/parsers/SourceExpressionParser.js.map +1 -0
- package/dist/parsers/SourceParser.js +116 -0
- package/dist/parsers/SourceParser.js.map +1 -0
- package/dist/parsers/SqlTokenizer.js +174 -0
- package/dist/parsers/SqlTokenizer.js.map +1 -0
- package/dist/parsers/StringSpecifierExpressionParser.js +22 -0
- package/dist/parsers/StringSpecifierExpressionParser.js.map +1 -0
- package/dist/parsers/UnaryExpressionParser.js +30 -0
- package/dist/parsers/UnaryExpressionParser.js.map +1 -0
- package/dist/parsers/ValueParser.js +134 -0
- package/dist/parsers/ValueParser.js.map +1 -0
- package/dist/parsers/ValuesQueryParser.js +86 -0
- package/dist/parsers/ValuesQueryParser.js.map +1 -0
- package/dist/parsers/WhereClauseParser.js +34 -0
- package/dist/parsers/WhereClauseParser.js.map +1 -0
- package/dist/parsers/WindowClauseParser.js +43 -0
- package/dist/parsers/WindowClauseParser.js.map +1 -0
- package/dist/parsers/WindowExpressionParser.js +151 -0
- package/dist/parsers/WindowExpressionParser.js.map +1 -0
- package/dist/parsers/WithClauseParser.js +55 -0
- package/dist/parsers/WithClauseParser.js.map +1 -0
- package/dist/tokenReaders/BaseTokenReader.js +82 -0
- package/dist/tokenReaders/BaseTokenReader.js.map +1 -0
- package/dist/tokenReaders/CommandTokenReader.js +145 -0
- package/dist/tokenReaders/CommandTokenReader.js.map +1 -0
- package/dist/tokenReaders/FunctionTokenReader.js +45 -0
- package/dist/tokenReaders/FunctionTokenReader.js.map +1 -0
- package/dist/tokenReaders/IdentifierTokenReader.js +70 -0
- package/dist/tokenReaders/IdentifierTokenReader.js.map +1 -0
- package/dist/tokenReaders/LiteralTokenReader.js +189 -0
- package/dist/tokenReaders/LiteralTokenReader.js.map +1 -0
- package/dist/tokenReaders/OperatorTokenReader.js +98 -0
- package/dist/tokenReaders/OperatorTokenReader.js.map +1 -0
- package/dist/tokenReaders/ParameterTokenReader.js +44 -0
- package/dist/tokenReaders/ParameterTokenReader.js.map +1 -0
- package/dist/tokenReaders/StringSpecifierTokenReader.js +31 -0
- package/dist/tokenReaders/StringSpecifierTokenReader.js.map +1 -0
- package/dist/tokenReaders/SymbolTokenReader.js +35 -0
- package/dist/tokenReaders/SymbolTokenReader.js.map +1 -0
- package/dist/tokenReaders/TokenReaderManager.js +110 -0
- package/dist/tokenReaders/TokenReaderManager.js.map +1 -0
- package/dist/tokenReaders/TypeTokenReader.js +59 -0
- package/dist/tokenReaders/TypeTokenReader.js.map +1 -0
- package/dist/transformers/CTEBuilder.js +188 -0
- package/dist/transformers/CTEBuilder.js.map +1 -0
- package/dist/transformers/CTECollector.js +384 -0
- package/dist/transformers/CTECollector.js.map +1 -0
- package/dist/transformers/CTEDisabler.js +325 -0
- package/dist/transformers/CTEDisabler.js.map +1 -0
- package/dist/transformers/CTEInjector.js +83 -0
- package/dist/transformers/CTEInjector.js.map +1 -0
- package/dist/transformers/CTENormalizer.js +42 -0
- package/dist/transformers/CTENormalizer.js.map +1 -0
- package/dist/transformers/Formatter.js +452 -0
- package/dist/transformers/Formatter.js.map +1 -0
- package/dist/transformers/QueryNormalizer.js +114 -0
- package/dist/transformers/QueryNormalizer.js.map +1 -0
- package/dist/transformers/SelectValueCollector.js +249 -0
- package/dist/transformers/SelectValueCollector.js.map +1 -0
- package/dist/transformers/SelectableColumnCollector.js +308 -0
- package/dist/transformers/SelectableColumnCollector.js.map +1 -0
- package/dist/transformers/TableSourceCollector.js +384 -0
- package/dist/transformers/TableSourceCollector.js.map +1 -0
- package/dist/transformers/UpstreamSelectQueryFinder.js +129 -0
- package/dist/transformers/UpstreamSelectQueryFinder.js.map +1 -0
- package/dist/utils/charLookupTable.js +73 -0
- package/dist/utils/charLookupTable.js.map +1 -0
- package/dist/utils/stringUtils.js +168 -0
- package/dist/utils/stringUtils.js.map +1 -0
- package/package.json +3 -3
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/vitest.config.js +0 -15
- package/dist/vitest.config.js.map +0 -1
@@ -0,0 +1,98 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.OperatorTokenReader = void 0;
|
4
|
+
const BaseTokenReader_1 = require("./BaseTokenReader");
|
5
|
+
const Lexeme_1 = require("../models/Lexeme");
|
6
|
+
const charLookupTable_1 = require("../utils/charLookupTable");
|
7
|
+
const KeywordParser_1 = require("../parsers/KeywordParser");
|
8
|
+
const KeywordTrie_1 = require("../models/KeywordTrie");
|
9
|
+
const trie = new KeywordTrie_1.KeywordTrie([
|
10
|
+
// binary
|
11
|
+
["and"],
|
12
|
+
["or"],
|
13
|
+
["is"],
|
14
|
+
["is", "not"],
|
15
|
+
["is", "distinct", "from"],
|
16
|
+
["is", "not", "distinct", "from"],
|
17
|
+
["like"],
|
18
|
+
["in"],
|
19
|
+
["exists"],
|
20
|
+
["between"],
|
21
|
+
["not", "like"],
|
22
|
+
["not", "in"],
|
23
|
+
["not", "exists"],
|
24
|
+
["not", "between"],
|
25
|
+
["escape"], // e.g. '10% OFF on all items' like '10\%%' escape '\'
|
26
|
+
["uescape"], // e.g. U&'d!0061t!+000061' uescape '!'
|
27
|
+
["similar"], // e.g. substring('abcdef' similar '%#"cd#"%' escape '#')
|
28
|
+
["placing"], // e.g. overlay('abcdef' placing 'cd' from 3 for 2)
|
29
|
+
// unary
|
30
|
+
["not"],
|
31
|
+
// unary - trim
|
32
|
+
["both"],
|
33
|
+
["leading"],
|
34
|
+
["trailing"],
|
35
|
+
["both", "from"], // Postgres
|
36
|
+
["leading", "from"], // Postgres
|
37
|
+
["trailing", "from"], // Postgres
|
38
|
+
// unary - extract
|
39
|
+
["year", "from"],
|
40
|
+
["month", "from"],
|
41
|
+
["day", "from"],
|
42
|
+
["hour", "from"],
|
43
|
+
["minute", "from"],
|
44
|
+
["second", "from"],
|
45
|
+
["dow", "from"],
|
46
|
+
["doy", "from"],
|
47
|
+
["isodow", "from"],
|
48
|
+
["quarter", "from"],
|
49
|
+
["week", "from"],
|
50
|
+
["epoch", "from"],
|
51
|
+
["at", "time", "zone"],
|
52
|
+
["interval"],
|
53
|
+
// The following are not considered operators.
|
54
|
+
// ["from"], can be used as an operator only within the substring function, but it cannot be distinguished from the Form Clause. This will be resolved with a dedicated substring parser.
|
55
|
+
// ["for"], can be used as an operator only within the substring function, but it cannot be distinguished from the For Clause. This will be resolved with a dedicated substring parser.
|
56
|
+
]);
|
57
|
+
const keywordParser = new KeywordParser_1.KeywordParser(trie);
|
58
|
+
class OperatorTokenReader extends BaseTokenReader_1.BaseTokenReader {
|
59
|
+
tryRead(previous) {
|
60
|
+
if (this.isEndOfInput()) {
|
61
|
+
return null;
|
62
|
+
}
|
63
|
+
/*
|
64
|
+
NOTE:
|
65
|
+
Asterisks could potentially be wildcard identifiers,
|
66
|
+
but since they're indistinguishable at this stage, they're treated as Operators at the token level.
|
67
|
+
The Parser needs to determine whether they are appropriate Operators or Identifiers.
|
68
|
+
*/
|
69
|
+
const char = this.input[this.position];
|
70
|
+
if (charLookupTable_1.CharLookupTable.isOperatorSymbol(char)) {
|
71
|
+
const start = this.position;
|
72
|
+
while (this.canRead() && charLookupTable_1.CharLookupTable.isOperatorSymbol(this.input[this.position])) {
|
73
|
+
// check for `--` and `/*` comments
|
74
|
+
if (this.canRead(1)) {
|
75
|
+
const current = this.input[this.position];
|
76
|
+
if (current === '-' && this.input[this.position + 1] === '-') {
|
77
|
+
break;
|
78
|
+
}
|
79
|
+
else if (current === '/' && this.input[this.position + 1] === '*') {
|
80
|
+
break; // end of operator
|
81
|
+
}
|
82
|
+
}
|
83
|
+
this.position++;
|
84
|
+
}
|
85
|
+
const resut = this.input.slice(start, this.position);
|
86
|
+
return this.createLexeme(Lexeme_1.TokenType.Operator, resut);
|
87
|
+
}
|
88
|
+
// Logical operators
|
89
|
+
const result = keywordParser.parse(this.input, this.position);
|
90
|
+
if (result !== null) {
|
91
|
+
this.position = result.newPosition;
|
92
|
+
return this.createLexeme(Lexeme_1.TokenType.Operator, result.keyword);
|
93
|
+
}
|
94
|
+
return null;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
exports.OperatorTokenReader = OperatorTokenReader;
|
98
|
+
//# sourceMappingURL=OperatorTokenReader.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"OperatorTokenReader.js","sourceRoot":"","sources":["../../src/tokenReaders/OperatorTokenReader.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.ParameterTokenReader = void 0;
|
4
|
+
const BaseTokenReader_1 = require("./BaseTokenReader");
|
5
|
+
const Lexeme_1 = require("../models/Lexeme");
|
6
|
+
const charLookupTable_1 = require("../utils/charLookupTable");
|
7
|
+
/**
|
8
|
+
* Reads SQL parameter tokens (@param, :param, $param, ?)
|
9
|
+
*/
|
10
|
+
class ParameterTokenReader extends BaseTokenReader_1.BaseTokenReader {
|
11
|
+
/**
|
12
|
+
* Try to read a parameter token
|
13
|
+
*/
|
14
|
+
tryRead(previous) {
|
15
|
+
if (this.isEndOfInput()) {
|
16
|
+
return null;
|
17
|
+
}
|
18
|
+
const char = this.input[this.position];
|
19
|
+
// named parameter (@param, :param, $param)
|
20
|
+
if (charLookupTable_1.CharLookupTable.isNamedParameterPrefix(char)) {
|
21
|
+
// However, do not recognize as a parameter if the next character is an operator symbol
|
22
|
+
// To avoid postgres `::`
|
23
|
+
if (this.canRead(1) && charLookupTable_1.CharLookupTable.isOperatorSymbol(this.input[this.position + 1])) {
|
24
|
+
return null;
|
25
|
+
}
|
26
|
+
this.position++;
|
27
|
+
// Read the identifier part after the prefix
|
28
|
+
const start = this.position;
|
29
|
+
while (this.canRead() && !charLookupTable_1.CharLookupTable.isDelimiter(this.input[this.position])) {
|
30
|
+
this.position++;
|
31
|
+
}
|
32
|
+
const identifier = this.input.slice(start, this.position);
|
33
|
+
return this.createLexeme(Lexeme_1.TokenType.Parameter, char + identifier);
|
34
|
+
}
|
35
|
+
// nameless parameter (?)
|
36
|
+
if (char === '?') {
|
37
|
+
this.position++;
|
38
|
+
return this.createLexeme(Lexeme_1.TokenType.Parameter, char);
|
39
|
+
}
|
40
|
+
return null;
|
41
|
+
}
|
42
|
+
}
|
43
|
+
exports.ParameterTokenReader = ParameterTokenReader;
|
44
|
+
//# sourceMappingURL=ParameterTokenReader.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"ParameterTokenReader.js","sourceRoot":"","sources":["../../src/tokenReaders/ParameterTokenReader.ts"],"names":[],"mappings":";;;AAAA,uDAAoD;AACpD,6CAAqD;AACrD,8DAA2D;AAE3D;;GAEG;AACH,MAAa,oBAAqB,SAAQ,iCAAe;IACrD;;OAEG;IACI,OAAO,CAAC,QAAuB;QAClC,IAAI,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEvC,2CAA2C;QAC3C,IAAI,iCAAe,CAAC,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;YAE/C,uFAAuF;YACvF,yBAAyB;YACzB,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,iCAAe,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrF,OAAO,IAAI,CAAC;YAChB,CAAC;YAED,IAAI,CAAC,QAAQ,EAAE,CAAC;YAEhB,4CAA4C;YAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC5B,OAAO,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,iCAAe,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;gBAC/E,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpB,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC1D,OAAO,IAAI,CAAC,YAAY,CAAC,kBAAS,CAAC,SAAS,EAAE,IAAI,GAAG,UAAU,CAAC,CAAC;QACrE,CAAC;QAED,yBAAyB;QACzB,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACf,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC,YAAY,CAAC,kBAAS,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACxD,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ;AAxCD,oDAwCC"}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.StringSpecifierTokenReader = void 0;
|
4
|
+
const Lexeme_1 = require("../models/Lexeme");
|
5
|
+
const BaseTokenReader_1 = require("./BaseTokenReader");
|
6
|
+
// Prefix sets for quick checks
|
7
|
+
const STRING_SPECIFIERS = new Set(['e\'', 'E\'', 'x\'', 'X\'', 'b\'', 'B\'']);
|
8
|
+
const UNICODE_STRING_SPECIFIERS = new Set(['u&\'', 'U&\'']);
|
9
|
+
class StringSpecifierTokenReader extends BaseTokenReader_1.BaseTokenReader {
|
10
|
+
/**
|
11
|
+
* Try to read an escaped literal like e'...', x'...', etc.
|
12
|
+
*/
|
13
|
+
tryRead(previous) {
|
14
|
+
const start = this.position;
|
15
|
+
// Check for prefixed literals: e', x', b'
|
16
|
+
if (this.canRead(1) && STRING_SPECIFIERS.has(this.input.slice(start, start + 2))) {
|
17
|
+
this.position += 1;
|
18
|
+
const result = this.createLexeme(Lexeme_1.TokenType.StringSpecifier, this.input.slice(start, this.position));
|
19
|
+
return result;
|
20
|
+
}
|
21
|
+
// Check for unicode literal: u&'
|
22
|
+
if (this.canRead(2) && UNICODE_STRING_SPECIFIERS.has(this.input.slice(start, start + 3))) {
|
23
|
+
this.position += 2;
|
24
|
+
const result = this.createLexeme(Lexeme_1.TokenType.StringSpecifier, this.input.slice(start, this.position));
|
25
|
+
return result;
|
26
|
+
}
|
27
|
+
return null;
|
28
|
+
}
|
29
|
+
}
|
30
|
+
exports.StringSpecifierTokenReader = StringSpecifierTokenReader;
|
31
|
+
//# sourceMappingURL=StringSpecifierTokenReader.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"StringSpecifierTokenReader.js","sourceRoot":"","sources":["../../src/tokenReaders/StringSpecifierTokenReader.ts"],"names":[],"mappings":";;;AAAA,6CAAqD;AACrD,uDAAoD;AAEpD,+BAA+B;AAC/B,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AAC9E,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE5D,MAAa,0BAA2B,SAAQ,iCAAe;IAE3D;;OAEG;IACI,OAAO,CAAC,QAAuB;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC;QAE5B,0CAA0C;QAC1C,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/E,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YACnB,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,kBAAS,CAAC,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YACpG,OAAO,MAAM,CAAC;QAClB,CAAC;QAED,iCAAiC;QACjC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,yBAAyB,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACvF,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YACnB,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,kBAAS,CAAC,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YACpG,OAAO,MAAM,CAAC;QAClB,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ;AAxBD,gEAwBC"}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.SpecialSymbolTokenReader = void 0;
|
4
|
+
const BaseTokenReader_1 = require("./BaseTokenReader");
|
5
|
+
const Lexeme_1 = require("../models/Lexeme");
|
6
|
+
/**
|
7
|
+
* Reads SQL symbol tokens (., ,, (, ))
|
8
|
+
*/
|
9
|
+
class SpecialSymbolTokenReader extends BaseTokenReader_1.BaseTokenReader {
|
10
|
+
/**
|
11
|
+
* Try to read a symbol token
|
12
|
+
*/
|
13
|
+
tryRead(previous) {
|
14
|
+
if (this.isEndOfInput()) {
|
15
|
+
return null;
|
16
|
+
}
|
17
|
+
const char = this.input[this.position];
|
18
|
+
// symbol tokens
|
19
|
+
if (char in SpecialSymbolTokenReader.SPECIAL_SYMBOL_TOKENS) {
|
20
|
+
this.position++;
|
21
|
+
return this.createLexeme(SpecialSymbolTokenReader.SPECIAL_SYMBOL_TOKENS[char], char);
|
22
|
+
}
|
23
|
+
return null;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
exports.SpecialSymbolTokenReader = SpecialSymbolTokenReader;
|
27
|
+
SpecialSymbolTokenReader.SPECIAL_SYMBOL_TOKENS = {
|
28
|
+
'.': Lexeme_1.TokenType.Dot,
|
29
|
+
',': Lexeme_1.TokenType.Comma,
|
30
|
+
'(': Lexeme_1.TokenType.OpenParen,
|
31
|
+
')': Lexeme_1.TokenType.CloseParen,
|
32
|
+
'[': Lexeme_1.TokenType.OpenBracket,
|
33
|
+
']': Lexeme_1.TokenType.CloseBracket,
|
34
|
+
};
|
35
|
+
//# sourceMappingURL=SymbolTokenReader.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"SymbolTokenReader.js","sourceRoot":"","sources":["../../src/tokenReaders/SymbolTokenReader.ts"],"names":[],"mappings":";;;AAAA,uDAAoD;AACpD,6CAAqD;AAErD;;GAEG;AACH,MAAa,wBAAyB,SAAQ,iCAAe;IAUzD;;OAEG;IACI,OAAO,CAAC,QAAuB;QAClC,IAAI,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEvC,gBAAgB;QAChB,IAAI,IAAI,IAAI,wBAAwB,CAAC,qBAAqB,EAAE,CAAC;YACzD,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC,YAAY,CACpB,wBAAwB,CAAC,qBAAqB,CAAC,IAAI,CAAC,EACpD,IAAI,CACP,CAAC;QACN,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;;AA7BL,4DA8BC;AA7B2B,8CAAqB,GAA8B;IACvE,GAAG,EAAE,kBAAS,CAAC,GAAG;IAClB,GAAG,EAAE,kBAAS,CAAC,KAAK;IACpB,GAAG,EAAE,kBAAS,CAAC,SAAS;IACxB,GAAG,EAAE,kBAAS,CAAC,UAAU;IACzB,GAAG,EAAE,kBAAS,CAAC,WAAW;IAC1B,GAAG,EAAE,kBAAS,CAAC,YAAY;CAC9B,CAAC"}
|
@@ -0,0 +1,110 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.TokenReaderManager = void 0;
|
4
|
+
/**
|
5
|
+
* Manages and coordinates multiple token readers
|
6
|
+
*/
|
7
|
+
class TokenReaderManager {
|
8
|
+
constructor(input, position = 0) {
|
9
|
+
this.cacheHits = 0;
|
10
|
+
this.cacheMisses = 0;
|
11
|
+
this.input = input;
|
12
|
+
this.position = position;
|
13
|
+
this.readers = [];
|
14
|
+
this.tokenCache = new Map();
|
15
|
+
}
|
16
|
+
/**
|
17
|
+
* Register a token reader
|
18
|
+
* @param reader The reader to register
|
19
|
+
* @returns This manager instance for chaining
|
20
|
+
*/
|
21
|
+
register(reader) {
|
22
|
+
this.readers.push(reader);
|
23
|
+
return this;
|
24
|
+
}
|
25
|
+
/**
|
26
|
+
* Register multiple token readers
|
27
|
+
* @param readers The readers to register
|
28
|
+
* @returns This manager instance for chaining
|
29
|
+
*/
|
30
|
+
registerAll(readers) {
|
31
|
+
readers.forEach(reader => this.register(reader));
|
32
|
+
return this;
|
33
|
+
}
|
34
|
+
/**
|
35
|
+
* Update the position for all readers
|
36
|
+
*/
|
37
|
+
setPosition(position) {
|
38
|
+
this.position = position;
|
39
|
+
for (const reader of this.readers) {
|
40
|
+
reader.setPosition(position);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
/**
|
44
|
+
* Try to read a token using all registered readers
|
45
|
+
* @param position The position to read from
|
46
|
+
* @param previous The previous token, if any
|
47
|
+
* @returns The lexeme if a reader could read it, null otherwise
|
48
|
+
*/
|
49
|
+
tryRead(position, previous) {
|
50
|
+
// Check cache - using position as the key
|
51
|
+
if (this.tokenCache.has(position)) {
|
52
|
+
// Cache hit
|
53
|
+
this.cacheHits++;
|
54
|
+
const lexeme = this.tokenCache.get(position) || null;
|
55
|
+
return lexeme;
|
56
|
+
}
|
57
|
+
// Cache miss - create new entry
|
58
|
+
this.cacheMisses++;
|
59
|
+
this.setPosition(position);
|
60
|
+
// Try to read with each reader
|
61
|
+
let lexeme = null;
|
62
|
+
for (const reader of this.readers) {
|
63
|
+
lexeme = reader.tryRead(previous);
|
64
|
+
if (lexeme) {
|
65
|
+
this.position = reader.getPosition();
|
66
|
+
break;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
// Update all readers' positions
|
70
|
+
for (const reader of this.readers) {
|
71
|
+
reader.setPosition(this.position);
|
72
|
+
}
|
73
|
+
// Save to cache (even if null)
|
74
|
+
this.tokenCache.set(position, lexeme);
|
75
|
+
return lexeme;
|
76
|
+
}
|
77
|
+
/**
|
78
|
+
* Get the maximum position among all readers
|
79
|
+
*/
|
80
|
+
getMaxPosition() {
|
81
|
+
let maxPosition = this.position;
|
82
|
+
for (const reader of this.readers) {
|
83
|
+
const position = reader.getPosition();
|
84
|
+
if (position > maxPosition) {
|
85
|
+
maxPosition = position;
|
86
|
+
}
|
87
|
+
}
|
88
|
+
return maxPosition;
|
89
|
+
}
|
90
|
+
/**
|
91
|
+
* Get the input string
|
92
|
+
*/
|
93
|
+
getInput() {
|
94
|
+
return this.input;
|
95
|
+
}
|
96
|
+
/**
|
97
|
+
* Get cache statistics
|
98
|
+
*/
|
99
|
+
getCacheStats() {
|
100
|
+
const total = this.cacheHits + this.cacheMisses;
|
101
|
+
const ratio = total > 0 ? this.cacheHits / total : 0;
|
102
|
+
return {
|
103
|
+
hits: this.cacheHits,
|
104
|
+
misses: this.cacheMisses,
|
105
|
+
ratio: ratio
|
106
|
+
};
|
107
|
+
}
|
108
|
+
}
|
109
|
+
exports.TokenReaderManager = TokenReaderManager;
|
110
|
+
//# sourceMappingURL=TokenReaderManager.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"TokenReaderManager.js","sourceRoot":"","sources":["../../src/tokenReaders/TokenReaderManager.ts"],"names":[],"mappings":";;;AAGA;;GAEG;AACH,MAAa,kBAAkB;IAQ3B,YAAY,KAAa,EAAE,WAAmB,CAAC;QAHvC,cAAS,GAAW,CAAC,CAAC;QACtB,gBAAW,GAAW,CAAC,CAAC;QAG5B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAClB,IAAI,CAAC,UAAU,GAAG,IAAI,GAAG,EAAE,CAAC;IAChC,CAAC;IAED;;;;OAIG;IACI,QAAQ,CAAC,MAAuB;QACnC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACI,WAAW,CAAC,OAA0B;QACzC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,WAAW,CAAC,QAAgB;QAChC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAChC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACI,OAAO,CAAC,QAAgB,EAAE,QAAuB;QACpD,0CAA0C;QAC1C,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAChC,YAAY;YACZ,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC;YACrD,OAAO,MAAM,CAAC;QAClB,CAAC;QAED,gCAAgC;QAChC,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAE3B,+BAA+B;QAC/B,IAAI,MAAM,GAAkB,IAAI,CAAC;QACjC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAChC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAClC,IAAI,MAAM,EAAE,CAAC;gBACT,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;gBACrC,MAAM;YACV,CAAC;QACL,CAAC;QAED,gCAAgC;QAChC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAChC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACtC,CAAC;QAED,+BAA+B;QAC/B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACtC,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;OAEG;IACI,cAAc;QACjB,IAAI,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC;QAChC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAChC,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;YACtC,IAAI,QAAQ,GAAG,WAAW,EAAE,CAAC;gBACzB,WAAW,GAAG,QAAQ,CAAC;YAC3B,CAAC;QACL,CAAC;QACD,OAAO,WAAW,CAAC;IACvB,CAAC;IAED;;OAEG;IACI,QAAQ;QACX,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED;;OAEG;IACI,aAAa;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC;QAChD,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,OAAO;YACH,IAAI,EAAE,IAAI,CAAC,SAAS;YACpB,MAAM,EAAE,IAAI,CAAC,WAAW;YACxB,KAAK,EAAE,KAAK;SACf,CAAC;IACN,CAAC;CACJ;AArHD,gDAqHC"}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.TypeTokenReader = void 0;
|
4
|
+
const BaseTokenReader_1 = require("./BaseTokenReader");
|
5
|
+
const Lexeme_1 = require("../models/Lexeme");
|
6
|
+
const stringUtils_1 = require("../utils/stringUtils");
|
7
|
+
const KeywordTrie_1 = require("../models/KeywordTrie");
|
8
|
+
const KeywordParser_1 = require("../parsers/KeywordParser");
|
9
|
+
// Use KeywordTrie to identify type names composed of multiple words.
|
10
|
+
const trie = new KeywordTrie_1.KeywordTrie([
|
11
|
+
// type
|
12
|
+
["double", "precision"],
|
13
|
+
["character", "varying"],
|
14
|
+
["time", "without", "time", "zone"],
|
15
|
+
["time", "with", "time", "zone"],
|
16
|
+
["timestamp", "without", "time", "zone"],
|
17
|
+
["timestamp", "with", "time", "zone"],
|
18
|
+
]);
|
19
|
+
const typeParser = new KeywordParser_1.KeywordParser(trie);
|
20
|
+
/**
|
21
|
+
* Reads SQL identifier tokens
|
22
|
+
*/
|
23
|
+
class TypeTokenReader extends BaseTokenReader_1.BaseTokenReader {
|
24
|
+
/**
|
25
|
+
* Try to read an identifier token
|
26
|
+
*/
|
27
|
+
tryRead(previous) {
|
28
|
+
if (this.isEndOfInput()) {
|
29
|
+
return null;
|
30
|
+
}
|
31
|
+
// Check for keyword identifiers
|
32
|
+
const keyword = typeParser.parse(this.input, this.position);
|
33
|
+
if (keyword !== null) {
|
34
|
+
this.position = keyword.newPosition;
|
35
|
+
return this.createLexeme(Lexeme_1.TokenType.Type, keyword.keyword);
|
36
|
+
}
|
37
|
+
// check pervious token
|
38
|
+
if (previous === null) {
|
39
|
+
return null;
|
40
|
+
}
|
41
|
+
const result = stringUtils_1.StringUtils.tryReadRegularIdentifier(this.input, this.position);
|
42
|
+
if (!result) {
|
43
|
+
return null;
|
44
|
+
}
|
45
|
+
this.position = result.newPosition;
|
46
|
+
// type cast command
|
47
|
+
if (previous.type === Lexeme_1.TokenType.Command && previous.value === "as") {
|
48
|
+
// If the previous token is the `as` keyword, it could be a type cast or an identifier
|
49
|
+
return this.createLexeme(Lexeme_1.TokenType.Identifier, result.identifier, true);
|
50
|
+
}
|
51
|
+
// postgres type conversion
|
52
|
+
if (previous.type === Lexeme_1.TokenType.Operator && previous.value === "::") {
|
53
|
+
return this.createLexeme(Lexeme_1.TokenType.Type, result.identifier);
|
54
|
+
}
|
55
|
+
return null;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
exports.TypeTokenReader = TypeTokenReader;
|
59
|
+
//# sourceMappingURL=TypeTokenReader.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"TypeTokenReader.js","sourceRoot":"","sources":["../../src/tokenReaders/TypeTokenReader.ts"],"names":[],"mappings":";;;AAAA,uDAAoD;AACpD,6CAAqD;AACrD,sDAAmD;AACnD,uDAAoD;AACpD,4DAAyD;AAEzD,qEAAqE;AACrE,MAAM,IAAI,GAAG,IAAI,yBAAW,CAAC;IACzB,OAAO;IACP,CAAC,QAAQ,EAAE,WAAW,CAAC;IACvB,CAAC,WAAW,EAAE,SAAS,CAAC;IACxB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC;IACnC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;IAChC,CAAC,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC;IACxC,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;CACxC,CAAC,CAAC;AACH,MAAM,UAAU,GAAG,IAAI,6BAAa,CAAC,IAAI,CAAC,CAAC;AAE3C;;GAEG;AACH,MAAa,eAAgB,SAAQ,iCAAe;IAChD;;OAEG;IACI,OAAO,CAAC,QAAuB;QAClC,IAAI,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,gCAAgC;QAChC,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5D,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACnB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC;YACpC,OAAO,IAAI,CAAC,YAAY,CAAC,kBAAS,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9D,CAAC;QAED,uBAAuB;QACvB,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,MAAM,GAAG,yBAAW,CAAC,wBAAwB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/E,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC;QAEnC,oBAAoB;QACpB,IAAI,QAAQ,CAAC,IAAI,KAAK,kBAAS,CAAC,OAAO,IAAI,QAAQ,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACjE,sFAAsF;YACtF,OAAO,IAAI,CAAC,YAAY,CAAC,kBAAS,CAAC,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAC5E,CAAC;QAED,2BAA2B;QAC3B,IAAI,QAAQ,CAAC,IAAI,KAAK,kBAAS,CAAC,QAAQ,IAAI,QAAQ,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YAClE,OAAO,IAAI,CAAC,YAAY,CAAC,kBAAS,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QAChE,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ;AAxCD,0CAwCC"}
|
@@ -0,0 +1,188 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.CTEBuilder = void 0;
|
4
|
+
const Clause_1 = require("../models/Clause");
|
5
|
+
const CTECollector_1 = require("./CTECollector");
|
6
|
+
const TableSourceCollector_1 = require("./TableSourceCollector");
|
7
|
+
const Formatter_1 = require("./Formatter");
|
8
|
+
/**
|
9
|
+
* CTENameConflictResolver is responsible for resolving name conflicts among Common Table Expressions (CTEs).
|
10
|
+
* It also sorts the tables in the proper order based on dependencies and recursiveness.
|
11
|
+
*/
|
12
|
+
class CTEBuilder {
|
13
|
+
constructor() {
|
14
|
+
this.sourceCollector = new TableSourceCollector_1.TableSourceCollector(true);
|
15
|
+
this.cteCollector = new CTECollector_1.CTECollector();
|
16
|
+
this.formatter = new Formatter_1.Formatter();
|
17
|
+
}
|
18
|
+
/**
|
19
|
+
* Resolves name conflicts among CommonTables.
|
20
|
+
* If there are duplicate CTE names, they must have identical definitions.
|
21
|
+
* Also sorts the tables so that:
|
22
|
+
* 1. Recursive CTEs come first (CTEs that reference themselves)
|
23
|
+
* 2. Then remaining tables are sorted so inner (deeper) CTEs come before outer CTEs
|
24
|
+
*
|
25
|
+
* @param commonTables The list of CommonTables to check for name conflicts
|
26
|
+
* @returns An object containing:
|
27
|
+
* - needRecursive: boolean indicating if any recursive CTEs are present
|
28
|
+
* - commonTables: A new list of CommonTables with resolved name conflicts and proper order
|
29
|
+
* @throws Error if there are duplicate CTE names with different definitions
|
30
|
+
*/
|
31
|
+
build(commonTables) {
|
32
|
+
// Early return for empty CTEs
|
33
|
+
// Note:
|
34
|
+
// Although it may seem reasonable to return early when there is only one element,
|
35
|
+
// the 'recursive' property is determined dynamically. Therefore, if there is at least one element,
|
36
|
+
// the CTEs must be rebuilt to ensure correct recursive detection.
|
37
|
+
if (commonTables.length === 0) {
|
38
|
+
return new Clause_1.WithClause(false, commonTables);
|
39
|
+
}
|
40
|
+
// Step 1: Resolve name conflicts
|
41
|
+
const resolvedTables = this.resolveDuplicateNames(commonTables);
|
42
|
+
// Step 2: Identify recursive CTEs and build dependency graph
|
43
|
+
const { tableMap, recursiveCTEs, dependencies } = this.buildDependencyGraph(resolvedTables);
|
44
|
+
// Step 3: Sort tables according to dependencies and recursiveness
|
45
|
+
const sortedTables = this.sortCommonTables(resolvedTables, tableMap, recursiveCTEs, dependencies);
|
46
|
+
return new Clause_1.WithClause(recursiveCTEs.size > 0, sortedTables);
|
47
|
+
}
|
48
|
+
/**
|
49
|
+
* Resolves duplicate CTE names by checking if they have identical definitions.
|
50
|
+
* If definitions differ, throws an error.
|
51
|
+
*
|
52
|
+
* @param commonTables The list of CTEs to check for duplicates
|
53
|
+
* @returns A list of CTEs with duplicates removed
|
54
|
+
* @throws Error if there are duplicate CTE names with different definitions
|
55
|
+
*/
|
56
|
+
resolveDuplicateNames(commonTables) {
|
57
|
+
// Group CTEs by their names
|
58
|
+
const ctesByName = new Map();
|
59
|
+
for (const table of commonTables) {
|
60
|
+
const tableName = table.aliasExpression.table.name;
|
61
|
+
if (!ctesByName.has(tableName)) {
|
62
|
+
ctesByName.set(tableName, []);
|
63
|
+
}
|
64
|
+
ctesByName.get(tableName).push(table);
|
65
|
+
}
|
66
|
+
// Resolve name duplications
|
67
|
+
const resolvedTables = [];
|
68
|
+
for (const [name, tables] of ctesByName.entries()) {
|
69
|
+
if (tables.length === 1) {
|
70
|
+
// No duplication
|
71
|
+
resolvedTables.push(tables[0]);
|
72
|
+
continue;
|
73
|
+
}
|
74
|
+
// For duplicate names, check if definitions are identical
|
75
|
+
const definitions = tables.map(table => this.formatter.visit(table.query));
|
76
|
+
const uniqueDefinitions = new Set(definitions);
|
77
|
+
if (uniqueDefinitions.size === 1) {
|
78
|
+
// If all definitions are identical, use only the first one
|
79
|
+
resolvedTables.push(tables[0]);
|
80
|
+
}
|
81
|
+
else {
|
82
|
+
// Error if definitions differ
|
83
|
+
throw new Error(`CTE name conflict detected: '${name}' has multiple different definitions`);
|
84
|
+
}
|
85
|
+
}
|
86
|
+
return resolvedTables;
|
87
|
+
}
|
88
|
+
/**
|
89
|
+
* Builds a dependency graph of CTEs and identifies recursive CTEs.
|
90
|
+
*
|
91
|
+
* @param tables The list of CTEs to analyze
|
92
|
+
* @returns Object containing the table map, set of recursive CTEs, and dependency map
|
93
|
+
*/
|
94
|
+
buildDependencyGraph(tables) {
|
95
|
+
// Create a map of table names for quick lookup
|
96
|
+
const tableMap = new Map();
|
97
|
+
for (const table of tables) {
|
98
|
+
tableMap.set(table.aliasExpression.table.name, table);
|
99
|
+
}
|
100
|
+
// Identify recursive CTEs (those that reference themselves)
|
101
|
+
const recursiveCTEs = new Set();
|
102
|
+
// Build dependency graph: which tables reference which other tables
|
103
|
+
const dependencies = new Map();
|
104
|
+
const referencedBy = new Map();
|
105
|
+
for (const table of tables) {
|
106
|
+
const tableName = table.aliasExpression.table.name;
|
107
|
+
// Check for self-references (recursive CTEs)
|
108
|
+
const referencedTables = this.sourceCollector.collect(table.query);
|
109
|
+
// Check if this CTE references itself
|
110
|
+
for (const referencedTable of referencedTables) {
|
111
|
+
if (referencedTable.table.name === tableName) {
|
112
|
+
recursiveCTEs.add(tableName);
|
113
|
+
break;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
// Setup dependencies
|
117
|
+
if (!dependencies.has(tableName)) {
|
118
|
+
dependencies.set(tableName, new Set());
|
119
|
+
}
|
120
|
+
// Find any references to other CTEs in this table's query
|
121
|
+
const referencedCTEs = this.cteCollector.collect(table.query);
|
122
|
+
for (const referencedCTE of referencedCTEs) {
|
123
|
+
const referencedName = referencedCTE.aliasExpression.table.name;
|
124
|
+
// Only consider references to tables in our collection
|
125
|
+
if (tableMap.has(referencedName)) {
|
126
|
+
dependencies.get(tableName).add(referencedName);
|
127
|
+
// Add the reverse relationship
|
128
|
+
if (!referencedBy.has(referencedName)) {
|
129
|
+
referencedBy.set(referencedName, new Set());
|
130
|
+
}
|
131
|
+
referencedBy.get(referencedName).add(tableName);
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|
135
|
+
return { tableMap, recursiveCTEs, dependencies };
|
136
|
+
}
|
137
|
+
/**
|
138
|
+
* Sorts the CTEs using topological sort, with recursive CTEs coming first.
|
139
|
+
*
|
140
|
+
* @param tables The list of CTEs to sort
|
141
|
+
* @param tableMap Map of table names to their CommonTable objects
|
142
|
+
* @param recursiveCTEs Set of table names that are recursive (self-referential)
|
143
|
+
* @param dependencies Map of table dependencies
|
144
|
+
* @returns Sorted list of CTEs
|
145
|
+
* @throws Error if a circular reference is detected
|
146
|
+
*/
|
147
|
+
sortCommonTables(tables, tableMap, recursiveCTEs, dependencies) {
|
148
|
+
const recursiveResult = [];
|
149
|
+
const nonRecursiveResult = [];
|
150
|
+
const visited = new Set();
|
151
|
+
const visiting = new Set();
|
152
|
+
// Topological sort function
|
153
|
+
const visit = (tableName) => {
|
154
|
+
if (visited.has(tableName))
|
155
|
+
return;
|
156
|
+
if (visiting.has(tableName)) {
|
157
|
+
throw new Error(`Circular reference detected in CTE: ${tableName}`);
|
158
|
+
}
|
159
|
+
visiting.add(tableName);
|
160
|
+
// Process dependencies first (inner CTEs)
|
161
|
+
const deps = dependencies.get(tableName) || new Set();
|
162
|
+
for (const dep of deps) {
|
163
|
+
visit(dep);
|
164
|
+
}
|
165
|
+
visiting.delete(tableName);
|
166
|
+
visited.add(tableName);
|
167
|
+
// Add this table after its dependencies
|
168
|
+
// Recursive CTEs go to recursiveResult, others to nonRecursiveResult
|
169
|
+
if (recursiveCTEs.has(tableName)) {
|
170
|
+
recursiveResult.push(tableMap.get(tableName));
|
171
|
+
}
|
172
|
+
else {
|
173
|
+
nonRecursiveResult.push(tableMap.get(tableName));
|
174
|
+
}
|
175
|
+
};
|
176
|
+
// Process all tables
|
177
|
+
for (const table of tables) {
|
178
|
+
const tableName = table.aliasExpression.table.name;
|
179
|
+
if (!visited.has(tableName)) {
|
180
|
+
visit(tableName);
|
181
|
+
}
|
182
|
+
}
|
183
|
+
// Combine the results: recursive CTEs first, then non-recursive CTEs
|
184
|
+
return [...recursiveResult, ...nonRecursiveResult];
|
185
|
+
}
|
186
|
+
}
|
187
|
+
exports.CTEBuilder = CTEBuilder;
|
188
|
+
//# sourceMappingURL=CTEBuilder.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"CTEBuilder.js","sourceRoot":"","sources":["../../src/transformers/CTEBuilder.ts"],"names":[],"mappings":"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"}
|