@nocobase/database 0.9.0-alpha.2 → 0.9.1-alpha.1

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.
Files changed (103) hide show
  1. package/lib/collection-importer.js +1 -1
  2. package/lib/collection.d.ts +7 -1
  3. package/lib/collection.js +135 -61
  4. package/lib/database-utils/index.d.ts +8 -0
  5. package/lib/database-utils/index.js +59 -0
  6. package/lib/database.d.ts +26 -3
  7. package/lib/database.js +224 -61
  8. package/lib/fields/array-field.d.ts +1 -1
  9. package/lib/fields/array-field.js +2 -2
  10. package/lib/fields/belongs-to-many-field.js +1 -2
  11. package/lib/fields/field.d.ts +1 -0
  12. package/lib/fields/field.js +37 -15
  13. package/lib/fields/has-one-field.d.ts +1 -1
  14. package/lib/fields/has-one-field.js +9 -5
  15. package/lib/fields/number-field.d.ts +9 -6
  16. package/lib/fields/number-field.js +8 -6
  17. package/lib/fields/sort-field.js +15 -1
  18. package/lib/index.d.ts +6 -4
  19. package/lib/index.js +59 -36
  20. package/lib/mock-database.d.ts +2 -0
  21. package/lib/mock-database.js +3 -1
  22. package/lib/model.js +10 -1
  23. package/lib/options-parser.js +3 -0
  24. package/lib/relation-repository/belongs-to-many-repository.js +4 -2
  25. package/lib/repository.js +5 -2
  26. package/lib/sync-runner.d.ts +1 -1
  27. package/lib/sync-runner.js +28 -18
  28. package/lib/types.d.ts +7 -1
  29. package/lib/update-associations.js +17 -3
  30. package/lib/update-guard.d.ts +1 -0
  31. package/lib/update-guard.js +6 -0
  32. package/lib/utils.d.ts +5 -0
  33. package/lib/utils.js +68 -0
  34. package/lib/value-parsers/array-value-parser.d.ts +8 -0
  35. package/lib/value-parsers/array-value-parser.js +76 -0
  36. package/lib/value-parsers/base-value-parser.d.ts +12 -0
  37. package/lib/value-parsers/base-value-parser.js +59 -0
  38. package/lib/value-parsers/boolean-value-parser.d.ts +4 -0
  39. package/lib/value-parsers/boolean-value-parser.js +46 -0
  40. package/lib/value-parsers/date-value-parser.d.ts +5 -0
  41. package/lib/value-parsers/date-value-parser.js +91 -0
  42. package/lib/value-parsers/index.d.ts +12 -0
  43. package/lib/value-parsers/index.js +102 -0
  44. package/lib/value-parsers/json-value-parser.d.ts +4 -0
  45. package/lib/value-parsers/json-value-parser.js +37 -0
  46. package/lib/value-parsers/number-value-parser.d.ts +4 -0
  47. package/lib/value-parsers/number-value-parser.js +49 -0
  48. package/lib/value-parsers/string-value-parser.d.ts +8 -0
  49. package/lib/value-parsers/string-value-parser.js +76 -0
  50. package/lib/value-parsers/to-many-value-parser.d.ts +13 -0
  51. package/lib/value-parsers/to-many-value-parser.js +169 -0
  52. package/lib/value-parsers/to-one-value-parser.d.ts +4 -0
  53. package/lib/value-parsers/to-one-value-parser.js +49 -0
  54. package/package.json +4 -3
  55. package/src/__tests__/bigint.test.ts +1 -1
  56. package/src/__tests__/collection-importer.test.ts +13 -1
  57. package/src/__tests__/collection.test.ts +19 -9
  58. package/src/__tests__/database.test.ts +32 -0
  59. package/src/__tests__/fields/sort-field.test.ts +23 -0
  60. package/src/__tests__/inhertits/collection-inherits.test.ts +7 -5
  61. package/src/__tests__/percent2float.test.ts +14 -0
  62. package/src/__tests__/postgres/schema.test.ts +120 -0
  63. package/src/__tests__/underscored-options.test.ts +207 -0
  64. package/src/__tests__/update-associations-through.test.ts +73 -0
  65. package/src/__tests__/value-parsers/base.test.ts +20 -0
  66. package/src/__tests__/value-parsers/date.test.ts +67 -0
  67. package/src/__tests__/value-parsers/number.test.ts +46 -0
  68. package/src/__tests__/value-parsers/to-many.test.ts +206 -0
  69. package/src/__tests__/value-parsers/to-one.test.ts +60 -0
  70. package/src/collection-importer.ts +2 -2
  71. package/src/collection.ts +97 -15
  72. package/src/database-utils/index.ts +38 -0
  73. package/src/database.ts +171 -33
  74. package/src/fields/array-field.ts +1 -1
  75. package/src/fields/belongs-to-field.ts +1 -1
  76. package/src/fields/belongs-to-many-field.ts +0 -1
  77. package/src/fields/field.ts +45 -16
  78. package/src/fields/has-many-field.ts +1 -1
  79. package/src/fields/has-one-field.ts +11 -7
  80. package/src/fields/number-field.ts +10 -6
  81. package/src/fields/sort-field.ts +13 -1
  82. package/src/index.ts +7 -4
  83. package/src/inherited-collection.ts +1 -0
  84. package/src/mock-database.ts +3 -1
  85. package/src/model.ts +11 -2
  86. package/src/options-parser.ts +5 -0
  87. package/src/relation-repository/belongs-to-many-repository.ts +4 -2
  88. package/src/repository.ts +8 -3
  89. package/src/sync-runner.ts +33 -19
  90. package/src/types.ts +12 -1
  91. package/src/update-associations.ts +12 -5
  92. package/src/update-guard.ts +6 -0
  93. package/src/utils.ts +94 -0
  94. package/src/value-parsers/array-value-parser.ts +30 -0
  95. package/src/value-parsers/base-value-parser.ts +40 -0
  96. package/src/value-parsers/boolean-value-parser.ts +29 -0
  97. package/src/value-parsers/date-value-parser.ts +38 -0
  98. package/src/value-parsers/index.ts +46 -0
  99. package/src/value-parsers/json-value-parser.ts +19 -0
  100. package/src/value-parsers/number-value-parser.ts +29 -0
  101. package/src/value-parsers/string-value-parser.ts +31 -0
  102. package/src/value-parsers/to-many-value-parser.ts +85 -0
  103. package/src/value-parsers/to-one-value-parser.ts +20 -0
@@ -12,6 +12,7 @@ import { Collection } from '../collection';
12
12
  import { Database } from '../database';
13
13
  import { InheritedCollection } from '../inherited-collection';
14
14
  import { ModelEventTypes } from '../types';
15
+ import { snakeCase } from '../utils';
15
16
 
16
17
  export interface FieldContext {
17
18
  database: Database;
@@ -39,9 +40,9 @@ export abstract class Field {
39
40
  [key: string]: any;
40
41
 
41
42
  constructor(options?: any, context?: FieldContext) {
42
- this.context = context;
43
- this.database = context.database;
44
- this.collection = context.collection;
43
+ this.context = context as any;
44
+ this.database = this.context.database;
45
+ this.collection = this.context.collection;
45
46
  this.options = options || {};
46
47
  this.init();
47
48
  }
@@ -89,6 +90,18 @@ export abstract class Field {
89
90
  return this.collection.removeField(this.name);
90
91
  }
91
92
 
93
+ columnName() {
94
+ if (this.options.field) {
95
+ return this.options.field;
96
+ }
97
+
98
+ if (this.database.options.underscored) {
99
+ return snakeCase(this.name);
100
+ }
101
+
102
+ return this.name;
103
+ }
104
+
92
105
  async removeFromDb(options?: QueryInterfaceOptions) {
93
106
  const attribute = this.collection.model.rawAttributes[this.name];
94
107
 
@@ -113,14 +126,19 @@ export abstract class Field {
113
126
  }
114
127
  if (this.collection.model.options.timestamps !== false) {
115
128
  // timestamps 相关字段不删除
116
- if (['createdAt', 'updatedAt', 'deletedAt'].includes(this.name)) {
129
+ let timestampsFields = ['createdAt', 'updatedAt', 'deletedAt'];
130
+ if (this.database.options.underscored) {
131
+ timestampsFields = timestampsFields.map((field) => snakeCase(field));
132
+ }
133
+ if (timestampsFields.includes(this.columnName())) {
134
+ this.collection.fields.delete(this.name);
117
135
  return;
118
136
  }
119
137
  }
120
138
  // 排序字段通过 sortable 控制
121
139
  const sortable = this.collection.options.sortable;
122
140
  if (sortable) {
123
- let sortField: string;
141
+ let sortField: any;
124
142
  if (sortable === true) {
125
143
  sortField = 'sort';
126
144
  } else if (typeof sortable === 'string') {
@@ -132,19 +150,28 @@ export abstract class Field {
132
150
  return;
133
151
  }
134
152
  }
135
- if (this.options.field && this.name !== this.options.field) {
136
- // field 指向的是真实的字段名,如果与 name 不一样,说明字段只是引用
137
- this.remove();
138
- return;
139
- }
153
+
154
+ // if (this.options.field && this.name !== this.options.field) {
155
+ // // field 指向的是真实的字段名,如果与 name 不一样,说明字段只是引用
156
+ // this.remove();
157
+ // return;
158
+ // }
159
+
160
+ const columnReferencesCount = _.filter(
161
+ this.collection.model.rawAttributes,
162
+ (attr) => attr.field == this.columnName(),
163
+ ).length;
164
+
140
165
  if (
141
- await this.existsInDb({
166
+ (await this.existsInDb({
142
167
  transaction: options?.transaction,
143
- })
168
+ })) &&
169
+ columnReferencesCount == 1
144
170
  ) {
145
171
  const queryInterface = this.database.sequelize.getQueryInterface();
146
- await queryInterface.removeColumn(this.collection.model.tableName, this.name, options);
172
+ await queryInterface.removeColumn(this.collection.model.tableName, this.columnName(), options);
147
173
  }
174
+
148
175
  this.remove();
149
176
  }
150
177
 
@@ -154,18 +181,20 @@ export abstract class Field {
154
181
  };
155
182
  let sql;
156
183
  if (this.database.sequelize.getDialect() === 'sqlite') {
157
- sql = `SELECT * from pragma_table_info('${this.collection.model.tableName}') WHERE name = '${this.name}'`;
184
+ sql = `SELECT * from pragma_table_info('${this.collection.model.tableName}') WHERE name = '${this.columnName()}'`;
158
185
  } else if (this.database.inDialect('mysql')) {
159
186
  sql = `
160
187
  select column_name
161
188
  from INFORMATION_SCHEMA.COLUMNS
162
- where TABLE_SCHEMA='${this.database.options.database}' AND TABLE_NAME='${this.collection.model.tableName}' AND column_name='${this.name}'
189
+ where TABLE_SCHEMA='${this.database.options.database}' AND TABLE_NAME='${
190
+ this.collection.model.tableName
191
+ }' AND column_name='${this.columnName()}'
163
192
  `;
164
193
  } else {
165
194
  sql = `
166
195
  select column_name
167
196
  from INFORMATION_SCHEMA.COLUMNS
168
- where TABLE_NAME='${this.collection.model.tableName}' AND column_name='${this.name}'
197
+ where TABLE_NAME='${this.collection.model.tableName}' AND column_name='${this.columnName()}'
169
198
  `;
170
199
  }
171
200
  const [rows] = await this.database.sequelize.query(sql, opts);
@@ -5,7 +5,7 @@ import {
5
5
  ForeignKeyOptions,
6
6
  HasManyOptions,
7
7
  HasManyOptions as SequelizeHasManyOptions,
8
- Utils,
8
+ Utils
9
9
  } from 'sequelize';
10
10
  import { Collection } from '../collection';
11
11
  import { Reference } from '../features/ReferencesMap';
@@ -5,12 +5,12 @@ import {
5
5
  ForeignKeyOptions,
6
6
  HasOneOptions,
7
7
  HasOneOptions as SequelizeHasOneOptions,
8
- Utils,
8
+ Utils
9
9
  } from 'sequelize';
10
10
  import { Collection } from '../collection';
11
+ import { Reference } from '../features/ReferencesMap';
11
12
  import { checkIdentifier } from '../utils';
12
13
  import { BaseRelationFieldOptions, RelationField } from './relation-field';
13
- import { Reference } from '../features/ReferencesMap';
14
14
 
15
15
  export interface HasOneFieldOptions extends HasOneOptions {
16
16
  /**
@@ -84,11 +84,15 @@ export class HasOneField extends RelationField {
84
84
  }
85
85
 
86
86
  get foreignKey() {
87
- if (this.options.foreignKey) {
88
- return this.options.foreignKey;
89
- }
90
- const { model } = this.context.collection;
91
- return Utils.camelize([model.options.name.singular, model.primaryKeyAttribute].join('_'));
87
+ const foreignKey = (() => {
88
+ if (this.options.foreignKey) {
89
+ return this.options.foreignKey;
90
+ }
91
+ const { model } = this.context.collection;
92
+ return Utils.camelize([model.options.name.singular, model.primaryKeyAttribute].join('_'));
93
+ })();
94
+
95
+ return foreignKey;
92
96
  }
93
97
 
94
98
  reference(association): Reference {
@@ -1,7 +1,11 @@
1
1
  import { DataTypes } from 'sequelize';
2
2
  import { BaseColumnFieldOptions, Field } from './field';
3
3
 
4
- export class IntegerField extends Field {
4
+ abstract class NumberField extends Field {
5
+
6
+ }
7
+
8
+ export class IntegerField extends NumberField {
5
9
  get dataType() {
6
10
  return DataTypes.INTEGER;
7
11
  }
@@ -11,7 +15,7 @@ export interface IntegerFieldOptions extends BaseColumnFieldOptions {
11
15
  type: 'integer';
12
16
  }
13
17
 
14
- export class BigIntField extends Field {
18
+ export class BigIntField extends NumberField {
15
19
  get dataType() {
16
20
  return DataTypes.BIGINT;
17
21
  }
@@ -21,7 +25,7 @@ export interface BigIntFieldOptions extends BaseColumnFieldOptions {
21
25
  type: 'bigInt';
22
26
  }
23
27
 
24
- export class FloatField extends Field {
28
+ export class FloatField extends NumberField {
25
29
  get dataType() {
26
30
  return DataTypes.FLOAT;
27
31
  }
@@ -31,7 +35,7 @@ export interface FloatFieldOptions extends BaseColumnFieldOptions {
31
35
  type: 'float';
32
36
  }
33
37
 
34
- export class DoubleField extends Field {
38
+ export class DoubleField extends NumberField {
35
39
  get dataType() {
36
40
  return DataTypes.DOUBLE;
37
41
  }
@@ -41,7 +45,7 @@ export interface DoubleFieldOptions extends BaseColumnFieldOptions {
41
45
  type: 'double';
42
46
  }
43
47
 
44
- export class RealField extends Field {
48
+ export class RealField extends NumberField {
45
49
  get dataType() {
46
50
  return DataTypes.REAL;
47
51
  }
@@ -51,7 +55,7 @@ export interface RealFieldOptions extends BaseColumnFieldOptions {
51
55
  type: 'real';
52
56
  }
53
57
 
54
- export class DecimalField extends Field {
58
+ export class DecimalField extends NumberField {
55
59
  get dataType() {
56
60
  return DataTypes.DECIMAL;
57
61
  }
@@ -53,9 +53,21 @@ export class SortField extends Field {
53
53
  transaction,
54
54
  });
55
55
 
56
+ const orderKey = (() => {
57
+ const model = this.collection.model;
58
+ if (model.primaryKeyAttribute) {
59
+ return model.primaryKeyAttribute;
60
+ }
61
+ if (model.rawAttributes['createdAt']) {
62
+ return 'createdAt';
63
+ }
64
+
65
+ throw new Error(`can not find order key for collection ${this.collection.name}`);
66
+ })();
67
+
56
68
  if (emptyCount === totalCount && emptyCount > 0) {
57
69
  const records = await this.collection.repository.find({
58
- order: [this.collection.model.primaryKeyAttribute],
70
+ order: [orderKey],
59
71
  transaction,
60
72
  });
61
73
 
package/src/index.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  export { DataTypes, ModelStatic, Op, SyncOptions } from 'sequelize';
2
2
  export * from './collection';
3
- export * from './inherited-collection';
3
+ export * from './collection-importer';
4
4
  export * from './database';
5
5
  export { Database as default } from './database';
6
+ export * from './field-repository/array-field-repository';
6
7
  export * from './fields';
8
+ export * from './filter-match';
9
+ export * from './inherited-collection';
7
10
  export * from './magic-attribute-model';
8
11
  export * from './migration';
9
12
  export * from './mock-database';
@@ -15,6 +18,6 @@ export * from './relation-repository/multiple-relation-repository';
15
18
  export * from './relation-repository/single-relation-repository';
16
19
  export * from './repository';
17
20
  export * from './update-associations';
18
- export * from './collection-importer';
19
- export * from './filter-match';
20
- export * from './field-repository/array-field-repository';
21
+ export { snakeCase } from './utils';
22
+ export * from './value-parsers';
23
+
@@ -4,6 +4,7 @@ import { Field } from '.';
4
4
 
5
5
  export class InheritedCollection extends Collection {
6
6
  parents?: Collection[];
7
+
7
8
  constructor(options: CollectionOptions, context: CollectionContext) {
8
9
  if (!options.inherits) {
9
10
  throw new Error('InheritedCollection must have inherits option');
@@ -31,10 +31,12 @@ export function getConfigByEnv() {
31
31
  collate: 'utf8mb4_unicode_ci',
32
32
  },
33
33
  timezone: process.env.DB_TIMEZONE,
34
+ underscored: process.env.DB_UNDERSCORED === 'true',
35
+ schema: process.env.DB_SCHEMA !== 'public' ? process.env.DB_SCHEMA : undefined,
34
36
  };
35
37
  }
36
38
 
37
39
  export function mockDatabase(options: IDatabaseOptions = {}): MockDatabase {
38
- const dbOptions = merge(getConfigByEnv(), options);
40
+ const dbOptions = merge(getConfigByEnv(), options) as any;
39
41
  return new MockDatabase(dbOptions);
40
42
  }
package/src/model.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  import lodash from 'lodash';
2
- import { DataTypes, Model as SequelizeModel, ModelStatic } from 'sequelize';
2
+ import { Model as SequelizeModel, ModelStatic } from 'sequelize';
3
3
  import { Collection } from './collection';
4
4
  import { Database } from './database';
5
5
  import { Field } from './fields';
6
- import type { InheritedCollection } from './inherited-collection';
7
6
  import { SyncRunner } from './sync-runner';
8
7
 
9
8
  const _ = lodash;
@@ -28,6 +27,7 @@ export class Model<TModelAttributes extends {} = any, TCreationAttributes extend
28
27
  public static collection: Collection;
29
28
 
30
29
  [key: string]: any;
30
+
31
31
  protected _changedWithAssociations = new Set();
32
32
  protected _previousDataValuesWithAssociations = {};
33
33
 
@@ -153,6 +153,15 @@ export class Model<TModelAttributes extends {} = any, TCreationAttributes extend
153
153
  static async sync(options) {
154
154
  const model = this as any;
155
155
 
156
+ const _schema = model._schema;
157
+
158
+ if (_schema && _schema != 'public') {
159
+ await this.sequelize.query(`CREATE SCHEMA IF NOT EXISTS "${_schema}";`, {
160
+ raw: true,
161
+ transaction: options?.transaction,
162
+ });
163
+ }
164
+
156
165
  // fix sequelize sync with model that not have any column
157
166
  if (Object.keys(model.tableAttributes).length === 0) {
158
167
  if (this.database.inDialect('sqlite', 'mysql')) {
@@ -75,6 +75,7 @@ export class OptionsParser {
75
75
  for (const sortKey of sort) {
76
76
  let direction = sortKey.startsWith('-') ? 'DESC' : 'ASC';
77
77
  let sortField: Array<any> = sortKey.replace('-', '').split('.');
78
+
78
79
  if (this.database.inDialect('postgres', 'sqlite')) {
79
80
  direction = `${direction} NULLS LAST`;
80
81
  }
@@ -86,7 +87,11 @@ export class OptionsParser {
86
87
  sortField[i] = associationModel.associations[associationKey].target;
87
88
  associationModel = sortField[i];
88
89
  }
90
+ } else {
91
+ const rawField = this.model.rawAttributes[sortField[0]];
92
+ sortField[0] = rawField?.field || sortField[0];
89
93
  }
94
+
90
95
  sortField.push(direction);
91
96
  if (this.database.inDialect('mysql')) {
92
97
  orderParams.push([Sequelize.fn('ISNULL', Sequelize.col(`${this.model.name}.${sortField[0]}`))]);
@@ -62,6 +62,8 @@ export class BelongsToManyRepository extends MultipleRelationRepository implemen
62
62
  const transaction = await this.getTransaction(options);
63
63
  const association = <BelongsToMany>this.association;
64
64
 
65
+ const throughModel = this.throughModel();
66
+
65
67
  const instancesToIds = (instances) => {
66
68
  return instances.map((instance) => instance.get(this.targetKey()));
67
69
  };
@@ -69,7 +71,7 @@ export class BelongsToManyRepository extends MultipleRelationRepository implemen
69
71
  // Through Table
70
72
  const throughTableWhere: Array<any> = [
71
73
  {
72
- [association.foreignKey]: this.sourceKeyValue,
74
+ [throughModel.rawAttributes[association.foreignKey].field]: this.sourceKeyValue,
73
75
  },
74
76
  ];
75
77
 
@@ -100,7 +102,7 @@ export class BelongsToManyRepository extends MultipleRelationRepository implemen
100
102
  }
101
103
 
102
104
  throughTableWhere.push({
103
- [association.otherKey]: {
105
+ [throughModel.rawAttributes[association.otherKey].field]: {
104
106
  [Op.in]: ids,
105
107
  },
106
108
  });
package/src/repository.ts CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  Op,
12
12
  Transactionable,
13
13
  UpdateOptions as SequelizeUpdateOptions,
14
- WhereOperators
14
+ WhereOperators,
15
15
  } from 'sequelize';
16
16
  import { Collection } from './collection';
17
17
  import { Database } from './database';
@@ -407,7 +407,12 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
407
407
 
408
408
  const transaction = await this.getTransaction(options);
409
409
 
410
- const guard = UpdateGuard.fromOptions(this.model, { ...options, action: 'create' });
410
+ const guard = UpdateGuard.fromOptions(this.model, {
411
+ ...options,
412
+ action: 'create',
413
+ underscored: this.collection.options.underscored,
414
+ });
415
+
411
416
  const values = guard.sanitize(options.values || {});
412
417
 
413
418
  const instance = await this.model.create<any>(values, {
@@ -476,7 +481,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
476
481
  }
477
482
  const transaction = await this.getTransaction(options);
478
483
 
479
- const guard = UpdateGuard.fromOptions(this.model, options);
484
+ const guard = UpdateGuard.fromOptions(this.model, { ...options, underscored: this.collection.options.underscored });
480
485
 
481
486
  const values = guard.sanitize(options.values);
482
487
 
@@ -1,6 +1,5 @@
1
1
  import { InheritedCollection } from './inherited-collection';
2
2
  import lodash from 'lodash';
3
- import { Sequelize } from 'sequelize';
4
3
 
5
4
  export class SyncRunner {
6
5
  static async syncInheritModel(model: any, options: any) {
@@ -8,6 +7,8 @@ export class SyncRunner {
8
7
 
9
8
  const inheritedCollection = model.collection as InheritedCollection;
10
9
  const db = inheritedCollection.context.database;
10
+ const schemaName = db.options.schema || 'public';
11
+
11
12
  const dialect = db.sequelize.getDialect();
12
13
 
13
14
  const queryInterface = db.sequelize.getQueryInterface();
@@ -28,7 +29,10 @@ export class SyncRunner {
28
29
 
29
30
  const parentTables = parents.map((parent) => parent.model.tableName);
30
31
 
31
- const tableName = model.getTableName();
32
+ const tableName = model.tableName;
33
+
34
+ const schemaTableName = db.utils.addSchema(tableName);
35
+ const quoteTableName = db.utils.quoteTable(tableName);
32
36
 
33
37
  const attributes = model.tableAttributes;
34
38
 
@@ -42,8 +46,11 @@ export class SyncRunner {
42
46
  if (childAttributes.id && childAttributes.id.autoIncrement) {
43
47
  for (const parent of parentTables) {
44
48
  const sequenceNameResult = await queryInterface.sequelize.query(
45
- `SELECT column_default FROM information_schema.columns WHERE
46
- table_name='${parent}' and "column_name" = 'id';`,
49
+ `SELECT column_default
50
+ FROM information_schema.columns
51
+ WHERE table_name = '${parent}'
52
+ and table_schema = '${schemaName}'
53
+ and "column_name" = 'id';`,
47
54
  {
48
55
  transaction,
49
56
  },
@@ -59,13 +66,14 @@ export class SyncRunner {
59
66
  throw new Error(`Can't find sequence name of ${parent}`);
60
67
  }
61
68
 
62
- const regex = new RegExp(/nextval\('("?\w+"?)\'.*\)/);
69
+ const regex = new RegExp(/nextval\('(.*)'::regclass\)/);
63
70
  const match = regex.exec(columnDefault);
64
71
 
65
72
  const sequenceName = match[1];
66
73
 
67
74
  const sequenceCurrentValResult = await queryInterface.sequelize.query(
68
- `select last_value from ${sequenceName}`,
75
+ `select last_value
76
+ from ${sequenceName}`,
69
77
  {
70
78
  transaction,
71
79
  },
@@ -80,26 +88,25 @@ export class SyncRunner {
80
88
  }
81
89
  }
82
90
 
83
- await this.createTable(tableName, childAttributes, options, model, parentTables);
91
+ await this.createTable(schemaTableName, childAttributes, options, model, parentTables, db);
84
92
 
85
93
  if (maxSequenceName) {
86
94
  const parentsDeep = Array.from(db.inheritanceMap.getParents(inheritedCollection.name)).map(
87
95
  (parent) => db.getCollection(parent).model.tableName,
88
96
  );
89
97
 
90
- const sequenceTables = [...parentsDeep, tableName];
98
+ const sequenceTables = [...parentsDeep, tableName.toString()];
91
99
 
92
100
  for (const sequenceTable of sequenceTables) {
93
- const queryName = Boolean(sequenceTable.match(/[A-Z]/)) ? `"${sequenceTable}"` : sequenceTable;
101
+ const queryName =
102
+ Boolean(sequenceTable.match(/[A-Z]/)) && !sequenceTable.includes(`"`) ? `"${sequenceTable}"` : sequenceTable;
94
103
 
95
104
  const idColumnQuery = await queryInterface.sequelize.query(
96
105
  `
97
- SELECT true
98
- FROM pg_attribute
99
- WHERE attrelid = '${queryName}'::regclass -- cast to a registered class (table)
100
- AND attname = 'id'
101
- AND NOT attisdropped
102
- `,
106
+ SELECT column_name
107
+ FROM information_schema.columns
108
+ WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schemaName}';
109
+ `,
103
110
  {
104
111
  transaction,
105
112
  },
@@ -110,7 +117,8 @@ AND NOT attisdropped
110
117
  }
111
118
 
112
119
  await queryInterface.sequelize.query(
113
- `alter table "${sequenceTable}" alter column id set default nextval('${maxSequenceName}')`,
120
+ `alter table "${schemaName}"."${sequenceTable}"
121
+ alter column id set default nextval('${maxSequenceName}')`,
114
122
  {
115
123
  transaction,
116
124
  },
@@ -121,7 +129,9 @@ AND NOT attisdropped
121
129
  if (options.alter) {
122
130
  const columns = await queryInterface.describeTable(tableName, options);
123
131
 
124
- for (const columnName in childAttributes) {
132
+ for (const attribute in childAttributes) {
133
+ const columnName = childAttributes[attribute].field;
134
+
125
135
  if (!columns[columnName]) {
126
136
  await queryInterface.addColumn(tableName, columnName, childAttributes[columnName], options);
127
137
  }
@@ -129,7 +139,7 @@ AND NOT attisdropped
129
139
  }
130
140
  }
131
141
 
132
- static async createTable(tableName, attributes, options, model, parentTables) {
142
+ static async createTable(tableName, attributes, options, model, parentTables, db) {
133
143
  let sql = '';
134
144
 
135
145
  options = { ...options };
@@ -154,7 +164,11 @@ AND NOT attisdropped
154
164
 
155
165
  sql = `${queryGenerator.createTableQuery(tableName, attributes, options)}`.replace(
156
166
  ';',
157
- ` INHERITS (${parentTables.map((t) => `"${t}"`).join(', ')});`,
167
+ ` INHERITS (${parentTables
168
+ .map((t) => {
169
+ return db.utils.quoteTable(db.utils.addSchema(t, db.options.schema));
170
+ })
171
+ .join(', ')});`,
158
172
  );
159
173
 
160
174
  return await model.sequelize.query(sql, options);
package/src/types.ts CHANGED
@@ -16,6 +16,10 @@ export type ModelCreateWithAssociationsEventType = 'afterCreateWithAssociations'
16
16
  export type ModelUpdateWithAssociationsEventType = 'afterUpdateWithAssociations';
17
17
  export type ModelSaveWithAssociationsEventType = 'afterSaveWithAssociations';
18
18
 
19
+ export type ModelBulkCreateEvnetType = 'beforeBulkCreate' | 'afterBulkCreate';
20
+ export type ModelBulkUpdateEvnetType = 'beforeBulkUpdate' | 'afterBulkUpdate';
21
+ export type ModelBulkDestroyEvnetType = 'beforeBulkDestroy' | 'afterBulkDestroy';
22
+
19
23
  export type ModelValidateEventTypes = ModelValidateEventType | `${CollectionNameType}.${ModelValidateEventType}`;
20
24
  export type ModelCreateEventTypes = ModelCreateEventType | `${CollectionNameType}.${ModelCreateEventType}`;
21
25
  export type ModelUpdateEventTypes = ModelUpdateEventType | `${CollectionNameType}.${ModelUpdateEventType}`;
@@ -25,6 +29,10 @@ export type ModelCreateWithAssociationsEventTypes = ModelCreateWithAssociationsE
25
29
  export type ModelUpdateWithAssociationsEventTypes = ModelUpdateWithAssociationsEventType | `${CollectionNameType}.${ModelUpdateWithAssociationsEventType}`;
26
30
  export type ModelSaveWithAssociationsEventTypes = ModelSaveWithAssociationsEventType | `${CollectionNameType}.${ModelSaveWithAssociationsEventType}`;
27
31
 
32
+ export type ModelBulkCreateEvnetTypes = ModelBulkCreateEvnetType | `${CollectionNameType}.${ModelBulkCreateEvnetType}`;
33
+ export type ModelBulkUpdateEvnetTypes = ModelBulkUpdateEvnetType | `${CollectionNameType}.${ModelBulkUpdateEvnetType}`;
34
+ export type ModelBulkDestroyEvnetTypes = ModelBulkDestroyEvnetType | `${CollectionNameType}.${ModelBulkDestroyEvnetType}`;
35
+
28
36
  export type ModelEventTypes = ModelSyncEventType
29
37
  | ModelValidateEventTypes
30
38
  | ModelCreateEventTypes
@@ -33,7 +41,10 @@ export type ModelEventTypes = ModelSyncEventType
33
41
  | ModelDestroyEventTypes
34
42
  | ModelCreateWithAssociationsEventTypes
35
43
  | ModelUpdateWithAssociationsEventTypes
36
- | ModelSaveWithAssociationsEventTypes;
44
+ | ModelSaveWithAssociationsEventTypes
45
+ | ModelBulkCreateEvnetTypes
46
+ | ModelBulkUpdateEvnetTypes
47
+ | ModelBulkDestroyEvnetTypes;
37
48
 
38
49
  export type DatabaseBeforeDefineCollectionEventType = 'beforeDefineCollection';
39
50
  export type DatabaseAfterDefineCollectionEventType = 'afterDefineCollection';
@@ -6,7 +6,7 @@ import {
6
6
  HasOne,
7
7
  Hookable,
8
8
  ModelStatic,
9
- Transactionable,
9
+ Transactionable
10
10
  } from 'sequelize';
11
11
  import { Model } from './model';
12
12
  import { UpdateGuard } from './update-guard';
@@ -392,7 +392,7 @@ export async function updateMultipleAssociation(
392
392
  }
393
393
 
394
394
  if (isStringOrNumber(value)) {
395
- await model[setAccessor](value, { transaction, context });
395
+ await model[setAccessor](value, { transaction, context, individualHooks: true });
396
396
  return;
397
397
  }
398
398
 
@@ -402,6 +402,7 @@ export async function updateMultipleAssociation(
402
402
 
403
403
  const list1 = []; // to be setted
404
404
  const list2 = []; // to be added
405
+ const created = [];
405
406
  for (const item of value) {
406
407
  if (isUndefinedOrNull(item)) {
407
408
  continue;
@@ -413,12 +414,17 @@ export async function updateMultipleAssociation(
413
414
  } else if (item.sequelize) {
414
415
  list1.push(item);
415
416
  } else if (typeof item === 'object') {
417
+ const targetKey = (association as any).targetKey || 'id';
418
+ if (item[targetKey]) {
419
+ created.push(item[targetKey]);
420
+ list1.push(item[targetKey]);
421
+ }
416
422
  list2.push(item);
417
423
  }
418
424
  }
419
425
 
420
426
  // associate targets in lists1
421
- await model[setAccessor](list1, { transaction, context });
427
+ await model[setAccessor](list1, { transaction, context, individualHooks: true });
422
428
 
423
429
  const list3 = [];
424
430
  for (const item of list2) {
@@ -456,8 +462,9 @@ export async function updateMultipleAssociation(
456
462
  continue;
457
463
  }
458
464
  const addAccessor = association.accessors.add;
459
-
460
- await model[addAccessor](item[pk], accessorOptions);
465
+ if (!created.includes(item[pk])) {
466
+ await model[addAccessor](item[pk], accessorOptions);
467
+ }
461
468
  if (!recursive) {
462
469
  continue;
463
470
  }
@@ -13,6 +13,7 @@ type UpdateAction = 'create' | 'update';
13
13
  export class UpdateGuard {
14
14
  model: ModelStatic<any>;
15
15
  action: UpdateAction;
16
+ underscored: boolean;
16
17
  private associationKeysToBeUpdate: AssociationKeysToBeUpdate;
17
18
  private blackList: BlackList;
18
19
  private whiteList: WhiteList;
@@ -162,6 +163,11 @@ export class UpdateGuard {
162
163
  guard.setBlackList(options.blacklist);
163
164
  guard.setAction(lodash.get(options, 'action', 'update'));
164
165
  guard.setAssociationKeysToBeUpdate(options.updateAssociationValues);
166
+
167
+ if (options.underscored) {
168
+ guard.underscored = options.underscored;
169
+ }
170
+
165
171
  return guard;
166
172
  }
167
173
  }