@deterministic-code/datasources 0.0.0-stage → 0.1.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/package.json +16 -4
- package/src/ArrayReader.ts +32 -0
- package/src/BatchCommandIBulkWriteStrategy.ts +228 -0
- package/src/BuiltRepository.ts +17 -0
- package/src/BulkCRUDRepository.ts +84 -0
- package/src/BulkCommand.ts +6 -0
- package/src/BulkWriteEnrichment.ts +19 -0
- package/src/CRUDRepository.ts +264 -0
- package/src/CRUDRepositoryBuilder.ts +53 -0
- package/src/CRUDRepositoryOptions.ts +8 -0
- package/src/CRUDRepositoryStatements.ts +43 -0
- package/src/CascadeDeleteEnrichmentStrategy.ts +51 -0
- package/src/CascadeDeleteUpdateEnrichmentStrategy.ts +28 -0
- package/src/CommandMiddlewareHost.ts +113 -0
- package/src/ConsoleCommandMiddleware.ts +64 -0
- package/src/CreateEnrichmentStrategy.ts +82 -0
- package/src/Datasource.ts +152 -0
- package/src/DatasourceError.ts +6 -0
- package/src/DatasourceFactory.ts +86 -0
- package/src/DatasourceOptions.ts +1 -0
- package/src/DatasourceUrl.ts +37 -0
- package/src/DatasourceUrlParser.ts +188 -0
- package/src/DefaultCreateEnrichmentStrategy.ts +21 -0
- package/src/DefaultReadonlyEnrichmentStrategy.ts +164 -0
- package/src/DefaultUpdateEnrichmentStrategy.ts +40 -0
- package/src/DefaultWriteEnrichmentStrategy.ts +151 -0
- package/src/DeleteEnrichmentStrategy.ts +56 -0
- package/src/DriverLoader.ts +97 -0
- package/src/Enrichment.ts +15 -0
- package/src/EnrichmentPropertyType.ts +9 -0
- package/src/ExpectConsoleMiddleware.ts +47 -0
- package/src/FirstResultReader.ts +49 -0
- package/src/HashsetOptimizedReadonlyEnrichmentStrategy.ts +80 -0
- package/src/IBulkWriteStrategy.ts +13 -0
- package/src/ICRUDRepository.ts +13 -0
- package/src/ICRUDRepositoryFactory.ts +8 -0
- package/src/ICommand.ts +45 -0
- package/src/ICommandMiddleware.ts +20 -0
- package/src/IConnection.ts +13 -0
- package/src/IDatasource.ts +27 -0
- package/src/IDatasourceFactory.ts +6 -0
- package/src/IDriver.ts +24 -0
- package/src/IEnriched.ts +11 -0
- package/src/IReader.ts +8 -0
- package/src/IReadonlyEnrichmentStrategy.ts +19 -0
- package/src/IReadonlyRepository.ts +21 -0
- package/src/IRepository.ts +5 -0
- package/src/IRepositoryFactory.ts +8 -0
- package/src/IRepositoryTree.ts +5 -0
- package/src/IRepositoryTreeNode.ts +8 -0
- package/src/ITransaction.ts +9 -0
- package/src/IUnitOfWork.ts +5 -0
- package/src/IWriteEnrichmentStrategy.ts +8 -0
- package/src/Information.ts +5 -0
- package/src/IsolationLevel.ts +8 -0
- package/src/LazyWriteEnrichmentStrategy.ts +5 -0
- package/src/NestedToObjectEnrichmentStrategy.ts +33 -0
- package/src/Parameter.ts +8 -0
- package/src/ParameterDirection.ts +9 -0
- package/src/ProviderCatalog.ts +21 -0
- package/src/ProviderNotImplementedError.ts +9 -0
- package/src/ProviderNotInstalledError.ts +9 -0
- package/src/ReadonlyRepository.ts +276 -0
- package/src/ReadonlyRepositoryBuilder.ts +53 -0
- package/src/ReadonlyRepositoryMappingOptions.ts +15 -0
- package/src/ReadonlyRepositoryStatements.ts +13 -0
- package/src/RepositoryAccess.ts +14 -0
- package/src/RepositoryFactory.ts +115 -0
- package/src/RepositoryNotRegisteredError.ts +9 -0
- package/src/RepositoryTree.ts +6 -0
- package/src/RepositoryTreeNode.ts +11 -0
- package/src/Response.ts +15 -0
- package/src/Row.ts +3 -0
- package/src/SingleCommandIBulkWriteStrategy.ts +35 -0
- package/src/StatementMappers.ts +110 -0
- package/src/StoredEnrichment.ts +25 -0
- package/src/StoredProcedureBulkWriteStrategy.ts +164 -0
- package/src/StreamingReader.ts +64 -0
- package/src/UnitOfWorkTransaction.ts +50 -0
- package/src/UpdateEnrichmentStrategy.ts +97 -0
- package/src/WriteMode.ts +1 -0
- package/src/builders/Field.ts +6 -0
- package/src/builders/Scalar.ts +1 -0
- package/src/builders/index.ts +2 -0
- package/src/collectAsync.ts +7 -0
- package/src/converters/Cell.ts +10 -0
- package/src/converters/ChangeCasingConverter.ts +59 -0
- package/src/converters/Constructable.ts +1 -0
- package/src/converters/MetaDataRowTypeConverter.ts +80 -0
- package/src/converters/PluralizerConverter.ts +67 -0
- package/src/converters/RegexConverter.ts +14 -0
- package/src/converters/ReplaceConverter.ts +6 -0
- package/src/converters/RowTypeConverter.ts +33 -0
- package/src/converters/ToLowerCaseConverter.ts +4 -0
- package/src/converters/ToUpperCaseConverter.ts +4 -0
- package/src/converters/TypeConverter.ts +6 -0
- package/src/createCRUDRepositoryOptions.ts +21 -0
- package/src/createReadonlyRepositoryMappingOptions.ts +26 -0
- package/src/dotEnv.ts +16 -0
- package/src/index.ts +222 -0
- package/src/mappers/ChangeCasingMapper.ts +15 -0
- package/src/mappers/DictionaryMapper.ts +16 -0
- package/src/mappers/FlatToNestedMapper.ts +127 -0
- package/src/mappers/IFlatToNestedMapper.ts +7 -0
- package/src/mappers/IMapper.ts +4 -0
- package/src/mappers/IdentityMapper.ts +6 -0
- package/src/mappers/PluralizerMapper.ts +9 -0
- package/src/mappers/RowTypeMapper.ts +13 -0
- package/src/mappers/TransformConverterMapper.ts +11 -0
- package/src/mappers/TransformMapper.ts +8 -0
- package/src/mappers/TypeMap.ts +10 -0
- package/src/mappers/TypeMapGroup.ts +6 -0
- package/src/mocks/index.ts +9 -0
- package/src/mocks/mock-command.ts +66 -0
- package/src/mocks/mock-connection.ts +57 -0
- package/src/mocks/mock-datasource.ts +65 -0
- package/src/mocks/mock-driver.ts +27 -0
- package/src/mocks/mock-reader.ts +38 -0
- package/src/mocks/mock-repository.ts +91 -0
- package/src/mocks/mock-select-statement.ts +17 -0
- package/src/mocks/mock-transaction.ts +31 -0
- package/src/mocks/mock-where.ts +13 -0
- package/src/parametersToRecord.ts +11 -0
- package/src/readerToAsyncIterable.ts +11 -0
- package/src/splitStatements.ts +99 -0
- package/src/statementParameters.ts +22 -0
- package/src/statementUsesParameter.ts +4 -0
- package/src/statements/Aggregate.ts +9 -0
- package/src/statements/ArrayExpression.ts +5 -0
- package/src/statements/BulkDeleteStatement.ts +18 -0
- package/src/statements/BulkDeleteStatementTextRenderer.ts +16 -0
- package/src/statements/BulkInsertStatement.ts +37 -0
- package/src/statements/BulkInsertStatementTextRenderer.ts +10 -0
- package/src/statements/BulkUpdateStatement.ts +24 -0
- package/src/statements/BulkUpdateStatementTextRenderer.ts +29 -0
- package/src/statements/CheckConstraint.ts +9 -0
- package/src/statements/Cmp.ts +12 -0
- package/src/statements/Column.ts +10 -0
- package/src/statements/ColumnDefault.ts +6 -0
- package/src/statements/Condition.ts +3 -0
- package/src/statements/ConditionRenderer.ts +17 -0
- package/src/statements/CreateIndexStatement.ts +18 -0
- package/src/statements/CreateIndexStatementTextRenderer.ts +11 -0
- package/src/statements/CreateTableStatement.ts +18 -0
- package/src/statements/CreateTableStatementTextRenderer.ts +92 -0
- package/src/statements/DefaultStatementTextRenderer.ts +9 -0
- package/src/statements/DeleteStatement.ts +15 -0
- package/src/statements/DeleteStatementTextRenderer.ts +12 -0
- package/src/statements/ExecuteStoredProcedureStatement.ts +15 -0
- package/src/statements/ExecuteStoredProcedureStatementTextRenderer.ts +15 -0
- package/src/statements/ExpressionEvaluator.ts +166 -0
- package/src/statements/ExpressionRenderer.ts +161 -0
- package/src/statements/ForeignKey.ts +23 -0
- package/src/statements/IBulkDeleteStatement.ts +3 -0
- package/src/statements/IBulkInsertStatement.ts +3 -0
- package/src/statements/IBulkUpdateStatement.ts +3 -0
- package/src/statements/ICondition.ts +5 -0
- package/src/statements/IConditionVisitor.ts +7 -0
- package/src/statements/ICreateIndexStatement.ts +3 -0
- package/src/statements/ICreateTableStatement.ts +3 -0
- package/src/statements/IDeleteStatement.ts +6 -0
- package/src/statements/IExecuteStoredProcedureStatement.ts +3 -0
- package/src/statements/IExpression.ts +1 -0
- package/src/statements/IInsertStatement.ts +3 -0
- package/src/statements/IReturnStatement.ts +3 -0
- package/src/statements/ISelectStatement.ts +6 -0
- package/src/statements/IStatement.ts +5 -0
- package/src/statements/IStatementVisitor.ts +27 -0
- package/src/statements/ITableConstraint.ts +6 -0
- package/src/statements/IUpdateStatement.ts +6 -0
- package/src/statements/IWhere.ts +7 -0
- package/src/statements/Identifier.ts +5 -0
- package/src/statements/InsertStatement.ts +15 -0
- package/src/statements/InsertStatementTextRenderer.ts +7 -0
- package/src/statements/Join.ts +26 -0
- package/src/statements/Literal.ts +6 -0
- package/src/statements/Native.ts +5 -0
- package/src/statements/NotNull.ts +3 -0
- package/src/statements/Operator.ts +234 -0
- package/src/statements/OrderBy.ts +19 -0
- package/src/statements/PrimaryKey.ts +9 -0
- package/src/statements/ReturnStatement.ts +15 -0
- package/src/statements/ReturnStatementTextRenderer.ts +15 -0
- package/src/statements/SelectInsertedIdStatement.ts +11 -0
- package/src/statements/SelectInsertedIdStatementTextRenderer.ts +7 -0
- package/src/statements/SelectStatement.ts +34 -0
- package/src/statements/SelectStatementOptions.ts +16 -0
- package/src/statements/SelectStatementTextRenderer.ts +71 -0
- package/src/statements/StatementEvaluator.ts +5 -0
- package/src/statements/StatementParser.ts +461 -0
- package/src/statements/StatementRenderer.ts +120 -0
- package/src/statements/StatementTextRenderer.ts +60 -0
- package/src/statements/Subquery.ts +6 -0
- package/src/statements/UniqueConstraint.ts +9 -0
- package/src/statements/UpdateStatement.ts +34 -0
- package/src/statements/UpdateStatementTextRenderer.ts +63 -0
- package/src/statements/Where.ts +250 -0
- package/src/statements/WhereText.ts +10 -0
- package/src/statements/index.ts +75 -0
- package/src/stubs/StubCommand.ts +30 -0
- package/src/stubs/StubConnection.ts +21 -0
- package/src/stubs/StubDatasource.ts +60 -0
- package/src/stubs/StubReader.ts +25 -0
- package/src/stubs/StubRepository.ts +42 -0
- package/src/stubs/StubTransaction.ts +17 -0
- package/src/stubs/createStubDatasource.ts +4 -0
- package/src/stubs/index.ts +7 -0
- package/src/stubs/notImplemented.ts +5 -0
- package/README.md +0 -3
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import type { Field } from "../builders/Field.ts";
|
|
2
|
+
import { ArrayExpression } from "./ArrayExpression.ts";
|
|
3
|
+
import { DeleteStatement } from "./DeleteStatement.ts";
|
|
4
|
+
import { Identifier } from "./Identifier.ts";
|
|
5
|
+
import type { IExpression } from "./IExpression.ts";
|
|
6
|
+
import type { IStatement } from "./IStatement.ts";
|
|
7
|
+
import { InsertStatement } from "./InsertStatement.ts";
|
|
8
|
+
import { Join, type JoinKind } from "./Join.ts";
|
|
9
|
+
import { Literal } from "./Literal.ts";
|
|
10
|
+
import { Operator } from "./Operator.ts";
|
|
11
|
+
import { OrderBy } from "./OrderBy.ts";
|
|
12
|
+
import { DefaultStatementTextRenderer } from "./DefaultStatementTextRenderer.ts";
|
|
13
|
+
import { SelectStatement } from "./SelectStatement.ts";
|
|
14
|
+
import { Subquery } from "./Subquery.ts";
|
|
15
|
+
import { UpdateStatement } from "./UpdateStatement.ts";
|
|
16
|
+
import { Where } from "./Where.ts";
|
|
17
|
+
|
|
18
|
+
const empty = (): never => {
|
|
19
|
+
throw new Error("empty statement");
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
class Cursor {
|
|
23
|
+
private i = 0;
|
|
24
|
+
|
|
25
|
+
constructor(private readonly text: string) {}
|
|
26
|
+
|
|
27
|
+
statement(): IStatement {
|
|
28
|
+
const word = (["SELECT", "INSERT", "UPDATE", "DELETE"] as const).find((name) => this.peekWord(name)) ?? empty();
|
|
29
|
+
return {
|
|
30
|
+
SELECT: () => this.selectStatement(),
|
|
31
|
+
INSERT: () => this.parseInsert(),
|
|
32
|
+
UPDATE: () => this.parseUpdate(),
|
|
33
|
+
DELETE: () => this.parseDelete(),
|
|
34
|
+
}[word]();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private parseInsert(): InsertStatement {
|
|
38
|
+
this.expectWord("INSERT");
|
|
39
|
+
this.expectWord("INTO");
|
|
40
|
+
const table = this.ident();
|
|
41
|
+
this.expectChar("(");
|
|
42
|
+
const columns = [this.ident()];
|
|
43
|
+
while (this.tryChar(",")) columns.push(this.ident());
|
|
44
|
+
this.expectChar(")");
|
|
45
|
+
this.expectWord("VALUES");
|
|
46
|
+
this.expectChar("(");
|
|
47
|
+
const values = [this.rawExpr()];
|
|
48
|
+
while (this.tryChar(",")) values.push(this.rawExpr());
|
|
49
|
+
this.expectChar(")");
|
|
50
|
+
const fields: Field[] = columns.map((name) => ({ name, value: null }));
|
|
51
|
+
return values.length === columns.length &&
|
|
52
|
+
values.every((value, index) => value.trim() === `$${columns[index]}`)
|
|
53
|
+
? new InsertStatement(table, fields)
|
|
54
|
+
: empty();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private parseUpdate(): UpdateStatement {
|
|
58
|
+
this.expectWord("UPDATE");
|
|
59
|
+
const table = this.ident();
|
|
60
|
+
this.expectWord("SET");
|
|
61
|
+
const columns: string[] = [];
|
|
62
|
+
let canonical = this.assignment(columns);
|
|
63
|
+
while (this.tryChar(",")) canonical = this.assignment(columns) && canonical;
|
|
64
|
+
const where = this.tryWord("WHERE") ? new Where(this.expr()) : undefined;
|
|
65
|
+
const fields: Field[] = columns.map((name) => ({ name, value: null }));
|
|
66
|
+
return canonical ? new UpdateStatement(table, fields, where) : empty();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private assignment(columns: string[]): boolean {
|
|
70
|
+
const column = this.ident();
|
|
71
|
+
this.expectChar("=");
|
|
72
|
+
const raw = this.rawExpr();
|
|
73
|
+
columns.push(column);
|
|
74
|
+
return raw.trim() === `$${column}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private parseDelete(): DeleteStatement {
|
|
78
|
+
this.expectWord("DELETE");
|
|
79
|
+
this.expectWord("FROM");
|
|
80
|
+
const table = this.ident();
|
|
81
|
+
return new DeleteStatement(table, this.tryWord("WHERE") ? new Where(this.expr()) : undefined);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private rawExpr(): string {
|
|
85
|
+
this.skip();
|
|
86
|
+
const start = this.i;
|
|
87
|
+
this.expr();
|
|
88
|
+
return this.text.slice(start, this.i);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
end(): void {
|
|
92
|
+
this.skip();
|
|
93
|
+
if (this.i !== this.text.length) empty();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private selectStatement(): SelectStatement {
|
|
97
|
+
this.expectWord("SELECT");
|
|
98
|
+
const distinct = this.tryWord("DISTINCT");
|
|
99
|
+
const fields = this.selectList();
|
|
100
|
+
this.expectWord("FROM");
|
|
101
|
+
const table = this.fromItem();
|
|
102
|
+
const joins = this.joins();
|
|
103
|
+
const where = this.tryWord("WHERE") ? new Where(this.expr()) : undefined;
|
|
104
|
+
const groupBy = this.tryWord("GROUP") ? this.group() : undefined;
|
|
105
|
+
const having = this.tryWord("HAVING") ? new Where(this.expr()) : undefined;
|
|
106
|
+
const orderBy = this.tryWord("ORDER") ? this.order() : undefined;
|
|
107
|
+
const limit = this.tryWord("LIMIT") ? this.integer() : undefined;
|
|
108
|
+
const offset = this.tryWord("OFFSET") ? this.integer() : undefined;
|
|
109
|
+
return new SelectStatement({
|
|
110
|
+
distinct,
|
|
111
|
+
table,
|
|
112
|
+
fields,
|
|
113
|
+
where,
|
|
114
|
+
joins,
|
|
115
|
+
groupBy,
|
|
116
|
+
having,
|
|
117
|
+
orderBy,
|
|
118
|
+
limit,
|
|
119
|
+
offset,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private selectList(): string[] {
|
|
124
|
+
if (this.tryChar("*")) return [];
|
|
125
|
+
const fields = [this.selectItem()];
|
|
126
|
+
while (this.tryChar(",")) fields.push(this.selectItem());
|
|
127
|
+
return fields;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private selectItem(): string {
|
|
131
|
+
for (const name of ["COUNT", "SUM", "MIN", "MAX"]) {
|
|
132
|
+
const mark = this.i;
|
|
133
|
+
if (!this.tryWord(name) || !this.tryChar("(")) {
|
|
134
|
+
this.i = mark;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const arg = this.tryChar("*") ? "*" : this.ident();
|
|
138
|
+
this.expectChar(")");
|
|
139
|
+
const alias = this.tryWord("AS") ? ` AS ${this.ident()}` : "";
|
|
140
|
+
return `${name}(${arg})${alias}`;
|
|
141
|
+
}
|
|
142
|
+
const column = this.ident();
|
|
143
|
+
return this.tryWord("AS") ? `${column} AS ${this.ident()}` : column;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private fromItem(): string {
|
|
147
|
+
if (!this.peek("(")) return this.ident();
|
|
148
|
+
const nested = this.nested();
|
|
149
|
+
this.expectWord("AS");
|
|
150
|
+
return `(${DefaultStatementTextRenderer.instance.render(nested)}) AS ${this.ident()}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private nested(): SelectStatement {
|
|
154
|
+
this.expectChar("(");
|
|
155
|
+
const query = this.selectStatement();
|
|
156
|
+
this.expectChar(")");
|
|
157
|
+
return query;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private joins(): Join[] {
|
|
161
|
+
const joins: Join[] = [];
|
|
162
|
+
for (let kind = this.kind(); kind; kind = this.kind()) {
|
|
163
|
+
this.expectWord(
|
|
164
|
+
kind === "cross"
|
|
165
|
+
? "CROSS"
|
|
166
|
+
: kind === "inner"
|
|
167
|
+
? "INNER"
|
|
168
|
+
: kind === "left"
|
|
169
|
+
? "LEFT"
|
|
170
|
+
: kind === "right"
|
|
171
|
+
? "RIGHT"
|
|
172
|
+
: "FULL",
|
|
173
|
+
);
|
|
174
|
+
this.expectWord("JOIN");
|
|
175
|
+
const table = this.fromItem();
|
|
176
|
+
joins.push(kind === "cross" ? Join.cross(table) : { kind, table, on: this.on() });
|
|
177
|
+
}
|
|
178
|
+
return joins;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private kind(): JoinKind | undefined {
|
|
182
|
+
return this.peekWord("CROSS")
|
|
183
|
+
? "cross"
|
|
184
|
+
: this.peekWord("INNER")
|
|
185
|
+
? "inner"
|
|
186
|
+
: this.peekWord("LEFT")
|
|
187
|
+
? "left"
|
|
188
|
+
: this.peekWord("RIGHT")
|
|
189
|
+
? "right"
|
|
190
|
+
: this.peekWord("FULL")
|
|
191
|
+
? "full"
|
|
192
|
+
: undefined;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private on(): IExpression {
|
|
196
|
+
this.expectWord("ON");
|
|
197
|
+
return this.expr();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private group(): string[] {
|
|
201
|
+
this.expectWord("BY");
|
|
202
|
+
return this.list(() => this.ident());
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private order(): OrderBy[] {
|
|
206
|
+
this.expectWord("BY");
|
|
207
|
+
return this.list(() => this.orderItem());
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private orderItem(): OrderBy {
|
|
211
|
+
const column = this.ident();
|
|
212
|
+
if (this.tryWord("DESC")) return OrderBy.desc(column);
|
|
213
|
+
this.expectWord("ASC");
|
|
214
|
+
return OrderBy.asc(column);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private list<T>(item: () => T): T[] {
|
|
218
|
+
const items = [item()];
|
|
219
|
+
while (this.tryChar(",")) items.push(item());
|
|
220
|
+
return items;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private expr(): IExpression {
|
|
224
|
+
let left = this.and();
|
|
225
|
+
while (this.tryWord("OR")) left = new Operator.Or(left, this.and());
|
|
226
|
+
return left;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private and(): IExpression {
|
|
230
|
+
let left = this.not();
|
|
231
|
+
while (this.tryWord("AND")) left = new Operator.And(left, this.not());
|
|
232
|
+
return left;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private not(): IExpression {
|
|
236
|
+
if (!this.peekWord("NOT") || !this.peekAfter("NOT", "(")) return this.predicate();
|
|
237
|
+
this.expectWord("NOT");
|
|
238
|
+
this.expectChar("(");
|
|
239
|
+
const inner = this.expr();
|
|
240
|
+
this.expectChar(")");
|
|
241
|
+
return new Operator.Not(inner);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private predicate(): IExpression {
|
|
245
|
+
if (this.tryWord("EXISTS")) return new Operator.Exists(new Subquery(this.nested()));
|
|
246
|
+
const left = this.primary();
|
|
247
|
+
if (this.tryWord("IS")) return this.nullTest(left);
|
|
248
|
+
if (this.tryWord("NOT")) return this.negated(left);
|
|
249
|
+
if (this.tryWord("IN")) return new Operator.In(left, this.inList());
|
|
250
|
+
if (this.tryWord("BETWEEN")) return this.bounds(left, false);
|
|
251
|
+
if (this.tryWord("LIKE")) return new Operator.Like(left, this.param());
|
|
252
|
+
if (this.tryText("<>")) return new Operator.NotEquals(left, this.primary());
|
|
253
|
+
if (this.tryText("<=")) return new Operator.LessThanOrEqual(left, this.primary());
|
|
254
|
+
if (this.tryText(">=")) return new Operator.GreaterThanOrEqual(left, this.primary());
|
|
255
|
+
if (this.tryChar("=")) return new Operator.Equals(left, this.primary());
|
|
256
|
+
if (this.tryChar("<")) return new Operator.LessThan(left, this.primary());
|
|
257
|
+
if (this.tryChar(">")) return new Operator.GreaterThan(left, this.primary());
|
|
258
|
+
return left;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private nullTest(left: IExpression): IExpression {
|
|
262
|
+
if (this.tryWord("NOT")) {
|
|
263
|
+
this.expectWord("NULL");
|
|
264
|
+
return new Operator.IsNotNull(left);
|
|
265
|
+
}
|
|
266
|
+
this.expectWord("NULL");
|
|
267
|
+
return new Operator.IsNull(left);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private negated(left: IExpression): IExpression {
|
|
271
|
+
if (this.tryWord("IN")) return new Operator.NotIn(left, this.inList());
|
|
272
|
+
if (this.tryWord("BETWEEN")) return this.bounds(left, true);
|
|
273
|
+
this.expectWord("LIKE");
|
|
274
|
+
return new Operator.NotLike(left, this.param());
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private bounds(left: IExpression, not: boolean): IExpression {
|
|
278
|
+
const low = this.param();
|
|
279
|
+
this.expectWord("AND");
|
|
280
|
+
const high = this.param();
|
|
281
|
+
return not ? new Operator.NotBetween(left, low, high) : new Operator.Between(left, low, high);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private inList(): IExpression {
|
|
285
|
+
this.expectChar("(");
|
|
286
|
+
if (this.peekWord("SELECT")) {
|
|
287
|
+
const query = this.selectStatement();
|
|
288
|
+
this.expectChar(")");
|
|
289
|
+
return new Subquery(query);
|
|
290
|
+
}
|
|
291
|
+
const items = [this.param()];
|
|
292
|
+
while (this.tryChar(",")) items.push(this.param());
|
|
293
|
+
this.expectChar(")");
|
|
294
|
+
return new ArrayExpression(items);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private primary(): IExpression {
|
|
298
|
+
if (this.tryChar("(")) {
|
|
299
|
+
if (this.peekWord("SELECT")) {
|
|
300
|
+
const query = this.selectStatement();
|
|
301
|
+
this.expectChar(")");
|
|
302
|
+
return new Subquery(query);
|
|
303
|
+
}
|
|
304
|
+
const inner = this.expr();
|
|
305
|
+
this.expectChar(")");
|
|
306
|
+
return inner;
|
|
307
|
+
}
|
|
308
|
+
if (this.tryChar("$")) {
|
|
309
|
+
this.ident();
|
|
310
|
+
return new Literal(null);
|
|
311
|
+
}
|
|
312
|
+
if (this.peekDigit()) return new Literal(this.integer());
|
|
313
|
+
if (this.tryWord("true")) return new Literal(true);
|
|
314
|
+
if (this.tryWord("false")) return new Literal(false);
|
|
315
|
+
if (this.tryWord("null")) return new Literal(null);
|
|
316
|
+
if (this.peek("'")) return this.string();
|
|
317
|
+
return new Identifier(this.ident());
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private param(): IExpression {
|
|
321
|
+
this.expectChar("$");
|
|
322
|
+
this.ident();
|
|
323
|
+
return new Literal(null);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private string(): IExpression {
|
|
327
|
+
this.expectChar("'");
|
|
328
|
+
let value = "";
|
|
329
|
+
while (this.i < this.text.length) {
|
|
330
|
+
if (this.text[this.i] === "'") {
|
|
331
|
+
if (this.text[this.i + 1] === "'") {
|
|
332
|
+
value += "'";
|
|
333
|
+
this.i += 2;
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
this.i++;
|
|
337
|
+
return new Literal(value);
|
|
338
|
+
}
|
|
339
|
+
value += this.text[this.i++];
|
|
340
|
+
}
|
|
341
|
+
return empty();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private ident(): string {
|
|
345
|
+
let name = this.part();
|
|
346
|
+
while (this.tryChar(".")) name += `.${this.part()}`;
|
|
347
|
+
return name;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private part(): string {
|
|
351
|
+
this.skip();
|
|
352
|
+
return this.peek('"') ? this.quoted() : this.name();
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
private quoted(): string {
|
|
356
|
+
this.expectChar('"');
|
|
357
|
+
let name = '"';
|
|
358
|
+
while (this.i < this.text.length) {
|
|
359
|
+
if (this.text[this.i] === '"') {
|
|
360
|
+
if (this.text[this.i + 1] === '"') {
|
|
361
|
+
name += '""';
|
|
362
|
+
this.i += 2;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
this.i++;
|
|
366
|
+
return `${name}"`;
|
|
367
|
+
}
|
|
368
|
+
name += this.text[this.i++];
|
|
369
|
+
}
|
|
370
|
+
return empty();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private name(): string {
|
|
374
|
+
this.skip();
|
|
375
|
+
if (this.i >= this.text.length || !/[A-Za-z]/.test(this.at(this.i))) empty();
|
|
376
|
+
const start = this.i++;
|
|
377
|
+
while (this.i < this.text.length && /[A-Za-z0-9_]/.test(this.at(this.i))) this.i++;
|
|
378
|
+
return this.text.slice(start, this.i);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
private integer(): number {
|
|
382
|
+
this.skip();
|
|
383
|
+
const start = this.i;
|
|
384
|
+
while (this.i < this.text.length && /[0-9]/.test(this.at(this.i))) this.i++;
|
|
385
|
+
if (start === this.i) empty();
|
|
386
|
+
return Number(this.text.slice(start, this.i));
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
private peekDigit(): boolean {
|
|
390
|
+
this.skip();
|
|
391
|
+
return /[0-9]/.test(this.at(this.i));
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
private peek(token: string): boolean {
|
|
395
|
+
this.skip();
|
|
396
|
+
return this.text[this.i] === token;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
private peekWord(word: string): boolean {
|
|
400
|
+
const mark = this.i;
|
|
401
|
+
const matched = this.tryWord(word);
|
|
402
|
+
this.i = mark;
|
|
403
|
+
return matched;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
private peekAfter(word: string, token: string): boolean {
|
|
407
|
+
const mark = this.i;
|
|
408
|
+
const matched = this.tryWord(word) && this.peek(token);
|
|
409
|
+
this.i = mark;
|
|
410
|
+
return matched;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
private tryWord(word: string): boolean {
|
|
414
|
+
this.skip();
|
|
415
|
+
if (this.i + word.length > this.text.length) return false;
|
|
416
|
+
if (this.text.slice(this.i, this.i + word.length).toLowerCase() !== word.toLowerCase()) return false;
|
|
417
|
+
const after = this.i + word.length;
|
|
418
|
+
if (/[A-Za-z0-9_]/.test(this.at(after))) return false;
|
|
419
|
+
this.i = after;
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
private tryChar(token: string): boolean {
|
|
424
|
+
this.skip();
|
|
425
|
+
if (this.text[this.i] !== token) return false;
|
|
426
|
+
this.i++;
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private tryText(token: string): boolean {
|
|
431
|
+
this.skip();
|
|
432
|
+
if (!this.text.startsWith(token, this.i)) return false;
|
|
433
|
+
this.i += token.length;
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
private expectChar(token: string): void {
|
|
438
|
+
if (!this.tryChar(token)) empty();
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
private expectWord(word: string): void {
|
|
442
|
+
if (!this.tryWord(word)) empty();
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
private skip(): void {
|
|
446
|
+
while (this.i < this.text.length && /\s/.test(this.at(this.i))) this.i++;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
private at(index: number): string {
|
|
450
|
+
return this.text[index] ?? "";
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export class StatementParser {
|
|
455
|
+
static parse(text: string): IStatement {
|
|
456
|
+
const cursor = new Cursor(text);
|
|
457
|
+
const statement = cursor.statement();
|
|
458
|
+
cursor.end();
|
|
459
|
+
return statement;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { IStatementVisitor } from "./IStatementVisitor.ts";
|
|
2
|
+
import { BulkDeleteStatement } from "./BulkDeleteStatement.ts";
|
|
3
|
+
import { BulkDeleteStatementTextRenderer } from "./BulkDeleteStatementTextRenderer.ts";
|
|
4
|
+
import { BulkInsertStatement } from "./BulkInsertStatement.ts";
|
|
5
|
+
import { BulkInsertStatementTextRenderer } from "./BulkInsertStatementTextRenderer.ts";
|
|
6
|
+
import { BulkUpdateStatement } from "./BulkUpdateStatement.ts";
|
|
7
|
+
import { BulkUpdateStatementTextRenderer } from "./BulkUpdateStatementTextRenderer.ts";
|
|
8
|
+
import { CreateIndexStatement } from "./CreateIndexStatement.ts";
|
|
9
|
+
import { CreateIndexStatementTextRenderer } from "./CreateIndexStatementTextRenderer.ts";
|
|
10
|
+
import { CreateTableStatement } from "./CreateTableStatement.ts";
|
|
11
|
+
import { CreateTableStatementTextRenderer } from "./CreateTableStatementTextRenderer.ts";
|
|
12
|
+
import { DeleteStatement } from "./DeleteStatement.ts";
|
|
13
|
+
import { DeleteStatementTextRenderer } from "./DeleteStatementTextRenderer.ts";
|
|
14
|
+
import { ExecuteStoredProcedureStatement } from "./ExecuteStoredProcedureStatement.ts";
|
|
15
|
+
import { ExecuteStoredProcedureStatementTextRenderer } from "./ExecuteStoredProcedureStatementTextRenderer.ts";
|
|
16
|
+
import { InsertStatement } from "./InsertStatement.ts";
|
|
17
|
+
import { InsertStatementTextRenderer } from "./InsertStatementTextRenderer.ts";
|
|
18
|
+
import { ReturnStatement } from "./ReturnStatement.ts";
|
|
19
|
+
import { ReturnStatementTextRenderer } from "./ReturnStatementTextRenderer.ts";
|
|
20
|
+
import { SelectInsertedIdStatement } from "./SelectInsertedIdStatement.ts";
|
|
21
|
+
import { SelectInsertedIdStatementTextRenderer } from "./SelectInsertedIdStatementTextRenderer.ts";
|
|
22
|
+
import { SelectStatement } from "./SelectStatement.ts";
|
|
23
|
+
import { SelectStatementTextRenderer } from "./SelectStatementTextRenderer.ts";
|
|
24
|
+
import type { StatementTextRenderer } from "./StatementTextRenderer.ts";
|
|
25
|
+
import { UpdateStatement } from "./UpdateStatement.ts";
|
|
26
|
+
import { UpdateStatementTextRenderer } from "./UpdateStatementTextRenderer.ts";
|
|
27
|
+
|
|
28
|
+
type AnyStatement =
|
|
29
|
+
| SelectStatement
|
|
30
|
+
| InsertStatement
|
|
31
|
+
| UpdateStatement
|
|
32
|
+
| DeleteStatement
|
|
33
|
+
| CreateTableStatement
|
|
34
|
+
| CreateIndexStatement
|
|
35
|
+
| ExecuteStoredProcedureStatement
|
|
36
|
+
| ReturnStatement
|
|
37
|
+
| SelectInsertedIdStatement
|
|
38
|
+
| BulkInsertStatement
|
|
39
|
+
| BulkUpdateStatement
|
|
40
|
+
| BulkDeleteStatement;
|
|
41
|
+
|
|
42
|
+
export class StatementRenderer implements IStatementVisitor {
|
|
43
|
+
constructor(protected readonly text: StatementTextRenderer) {}
|
|
44
|
+
|
|
45
|
+
visit(statement: SelectStatement): string;
|
|
46
|
+
visit(statement: InsertStatement): string;
|
|
47
|
+
visit(statement: UpdateStatement): string;
|
|
48
|
+
visit(statement: DeleteStatement): string;
|
|
49
|
+
visit(statement: CreateTableStatement): string;
|
|
50
|
+
visit(statement: CreateIndexStatement): string;
|
|
51
|
+
visit(statement: ExecuteStoredProcedureStatement): string;
|
|
52
|
+
visit(statement: ReturnStatement): string;
|
|
53
|
+
visit(statement: SelectInsertedIdStatement): string;
|
|
54
|
+
visit(statement: BulkInsertStatement): string;
|
|
55
|
+
visit(statement: BulkUpdateStatement): string;
|
|
56
|
+
visit(statement: BulkDeleteStatement): string;
|
|
57
|
+
visit(statement: AnyStatement): string {
|
|
58
|
+
if (statement instanceof SelectStatement) return this.visitSelect(statement);
|
|
59
|
+
if (statement instanceof InsertStatement) return this.visitInsert(statement);
|
|
60
|
+
if (statement instanceof UpdateStatement) return this.visitUpdate(statement);
|
|
61
|
+
if (statement instanceof DeleteStatement) return this.visitDelete(statement);
|
|
62
|
+
if (statement instanceof CreateTableStatement) return this.visitCreateTable(statement);
|
|
63
|
+
if (statement instanceof CreateIndexStatement) return this.visitCreateIndex(statement);
|
|
64
|
+
if (statement instanceof ExecuteStoredProcedureStatement)
|
|
65
|
+
return this.visitExecuteStoredProcedure(statement);
|
|
66
|
+
if (statement instanceof ReturnStatement) return this.visitReturn(statement);
|
|
67
|
+
if (statement instanceof BulkInsertStatement) return this.visitBulkInsert(statement);
|
|
68
|
+
if (statement instanceof BulkUpdateStatement) return this.visitBulkUpdate(statement);
|
|
69
|
+
if (statement instanceof BulkDeleteStatement) return this.visitBulkDelete(statement);
|
|
70
|
+
return this.visitSelectInsertedId(statement as SelectInsertedIdStatement);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected visitSelect(statement: SelectStatement): string {
|
|
74
|
+
return new SelectStatementTextRenderer(this.text).render(statement);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
protected visitInsert(statement: InsertStatement): string {
|
|
78
|
+
return new InsertStatementTextRenderer().render(statement);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected visitUpdate(statement: UpdateStatement): string {
|
|
82
|
+
return new UpdateStatementTextRenderer(this.text).render(statement);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
protected visitDelete(statement: DeleteStatement): string {
|
|
86
|
+
return new DeleteStatementTextRenderer(this.text).render(statement);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
protected visitCreateTable(statement: CreateTableStatement): string {
|
|
90
|
+
return new CreateTableStatementTextRenderer().render(statement);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
protected visitCreateIndex(statement: CreateIndexStatement): string {
|
|
94
|
+
return new CreateIndexStatementTextRenderer().render(statement);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
protected visitExecuteStoredProcedure(statement: ExecuteStoredProcedureStatement): string {
|
|
98
|
+
return new ExecuteStoredProcedureStatementTextRenderer().render(statement);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
protected visitReturn(statement: ReturnStatement): string {
|
|
102
|
+
return new ReturnStatementTextRenderer().render(statement);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
protected visitSelectInsertedId(statement: SelectInsertedIdStatement): string {
|
|
106
|
+
return new SelectInsertedIdStatementTextRenderer().render(statement);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
protected visitBulkInsert(statement: BulkInsertStatement): string {
|
|
110
|
+
return new BulkInsertStatementTextRenderer().render(statement);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
protected visitBulkUpdate(statement: BulkUpdateStatement): string {
|
|
114
|
+
return new BulkUpdateStatementTextRenderer().render(statement);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
protected visitBulkDelete(statement: BulkDeleteStatement): string {
|
|
118
|
+
return new BulkDeleteStatementTextRenderer().render(statement);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ICondition } from "./ICondition.ts";
|
|
2
|
+
import type { IConditionVisitor } from "./IConditionVisitor.ts";
|
|
3
|
+
import type { IStatement } from "./IStatement.ts";
|
|
4
|
+
import type { IStatementVisitor } from "./IStatementVisitor.ts";
|
|
5
|
+
import { ConditionRenderer } from "./ConditionRenderer.ts";
|
|
6
|
+
import { BulkDeleteStatement } from "./BulkDeleteStatement.ts";
|
|
7
|
+
import { BulkInsertStatement } from "./BulkInsertStatement.ts";
|
|
8
|
+
import { BulkUpdateStatement } from "./BulkUpdateStatement.ts";
|
|
9
|
+
import { CreateIndexStatement } from "./CreateIndexStatement.ts";
|
|
10
|
+
import { CreateTableStatement } from "./CreateTableStatement.ts";
|
|
11
|
+
import { DeleteStatement } from "./DeleteStatement.ts";
|
|
12
|
+
import { ExecuteStoredProcedureStatement } from "./ExecuteStoredProcedureStatement.ts";
|
|
13
|
+
import { ExpressionRenderer } from "./ExpressionRenderer.ts";
|
|
14
|
+
import { InsertStatement } from "./InsertStatement.ts";
|
|
15
|
+
import { ReturnStatement } from "./ReturnStatement.ts";
|
|
16
|
+
import { SelectInsertedIdStatement } from "./SelectInsertedIdStatement.ts";
|
|
17
|
+
import { SelectStatement } from "./SelectStatement.ts";
|
|
18
|
+
import { StatementRenderer } from "./StatementRenderer.ts";
|
|
19
|
+
import { UpdateStatement } from "./UpdateStatement.ts";
|
|
20
|
+
import { Where } from "./Where.ts";
|
|
21
|
+
import { WhereText } from "./WhereText.ts";
|
|
22
|
+
|
|
23
|
+
export class StatementTextRenderer {
|
|
24
|
+
protected readonly statements: IStatementVisitor;
|
|
25
|
+
protected readonly conditions: IConditionVisitor;
|
|
26
|
+
|
|
27
|
+
constructor(
|
|
28
|
+
statements?: (text: StatementTextRenderer) => StatementRenderer,
|
|
29
|
+
conditions?: (expressions: ExpressionRenderer) => ConditionRenderer,
|
|
30
|
+
expressions?: (text: StatementTextRenderer) => ExpressionRenderer,
|
|
31
|
+
) {
|
|
32
|
+
const expressionRenderer = expressions?.(this) ?? new ExpressionRenderer(this);
|
|
33
|
+
this.statements = statements?.(this) ?? new StatementRenderer(this);
|
|
34
|
+
this.conditions = conditions?.(expressionRenderer) ?? new ConditionRenderer(expressionRenderer);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
render(statement: IStatement): string;
|
|
38
|
+
render(condition: ICondition): string;
|
|
39
|
+
render(target: IStatement | ICondition): string {
|
|
40
|
+
if (target instanceof Where || target instanceof WhereText) return target.accept(this.conditions);
|
|
41
|
+
if (
|
|
42
|
+
target instanceof SelectStatement ||
|
|
43
|
+
target instanceof InsertStatement ||
|
|
44
|
+
target instanceof UpdateStatement ||
|
|
45
|
+
target instanceof DeleteStatement ||
|
|
46
|
+
target instanceof CreateTableStatement ||
|
|
47
|
+
target instanceof CreateIndexStatement ||
|
|
48
|
+
target instanceof ExecuteStoredProcedureStatement ||
|
|
49
|
+
target instanceof ReturnStatement ||
|
|
50
|
+
target instanceof SelectInsertedIdStatement ||
|
|
51
|
+
target instanceof BulkInsertStatement ||
|
|
52
|
+
target instanceof BulkUpdateStatement ||
|
|
53
|
+
target instanceof BulkDeleteStatement
|
|
54
|
+
)
|
|
55
|
+
return target.accept(this.statements);
|
|
56
|
+
return "where" in target || "fields" in target || "joins" in target || "column" in target
|
|
57
|
+
? (target as IStatement).accept(this.statements)
|
|
58
|
+
: (target as ICondition).accept(this.conditions);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Field } from "../builders/Field.ts";
|
|
2
|
+
import type { Parameter } from "../Parameter.ts";
|
|
3
|
+
import { DefaultStatementTextRenderer } from "./DefaultStatementTextRenderer.ts";
|
|
4
|
+
import type { IStatementVisitor } from "./IStatementVisitor.ts";
|
|
5
|
+
import type { IUpdateStatement } from "./IUpdateStatement.ts";
|
|
6
|
+
import type { IWhere } from "./IWhere.ts";
|
|
7
|
+
import { UpdateStatementTextRenderer } from "./UpdateStatementTextRenderer.ts";
|
|
8
|
+
|
|
9
|
+
export class UpdateStatement implements IUpdateStatement {
|
|
10
|
+
readonly table: string;
|
|
11
|
+
readonly fields: Field[];
|
|
12
|
+
readonly where?: IWhere;
|
|
13
|
+
|
|
14
|
+
constructor(table: string, fields: Field[], where?: IWhere) {
|
|
15
|
+
this.table = table;
|
|
16
|
+
this.fields = fields;
|
|
17
|
+
this.where = where;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
accept = (visitor: IStatementVisitor): string => visitor.visit(this);
|
|
21
|
+
|
|
22
|
+
get parameters(): Parameter[] {
|
|
23
|
+
return new UpdateStatementTextRenderer(DefaultStatementTextRenderer.instance)
|
|
24
|
+
.compose(
|
|
25
|
+
`UPDATE ${this.table} SET ${this.fields.map((item) => `${item.name} = $${item.name}`).join(", ")}`,
|
|
26
|
+
this.where,
|
|
27
|
+
this.fields,
|
|
28
|
+
).parameters;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static parameterList = (fields: Field[], where?: IWhere): Parameter[] =>
|
|
32
|
+
new UpdateStatementTextRenderer(DefaultStatementTextRenderer.instance).compose("", where, fields)
|
|
33
|
+
.parameters;
|
|
34
|
+
}
|