@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.
Files changed (209) hide show
  1. package/package.json +16 -4
  2. package/src/ArrayReader.ts +32 -0
  3. package/src/BatchCommandIBulkWriteStrategy.ts +228 -0
  4. package/src/BuiltRepository.ts +17 -0
  5. package/src/BulkCRUDRepository.ts +84 -0
  6. package/src/BulkCommand.ts +6 -0
  7. package/src/BulkWriteEnrichment.ts +19 -0
  8. package/src/CRUDRepository.ts +264 -0
  9. package/src/CRUDRepositoryBuilder.ts +53 -0
  10. package/src/CRUDRepositoryOptions.ts +8 -0
  11. package/src/CRUDRepositoryStatements.ts +43 -0
  12. package/src/CascadeDeleteEnrichmentStrategy.ts +51 -0
  13. package/src/CascadeDeleteUpdateEnrichmentStrategy.ts +28 -0
  14. package/src/CommandMiddlewareHost.ts +113 -0
  15. package/src/ConsoleCommandMiddleware.ts +64 -0
  16. package/src/CreateEnrichmentStrategy.ts +82 -0
  17. package/src/Datasource.ts +152 -0
  18. package/src/DatasourceError.ts +6 -0
  19. package/src/DatasourceFactory.ts +86 -0
  20. package/src/DatasourceOptions.ts +1 -0
  21. package/src/DatasourceUrl.ts +37 -0
  22. package/src/DatasourceUrlParser.ts +188 -0
  23. package/src/DefaultCreateEnrichmentStrategy.ts +21 -0
  24. package/src/DefaultReadonlyEnrichmentStrategy.ts +164 -0
  25. package/src/DefaultUpdateEnrichmentStrategy.ts +40 -0
  26. package/src/DefaultWriteEnrichmentStrategy.ts +151 -0
  27. package/src/DeleteEnrichmentStrategy.ts +56 -0
  28. package/src/DriverLoader.ts +97 -0
  29. package/src/Enrichment.ts +15 -0
  30. package/src/EnrichmentPropertyType.ts +9 -0
  31. package/src/ExpectConsoleMiddleware.ts +47 -0
  32. package/src/FirstResultReader.ts +49 -0
  33. package/src/HashsetOptimizedReadonlyEnrichmentStrategy.ts +80 -0
  34. package/src/IBulkWriteStrategy.ts +13 -0
  35. package/src/ICRUDRepository.ts +13 -0
  36. package/src/ICRUDRepositoryFactory.ts +8 -0
  37. package/src/ICommand.ts +45 -0
  38. package/src/ICommandMiddleware.ts +20 -0
  39. package/src/IConnection.ts +13 -0
  40. package/src/IDatasource.ts +27 -0
  41. package/src/IDatasourceFactory.ts +6 -0
  42. package/src/IDriver.ts +24 -0
  43. package/src/IEnriched.ts +11 -0
  44. package/src/IReader.ts +8 -0
  45. package/src/IReadonlyEnrichmentStrategy.ts +19 -0
  46. package/src/IReadonlyRepository.ts +21 -0
  47. package/src/IRepository.ts +5 -0
  48. package/src/IRepositoryFactory.ts +8 -0
  49. package/src/IRepositoryTree.ts +5 -0
  50. package/src/IRepositoryTreeNode.ts +8 -0
  51. package/src/ITransaction.ts +9 -0
  52. package/src/IUnitOfWork.ts +5 -0
  53. package/src/IWriteEnrichmentStrategy.ts +8 -0
  54. package/src/Information.ts +5 -0
  55. package/src/IsolationLevel.ts +8 -0
  56. package/src/LazyWriteEnrichmentStrategy.ts +5 -0
  57. package/src/NestedToObjectEnrichmentStrategy.ts +33 -0
  58. package/src/Parameter.ts +8 -0
  59. package/src/ParameterDirection.ts +9 -0
  60. package/src/ProviderCatalog.ts +21 -0
  61. package/src/ProviderNotImplementedError.ts +9 -0
  62. package/src/ProviderNotInstalledError.ts +9 -0
  63. package/src/ReadonlyRepository.ts +276 -0
  64. package/src/ReadonlyRepositoryBuilder.ts +53 -0
  65. package/src/ReadonlyRepositoryMappingOptions.ts +15 -0
  66. package/src/ReadonlyRepositoryStatements.ts +13 -0
  67. package/src/RepositoryAccess.ts +14 -0
  68. package/src/RepositoryFactory.ts +115 -0
  69. package/src/RepositoryNotRegisteredError.ts +9 -0
  70. package/src/RepositoryTree.ts +6 -0
  71. package/src/RepositoryTreeNode.ts +11 -0
  72. package/src/Response.ts +15 -0
  73. package/src/Row.ts +3 -0
  74. package/src/SingleCommandIBulkWriteStrategy.ts +35 -0
  75. package/src/StatementMappers.ts +110 -0
  76. package/src/StoredEnrichment.ts +25 -0
  77. package/src/StoredProcedureBulkWriteStrategy.ts +164 -0
  78. package/src/StreamingReader.ts +64 -0
  79. package/src/UnitOfWorkTransaction.ts +50 -0
  80. package/src/UpdateEnrichmentStrategy.ts +97 -0
  81. package/src/WriteMode.ts +1 -0
  82. package/src/builders/Field.ts +6 -0
  83. package/src/builders/Scalar.ts +1 -0
  84. package/src/builders/index.ts +2 -0
  85. package/src/collectAsync.ts +7 -0
  86. package/src/converters/Cell.ts +10 -0
  87. package/src/converters/ChangeCasingConverter.ts +59 -0
  88. package/src/converters/Constructable.ts +1 -0
  89. package/src/converters/MetaDataRowTypeConverter.ts +80 -0
  90. package/src/converters/PluralizerConverter.ts +67 -0
  91. package/src/converters/RegexConverter.ts +14 -0
  92. package/src/converters/ReplaceConverter.ts +6 -0
  93. package/src/converters/RowTypeConverter.ts +33 -0
  94. package/src/converters/ToLowerCaseConverter.ts +4 -0
  95. package/src/converters/ToUpperCaseConverter.ts +4 -0
  96. package/src/converters/TypeConverter.ts +6 -0
  97. package/src/createCRUDRepositoryOptions.ts +21 -0
  98. package/src/createReadonlyRepositoryMappingOptions.ts +26 -0
  99. package/src/dotEnv.ts +16 -0
  100. package/src/index.ts +222 -0
  101. package/src/mappers/ChangeCasingMapper.ts +15 -0
  102. package/src/mappers/DictionaryMapper.ts +16 -0
  103. package/src/mappers/FlatToNestedMapper.ts +127 -0
  104. package/src/mappers/IFlatToNestedMapper.ts +7 -0
  105. package/src/mappers/IMapper.ts +4 -0
  106. package/src/mappers/IdentityMapper.ts +6 -0
  107. package/src/mappers/PluralizerMapper.ts +9 -0
  108. package/src/mappers/RowTypeMapper.ts +13 -0
  109. package/src/mappers/TransformConverterMapper.ts +11 -0
  110. package/src/mappers/TransformMapper.ts +8 -0
  111. package/src/mappers/TypeMap.ts +10 -0
  112. package/src/mappers/TypeMapGroup.ts +6 -0
  113. package/src/mocks/index.ts +9 -0
  114. package/src/mocks/mock-command.ts +66 -0
  115. package/src/mocks/mock-connection.ts +57 -0
  116. package/src/mocks/mock-datasource.ts +65 -0
  117. package/src/mocks/mock-driver.ts +27 -0
  118. package/src/mocks/mock-reader.ts +38 -0
  119. package/src/mocks/mock-repository.ts +91 -0
  120. package/src/mocks/mock-select-statement.ts +17 -0
  121. package/src/mocks/mock-transaction.ts +31 -0
  122. package/src/mocks/mock-where.ts +13 -0
  123. package/src/parametersToRecord.ts +11 -0
  124. package/src/readerToAsyncIterable.ts +11 -0
  125. package/src/splitStatements.ts +99 -0
  126. package/src/statementParameters.ts +22 -0
  127. package/src/statementUsesParameter.ts +4 -0
  128. package/src/statements/Aggregate.ts +9 -0
  129. package/src/statements/ArrayExpression.ts +5 -0
  130. package/src/statements/BulkDeleteStatement.ts +18 -0
  131. package/src/statements/BulkDeleteStatementTextRenderer.ts +16 -0
  132. package/src/statements/BulkInsertStatement.ts +37 -0
  133. package/src/statements/BulkInsertStatementTextRenderer.ts +10 -0
  134. package/src/statements/BulkUpdateStatement.ts +24 -0
  135. package/src/statements/BulkUpdateStatementTextRenderer.ts +29 -0
  136. package/src/statements/CheckConstraint.ts +9 -0
  137. package/src/statements/Cmp.ts +12 -0
  138. package/src/statements/Column.ts +10 -0
  139. package/src/statements/ColumnDefault.ts +6 -0
  140. package/src/statements/Condition.ts +3 -0
  141. package/src/statements/ConditionRenderer.ts +17 -0
  142. package/src/statements/CreateIndexStatement.ts +18 -0
  143. package/src/statements/CreateIndexStatementTextRenderer.ts +11 -0
  144. package/src/statements/CreateTableStatement.ts +18 -0
  145. package/src/statements/CreateTableStatementTextRenderer.ts +92 -0
  146. package/src/statements/DefaultStatementTextRenderer.ts +9 -0
  147. package/src/statements/DeleteStatement.ts +15 -0
  148. package/src/statements/DeleteStatementTextRenderer.ts +12 -0
  149. package/src/statements/ExecuteStoredProcedureStatement.ts +15 -0
  150. package/src/statements/ExecuteStoredProcedureStatementTextRenderer.ts +15 -0
  151. package/src/statements/ExpressionEvaluator.ts +166 -0
  152. package/src/statements/ExpressionRenderer.ts +161 -0
  153. package/src/statements/ForeignKey.ts +23 -0
  154. package/src/statements/IBulkDeleteStatement.ts +3 -0
  155. package/src/statements/IBulkInsertStatement.ts +3 -0
  156. package/src/statements/IBulkUpdateStatement.ts +3 -0
  157. package/src/statements/ICondition.ts +5 -0
  158. package/src/statements/IConditionVisitor.ts +7 -0
  159. package/src/statements/ICreateIndexStatement.ts +3 -0
  160. package/src/statements/ICreateTableStatement.ts +3 -0
  161. package/src/statements/IDeleteStatement.ts +6 -0
  162. package/src/statements/IExecuteStoredProcedureStatement.ts +3 -0
  163. package/src/statements/IExpression.ts +1 -0
  164. package/src/statements/IInsertStatement.ts +3 -0
  165. package/src/statements/IReturnStatement.ts +3 -0
  166. package/src/statements/ISelectStatement.ts +6 -0
  167. package/src/statements/IStatement.ts +5 -0
  168. package/src/statements/IStatementVisitor.ts +27 -0
  169. package/src/statements/ITableConstraint.ts +6 -0
  170. package/src/statements/IUpdateStatement.ts +6 -0
  171. package/src/statements/IWhere.ts +7 -0
  172. package/src/statements/Identifier.ts +5 -0
  173. package/src/statements/InsertStatement.ts +15 -0
  174. package/src/statements/InsertStatementTextRenderer.ts +7 -0
  175. package/src/statements/Join.ts +26 -0
  176. package/src/statements/Literal.ts +6 -0
  177. package/src/statements/Native.ts +5 -0
  178. package/src/statements/NotNull.ts +3 -0
  179. package/src/statements/Operator.ts +234 -0
  180. package/src/statements/OrderBy.ts +19 -0
  181. package/src/statements/PrimaryKey.ts +9 -0
  182. package/src/statements/ReturnStatement.ts +15 -0
  183. package/src/statements/ReturnStatementTextRenderer.ts +15 -0
  184. package/src/statements/SelectInsertedIdStatement.ts +11 -0
  185. package/src/statements/SelectInsertedIdStatementTextRenderer.ts +7 -0
  186. package/src/statements/SelectStatement.ts +34 -0
  187. package/src/statements/SelectStatementOptions.ts +16 -0
  188. package/src/statements/SelectStatementTextRenderer.ts +71 -0
  189. package/src/statements/StatementEvaluator.ts +5 -0
  190. package/src/statements/StatementParser.ts +461 -0
  191. package/src/statements/StatementRenderer.ts +120 -0
  192. package/src/statements/StatementTextRenderer.ts +60 -0
  193. package/src/statements/Subquery.ts +6 -0
  194. package/src/statements/UniqueConstraint.ts +9 -0
  195. package/src/statements/UpdateStatement.ts +34 -0
  196. package/src/statements/UpdateStatementTextRenderer.ts +63 -0
  197. package/src/statements/Where.ts +250 -0
  198. package/src/statements/WhereText.ts +10 -0
  199. package/src/statements/index.ts +75 -0
  200. package/src/stubs/StubCommand.ts +30 -0
  201. package/src/stubs/StubConnection.ts +21 -0
  202. package/src/stubs/StubDatasource.ts +60 -0
  203. package/src/stubs/StubReader.ts +25 -0
  204. package/src/stubs/StubRepository.ts +42 -0
  205. package/src/stubs/StubTransaction.ts +17 -0
  206. package/src/stubs/createStubDatasource.ts +4 -0
  207. package/src/stubs/index.ts +7 -0
  208. package/src/stubs/notImplemented.ts +5 -0
  209. 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,9 @@
1
+ import { StatementTextRenderer } from "./StatementTextRenderer.ts";
2
+
3
+ export class DefaultStatementTextRenderer extends StatementTextRenderer {
4
+ static readonly instance = new DefaultStatementTextRenderer();
5
+
6
+ constructor() {
7
+ super();
8
+ }
9
+ }
@@ -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,3 @@
1
+ import type { IStatement } from "./IStatement.ts";
2
+
3
+ export interface IBulkDeleteStatement extends IStatement {}
@@ -0,0 +1,3 @@
1
+ import type { IStatement } from "./IStatement.ts";
2
+
3
+ export interface IBulkInsertStatement extends IStatement {}
@@ -0,0 +1,3 @@
1
+ import type { IStatement } from "./IStatement.ts";
2
+
3
+ export interface IBulkUpdateStatement extends IStatement {}
@@ -0,0 +1,5 @@
1
+ import type { IConditionVisitor } from "./IConditionVisitor.ts";
2
+
3
+ export interface ICondition {
4
+ accept(visitor: IConditionVisitor): string;
5
+ }
@@ -0,0 +1,7 @@
1
+ import type { Where } from "./Where.ts";
2
+ import type { WhereText } from "./WhereText.ts";
3
+
4
+ export interface IConditionVisitor {
5
+ visit(where: Where): string;
6
+ visit(where: WhereText): string;
7
+ }
@@ -0,0 +1,3 @@
1
+ import type { IStatement } from "./IStatement.ts";
2
+
3
+ export interface ICreateIndexStatement extends IStatement {}
@@ -0,0 +1,3 @@
1
+ import type { IStatement } from "./IStatement.ts";
2
+
3
+ export interface ICreateTableStatement extends IStatement {}
@@ -0,0 +1,6 @@
1
+ import type { IStatement } from "./IStatement.ts";
2
+ import type { IWhere } from "./IWhere.ts";
3
+
4
+ export interface IDeleteStatement extends IStatement {
5
+ readonly where?: IWhere;
6
+ }
@@ -0,0 +1,3 @@
1
+ import type { IStatement } from "./IStatement.ts";
2
+
3
+ export interface IExecuteStoredProcedureStatement extends IStatement {}
@@ -0,0 +1 @@
1
+ export interface IExpression {}
@@ -0,0 +1,3 @@
1
+ import type { IStatement } from "./IStatement.ts";
2
+
3
+ export interface IInsertStatement extends IStatement {}
@@ -0,0 +1,3 @@
1
+ import type { IStatement } from "./IStatement.ts";
2
+
3
+ export interface IReturnStatement extends IStatement {}
@@ -0,0 +1,6 @@
1
+ import type { IStatement } from "./IStatement.ts";
2
+ import type { IWhere } from "./IWhere.ts";
3
+
4
+ export interface ISelectStatement extends IStatement {
5
+ readonly where?: IWhere;
6
+ }
@@ -0,0 +1,5 @@
1
+ import type { IStatementVisitor } from "./IStatementVisitor.ts";
2
+
3
+ export interface IStatement {
4
+ accept(visitor: IStatementVisitor): string;
5
+ }
@@ -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
+ }
@@ -0,0 +1,6 @@
1
+ export type TableConstraintKind = "primaryKey" | "foreignKey" | "unique" | "check";
2
+
3
+ export interface ITableConstraint {
4
+ readonly kind: TableConstraintKind;
5
+ readonly name: string;
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { IStatement } from "./IStatement.ts";
2
+ import type { IWhere } from "./IWhere.ts";
3
+
4
+ export interface IUpdateStatement extends IStatement {
5
+ readonly where?: IWhere;
6
+ }
@@ -0,0 +1,7 @@
1
+ import type { Parameter } from "../Parameter.ts";
2
+ import type { ICondition } from "./ICondition.ts";
3
+ import type { IExpression } from "./IExpression.ts";
4
+
5
+ export interface IWhere extends IExpression, ICondition {
6
+ readonly parameters?: Parameter[];
7
+ }
@@ -0,0 +1,5 @@
1
+ import type { IExpression } from "./IExpression.ts";
2
+
3
+ export class Identifier implements IExpression {
4
+ constructor(readonly name: string) {}
5
+ }