@aurios/mizzle 1.1.2 → 1.1.3
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/.turbo/turbo-build.log +37 -0
- package/LICENSE +21 -0
- package/README.md +57 -57
- package/dist/chunk-AQVECMXP.js +1 -0
- package/dist/chunk-DU7UPWBW.js +1 -0
- package/dist/chunk-GPYZK4WY.js +1 -0
- package/dist/chunk-NPPZW6VT.js +1 -0
- package/dist/chunk-TOYV2M4M.js +1 -0
- package/dist/chunk-UM3YF5EC.js +1 -0
- package/dist/columns.d.ts +1 -0
- package/dist/columns.js +1 -0
- package/dist/db-zHIHBm1E.d.ts +815 -0
- package/dist/db.d.ts +3 -0
- package/dist/db.js +1 -0
- package/dist/diff.d.ts +18 -0
- package/dist/diff.js +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +1 -0
- package/dist/introspection.d.ts +7 -0
- package/dist/introspection.js +1 -0
- package/dist/operators-BVreW0ky.d.ts +719 -0
- package/dist/snapshot.d.ts +24 -0
- package/dist/snapshot.js +1 -0
- package/dist/table.d.ts +1 -0
- package/dist/table.js +1 -0
- package/dist/transaction-RE7LXTGV.js +1 -0
- package/package.json +82 -66
- package/src/builders/base.ts +53 -56
- package/src/builders/batch-get.ts +63 -58
- package/src/builders/batch-write.ts +81 -78
- package/src/builders/delete.ts +46 -53
- package/src/builders/insert.ts +158 -150
- package/src/builders/query-promise.ts +26 -35
- package/src/builders/relational-builder.ts +214 -191
- package/src/builders/select.ts +250 -237
- package/src/builders/transaction.ts +170 -152
- package/src/builders/update.ts +197 -192
- package/src/columns/binary-set.ts +29 -38
- package/src/columns/binary.ts +25 -35
- package/src/columns/boolean.ts +25 -30
- package/src/columns/date.ts +57 -64
- package/src/columns/index.ts +15 -15
- package/src/columns/json.ts +39 -48
- package/src/columns/list.ts +26 -36
- package/src/columns/map.ts +26 -34
- package/src/columns/number-set.ts +29 -38
- package/src/columns/number.ts +33 -40
- package/src/columns/string-set.ts +38 -47
- package/src/columns/string.ts +37 -49
- package/src/columns/uuid.ts +26 -33
- package/src/core/client.ts +9 -9
- package/src/core/column-builder.ts +194 -220
- package/src/core/column.ts +127 -135
- package/src/core/diff.ts +40 -34
- package/src/core/errors.ts +20 -17
- package/src/core/introspection.ts +62 -58
- package/src/core/operations.ts +17 -23
- package/src/core/parser.ts +82 -89
- package/src/core/relations.ts +164 -154
- package/src/core/retry.ts +52 -52
- package/src/core/snapshot.ts +131 -130
- package/src/core/strategies.ts +222 -218
- package/src/core/table.ts +189 -202
- package/src/core/validation.ts +52 -52
- package/src/db.ts +211 -209
- package/src/expressions/actions.ts +26 -26
- package/src/expressions/builder.ts +62 -54
- package/src/expressions/operators.ts +48 -48
- package/src/expressions/update-builder.ts +78 -76
- package/src/index.ts +1 -1
- package/src/indexes.ts +8 -8
- package/test/batch-resilience.test.ts +138 -0
- package/test/builders/delete.test.ts +100 -0
- package/test/builders/insert.test.ts +216 -0
- package/test/builders/relational-types.test.ts +55 -0
- package/test/builders/relational.integration.test.ts +291 -0
- package/test/builders/relational.test.ts +66 -0
- package/test/builders/select.test.ts +411 -0
- package/test/builders/transaction-errors.test.ts +46 -0
- package/test/builders/transaction-execution.test.ts +99 -0
- package/test/builders/transaction-proxy.test.ts +41 -0
- package/test/builders/update-expression.test.ts +106 -0
- package/test/builders/update.test.ts +179 -0
- package/test/core/diff.test.ts +152 -0
- package/test/core/expressions.test.ts +64 -0
- package/test/core/introspection.test.ts +47 -0
- package/test/core/parser.test.ts +69 -0
- package/test/core/snapshot-gen.test.ts +155 -0
- package/test/core/snapshot.test.ts +52 -0
- package/test/date-column.test.ts +159 -0
- package/test/fluent-writes.integration.test.ts +148 -0
- package/test/integration-retry.test.ts +77 -0
- package/test/integration.test.ts +105 -0
- package/test/item-size-error.test.ts +16 -0
- package/test/item-size-validation.test.ts +82 -0
- package/test/item-size.test.ts +47 -0
- package/test/iterator-pagination.integration.test.ts +132 -0
- package/test/jsdoc-builders.test.ts +55 -0
- package/test/jsdoc-schema.test.ts +107 -0
- package/test/json-column.test.ts +51 -0
- package/test/metadata.test.ts +54 -0
- package/test/mizzle-package.test.ts +20 -0
- package/test/relational-centralized.test.ts +83 -0
- package/test/relational-definition.test.ts +75 -0
- package/test/relational-init.test.ts +30 -0
- package/test/relational-proxy.test.ts +52 -0
- package/test/relations.test.ts +45 -0
- package/test/resilience-config.test.ts +34 -0
- package/test/retry-handler.test.ts +63 -0
- package/test/transaction.integration.test.ts +153 -0
- package/test/unified-select.integration.test.ts +153 -0
- package/test/unified-update.integration.test.ts +139 -0
- package/test/update.integration.test.ts +132 -0
- package/tsconfig.json +12 -8
- package/tsup.config.ts +11 -11
- package/vitest.config.ts +8 -0
package/src/core/retry.ts
CHANGED
|
@@ -1,71 +1,71 @@
|
|
|
1
1
|
export interface RetryConfig {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Maximum number of attempts (including the initial one).
|
|
4
|
+
* @default 3
|
|
5
|
+
*/
|
|
6
|
+
maxAttempts: number;
|
|
7
|
+
/**
|
|
8
|
+
* Base delay in milliseconds for exponential backoff.
|
|
9
|
+
* @default 100
|
|
10
|
+
*/
|
|
11
|
+
baseDelay: number;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const RETRYABLE_ERRORS = new Set([
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
"ProvisionedThroughputExceededException",
|
|
16
|
+
"RequestLimitExceeded",
|
|
17
|
+
"InternalServerError",
|
|
18
|
+
"ServiceUnavailable",
|
|
19
|
+
"ThrottlingException",
|
|
20
20
|
]);
|
|
21
21
|
|
|
22
22
|
const RETRYABLE_STATUS_CODES = new Set([500, 503]);
|
|
23
23
|
|
|
24
24
|
export class RetryHandler {
|
|
25
|
-
|
|
25
|
+
constructor(private config: RetryConfig) {}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
await this.delay(attempt - 1);
|
|
41
|
-
}
|
|
27
|
+
async execute<T>(operation: () => Promise<T>): Promise<T> {
|
|
28
|
+
let lastError: unknown;
|
|
29
|
+
|
|
30
|
+
for (let attempt = 1; attempt <= this.config.maxAttempts; attempt++) {
|
|
31
|
+
try {
|
|
32
|
+
return await operation();
|
|
33
|
+
} catch (error) {
|
|
34
|
+
lastError = error;
|
|
35
|
+
|
|
36
|
+
if (attempt >= this.config.maxAttempts || !this.isRetryable(error)) {
|
|
37
|
+
throw error;
|
|
42
38
|
}
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
|
|
40
|
+
await this.delay(attempt - 1);
|
|
41
|
+
}
|
|
45
42
|
}
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const err = error as { name?: string; $metadata?: { httpStatusCode?: number } };
|
|
51
|
-
if (err.name && RETRYABLE_ERRORS.has(err.name)) {
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
44
|
+
throw lastError;
|
|
45
|
+
}
|
|
54
46
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
47
|
+
private isRetryable(error: unknown): boolean {
|
|
48
|
+
if (!error || typeof error !== "object") return false;
|
|
58
49
|
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
const err = error as { name?: string; $metadata?: { httpStatusCode?: number } };
|
|
51
|
+
if (err.name && RETRYABLE_ERRORS.has(err.name)) {
|
|
52
|
+
return true;
|
|
61
53
|
}
|
|
62
54
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const jitter = Math.random() * base; // Full jitter or equal jitter? Standard is random between 0 and base.
|
|
66
|
-
// Cap at some reasonable max if needed, but for now standard exponential + jitter
|
|
67
|
-
const delayTime = base + jitter;
|
|
68
|
-
|
|
69
|
-
return new Promise(resolve => setTimeout(resolve, delayTime));
|
|
55
|
+
if (err.$metadata?.httpStatusCode && RETRYABLE_STATUS_CODES.has(err.$metadata.httpStatusCode)) {
|
|
56
|
+
return true;
|
|
70
57
|
}
|
|
58
|
+
|
|
59
|
+
// AWS SDK v3 specific error structure sometimes wraps things
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private async delay(retryCount: number): Promise<void> {
|
|
64
|
+
const base = this.config.baseDelay * Math.pow(2, retryCount);
|
|
65
|
+
const jitter = Math.random() * base; // Full jitter or equal jitter? Standard is random between 0 and base.
|
|
66
|
+
// Cap at some reasonable max if needed, but for now standard exponential + jitter
|
|
67
|
+
const delayTime = base + jitter;
|
|
68
|
+
|
|
69
|
+
return new Promise((resolve) => setTimeout(resolve, delayTime));
|
|
70
|
+
}
|
|
71
71
|
}
|
package/src/core/snapshot.ts
CHANGED
|
@@ -2,21 +2,21 @@ import { join } from "path";
|
|
|
2
2
|
import { writeFile, readFile, mkdir, readdir } from "fs/promises";
|
|
3
3
|
import { existsSync } from "fs";
|
|
4
4
|
import { PhysicalTable, Entity } from "./table";
|
|
5
|
-
import { TABLE_SYMBOLS, ENTITY_SYMBOLS } from "@
|
|
5
|
+
import { TABLE_SYMBOLS, ENTITY_SYMBOLS } from "@repo/shared";
|
|
6
6
|
import { Column } from "./column";
|
|
7
7
|
import type {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
AttributeDefinition,
|
|
9
|
+
KeySchemaElement,
|
|
10
|
+
GlobalSecondaryIndex,
|
|
11
|
+
LocalSecondaryIndex,
|
|
12
12
|
} from "@aws-sdk/client-dynamodb";
|
|
13
13
|
|
|
14
14
|
export interface TableSnapshot {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
TableName: string;
|
|
16
|
+
AttributeDefinitions: AttributeDefinition[];
|
|
17
|
+
KeySchema: KeySchemaElement[];
|
|
18
|
+
GlobalSecondaryIndexes?: GlobalSecondaryIndex[];
|
|
19
|
+
LocalSecondaryIndexes?: LocalSecondaryIndex[];
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export interface MizzleSnapshot {
|
|
@@ -25,8 +25,8 @@ export interface MizzleSnapshot {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export interface SchemaCurrent {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
tables: PhysicalTable[];
|
|
29
|
+
entities: Entity[];
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const SNAPSHOT_FILENAME = "snapshot.json";
|
|
@@ -49,144 +49,145 @@ export async function loadSnapshot(dir: string): Promise<MizzleSnapshot | null>
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
export function generateSnapshot(schema: SchemaCurrent): MizzleSnapshot {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
52
|
+
const tables: Record<string, TableSnapshot> = {};
|
|
53
|
+
|
|
54
|
+
for (const table of schema.tables) {
|
|
55
|
+
const associatedEntities = schema.entities.filter(
|
|
56
|
+
(e) => e[ENTITY_SYMBOLS.PHYSICAL_TABLE] === table,
|
|
57
|
+
);
|
|
58
|
+
const tableSnapshot = physicalTableToSnapshot(table, associatedEntities);
|
|
59
|
+
tables[tableSnapshot.TableName] = tableSnapshot;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
version: "1",
|
|
64
|
+
tables,
|
|
65
|
+
};
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
export async function getNextMigrationVersion(migrationsDir: string): Promise<string> {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
69
|
+
if (!existsSync(migrationsDir)) return "0000";
|
|
70
|
+
|
|
71
|
+
const files = await readdir(migrationsDir);
|
|
72
|
+
let maxVersion = -1;
|
|
73
|
+
|
|
74
|
+
for (const file of files) {
|
|
75
|
+
if (!file.endsWith(".ts")) continue;
|
|
76
|
+
const match = file.match(/^(\d{4})_/);
|
|
77
|
+
if (match) {
|
|
78
|
+
const version = parseInt(match[1]!, 10);
|
|
79
|
+
if (version > maxVersion) maxVersion = version;
|
|
79
80
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (maxVersion === -1) return "0000";
|
|
84
|
+
return (maxVersion + 1).toString().padStart(4, "0");
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
function physicalTableToSnapshot(table: PhysicalTable, entities: Entity[]): TableSnapshot {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
const tableName = table[TABLE_SYMBOLS.TABLE_NAME];
|
|
89
|
+
const attributeDefinitionsMap = new Map<string, string>(); // Name -> Type
|
|
88
90
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
// PK
|
|
92
|
+
const pk = table[TABLE_SYMBOLS.PARTITION_KEY] as Column;
|
|
93
|
+
attributeDefinitionsMap.set(pk.name, pk.getDynamoType());
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
// SK
|
|
96
|
+
const sk = table[TABLE_SYMBOLS.SORT_KEY] as Column | undefined;
|
|
97
|
+
if (sk) {
|
|
98
|
+
attributeDefinitionsMap.set(sk.name, sk.getDynamoType());
|
|
99
|
+
}
|
|
98
100
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
keySchema.push({ AttributeName: sk.name, KeyType: "RANGE" });
|
|
104
|
-
}
|
|
101
|
+
const keySchema: KeySchemaElement[] = [{ AttributeName: pk.name, KeyType: "HASH" }];
|
|
102
|
+
if (sk) {
|
|
103
|
+
keySchema.push({ AttributeName: sk.name, KeyType: "RANGE" });
|
|
104
|
+
}
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
Projection: { ProjectionType: "ALL" }
|
|
149
|
-
};
|
|
150
|
-
lsis.push(lsiDef);
|
|
151
|
-
}
|
|
106
|
+
const gsis: GlobalSecondaryIndex[] = [];
|
|
107
|
+
const lsis: LocalSecondaryIndex[] = [];
|
|
108
|
+
|
|
109
|
+
const indexes = table[TABLE_SYMBOLS.INDEXES] || {};
|
|
110
|
+
for (const [indexName, indexBuilder] of Object.entries(indexes)) {
|
|
111
|
+
const type = (indexBuilder as { type: string }).type;
|
|
112
|
+
const config = (indexBuilder as { config: { pk?: string; sk?: string } }).config;
|
|
113
|
+
|
|
114
|
+
if (type === "gsi") {
|
|
115
|
+
if (config.pk) {
|
|
116
|
+
const pkType = resolveColumnType(config.pk, table, entities);
|
|
117
|
+
attributeDefinitionsMap.set(config.pk, pkType);
|
|
118
|
+
}
|
|
119
|
+
if (config.sk) {
|
|
120
|
+
const skType = resolveColumnType(config.sk, table, entities);
|
|
121
|
+
attributeDefinitionsMap.set(config.sk, skType);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const gsiDef: GlobalSecondaryIndex = {
|
|
125
|
+
IndexName: indexName,
|
|
126
|
+
KeySchema: [{ AttributeName: config.pk!, KeyType: "HASH" }],
|
|
127
|
+
Projection: { ProjectionType: "ALL" },
|
|
128
|
+
};
|
|
129
|
+
if (config.sk) {
|
|
130
|
+
gsiDef.KeySchema!.push({ AttributeName: config.sk, KeyType: "RANGE" });
|
|
131
|
+
}
|
|
132
|
+
gsis.push(gsiDef);
|
|
133
|
+
} else if (type === "lsi") {
|
|
134
|
+
if (config.sk) {
|
|
135
|
+
const skType = resolveColumnType(config.sk, table, entities);
|
|
136
|
+
attributeDefinitionsMap.set(config.sk, skType);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const lsiDef: LocalSecondaryIndex = {
|
|
140
|
+
IndexName: indexName,
|
|
141
|
+
KeySchema: [
|
|
142
|
+
{ AttributeName: pk.name, KeyType: "HASH" },
|
|
143
|
+
{ AttributeName: config.sk!, KeyType: "RANGE" },
|
|
144
|
+
],
|
|
145
|
+
Projection: { ProjectionType: "ALL" },
|
|
146
|
+
};
|
|
147
|
+
lsis.push(lsiDef);
|
|
152
148
|
}
|
|
149
|
+
}
|
|
153
150
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
151
|
+
const attributeDefinitions = Array.from(attributeDefinitionsMap.entries())
|
|
152
|
+
.map(([name, type]) => ({
|
|
153
|
+
AttributeName: name,
|
|
154
|
+
AttributeType: type as "S" | "N" | "B",
|
|
155
|
+
}))
|
|
156
|
+
.sort((a, b) => a.AttributeName.localeCompare(b.AttributeName));
|
|
158
157
|
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
gsis.sort((a, b) => (a.IndexName || "").localeCompare(b.IndexName || ""));
|
|
159
|
+
lsis.sort((a, b) => (a.IndexName || "").localeCompare(b.IndexName || ""));
|
|
161
160
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
const result: TableSnapshot = {
|
|
162
|
+
TableName: tableName as string,
|
|
163
|
+
AttributeDefinitions: attributeDefinitions,
|
|
164
|
+
KeySchema: keySchema,
|
|
165
|
+
};
|
|
167
166
|
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
if (gsis.length > 0) result.GlobalSecondaryIndexes = gsis;
|
|
168
|
+
if (lsis.length > 0) result.LocalSecondaryIndexes = lsis;
|
|
170
169
|
|
|
171
|
-
|
|
170
|
+
return result;
|
|
172
171
|
}
|
|
173
172
|
|
|
174
173
|
function resolveColumnType(columnName: string, table: PhysicalTable, entities: Entity[]): string {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
174
|
+
const pk = table[TABLE_SYMBOLS.PARTITION_KEY] as Column;
|
|
175
|
+
if (pk.name === columnName) return pk.getDynamoType();
|
|
176
|
+
|
|
177
|
+
const sk = table[TABLE_SYMBOLS.SORT_KEY] as Column | undefined;
|
|
178
|
+
if (sk && sk.name === columnName) return sk.getDynamoType();
|
|
179
|
+
|
|
180
|
+
for (const entity of entities) {
|
|
181
|
+
const columns = entity[ENTITY_SYMBOLS.COLUMNS] as Record<string, Column> | undefined;
|
|
182
|
+
if (columns) {
|
|
183
|
+
const col = columns[columnName];
|
|
184
|
+
if (col) {
|
|
185
|
+
return col.getDynamoType();
|
|
186
|
+
}
|
|
189
187
|
}
|
|
188
|
+
}
|
|
190
189
|
|
|
191
|
-
|
|
190
|
+
throw new Error(
|
|
191
|
+
`Could not resolve type for column '${columnName}' in table '${table[TABLE_SYMBOLS.TABLE_NAME]}'. Ensure it is defined in an Entity.`,
|
|
192
|
+
);
|
|
192
193
|
}
|