@deterministic-code/datasources 0.1.15 → 0.1.17
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/dist/Datasource.d.ts +2 -0
- package/dist/Datasource.js +13 -21
- package/dist/IDatasource.d.ts +2 -0
- package/dist/IReadonlyRepository.d.ts +1 -0
- package/dist/ReadonlyRepository.d.ts +1 -0
- package/dist/ReadonlyRepository.js +1 -0
- package/dist/ReadonlyRepositoryMappingOptions.d.ts +2 -0
- package/dist/Schema.d.ts +4 -0
- package/dist/Schema.js +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/mappers/DeterministicTypeMapper.d.ts +5 -0
- package/dist/mappers/DeterministicTypeMapper.js +21 -0
- package/dist/mocks/mock-datasource.d.ts +2 -0
- package/dist/mocks/mock-datasource.js +2 -0
- package/dist/statements/ArrayExpression.js +2 -0
- package/dist/statements/ConditionRenderer.js +8 -2
- package/dist/statements/ExpressionRenderer.js +63 -77
- package/dist/statements/Identifier.js +2 -0
- package/dist/statements/Literal.js +2 -0
- package/dist/statements/Operator.js +19 -0
- package/dist/statements/StatementTextRenderer.js +4 -11
- package/dist/statements/Subquery.js +2 -0
- package/dist/statements/Where.js +2 -0
- package/dist/statements/WhereText.js +2 -0
- package/dist/statements/brand.d.ts +5 -0
- package/dist/statements/brand.js +5 -0
- package/dist/stubs/StubDatasource.d.ts +2 -0
- package/dist/stubs/StubDatasource.js +6 -0
- package/package.json +1 -1
package/dist/Datasource.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export declare class Datasource implements IDatasource {
|
|
|
17
17
|
readonly repositories: RepositoryFactory;
|
|
18
18
|
constructor(provider: string, driver: IDriver, options?: DatasourceOptions | undefined);
|
|
19
19
|
private defaultConnection;
|
|
20
|
+
close: () => Promise<void>;
|
|
21
|
+
[Symbol.asyncDispose]: () => Promise<void>;
|
|
20
22
|
createConnection: () => Promise<IConnection>;
|
|
21
23
|
createCommand: (connection?: IConnection) => Promise<ICommand>;
|
|
22
24
|
private applyCrud;
|
package/dist/Datasource.js
CHANGED
|
@@ -4,6 +4,7 @@ import { ReadonlyRepository } from "./ReadonlyRepository.js";
|
|
|
4
4
|
import { RepositoryFactory } from "./RepositoryFactory.js";
|
|
5
5
|
import { RepositoryNotRegisteredError } from "./RepositoryNotRegisteredError.js";
|
|
6
6
|
import { RowTypeConverter } from "./converters/RowTypeConverter.js";
|
|
7
|
+
import { Schema } from "./Schema.js";
|
|
7
8
|
import { createCRUDRepositoryOptions } from "./createCRUDRepositoryOptions.js";
|
|
8
9
|
import { createReadonlyRepositoryMappingOptions } from "./createReadonlyRepositoryMappingOptions.js";
|
|
9
10
|
export class Datasource {
|
|
@@ -28,6 +29,12 @@ export class Datasource {
|
|
|
28
29
|
});
|
|
29
30
|
}
|
|
30
31
|
defaultConnection = () => (this.connection ??= this.driver.connection(this.options));
|
|
32
|
+
close = async () => {
|
|
33
|
+
const connection = this.connection;
|
|
34
|
+
this.connection = undefined;
|
|
35
|
+
await connection?.close();
|
|
36
|
+
};
|
|
37
|
+
[Symbol.asyncDispose] = () => this.close();
|
|
31
38
|
createConnection = () => Promise.resolve(this.defaultConnection());
|
|
32
39
|
createCommand = async (connection) => {
|
|
33
40
|
if (!connection) {
|
|
@@ -41,10 +48,12 @@ export class Datasource {
|
|
|
41
48
|
applyCrud = (type, options) => {
|
|
42
49
|
const built = createCRUDRepositoryOptions(type, this.driver.rowTypeConverters);
|
|
43
50
|
const types = this.driver.rowTypeConverters;
|
|
44
|
-
const
|
|
51
|
+
const schema = options?.schema ?? new Schema({});
|
|
52
|
+
const rows = options?.rowConverter ?? RowTypeConverter.from(this.driver.rowTypeConverters, schema.columns);
|
|
45
53
|
return {
|
|
46
54
|
...(options ?? built),
|
|
47
55
|
entity: options?.entity ?? built.entity,
|
|
56
|
+
schema,
|
|
48
57
|
rowConverter: rows,
|
|
49
58
|
rowTypeConverters: types,
|
|
50
59
|
type,
|
|
@@ -53,10 +62,12 @@ export class Datasource {
|
|
|
53
62
|
applyReadonly = (type, options) => {
|
|
54
63
|
const built = createReadonlyRepositoryMappingOptions(type, this.driver.rowTypeConverters);
|
|
55
64
|
const types = this.driver.rowTypeConverters;
|
|
56
|
-
const
|
|
65
|
+
const schema = options?.schema ?? new Schema({});
|
|
66
|
+
const rows = options?.rowConverter ?? RowTypeConverter.from(this.driver.rowTypeConverters, schema.columns);
|
|
57
67
|
return {
|
|
58
68
|
...(options ?? built),
|
|
59
69
|
entity: options?.entity ?? built.entity,
|
|
70
|
+
schema,
|
|
60
71
|
rowConverter: rows,
|
|
61
72
|
rowTypeConverters: types,
|
|
62
73
|
type,
|
|
@@ -77,22 +88,3 @@ export class Datasource {
|
|
|
77
88
|
return Datasource.create();
|
|
78
89
|
}
|
|
79
90
|
}
|
|
80
|
-
const columnsOf = (type) => {
|
|
81
|
-
const instance = new type();
|
|
82
|
-
const columns = {};
|
|
83
|
-
for (const [name, value] of Object.entries(instance)) {
|
|
84
|
-
if (Array.isArray(value))
|
|
85
|
-
continue;
|
|
86
|
-
const canonical = canonicalOf(value);
|
|
87
|
-
if (canonical)
|
|
88
|
-
columns[name] = canonical;
|
|
89
|
-
}
|
|
90
|
-
return columns;
|
|
91
|
-
};
|
|
92
|
-
const canonicalOf = (value) => typeof value === "boolean"
|
|
93
|
-
? "bool"
|
|
94
|
-
: value instanceof Date
|
|
95
|
-
? "datetime"
|
|
96
|
-
: value instanceof Uint8Array
|
|
97
|
-
? "binary"
|
|
98
|
-
: undefined;
|
package/dist/IDatasource.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ import type { RepositoryFactory } from "./RepositoryFactory.js";
|
|
|
9
9
|
export interface IDatasource {
|
|
10
10
|
readonly provider: string;
|
|
11
11
|
readonly repositories: RepositoryFactory;
|
|
12
|
+
close(): Promise<void>;
|
|
13
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
12
14
|
createConnection(): Promise<IConnection>;
|
|
13
15
|
createCommand(connection?: IConnection): Promise<ICommand>;
|
|
14
16
|
createRepository<T>(type: Constructable<T>, connection?: IConnection, options?: CRUDRepositoryOptions<T>): ICRUDRepository<T>;
|
|
@@ -6,6 +6,7 @@ import type { IWhere } from "./statements/IWhere.js";
|
|
|
6
6
|
import type { ReadonlyRepositoryMappingOptions } from "./ReadonlyRepositoryMappingOptions.js";
|
|
7
7
|
export type ReadQuery = ISelectStatement | IWhere | IExecuteStoredProcedureStatement;
|
|
8
8
|
export interface IReadonlyRepository<T = unknown> extends IRepository<T> {
|
|
9
|
+
readonly isSchemaRequired: boolean;
|
|
9
10
|
readSingle(query?: ReadQuery, parameters?: Parameter[], options?: ReadonlyRepositoryMappingOptions<T>): Promise<T | undefined>;
|
|
10
11
|
read(query?: ReadQuery, parameters?: Parameter[], options?: ReadonlyRepositoryMappingOptions<T>): AsyncIterable<T>;
|
|
11
12
|
}
|
|
@@ -23,6 +23,7 @@ export declare const isBuilt: <T>(value: IConnection | BuiltRepository<T>) => va
|
|
|
23
23
|
export declare const builtOf: <T>(typeOrConnectionOrBuilt: Constructable<T> | IConnection | BuiltRepository<T>, connectionOrOptions?: IConnection | ReadonlyRepositoryMappingOptions<T>) => BuiltRepository<T> | undefined;
|
|
24
24
|
export declare class ReadonlyRepository<T> implements IReadonlyRepository<T>, IEnriched<T> {
|
|
25
25
|
readonly type: Constructable<T>;
|
|
26
|
+
readonly isSchemaRequired = false;
|
|
26
27
|
private connection?;
|
|
27
28
|
protected rowConverter?: TypeConverter<Cells, Cells>;
|
|
28
29
|
protected objectConverter?: TypeConverter<T, Cells>;
|
|
@@ -22,6 +22,7 @@ export const builtOf = (typeOrConnectionOrBuilt, connectionOrOptions) => typeof
|
|
|
22
22
|
: new ReadonlyRepositoryBuilder().parts(typeOrConnectionOrBuilt, connectionOrOptions);
|
|
23
23
|
export class ReadonlyRepository {
|
|
24
24
|
type;
|
|
25
|
+
isSchemaRequired = false;
|
|
25
26
|
connection;
|
|
26
27
|
rowConverter;
|
|
27
28
|
objectConverter;
|
|
@@ -2,6 +2,7 @@ import type { Cell, Cells } from "./converters/Cell.js";
|
|
|
2
2
|
import type { Constructable } from "./converters/Constructable.js";
|
|
3
3
|
import type { TypeConverter } from "./converters/TypeConverter.js";
|
|
4
4
|
import type { IMapper } from "./mappers/IMapper.js";
|
|
5
|
+
import type { Schema } from "./Schema.js";
|
|
5
6
|
export type ReadonlyRepositoryMappingOptions<T = Cells> = {
|
|
6
7
|
entity?: string;
|
|
7
8
|
entityMapper?: IMapper<string, string>;
|
|
@@ -11,4 +12,5 @@ export type ReadonlyRepositoryMappingOptions<T = Cells> = {
|
|
|
11
12
|
type?: Constructable<T>;
|
|
12
13
|
rowTypeConverters?: Record<string, TypeConverter<Cell, Cell>>;
|
|
13
14
|
readProperties?: string[];
|
|
15
|
+
schema?: Schema;
|
|
14
16
|
};
|
package/dist/Schema.d.ts
ADDED
package/dist/Schema.js
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -118,6 +118,8 @@ export type { TypeMapGroup } from "./mappers/TypeMapGroup.js";
|
|
|
118
118
|
export { FlatToNestedMapper } from "./mappers/FlatToNestedMapper.js";
|
|
119
119
|
export { ChangeCasingMapper } from "./mappers/ChangeCasingMapper.js";
|
|
120
120
|
export { DictionaryMapper } from "./mappers/DictionaryMapper.js";
|
|
121
|
+
export { DeterministicTypeMapper } from "./mappers/DeterministicTypeMapper.js";
|
|
122
|
+
export { Schema } from "./Schema.js";
|
|
121
123
|
export { IdentityMapper } from "./mappers/IdentityMapper.js";
|
|
122
124
|
export { PluralizerMapper } from "./mappers/PluralizerMapper.js";
|
|
123
125
|
export { TransformMapper } from "./mappers/TransformMapper.js";
|
package/dist/index.js
CHANGED
|
@@ -72,6 +72,8 @@ export { ReplaceConverter } from "./converters/ReplaceConverter.js";
|
|
|
72
72
|
export { FlatToNestedMapper } from "./mappers/FlatToNestedMapper.js";
|
|
73
73
|
export { ChangeCasingMapper } from "./mappers/ChangeCasingMapper.js";
|
|
74
74
|
export { DictionaryMapper } from "./mappers/DictionaryMapper.js";
|
|
75
|
+
export { DeterministicTypeMapper } from "./mappers/DeterministicTypeMapper.js";
|
|
76
|
+
export { Schema } from "./Schema.js";
|
|
75
77
|
export { IdentityMapper } from "./mappers/IdentityMapper.js";
|
|
76
78
|
export { PluralizerMapper } from "./mappers/PluralizerMapper.js";
|
|
77
79
|
export { TransformMapper } from "./mappers/TransformMapper.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const forward = {
|
|
2
|
+
boolean: "boolean",
|
|
3
|
+
Boolean: "boolean",
|
|
4
|
+
number: "integer",
|
|
5
|
+
Number: "integer",
|
|
6
|
+
bigint: "biginteger",
|
|
7
|
+
BigInt: "biginteger",
|
|
8
|
+
Date: "datetime",
|
|
9
|
+
Uint8Array: "binary",
|
|
10
|
+
};
|
|
11
|
+
const reverse = {
|
|
12
|
+
boolean: "boolean",
|
|
13
|
+
integer: "number",
|
|
14
|
+
biginteger: "bigint",
|
|
15
|
+
datetime: "Date",
|
|
16
|
+
binary: "Uint8Array",
|
|
17
|
+
};
|
|
18
|
+
export class DeterministicTypeMapper {
|
|
19
|
+
map = (value) => forward[value] ?? "";
|
|
20
|
+
reverseMap = (value) => reverse[value] ?? "";
|
|
21
|
+
}
|
|
@@ -15,6 +15,8 @@ export declare class MockDatasource implements IDatasource {
|
|
|
15
15
|
repository?: ICRUDRepository;
|
|
16
16
|
readonly repositories: RepositoryFactory;
|
|
17
17
|
constructor(provider?: string);
|
|
18
|
+
close: () => Promise<void>;
|
|
19
|
+
[Symbol.asyncDispose]: () => Promise<void>;
|
|
18
20
|
createConnection(): Promise<IConnection>;
|
|
19
21
|
createCommand(connection?: IConnection): Promise<ICommand>;
|
|
20
22
|
createRepository<T>(type: Constructable<T>, connection?: IConnection): ICRUDRepository<T>;
|
|
@@ -16,6 +16,8 @@ export class MockDatasource {
|
|
|
16
16
|
this.provider = provider;
|
|
17
17
|
this.connection = new MockConnection();
|
|
18
18
|
}
|
|
19
|
+
close = () => this.connection.close();
|
|
20
|
+
[Symbol.asyncDispose] = () => this.close();
|
|
19
21
|
createConnection() {
|
|
20
22
|
this.createConnectionCalls += 1;
|
|
21
23
|
return Promise.resolve(this.connection);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { is } from "./brand.js";
|
|
1
2
|
import { Where } from "./Where.js";
|
|
3
|
+
import { WhereText } from "./WhereText.js";
|
|
2
4
|
export class ConditionRenderer {
|
|
3
5
|
expressions;
|
|
4
6
|
constructor(expressions) {
|
|
@@ -6,8 +8,12 @@ export class ConditionRenderer {
|
|
|
6
8
|
}
|
|
7
9
|
render = (condition) => condition.accept(this);
|
|
8
10
|
visit(where) {
|
|
9
|
-
return where
|
|
11
|
+
return is(where, Where)
|
|
10
12
|
? this.expressions.render(where.condition)
|
|
11
|
-
: where
|
|
13
|
+
: is(where, WhereText)
|
|
14
|
+
? where.rawText
|
|
15
|
+
: (() => {
|
|
16
|
+
throw new Error("empty expression");
|
|
17
|
+
})();
|
|
12
18
|
}
|
|
13
19
|
}
|
|
@@ -1,73 +1,65 @@
|
|
|
1
1
|
import { ArrayExpression } from "./ArrayExpression.js";
|
|
2
|
+
import { is } from "./brand.js";
|
|
2
3
|
import { Identifier } from "./Identifier.js";
|
|
3
4
|
import { Literal } from "./Literal.js";
|
|
4
5
|
import { Operator } from "./Operator.js";
|
|
5
6
|
import { Subquery } from "./Subquery.js";
|
|
6
7
|
import { Where } from "./Where.js";
|
|
7
|
-
const is = (value, type, name) => value instanceof type || value.constructor.name === name;
|
|
8
8
|
export class ExpressionRenderer {
|
|
9
9
|
text;
|
|
10
10
|
constructor(text) {
|
|
11
11
|
this.text = text;
|
|
12
12
|
}
|
|
13
13
|
render = (expression) => {
|
|
14
|
-
if (is(expression, Where
|
|
14
|
+
if (is(expression, Where))
|
|
15
15
|
return this.render(expression.condition);
|
|
16
|
-
if (is(expression, Identifier
|
|
16
|
+
if (is(expression, Identifier))
|
|
17
17
|
return expression.name;
|
|
18
|
-
if (is(expression, Literal
|
|
18
|
+
if (is(expression, Literal))
|
|
19
19
|
return inline(expression.value);
|
|
20
|
-
if (is(expression, ArrayExpression
|
|
20
|
+
if (is(expression, ArrayExpression))
|
|
21
21
|
return expression.elements.map((e) => this.render(e)).join(", ");
|
|
22
|
-
if (is(expression, Operator.Not
|
|
22
|
+
if (is(expression, Operator.Not))
|
|
23
23
|
return `NOT (${this.render(expression.argument)})`;
|
|
24
|
-
if (is(expression, Operator.IsNull
|
|
24
|
+
if (is(expression, Operator.IsNull))
|
|
25
25
|
return `${this.render(expression.argument)} IS NULL`;
|
|
26
|
-
if (is(expression, Operator.IsNotNull
|
|
26
|
+
if (is(expression, Operator.IsNotNull))
|
|
27
27
|
return `${this.render(expression.argument)} IS NOT NULL`;
|
|
28
|
-
if (is(expression, Operator.Exists
|
|
28
|
+
if (is(expression, Operator.Exists))
|
|
29
29
|
return `EXISTS ${this.render(expression.argument)}`;
|
|
30
|
-
if (is(expression, Subquery
|
|
30
|
+
if (is(expression, Subquery))
|
|
31
31
|
return `(${this.text.render(expression.query)})`;
|
|
32
|
-
if (is(expression, Operator.In,
|
|
33
|
-
is(expression.left, Identifier, "Identifier") &&
|
|
34
|
-
is(expression.right, ArrayExpression, "ArrayExpression"))
|
|
32
|
+
if (is(expression, Operator.In) && is(expression.left, Identifier) && is(expression.right, ArrayExpression))
|
|
35
33
|
return expression.right.elements.length === 0
|
|
36
34
|
? "1 = 0"
|
|
37
35
|
: `${expression.left.name} IN (${slots(expression.left.name, expression.right.elements.length)})`;
|
|
38
|
-
if (is(expression, Operator.In,
|
|
39
|
-
is(expression.left, Identifier, "Identifier") &&
|
|
40
|
-
is(expression.right, Subquery, "Subquery"))
|
|
36
|
+
if (is(expression, Operator.In) && is(expression.left, Identifier) && is(expression.right, Subquery))
|
|
41
37
|
return `${expression.left.name} IN ${this.render(expression.right)}`;
|
|
42
|
-
if (is(expression, Operator.NotIn,
|
|
43
|
-
is(expression.left, Identifier, "Identifier") &&
|
|
44
|
-
is(expression.right, ArrayExpression, "ArrayExpression"))
|
|
38
|
+
if (is(expression, Operator.NotIn) && is(expression.left, Identifier) && is(expression.right, ArrayExpression))
|
|
45
39
|
return expression.right.elements.length === 0
|
|
46
40
|
? "1 = 1"
|
|
47
41
|
: `${expression.left.name} NOT IN (${slots(expression.left.name, expression.right.elements.length)})`;
|
|
48
|
-
if (is(expression, Operator.NotIn,
|
|
49
|
-
is(expression.left, Identifier, "Identifier") &&
|
|
50
|
-
is(expression.right, Subquery, "Subquery"))
|
|
42
|
+
if (is(expression, Operator.NotIn) && is(expression.left, Identifier) && is(expression.right, Subquery))
|
|
51
43
|
return `${expression.left.name} NOT IN ${this.render(expression.right)}`;
|
|
52
|
-
if ((is(expression, Operator.Between
|
|
53
|
-
is(expression.value, Identifier
|
|
54
|
-
is(expression.low, Literal
|
|
55
|
-
is(expression.high, Literal
|
|
56
|
-
const word = is(expression, Operator.NotBetween
|
|
44
|
+
if ((is(expression, Operator.Between) || is(expression, Operator.NotBetween)) &&
|
|
45
|
+
is(expression.value, Identifier) &&
|
|
46
|
+
is(expression.low, Literal) &&
|
|
47
|
+
is(expression.high, Literal)) {
|
|
48
|
+
const word = is(expression, Operator.NotBetween) ? "NOT BETWEEN" : "BETWEEN";
|
|
57
49
|
return `${expression.value.name} ${word} $${parameterName(expression.value.name)}_0 AND $${parameterName(expression.value.name)}_1`;
|
|
58
50
|
}
|
|
59
|
-
if (is(expression, Operator.Between
|
|
51
|
+
if (is(expression, Operator.Between))
|
|
60
52
|
return `${this.render(expression.value)} BETWEEN ${this.render(expression.low)} AND ${this.render(expression.high)}`;
|
|
61
|
-
if (is(expression, Operator.NotBetween
|
|
53
|
+
if (is(expression, Operator.NotBetween))
|
|
62
54
|
return `${this.render(expression.value)} NOT BETWEEN ${this.render(expression.low)} AND ${this.render(expression.high)}`;
|
|
63
|
-
if (is(expression, Operator.And
|
|
55
|
+
if (is(expression, Operator.And))
|
|
64
56
|
return `${this.wrap(expression.left, true)} AND ${this.wrap(expression.right, true)}`;
|
|
65
|
-
if (is(expression, Operator.Or
|
|
57
|
+
if (is(expression, Operator.Or))
|
|
66
58
|
return `${this.wrap(expression.left, false)} OR ${this.wrap(expression.right, false)}`;
|
|
67
59
|
const operands = pair(expression);
|
|
68
|
-
if (operands && is(operands.left, Identifier
|
|
60
|
+
if (operands && is(operands.left, Identifier) && is(operands.right, Literal))
|
|
69
61
|
return `${operands.left.name} ${symbol(expression)} $${parameterName(operands.left.name)}`;
|
|
70
|
-
if (operands && is(operands.left, Literal
|
|
62
|
+
if (operands && is(operands.left, Literal) && is(operands.right, Literal))
|
|
71
63
|
return `${inline(operands.left.value)} ${symbol(expression)} ${inline(operands.right.value)}`;
|
|
72
64
|
if (operands)
|
|
73
65
|
return `${this.render(operands.left)} ${symbol(expression)} ${this.render(operands.right)}`;
|
|
@@ -75,34 +67,27 @@ export class ExpressionRenderer {
|
|
|
75
67
|
};
|
|
76
68
|
wrap = (child, and) => needsParens(child, and) ? `(${this.render(child)})` : this.render(child);
|
|
77
69
|
}
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
70
|
+
const symbol = (op) => {
|
|
71
|
+
if (is(op, Operator.Equals))
|
|
72
|
+
return "=";
|
|
73
|
+
if (is(op, Operator.NotEquals))
|
|
74
|
+
return "<>";
|
|
75
|
+
if (is(op, Operator.GreaterThan))
|
|
76
|
+
return ">";
|
|
77
|
+
if (is(op, Operator.LessThan))
|
|
78
|
+
return "<";
|
|
79
|
+
if (is(op, Operator.GreaterThanOrEqual))
|
|
80
|
+
return ">=";
|
|
81
|
+
if (is(op, Operator.LessThanOrEqual))
|
|
82
|
+
return "<=";
|
|
83
|
+
if (is(op, Operator.Like))
|
|
84
|
+
return "LIKE";
|
|
85
|
+
if (is(op, Operator.NotLike))
|
|
86
|
+
return "NOT LIKE";
|
|
87
|
+
if (is(op, Operator.In))
|
|
88
|
+
return "IN";
|
|
89
|
+
throw new Error("empty expression");
|
|
87
90
|
};
|
|
88
|
-
const symbol = (op) => symbols[op.constructor.name] ??
|
|
89
|
-
(is(op, Operator.Equals, "Equals")
|
|
90
|
-
? "="
|
|
91
|
-
: is(op, Operator.NotEquals, "NotEquals")
|
|
92
|
-
? "<>"
|
|
93
|
-
: is(op, Operator.GreaterThan, "GreaterThan")
|
|
94
|
-
? ">"
|
|
95
|
-
: is(op, Operator.LessThan, "LessThan")
|
|
96
|
-
? "<"
|
|
97
|
-
: is(op, Operator.GreaterThanOrEqual, "GreaterThanOrEqual")
|
|
98
|
-
? ">="
|
|
99
|
-
: is(op, Operator.LessThanOrEqual, "LessThanOrEqual")
|
|
100
|
-
? "<="
|
|
101
|
-
: is(op, Operator.Like, "Like")
|
|
102
|
-
? "LIKE"
|
|
103
|
-
: is(op, Operator.NotLike, "NotLike")
|
|
104
|
-
? "NOT LIKE"
|
|
105
|
-
: "IN");
|
|
106
91
|
const inline = (value) => value === null
|
|
107
92
|
? "null"
|
|
108
93
|
: typeof value === "boolean"
|
|
@@ -110,24 +95,25 @@ const inline = (value) => value === null
|
|
|
110
95
|
: typeof value === "string"
|
|
111
96
|
? `'${value.replace(/'/g, "''")}'`
|
|
112
97
|
: String(value);
|
|
113
|
-
const needsParens = (child, and) => is(child, Where
|
|
98
|
+
const needsParens = (child, and) => is(child, Where)
|
|
114
99
|
? needsParens(child.condition, and)
|
|
115
|
-
: is(child, Operator.Not
|
|
116
|
-
is(child, Operator.In
|
|
117
|
-
is(child, Operator.NotIn
|
|
118
|
-
(is(child, Operator.And
|
|
119
|
-
(is(child, Operator.Or
|
|
120
|
-
const pair = (expression) => is(expression, Operator.Equals
|
|
121
|
-
is(expression, Operator.NotEquals
|
|
122
|
-
is(expression, Operator.GreaterThan
|
|
123
|
-
is(expression, Operator.LessThan
|
|
124
|
-
is(expression, Operator.GreaterThanOrEqual
|
|
125
|
-
is(expression, Operator.LessThanOrEqual
|
|
126
|
-
is(expression, Operator.And
|
|
127
|
-
is(expression, Operator.Or
|
|
128
|
-
is(expression, Operator.In
|
|
129
|
-
is(expression, Operator.Like
|
|
130
|
-
is(expression, Operator.NotLike
|
|
100
|
+
: is(child, Operator.Not) ||
|
|
101
|
+
is(child, Operator.In) ||
|
|
102
|
+
is(child, Operator.NotIn) ||
|
|
103
|
+
(is(child, Operator.And) && !and) ||
|
|
104
|
+
(is(child, Operator.Or) && and);
|
|
105
|
+
const pair = (expression) => is(expression, Operator.Equals) ||
|
|
106
|
+
is(expression, Operator.NotEquals) ||
|
|
107
|
+
is(expression, Operator.GreaterThan) ||
|
|
108
|
+
is(expression, Operator.LessThan) ||
|
|
109
|
+
is(expression, Operator.GreaterThanOrEqual) ||
|
|
110
|
+
is(expression, Operator.LessThanOrEqual) ||
|
|
111
|
+
is(expression, Operator.And) ||
|
|
112
|
+
is(expression, Operator.Or) ||
|
|
113
|
+
is(expression, Operator.In) ||
|
|
114
|
+
is(expression, Operator.Like) ||
|
|
115
|
+
is(expression, Operator.NotLike) ||
|
|
116
|
+
is(expression, Operator.NotIn)
|
|
131
117
|
? expression
|
|
132
118
|
: undefined;
|
|
133
119
|
const parameterName = (name) => parts(name)
|
|
@@ -2,6 +2,7 @@ import { ArrayExpression } from "./ArrayExpression.js";
|
|
|
2
2
|
import { Condition } from "./Condition.js";
|
|
3
3
|
import { Identifier } from "./Identifier.js";
|
|
4
4
|
import { Literal } from "./Literal.js";
|
|
5
|
+
import { brand } from "./brand.js";
|
|
5
6
|
const pair = (left, right) => typeof left === "string"
|
|
6
7
|
? [new Identifier(left), new Literal(right)]
|
|
7
8
|
: [left, right];
|
|
@@ -205,3 +206,21 @@ export class Operator extends Condition {
|
|
|
205
206
|
}
|
|
206
207
|
Operator.Exists = Exists;
|
|
207
208
|
})(Operator || (Operator = {}));
|
|
209
|
+
brand(Operator.Equals);
|
|
210
|
+
brand(Operator.NotEquals);
|
|
211
|
+
brand(Operator.GreaterThan);
|
|
212
|
+
brand(Operator.LessThan);
|
|
213
|
+
brand(Operator.GreaterThanOrEqual);
|
|
214
|
+
brand(Operator.LessThanOrEqual);
|
|
215
|
+
brand(Operator.And);
|
|
216
|
+
brand(Operator.Or);
|
|
217
|
+
brand(Operator.In);
|
|
218
|
+
brand(Operator.Not);
|
|
219
|
+
brand(Operator.IsNull);
|
|
220
|
+
brand(Operator.IsNotNull);
|
|
221
|
+
brand(Operator.Like);
|
|
222
|
+
brand(Operator.NotLike);
|
|
223
|
+
brand(Operator.Between);
|
|
224
|
+
brand(Operator.NotBetween);
|
|
225
|
+
brand(Operator.NotIn);
|
|
226
|
+
brand(Operator.Exists);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { is } from "./brand.js";
|
|
1
2
|
import { ConditionRenderer } from "./ConditionRenderer.js";
|
|
2
3
|
import { ExpressionRenderer } from "./ExpressionRenderer.js";
|
|
3
4
|
import { StatementRenderer } from "./StatementRenderer.js";
|
|
@@ -12,16 +13,8 @@ export class StatementTextRenderer {
|
|
|
12
13
|
this.conditions = conditions?.(expressionRenderer) ?? new ConditionRenderer(expressionRenderer);
|
|
13
14
|
}
|
|
14
15
|
render(target) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (target.constructor.name.endsWith("Statement")) {
|
|
19
|
-
return target.accept(this.statements);
|
|
20
|
-
}
|
|
21
|
-
if (target instanceof Where || target instanceof WhereText)
|
|
22
|
-
return target.accept(this.conditions);
|
|
23
|
-
return "where" in target || "fields" in target || "joins" in target || "column" in target
|
|
24
|
-
? target.accept(this.statements)
|
|
25
|
-
: target.accept(this.conditions);
|
|
16
|
+
return is(target, Where) || is(target, WhereText)
|
|
17
|
+
? target.accept(this.conditions)
|
|
18
|
+
: target.accept(this.statements);
|
|
26
19
|
}
|
|
27
20
|
}
|
package/dist/statements/Where.js
CHANGED
|
@@ -5,6 +5,7 @@ import { Operator } from "./Operator.js";
|
|
|
5
5
|
import { eq } from "./Cmp.js";
|
|
6
6
|
import { SelectStatement } from "./SelectStatement.js";
|
|
7
7
|
import { Subquery } from "./Subquery.js";
|
|
8
|
+
import { brand } from "./brand.js";
|
|
8
9
|
const pair = (expression) => expression instanceof Operator.Equals ||
|
|
9
10
|
expression instanceof Operator.NotEquals ||
|
|
10
11
|
expression instanceof Operator.GreaterThan ||
|
|
@@ -181,3 +182,4 @@ export class Where {
|
|
|
181
182
|
return collect(this.condition);
|
|
182
183
|
}
|
|
183
184
|
}
|
|
185
|
+
brand(Where);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const key = (name) => Symbol.for(`@deterministic-code/datasources/${name}`);
|
|
2
|
+
export const brand = (type) => {
|
|
3
|
+
Object.defineProperty(type.prototype, key(type.name), { value: true });
|
|
4
|
+
};
|
|
5
|
+
export const is = (value, type) => value instanceof type || Boolean(value[key(type.name)]);
|
|
@@ -11,6 +11,8 @@ export declare class StubDatasource implements IDatasource {
|
|
|
11
11
|
readonly repositories: RepositoryFactory;
|
|
12
12
|
constructor(provider: string);
|
|
13
13
|
private defaultConnection;
|
|
14
|
+
close: () => Promise<void>;
|
|
15
|
+
[Symbol.asyncDispose]: () => Promise<void>;
|
|
14
16
|
createConnection: () => Promise<IConnection>;
|
|
15
17
|
createCommand: (connection?: IConnection) => Promise<ICommand>;
|
|
16
18
|
createRepository<T>(type: Constructable<T>, connection?: IConnection): ICRUDRepository<T>;
|
|
@@ -11,6 +11,12 @@ export class StubDatasource {
|
|
|
11
11
|
this.provider = provider;
|
|
12
12
|
}
|
|
13
13
|
defaultConnection = () => (this.connection ??= new StubConnection(this.provider));
|
|
14
|
+
close = async () => {
|
|
15
|
+
const connection = this.connection;
|
|
16
|
+
this.connection = undefined;
|
|
17
|
+
await connection?.close();
|
|
18
|
+
};
|
|
19
|
+
[Symbol.asyncDispose] = () => this.close();
|
|
14
20
|
createConnection = () => Promise.resolve(this.defaultConnection());
|
|
15
21
|
createCommand = async (connection) => {
|
|
16
22
|
if (!connection) {
|