@uql/core 3.4.4 → 3.5.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/CHANGELOG.md +13 -2
- package/README.md +1 -1
- package/dist/browser/uql-browser.min.js +810 -964
- package/dist/browser/uql-browser.min.js.map +1 -1
- package/dist/migrate/generator/postgresSchemaGenerator.js +1 -1
- package/dist/migrate/generator/postgresSchemaGenerator.js.map +1 -1
- package/dist/migrate/schemaGenerator.d.ts.map +1 -1
- package/dist/migrate/schemaGenerator.js +3 -4
- package/dist/migrate/schemaGenerator.js.map +1 -1
- package/package.json +6 -9
|
@@ -1,1018 +1,864 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import '../
|
|
5
|
-
import {
|
|
6
|
-
import '
|
|
7
|
-
import {
|
|
1
|
+
import { isSqlQuerier } from '../type/index.js';
|
|
2
|
+
export { isSqlQuerier } from '../type/index.js';
|
|
3
|
+
import { AbstractDialect } from '../dialect/index.js';
|
|
4
|
+
import { getMeta, getEntities } from '../entity/index.js';
|
|
5
|
+
import { escapeSqlId, getKeys } from '../util/index.js';
|
|
6
|
+
import { readdir, readFile, mkdir, writeFile } from 'node:fs/promises';
|
|
7
|
+
import { join, basename, extname, dirname } from 'node:path';
|
|
8
|
+
import { pathToFileURL } from 'node:url';
|
|
8
9
|
|
|
9
|
-
var _dec, _dec1, _dec2, _dec3, _dec4, _dec5, _dec6, /**
|
|
10
|
-
* auto-generated primary-key (when the `onInsert` property is omitted).
|
|
11
|
-
*/ _init_id, /**
|
|
12
|
-
* foreign-keys are really simple to specify with the `reference` property.
|
|
13
|
-
*/ _init_companyId, /**
|
|
14
|
-
* The `Relation` wrapper type can be used in ESM projects for the relations to
|
|
15
|
-
* avoid circular dependency issues.
|
|
16
|
-
*/ _init_company, _init_creatorId, _init_creator, /**
|
|
17
|
-
* 'onInsert' property can be used to specify a custom mechanism for
|
|
18
|
-
* obtaining the value of a field when inserting:
|
|
19
|
-
*/ _init_createdAt, /**
|
|
20
|
-
* 'onUpdate' property can be used to specify a custom mechanism for
|
|
21
|
-
* obtaining the value of a field when updating:
|
|
22
|
-
*/ _init_updatedAt, _dec7, _initClass, _BaseEntity, _dec8, _dec9, _dec10, _init_name, _init_description, _init_kind, _dec11, _initClass1, _BaseEntity1, _dec12, _dec13, /**
|
|
23
|
-
* an entity can specify its own ID Field and still inherit the others
|
|
24
|
-
* columns/relations from its parent entity.
|
|
25
|
-
*/ _init_pk, _init_picture, _dec14, _initClass2, _BaseEntity2, _dec15, _dec16, _dec17, _dec18, _dec19, _init_name1, _init_email, _init_password, /**
|
|
26
|
-
* `mappedBy` property can be a callback or a string (callback is useful for auto-refactoring).
|
|
27
|
-
*/ _init_profile, _init_users, _dec20, _initClass3, _dec21, _dec22, _init_id1, _init_name2, _dec23, _initClass4, _BaseEntity3, _dec24, _dec25, _dec26, _dec27, _init_name3, _init_description1, _init_parentLedgerId, _init_parentLedger, _dec28, _initClass5, _BaseEntity4, _dec29, _dec30, _dec31, /**
|
|
28
|
-
* an entity can override the ID Field and still inherit the others
|
|
29
|
-
* columns/relations from its parent entity.
|
|
30
|
-
* 'onInsert' property can be used to specify a custom mechanism for
|
|
31
|
-
* auto-generating the primary-key's value when inserting.
|
|
32
|
-
*/ _init_pk1, _init_name4, _init_description2, _dec32, _initClass6, _BaseEntity5, _dec33, _dec34, _dec35, _dec36, _dec37, _init_name5, _init_percentage, _init_categoryId, _init_category, _init_description3, _dec38, _initClass7, _BaseEntity6, _dec39, _dec40, _dec41, _init_name6, _init_measureUnits, /**
|
|
33
|
-
* `onDelete` callback allows to specify which field will be used when deleting/querying this entity.
|
|
34
|
-
*/ _init_deletedAt, _dec42, _initClass8, _BaseEntity7, _dec43, _dec44, _dec45, _dec46, _init_name7, _init_categoryId1, _init_category1, _init_deletedAt1, _dec47, _initClass9, _BaseEntity8, _dec48, _dec49, _dec50, _init_name8, _init_address, _init_description4, _dec51, _initClass10, _BaseEntity9, _dec52, _dec53, _dec54, _dec55, _dec56, _dec57, _dec58, _dec59, _dec60, _dec61, _dec62, _dec63, _dec64, _dec65, _dec66, _init_name9, _init_description5, _init_code, _init_buyLedgerAccountId, _init_buyLedgerAccount, _init_saleLedgerAccountId, _init_saleLedgerAccount, _init_taxId, _init_tax, _init_measureUnitId, _init_measureUnit, _init_salePrice, _init_inventoryable, _init_tags, _init_tagsCount, _dec67, _initClass11, _BaseEntity10, _dec68, _dec69, _dec70, _init_name10, _init_items, _init_itemsCount, _dec71, _initClass12, _dec72, _dec73, _dec74, _init_id2, _init_itemId, _init_tagId, _dec75, _initClass13, _BaseEntity11, _dec76, _dec77, _dec78, _init_itemAdjustments, _init_date, _init_description6, _dec79, _initClass14, _BaseEntity12, _dec80, _dec81, _dec82, _dec83, _dec84, _dec85, _dec86, _dec87, _init_itemId1, _init_item, _init_number, _init_buyPrice, _init_storehouseId, _init_storehouse, _init_inventoryAdjustmentId, _init_inventoryAdjustment;
|
|
35
|
-
_dec = Id(), _dec1 = Field({
|
|
36
|
-
reference: ()=>_Company
|
|
37
|
-
}), _dec2 = ManyToOne({
|
|
38
|
-
entity: ()=>_Company
|
|
39
|
-
}), _dec3 = Field({
|
|
40
|
-
reference: ()=>_User
|
|
41
|
-
}), _dec4 = ManyToOne({
|
|
42
|
-
entity: ()=>_User
|
|
43
|
-
}), _dec5 = Field({
|
|
44
|
-
onInsert: Date.now
|
|
45
|
-
}), _dec6 = Field({
|
|
46
|
-
onUpdate: Date.now
|
|
47
|
-
});
|
|
48
10
|
/**
|
|
49
|
-
*
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
({ e: [_init_id, _init_companyId, _init_company, _init_creatorId, _init_creator, _init_createdAt, _init_updatedAt] } = _apply_decs_2203_r(this, [
|
|
54
|
-
[
|
|
55
|
-
_dec,
|
|
56
|
-
0,
|
|
57
|
-
"id"
|
|
58
|
-
],
|
|
59
|
-
[
|
|
60
|
-
_dec1,
|
|
61
|
-
0,
|
|
62
|
-
"companyId"
|
|
63
|
-
],
|
|
64
|
-
[
|
|
65
|
-
_dec2,
|
|
66
|
-
0,
|
|
67
|
-
"company"
|
|
68
|
-
],
|
|
69
|
-
[
|
|
70
|
-
_dec3,
|
|
71
|
-
0,
|
|
72
|
-
"creatorId"
|
|
73
|
-
],
|
|
74
|
-
[
|
|
75
|
-
_dec4,
|
|
76
|
-
0,
|
|
77
|
-
"creator"
|
|
78
|
-
],
|
|
79
|
-
[
|
|
80
|
-
_dec5,
|
|
81
|
-
0,
|
|
82
|
-
"createdAt"
|
|
83
|
-
],
|
|
84
|
-
[
|
|
85
|
-
_dec6,
|
|
86
|
-
0,
|
|
87
|
-
"updatedAt"
|
|
88
|
-
]
|
|
89
|
-
], []));
|
|
90
|
-
}
|
|
91
|
-
constructor(){
|
|
92
|
-
this.id = _init_id(this);
|
|
93
|
-
this.companyId = _init_companyId(this);
|
|
94
|
-
this.company = _init_company(this);
|
|
95
|
-
this.creatorId = _init_creatorId(this);
|
|
96
|
-
this.creator = _init_creator(this);
|
|
97
|
-
this.createdAt = _init_createdAt(this);
|
|
98
|
-
this.updatedAt = _init_updatedAt(this);
|
|
11
|
+
* Abstract base class for SQL schema generation
|
|
12
|
+
*/ class AbstractSchemaGenerator extends AbstractDialect {
|
|
13
|
+
constructor(namingStrategy, escapeIdChar = '`'){
|
|
14
|
+
super(namingStrategy), this.escapeIdChar = escapeIdChar;
|
|
99
15
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
});
|
|
105
|
-
class Company extends (_BaseEntity = BaseEntity) {
|
|
106
|
-
static{
|
|
107
|
-
({ e: [_init_name, _init_description, _init_kind], c: [_Company, _initClass] } = _apply_decs_2203_r(this, [
|
|
108
|
-
[
|
|
109
|
-
_dec8,
|
|
110
|
-
0,
|
|
111
|
-
"name"
|
|
112
|
-
],
|
|
113
|
-
[
|
|
114
|
-
_dec9,
|
|
115
|
-
0,
|
|
116
|
-
"description"
|
|
117
|
-
],
|
|
118
|
-
[
|
|
119
|
-
_dec10,
|
|
120
|
-
0,
|
|
121
|
-
"kind"
|
|
122
|
-
]
|
|
123
|
-
], [
|
|
124
|
-
_dec7
|
|
125
|
-
], _BaseEntity));
|
|
126
|
-
}
|
|
127
|
-
static{
|
|
128
|
-
_initClass();
|
|
16
|
+
/**
|
|
17
|
+
* Escape an identifier (table name, column name, etc.)
|
|
18
|
+
*/ escapeId(identifier) {
|
|
19
|
+
return escapeSqlId(identifier, this.escapeIdChar);
|
|
129
20
|
}
|
|
130
|
-
|
|
131
|
-
|
|
21
|
+
generateCreateTable(entity, options = {}) {
|
|
22
|
+
const meta = getMeta(entity);
|
|
23
|
+
const tableName = this.resolveTableName(entity, meta);
|
|
24
|
+
const columns = this.generateColumnDefinitions(meta);
|
|
25
|
+
const constraints = this.generateTableConstraints(meta);
|
|
26
|
+
const ifNotExists = options.ifNotExists ? 'IF NOT EXISTS ' : '';
|
|
27
|
+
let sql = `CREATE TABLE ${ifNotExists}${this.escapeId(tableName)} (\n`;
|
|
28
|
+
sql += columns.map((col)=>` ${col}`).join(',\n');
|
|
29
|
+
if (constraints.length > 0) {
|
|
30
|
+
sql += ',\n';
|
|
31
|
+
sql += constraints.map((c)=>` ${c}`).join(',\n');
|
|
32
|
+
}
|
|
33
|
+
sql += '\n)';
|
|
34
|
+
sql += this.getTableOptions(meta);
|
|
35
|
+
sql += ';';
|
|
36
|
+
return sql;
|
|
132
37
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}), _dec12 = Id(), _dec13 = Field({
|
|
138
|
-
name: 'image'
|
|
139
|
-
});
|
|
140
|
-
class Profile extends (_BaseEntity1 = BaseEntity) {
|
|
141
|
-
static{
|
|
142
|
-
({ e: [_init_pk, _init_picture], c: [_Profile, _initClass1] } = _apply_decs_2203_r(this, [
|
|
143
|
-
[
|
|
144
|
-
_dec12,
|
|
145
|
-
0,
|
|
146
|
-
"pk"
|
|
147
|
-
],
|
|
148
|
-
[
|
|
149
|
-
_dec13,
|
|
150
|
-
0,
|
|
151
|
-
"picture"
|
|
152
|
-
]
|
|
153
|
-
], [
|
|
154
|
-
_dec11
|
|
155
|
-
], _BaseEntity1));
|
|
156
|
-
}
|
|
157
|
-
static{
|
|
158
|
-
_initClass1();
|
|
38
|
+
generateDropTable(entity) {
|
|
39
|
+
const meta = getMeta(entity);
|
|
40
|
+
const tableName = this.resolveTableName(entity, meta);
|
|
41
|
+
return `DROP TABLE IF EXISTS ${this.escapeId(tableName)};`;
|
|
159
42
|
}
|
|
160
|
-
|
|
161
|
-
|
|
43
|
+
generateAlterTable(diff) {
|
|
44
|
+
const statements = [];
|
|
45
|
+
const tableName = this.escapeId(diff.tableName);
|
|
46
|
+
// Add new columns
|
|
47
|
+
if (diff.columnsToAdd?.length) {
|
|
48
|
+
for (const column of diff.columnsToAdd){
|
|
49
|
+
const colDef = this.generateColumnDefinitionFromSchema(column);
|
|
50
|
+
statements.push(`ALTER TABLE ${tableName} ADD COLUMN ${colDef};`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Alter existing columns
|
|
54
|
+
if (diff.columnsToAlter?.length) {
|
|
55
|
+
for (const { to } of diff.columnsToAlter){
|
|
56
|
+
const colDef = this.generateColumnDefinitionFromSchema(to);
|
|
57
|
+
const colStatements = this.generateAlterColumnStatements(diff.tableName, to, colDef);
|
|
58
|
+
statements.push(...colStatements);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Drop columns
|
|
62
|
+
if (diff.columnsToDrop?.length) {
|
|
63
|
+
for (const columnName of diff.columnsToDrop){
|
|
64
|
+
statements.push(`ALTER TABLE ${tableName} DROP COLUMN ${this.escapeId(columnName)};`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Add indexes
|
|
68
|
+
if (diff.indexesToAdd?.length) {
|
|
69
|
+
for (const index of diff.indexesToAdd){
|
|
70
|
+
statements.push(this.generateCreateIndex(diff.tableName, index));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Drop indexes
|
|
74
|
+
if (diff.indexesToDrop?.length) {
|
|
75
|
+
for (const indexName of diff.indexesToDrop){
|
|
76
|
+
statements.push(this.generateDropIndex(diff.tableName, indexName));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return statements;
|
|
162
80
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}), _dec19 = OneToMany({
|
|
174
|
-
entity: ()=>_User,
|
|
175
|
-
mappedBy: 'creator'
|
|
176
|
-
});
|
|
177
|
-
class User extends (_BaseEntity2 = BaseEntity) {
|
|
178
|
-
static{
|
|
179
|
-
({ e: [_init_name1, _init_email, _init_password, _init_profile, _init_users], c: [_User, _initClass2] } = _apply_decs_2203_r(this, [
|
|
180
|
-
[
|
|
181
|
-
_dec15,
|
|
182
|
-
0,
|
|
183
|
-
"name"
|
|
184
|
-
],
|
|
185
|
-
[
|
|
186
|
-
_dec16,
|
|
187
|
-
0,
|
|
188
|
-
"email"
|
|
189
|
-
],
|
|
190
|
-
[
|
|
191
|
-
_dec17,
|
|
192
|
-
0,
|
|
193
|
-
"password"
|
|
194
|
-
],
|
|
195
|
-
[
|
|
196
|
-
_dec18,
|
|
197
|
-
0,
|
|
198
|
-
"profile"
|
|
199
|
-
],
|
|
200
|
-
[
|
|
201
|
-
_dec19,
|
|
202
|
-
0,
|
|
203
|
-
"users"
|
|
204
|
-
]
|
|
205
|
-
], [
|
|
206
|
-
_dec14
|
|
207
|
-
], _BaseEntity2));
|
|
208
|
-
}
|
|
209
|
-
static{
|
|
210
|
-
_initClass2();
|
|
81
|
+
generateAlterTableDown(diff) {
|
|
82
|
+
const statements = [];
|
|
83
|
+
const tableName = this.escapeId(diff.tableName);
|
|
84
|
+
// Rollback additions by dropping columns
|
|
85
|
+
if (diff.columnsToAdd?.length) {
|
|
86
|
+
for (const column of diff.columnsToAdd){
|
|
87
|
+
statements.push(`ALTER TABLE ${tableName} DROP COLUMN ${this.escapeId(column.name)};`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return statements;
|
|
211
91
|
}
|
|
212
|
-
|
|
213
|
-
|
|
92
|
+
generateCreateIndex(tableName, index) {
|
|
93
|
+
const unique = index.unique ? 'UNIQUE ' : '';
|
|
94
|
+
const columns = index.columns.map((c)=>this.escapeId(c)).join(', ');
|
|
95
|
+
return `CREATE ${unique}INDEX ${this.escapeId(index.name)} ON ${this.escapeId(tableName)} (${columns});`;
|
|
214
96
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
_dec20 = Entity(), _dec21 = Id({
|
|
218
|
-
updatable: false
|
|
219
|
-
}), _dec22 = Field();
|
|
220
|
-
class UserWithNonUpdatableId {
|
|
221
|
-
static{
|
|
222
|
-
({ e: [_init_id1, _init_name2], c: [_UserWithNonUpdatableId, _initClass3] } = _apply_decs_2203_r(this, [
|
|
223
|
-
[
|
|
224
|
-
_dec21,
|
|
225
|
-
0,
|
|
226
|
-
"id"
|
|
227
|
-
],
|
|
228
|
-
[
|
|
229
|
-
_dec22,
|
|
230
|
-
0,
|
|
231
|
-
"name"
|
|
232
|
-
]
|
|
233
|
-
], [
|
|
234
|
-
_dec20
|
|
235
|
-
]));
|
|
236
|
-
}
|
|
237
|
-
static{
|
|
238
|
-
_initClass3();
|
|
239
|
-
}
|
|
240
|
-
constructor(){
|
|
241
|
-
this.id = _init_id1(this);
|
|
242
|
-
this.name = _init_name2(this);
|
|
97
|
+
generateDropIndex(tableName, indexName) {
|
|
98
|
+
return `DROP INDEX IF EXISTS ${this.escapeId(indexName)};`;
|
|
243
99
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
],
|
|
257
|
-
[
|
|
258
|
-
_dec25,
|
|
259
|
-
0,
|
|
260
|
-
"description"
|
|
261
|
-
],
|
|
262
|
-
[
|
|
263
|
-
_dec26,
|
|
264
|
-
0,
|
|
265
|
-
"parentLedgerId"
|
|
266
|
-
],
|
|
267
|
-
[
|
|
268
|
-
_dec27,
|
|
269
|
-
0,
|
|
270
|
-
"parentLedger"
|
|
271
|
-
]
|
|
272
|
-
], [
|
|
273
|
-
_dec23
|
|
274
|
-
], _BaseEntity3));
|
|
275
|
-
}
|
|
276
|
-
static{
|
|
277
|
-
_initClass4();
|
|
100
|
+
/**
|
|
101
|
+
* Generate column definitions from entity metadata
|
|
102
|
+
*/ generateColumnDefinitions(meta) {
|
|
103
|
+
const columns = [];
|
|
104
|
+
const fieldKeys = getKeys(meta.fields);
|
|
105
|
+
for (const key of fieldKeys){
|
|
106
|
+
const field = meta.fields[key];
|
|
107
|
+
if (field?.virtual) continue; // Skip virtual fields
|
|
108
|
+
const colDef = this.generateColumnDefinition(key, field, meta);
|
|
109
|
+
columns.push(colDef);
|
|
110
|
+
}
|
|
111
|
+
return columns;
|
|
278
112
|
}
|
|
279
|
-
|
|
280
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Generate a single column definition
|
|
115
|
+
*/ generateColumnDefinition(fieldKey, field, meta) {
|
|
116
|
+
const columnName = this.escapeId(this.resolveColumnName(fieldKey, field));
|
|
117
|
+
const isId = field.isId === true;
|
|
118
|
+
const isPrimaryKey = isId && meta.id === fieldKey;
|
|
119
|
+
// Determine SQL type
|
|
120
|
+
let sqlType;
|
|
121
|
+
if (isPrimaryKey && field.autoIncrement !== false && !field.onInsert) {
|
|
122
|
+
// Auto-increment primary key
|
|
123
|
+
sqlType = this.serialPrimaryKeyType;
|
|
124
|
+
} else {
|
|
125
|
+
sqlType = this.getSqlType(field, field.type);
|
|
126
|
+
}
|
|
127
|
+
let definition = `${columnName} ${sqlType}`;
|
|
128
|
+
// PRIMARY KEY constraint (for non-serial types)
|
|
129
|
+
if (isPrimaryKey && !sqlType.includes('PRIMARY KEY')) {
|
|
130
|
+
definition += ' PRIMARY KEY';
|
|
131
|
+
}
|
|
132
|
+
// NULL/NOT NULL
|
|
133
|
+
if (!isPrimaryKey) {
|
|
134
|
+
const nullable = field.nullable ?? true;
|
|
135
|
+
if (!nullable) {
|
|
136
|
+
definition += ' NOT NULL';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// UNIQUE constraint
|
|
140
|
+
if (field.unique && !isPrimaryKey) {
|
|
141
|
+
definition += ' UNIQUE';
|
|
142
|
+
}
|
|
143
|
+
// DEFAULT value
|
|
144
|
+
if (field.defaultValue !== undefined) {
|
|
145
|
+
definition += ` DEFAULT ${this.formatDefaultValue(field.defaultValue)}`;
|
|
146
|
+
}
|
|
147
|
+
// COMMENT (if supported)
|
|
148
|
+
if (field.comment) {
|
|
149
|
+
definition += this.generateColumnComment(this.resolveTableName(meta.entity, meta), this.resolveColumnName(fieldKey, field), field.comment);
|
|
150
|
+
}
|
|
151
|
+
return definition;
|
|
281
152
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
_initClass5();
|
|
153
|
+
/**
|
|
154
|
+
* Generate column definition from a ColumnSchema object
|
|
155
|
+
*/ generateColumnDefinitionFromSchema(column) {
|
|
156
|
+
const columnName = this.escapeId(column.name);
|
|
157
|
+
let type = column.type;
|
|
158
|
+
if (column.length && !type.includes('(')) {
|
|
159
|
+
type = `${type}(${column.length})`;
|
|
160
|
+
} else if (column.precision !== undefined && !type.includes('(')) {
|
|
161
|
+
if (column.scale !== undefined) {
|
|
162
|
+
type = `${type}(${column.precision}, ${column.scale})`;
|
|
163
|
+
} else {
|
|
164
|
+
type = `${type}(${column.precision})`;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
let definition = `${columnName} ${type}`;
|
|
168
|
+
if (column.isPrimaryKey) {
|
|
169
|
+
definition += ' PRIMARY KEY';
|
|
170
|
+
}
|
|
171
|
+
if (!column.nullable && !column.isPrimaryKey) {
|
|
172
|
+
definition += ' NOT NULL';
|
|
173
|
+
}
|
|
174
|
+
if (column.isUnique && !column.isPrimaryKey) {
|
|
175
|
+
definition += ' UNIQUE';
|
|
176
|
+
}
|
|
177
|
+
if (column.defaultValue !== undefined) {
|
|
178
|
+
definition += ` DEFAULT ${this.formatDefaultValue(column.defaultValue)}`;
|
|
179
|
+
}
|
|
180
|
+
return definition;
|
|
311
181
|
}
|
|
312
|
-
|
|
313
|
-
|
|
182
|
+
/**
|
|
183
|
+
* Generate table constraints (indexes, foreign keys, etc.)
|
|
184
|
+
*/ generateTableConstraints(meta) {
|
|
185
|
+
const constraints = [];
|
|
186
|
+
const fieldKeys = getKeys(meta.fields);
|
|
187
|
+
const tableName = this.resolveTableName(meta.entity, meta);
|
|
188
|
+
// Generate indexes from field options
|
|
189
|
+
for (const key of fieldKeys){
|
|
190
|
+
const field = meta.fields[key];
|
|
191
|
+
if (field?.index) {
|
|
192
|
+
const columnName = this.resolveColumnName(key, field);
|
|
193
|
+
const indexName = typeof field.index === 'string' ? field.index : `idx_${tableName}_${columnName}`;
|
|
194
|
+
constraints.push(`INDEX ${this.escapeId(indexName)} (${this.escapeId(columnName)})`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Generate foreign key constraints from references
|
|
198
|
+
for (const key of fieldKeys){
|
|
199
|
+
const field = meta.fields[key];
|
|
200
|
+
if (field?.reference) {
|
|
201
|
+
const refEntity = field.reference();
|
|
202
|
+
const refMeta = getMeta(refEntity);
|
|
203
|
+
const refIdField = refMeta.fields[refMeta.id];
|
|
204
|
+
const columnName = this.resolveColumnName(key, field);
|
|
205
|
+
const refTableName = this.resolveTableName(refEntity, refMeta);
|
|
206
|
+
const refColumnName = this.resolveColumnName(refMeta.id, refIdField);
|
|
207
|
+
const fkName = `fk_${tableName}_${columnName}`;
|
|
208
|
+
constraints.push(`CONSTRAINT ${this.escapeId(fkName)} FOREIGN KEY (${this.escapeId(columnName)}) ` + `REFERENCES ${this.escapeId(refTableName)} (${this.escapeId(refColumnName)})`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return constraints;
|
|
314
212
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
"description"
|
|
347
|
-
]
|
|
348
|
-
], [
|
|
349
|
-
_dec32
|
|
350
|
-
], _BaseEntity5));
|
|
351
|
-
}
|
|
352
|
-
static{
|
|
353
|
-
_initClass6();
|
|
213
|
+
getSqlType(field, fieldType) {
|
|
214
|
+
// Use explicit column type if specified
|
|
215
|
+
if (field.columnType) {
|
|
216
|
+
return this.mapColumnType(field.columnType, field);
|
|
217
|
+
}
|
|
218
|
+
// Handle special types
|
|
219
|
+
if (field.type === 'json' || field.type === 'jsonb') {
|
|
220
|
+
return this.mapColumnType(field.type, field);
|
|
221
|
+
}
|
|
222
|
+
if (field.type === 'vector') {
|
|
223
|
+
return this.mapColumnType('vector', field);
|
|
224
|
+
}
|
|
225
|
+
// Infer from TypeScript type
|
|
226
|
+
const type = fieldType ?? field.type;
|
|
227
|
+
if (type === Number || type === 'number') {
|
|
228
|
+
return field.precision ? this.mapColumnType('decimal', field) : 'BIGINT';
|
|
229
|
+
}
|
|
230
|
+
if (type === String || type === 'string') {
|
|
231
|
+
return this.mapColumnType('varchar', field);
|
|
232
|
+
}
|
|
233
|
+
if (type === Boolean || type === 'boolean') {
|
|
234
|
+
return this.getBooleanType();
|
|
235
|
+
}
|
|
236
|
+
if (type === Date || type === 'date') {
|
|
237
|
+
return 'TIMESTAMP';
|
|
238
|
+
}
|
|
239
|
+
if (type === BigInt || type === 'bigint') {
|
|
240
|
+
return 'BIGINT';
|
|
241
|
+
}
|
|
242
|
+
// Default to varchar
|
|
243
|
+
return this.mapColumnType('varchar', field);
|
|
354
244
|
}
|
|
355
|
-
|
|
356
|
-
|
|
245
|
+
/**
|
|
246
|
+
* Get table options (e.g., ENGINE for MySQL)
|
|
247
|
+
*/ getTableOptions(meta) {
|
|
248
|
+
return '';
|
|
357
249
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
(
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
_dec40,
|
|
378
|
-
0,
|
|
379
|
-
"measureUnits"
|
|
380
|
-
],
|
|
381
|
-
[
|
|
382
|
-
_dec41,
|
|
383
|
-
0,
|
|
384
|
-
"deletedAt"
|
|
385
|
-
]
|
|
386
|
-
], [
|
|
387
|
-
_dec38
|
|
388
|
-
], _BaseEntity6));
|
|
389
|
-
}
|
|
390
|
-
static{
|
|
391
|
-
_initClass7();
|
|
250
|
+
/**
|
|
251
|
+
* Format a default value for SQL
|
|
252
|
+
*/ formatDefaultValue(value) {
|
|
253
|
+
if (value === null) {
|
|
254
|
+
return 'NULL';
|
|
255
|
+
}
|
|
256
|
+
if (typeof value === 'string') {
|
|
257
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
258
|
+
}
|
|
259
|
+
if (typeof value === 'boolean') {
|
|
260
|
+
return value ? 'TRUE' : 'FALSE';
|
|
261
|
+
}
|
|
262
|
+
if (typeof value === 'number' || typeof value === 'bigint') {
|
|
263
|
+
return String(value);
|
|
264
|
+
}
|
|
265
|
+
if (value instanceof Date) {
|
|
266
|
+
return `'${value.toISOString()}'`;
|
|
267
|
+
}
|
|
268
|
+
return String(value);
|
|
392
269
|
}
|
|
393
|
-
|
|
394
|
-
|
|
270
|
+
/**
|
|
271
|
+
* Compare two schemas and return the differences
|
|
272
|
+
*/ diffSchema(entity, currentSchema) {
|
|
273
|
+
const meta = getMeta(entity);
|
|
274
|
+
if (!currentSchema) {
|
|
275
|
+
// Table doesn't exist, need to create
|
|
276
|
+
return {
|
|
277
|
+
tableName: this.resolveTableName(entity, meta),
|
|
278
|
+
type: 'create'
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const columnsToAdd = [];
|
|
282
|
+
const columnsToAlter = [];
|
|
283
|
+
const columnsToDrop = [];
|
|
284
|
+
const currentColumns = new Map(currentSchema.columns.map((c)=>[
|
|
285
|
+
c.name,
|
|
286
|
+
c
|
|
287
|
+
]));
|
|
288
|
+
const fieldKeys = getKeys(meta.fields);
|
|
289
|
+
// Check for new or altered columns
|
|
290
|
+
for (const key of fieldKeys){
|
|
291
|
+
const field = meta.fields[key];
|
|
292
|
+
if (field?.virtual) continue;
|
|
293
|
+
const columnName = this.resolveColumnName(key, field);
|
|
294
|
+
const currentColumn = currentColumns.get(columnName);
|
|
295
|
+
if (!currentColumn) {
|
|
296
|
+
// Column needs to be added
|
|
297
|
+
columnsToAdd.push(this.fieldToColumnSchema(key, field, meta));
|
|
298
|
+
} else {
|
|
299
|
+
// Check if column needs alteration
|
|
300
|
+
const desiredColumn = this.fieldToColumnSchema(key, field, meta);
|
|
301
|
+
if (this.columnsNeedAlteration(currentColumn, desiredColumn)) {
|
|
302
|
+
columnsToAlter.push({
|
|
303
|
+
from: currentColumn,
|
|
304
|
+
to: desiredColumn
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
currentColumns.delete(columnName);
|
|
309
|
+
}
|
|
310
|
+
// Remaining columns in currentColumns should be dropped
|
|
311
|
+
for (const [name] of currentColumns){
|
|
312
|
+
columnsToDrop.push(name);
|
|
313
|
+
}
|
|
314
|
+
if (columnsToAdd.length === 0 && columnsToAlter.length === 0 && columnsToDrop.length === 0) {
|
|
315
|
+
return undefined; // No changes needed
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
tableName: this.resolveTableName(entity, meta),
|
|
319
|
+
type: 'alter',
|
|
320
|
+
columnsToAdd: columnsToAdd.length > 0 ? columnsToAdd : undefined,
|
|
321
|
+
columnsToAlter: columnsToAlter.length > 0 ? columnsToAlter : undefined,
|
|
322
|
+
columnsToDrop: columnsToDrop.length > 0 ? columnsToDrop : undefined
|
|
323
|
+
};
|
|
395
324
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
],
|
|
415
|
-
[
|
|
416
|
-
_dec44,
|
|
417
|
-
0,
|
|
418
|
-
"categoryId"
|
|
419
|
-
],
|
|
420
|
-
[
|
|
421
|
-
_dec45,
|
|
422
|
-
0,
|
|
423
|
-
"category"
|
|
424
|
-
],
|
|
425
|
-
[
|
|
426
|
-
_dec46,
|
|
427
|
-
0,
|
|
428
|
-
"deletedAt"
|
|
429
|
-
]
|
|
430
|
-
], [
|
|
431
|
-
_dec42
|
|
432
|
-
], _BaseEntity7));
|
|
433
|
-
}
|
|
434
|
-
static{
|
|
435
|
-
_initClass8();
|
|
325
|
+
/**
|
|
326
|
+
* Convert field options to ColumnSchema
|
|
327
|
+
*/ fieldToColumnSchema(fieldKey, field, meta) {
|
|
328
|
+
const isId = field.isId === true;
|
|
329
|
+
const isPrimaryKey = isId && meta.id === fieldKey;
|
|
330
|
+
return {
|
|
331
|
+
name: this.resolveColumnName(fieldKey, field),
|
|
332
|
+
type: this.getSqlType(field, field.type),
|
|
333
|
+
nullable: field.nullable ?? !isPrimaryKey,
|
|
334
|
+
defaultValue: field.defaultValue,
|
|
335
|
+
isPrimaryKey,
|
|
336
|
+
isAutoIncrement: isPrimaryKey && field.autoIncrement !== false && !field.onInsert,
|
|
337
|
+
isUnique: field.unique ?? false,
|
|
338
|
+
length: field.length,
|
|
339
|
+
precision: field.precision,
|
|
340
|
+
scale: field.scale,
|
|
341
|
+
comment: field.comment
|
|
342
|
+
};
|
|
436
343
|
}
|
|
437
|
-
|
|
438
|
-
|
|
344
|
+
/**
|
|
345
|
+
* Check if two columns differ enough to require alteration
|
|
346
|
+
*/ columnsNeedAlteration(current, desired) {
|
|
347
|
+
// Compare relevant properties
|
|
348
|
+
return current.type.toLowerCase() !== desired.type.toLowerCase() || current.nullable !== desired.nullable || current.isUnique !== desired.isUnique || JSON.stringify(current.defaultValue) !== JSON.stringify(desired.defaultValue);
|
|
439
349
|
}
|
|
440
350
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* MySQL/MariaDB-specific schema generator
|
|
354
|
+
*/ class MysqlSchemaGenerator extends AbstractSchemaGenerator {
|
|
355
|
+
mapColumnType(columnType, field) {
|
|
356
|
+
switch(columnType){
|
|
357
|
+
case 'int':
|
|
358
|
+
return 'INT';
|
|
359
|
+
case 'smallint':
|
|
360
|
+
return 'SMALLINT';
|
|
361
|
+
case 'bigint':
|
|
362
|
+
return 'BIGINT';
|
|
363
|
+
case 'float':
|
|
364
|
+
return 'FLOAT';
|
|
365
|
+
case 'double':
|
|
366
|
+
case 'real':
|
|
367
|
+
return 'DOUBLE';
|
|
368
|
+
case 'decimal':
|
|
369
|
+
case 'numeric':
|
|
370
|
+
if (field.precision !== undefined) {
|
|
371
|
+
if (field.scale !== undefined) {
|
|
372
|
+
return `DECIMAL(${field.precision}, ${field.scale})`;
|
|
373
|
+
}
|
|
374
|
+
return `DECIMAL(${field.precision})`;
|
|
375
|
+
}
|
|
376
|
+
return 'DECIMAL(10, 2)';
|
|
377
|
+
case 'boolean':
|
|
378
|
+
return 'TINYINT(1)';
|
|
379
|
+
case 'char':
|
|
380
|
+
return `CHAR(${field.length ?? 1})`;
|
|
381
|
+
case 'varchar':
|
|
382
|
+
return `VARCHAR(${field.length ?? 255})`;
|
|
383
|
+
case 'text':
|
|
384
|
+
return 'TEXT';
|
|
385
|
+
case 'uuid':
|
|
386
|
+
return 'CHAR(36)';
|
|
387
|
+
case 'date':
|
|
388
|
+
return 'DATE';
|
|
389
|
+
case 'time':
|
|
390
|
+
return 'TIME';
|
|
391
|
+
case 'timestamp':
|
|
392
|
+
case 'timestamptz':
|
|
393
|
+
return 'TIMESTAMP';
|
|
394
|
+
case 'json':
|
|
395
|
+
case 'jsonb':
|
|
396
|
+
return 'JSON';
|
|
397
|
+
case 'blob':
|
|
398
|
+
case 'bytea':
|
|
399
|
+
return 'BLOB';
|
|
400
|
+
case 'vector':
|
|
401
|
+
// MySQL doesn't have native vector support, use JSON
|
|
402
|
+
return 'JSON';
|
|
403
|
+
case 'serial':
|
|
404
|
+
return 'INT AUTO_INCREMENT';
|
|
405
|
+
case 'bigserial':
|
|
406
|
+
return 'BIGINT AUTO_INCREMENT';
|
|
407
|
+
default:
|
|
408
|
+
return 'TEXT';
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
getBooleanType() {
|
|
412
|
+
return 'TINYINT(1)';
|
|
413
|
+
}
|
|
414
|
+
generateAlterColumnStatements(tableName, column, newDefinition) {
|
|
415
|
+
return [
|
|
416
|
+
`ALTER TABLE ${this.escapeId(tableName)} MODIFY COLUMN ${newDefinition};`
|
|
417
|
+
];
|
|
418
|
+
}
|
|
419
|
+
generateColumnComment(tableName, columnName, comment) {
|
|
420
|
+
const escapedComment = comment.replace(/'/g, "''");
|
|
421
|
+
return ` COMMENT '${escapedComment}'`;
|
|
422
|
+
}
|
|
423
|
+
getTableOptions() {
|
|
424
|
+
return ' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci';
|
|
425
|
+
}
|
|
426
|
+
generateDropIndex(tableName, indexName) {
|
|
427
|
+
// MySQL requires table name in DROP INDEX
|
|
428
|
+
return `DROP INDEX ${this.escapeId(indexName)} ON ${this.escapeId(tableName)};`;
|
|
467
429
|
}
|
|
468
430
|
constructor(...args){
|
|
469
|
-
super(...args), this.
|
|
431
|
+
super(...args), this.serialPrimaryKeyType = 'INT AUTO_INCREMENT PRIMARY KEY';
|
|
470
432
|
}
|
|
471
433
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* PostgreSQL-specific schema generator
|
|
437
|
+
*/ class PostgresSchemaGenerator extends AbstractSchemaGenerator {
|
|
438
|
+
constructor(namingStrategy){
|
|
439
|
+
super(namingStrategy, '"'), this.serialPrimaryKeyType = 'SERIAL PRIMARY KEY';
|
|
440
|
+
}
|
|
441
|
+
mapColumnType(columnType, field) {
|
|
442
|
+
switch(columnType){
|
|
443
|
+
case 'int':
|
|
444
|
+
return 'INTEGER';
|
|
445
|
+
case 'smallint':
|
|
446
|
+
return 'SMALLINT';
|
|
447
|
+
case 'bigint':
|
|
448
|
+
return 'BIGINT';
|
|
449
|
+
case 'float':
|
|
450
|
+
case 'double':
|
|
451
|
+
case 'real':
|
|
452
|
+
return 'DOUBLE PRECISION';
|
|
453
|
+
case 'decimal':
|
|
454
|
+
case 'numeric':
|
|
455
|
+
if (field.precision !== undefined) {
|
|
456
|
+
if (field.scale !== undefined) {
|
|
457
|
+
return `NUMERIC(${field.precision}, ${field.scale})`;
|
|
458
|
+
}
|
|
459
|
+
return `NUMERIC(${field.precision})`;
|
|
460
|
+
}
|
|
461
|
+
return 'NUMERIC';
|
|
462
|
+
case 'boolean':
|
|
463
|
+
return 'BOOLEAN';
|
|
464
|
+
case 'char':
|
|
465
|
+
return `CHAR(${field.length ?? 1})`;
|
|
466
|
+
case 'varchar':
|
|
467
|
+
return field.length !== undefined ? `VARCHAR(${field.length})` : 'TEXT';
|
|
468
|
+
case 'text':
|
|
469
|
+
return 'TEXT';
|
|
470
|
+
case 'uuid':
|
|
471
|
+
return 'UUID';
|
|
472
|
+
case 'date':
|
|
473
|
+
return 'DATE';
|
|
474
|
+
case 'time':
|
|
475
|
+
return 'TIME';
|
|
476
|
+
case 'timestamp':
|
|
477
|
+
return 'TIMESTAMP';
|
|
478
|
+
case 'timestamptz':
|
|
479
|
+
return 'TIMESTAMPTZ';
|
|
480
|
+
case 'json':
|
|
481
|
+
return 'JSON';
|
|
482
|
+
case 'jsonb':
|
|
483
|
+
return 'JSONB';
|
|
484
|
+
case 'blob':
|
|
485
|
+
case 'bytea':
|
|
486
|
+
return 'BYTEA';
|
|
487
|
+
case 'vector':
|
|
488
|
+
if (field.length) {
|
|
489
|
+
return `VECTOR(${field.length})`;
|
|
490
|
+
}
|
|
491
|
+
return 'VECTOR';
|
|
492
|
+
case 'serial':
|
|
493
|
+
return 'SERIAL';
|
|
494
|
+
case 'bigserial':
|
|
495
|
+
return 'BIGSERIAL';
|
|
496
|
+
default:
|
|
497
|
+
return 'TEXT';
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
getBooleanType() {
|
|
501
|
+
return 'BOOLEAN';
|
|
502
|
+
}
|
|
503
|
+
generateAlterColumnStatements(tableName, column, newDefinition) {
|
|
504
|
+
const statements = [];
|
|
505
|
+
const escapedTableName = this.escapeId(tableName);
|
|
506
|
+
const escapedColumnName = this.escapeId(column.name);
|
|
507
|
+
// PostgreSQL uses separate ALTER COLUMN clauses for different changes
|
|
508
|
+
// 1. Change type
|
|
509
|
+
statements.push(`ALTER TABLE ${escapedTableName} ALTER COLUMN ${escapedColumnName} TYPE ${column.type};`);
|
|
510
|
+
// 2. Change nullability
|
|
511
|
+
if (column.nullable) {
|
|
512
|
+
statements.push(`ALTER TABLE ${escapedTableName} ALTER COLUMN ${escapedColumnName} DROP NOT NULL;`);
|
|
513
|
+
} else {
|
|
514
|
+
statements.push(`ALTER TABLE ${escapedTableName} ALTER COLUMN ${escapedColumnName} SET NOT NULL;`);
|
|
515
|
+
}
|
|
516
|
+
// 3. Change default value
|
|
517
|
+
if (column.defaultValue !== undefined) {
|
|
518
|
+
statements.push(`ALTER TABLE ${escapedTableName} ALTER COLUMN ${escapedColumnName} SET DEFAULT ${this.formatDefaultValue(column.defaultValue)};`);
|
|
519
|
+
} else {
|
|
520
|
+
statements.push(`ALTER TABLE ${escapedTableName} ALTER COLUMN ${escapedColumnName} DROP DEFAULT;`);
|
|
521
|
+
}
|
|
522
|
+
return statements;
|
|
523
|
+
}
|
|
524
|
+
generateColumnComment(tableName, columnName, comment) {
|
|
525
|
+
// PostgreSQL handles comments separately via COMMENT ON COLUMN
|
|
526
|
+
return '';
|
|
527
|
+
}
|
|
486
528
|
/**
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
*/ virtual: raw(({ ctx, escapedPrefix, dialect })=>{
|
|
492
|
-
ctx.append('(');
|
|
493
|
-
dialect.count(ctx, _ItemTag, {
|
|
494
|
-
$where: {
|
|
495
|
-
itemId: raw(({ ctx })=>{
|
|
496
|
-
ctx.append(escapedPrefix + dialect.escapeId('id'));
|
|
497
|
-
})
|
|
498
|
-
}
|
|
499
|
-
}, {
|
|
500
|
-
autoPrefix: true
|
|
501
|
-
});
|
|
502
|
-
ctx.append(')');
|
|
503
|
-
})
|
|
504
|
-
});
|
|
505
|
-
class Item extends (_BaseEntity9 = BaseEntity) {
|
|
506
|
-
static{
|
|
507
|
-
({ e: [_init_name9, _init_description5, _init_code, _init_buyLedgerAccountId, _init_buyLedgerAccount, _init_saleLedgerAccountId, _init_saleLedgerAccount, _init_taxId, _init_tax, _init_measureUnitId, _init_measureUnit, _init_salePrice, _init_inventoryable, _init_tags, _init_tagsCount], c: [_Item, _initClass10] } = _apply_decs_2203_r(this, [
|
|
508
|
-
[
|
|
509
|
-
_dec52,
|
|
510
|
-
0,
|
|
511
|
-
"name"
|
|
512
|
-
],
|
|
513
|
-
[
|
|
514
|
-
_dec53,
|
|
515
|
-
0,
|
|
516
|
-
"description"
|
|
517
|
-
],
|
|
518
|
-
[
|
|
519
|
-
_dec54,
|
|
520
|
-
0,
|
|
521
|
-
"code"
|
|
522
|
-
],
|
|
523
|
-
[
|
|
524
|
-
_dec55,
|
|
525
|
-
0,
|
|
526
|
-
"buyLedgerAccountId"
|
|
527
|
-
],
|
|
528
|
-
[
|
|
529
|
-
_dec56,
|
|
530
|
-
0,
|
|
531
|
-
"buyLedgerAccount"
|
|
532
|
-
],
|
|
533
|
-
[
|
|
534
|
-
_dec57,
|
|
535
|
-
0,
|
|
536
|
-
"saleLedgerAccountId"
|
|
537
|
-
],
|
|
538
|
-
[
|
|
539
|
-
_dec58,
|
|
540
|
-
0,
|
|
541
|
-
"saleLedgerAccount"
|
|
542
|
-
],
|
|
543
|
-
[
|
|
544
|
-
_dec59,
|
|
545
|
-
0,
|
|
546
|
-
"taxId"
|
|
547
|
-
],
|
|
548
|
-
[
|
|
549
|
-
_dec60,
|
|
550
|
-
0,
|
|
551
|
-
"tax"
|
|
552
|
-
],
|
|
553
|
-
[
|
|
554
|
-
_dec61,
|
|
555
|
-
0,
|
|
556
|
-
"measureUnitId"
|
|
557
|
-
],
|
|
558
|
-
[
|
|
559
|
-
_dec62,
|
|
560
|
-
0,
|
|
561
|
-
"measureUnit"
|
|
562
|
-
],
|
|
563
|
-
[
|
|
564
|
-
_dec63,
|
|
565
|
-
0,
|
|
566
|
-
"salePrice"
|
|
567
|
-
],
|
|
568
|
-
[
|
|
569
|
-
_dec64,
|
|
570
|
-
0,
|
|
571
|
-
"inventoryable"
|
|
572
|
-
],
|
|
573
|
-
[
|
|
574
|
-
_dec65,
|
|
575
|
-
0,
|
|
576
|
-
"tags"
|
|
577
|
-
],
|
|
578
|
-
[
|
|
579
|
-
_dec66,
|
|
580
|
-
0,
|
|
581
|
-
"tagsCount"
|
|
582
|
-
]
|
|
583
|
-
], [
|
|
584
|
-
_dec51
|
|
585
|
-
], _BaseEntity9));
|
|
586
|
-
}
|
|
587
|
-
static{
|
|
588
|
-
_initClass10();
|
|
529
|
+
* Generate COMMENT ON COLUMN statement for PostgreSQL
|
|
530
|
+
*/ generateColumnCommentStatement(tableName, columnName, comment) {
|
|
531
|
+
const escapedComment = comment.replace(/'/g, "''");
|
|
532
|
+
return `COMMENT ON COLUMN ${this.escapeId(tableName)}.${this.escapeId(columnName)} IS '${escapedComment}';`;
|
|
589
533
|
}
|
|
590
|
-
|
|
591
|
-
|
|
534
|
+
generateDropIndex(tableName, indexName) {
|
|
535
|
+
// PostgreSQL doesn't require table name in DROP INDEX
|
|
536
|
+
return `DROP INDEX IF EXISTS ${this.escapeId(indexName)};`;
|
|
592
537
|
}
|
|
593
538
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
_initClass11();
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* SQLite-specific schema generator
|
|
542
|
+
*/ class SqliteSchemaGenerator extends AbstractSchemaGenerator {
|
|
543
|
+
mapColumnType(columnType, field) {
|
|
544
|
+
// SQLite has dynamic typing, but we use type affinity
|
|
545
|
+
switch(columnType){
|
|
546
|
+
case 'int':
|
|
547
|
+
case 'smallint':
|
|
548
|
+
case 'bigint':
|
|
549
|
+
case 'serial':
|
|
550
|
+
case 'bigserial':
|
|
551
|
+
return 'INTEGER';
|
|
552
|
+
case 'float':
|
|
553
|
+
case 'double':
|
|
554
|
+
case 'real':
|
|
555
|
+
case 'decimal':
|
|
556
|
+
case 'numeric':
|
|
557
|
+
return 'REAL';
|
|
558
|
+
case 'boolean':
|
|
559
|
+
return 'INTEGER'; // SQLite uses 0/1 for booleans
|
|
560
|
+
case 'char':
|
|
561
|
+
case 'varchar':
|
|
562
|
+
case 'text':
|
|
563
|
+
case 'uuid':
|
|
564
|
+
return 'TEXT';
|
|
565
|
+
case 'date':
|
|
566
|
+
case 'time':
|
|
567
|
+
case 'timestamp':
|
|
568
|
+
case 'timestamptz':
|
|
569
|
+
return 'TEXT'; // SQLite stores dates as TEXT or INTEGER
|
|
570
|
+
case 'json':
|
|
571
|
+
case 'jsonb':
|
|
572
|
+
return 'TEXT'; // SQLite stores JSON as TEXT
|
|
573
|
+
case 'blob':
|
|
574
|
+
case 'bytea':
|
|
575
|
+
return 'BLOB';
|
|
576
|
+
case 'vector':
|
|
577
|
+
return 'TEXT'; // Store as JSON array
|
|
578
|
+
default:
|
|
579
|
+
return 'TEXT';
|
|
580
|
+
}
|
|
637
581
|
}
|
|
638
|
-
|
|
639
|
-
|
|
582
|
+
getBooleanType() {
|
|
583
|
+
return 'INTEGER';
|
|
640
584
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
}), _dec74 = Field({
|
|
646
|
-
reference: ()=>_Tag
|
|
647
|
-
});
|
|
648
|
-
class ItemTag {
|
|
649
|
-
static{
|
|
650
|
-
({ e: [_init_id2, _init_itemId, _init_tagId], c: [_ItemTag, _initClass12] } = _apply_decs_2203_r(this, [
|
|
651
|
-
[
|
|
652
|
-
_dec72,
|
|
653
|
-
0,
|
|
654
|
-
"id"
|
|
655
|
-
],
|
|
656
|
-
[
|
|
657
|
-
_dec73,
|
|
658
|
-
0,
|
|
659
|
-
"itemId"
|
|
660
|
-
],
|
|
661
|
-
[
|
|
662
|
-
_dec74,
|
|
663
|
-
0,
|
|
664
|
-
"tagId"
|
|
665
|
-
]
|
|
666
|
-
], [
|
|
667
|
-
_dec71
|
|
668
|
-
]));
|
|
669
|
-
}
|
|
670
|
-
static{
|
|
671
|
-
_initClass12();
|
|
672
|
-
}
|
|
673
|
-
constructor(){
|
|
674
|
-
this.id = _init_id2(this);
|
|
675
|
-
this.itemId = _init_itemId(this);
|
|
676
|
-
this.tagId = _init_tagId(this);
|
|
585
|
+
generateAlterColumnStatements(tableName, column, newDefinition) {
|
|
586
|
+
// SQLite has very limited ALTER TABLE support
|
|
587
|
+
// Column type changes require recreating the table
|
|
588
|
+
throw new Error(`SQLite does not support altering column '${column.name}' in table '${tableName}'. ` + 'You need to recreate the table to change column types.');
|
|
677
589
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
_dec75 = Entity(), _dec76 = OneToMany({
|
|
681
|
-
entity: ()=>_ItemAdjustment,
|
|
682
|
-
mappedBy: (rel)=>rel.inventoryAdjustment,
|
|
683
|
-
cascade: true
|
|
684
|
-
}), _dec77 = Field(), _dec78 = Field();
|
|
685
|
-
class InventoryAdjustment extends (_BaseEntity11 = BaseEntity) {
|
|
686
|
-
static{
|
|
687
|
-
({ e: [_init_itemAdjustments, _init_date, _init_description6], c: [_InventoryAdjustment, _initClass13] } = _apply_decs_2203_r(this, [
|
|
688
|
-
[
|
|
689
|
-
_dec76,
|
|
690
|
-
0,
|
|
691
|
-
"itemAdjustments"
|
|
692
|
-
],
|
|
693
|
-
[
|
|
694
|
-
_dec77,
|
|
695
|
-
0,
|
|
696
|
-
"date"
|
|
697
|
-
],
|
|
698
|
-
[
|
|
699
|
-
_dec78,
|
|
700
|
-
0,
|
|
701
|
-
"description"
|
|
702
|
-
]
|
|
703
|
-
], [
|
|
704
|
-
_dec75
|
|
705
|
-
], _BaseEntity11));
|
|
706
|
-
}
|
|
707
|
-
static{
|
|
708
|
-
_initClass13();
|
|
590
|
+
generateColumnComment(tableName, columnName, comment) {
|
|
591
|
+
return '';
|
|
709
592
|
}
|
|
710
|
-
|
|
711
|
-
|
|
593
|
+
generateDropIndex(tableName, indexName) {
|
|
594
|
+
return `DROP INDEX IF EXISTS ${this.escapeId(indexName)};`;
|
|
712
595
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
reference: ()=>_Storehouse
|
|
719
|
-
}), _dec85 = ManyToOne(), _dec86 = Field({
|
|
720
|
-
reference: ()=>_InventoryAdjustment
|
|
721
|
-
}), _dec87 = ManyToOne();
|
|
722
|
-
class ItemAdjustment extends (_BaseEntity12 = BaseEntity) {
|
|
723
|
-
static{
|
|
724
|
-
({ e: [_init_itemId1, _init_item, _init_number, _init_buyPrice, _init_storehouseId, _init_storehouse, _init_inventoryAdjustmentId, _init_inventoryAdjustment], c: [_ItemAdjustment, _initClass14] } = _apply_decs_2203_r(this, [
|
|
725
|
-
[
|
|
726
|
-
_dec80,
|
|
727
|
-
0,
|
|
728
|
-
"itemId"
|
|
729
|
-
],
|
|
730
|
-
[
|
|
731
|
-
_dec81,
|
|
732
|
-
0,
|
|
733
|
-
"item"
|
|
734
|
-
],
|
|
735
|
-
[
|
|
736
|
-
_dec82,
|
|
737
|
-
0,
|
|
738
|
-
"number"
|
|
739
|
-
],
|
|
740
|
-
[
|
|
741
|
-
_dec83,
|
|
742
|
-
0,
|
|
743
|
-
"buyPrice"
|
|
744
|
-
],
|
|
745
|
-
[
|
|
746
|
-
_dec84,
|
|
747
|
-
0,
|
|
748
|
-
"storehouseId"
|
|
749
|
-
],
|
|
750
|
-
[
|
|
751
|
-
_dec85,
|
|
752
|
-
0,
|
|
753
|
-
"storehouse"
|
|
754
|
-
],
|
|
755
|
-
[
|
|
756
|
-
_dec86,
|
|
757
|
-
0,
|
|
758
|
-
"inventoryAdjustmentId"
|
|
759
|
-
],
|
|
760
|
-
[
|
|
761
|
-
_dec87,
|
|
762
|
-
0,
|
|
763
|
-
"inventoryAdjustment"
|
|
764
|
-
]
|
|
765
|
-
], [
|
|
766
|
-
_dec79
|
|
767
|
-
], _BaseEntity12));
|
|
768
|
-
}
|
|
769
|
-
static{
|
|
770
|
-
_initClass14();
|
|
596
|
+
formatDefaultValue(value) {
|
|
597
|
+
if (typeof value === 'boolean') {
|
|
598
|
+
return value ? '1' : '0';
|
|
599
|
+
}
|
|
600
|
+
return super.formatDefaultValue(value);
|
|
771
601
|
}
|
|
772
602
|
constructor(...args){
|
|
773
|
-
super(...args), this.
|
|
603
|
+
super(...args), this.serialPrimaryKeyType = 'INTEGER PRIMARY KEY AUTOINCREMENT';
|
|
774
604
|
}
|
|
775
605
|
}
|
|
776
606
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
throw TypeError(`'${entity.name}' is not an entity`);
|
|
607
|
+
/**
|
|
608
|
+
* MySQL/MariaDB schema introspector.
|
|
609
|
+
* Works with both MySQL and MariaDB as they share the same information_schema structure.
|
|
610
|
+
*/ class MysqlSchemaIntrospector {
|
|
611
|
+
constructor(pool){
|
|
612
|
+
this.pool = pool;
|
|
613
|
+
}
|
|
614
|
+
async getTableSchema(tableName) {
|
|
615
|
+
const querier = await this.getQuerier();
|
|
616
|
+
try {
|
|
617
|
+
const exists = await this.tableExistsInternal(querier, tableName);
|
|
618
|
+
if (!exists) {
|
|
619
|
+
return undefined;
|
|
620
|
+
}
|
|
621
|
+
const [columns, indexes, foreignKeys, primaryKey] = await Promise.all([
|
|
622
|
+
this.getColumns(querier, tableName),
|
|
623
|
+
this.getIndexes(querier, tableName),
|
|
624
|
+
this.getForeignKeys(querier, tableName),
|
|
625
|
+
this.getPrimaryKey(querier, tableName)
|
|
626
|
+
]);
|
|
627
|
+
return {
|
|
628
|
+
name: tableName,
|
|
629
|
+
columns,
|
|
630
|
+
primaryKey,
|
|
631
|
+
indexes,
|
|
632
|
+
foreignKeys
|
|
633
|
+
};
|
|
634
|
+
} finally{
|
|
635
|
+
await querier.release();
|
|
636
|
+
}
|
|
808
637
|
}
|
|
809
|
-
|
|
810
|
-
|
|
638
|
+
async getTableNames() {
|
|
639
|
+
const querier = await this.getQuerier();
|
|
640
|
+
try {
|
|
641
|
+
const sql = `
|
|
642
|
+
SELECT TABLE_NAME as table_name
|
|
643
|
+
FROM information_schema.TABLES
|
|
644
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
645
|
+
AND TABLE_TYPE = 'BASE TABLE'
|
|
646
|
+
ORDER BY TABLE_NAME
|
|
647
|
+
`;
|
|
648
|
+
const results = await querier.all(sql);
|
|
649
|
+
return results.map((r)=>r.table_name);
|
|
650
|
+
} finally{
|
|
651
|
+
await querier.release();
|
|
652
|
+
}
|
|
811
653
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
if (relOpts.references) {
|
|
819
|
-
continue;
|
|
820
|
-
}
|
|
821
|
-
if (relOpts.mappedBy) {
|
|
822
|
-
fillInverseSideRelations(relOpts);
|
|
823
|
-
continue;
|
|
824
|
-
}
|
|
825
|
-
const relEntity = relOpts.entity();
|
|
826
|
-
const relMeta = ensureMeta(relEntity);
|
|
827
|
-
if (relOpts.cardinality === 'mm') {
|
|
828
|
-
const idName = meta.fields[meta.id].name;
|
|
829
|
-
const relIdName = relMeta.fields[relMeta.id].name;
|
|
830
|
-
const source = lowerFirst(meta.name) + upperFirst(idName);
|
|
831
|
-
const target = lowerFirst(relMeta.name) + upperFirst(relIdName);
|
|
832
|
-
relOpts.references = [
|
|
833
|
-
{
|
|
834
|
-
local: source,
|
|
835
|
-
foreign: meta.id
|
|
836
|
-
},
|
|
837
|
-
{
|
|
838
|
-
local: target,
|
|
839
|
-
foreign: relMeta.id
|
|
840
|
-
}
|
|
841
|
-
];
|
|
842
|
-
} else {
|
|
843
|
-
relOpts.references = [
|
|
844
|
-
{
|
|
845
|
-
local: `${relKey}Id`,
|
|
846
|
-
foreign: relMeta.id
|
|
847
|
-
}
|
|
848
|
-
];
|
|
654
|
+
async tableExists(tableName) {
|
|
655
|
+
const querier = await this.getQuerier();
|
|
656
|
+
try {
|
|
657
|
+
return this.tableExistsInternal(querier, tableName);
|
|
658
|
+
} finally{
|
|
659
|
+
await querier.release();
|
|
849
660
|
}
|
|
850
|
-
|
|
851
|
-
|
|
661
|
+
}
|
|
662
|
+
async tableExistsInternal(querier, tableName) {
|
|
663
|
+
const sql = `
|
|
664
|
+
SELECT COUNT(*) as count
|
|
665
|
+
FROM information_schema.TABLES
|
|
666
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
667
|
+
AND TABLE_NAME = ?
|
|
668
|
+
`;
|
|
669
|
+
const results = await querier.all(sql, [
|
|
670
|
+
tableName
|
|
671
|
+
]);
|
|
672
|
+
return (results[0]?.count ?? 0) > 0;
|
|
673
|
+
}
|
|
674
|
+
async getQuerier() {
|
|
675
|
+
const querier = await this.pool.getQuerier();
|
|
676
|
+
if (!isSqlQuerier(querier)) {
|
|
677
|
+
await querier.release();
|
|
678
|
+
throw new Error('MysqlSchemaIntrospector requires a SQL-based querier');
|
|
852
679
|
}
|
|
680
|
+
return querier;
|
|
853
681
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
682
|
+
async getColumns(querier, tableName) {
|
|
683
|
+
const sql = `
|
|
684
|
+
SELECT
|
|
685
|
+
COLUMN_NAME as column_name,
|
|
686
|
+
DATA_TYPE as data_type,
|
|
687
|
+
COLUMN_TYPE as column_type,
|
|
688
|
+
IS_NULLABLE as is_nullable,
|
|
689
|
+
COLUMN_DEFAULT as column_default,
|
|
690
|
+
CHARACTER_MAXIMUM_LENGTH as character_maximum_length,
|
|
691
|
+
NUMERIC_PRECISION as numeric_precision,
|
|
692
|
+
NUMERIC_SCALE as numeric_scale,
|
|
693
|
+
COLUMN_KEY as column_key,
|
|
694
|
+
EXTRA as extra,
|
|
695
|
+
COLUMN_COMMENT as column_comment
|
|
696
|
+
FROM information_schema.COLUMNS
|
|
697
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
698
|
+
AND TABLE_NAME = ?
|
|
699
|
+
ORDER BY ORDINAL_POSITION
|
|
700
|
+
`;
|
|
701
|
+
const results = await querier.all(sql, [
|
|
702
|
+
tableName
|
|
703
|
+
]);
|
|
704
|
+
return results.map((row)=>({
|
|
705
|
+
name: row.column_name,
|
|
706
|
+
type: row.data_type.toUpperCase(),
|
|
707
|
+
nullable: row.is_nullable === 'YES',
|
|
708
|
+
defaultValue: this.parseDefaultValue(row.column_default),
|
|
709
|
+
isPrimaryKey: row.column_key === 'PRI',
|
|
710
|
+
isAutoIncrement: row.extra.toLowerCase().includes('auto_increment'),
|
|
711
|
+
isUnique: row.column_key === 'UNI',
|
|
712
|
+
length: row.character_maximum_length ?? undefined,
|
|
713
|
+
precision: row.numeric_precision ?? undefined,
|
|
714
|
+
scale: row.numeric_scale ?? undefined,
|
|
715
|
+
comment: row.column_comment || undefined
|
|
716
|
+
}));
|
|
717
|
+
}
|
|
718
|
+
async getIndexes(querier, tableName) {
|
|
719
|
+
const sql = `
|
|
720
|
+
SELECT
|
|
721
|
+
INDEX_NAME as index_name,
|
|
722
|
+
GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) as columns,
|
|
723
|
+
NOT NON_UNIQUE as is_unique
|
|
724
|
+
FROM information_schema.STATISTICS
|
|
725
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
726
|
+
AND TABLE_NAME = ?
|
|
727
|
+
AND INDEX_NAME != 'PRIMARY'
|
|
728
|
+
GROUP BY INDEX_NAME, NON_UNIQUE
|
|
729
|
+
ORDER BY INDEX_NAME
|
|
730
|
+
`;
|
|
731
|
+
const results = await querier.all(sql, [
|
|
732
|
+
tableName
|
|
733
|
+
]);
|
|
734
|
+
return results.map((row)=>({
|
|
735
|
+
name: row.index_name,
|
|
736
|
+
columns: row.columns.split(','),
|
|
737
|
+
unique: Boolean(row.is_unique)
|
|
738
|
+
}));
|
|
739
|
+
}
|
|
740
|
+
async getForeignKeys(querier, tableName) {
|
|
741
|
+
const sql = `
|
|
742
|
+
SELECT
|
|
743
|
+
kcu.CONSTRAINT_NAME as constraint_name,
|
|
744
|
+
GROUP_CONCAT(kcu.COLUMN_NAME ORDER BY kcu.ORDINAL_POSITION) as columns,
|
|
745
|
+
kcu.REFERENCED_TABLE_NAME as referenced_table,
|
|
746
|
+
GROUP_CONCAT(kcu.REFERENCED_COLUMN_NAME ORDER BY kcu.ORDINAL_POSITION) as referenced_columns,
|
|
747
|
+
rc.DELETE_RULE as delete_rule,
|
|
748
|
+
rc.UPDATE_RULE as update_rule
|
|
749
|
+
FROM information_schema.KEY_COLUMN_USAGE kcu
|
|
750
|
+
JOIN information_schema.REFERENTIAL_CONSTRAINTS rc
|
|
751
|
+
ON kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
|
|
752
|
+
AND kcu.TABLE_SCHEMA = rc.CONSTRAINT_SCHEMA
|
|
753
|
+
WHERE kcu.TABLE_SCHEMA = DATABASE()
|
|
754
|
+
AND kcu.TABLE_NAME = ?
|
|
755
|
+
AND kcu.REFERENCED_TABLE_NAME IS NOT NULL
|
|
756
|
+
GROUP BY kcu.CONSTRAINT_NAME, kcu.REFERENCED_TABLE_NAME, rc.DELETE_RULE, rc.UPDATE_RULE
|
|
757
|
+
ORDER BY kcu.CONSTRAINT_NAME
|
|
758
|
+
`;
|
|
759
|
+
const results = await querier.all(sql, [
|
|
760
|
+
tableName
|
|
761
|
+
]);
|
|
762
|
+
return results.map((row)=>({
|
|
763
|
+
name: row.constraint_name,
|
|
764
|
+
columns: row.columns.split(','),
|
|
765
|
+
referencedTable: row.referenced_table,
|
|
766
|
+
referencedColumns: row.referenced_columns.split(','),
|
|
767
|
+
onDelete: this.normalizeReferentialAction(row.delete_rule),
|
|
768
|
+
onUpdate: this.normalizeReferentialAction(row.update_rule)
|
|
769
|
+
}));
|
|
770
|
+
}
|
|
771
|
+
async getPrimaryKey(querier, tableName) {
|
|
772
|
+
const sql = `
|
|
773
|
+
SELECT COLUMN_NAME as column_name
|
|
774
|
+
FROM information_schema.KEY_COLUMN_USAGE
|
|
775
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
776
|
+
AND TABLE_NAME = ?
|
|
777
|
+
AND CONSTRAINT_NAME = 'PRIMARY'
|
|
778
|
+
ORDER BY ORDINAL_POSITION
|
|
779
|
+
`;
|
|
780
|
+
const results = await querier.all(sql, [
|
|
781
|
+
tableName
|
|
782
|
+
]);
|
|
783
|
+
if (results.length === 0) {
|
|
784
|
+
return undefined;
|
|
899
785
|
}
|
|
900
|
-
return
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
await Promise.all(entities.map((entity)=>{
|
|
937
|
-
const ctx = querier.dialect.createContext();
|
|
938
|
-
querier.dialect.delete(ctx, entity, {});
|
|
939
|
-
return querier.run(ctx.sql, ctx.values);
|
|
940
|
-
}));
|
|
941
|
-
}
|
|
942
|
-
function getDdlForTable(entity, querier, primaryKeyType) {
|
|
943
|
-
const meta = getMeta(entity);
|
|
944
|
-
let sql = `CREATE TABLE ${querier.dialect.escapeId(meta.name)} (\n\t`;
|
|
945
|
-
const insertableIdType = 'VARCHAR(36)';
|
|
946
|
-
const defaultType = 'VARCHAR(255)';
|
|
947
|
-
const columns = getKeys(meta.fields).map((key)=>{
|
|
948
|
-
const field = meta.fields[key];
|
|
949
|
-
let propSql = querier.dialect.escapeId(field.name) + ' ';
|
|
950
|
-
if (field.isId) {
|
|
951
|
-
propSql += field.onInsert ? `${insertableIdType} PRIMARY KEY` : primaryKeyType;
|
|
952
|
-
} else {
|
|
953
|
-
if (field.type === Number) {
|
|
954
|
-
propSql += 'BIGINT';
|
|
955
|
-
} else if (field.type === Date) {
|
|
956
|
-
propSql += 'TIMESTAMP';
|
|
957
|
-
} else {
|
|
958
|
-
propSql += defaultType;
|
|
959
|
-
}
|
|
786
|
+
return results.map((r)=>r.column_name);
|
|
787
|
+
}
|
|
788
|
+
parseDefaultValue(defaultValue) {
|
|
789
|
+
if (defaultValue === null) {
|
|
790
|
+
return undefined;
|
|
791
|
+
}
|
|
792
|
+
// Check for common patterns
|
|
793
|
+
if (defaultValue === 'NULL') {
|
|
794
|
+
return null;
|
|
795
|
+
}
|
|
796
|
+
if (defaultValue === 'CURRENT_TIMESTAMP') {
|
|
797
|
+
return defaultValue;
|
|
798
|
+
}
|
|
799
|
+
if (/^'.*'$/.test(defaultValue)) {
|
|
800
|
+
return defaultValue.slice(1, -1);
|
|
801
|
+
}
|
|
802
|
+
if (/^-?\d+$/.test(defaultValue)) {
|
|
803
|
+
return Number.parseInt(defaultValue, 10);
|
|
804
|
+
}
|
|
805
|
+
if (/^-?\d+\.\d+$/.test(defaultValue)) {
|
|
806
|
+
return Number.parseFloat(defaultValue);
|
|
807
|
+
}
|
|
808
|
+
return defaultValue;
|
|
809
|
+
}
|
|
810
|
+
normalizeReferentialAction(action) {
|
|
811
|
+
switch(action.toUpperCase()){
|
|
812
|
+
case 'CASCADE':
|
|
813
|
+
return 'CASCADE';
|
|
814
|
+
case 'SET NULL':
|
|
815
|
+
return 'SET NULL';
|
|
816
|
+
case 'RESTRICT':
|
|
817
|
+
return 'RESTRICT';
|
|
818
|
+
case 'NO ACTION':
|
|
819
|
+
return 'NO ACTION';
|
|
820
|
+
default:
|
|
821
|
+
return undefined;
|
|
960
822
|
}
|
|
961
|
-
|
|
962
|
-
});
|
|
963
|
-
sql += columns.join(',\n\t');
|
|
964
|
-
sql += `\n);`;
|
|
965
|
-
// log('sql', sql);
|
|
966
|
-
return sql;
|
|
823
|
+
}
|
|
967
824
|
}
|
|
825
|
+
/**
|
|
826
|
+
* Alias for MysqlSchemaIntrospector.
|
|
827
|
+
* MariaDB uses the same information_schema structure as MySQL.
|
|
828
|
+
*/ const MariadbSchemaIntrospector = MysqlSchemaIntrospector;
|
|
968
829
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
}
|
|
982
|
-
function createTestCases(spec) {
|
|
983
|
-
let proto = Object.getPrototypeOf(spec);
|
|
984
|
-
const processedMethodsMap = {};
|
|
985
|
-
while(proto.constructor !== Object){
|
|
986
|
-
for (const key of Object.getOwnPropertyNames(proto)){
|
|
987
|
-
const isProcessed = processedMethodsMap[key];
|
|
988
|
-
processedMethodsMap[key] = true;
|
|
989
|
-
if (isProcessed || key === 'constructor' || typeof spec[key] !== 'function') {
|
|
990
|
-
continue;
|
|
991
|
-
}
|
|
992
|
-
const callback = spec[key].bind(spec);
|
|
993
|
-
if (hooks[key]) {
|
|
994
|
-
hooks[key](callback);
|
|
995
|
-
} else if (key.startsWith('should')) {
|
|
996
|
-
it(key, callback);
|
|
997
|
-
} else if (key.startsWith('fffShould')) {
|
|
998
|
-
it.only(key, callback);
|
|
999
|
-
} else if (key.startsWith('xxxShould')) {
|
|
1000
|
-
it.skip(key, callback);
|
|
830
|
+
/**
|
|
831
|
+
* PostgreSQL schema introspector
|
|
832
|
+
*/ class PostgresSchemaIntrospector {
|
|
833
|
+
constructor(pool){
|
|
834
|
+
this.pool = pool;
|
|
835
|
+
}
|
|
836
|
+
async getTableSchema(tableName) {
|
|
837
|
+
const querier = await this.getQuerier();
|
|
838
|
+
try {
|
|
839
|
+
const exists = await this.tableExistsInternal(querier, tableName);
|
|
840
|
+
if (!exists) {
|
|
841
|
+
return undefined;
|
|
1001
842
|
}
|
|
843
|
+
const [columns, indexes, foreignKeys, primaryKey] = await Promise.all([
|
|
844
|
+
this.getColumns(querier, tableName),
|
|
845
|
+
this.getIndexes(querier, tableName),
|
|
846
|
+
this.getForeignKeys(querier, tableName),
|
|
847
|
+
this.getPrimaryKey(querier, tableName)
|
|
848
|
+
]);
|
|
849
|
+
return {
|
|
850
|
+
name: tableName,
|
|
851
|
+
columns,
|
|
852
|
+
primaryKey,
|
|
853
|
+
indexes,
|
|
854
|
+
foreignKeys
|
|
855
|
+
};
|
|
856
|
+
} finally{
|
|
857
|
+
await querier.release();
|
|
1002
858
|
}
|
|
1003
|
-
proto = Object.getPrototypeOf(proto);
|
|
1004
859
|
}
|
|
1005
|
-
|
|
1006
|
-
const
|
|
1007
|
-
beforeAll,
|
|
1008
|
-
beforeEach,
|
|
1009
|
-
afterEach,
|
|
1010
|
-
afterAll
|
|
1011
|
-
};
|
|
1012
|
-
|
|
1013
|
-
export { BaseEntity, _Company as Company, _InventoryAdjustment as InventoryAdjustment, _Item as Item, _ItemAdjustment as ItemAdjustment, _ItemTag as ItemTag, _LedgerAccount as LedgerAccount, _MeasureUnit as MeasureUnit, _MeasureUnitCategory as MeasureUnitCategory, _Profile as Profile, _Storehouse as Storehouse, _Tag as Tag, _Tax as Tax, _TaxCategory as TaxCategory, _User as User, _UserWithNonUpdatableId as UserWithNonUpdatableId, clearTables, createSpec, createTables, dropTables };
|
|
1014
|
-
//# sourceMappingURL=uql-browser.min.js.map
|
|
1015
|
-
nst querier = await this.getQuerier();
|
|
860
|
+
async getTableNames() {
|
|
861
|
+
const querier = await this.getQuerier();
|
|
1016
862
|
try {
|
|
1017
863
|
const sql = `
|
|
1018
864
|
SELECT table_name
|