@travetto/model-sql 8.0.0-alpha.23 → 8.0.0-alpha.25
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/README.md +2 -2
- package/__index__.ts +2 -2
- package/package.json +8 -8
- package/src/config.ts +1 -1
- package/src/connection/base.ts +8 -7
- package/src/connection/decorator.ts +5 -10
- package/src/dialect/base.ts +152 -112
- package/src/internal/types.ts +2 -2
- package/src/service.ts +139 -116
- package/src/table-manager.ts +20 -12
- package/src/types.ts +1 -1
- package/src/util.ts +43 -22
- package/support/test/query.ts +8 -5
package/src/util.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { castKey, castTo, type Class, TypedObject } from '@travetto/runtime';
|
|
2
|
-
import type { SelectClause, SortClause } from '@travetto/model-query';
|
|
3
1
|
import { ModelRegistryIndex, type ModelType, type OptionalId } from '@travetto/model';
|
|
4
|
-
import
|
|
2
|
+
import type { SelectClause, SortClause } from '@travetto/model-query';
|
|
3
|
+
import { type Class, castKey, castTo, TypedObject } from '@travetto/runtime';
|
|
4
|
+
import { DataUtil, type SchemaClassConfig, type SchemaFieldConfig, SchemaRegistryIndex } from '@travetto/schema';
|
|
5
5
|
|
|
6
|
-
import type { DialectState, InsertWrapper,
|
|
6
|
+
import type { DialectState, InsertWrapper, OrderBy, VisitHandler, VisitInstanceNode, VisitState } from './internal/types.ts';
|
|
7
7
|
import { TableSymbol, type VisitStack } from './types.ts';
|
|
8
8
|
|
|
9
9
|
type FieldCacheEntry = {
|
|
@@ -17,7 +17,6 @@ type FieldCacheEntry = {
|
|
|
17
17
|
* Utilities for dealing with SQL operations
|
|
18
18
|
*/
|
|
19
19
|
export class SQLModelUtil {
|
|
20
|
-
|
|
21
20
|
static #schemaFieldsCache = new Map<Class, FieldCacheEntry>();
|
|
22
21
|
|
|
23
22
|
/**
|
|
@@ -37,7 +36,13 @@ export class SQLModelUtil {
|
|
|
37
36
|
return item.filter(value => value !== null && value !== undefined).map(value => this.cleanResults(state, value));
|
|
38
37
|
} else if (!DataUtil.isSimpleValue(item)) {
|
|
39
38
|
for (const key of TypedObject.keys(item)) {
|
|
40
|
-
if (
|
|
39
|
+
if (
|
|
40
|
+
item[key] === null ||
|
|
41
|
+
item[key] === undefined ||
|
|
42
|
+
key === state.parentPathField.name ||
|
|
43
|
+
key === state.pathField.name ||
|
|
44
|
+
key === state.idxField.name
|
|
45
|
+
) {
|
|
41
46
|
delete item[key];
|
|
42
47
|
} else {
|
|
43
48
|
item[key] = this.cleanResults(state, item[key]);
|
|
@@ -60,11 +65,14 @@ export class SQLModelUtil {
|
|
|
60
65
|
return this.#schemaFieldsCache.get(config.class)!;
|
|
61
66
|
}
|
|
62
67
|
|
|
63
|
-
if (!config) {
|
|
68
|
+
if (!config) {
|
|
69
|
+
// If a simple type, it is it's own field
|
|
64
70
|
const field: SchemaFieldConfig = castTo({ ...top });
|
|
65
71
|
return {
|
|
66
|
-
local: [field],
|
|
67
|
-
|
|
72
|
+
local: [field],
|
|
73
|
+
localMap: { [field.name]: field },
|
|
74
|
+
foreign: [],
|
|
75
|
+
foreignMap: {}
|
|
68
76
|
};
|
|
69
77
|
}
|
|
70
78
|
|
|
@@ -103,7 +111,11 @@ export class SQLModelUtil {
|
|
|
103
111
|
/**
|
|
104
112
|
* Process a schema structure, synchronously
|
|
105
113
|
*/
|
|
106
|
-
static visitSchemaSync(
|
|
114
|
+
static visitSchemaSync(
|
|
115
|
+
config: SchemaClassConfig | SchemaFieldConfig,
|
|
116
|
+
handler: VisitHandler<void>,
|
|
117
|
+
state: VisitState = { path: [] }
|
|
118
|
+
): void {
|
|
107
119
|
const path = 'fields' in config ? this.classToStack(config.class) : [...state.path, config];
|
|
108
120
|
const { local: fields, foreign } = this.getFieldsByLocation(path);
|
|
109
121
|
|
|
@@ -122,16 +134,20 @@ export class SQLModelUtil {
|
|
|
122
134
|
};
|
|
123
135
|
|
|
124
136
|
if ('fields' in config) {
|
|
125
|
-
|
|
137
|
+
handler.onRoot({ config, fields, descend, path });
|
|
126
138
|
} else {
|
|
127
|
-
|
|
139
|
+
handler.onSub({ config, fields, descend, path });
|
|
128
140
|
}
|
|
129
141
|
}
|
|
130
142
|
|
|
131
143
|
/**
|
|
132
144
|
* Visit a Schema structure
|
|
133
145
|
*/
|
|
134
|
-
static async visitSchema(
|
|
146
|
+
static async visitSchema(
|
|
147
|
+
config: SchemaClassConfig | SchemaFieldConfig,
|
|
148
|
+
handler: VisitHandler<Promise<void>>,
|
|
149
|
+
state: VisitState = { path: [] }
|
|
150
|
+
): Promise<void> {
|
|
135
151
|
const path = 'fields' in config ? this.classToStack(config.class) : [...state.path, config];
|
|
136
152
|
const { local: fields, foreign } = this.getFieldsByLocation(path);
|
|
137
153
|
|
|
@@ -159,16 +175,20 @@ export class SQLModelUtil {
|
|
|
159
175
|
/**
|
|
160
176
|
* Process a schema instance by visiting it synchronously. This is synchronous to prevent concurrent calls from breaking
|
|
161
177
|
*/
|
|
162
|
-
static visitSchemaInstance<T extends ModelType>(
|
|
178
|
+
static visitSchemaInstance<T extends ModelType>(
|
|
179
|
+
cls: Class<T>,
|
|
180
|
+
instance: T | OptionalId<T>,
|
|
181
|
+
handler: VisitHandler<unknown, VisitInstanceNode<unknown>>
|
|
182
|
+
): void {
|
|
163
183
|
const pathStack: unknown[] = [instance];
|
|
164
184
|
this.visitSchemaSync(SchemaRegistryIndex.getConfig(cls), {
|
|
165
|
-
onRoot:
|
|
185
|
+
onRoot: config => {
|
|
166
186
|
const { path } = config;
|
|
167
187
|
path[0].name = instance.id!;
|
|
168
188
|
handler.onRoot({ ...config, value: instance });
|
|
169
189
|
return config.descend();
|
|
170
190
|
},
|
|
171
|
-
onSub:
|
|
191
|
+
onSub: config => {
|
|
172
192
|
const { config: field } = config;
|
|
173
193
|
const topObject: Record<string, unknown> = castTo(pathStack.at(-1));
|
|
174
194
|
const top = config.path.at(-1)!;
|
|
@@ -196,7 +216,7 @@ export class SQLModelUtil {
|
|
|
196
216
|
}
|
|
197
217
|
}
|
|
198
218
|
},
|
|
199
|
-
onSimple:
|
|
219
|
+
onSimple: config => {
|
|
200
220
|
const { config: field } = config;
|
|
201
221
|
const topObject: Record<string, unknown> = castTo(pathStack.at(-1));
|
|
202
222
|
const value = topObject[field.name];
|
|
@@ -266,7 +286,7 @@ export class SQLModelUtil {
|
|
|
266
286
|
for (const item of items) {
|
|
267
287
|
const parentKey: string = castTo(item[castKey<T>(state.parentPathField.name)]);
|
|
268
288
|
const root = castTo<Record<string, Record<string, unknown>>>(parent)[parentKey];
|
|
269
|
-
const fieldKey = castKey<
|
|
289
|
+
const fieldKey = castKey<typeof root | T>(field.name);
|
|
270
290
|
if (field.array) {
|
|
271
291
|
if (!root[fieldKey]) {
|
|
272
292
|
root[fieldKey] = [isSimple ? item : item[fieldKey]];
|
|
@@ -295,7 +315,7 @@ export class SQLModelUtil {
|
|
|
295
315
|
static buildTable(list: VisitStack[]): string {
|
|
296
316
|
const top = list.at(-1)!;
|
|
297
317
|
if (!top[TableSymbol]) {
|
|
298
|
-
top[TableSymbol] = list.map((item, i) => i === 0 ? ModelRegistryIndex.getStoreName(item.type) : item.name).join('_');
|
|
318
|
+
top[TableSymbol] = list.map((item, i) => (i === 0 ? ModelRegistryIndex.getStoreName(item.type) : item.name)).join('_');
|
|
299
319
|
}
|
|
300
320
|
return top[TableSymbol]!;
|
|
301
321
|
}
|
|
@@ -304,7 +324,7 @@ export class SQLModelUtil {
|
|
|
304
324
|
* Build property path for a table/field given the current stack
|
|
305
325
|
*/
|
|
306
326
|
static buildPath(list: VisitStack[]): string {
|
|
307
|
-
return list.map(
|
|
327
|
+
return list.map(item => `${item.name}${item.index ? `[${item.index}]` : ''}`).join('.');
|
|
308
328
|
}
|
|
309
329
|
|
|
310
330
|
/**
|
|
@@ -323,9 +343,10 @@ export class SQLModelUtil {
|
|
|
323
343
|
onRoot: ({ path, value }) => track(path, value),
|
|
324
344
|
onSub: ({ path, value }) => track(path, value),
|
|
325
345
|
onSimple: ({ path, value }) => track(path, value)
|
|
326
|
-
})
|
|
346
|
+
})
|
|
347
|
+
);
|
|
327
348
|
|
|
328
349
|
const result = [...Object.values(wrappers)].toSorted((a, b) => a.stack.length - b.stack.length);
|
|
329
350
|
return result;
|
|
330
351
|
}
|
|
331
|
-
}
|
|
352
|
+
}
|
package/support/test/query.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
|
|
3
|
+
import { castTo } from '@travetto/runtime';
|
|
3
4
|
import { Schema, type SchemaFieldConfig } from '@travetto/schema';
|
|
4
5
|
import { Suite, Test } from '@travetto/test';
|
|
5
|
-
|
|
6
|
+
|
|
6
7
|
import { BaseModelSuite } from '@travetto/model/support/test/base.ts';
|
|
7
8
|
|
|
8
|
-
import type { VisitStack } from '../../src/types.ts';
|
|
9
9
|
import type { SQLModelService } from '../../src/service.ts';
|
|
10
|
+
import type { VisitStack } from '../../src/types.ts';
|
|
10
11
|
|
|
11
12
|
@Schema()
|
|
12
13
|
class User {
|
|
@@ -46,7 +47,6 @@ class WhereType {
|
|
|
46
47
|
|
|
47
48
|
@Suite()
|
|
48
49
|
export abstract class BaseSQLTest extends BaseModelSuite<SQLModelService> {
|
|
49
|
-
|
|
50
50
|
get dialect() {
|
|
51
51
|
return this.service.then(s => s.client);
|
|
52
52
|
}
|
|
@@ -73,7 +73,10 @@ export abstract class BaseSQLTest extends BaseModelSuite<SQLModelService> {
|
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
const qryStr = dct.getWhereGroupingSQL(WhereType, qry);
|
|
76
|
-
assert(
|
|
76
|
+
assert(
|
|
77
|
+
qryStr ===
|
|
78
|
+
"(WhereTypeAB.c = 5 AND WhereTypeD.e = TRUE AND (WhereType.name = 5 OR WhereType.age = 10) AND z.z IN ('a','b','c') AND WhereTypeA.d > 20)"
|
|
79
|
+
);
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
@Test()
|
|
@@ -100,4 +103,4 @@ export abstract class BaseSQLTest extends BaseModelSuite<SQLModelService> {
|
|
|
100
103
|
|
|
101
104
|
assert(outBoundary === `User.name ${dct.SQL_OPS.$regex} '${dct.regexWordBoundary}google${dct.regexWordBoundary}'`);
|
|
102
105
|
}
|
|
103
|
-
}
|
|
106
|
+
}
|