@nocobase/database 0.9.1-alpha.1 → 0.9.1-alpha.2
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/lib/collection.d.ts +2 -1
- package/lib/collection.js +20 -4
- package/lib/database.d.ts +6 -0
- package/lib/database.js +14 -4
- package/lib/fields/belongs-to-many-field.js +8 -2
- package/lib/fields/field.js +2 -2
- package/lib/filter-parser.js +1 -1
- package/lib/query-interface/mysql-query-interface.d.ts +7 -0
- package/lib/query-interface/mysql-query-interface.js +39 -0
- package/lib/query-interface/postgres-query-interface.d.ts +6 -0
- package/lib/query-interface/postgres-query-interface.js +41 -0
- package/lib/query-interface/query-interface-builder.d.ts +2 -0
- package/lib/query-interface/query-interface-builder.js +23 -0
- package/lib/query-interface/query-interface.d.ts +9 -0
- package/lib/query-interface/query-interface.js +18 -0
- package/lib/query-interface/sqlite-query-interface.d.ts +6 -0
- package/lib/query-interface/sqlite-query-interface.js +38 -0
- package/lib/relation-repository/multiple-relation-repository.js +2 -0
- package/lib/relation-repository/single-relation-repository.js +1 -0
- package/lib/sync-runner.d.ts +1 -1
- package/lib/sync-runner.js +16 -19
- package/lib/utils.js +13 -3
- package/package.json +4 -3
- package/src/__tests__/filter.test.ts +60 -0
- package/src/collection.ts +20 -4
- package/src/database.ts +19 -5
- package/src/fields/belongs-to-many-field.ts +8 -2
- package/src/fields/field.ts +5 -3
- package/src/filter-parser.ts +1 -1
- package/src/query-interface/mysql-query-interface.ts +20 -0
- package/src/query-interface/postgres-query-interface.ts +22 -0
- package/src/query-interface/query-interface-builder.ts +14 -0
- package/src/query-interface/query-interface.ts +12 -0
- package/src/query-interface/sqlite-query-interface.ts +18 -0
- package/src/relation-repository/multiple-relation-repository.ts +4 -0
- package/src/relation-repository/single-relation-repository.ts +2 -0
- package/src/sync-runner.ts +19 -22
- package/src/utils.ts +4 -3
package/lib/collection.d.ts
CHANGED
|
@@ -65,7 +65,7 @@ export declare class Collection<TModelAttributes extends {} = any, TCreationAttr
|
|
|
65
65
|
resetFields(): void;
|
|
66
66
|
remove(): void;
|
|
67
67
|
removeFromDb(options?: QueryInterfaceDropTableOptions): Promise<void>;
|
|
68
|
-
existsInDb(options?: Transactionable):
|
|
68
|
+
existsInDb(options?: Transactionable): any;
|
|
69
69
|
removeField(name: string): void | Field;
|
|
70
70
|
/**
|
|
71
71
|
* TODO
|
|
@@ -91,4 +91,5 @@ export declare class Collection<TModelAttributes extends {} = any, TCreationAttr
|
|
|
91
91
|
isParent(): boolean;
|
|
92
92
|
addSchemaTableName(): any;
|
|
93
93
|
quotedTableName(): any;
|
|
94
|
+
collectionSchema(): string;
|
|
94
95
|
}
|
package/lib/collection.js
CHANGED
|
@@ -396,7 +396,7 @@ class Collection extends _events().EventEmitter {
|
|
|
396
396
|
})) {
|
|
397
397
|
const queryInterface = _this.db.sequelize.getQueryInterface();
|
|
398
398
|
|
|
399
|
-
yield queryInterface.dropTable(_this.
|
|
399
|
+
yield queryInterface.dropTable(_this.addSchemaTableName(), options);
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
_this.remove();
|
|
@@ -407,7 +407,7 @@ class Collection extends _events().EventEmitter {
|
|
|
407
407
|
var _this2 = this;
|
|
408
408
|
|
|
409
409
|
return _asyncToGenerator(function* () {
|
|
410
|
-
return _this2.db.
|
|
410
|
+
return _this2.db.queryInterface.collectionTableExists(_this2, options);
|
|
411
411
|
})();
|
|
412
412
|
}
|
|
413
413
|
|
|
@@ -659,8 +659,8 @@ class Collection extends _events().EventEmitter {
|
|
|
659
659
|
addSchemaTableName() {
|
|
660
660
|
const tableName = this.model.tableName;
|
|
661
661
|
|
|
662
|
-
if (this.
|
|
663
|
-
return this.db.utils.addSchema(tableName, this.
|
|
662
|
+
if (this.collectionSchema()) {
|
|
663
|
+
return this.db.utils.addSchema(tableName, this.collectionSchema());
|
|
664
664
|
}
|
|
665
665
|
|
|
666
666
|
return tableName;
|
|
@@ -670,6 +670,22 @@ class Collection extends _events().EventEmitter {
|
|
|
670
670
|
return this.db.utils.quoteTable(this.addSchemaTableName());
|
|
671
671
|
}
|
|
672
672
|
|
|
673
|
+
collectionSchema() {
|
|
674
|
+
if (this.options.schema) {
|
|
675
|
+
return this.options.schema;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (this.db.options.schema) {
|
|
679
|
+
return this.db.options.schema;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
if (this.db.inDialect('postgres')) {
|
|
683
|
+
return 'public';
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
return undefined;
|
|
687
|
+
}
|
|
688
|
+
|
|
673
689
|
}
|
|
674
690
|
|
|
675
691
|
exports.Collection = Collection;
|
package/lib/database.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ import { Repository } from './repository';
|
|
|
19
19
|
import { AfterDefineCollectionListener, BeforeDefineCollectionListener, CreateListener, CreateWithAssociationsListener, DatabaseAfterDefineCollectionEventType, DatabaseAfterRemoveCollectionEventType, DatabaseBeforeDefineCollectionEventType, DatabaseBeforeRemoveCollectionEventType, DestroyListener, EventType, ModelCreateEventTypes, ModelCreateWithAssociationsEventTypes, ModelDestroyEventTypes, ModelSaveEventTypes, ModelSaveWithAssociationsEventTypes, ModelUpdateEventTypes, ModelUpdateWithAssociationsEventTypes, ModelValidateEventTypes, RemoveCollectionListener, SaveListener, SaveWithAssociationsListener, SyncListener, UpdateListener, UpdateWithAssociationsListener, ValidateListener } from './types';
|
|
20
20
|
import DatabaseUtils from './database-utils';
|
|
21
21
|
import { BaseValueParser } from './value-parsers';
|
|
22
|
+
import QueryInterface from './query-interface/query-interface';
|
|
23
|
+
import { Logger } from '@nocobase/logger';
|
|
22
24
|
export interface MergeOptions extends merge.Options {
|
|
23
25
|
}
|
|
24
26
|
export interface PendingOptions {
|
|
@@ -84,6 +86,7 @@ export declare class Database extends EventEmitter implements AsyncEmitter {
|
|
|
84
86
|
pendingFields: Map<string, FieldTypes.RelationField[]>;
|
|
85
87
|
modelCollection: Map<ModelStatic<any>, Collection<any, any>>;
|
|
86
88
|
tableNameCollectionMap: Map<string, Collection<any, any>>;
|
|
89
|
+
queryInterface: QueryInterface;
|
|
87
90
|
utils: DatabaseUtils;
|
|
88
91
|
referenceMap: ReferencesMap;
|
|
89
92
|
inheritanceMap: InheritanceMap;
|
|
@@ -94,11 +97,14 @@ export declare class Database extends EventEmitter implements AsyncEmitter {
|
|
|
94
97
|
collectionOptions: CollectionOptions;
|
|
95
98
|
mergeOptions?: any;
|
|
96
99
|
}[]>;
|
|
100
|
+
logger: Logger;
|
|
97
101
|
constructor(options: DatabaseOptions);
|
|
102
|
+
setLogger(logger: Logger): void;
|
|
98
103
|
initListener(): void;
|
|
99
104
|
addMigration(item: MigrationItem): void;
|
|
100
105
|
addMigrations(options: AddMigrationsOptions): void;
|
|
101
106
|
inDialect(...dialect: string[]): boolean;
|
|
107
|
+
escapeId(identifier: string): string;
|
|
102
108
|
/**
|
|
103
109
|
* Add collection to database
|
|
104
110
|
* @param options
|
package/lib/database.js
CHANGED
|
@@ -112,6 +112,8 @@ var _databaseUtils = _interopRequireDefault(require("./database-utils"));
|
|
|
112
112
|
|
|
113
113
|
var _valueParsers = require("./value-parsers");
|
|
114
114
|
|
|
115
|
+
var _queryInterfaceBuilder = _interopRequireDefault(require("./query-interface/query-interface-builder"));
|
|
116
|
+
|
|
115
117
|
const _excluded = ["drop"],
|
|
116
118
|
_excluded2 = ["retry"];
|
|
117
119
|
|
|
@@ -231,6 +233,7 @@ class Database extends _events().EventEmitter {
|
|
|
231
233
|
this.pendingFields = new Map();
|
|
232
234
|
this.modelCollection = new Map();
|
|
233
235
|
this.tableNameCollectionMap = new Map();
|
|
236
|
+
this.queryInterface = void 0;
|
|
234
237
|
this.utils = new _databaseUtils.default(this);
|
|
235
238
|
this.referenceMap = new _ReferencesMap.default();
|
|
236
239
|
this.inheritanceMap = new _inheritedMap.default();
|
|
@@ -238,6 +241,7 @@ class Database extends _events().EventEmitter {
|
|
|
238
241
|
this.modelHook = void 0;
|
|
239
242
|
this.version = void 0;
|
|
240
243
|
this.delayCollectionExtend = new Map();
|
|
244
|
+
this.logger = void 0;
|
|
241
245
|
this.version = new DatabaseVersion(this);
|
|
242
246
|
|
|
243
247
|
const opts = _objectSpread({
|
|
@@ -268,6 +272,7 @@ class Database extends _events().EventEmitter {
|
|
|
268
272
|
|
|
269
273
|
this.options = opts;
|
|
270
274
|
this.sequelize = new (_sequelize().Sequelize)(opts);
|
|
275
|
+
this.queryInterface = (0, _queryInterfaceBuilder.default)(this);
|
|
271
276
|
this.collections = new Map();
|
|
272
277
|
this.modelHook = new _modelHook.ModelHook(this);
|
|
273
278
|
this.on('afterDefineCollection', collection => {
|
|
@@ -337,6 +342,10 @@ class Database extends _events().EventEmitter {
|
|
|
337
342
|
(0, _utils2.patchSequelizeQueryInterface)(this);
|
|
338
343
|
}
|
|
339
344
|
|
|
345
|
+
setLogger(logger) {
|
|
346
|
+
this.logger = logger;
|
|
347
|
+
}
|
|
348
|
+
|
|
340
349
|
initListener() {
|
|
341
350
|
var _this2 = this;
|
|
342
351
|
|
|
@@ -464,6 +473,10 @@ class Database extends _events().EventEmitter {
|
|
|
464
473
|
inDialect(...dialect) {
|
|
465
474
|
return dialect.includes(this.sequelize.getDialect());
|
|
466
475
|
}
|
|
476
|
+
|
|
477
|
+
escapeId(identifier) {
|
|
478
|
+
return this.inDialect('mysql') ? `\`${identifier}\`` : `"${identifier}"`;
|
|
479
|
+
}
|
|
467
480
|
/**
|
|
468
481
|
* Add collection to database
|
|
469
482
|
* @param options
|
|
@@ -773,10 +786,7 @@ class Database extends _events().EventEmitter {
|
|
|
773
786
|
return false;
|
|
774
787
|
}
|
|
775
788
|
|
|
776
|
-
|
|
777
|
-
transaction: options === null || options === void 0 ? void 0 : options.transaction
|
|
778
|
-
});
|
|
779
|
-
return tables.includes(_this5.getCollection(name).model.tableName);
|
|
789
|
+
return yield _this5.queryInterface.collectionTableExists(collection, options);
|
|
780
790
|
})();
|
|
781
791
|
}
|
|
782
792
|
|
|
@@ -73,9 +73,15 @@ class BelongsToManyField extends _relationField.RelationField {
|
|
|
73
73
|
if (database.hasCollection(through)) {
|
|
74
74
|
Through = database.getCollection(through);
|
|
75
75
|
} else {
|
|
76
|
-
|
|
76
|
+
const throughCollectionOptions = {
|
|
77
77
|
name: through
|
|
78
|
-
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
if (this.collection.collectionSchema()) {
|
|
81
|
+
throughCollectionOptions['schema'] = this.collection.collectionSchema();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Through = database.collection(throughCollectionOptions);
|
|
79
85
|
Object.defineProperty(Through.model, 'isThrough', {
|
|
80
86
|
value: true
|
|
81
87
|
});
|
package/lib/fields/field.js
CHANGED
|
@@ -184,7 +184,7 @@ class Field {
|
|
|
184
184
|
})) && columnReferencesCount == 1) {
|
|
185
185
|
const queryInterface = _this2.database.sequelize.getQueryInterface();
|
|
186
186
|
|
|
187
|
-
yield queryInterface.removeColumn(_this2.collection.
|
|
187
|
+
yield queryInterface.removeColumn(_this2.collection.addSchemaTableName(), _this2.columnName(), options);
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
_this2.remove();
|
|
@@ -212,7 +212,7 @@ class Field {
|
|
|
212
212
|
sql = `
|
|
213
213
|
select column_name
|
|
214
214
|
from INFORMATION_SCHEMA.COLUMNS
|
|
215
|
-
where TABLE_NAME='${_this3.collection.model.tableName}' AND column_name='${_this3.columnName()}'
|
|
215
|
+
where TABLE_NAME='${_this3.collection.model.tableName}' AND column_name='${_this3.columnName()}' AND table_schema='${_this3.collection.collectionSchema() || 'public'}'
|
|
216
216
|
`;
|
|
217
217
|
}
|
|
218
218
|
|
package/lib/filter-parser.js
CHANGED
|
@@ -215,7 +215,7 @@ class FilterParser {
|
|
|
215
215
|
origins.push(attr); // if it is target model attribute
|
|
216
216
|
|
|
217
217
|
if (target.rawAttributes[attr]) {
|
|
218
|
-
associationKeys.push(attr);
|
|
218
|
+
associationKeys.push(target.rawAttributes[attr].field || attr);
|
|
219
219
|
target = null;
|
|
220
220
|
} else if (target.associations[attr]) {
|
|
221
221
|
// if it is target model association (nested association filter)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import QueryInterface from './query-interface';
|
|
2
|
+
import { Collection } from '../collection';
|
|
3
|
+
import { Transactionable } from 'sequelize';
|
|
4
|
+
export default class MysqlQueryInterface extends QueryInterface {
|
|
5
|
+
constructor(db: any);
|
|
6
|
+
collectionTableExists(collection: Collection, options?: Transactionable): Promise<boolean>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _queryInterface = _interopRequireDefault(require("./query-interface"));
|
|
9
|
+
|
|
10
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
+
|
|
12
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
13
|
+
|
|
14
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
15
|
+
|
|
16
|
+
class MysqlQueryInterface extends _queryInterface.default {
|
|
17
|
+
constructor(db) {
|
|
18
|
+
super(db);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
collectionTableExists(collection, options) {
|
|
22
|
+
var _this = this;
|
|
23
|
+
|
|
24
|
+
return _asyncToGenerator(function* () {
|
|
25
|
+
const transaction = options === null || options === void 0 ? void 0 : options.transaction;
|
|
26
|
+
const tableName = collection.model.tableName;
|
|
27
|
+
const databaseName = _this.db.options.database;
|
|
28
|
+
const sql = `SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${databaseName}' AND TABLE_NAME = '${tableName}'`;
|
|
29
|
+
const results = yield _this.db.sequelize.query(sql, {
|
|
30
|
+
type: 'SELECT',
|
|
31
|
+
transaction
|
|
32
|
+
});
|
|
33
|
+
return results.length > 0;
|
|
34
|
+
})();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exports.default = MysqlQueryInterface;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import QueryInterface from './query-interface';
|
|
2
|
+
import { Collection } from '../collection';
|
|
3
|
+
export default class PostgresQueryInterface extends QueryInterface {
|
|
4
|
+
constructor(db: any);
|
|
5
|
+
collectionTableExists(collection: Collection, options?: any): Promise<any>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _queryInterface = _interopRequireDefault(require("./query-interface"));
|
|
9
|
+
|
|
10
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
+
|
|
12
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
13
|
+
|
|
14
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
15
|
+
|
|
16
|
+
class PostgresQueryInterface extends _queryInterface.default {
|
|
17
|
+
constructor(db) {
|
|
18
|
+
super(db);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
collectionTableExists(collection, options) {
|
|
22
|
+
var _this = this;
|
|
23
|
+
|
|
24
|
+
return _asyncToGenerator(function* () {
|
|
25
|
+
const transaction = options === null || options === void 0 ? void 0 : options.transaction;
|
|
26
|
+
const tableName = collection.model.tableName;
|
|
27
|
+
const schema = collection.collectionSchema() || 'public';
|
|
28
|
+
const sql = `SELECT EXISTS(SELECT 1 FROM information_schema.tables
|
|
29
|
+
WHERE table_schema = '${schema}'
|
|
30
|
+
AND table_name = '${tableName}')`;
|
|
31
|
+
const results = yield _this.db.sequelize.query(sql, {
|
|
32
|
+
type: 'SELECT',
|
|
33
|
+
transaction
|
|
34
|
+
});
|
|
35
|
+
return results[0]['exists'];
|
|
36
|
+
})();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
exports.default = PostgresQueryInterface;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = buildQueryInterface;
|
|
7
|
+
|
|
8
|
+
var _mysqlQueryInterface = _interopRequireDefault(require("./mysql-query-interface"));
|
|
9
|
+
|
|
10
|
+
var _postgresQueryInterface = _interopRequireDefault(require("./postgres-query-interface"));
|
|
11
|
+
|
|
12
|
+
var _sqliteQueryInterface = _interopRequireDefault(require("./sqlite-query-interface"));
|
|
13
|
+
|
|
14
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
+
|
|
16
|
+
function buildQueryInterface(db) {
|
|
17
|
+
const map = {
|
|
18
|
+
mysql: _mysqlQueryInterface.default,
|
|
19
|
+
postgres: _postgresQueryInterface.default,
|
|
20
|
+
sqlite: _sqliteQueryInterface.default
|
|
21
|
+
};
|
|
22
|
+
return new map[db.options.dialect](db);
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import Database from '../database';
|
|
2
|
+
import { Collection } from '../collection';
|
|
3
|
+
import { QueryInterface as SequelizeQueryInterface, Transactionable } from 'sequelize';
|
|
4
|
+
export default abstract class QueryInterface {
|
|
5
|
+
db: Database;
|
|
6
|
+
sequelizeQueryInterface: SequelizeQueryInterface;
|
|
7
|
+
protected constructor(db: Database);
|
|
8
|
+
abstract collectionTableExists(collection: Collection, options?: Transactionable): Promise<boolean>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
class QueryInterface {
|
|
9
|
+
constructor(db) {
|
|
10
|
+
this.db = void 0;
|
|
11
|
+
this.sequelizeQueryInterface = void 0;
|
|
12
|
+
this.db = db;
|
|
13
|
+
this.sequelizeQueryInterface = db.sequelize.getQueryInterface();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
exports.default = QueryInterface;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import QueryInterface from './query-interface';
|
|
2
|
+
import { Collection } from '../collection';
|
|
3
|
+
export default class SqliteQueryInterface extends QueryInterface {
|
|
4
|
+
constructor(db: any);
|
|
5
|
+
collectionTableExists(collection: Collection, options?: any): Promise<boolean>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _queryInterface = _interopRequireDefault(require("./query-interface"));
|
|
9
|
+
|
|
10
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
+
|
|
12
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
13
|
+
|
|
14
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
15
|
+
|
|
16
|
+
class SqliteQueryInterface extends _queryInterface.default {
|
|
17
|
+
constructor(db) {
|
|
18
|
+
super(db);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
collectionTableExists(collection, options) {
|
|
22
|
+
var _this = this;
|
|
23
|
+
|
|
24
|
+
return _asyncToGenerator(function* () {
|
|
25
|
+
const transaction = options === null || options === void 0 ? void 0 : options.transaction;
|
|
26
|
+
const tableName = collection.model.tableName;
|
|
27
|
+
const sql = `SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}';`;
|
|
28
|
+
const results = yield _this.db.sequelize.query(sql, {
|
|
29
|
+
type: 'SELECT',
|
|
30
|
+
transaction
|
|
31
|
+
});
|
|
32
|
+
return results.length > 0;
|
|
33
|
+
})();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
exports.default = SqliteQueryInterface;
|
|
@@ -75,6 +75,7 @@ class MultipleRelationRepository extends _relationRepository.RelationRepository
|
|
|
75
75
|
const getAccessor = _this.accessors().get;
|
|
76
76
|
|
|
77
77
|
const sourceModel = yield _this.getSourceModel(transaction);
|
|
78
|
+
if (!sourceModel) return [];
|
|
78
79
|
|
|
79
80
|
if (findOptions.include && findOptions.include.length > 0) {
|
|
80
81
|
const ids = (yield sourceModel[getAccessor](_objectSpread(_objectSpread({}, findOptions), {}, {
|
|
@@ -139,6 +140,7 @@ class MultipleRelationRepository extends _relationRepository.RelationRepository
|
|
|
139
140
|
return _asyncToGenerator(function* () {
|
|
140
141
|
const transaction = yield _this3.getTransaction(options);
|
|
141
142
|
const sourceModel = yield _this3.getSourceModel(transaction);
|
|
143
|
+
if (!sourceModel) return 0;
|
|
142
144
|
|
|
143
145
|
const queryOptions = _this3.buildQueryOptions(options);
|
|
144
146
|
|
|
@@ -80,6 +80,7 @@ class SingleRelationRepository extends _relationRepository.RelationRepository {
|
|
|
80
80
|
const getAccessor = _this3.accessors().get;
|
|
81
81
|
|
|
82
82
|
const sourceModel = yield _this3.getSourceModel(transaction);
|
|
83
|
+
if (!sourceModel) return null;
|
|
83
84
|
|
|
84
85
|
if ((findOptions === null || findOptions === void 0 ? void 0 : (_findOptions$include = findOptions.include) === null || _findOptions$include === void 0 ? void 0 : _findOptions$include.length) > 0) {
|
|
85
86
|
const templateModel = yield sourceModel[getAccessor](_objectSpread(_objectSpread({}, findOptions), {}, {
|
package/lib/sync-runner.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare class SyncRunner {
|
|
2
2
|
static syncInheritModel(model: any, options: any): Promise<void>;
|
|
3
|
-
static createTable(tableName: any, attributes: any, options: any, model: any,
|
|
3
|
+
static createTable(tableName: any, attributes: any, options: any, model: any, parents: any): Promise<any>;
|
|
4
4
|
}
|
package/lib/sync-runner.js
CHANGED
|
@@ -41,7 +41,6 @@ class SyncRunner {
|
|
|
41
41
|
const transaction = options.transaction;
|
|
42
42
|
const inheritedCollection = model.collection;
|
|
43
43
|
const db = inheritedCollection.context.database;
|
|
44
|
-
const schemaName = db.options.schema || 'public';
|
|
45
44
|
const dialect = db.sequelize.getDialect();
|
|
46
45
|
const queryInterface = db.sequelize.getQueryInterface();
|
|
47
46
|
|
|
@@ -55,10 +54,7 @@ class SyncRunner {
|
|
|
55
54
|
throw new Error(`Inherit model ${inheritedCollection.name} can't be created without parents, parents option is ${_lodash().default.castArray(inheritedCollection.options.inherits).join(', ')}`);
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
const
|
|
59
|
-
const tableName = model.tableName;
|
|
60
|
-
const schemaTableName = db.utils.addSchema(tableName);
|
|
61
|
-
const quoteTableName = db.utils.quoteTable(tableName);
|
|
57
|
+
const tableName = inheritedCollection.addSchemaTableName();
|
|
62
58
|
const attributes = model.tableAttributes;
|
|
63
59
|
|
|
64
60
|
const childAttributes = _lodash().default.pickBy(attributes, value => {
|
|
@@ -66,10 +62,10 @@ class SyncRunner {
|
|
|
66
62
|
});
|
|
67
63
|
|
|
68
64
|
let maxSequenceVal = 0;
|
|
69
|
-
let maxSequenceName;
|
|
65
|
+
let maxSequenceName; // find max sequence
|
|
70
66
|
|
|
71
67
|
if (childAttributes.id && childAttributes.id.autoIncrement) {
|
|
72
|
-
var _iterator = _createForOfIteratorHelper(
|
|
68
|
+
var _iterator = _createForOfIteratorHelper(parents),
|
|
73
69
|
_step;
|
|
74
70
|
|
|
75
71
|
try {
|
|
@@ -77,8 +73,8 @@ class SyncRunner {
|
|
|
77
73
|
const parent = _step.value;
|
|
78
74
|
const sequenceNameResult = yield queryInterface.sequelize.query(`SELECT column_default
|
|
79
75
|
FROM information_schema.columns
|
|
80
|
-
WHERE table_name = '${parent}'
|
|
81
|
-
and table_schema = '${
|
|
76
|
+
WHERE table_name = '${parent.model.tableName}'
|
|
77
|
+
and table_schema = '${parent.collectionSchema()}'
|
|
82
78
|
and "column_name" = 'id';`, {
|
|
83
79
|
transaction
|
|
84
80
|
});
|
|
@@ -114,17 +110,18 @@ class SyncRunner {
|
|
|
114
110
|
}
|
|
115
111
|
}
|
|
116
112
|
|
|
117
|
-
yield _this.createTable(
|
|
113
|
+
yield _this.createTable(tableName, childAttributes, options, model, parents); // if we have max sequence, set it to child table
|
|
118
114
|
|
|
119
115
|
if (maxSequenceName) {
|
|
120
|
-
const parentsDeep = Array.from(db.inheritanceMap.getParents(inheritedCollection.name)).map(parent => db.getCollection(parent).
|
|
121
|
-
const sequenceTables = [...parentsDeep, tableName
|
|
116
|
+
const parentsDeep = Array.from(db.inheritanceMap.getParents(inheritedCollection.name)).map(parent => db.getCollection(parent).addSchemaTableName());
|
|
117
|
+
const sequenceTables = [...parentsDeep, tableName];
|
|
122
118
|
|
|
123
119
|
for (var _i = 0, _sequenceTables = sequenceTables; _i < _sequenceTables.length; _i++) {
|
|
124
120
|
const sequenceTable = _sequenceTables[_i];
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
|
|
121
|
+
const tableName = sequenceTable.tableName;
|
|
122
|
+
const schemaName = sequenceTable.schema;
|
|
123
|
+
const queryName = Boolean(tableName.match(/[A-Z]/)) && !tableName.includes(`"`) ? `"${tableName}"` : tableName;
|
|
124
|
+
const idColumnQuery = yield queryInterface.sequelize.query(`SELECT column_name
|
|
128
125
|
FROM information_schema.columns
|
|
129
126
|
WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schemaName}';
|
|
130
127
|
`, {
|
|
@@ -135,7 +132,7 @@ WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schem
|
|
|
135
132
|
continue;
|
|
136
133
|
}
|
|
137
134
|
|
|
138
|
-
yield queryInterface.sequelize.query(`alter table
|
|
135
|
+
yield queryInterface.sequelize.query(`alter table ${db.utils.quoteTable(sequenceTable)}
|
|
139
136
|
alter column id set default nextval('${maxSequenceName}')`, {
|
|
140
137
|
transaction
|
|
141
138
|
});
|
|
@@ -156,7 +153,7 @@ WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schem
|
|
|
156
153
|
})();
|
|
157
154
|
}
|
|
158
155
|
|
|
159
|
-
static createTable(tableName, attributes, options, model,
|
|
156
|
+
static createTable(tableName, attributes, options, model, parents) {
|
|
160
157
|
return _asyncToGenerator(function* () {
|
|
161
158
|
let sql = '';
|
|
162
159
|
options = _objectSpread({}, options);
|
|
@@ -179,8 +176,8 @@ WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schem
|
|
|
179
176
|
table: tableName,
|
|
180
177
|
context: 'createTable'
|
|
181
178
|
});
|
|
182
|
-
sql = `${queryGenerator.createTableQuery(tableName, attributes, options)}`.replace(';', ` INHERITS (${
|
|
183
|
-
return
|
|
179
|
+
sql = `${queryGenerator.createTableQuery(tableName, attributes, options)}`.replace(';', ` INHERITS (${parents.map(t => {
|
|
180
|
+
return t.addSchemaTableName();
|
|
184
181
|
}).join(', ')});`);
|
|
185
182
|
return yield model.sequelize.query(sql, options);
|
|
186
183
|
})();
|
package/lib/utils.js
CHANGED
|
@@ -23,6 +23,16 @@ function _crypto() {
|
|
|
23
23
|
|
|
24
24
|
var _identifierError = require("./errors/identifier-error");
|
|
25
25
|
|
|
26
|
+
function _lodash() {
|
|
27
|
+
const data = _interopRequireDefault(require("lodash"));
|
|
28
|
+
|
|
29
|
+
_lodash = function _lodash() {
|
|
30
|
+
return data;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return data;
|
|
34
|
+
}
|
|
35
|
+
|
|
26
36
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
27
37
|
|
|
28
38
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
@@ -144,14 +154,14 @@ function snakeCase(name) {
|
|
|
144
154
|
|
|
145
155
|
function patchShowConstraintsQuery(queryGenerator, db) {
|
|
146
156
|
queryGenerator.showConstraintsQuery = (tableName, constraintName) => {
|
|
147
|
-
const lines = ['SELECT constraint_catalog AS "constraintCatalog",', 'constraint_schema AS "constraintSchema",', 'constraint_name AS "constraintName",', 'table_catalog AS "tableCatalog",', 'table_schema AS "tableSchema",', 'table_name AS "tableName",', 'constraint_type AS "constraintType",', 'is_deferrable AS "isDeferrable",', 'initially_deferred AS "initiallyDeferred"', 'from INFORMATION_SCHEMA.table_constraints', `WHERE table_name='${tableName}'`];
|
|
157
|
+
const lines = ['SELECT constraint_catalog AS "constraintCatalog",', 'constraint_schema AS "constraintSchema",', 'constraint_name AS "constraintName",', 'table_catalog AS "tableCatalog",', 'table_schema AS "tableSchema",', 'table_name AS "tableName",', 'constraint_type AS "constraintType",', 'is_deferrable AS "isDeferrable",', 'initially_deferred AS "initiallyDeferred"', 'from INFORMATION_SCHEMA.table_constraints', `WHERE table_name='${_lodash().default.isPlainObject(tableName) ? tableName.tableName : tableName}'`];
|
|
148
158
|
|
|
149
159
|
if (!constraintName) {
|
|
150
160
|
lines.push(`AND constraint_name='${constraintName}'`);
|
|
151
161
|
}
|
|
152
162
|
|
|
153
|
-
if (
|
|
154
|
-
lines.push(`AND table_schema='${
|
|
163
|
+
if (_lodash().default.isPlainObject(tableName) && tableName.schema) {
|
|
164
|
+
lines.push(`AND table_schema='${tableName.schema}'`);
|
|
155
165
|
}
|
|
156
166
|
|
|
157
167
|
return lines.join(' ');
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/database",
|
|
3
|
-
"version": "0.9.1-alpha.
|
|
3
|
+
"version": "0.9.1-alpha.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"types": "./lib/index.d.ts",
|
|
7
7
|
"license": "Apache-2.0",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@nocobase/
|
|
9
|
+
"@nocobase/logger": "0.9.1-alpha.2",
|
|
10
|
+
"@nocobase/utils": "0.9.1-alpha.2",
|
|
10
11
|
"async-mutex": "^0.3.2",
|
|
11
12
|
"cron-parser": "4.4.0",
|
|
12
13
|
"deepmerge": "^4.2.2",
|
|
@@ -27,5 +28,5 @@
|
|
|
27
28
|
"url": "git+https://github.com/nocobase/nocobase.git",
|
|
28
29
|
"directory": "packages/database"
|
|
29
30
|
},
|
|
30
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "d588a68eca4feed4642a4cb317301011266fe5c9"
|
|
31
32
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import Database from '../database';
|
|
2
|
+
import { mockDatabase } from '../mock-database';
|
|
3
|
+
|
|
4
|
+
describe('filter', () => {
|
|
5
|
+
let db: Database;
|
|
6
|
+
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
db = mockDatabase();
|
|
9
|
+
|
|
10
|
+
await db.clean({ drop: true });
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterEach(async () => {
|
|
14
|
+
await db.close();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should filter by association field', async () => {
|
|
18
|
+
const UserCollection = db.collection({
|
|
19
|
+
name: 'users',
|
|
20
|
+
fields: [
|
|
21
|
+
{ type: 'string', name: 'name' },
|
|
22
|
+
{ type: 'hasMany', name: 'posts' },
|
|
23
|
+
],
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const PostCollection = db.collection({
|
|
27
|
+
name: 'posts',
|
|
28
|
+
fields: [
|
|
29
|
+
{ type: 'string', name: 'title' },
|
|
30
|
+
{ type: 'belongsTo', name: 'user' },
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
await db.sync();
|
|
35
|
+
|
|
36
|
+
const user = await UserCollection.repository.create({
|
|
37
|
+
values: {
|
|
38
|
+
name: 'John',
|
|
39
|
+
posts: [
|
|
40
|
+
{
|
|
41
|
+
title: 'p1',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
title: 'p2',
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const response = await UserCollection.repository.find({
|
|
51
|
+
filter: {
|
|
52
|
+
'posts.createdAt': {
|
|
53
|
+
$dateOn: user.get('createdAt'),
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
expect(response).toHaveLength(1);
|
|
59
|
+
});
|
|
60
|
+
});
|
package/src/collection.ts
CHANGED
|
@@ -308,13 +308,13 @@ export class Collection<
|
|
|
308
308
|
})
|
|
309
309
|
) {
|
|
310
310
|
const queryInterface = this.db.sequelize.getQueryInterface();
|
|
311
|
-
await queryInterface.dropTable(this.
|
|
311
|
+
await queryInterface.dropTable(this.addSchemaTableName(), options);
|
|
312
312
|
}
|
|
313
313
|
this.remove();
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
async existsInDb(options?: Transactionable) {
|
|
317
|
-
return this.db.
|
|
317
|
+
return this.db.queryInterface.collectionTableExists(this, options);
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
removeField(name: string): void | Field {
|
|
@@ -542,8 +542,8 @@ export class Collection<
|
|
|
542
542
|
public addSchemaTableName() {
|
|
543
543
|
const tableName = this.model.tableName;
|
|
544
544
|
|
|
545
|
-
if (this.
|
|
546
|
-
return this.db.utils.addSchema(tableName, this.
|
|
545
|
+
if (this.collectionSchema()) {
|
|
546
|
+
return this.db.utils.addSchema(tableName, this.collectionSchema());
|
|
547
547
|
}
|
|
548
548
|
|
|
549
549
|
return tableName;
|
|
@@ -552,4 +552,20 @@ export class Collection<
|
|
|
552
552
|
public quotedTableName() {
|
|
553
553
|
return this.db.utils.quoteTable(this.addSchemaTableName());
|
|
554
554
|
}
|
|
555
|
+
|
|
556
|
+
public collectionSchema() {
|
|
557
|
+
if (this.options.schema) {
|
|
558
|
+
return this.options.schema;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (this.db.options.schema) {
|
|
562
|
+
return this.db.options.schema;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (this.db.inDialect('postgres')) {
|
|
566
|
+
return 'public';
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return undefined;
|
|
570
|
+
}
|
|
555
571
|
}
|
package/src/database.ts
CHANGED
|
@@ -64,6 +64,9 @@ import { patchSequelizeQueryInterface, snakeCase } from './utils';
|
|
|
64
64
|
|
|
65
65
|
import DatabaseUtils from './database-utils';
|
|
66
66
|
import { BaseValueParser, registerFieldValueParsers } from './value-parsers';
|
|
67
|
+
import buildQueryInterface from './query-interface/query-interface-builder';
|
|
68
|
+
import QueryInterface from './query-interface/query-interface';
|
|
69
|
+
import { Logger } from '@nocobase/logger';
|
|
67
70
|
|
|
68
71
|
export interface MergeOptions extends merge.Options {}
|
|
69
72
|
|
|
@@ -166,6 +169,8 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|
|
166
169
|
modelCollection = new Map<ModelStatic<any>, Collection>();
|
|
167
170
|
tableNameCollectionMap = new Map<string, Collection>();
|
|
168
171
|
|
|
172
|
+
queryInterface: QueryInterface;
|
|
173
|
+
|
|
169
174
|
utils = new DatabaseUtils(this);
|
|
170
175
|
referenceMap = new ReferencesMap();
|
|
171
176
|
inheritanceMap = new InheritanceMap();
|
|
@@ -177,6 +182,8 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|
|
177
182
|
|
|
178
183
|
delayCollectionExtend = new Map<string, { collectionOptions: CollectionOptions; mergeOptions?: any }[]>();
|
|
179
184
|
|
|
185
|
+
logger: Logger;
|
|
186
|
+
|
|
180
187
|
constructor(options: DatabaseOptions) {
|
|
181
188
|
super();
|
|
182
189
|
|
|
@@ -212,6 +219,8 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|
|
212
219
|
|
|
213
220
|
this.sequelize = new Sequelize(opts);
|
|
214
221
|
|
|
222
|
+
this.queryInterface = buildQueryInterface(this);
|
|
223
|
+
|
|
215
224
|
this.collections = new Map();
|
|
216
225
|
this.modelHook = new ModelHook(this);
|
|
217
226
|
|
|
@@ -280,6 +289,10 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|
|
280
289
|
patchSequelizeQueryInterface(this);
|
|
281
290
|
}
|
|
282
291
|
|
|
292
|
+
setLogger(logger: Logger) {
|
|
293
|
+
this.logger = logger;
|
|
294
|
+
}
|
|
295
|
+
|
|
283
296
|
initListener() {
|
|
284
297
|
this.on('beforeDefine', (model, options) => {
|
|
285
298
|
if (this.options.underscored) {
|
|
@@ -374,6 +387,10 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|
|
374
387
|
return dialect.includes(this.sequelize.getDialect());
|
|
375
388
|
}
|
|
376
389
|
|
|
390
|
+
escapeId(identifier: string) {
|
|
391
|
+
return this.inDialect('mysql') ? `\`${identifier}\`` : `"${identifier}"`;
|
|
392
|
+
}
|
|
393
|
+
|
|
377
394
|
/**
|
|
378
395
|
* Add collection to database
|
|
379
396
|
* @param options
|
|
@@ -627,15 +644,12 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|
|
627
644
|
|
|
628
645
|
async collectionExistsInDb(name: string, options?: Transactionable) {
|
|
629
646
|
const collection = this.getCollection(name);
|
|
647
|
+
|
|
630
648
|
if (!collection) {
|
|
631
649
|
return false;
|
|
632
650
|
}
|
|
633
651
|
|
|
634
|
-
|
|
635
|
-
transaction: options?.transaction,
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
return tables.includes(this.getCollection(name).model.tableName);
|
|
652
|
+
return await this.queryInterface.collectionTableExists(collection, options);
|
|
639
653
|
}
|
|
640
654
|
|
|
641
655
|
public isSqliteMemory() {
|
|
@@ -55,9 +55,15 @@ export class BelongsToManyField extends RelationField {
|
|
|
55
55
|
if (database.hasCollection(through)) {
|
|
56
56
|
Through = database.getCollection(through);
|
|
57
57
|
} else {
|
|
58
|
-
|
|
58
|
+
const throughCollectionOptions = {
|
|
59
59
|
name: through,
|
|
60
|
-
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
if (this.collection.collectionSchema()) {
|
|
63
|
+
throughCollectionOptions['schema'] = this.collection.collectionSchema();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
Through = database.collection(throughCollectionOptions);
|
|
61
67
|
|
|
62
68
|
Object.defineProperty(Through.model, 'isThrough', { value: true });
|
|
63
69
|
}
|
package/src/fields/field.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
ModelIndexesOptions,
|
|
7
7
|
QueryInterfaceOptions,
|
|
8
8
|
SyncOptions,
|
|
9
|
-
Transactionable
|
|
9
|
+
Transactionable,
|
|
10
10
|
} from 'sequelize';
|
|
11
11
|
import { Collection } from '../collection';
|
|
12
12
|
import { Database } from '../database';
|
|
@@ -169,7 +169,7 @@ export abstract class Field {
|
|
|
169
169
|
columnReferencesCount == 1
|
|
170
170
|
) {
|
|
171
171
|
const queryInterface = this.database.sequelize.getQueryInterface();
|
|
172
|
-
await queryInterface.removeColumn(this.collection.
|
|
172
|
+
await queryInterface.removeColumn(this.collection.addSchemaTableName(), this.columnName(), options);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
this.remove();
|
|
@@ -194,7 +194,9 @@ export abstract class Field {
|
|
|
194
194
|
sql = `
|
|
195
195
|
select column_name
|
|
196
196
|
from INFORMATION_SCHEMA.COLUMNS
|
|
197
|
-
where TABLE_NAME='${
|
|
197
|
+
where TABLE_NAME='${
|
|
198
|
+
this.collection.model.tableName
|
|
199
|
+
}' AND column_name='${this.columnName()}' AND table_schema='${this.collection.collectionSchema() || 'public'}'
|
|
198
200
|
`;
|
|
199
201
|
}
|
|
200
202
|
const [rows] = await this.database.sequelize.query(sql, opts);
|
package/src/filter-parser.ts
CHANGED
|
@@ -178,7 +178,7 @@ export default class FilterParser {
|
|
|
178
178
|
origins.push(attr);
|
|
179
179
|
// if it is target model attribute
|
|
180
180
|
if (target.rawAttributes[attr]) {
|
|
181
|
-
associationKeys.push(attr);
|
|
181
|
+
associationKeys.push(target.rawAttributes[attr].field || attr);
|
|
182
182
|
target = null;
|
|
183
183
|
} else if (target.associations[attr]) {
|
|
184
184
|
// if it is target model association (nested association filter)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import QueryInterface from './query-interface';
|
|
2
|
+
import { Collection } from '../collection';
|
|
3
|
+
import { Transactionable } from 'sequelize';
|
|
4
|
+
|
|
5
|
+
export default class MysqlQueryInterface extends QueryInterface {
|
|
6
|
+
constructor(db) {
|
|
7
|
+
super(db);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async collectionTableExists(collection: Collection, options?: Transactionable) {
|
|
11
|
+
const transaction = options?.transaction;
|
|
12
|
+
|
|
13
|
+
const tableName = collection.model.tableName;
|
|
14
|
+
const databaseName = this.db.options.database;
|
|
15
|
+
const sql = `SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${databaseName}' AND TABLE_NAME = '${tableName}'`;
|
|
16
|
+
|
|
17
|
+
const results = await this.db.sequelize.query(sql, { type: 'SELECT', transaction });
|
|
18
|
+
return results.length > 0;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import QueryInterface from './query-interface';
|
|
2
|
+
import { Collection } from '../collection';
|
|
3
|
+
|
|
4
|
+
export default class PostgresQueryInterface extends QueryInterface {
|
|
5
|
+
constructor(db) {
|
|
6
|
+
super(db);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async collectionTableExists(collection: Collection, options?) {
|
|
10
|
+
const transaction = options?.transaction;
|
|
11
|
+
|
|
12
|
+
const tableName = collection.model.tableName;
|
|
13
|
+
const schema = collection.collectionSchema() || 'public';
|
|
14
|
+
|
|
15
|
+
const sql = `SELECT EXISTS(SELECT 1 FROM information_schema.tables
|
|
16
|
+
WHERE table_schema = '${schema}'
|
|
17
|
+
AND table_name = '${tableName}')`;
|
|
18
|
+
|
|
19
|
+
const results = await this.db.sequelize.query(sql, { type: 'SELECT', transaction });
|
|
20
|
+
return results[0]['exists'];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Database from '../database';
|
|
2
|
+
import MysqlQueryInterface from './mysql-query-interface';
|
|
3
|
+
import PostgresQueryInterface from './postgres-query-interface';
|
|
4
|
+
import SqliteQueryInterface from './sqlite-query-interface';
|
|
5
|
+
|
|
6
|
+
export default function buildQueryInterface(db: Database) {
|
|
7
|
+
const map = {
|
|
8
|
+
mysql: MysqlQueryInterface,
|
|
9
|
+
postgres: PostgresQueryInterface,
|
|
10
|
+
sqlite: SqliteQueryInterface,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
return new map[db.options.dialect](db);
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import Database from '../database';
|
|
2
|
+
import { Collection } from '../collection';
|
|
3
|
+
import { QueryInterface as SequelizeQueryInterface, Transactionable } from 'sequelize';
|
|
4
|
+
|
|
5
|
+
export default abstract class QueryInterface {
|
|
6
|
+
sequelizeQueryInterface: SequelizeQueryInterface;
|
|
7
|
+
protected constructor(public db: Database) {
|
|
8
|
+
this.sequelizeQueryInterface = db.sequelize.getQueryInterface();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
abstract collectionTableExists(collection: Collection, options?: Transactionable): Promise<boolean>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import QueryInterface from './query-interface';
|
|
2
|
+
import { Collection } from '../collection';
|
|
3
|
+
|
|
4
|
+
export default class SqliteQueryInterface extends QueryInterface {
|
|
5
|
+
constructor(db) {
|
|
6
|
+
super(db);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async collectionTableExists(collection: Collection, options?) {
|
|
10
|
+
const transaction = options?.transaction;
|
|
11
|
+
|
|
12
|
+
const tableName = collection.model.tableName;
|
|
13
|
+
|
|
14
|
+
const sql = `SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}';`;
|
|
15
|
+
const results = await this.db.sequelize.query(sql, { type: 'SELECT', transaction });
|
|
16
|
+
return results.length > 0;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -42,6 +42,8 @@ export abstract class MultipleRelationRepository extends RelationRepository {
|
|
|
42
42
|
const getAccessor = this.accessors().get;
|
|
43
43
|
const sourceModel = await this.getSourceModel(transaction);
|
|
44
44
|
|
|
45
|
+
if (!sourceModel) return [];
|
|
46
|
+
|
|
45
47
|
if (findOptions.include && findOptions.include.length > 0) {
|
|
46
48
|
const ids = (
|
|
47
49
|
await sourceModel[getAccessor]({
|
|
@@ -103,6 +105,8 @@ export abstract class MultipleRelationRepository extends RelationRepository {
|
|
|
103
105
|
const transaction = await this.getTransaction(options);
|
|
104
106
|
|
|
105
107
|
const sourceModel = await this.getSourceModel(transaction);
|
|
108
|
+
if (!sourceModel) return 0;
|
|
109
|
+
|
|
106
110
|
const queryOptions = this.buildQueryOptions(options);
|
|
107
111
|
|
|
108
112
|
const count = await sourceModel[this.accessors().get]({
|
|
@@ -54,6 +54,8 @@ export abstract class SingleRelationRepository extends RelationRepository {
|
|
|
54
54
|
const getAccessor = this.accessors().get;
|
|
55
55
|
const sourceModel = await this.getSourceModel(transaction);
|
|
56
56
|
|
|
57
|
+
if (!sourceModel) return null;
|
|
58
|
+
|
|
57
59
|
if (findOptions?.include?.length > 0) {
|
|
58
60
|
const templateModel = await sourceModel[getAccessor]({
|
|
59
61
|
...findOptions,
|
package/src/sync-runner.ts
CHANGED
|
@@ -7,7 +7,6 @@ export class SyncRunner {
|
|
|
7
7
|
|
|
8
8
|
const inheritedCollection = model.collection as InheritedCollection;
|
|
9
9
|
const db = inheritedCollection.context.database;
|
|
10
|
-
const schemaName = db.options.schema || 'public';
|
|
11
10
|
|
|
12
11
|
const dialect = db.sequelize.getDialect();
|
|
13
12
|
|
|
@@ -27,12 +26,7 @@ export class SyncRunner {
|
|
|
27
26
|
);
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
const tableName = model.tableName;
|
|
33
|
-
|
|
34
|
-
const schemaTableName = db.utils.addSchema(tableName);
|
|
35
|
-
const quoteTableName = db.utils.quoteTable(tableName);
|
|
29
|
+
const tableName = inheritedCollection.addSchemaTableName();
|
|
36
30
|
|
|
37
31
|
const attributes = model.tableAttributes;
|
|
38
32
|
|
|
@@ -43,13 +37,14 @@ export class SyncRunner {
|
|
|
43
37
|
let maxSequenceVal = 0;
|
|
44
38
|
let maxSequenceName;
|
|
45
39
|
|
|
40
|
+
// find max sequence
|
|
46
41
|
if (childAttributes.id && childAttributes.id.autoIncrement) {
|
|
47
|
-
for (const parent of
|
|
42
|
+
for (const parent of parents) {
|
|
48
43
|
const sequenceNameResult = await queryInterface.sequelize.query(
|
|
49
44
|
`SELECT column_default
|
|
50
45
|
FROM information_schema.columns
|
|
51
|
-
WHERE table_name = '${parent}'
|
|
52
|
-
and table_schema = '${
|
|
46
|
+
WHERE table_name = '${parent.model.tableName}'
|
|
47
|
+
and table_schema = '${parent.collectionSchema()}'
|
|
53
48
|
and "column_name" = 'id';`,
|
|
54
49
|
{
|
|
55
50
|
transaction,
|
|
@@ -88,22 +83,24 @@ export class SyncRunner {
|
|
|
88
83
|
}
|
|
89
84
|
}
|
|
90
85
|
|
|
91
|
-
await this.createTable(
|
|
86
|
+
await this.createTable(tableName, childAttributes, options, model, parents);
|
|
92
87
|
|
|
88
|
+
// if we have max sequence, set it to child table
|
|
93
89
|
if (maxSequenceName) {
|
|
94
|
-
const parentsDeep = Array.from(db.inheritanceMap.getParents(inheritedCollection.name)).map(
|
|
95
|
-
|
|
90
|
+
const parentsDeep = Array.from(db.inheritanceMap.getParents(inheritedCollection.name)).map((parent) =>
|
|
91
|
+
db.getCollection(parent).addSchemaTableName(),
|
|
96
92
|
);
|
|
97
93
|
|
|
98
|
-
const sequenceTables = [...parentsDeep, tableName
|
|
94
|
+
const sequenceTables = [...parentsDeep, tableName];
|
|
99
95
|
|
|
100
96
|
for (const sequenceTable of sequenceTables) {
|
|
101
|
-
const
|
|
102
|
-
|
|
97
|
+
const tableName = sequenceTable.tableName;
|
|
98
|
+
const schemaName = sequenceTable.schema;
|
|
99
|
+
|
|
100
|
+
const queryName = Boolean(tableName.match(/[A-Z]/)) && !tableName.includes(`"`) ? `"${tableName}"` : tableName;
|
|
103
101
|
|
|
104
102
|
const idColumnQuery = await queryInterface.sequelize.query(
|
|
105
|
-
`
|
|
106
|
-
SELECT column_name
|
|
103
|
+
`SELECT column_name
|
|
107
104
|
FROM information_schema.columns
|
|
108
105
|
WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schemaName}';
|
|
109
106
|
`,
|
|
@@ -117,7 +114,7 @@ WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schem
|
|
|
117
114
|
}
|
|
118
115
|
|
|
119
116
|
await queryInterface.sequelize.query(
|
|
120
|
-
`alter table
|
|
117
|
+
`alter table ${db.utils.quoteTable(sequenceTable)}
|
|
121
118
|
alter column id set default nextval('${maxSequenceName}')`,
|
|
122
119
|
{
|
|
123
120
|
transaction,
|
|
@@ -139,7 +136,7 @@ WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schem
|
|
|
139
136
|
}
|
|
140
137
|
}
|
|
141
138
|
|
|
142
|
-
static async createTable(tableName, attributes, options, model,
|
|
139
|
+
static async createTable(tableName, attributes, options, model, parents) {
|
|
143
140
|
let sql = '';
|
|
144
141
|
|
|
145
142
|
options = { ...options };
|
|
@@ -164,9 +161,9 @@ WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schem
|
|
|
164
161
|
|
|
165
162
|
sql = `${queryGenerator.createTableQuery(tableName, attributes, options)}`.replace(
|
|
166
163
|
';',
|
|
167
|
-
` INHERITS (${
|
|
164
|
+
` INHERITS (${parents
|
|
168
165
|
.map((t) => {
|
|
169
|
-
return
|
|
166
|
+
return t.addSchemaTableName();
|
|
170
167
|
})
|
|
171
168
|
.join(', ')});`,
|
|
172
169
|
);
|
package/src/utils.ts
CHANGED
|
@@ -2,6 +2,7 @@ import crypto from 'crypto';
|
|
|
2
2
|
import Database from './database';
|
|
3
3
|
import { IdentifierError } from './errors/identifier-error';
|
|
4
4
|
import { Model } from './model';
|
|
5
|
+
import lodash from 'lodash';
|
|
5
6
|
|
|
6
7
|
type HandleAppendsQueryOptions = {
|
|
7
8
|
templateModel: any;
|
|
@@ -96,15 +97,15 @@ function patchShowConstraintsQuery(queryGenerator, db) {
|
|
|
96
97
|
'is_deferrable AS "isDeferrable",',
|
|
97
98
|
'initially_deferred AS "initiallyDeferred"',
|
|
98
99
|
'from INFORMATION_SCHEMA.table_constraints',
|
|
99
|
-
`WHERE table_name='${tableName}'`,
|
|
100
|
+
`WHERE table_name='${lodash.isPlainObject(tableName) ? tableName.tableName : tableName}'`,
|
|
100
101
|
];
|
|
101
102
|
|
|
102
103
|
if (!constraintName) {
|
|
103
104
|
lines.push(`AND constraint_name='${constraintName}'`);
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
if (
|
|
107
|
-
lines.push(`AND table_schema='${
|
|
107
|
+
if (lodash.isPlainObject(tableName) && tableName.schema) {
|
|
108
|
+
lines.push(`AND table_schema='${tableName.schema}'`);
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
return lines.join(' ');
|