@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,92 @@
|
|
|
1
|
+
import type { CheckConstraint } from "./CheckConstraint.ts";
|
|
2
|
+
import type { Column } from "./Column.ts";
|
|
3
|
+
import type { CreateTableStatement } from "./CreateTableStatement.ts";
|
|
4
|
+
import type { ForeignKey, ReferentialAction } from "./ForeignKey.ts";
|
|
5
|
+
import type { ITableConstraint } from "./ITableConstraint.ts";
|
|
6
|
+
import type { NotNull } from "./NotNull.ts";
|
|
7
|
+
import type { PrimaryKey } from "./PrimaryKey.ts";
|
|
8
|
+
import type { UniqueConstraint } from "./UniqueConstraint.ts";
|
|
9
|
+
|
|
10
|
+
const actionKeyword: Record<ReferentialAction, string> = {
|
|
11
|
+
cascade: "CASCADE",
|
|
12
|
+
setNull: "SET NULL",
|
|
13
|
+
setDefault: "SET DEFAULT",
|
|
14
|
+
restrict: "RESTRICT",
|
|
15
|
+
noAction: "NO ACTION",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export class CreateTableStatementTextRenderer {
|
|
19
|
+
render(statement: CreateTableStatement): string {
|
|
20
|
+
return `CREATE TABLE ${this.quote(statement.table!)} (${this.parts(statement).join(", ")})${this.suffix()}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
protected quote(name: string): string {
|
|
24
|
+
return name;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected suffix(): string {
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
protected typeName(column: Column): string {
|
|
32
|
+
return column.type;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
protected columnDefault(column: Column): string {
|
|
36
|
+
return column.default == null
|
|
37
|
+
? ""
|
|
38
|
+
: column.default.name == null
|
|
39
|
+
? ` DEFAULT ${column.default.value}`
|
|
40
|
+
: ` CONSTRAINT ${this.quote(column.default.name)} DEFAULT ${column.default.value}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected identity(_column: Column): string {
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected column(column: Column): string {
|
|
48
|
+
return `${this.quote(column.name)} ${this.typeName(column)}${this.identity(column)}${this.columnDefault(column)}${this.notNullSql(column.notNull)}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected constraint(constraint: ITableConstraint): string {
|
|
52
|
+
return (
|
|
53
|
+
{
|
|
54
|
+
primaryKey: (item: PrimaryKey) =>
|
|
55
|
+
`CONSTRAINT ${this.quote(item.name)} PRIMARY KEY (${this.quoted(item.columns)})`,
|
|
56
|
+
foreignKey: (item: ForeignKey) => this.foreignKey(item),
|
|
57
|
+
unique: (item: UniqueConstraint) =>
|
|
58
|
+
`CONSTRAINT ${this.quote(item.name)} UNIQUE (${this.quoted(item.columns)})`,
|
|
59
|
+
check: (item: CheckConstraint) =>
|
|
60
|
+
`CONSTRAINT ${this.quote(item.name)} CHECK (${item.expression})`,
|
|
61
|
+
} as Record<ITableConstraint["kind"], (item: ITableConstraint) => string>
|
|
62
|
+
)[constraint.kind]?.(constraint) ??
|
|
63
|
+
(() => {
|
|
64
|
+
throw new Error("empty constraint");
|
|
65
|
+
})();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private parts(statement: CreateTableStatement): string[] {
|
|
69
|
+
return statement.constraints.length === 0
|
|
70
|
+
? statement.columns.map((column) => this.column(column))
|
|
71
|
+
: [
|
|
72
|
+
...statement.columns.map((column) => this.column(column)),
|
|
73
|
+
...statement.constraints.map((constraint) => this.constraint(constraint)),
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private notNullSql(value?: NotNull): string {
|
|
78
|
+
return value == null ? "" : value.name == null ? " NOT NULL" : ` CONSTRAINT ${this.quote(value.name)} NOT NULL`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private foreignKey(fk: ForeignKey): string {
|
|
82
|
+
return `CONSTRAINT ${this.quote(fk.name)} FOREIGN KEY (${this.quoted(fk.columns)}) REFERENCES ${this.quote(fk.referencesTable)} (${this.quoted(fk.referencesColumns)})${this.actionSql("DELETE", fk.onDelete)}${this.actionSql("UPDATE", fk.onUpdate)}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private quoted(names: string[]): string {
|
|
86
|
+
return names.map((name) => this.quote(name)).join(", ");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private actionSql(kind: string, action?: ReferentialAction): string {
|
|
90
|
+
return action == null ? "" : ` ON ${kind} ${actionKeyword[action]}`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IDeleteStatement } from "./IDeleteStatement.ts";
|
|
2
|
+
import type { IStatementVisitor } from "./IStatementVisitor.ts";
|
|
3
|
+
import type { IWhere } from "./IWhere.ts";
|
|
4
|
+
|
|
5
|
+
export class DeleteStatement implements IDeleteStatement {
|
|
6
|
+
readonly table: string;
|
|
7
|
+
readonly where?: IWhere;
|
|
8
|
+
|
|
9
|
+
constructor(table: string, where?: IWhere) {
|
|
10
|
+
this.table = table;
|
|
11
|
+
this.where = where;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
accept = (visitor: IStatementVisitor): string => visitor.visit(this);
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DeleteStatement } from "./DeleteStatement.ts";
|
|
2
|
+
import type { StatementTextRenderer } from "./StatementTextRenderer.ts";
|
|
3
|
+
|
|
4
|
+
export class DeleteStatementTextRenderer {
|
|
5
|
+
constructor(private readonly text: StatementTextRenderer) {}
|
|
6
|
+
|
|
7
|
+
render(statement: DeleteStatement): string {
|
|
8
|
+
return statement.where
|
|
9
|
+
? `DELETE FROM ${statement.table} WHERE ${this.text.render(statement.where)}`
|
|
10
|
+
: `DELETE FROM ${statement.table}`;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Parameter } from "../Parameter.ts";
|
|
2
|
+
import type { IExecuteStoredProcedureStatement } from "./IExecuteStoredProcedureStatement.ts";
|
|
3
|
+
import type { IStatementVisitor } from "./IStatementVisitor.ts";
|
|
4
|
+
|
|
5
|
+
export class ExecuteStoredProcedureStatement implements IExecuteStoredProcedureStatement {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly parameters: Parameter[];
|
|
8
|
+
|
|
9
|
+
constructor(name: string, parameters: Parameter[]) {
|
|
10
|
+
this.name = name;
|
|
11
|
+
this.parameters = parameters;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
accept = (visitor: IStatementVisitor): string => visitor.visit(this);
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ParameterDirection } from "../ParameterDirection.ts";
|
|
2
|
+
import type { Parameter } from "../Parameter.ts";
|
|
3
|
+
import type { ExecuteStoredProcedureStatement } from "./ExecuteStoredProcedureStatement.ts";
|
|
4
|
+
|
|
5
|
+
const args = (parameters: Parameter[]): string =>
|
|
6
|
+
parameters
|
|
7
|
+
.filter((parameter) => parameter.direction !== ParameterDirection.ReturnValue)
|
|
8
|
+
.map((parameter) => `$${parameter.name}`)
|
|
9
|
+
.join(", ");
|
|
10
|
+
|
|
11
|
+
export class ExecuteStoredProcedureStatementTextRenderer {
|
|
12
|
+
render(statement: ExecuteStoredProcedureStatement): string {
|
|
13
|
+
return `CALL ${statement.name}(${args(statement.parameters)})`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import type { Scalar } from "../builders/Scalar.ts";
|
|
2
|
+
import { ArrayExpression } from "./ArrayExpression.ts";
|
|
3
|
+
import { Identifier } from "./Identifier.ts";
|
|
4
|
+
import type { IExpression } from "./IExpression.ts";
|
|
5
|
+
import { Literal } from "./Literal.ts";
|
|
6
|
+
import { Operator } from "./Operator.ts";
|
|
7
|
+
import { Subquery } from "./Subquery.ts";
|
|
8
|
+
import { WhereText } from "./WhereText.ts";
|
|
9
|
+
import { Where } from "./Where.ts";
|
|
10
|
+
|
|
11
|
+
const isTrue = (value: Scalar): boolean => value === true;
|
|
12
|
+
|
|
13
|
+
const like = (text: Scalar, pattern: Scalar): boolean =>
|
|
14
|
+
text === null || pattern === null
|
|
15
|
+
? false
|
|
16
|
+
: typeof text === "string" && typeof pattern === "string"
|
|
17
|
+
? wild(text, pattern)
|
|
18
|
+
: (() => {
|
|
19
|
+
throw new Error("empty like");
|
|
20
|
+
})();
|
|
21
|
+
|
|
22
|
+
const wild = (text: string, pattern: string): boolean => {
|
|
23
|
+
let t = 0;
|
|
24
|
+
let p = 0;
|
|
25
|
+
let starT = -1;
|
|
26
|
+
let starP = -1;
|
|
27
|
+
while (t < text.length) {
|
|
28
|
+
if (p < pattern.length && (pattern[p] === "_" || pattern[p] === text[t])) {
|
|
29
|
+
t++;
|
|
30
|
+
p++;
|
|
31
|
+
} else if (p < pattern.length && pattern[p] === "%") {
|
|
32
|
+
starP = p++;
|
|
33
|
+
starT = t;
|
|
34
|
+
} else if (starP >= 0) {
|
|
35
|
+
p = starP + 1;
|
|
36
|
+
t = ++starT;
|
|
37
|
+
} else return false;
|
|
38
|
+
}
|
|
39
|
+
while (p < pattern.length && pattern[p] === "%") p++;
|
|
40
|
+
return p === pattern.length;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const between = (value: Scalar, low: Scalar, high: Scalar): boolean =>
|
|
44
|
+
value === null || low === null || high === null ? false : compare(value, low) >= 0 && compare(value, high) <= 0;
|
|
45
|
+
|
|
46
|
+
const equal = (left: Scalar, right: Scalar): boolean => left === right;
|
|
47
|
+
|
|
48
|
+
const compare = (left: Scalar, right: Scalar): number =>
|
|
49
|
+
typeof left === "number" && typeof right === "number"
|
|
50
|
+
? left - right
|
|
51
|
+
: typeof left === "bigint" && typeof right === "bigint"
|
|
52
|
+
? Number(left - right)
|
|
53
|
+
: typeof left === "string" && typeof right === "string"
|
|
54
|
+
? left < right
|
|
55
|
+
? -1
|
|
56
|
+
: left > right
|
|
57
|
+
? 1
|
|
58
|
+
: 0
|
|
59
|
+
: (() => {
|
|
60
|
+
throw new Error("empty compare");
|
|
61
|
+
})();
|
|
62
|
+
|
|
63
|
+
export class ExpressionEvaluator {
|
|
64
|
+
evaluate = (expression: IExpression, parameters?: Record<string, Scalar>): Scalar =>
|
|
65
|
+
expression instanceof Where
|
|
66
|
+
? this.evaluate(expression.condition, parameters)
|
|
67
|
+
: expression instanceof WhereText
|
|
68
|
+
? (() => {
|
|
69
|
+
throw new Error("text where is not evaluable");
|
|
70
|
+
})()
|
|
71
|
+
: expression instanceof Identifier
|
|
72
|
+
? (parameters?.[expression.name] ?? null)
|
|
73
|
+
: expression instanceof Literal
|
|
74
|
+
? expression.value
|
|
75
|
+
: expression instanceof Operator.Not
|
|
76
|
+
? !isTrue(this.evaluate(expression.argument, parameters))
|
|
77
|
+
: expression instanceof Operator.IsNull
|
|
78
|
+
? this.evaluate(expression.argument, parameters) === null
|
|
79
|
+
: expression instanceof Operator.IsNotNull
|
|
80
|
+
? this.evaluate(expression.argument, parameters) !== null
|
|
81
|
+
: expression instanceof Operator.Like
|
|
82
|
+
? like(this.evaluate(expression.left, parameters), this.evaluate(expression.right, parameters))
|
|
83
|
+
: expression instanceof Operator.NotLike
|
|
84
|
+
? !like(this.evaluate(expression.left, parameters), this.evaluate(expression.right, parameters))
|
|
85
|
+
: expression instanceof Operator.Between
|
|
86
|
+
? between(
|
|
87
|
+
this.evaluate(expression.value, parameters),
|
|
88
|
+
this.evaluate(expression.low, parameters),
|
|
89
|
+
this.evaluate(expression.high, parameters),
|
|
90
|
+
)
|
|
91
|
+
: expression instanceof Operator.NotBetween
|
|
92
|
+
? !between(
|
|
93
|
+
this.evaluate(expression.value, parameters),
|
|
94
|
+
this.evaluate(expression.low, parameters),
|
|
95
|
+
this.evaluate(expression.high, parameters),
|
|
96
|
+
)
|
|
97
|
+
: expression instanceof Operator.NotIn
|
|
98
|
+
? expression.right instanceof ArrayExpression
|
|
99
|
+
? !expression.right.elements.some((element) =>
|
|
100
|
+
equal(
|
|
101
|
+
this.evaluate(expression.left, parameters),
|
|
102
|
+
this.evaluate(element, parameters),
|
|
103
|
+
),
|
|
104
|
+
)
|
|
105
|
+
: (() => {
|
|
106
|
+
throw new Error("empty in");
|
|
107
|
+
})()
|
|
108
|
+
: expression instanceof Operator.Exists || expression instanceof Subquery
|
|
109
|
+
? (() => {
|
|
110
|
+
throw new Error("subquery is not evaluable");
|
|
111
|
+
})()
|
|
112
|
+
: expression instanceof Operator.And
|
|
113
|
+
? isTrue(this.evaluate(expression.left, parameters)) &&
|
|
114
|
+
isTrue(this.evaluate(expression.right, parameters))
|
|
115
|
+
: expression instanceof Operator.Or
|
|
116
|
+
? isTrue(this.evaluate(expression.left, parameters)) ||
|
|
117
|
+
isTrue(this.evaluate(expression.right, parameters))
|
|
118
|
+
: expression instanceof Operator.In
|
|
119
|
+
? expression.right instanceof ArrayExpression
|
|
120
|
+
? expression.right.elements.some((element) =>
|
|
121
|
+
equal(
|
|
122
|
+
this.evaluate(expression.left, parameters),
|
|
123
|
+
this.evaluate(element, parameters),
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
: (() => {
|
|
127
|
+
throw new Error("empty in");
|
|
128
|
+
})()
|
|
129
|
+
: expression instanceof Operator.Equals
|
|
130
|
+
? equal(
|
|
131
|
+
this.evaluate(expression.left, parameters),
|
|
132
|
+
this.evaluate(expression.right, parameters),
|
|
133
|
+
)
|
|
134
|
+
: expression instanceof Operator.NotEquals
|
|
135
|
+
? !equal(
|
|
136
|
+
this.evaluate(expression.left, parameters),
|
|
137
|
+
this.evaluate(expression.right, parameters),
|
|
138
|
+
)
|
|
139
|
+
: expression instanceof Operator.GreaterThan
|
|
140
|
+
? compare(
|
|
141
|
+
this.evaluate(expression.left, parameters),
|
|
142
|
+
this.evaluate(expression.right, parameters),
|
|
143
|
+
) > 0
|
|
144
|
+
: expression instanceof Operator.LessThan
|
|
145
|
+
? compare(
|
|
146
|
+
this.evaluate(expression.left, parameters),
|
|
147
|
+
this.evaluate(expression.right, parameters),
|
|
148
|
+
) < 0
|
|
149
|
+
: expression instanceof Operator.GreaterThanOrEqual
|
|
150
|
+
? compare(
|
|
151
|
+
this.evaluate(expression.left, parameters),
|
|
152
|
+
this.evaluate(expression.right, parameters),
|
|
153
|
+
) >= 0
|
|
154
|
+
: expression instanceof Operator.LessThanOrEqual
|
|
155
|
+
? compare(
|
|
156
|
+
this.evaluate(expression.left, parameters),
|
|
157
|
+
this.evaluate(expression.right, parameters),
|
|
158
|
+
) <= 0
|
|
159
|
+
: expression instanceof ArrayExpression
|
|
160
|
+
? (() => {
|
|
161
|
+
throw new Error("array is not a scalar");
|
|
162
|
+
})()
|
|
163
|
+
: (() => {
|
|
164
|
+
throw new Error("empty expression");
|
|
165
|
+
})();
|
|
166
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { Cell } from "../converters/Cell.ts";
|
|
2
|
+
import { ArrayExpression } from "./ArrayExpression.ts";
|
|
3
|
+
import type { IExpression } from "./IExpression.ts";
|
|
4
|
+
import { Identifier } from "./Identifier.ts";
|
|
5
|
+
import { Literal } from "./Literal.ts";
|
|
6
|
+
import { Operator } from "./Operator.ts";
|
|
7
|
+
import type { StatementTextRenderer } from "./StatementTextRenderer.ts";
|
|
8
|
+
import { Subquery } from "./Subquery.ts";
|
|
9
|
+
import { Where } from "./Where.ts";
|
|
10
|
+
|
|
11
|
+
export class ExpressionRenderer {
|
|
12
|
+
constructor(private readonly text: StatementTextRenderer) {}
|
|
13
|
+
|
|
14
|
+
render = (expression: IExpression): string => {
|
|
15
|
+
if (expression instanceof Where) return this.render(expression.condition);
|
|
16
|
+
if (expression instanceof Identifier) return expression.name;
|
|
17
|
+
if (expression instanceof Literal) return inline(expression.value);
|
|
18
|
+
if (expression instanceof ArrayExpression) return expression.elements.map((e) => this.render(e)).join(", ");
|
|
19
|
+
if (expression instanceof Operator.Not) return `NOT (${this.render(expression.argument)})`;
|
|
20
|
+
if (expression instanceof Operator.IsNull) return `${this.render(expression.argument)} IS NULL`;
|
|
21
|
+
if (expression instanceof Operator.IsNotNull) return `${this.render(expression.argument)} IS NOT NULL`;
|
|
22
|
+
if (expression instanceof Operator.Exists) return `EXISTS ${this.render(expression.argument)}`;
|
|
23
|
+
if (expression instanceof Subquery) return `(${this.text.render(expression.query)})`;
|
|
24
|
+
if (
|
|
25
|
+
expression instanceof Operator.In &&
|
|
26
|
+
expression.left instanceof Identifier &&
|
|
27
|
+
expression.right instanceof ArrayExpression
|
|
28
|
+
)
|
|
29
|
+
return expression.right.elements.length === 0
|
|
30
|
+
? "1 = 0"
|
|
31
|
+
: `${expression.left.name} IN (${slots(expression.left.name, expression.right.elements.length)})`;
|
|
32
|
+
if (
|
|
33
|
+
expression instanceof Operator.In &&
|
|
34
|
+
expression.left instanceof Identifier &&
|
|
35
|
+
expression.right instanceof Subquery
|
|
36
|
+
)
|
|
37
|
+
return `${expression.left.name} IN ${this.render(expression.right)}`;
|
|
38
|
+
if (
|
|
39
|
+
expression instanceof Operator.NotIn &&
|
|
40
|
+
expression.left instanceof Identifier &&
|
|
41
|
+
expression.right instanceof ArrayExpression
|
|
42
|
+
)
|
|
43
|
+
return expression.right.elements.length === 0
|
|
44
|
+
? "1 = 1"
|
|
45
|
+
: `${expression.left.name} NOT IN (${slots(expression.left.name, expression.right.elements.length)})`;
|
|
46
|
+
if (
|
|
47
|
+
expression instanceof Operator.NotIn &&
|
|
48
|
+
expression.left instanceof Identifier &&
|
|
49
|
+
expression.right instanceof Subquery
|
|
50
|
+
)
|
|
51
|
+
return `${expression.left.name} NOT IN ${this.render(expression.right)}`;
|
|
52
|
+
if (
|
|
53
|
+
(expression instanceof Operator.Between || expression instanceof Operator.NotBetween) &&
|
|
54
|
+
expression.value instanceof Identifier &&
|
|
55
|
+
expression.low instanceof Literal &&
|
|
56
|
+
expression.high instanceof Literal
|
|
57
|
+
) {
|
|
58
|
+
const word = expression instanceof Operator.NotBetween ? "NOT BETWEEN" : "BETWEEN";
|
|
59
|
+
return `${expression.value.name} ${word} $${parameterName(expression.value.name)}_0 AND $${parameterName(expression.value.name)}_1`;
|
|
60
|
+
}
|
|
61
|
+
if (expression instanceof Operator.Between)
|
|
62
|
+
return `${this.render(expression.value)} BETWEEN ${this.render(expression.low)} AND ${this.render(expression.high)}`;
|
|
63
|
+
if (expression instanceof Operator.NotBetween)
|
|
64
|
+
return `${this.render(expression.value)} NOT BETWEEN ${this.render(expression.low)} AND ${this.render(expression.high)}`;
|
|
65
|
+
if (expression instanceof Operator.And)
|
|
66
|
+
return `${this.wrap(expression.left, true)} AND ${this.wrap(expression.right, true)}`;
|
|
67
|
+
if (expression instanceof Operator.Or)
|
|
68
|
+
return `${this.wrap(expression.left, false)} OR ${this.wrap(expression.right, false)}`;
|
|
69
|
+
const operands = expression instanceof Operator ? pair(expression) : undefined;
|
|
70
|
+
if (operands && operands.left instanceof Identifier && operands.right instanceof Literal)
|
|
71
|
+
return `${operands.left.name} ${symbol(expression as Operator)} $${parameterName(operands.left.name)}`;
|
|
72
|
+
if (operands && operands.left instanceof Literal && operands.right instanceof Literal)
|
|
73
|
+
return `${inline(operands.left.value)} ${symbol(expression as Operator)} ${inline(operands.right.value)}`;
|
|
74
|
+
if (operands)
|
|
75
|
+
return `${this.render(operands.left)} ${symbol(expression as Operator)} ${this.render(operands.right)}`;
|
|
76
|
+
throw new Error("empty expression");
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
private wrap = (child: IExpression, and: boolean): string =>
|
|
80
|
+
needsParens(child, and) ? `(${this.render(child)})` : this.render(child);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const symbol = (op: Operator): string =>
|
|
84
|
+
op instanceof Operator.Equals
|
|
85
|
+
? "="
|
|
86
|
+
: op instanceof Operator.NotEquals
|
|
87
|
+
? "<>"
|
|
88
|
+
: op instanceof Operator.GreaterThan
|
|
89
|
+
? ">"
|
|
90
|
+
: op instanceof Operator.LessThan
|
|
91
|
+
? "<"
|
|
92
|
+
: op instanceof Operator.GreaterThanOrEqual
|
|
93
|
+
? ">="
|
|
94
|
+
: op instanceof Operator.LessThanOrEqual
|
|
95
|
+
? "<="
|
|
96
|
+
: op instanceof Operator.Like
|
|
97
|
+
? "LIKE"
|
|
98
|
+
: op instanceof Operator.NotLike
|
|
99
|
+
? "NOT LIKE"
|
|
100
|
+
: "IN";
|
|
101
|
+
|
|
102
|
+
const inline = (value: Cell): string =>
|
|
103
|
+
value === null
|
|
104
|
+
? "null"
|
|
105
|
+
: typeof value === "boolean"
|
|
106
|
+
? String(value)
|
|
107
|
+
: typeof value === "string"
|
|
108
|
+
? `'${value.replace(/'/g, "''")}'`
|
|
109
|
+
: String(value);
|
|
110
|
+
|
|
111
|
+
const needsParens = (child: IExpression, and: boolean): boolean =>
|
|
112
|
+
child instanceof Where
|
|
113
|
+
? needsParens(child.condition, and)
|
|
114
|
+
: child instanceof Operator.Not ||
|
|
115
|
+
child instanceof Operator.In ||
|
|
116
|
+
child instanceof Operator.NotIn ||
|
|
117
|
+
(child instanceof Operator.And && !and) ||
|
|
118
|
+
(child instanceof Operator.Or && and);
|
|
119
|
+
|
|
120
|
+
const pair = (
|
|
121
|
+
expression: Operator,
|
|
122
|
+
): { left: IExpression; right: IExpression } | undefined =>
|
|
123
|
+
expression instanceof Operator.Equals ||
|
|
124
|
+
expression instanceof Operator.NotEquals ||
|
|
125
|
+
expression instanceof Operator.GreaterThan ||
|
|
126
|
+
expression instanceof Operator.LessThan ||
|
|
127
|
+
expression instanceof Operator.GreaterThanOrEqual ||
|
|
128
|
+
expression instanceof Operator.LessThanOrEqual ||
|
|
129
|
+
expression instanceof Operator.And ||
|
|
130
|
+
expression instanceof Operator.Or ||
|
|
131
|
+
expression instanceof Operator.In ||
|
|
132
|
+
expression instanceof Operator.Like ||
|
|
133
|
+
expression instanceof Operator.NotLike
|
|
134
|
+
? expression
|
|
135
|
+
: undefined;
|
|
136
|
+
|
|
137
|
+
const parameterName = (name: string): string =>
|
|
138
|
+
parts(name)
|
|
139
|
+
.map((part) =>
|
|
140
|
+
part.length >= 2 && part.startsWith('"') && part.endsWith('"')
|
|
141
|
+
? part.slice(1, -1).replace(/""/g, '"')
|
|
142
|
+
: part,
|
|
143
|
+
)
|
|
144
|
+
.join(".");
|
|
145
|
+
|
|
146
|
+
const parts = (name: string): string[] => {
|
|
147
|
+
const found: string[] = [];
|
|
148
|
+
let start = 0;
|
|
149
|
+
let quoted = false;
|
|
150
|
+
for (let i = 0; i < name.length; i++) {
|
|
151
|
+
if (name[i] === '"') quoted = !quoted;
|
|
152
|
+
if (name[i] !== "." || quoted) continue;
|
|
153
|
+
found.push(name.slice(start, i));
|
|
154
|
+
start = i + 1;
|
|
155
|
+
}
|
|
156
|
+
found.push(name.slice(start));
|
|
157
|
+
return found;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const slots = (name: string, count: number): string =>
|
|
161
|
+
Array.from({ length: count }, (_, i) => `$${parameterName(name)}_${i}`).join(", ");
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ITableConstraint } from "./ITableConstraint.ts";
|
|
2
|
+
|
|
3
|
+
export const ReferentialAction = {
|
|
4
|
+
Cascade: "cascade",
|
|
5
|
+
SetNull: "setNull",
|
|
6
|
+
SetDefault: "setDefault",
|
|
7
|
+
Restrict: "restrict",
|
|
8
|
+
NoAction: "noAction",
|
|
9
|
+
} as const;
|
|
10
|
+
|
|
11
|
+
export type ReferentialAction = (typeof ReferentialAction)[keyof typeof ReferentialAction];
|
|
12
|
+
|
|
13
|
+
export class ForeignKey implements ITableConstraint {
|
|
14
|
+
readonly kind = "foreignKey" as const;
|
|
15
|
+
constructor(
|
|
16
|
+
readonly name: string,
|
|
17
|
+
readonly columns: string[],
|
|
18
|
+
readonly referencesTable: string,
|
|
19
|
+
readonly referencesColumns: string[],
|
|
20
|
+
readonly onDelete?: ReferentialAction,
|
|
21
|
+
readonly onUpdate?: ReferentialAction,
|
|
22
|
+
) {}
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export interface IExpression {}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { BulkDeleteStatement } from "./BulkDeleteStatement.ts";
|
|
2
|
+
import type { BulkInsertStatement } from "./BulkInsertStatement.ts";
|
|
3
|
+
import type { BulkUpdateStatement } from "./BulkUpdateStatement.ts";
|
|
4
|
+
import type { CreateIndexStatement } from "./CreateIndexStatement.ts";
|
|
5
|
+
import type { CreateTableStatement } from "./CreateTableStatement.ts";
|
|
6
|
+
import type { DeleteStatement } from "./DeleteStatement.ts";
|
|
7
|
+
import type { ExecuteStoredProcedureStatement } from "./ExecuteStoredProcedureStatement.ts";
|
|
8
|
+
import type { InsertStatement } from "./InsertStatement.ts";
|
|
9
|
+
import type { ReturnStatement } from "./ReturnStatement.ts";
|
|
10
|
+
import type { SelectInsertedIdStatement } from "./SelectInsertedIdStatement.ts";
|
|
11
|
+
import type { SelectStatement } from "./SelectStatement.ts";
|
|
12
|
+
import type { UpdateStatement } from "./UpdateStatement.ts";
|
|
13
|
+
|
|
14
|
+
export interface IStatementVisitor {
|
|
15
|
+
visit(statement: SelectStatement): string;
|
|
16
|
+
visit(statement: InsertStatement): string;
|
|
17
|
+
visit(statement: UpdateStatement): string;
|
|
18
|
+
visit(statement: DeleteStatement): string;
|
|
19
|
+
visit(statement: CreateTableStatement): string;
|
|
20
|
+
visit(statement: CreateIndexStatement): string;
|
|
21
|
+
visit(statement: ExecuteStoredProcedureStatement): string;
|
|
22
|
+
visit(statement: ReturnStatement): string;
|
|
23
|
+
visit(statement: SelectInsertedIdStatement): string;
|
|
24
|
+
visit(statement: BulkInsertStatement): string;
|
|
25
|
+
visit(statement: BulkUpdateStatement): string;
|
|
26
|
+
visit(statement: BulkDeleteStatement): string;
|
|
27
|
+
}
|