@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,27 @@
|
|
|
1
|
+
import { columnType as identityColumnType } from "../IDriver.ts";
|
|
2
|
+
import type { DatasourceOptions } from "../DatasourceOptions.ts";
|
|
3
|
+
import type { ICommand } from "../ICommand.ts";
|
|
4
|
+
import type { IConnection } from "../IConnection.ts";
|
|
5
|
+
import type { IDriver } from "../IDriver.ts";
|
|
6
|
+
import type { ITransaction } from "../ITransaction.ts";
|
|
7
|
+
import type { IsolationLevel } from "../IsolationLevel.ts";
|
|
8
|
+
import type { Cell } from "../converters/Cell.ts";
|
|
9
|
+
import type { TypeConverter } from "../converters/TypeConverter.ts";
|
|
10
|
+
import { MockCommand } from "./mock-command.ts";
|
|
11
|
+
import { MockConnection } from "./mock-connection.ts";
|
|
12
|
+
import { MockTransaction } from "./mock-transaction.ts";
|
|
13
|
+
|
|
14
|
+
export class MockDriver implements IDriver {
|
|
15
|
+
rowTypeConverters: Record<string, TypeConverter<Cell, Cell>> = {};
|
|
16
|
+
columnType = identityColumnType;
|
|
17
|
+
connection = (_options?: DatasourceOptions): IConnection => new MockConnection();
|
|
18
|
+
command = (connection: IConnection): ICommand => new MockCommand(connection);
|
|
19
|
+
transaction = (
|
|
20
|
+
connection: IConnection,
|
|
21
|
+
isolation: IsolationLevel = "readCommitted",
|
|
22
|
+
): ITransaction => {
|
|
23
|
+
const txn = new MockTransaction(connection);
|
|
24
|
+
txn.isolation = isolation;
|
|
25
|
+
return txn;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { IReader } from "../IReader.ts";
|
|
2
|
+
import { readerToAsyncIterable } from "../readerToAsyncIterable.ts";
|
|
3
|
+
|
|
4
|
+
export class MockReader<T = unknown> implements IReader<T> {
|
|
5
|
+
current: T | undefined = undefined;
|
|
6
|
+
nextCalls = 0;
|
|
7
|
+
nextError?: unknown;
|
|
8
|
+
private index = -1;
|
|
9
|
+
|
|
10
|
+
constructor(public rows: readonly T[] = []) {}
|
|
11
|
+
|
|
12
|
+
async next(): Promise<boolean> {
|
|
13
|
+
this.nextCalls += 1;
|
|
14
|
+
if (this.nextError) {
|
|
15
|
+
throw this.nextError;
|
|
16
|
+
}
|
|
17
|
+
this.index += 1;
|
|
18
|
+
if (this.index < this.rows.length) {
|
|
19
|
+
this.current = this.rows[this.index];
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
this.current = undefined;
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
read(): AsyncIterable<T> {
|
|
27
|
+
return readerToAsyncIterable(this);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async nextResult(): Promise<boolean> {
|
|
31
|
+
this.current = undefined;
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async dispose(): Promise<void> {}
|
|
36
|
+
|
|
37
|
+
[Symbol.asyncDispose] = (): Promise<void> => this.dispose();
|
|
38
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Scalar } from "../builders/Scalar.ts";
|
|
2
|
+
import type { Cells } from "../converters/Cell.ts";
|
|
3
|
+
import type { Constructable } from "../converters/Constructable.ts";
|
|
4
|
+
import type { IConnection } from "../IConnection.ts";
|
|
5
|
+
import type { Parameter } from "../Parameter.ts";
|
|
6
|
+
import type { IExecuteStoredProcedureStatement } from "../statements/IExecuteStoredProcedureStatement.ts";
|
|
7
|
+
import type { ISelectStatement } from "../statements/ISelectStatement.ts";
|
|
8
|
+
import type { ICRUDRepository } from "../ICRUDRepository.ts";
|
|
9
|
+
import { ReadonlyRepository, untyped } from "../ReadonlyRepository.ts";
|
|
10
|
+
import type { ReadonlyRepositoryMappingOptions } from "../ReadonlyRepositoryMappingOptions.ts";
|
|
11
|
+
import type { Response } from "../Response.ts";
|
|
12
|
+
import type { IWhere } from "../statements/IWhere.ts";
|
|
13
|
+
|
|
14
|
+
export class MockRepository<T = unknown> extends ReadonlyRepository<T> implements ICRUDRepository<T> {
|
|
15
|
+
readonly ids: readonly string[] = [];
|
|
16
|
+
items: T[] = [];
|
|
17
|
+
readSingleCalls: Array<ISelectStatement | IWhere | IExecuteStoredProcedureStatement | undefined> = [];
|
|
18
|
+
readCalls: Array<ISelectStatement | IWhere | IExecuteStoredProcedureStatement | undefined> = [];
|
|
19
|
+
addCalls: T[] = [];
|
|
20
|
+
updateCalls: Array<{ item: T; byWhere?: IWhere }> = [];
|
|
21
|
+
deleteCalls: Array<{ item: T; byWhere?: IWhere }> = [];
|
|
22
|
+
readSingleError?: unknown;
|
|
23
|
+
readError?: unknown;
|
|
24
|
+
addError?: unknown;
|
|
25
|
+
updateError?: unknown;
|
|
26
|
+
deleteError?: unknown;
|
|
27
|
+
addResponse: Response = { result: undefined, rowsAffected: 1 };
|
|
28
|
+
updateResponse: Response = { result: undefined, rowsAffected: 1 };
|
|
29
|
+
deleteResponse: Response = { result: undefined, rowsAffected: 1 };
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
_connection: IConnection,
|
|
33
|
+
type: Constructable<T> = untyped<T>(),
|
|
34
|
+
) {
|
|
35
|
+
super(type);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async readSingle(
|
|
39
|
+
query?: ISelectStatement | IWhere | IExecuteStoredProcedureStatement,
|
|
40
|
+
_parameters?: Parameter[],
|
|
41
|
+
_options?: ReadonlyRepositoryMappingOptions<T>,
|
|
42
|
+
): Promise<T | undefined> {
|
|
43
|
+
this.readSingleCalls.push(query);
|
|
44
|
+
if (this.readSingleError) {
|
|
45
|
+
throw this.readSingleError;
|
|
46
|
+
}
|
|
47
|
+
return this.items[0];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async *read(
|
|
51
|
+
query?: ISelectStatement | IWhere | IExecuteStoredProcedureStatement,
|
|
52
|
+
_parameters?: Parameter[],
|
|
53
|
+
_options?: ReadonlyRepositoryMappingOptions<T>,
|
|
54
|
+
): AsyncIterable<T> {
|
|
55
|
+
this.readCalls.push(query);
|
|
56
|
+
if (this.readError) {
|
|
57
|
+
throw this.readError;
|
|
58
|
+
}
|
|
59
|
+
for (const item of this.items) {
|
|
60
|
+
yield item;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async create(item: T): Promise<T> {
|
|
65
|
+
this.addCalls.push(item);
|
|
66
|
+
if (this.addError) {
|
|
67
|
+
throw this.addError;
|
|
68
|
+
}
|
|
69
|
+
this.items.push(item);
|
|
70
|
+
this.addResponse = { result: item, rowsAffected: 1 };
|
|
71
|
+
return item;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async update(item: T, byWhere?: IWhere): Promise<Response> {
|
|
75
|
+
this.updateCalls.push({ item, byWhere });
|
|
76
|
+
if (this.updateError) {
|
|
77
|
+
throw this.updateError;
|
|
78
|
+
}
|
|
79
|
+
return this.updateResponse;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async delete(item: T, byWhere?: IWhere): Promise<Response> {
|
|
83
|
+
this.deleteCalls.push({ item, byWhere });
|
|
84
|
+
if (this.deleteError) {
|
|
85
|
+
throw this.deleteError;
|
|
86
|
+
}
|
|
87
|
+
return this.deleteResponse;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
readSingleById = (_id: Scalar | Cells): Promise<T | undefined> => this.readSingle();
|
|
91
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ISelectStatement } from "../statements/ISelectStatement.ts";
|
|
2
|
+
import type { IStatementVisitor } from "../statements/IStatementVisitor.ts";
|
|
3
|
+
import type { IWhere } from "../statements/IWhere.ts";
|
|
4
|
+
|
|
5
|
+
export class MockSelectStatement implements ISelectStatement {
|
|
6
|
+
acceptCalls = 0;
|
|
7
|
+
|
|
8
|
+
constructor(
|
|
9
|
+
public text: string,
|
|
10
|
+
public where?: IWhere,
|
|
11
|
+
) {}
|
|
12
|
+
|
|
13
|
+
accept(_visitor: IStatementVisitor): string {
|
|
14
|
+
this.acceptCalls += 1;
|
|
15
|
+
return this.text;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { IConnection } from "../IConnection.ts";
|
|
2
|
+
import type { ITransaction } from "../ITransaction.ts";
|
|
3
|
+
import { IsolationLevel } from "../IsolationLevel.ts";
|
|
4
|
+
|
|
5
|
+
export class MockTransaction implements ITransaction {
|
|
6
|
+
commitCalls = 0;
|
|
7
|
+
abortCalls = 0;
|
|
8
|
+
commitError?: unknown;
|
|
9
|
+
abortError?: unknown;
|
|
10
|
+
committed = false;
|
|
11
|
+
aborted = false;
|
|
12
|
+
isolation: IsolationLevel = IsolationLevel.ReadCommitted;
|
|
13
|
+
|
|
14
|
+
constructor(readonly connection: IConnection) {}
|
|
15
|
+
|
|
16
|
+
async commit(): Promise<void> {
|
|
17
|
+
this.commitCalls += 1;
|
|
18
|
+
if (this.commitError) {
|
|
19
|
+
throw this.commitError;
|
|
20
|
+
}
|
|
21
|
+
this.committed = true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async abort(): Promise<void> {
|
|
25
|
+
this.abortCalls += 1;
|
|
26
|
+
if (this.abortError) {
|
|
27
|
+
throw this.abortError;
|
|
28
|
+
}
|
|
29
|
+
this.aborted = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IConditionVisitor } from "../statements/IConditionVisitor.ts";
|
|
2
|
+
import type { IWhere } from "../statements/IWhere.ts";
|
|
3
|
+
|
|
4
|
+
export class MockWhere implements IWhere {
|
|
5
|
+
acceptCalls = 0;
|
|
6
|
+
|
|
7
|
+
constructor(public text: string) {}
|
|
8
|
+
|
|
9
|
+
accept(_visitor: IConditionVisitor): string {
|
|
10
|
+
this.acceptCalls += 1;
|
|
11
|
+
return this.text;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Parameter } from "./Parameter.ts";
|
|
2
|
+
|
|
3
|
+
export function parametersToRecord<T>(
|
|
4
|
+
parameters?: Parameter<T>[] | null,
|
|
5
|
+
): Record<string, T> {
|
|
6
|
+
if (!parameters) return {};
|
|
7
|
+
const record: Record<string, T> = {};
|
|
8
|
+
for (const parameter of parameters)
|
|
9
|
+
if (parameter.value !== undefined) record[parameter.name] = parameter.value;
|
|
10
|
+
return record;
|
|
11
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const quoteOf = (char: string): "'" | "\"" | "`" | undefined =>
|
|
2
|
+
char === "'" || char === "\"" || char === "`" ? char : undefined;
|
|
3
|
+
|
|
4
|
+
const word = (sql: string, i: number, token: string): boolean => {
|
|
5
|
+
if (!sql.startsWith(token, i)) return false;
|
|
6
|
+
const before = i === 0 || !/[0-9A-Za-z_]/.test(sql[i - 1]!);
|
|
7
|
+
const after = i + token.length === sql.length || !/[0-9A-Za-z_]/.test(sql[i + token.length]!);
|
|
8
|
+
return before && after;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const dollarTag = (sql: string, i: number): string | undefined => {
|
|
12
|
+
if (sql[i] !== "$") return undefined;
|
|
13
|
+
let end = i + 1;
|
|
14
|
+
while (end < sql.length && /[0-9A-Za-z_]/.test(sql[end]!)) end += 1;
|
|
15
|
+
return sql[end] === "$" ? sql.slice(i, end + 1) : undefined;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const routine = (sql: string, start: number, i: number): boolean => {
|
|
19
|
+
const head = sql.slice(start, i).toUpperCase();
|
|
20
|
+
return head.includes("CREATE PROCEDURE") || head.includes("CREATE FUNCTION");
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const splitStatements = (sql: string): string[] => {
|
|
24
|
+
const parts: string[] = [];
|
|
25
|
+
let start = 0;
|
|
26
|
+
let quote: "'" | "\"" | "`" | undefined;
|
|
27
|
+
let dollar: string | undefined;
|
|
28
|
+
let line = false;
|
|
29
|
+
let block = false;
|
|
30
|
+
let depth = 0;
|
|
31
|
+
for (let i = 0; i < sql.length; i++) {
|
|
32
|
+
const char = sql.charAt(i);
|
|
33
|
+
const next = sql.charAt(i + 1);
|
|
34
|
+
if (line) {
|
|
35
|
+
if (char === "\n") line = false;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (block) {
|
|
39
|
+
if (char === "*" && next === "/") {
|
|
40
|
+
block = false;
|
|
41
|
+
i += 1;
|
|
42
|
+
}
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (dollar) {
|
|
46
|
+
if (sql.startsWith(dollar, i)) {
|
|
47
|
+
i += dollar.length - 1;
|
|
48
|
+
dollar = undefined;
|
|
49
|
+
}
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (quote) {
|
|
53
|
+
if (char === quote) {
|
|
54
|
+
if (next === quote) i += 1;
|
|
55
|
+
else quote = undefined;
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (char === "-" && next === "-") {
|
|
60
|
+
line = true;
|
|
61
|
+
i += 1;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (char === "/" && next === "*") {
|
|
65
|
+
block = true;
|
|
66
|
+
i += 1;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const tag = dollarTag(sql, i);
|
|
70
|
+
if (tag) {
|
|
71
|
+
dollar = tag;
|
|
72
|
+
i += tag.length - 1;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const opened = quoteOf(char);
|
|
76
|
+
if (opened) {
|
|
77
|
+
quote = opened;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (word(sql, i, "BEGIN") && routine(sql, start, i)) {
|
|
81
|
+
depth += 1;
|
|
82
|
+
i += 4;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (char === ";") {
|
|
86
|
+
if (depth > 0 && routine(sql, start, i)) {
|
|
87
|
+
if (/\bEND$/i.test(sql.slice(start, i).trimEnd())) depth -= 1;
|
|
88
|
+
if (depth > 0) continue;
|
|
89
|
+
}
|
|
90
|
+
const part = sql.slice(start, i).trim();
|
|
91
|
+
if (part) parts.push(part);
|
|
92
|
+
start = i + 1;
|
|
93
|
+
depth = 0;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const tail = sql.slice(start).trim();
|
|
97
|
+
if (tail) parts.push(tail);
|
|
98
|
+
return parts;
|
|
99
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const statementParameters = (sql: string): string[] => {
|
|
2
|
+
const names: string[] = [];
|
|
3
|
+
const seen = new Set<string>();
|
|
4
|
+
for (let i = 0; i < sql.length; i++) {
|
|
5
|
+
const char = sql[i];
|
|
6
|
+
if (char !== "$" && char !== ":" && char !== "@") continue;
|
|
7
|
+
let end = i + 1;
|
|
8
|
+
while (end < sql.length && /[0-9A-Za-z_]/.test(sql[end]!)) end += 1;
|
|
9
|
+
if (end === i + 1) continue;
|
|
10
|
+
if (char === "$" && sql[end] === "$") {
|
|
11
|
+
i = end;
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
const name = sql.slice(i + 1, end);
|
|
15
|
+
if (!seen.has(name)) {
|
|
16
|
+
seen.add(name);
|
|
17
|
+
names.push(name);
|
|
18
|
+
}
|
|
19
|
+
i = end - 1;
|
|
20
|
+
}
|
|
21
|
+
return names;
|
|
22
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const fragment = (op: string, column: string, alias?: string): string =>
|
|
2
|
+
alias === undefined ? `${op}(${column})` : `${op}(${column}) AS ${alias}`;
|
|
3
|
+
|
|
4
|
+
export const Aggregate = {
|
|
5
|
+
count: (column: string, alias?: string): string => fragment("COUNT", column, alias),
|
|
6
|
+
sum: (column: string, alias?: string): string => fragment("SUM", column, alias),
|
|
7
|
+
min: (column: string, alias?: string): string => fragment("MIN", column, alias),
|
|
8
|
+
max: (column: string, alias?: string): string => fragment("MAX", column, alias),
|
|
9
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Parameter } from "../Parameter.ts";
|
|
2
|
+
import type { IBulkDeleteStatement } from "./IBulkDeleteStatement.ts";
|
|
3
|
+
import type { IStatementVisitor } from "./IStatementVisitor.ts";
|
|
4
|
+
import { flattenBulkParameters } from "./BulkInsertStatement.ts";
|
|
5
|
+
|
|
6
|
+
export class BulkDeleteStatement implements IBulkDeleteStatement {
|
|
7
|
+
constructor(
|
|
8
|
+
readonly table: string,
|
|
9
|
+
readonly ids: readonly string[],
|
|
10
|
+
readonly rows: readonly Parameter[][],
|
|
11
|
+
) {}
|
|
12
|
+
|
|
13
|
+
accept = (visitor: IStatementVisitor): string => visitor.visit(this);
|
|
14
|
+
|
|
15
|
+
get parameters(): Parameter[] {
|
|
16
|
+
return flattenBulkParameters(this.ids, this.rows);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { BulkDeleteStatement } from "./BulkDeleteStatement.ts";
|
|
2
|
+
|
|
3
|
+
export class BulkDeleteStatementTextRenderer {
|
|
4
|
+
render = (statement: BulkDeleteStatement): string => {
|
|
5
|
+
if (statement.ids.length === 0) return `DELETE FROM ${statement.table} WHERE 1 = 1`;
|
|
6
|
+
if (statement.ids.length === 1) {
|
|
7
|
+
const column = statement.ids[0]!;
|
|
8
|
+
const names = statement.rows.map((_, index) => `$${column}__${index}`).join(", ");
|
|
9
|
+
return `DELETE FROM ${statement.table} WHERE ${column} IN (${names})`;
|
|
10
|
+
}
|
|
11
|
+
const tuples = statement.rows
|
|
12
|
+
.map((_, index) => `(${statement.ids.map((column) => `$${column}__${index}`).join(", ")})`)
|
|
13
|
+
.join(", ");
|
|
14
|
+
return `DELETE FROM ${statement.table} WHERE (${statement.ids.join(", ")}) IN (${tuples})`;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Cell } from "../converters/Cell.ts";
|
|
2
|
+
import type { Parameter } from "../Parameter.ts";
|
|
3
|
+
import type { IBulkInsertStatement } from "./IBulkInsertStatement.ts";
|
|
4
|
+
import type { IStatementVisitor } from "./IStatementVisitor.ts";
|
|
5
|
+
|
|
6
|
+
const lookup = (fields: readonly Parameter[]): Map<string, Cell> => {
|
|
7
|
+
const map = new Map<string, Cell>();
|
|
8
|
+
for (const field of fields) map.set(field.name, (field.value ?? null) as Cell);
|
|
9
|
+
return map;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const flattenBulkParameters = (
|
|
13
|
+
columns: readonly string[],
|
|
14
|
+
rows: readonly Parameter[][],
|
|
15
|
+
): Parameter[] => {
|
|
16
|
+
const parameters: Parameter[] = [];
|
|
17
|
+
rows.forEach((row, index) => {
|
|
18
|
+
const byName = lookup(row);
|
|
19
|
+
for (const column of columns)
|
|
20
|
+
parameters.push({ name: `${column}__${index}`, value: (byName.get(column) ?? null) as Parameter["value"] });
|
|
21
|
+
});
|
|
22
|
+
return parameters;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export class BulkInsertStatement implements IBulkInsertStatement {
|
|
26
|
+
constructor(
|
|
27
|
+
readonly table: string,
|
|
28
|
+
readonly columns: readonly string[],
|
|
29
|
+
readonly rows: readonly Parameter[][],
|
|
30
|
+
) {}
|
|
31
|
+
|
|
32
|
+
accept = (visitor: IStatementVisitor): string => visitor.visit(this);
|
|
33
|
+
|
|
34
|
+
get parameters(): Parameter[] {
|
|
35
|
+
return flattenBulkParameters(this.columns, this.rows);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BulkInsertStatement } from "./BulkInsertStatement.ts";
|
|
2
|
+
|
|
3
|
+
export class BulkInsertStatementTextRenderer {
|
|
4
|
+
render = (statement: BulkInsertStatement): string => {
|
|
5
|
+
const values = statement.rows.map(
|
|
6
|
+
(_, index) => `(${statement.columns.map((column) => `$${column}__${index}`).join(", ")})`,
|
|
7
|
+
);
|
|
8
|
+
return `INSERT INTO ${statement.table} (${statement.columns.join(", ")}) VALUES ${values.join(", ")}`;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Parameter } from "../Parameter.ts";
|
|
2
|
+
import type { IBulkUpdateStatement } from "./IBulkUpdateStatement.ts";
|
|
3
|
+
import type { IStatementVisitor } from "./IStatementVisitor.ts";
|
|
4
|
+
import { flattenBulkParameters } from "./BulkInsertStatement.ts";
|
|
5
|
+
|
|
6
|
+
export type BulkUpdatedRow = { fields: Parameter[]; ids: Parameter[] };
|
|
7
|
+
|
|
8
|
+
export class BulkUpdateStatement implements IBulkUpdateStatement {
|
|
9
|
+
constructor(
|
|
10
|
+
readonly table: string,
|
|
11
|
+
readonly set: readonly string[],
|
|
12
|
+
readonly ids: readonly string[],
|
|
13
|
+
readonly rows: readonly BulkUpdatedRow[],
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
accept = (visitor: IStatementVisitor): string => visitor.visit(this);
|
|
17
|
+
|
|
18
|
+
get parameters(): Parameter[] {
|
|
19
|
+
return flattenBulkParameters(
|
|
20
|
+
[...this.set, ...this.ids],
|
|
21
|
+
this.rows.map((row) => [...row.fields, ...row.ids]),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Cell } from "../converters/Cell.ts";
|
|
2
|
+
import type { Parameter } from "../Parameter.ts";
|
|
3
|
+
import type { BulkUpdateStatement } from "./BulkUpdateStatement.ts";
|
|
4
|
+
|
|
5
|
+
export class BulkUpdateStatementTextRenderer {
|
|
6
|
+
render = (statement: BulkUpdateStatement): string => {
|
|
7
|
+
const assigns = statement.set.map((column) => `${column} = data.${column}`).join(", ");
|
|
8
|
+
return `UPDATE ${statement.table} SET ${assigns} FROM ${this.source(statement)} WHERE ${this.on(statement)}`;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
protected on = (statement: BulkUpdateStatement): string =>
|
|
12
|
+
statement.ids.map((column) => `${statement.table}.${column} = data.${column}`).join(" AND ");
|
|
13
|
+
|
|
14
|
+
protected source = (statement: BulkUpdateStatement): string => {
|
|
15
|
+
const columns = [...statement.set, ...statement.ids];
|
|
16
|
+
const selects = statement.rows.map((row, index) => {
|
|
17
|
+
const byName = new Map<string, Cell>();
|
|
18
|
+
for (const field of [...row.fields, ...row.ids] as Parameter[])
|
|
19
|
+
byName.set(field.name, (field.value ?? null) as Cell);
|
|
20
|
+
return `SELECT ${columns
|
|
21
|
+
.map((column) => this.cell(column, index, byName.get(column) ?? null))
|
|
22
|
+
.join(", ")}`;
|
|
23
|
+
});
|
|
24
|
+
return `(${selects.join(" UNION ALL ")}) AS data`;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
protected cell = (column: string, index: number, _value: Cell): string =>
|
|
28
|
+
`$${column}__${index} AS ${column}`;
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Cell } from "../converters/Cell.ts";
|
|
2
|
+
import type { Condition } from "./Condition.ts";
|
|
3
|
+
import { Operator } from "./Operator.ts";
|
|
4
|
+
|
|
5
|
+
export type Comparison = (column: string, value: Cell) => Condition;
|
|
6
|
+
|
|
7
|
+
export const eq: Comparison = (column, value) => new Operator.Equals(column, value);
|
|
8
|
+
export const ne: Comparison = (column, value) => new Operator.NotEquals(column, value);
|
|
9
|
+
export const gt: Comparison = (column, value) => new Operator.GreaterThan(column, value);
|
|
10
|
+
export const lt: Comparison = (column, value) => new Operator.LessThan(column, value);
|
|
11
|
+
export const ge: Comparison = (column, value) => new Operator.GreaterThanOrEqual(column, value);
|
|
12
|
+
export const le: Comparison = (column, value) => new Operator.LessThanOrEqual(column, value);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ICondition } from "./ICondition.ts";
|
|
2
|
+
import type { IConditionVisitor } from "./IConditionVisitor.ts";
|
|
3
|
+
import type { ExpressionRenderer } from "./ExpressionRenderer.ts";
|
|
4
|
+
import { Where } from "./Where.ts";
|
|
5
|
+
import { WhereText } from "./WhereText.ts";
|
|
6
|
+
|
|
7
|
+
export class ConditionRenderer implements IConditionVisitor {
|
|
8
|
+
constructor(private readonly expressions: ExpressionRenderer) {}
|
|
9
|
+
|
|
10
|
+
render = (condition: ICondition): string => condition.accept(this);
|
|
11
|
+
|
|
12
|
+
visit(where: Where): string;
|
|
13
|
+
visit(where: WhereText): string;
|
|
14
|
+
visit(where: Where | WhereText): string {
|
|
15
|
+
return where instanceof Where ? this.expressions.render(where.condition) : where.rawText;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ICreateIndexStatement } from "./ICreateIndexStatement.ts";
|
|
2
|
+
import type { IStatementVisitor } from "./IStatementVisitor.ts";
|
|
3
|
+
|
|
4
|
+
export class CreateIndexStatement implements ICreateIndexStatement {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly table: string;
|
|
7
|
+
readonly columns: string[];
|
|
8
|
+
readonly unique: boolean;
|
|
9
|
+
|
|
10
|
+
constructor(name: string, table: string, columns: string[], unique = false) {
|
|
11
|
+
this.name = name;
|
|
12
|
+
this.table = table;
|
|
13
|
+
this.columns = columns;
|
|
14
|
+
this.unique = unique;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
accept = (visitor: IStatementVisitor): string => visitor.visit(this);
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CreateIndexStatement } from "./CreateIndexStatement.ts";
|
|
2
|
+
|
|
3
|
+
export class CreateIndexStatementTextRenderer {
|
|
4
|
+
render(statement: CreateIndexStatement): string {
|
|
5
|
+
return `CREATE ${statement.unique ? "UNIQUE " : ""}INDEX ${this.quote(statement.name)} ON ${this.quote(statement.table)} (${statement.columns.map((column) => this.quote(column)).join(", ")})`;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
protected quote(name: string): string {
|
|
9
|
+
return name;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Column } from "./Column.ts";
|
|
2
|
+
import type { ICreateTableStatement } from "./ICreateTableStatement.ts";
|
|
3
|
+
import type { IStatementVisitor } from "./IStatementVisitor.ts";
|
|
4
|
+
import type { ITableConstraint } from "./ITableConstraint.ts";
|
|
5
|
+
|
|
6
|
+
export class CreateTableStatement implements ICreateTableStatement {
|
|
7
|
+
readonly table: string;
|
|
8
|
+
readonly columns: Column[];
|
|
9
|
+
readonly constraints: ITableConstraint[];
|
|
10
|
+
|
|
11
|
+
constructor(table: string, columns: Column[], constraints: ITableConstraint[] = []) {
|
|
12
|
+
this.table = table;
|
|
13
|
+
this.columns = columns;
|
|
14
|
+
this.constraints = constraints;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
accept = (visitor: IStatementVisitor): string => visitor.visit(this);
|
|
18
|
+
}
|