@strapi/database 4.2.1-alpha.0 → 4.3.0-beta.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.
@@ -26,19 +26,32 @@ const SQL_QUERIES = {
26
26
  `,
27
27
  FOREIGN_KEY_LIST: /* sql */ `
28
28
  SELECT
29
- tc.constraint_name as constraint_name,
29
+ tc.constraint_name as constraint_name
30
+ FROM information_schema.table_constraints tc
31
+ WHERE tc.constraint_type = 'FOREIGN KEY'
32
+ AND tc.table_schema = database()
33
+ AND tc.table_name = ?;
34
+ `,
35
+ FOREIGN_KEY_REFERENCES: /* sql */ `
36
+ SELECT
37
+ kcu.constraint_name as constraint_name,
30
38
  kcu.column_name as column_name,
31
39
  kcu.referenced_table_name as referenced_table_name,
32
- kcu.referenced_column_name as referenced_column_name,
40
+ kcu.referenced_column_name as referenced_column_name
41
+ FROM information_schema.key_column_usage kcu
42
+ WHERE kcu.constraint_name in (?)
43
+ AND kcu.table_schema = database()
44
+ AND kcu.table_name = ?;
45
+ `,
46
+ FOREIGN_KEY_REFERENTIALS_CONSTRAINTS: /* sql */ `
47
+ SELECT
48
+ rc.constraint_name as constraint_name,
33
49
  rc.update_rule as on_update,
34
50
  rc.delete_rule as on_delete
35
- FROM information_schema.table_constraints tc
36
- INNER JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name
37
- INNER JOIN information_schema.referential_constraints AS rc ON kcu.constraint_name = rc.constraint_name
38
- WHERE constraint_type = 'FOREIGN KEY'
39
- AND tc.table_schema = database()
40
- AND tc.table_name = ?;
41
-
51
+ FROM information_schema.referential_constraints AS rc
52
+ WHERE rc.constraint_name in (?)
53
+ AND rc.constraint_schema = database()
54
+ AND rc.table_name = ?;
42
55
  `,
43
56
  };
44
57
 
@@ -177,18 +190,44 @@ class MysqlSchemaInspector {
177
190
  const ret = {};
178
191
 
179
192
  for (const fk of rows) {
180
- if (!ret[fk.constraint_name]) {
181
- ret[fk.constraint_name] = {
182
- name: fk.constraint_name,
183
- columns: [fk.column_name],
184
- referencedColumns: [fk.referenced_column_name],
185
- referencedTable: fk.referenced_table_name,
186
- onUpdate: fk.on_update.toUpperCase(),
187
- onDelete: fk.on_delete.toUpperCase(),
188
- };
189
- } else {
190
- ret[fk.constraint_name].columns.push(fk.column_name);
191
- ret[fk.constraint_name].referencedColumns.push(fk.referenced_column_name);
193
+ ret[fk.constraint_name] = {
194
+ name: fk.constraint_name,
195
+ columns: [],
196
+ referencedColumns: [],
197
+ referencedTable: null,
198
+ onUpdate: null,
199
+ onDelete: null,
200
+ };
201
+ }
202
+
203
+ const contraintNames = Object.keys(ret);
204
+
205
+ if (contraintNames.length > 0) {
206
+ const [fkReferences] = await this.db.connection.raw(SQL_QUERIES.FOREIGN_KEY_REFERENCES, [
207
+ contraintNames,
208
+ tableName,
209
+ ]);
210
+
211
+ for (const fkReference of fkReferences) {
212
+ ret[fkReference.constraint_name].referencedTable = fkReference.referenced_table_name;
213
+ ret[fkReference.constraint_name].columns.push(fkReference.column_name);
214
+ ret[fkReference.constraint_name].referencedColumns.push(fkReference.referenced_column_name);
215
+ }
216
+
217
+ const [
218
+ fkReferentialConstraints,
219
+ ] = await this.db.connection.raw(SQL_QUERIES.FOREIGN_KEY_REFERENTIALS_CONSTRAINTS, [
220
+ contraintNames,
221
+ tableName,
222
+ ]);
223
+
224
+ for (const fkReferentialConstraint of fkReferentialConstraints) {
225
+ ret[
226
+ fkReferentialConstraint.constraint_name
227
+ ].onUpdate = fkReferentialConstraint.on_update.toUpperCase();
228
+ ret[
229
+ fkReferentialConstraint.constraint_name
230
+ ].onDelete = fkReferentialConstraint.on_delete.toUpperCase();
192
231
  }
193
232
  }
194
233
 
@@ -40,29 +40,41 @@ const SQL_QUERIES = {
40
40
  `,
41
41
  FOREIGN_KEY_LIST: /* sql */ `
42
42
  SELECT
43
- tco."constraint_name" as constraint_name,
44
- kcu."column_name" as column_name,
45
- rel_kcu."table_name" as foreign_table,
46
- rel_kcu."column_name" as fk_column_name,
47
- rco.update_rule as on_update,
48
- rco.delete_rule as on_delete
43
+ tco."constraint_name" as constraint_name
49
44
  FROM information_schema.table_constraints tco
50
- JOIN information_schema.key_column_usage kcu
51
- ON tco.constraint_schema = kcu.constraint_schema
52
- AND tco.constraint_name = kcu.constraint_name
53
- JOIN information_schema.referential_constraints rco
54
- ON tco.constraint_schema = rco.constraint_schema
55
- AND tco.constraint_name = rco.constraint_name
56
- JOIN information_schema.key_column_usage rel_kcu
57
- ON rco.unique_constraint_schema = rel_kcu.constraint_schema
58
- AND rco.unique_constraint_name = rel_kcu.constraint_name
59
- AND kcu.ordinal_position = rel_kcu.ordinal_position
60
45
  WHERE
61
46
  tco.constraint_type = 'FOREIGN KEY'
62
47
  AND tco.constraint_schema = ?
63
48
  AND tco.table_name = ?
64
- ORDER BY kcu.table_schema, kcu.table_name, kcu.ordinal_position, kcu.constraint_name;
65
49
  `,
50
+ FOREIGN_KEY_REFERENCES: /* sql */ `
51
+ SELECT
52
+ kcu."constraint_name" as constraint_name,
53
+ kcu."column_name" as column_name
54
+
55
+ FROM information_schema.key_column_usage kcu
56
+ WHERE kcu.constraint_name=ANY(?)
57
+ AND kcu.table_schema = ?
58
+ AND kcu.table_name = ?;
59
+ `,
60
+
61
+ FOREIGN_KEY_REFERENCES_CONSTRAIN: /* sql */ `
62
+ SELECT
63
+ rco.update_rule as on_update,
64
+ rco.delete_rule as on_delete,
65
+ rco."unique_constraint_name" as unique_constraint_name
66
+ FROM information_schema.referential_constraints rco
67
+ WHERE rco.constraint_name=ANY(?)
68
+ AND rco.constraint_schema = ?
69
+ `,
70
+ FOREIGN_KEY_REFERENCES_CONSTRAIN_RFERENCE: /* sql */ `
71
+ SELECT
72
+ rel_kcu."table_name" as foreign_table,
73
+ rel_kcu."column_name" as fk_column_name
74
+ FROM information_schema.key_column_usage rel_kcu
75
+ WHERE rel_kcu.constraint_name=?
76
+ AND rel_kcu.table_schema = ?
77
+ `,
66
78
  };
67
79
 
68
80
  const toStrapiType = column => {
@@ -210,18 +222,52 @@ class PostgresqlSchemaInspector {
210
222
  const ret = {};
211
223
 
212
224
  for (const fk of rows) {
213
- if (!ret[fk.constraint_name]) {
214
- ret[fk.constraint_name] = {
215
- name: fk.constraint_name,
216
- columns: [fk.column_name],
217
- referencedColumns: [fk.fk_column_name],
218
- referencedTable: fk.foreign_table,
219
- onUpdate: fk.on_update.toUpperCase(),
220
- onDelete: fk.on_delete.toUpperCase(),
221
- };
222
- } else {
223
- ret[fk.constraint_name].columns.push(fk.column_name);
224
- ret[fk.constraint_name].referencedColumns.push(fk.fk_column_name);
225
+ ret[fk.constraint_name] = {
226
+ name: fk.constraint_name,
227
+ columns: [],
228
+ referencedColumns: [],
229
+ referencedTable: null,
230
+ onUpdate: null,
231
+ onDelete: null,
232
+ };
233
+ }
234
+ const constraintNames = Object.keys(ret);
235
+ const dbSchema = this.getDatabaseSchema();
236
+ if (constraintNames.length > 0) {
237
+ const {
238
+ rows: fkReferences,
239
+ } = await this.db.connection.raw(SQL_QUERIES.FOREIGN_KEY_REFERENCES, [
240
+ [constraintNames],
241
+ dbSchema,
242
+ tableName,
243
+ ]);
244
+
245
+ for (const fkReference of fkReferences) {
246
+ ret[fkReference.constraint_name].columns.push(fkReference.column_name);
247
+
248
+ const {
249
+ rows: fkReferencesConstraint,
250
+ } = await this.db.connection.raw(SQL_QUERIES.FOREIGN_KEY_REFERENCES_CONSTRAIN, [
251
+ [fkReference.constraint_name],
252
+ dbSchema,
253
+ ]);
254
+
255
+ for (const fkReferenceC of fkReferencesConstraint) {
256
+ const {
257
+ rows: fkReferencesConstraintReferece,
258
+ } = await this.db.connection.raw(SQL_QUERIES.FOREIGN_KEY_REFERENCES_CONSTRAIN_RFERENCE, [
259
+ fkReferenceC.unique_constraint_name,
260
+ dbSchema,
261
+ ]);
262
+ for (const fkReferenceConst of fkReferencesConstraintReferece) {
263
+ ret[fkReference.constraint_name].referencedTable = fkReferenceConst.foreign_table;
264
+ ret[fkReference.constraint_name].referencedColumns.push(
265
+ fkReferenceConst.fk_column_name
266
+ );
267
+ }
268
+ ret[fkReference.constraint_name].onUpdate = fkReferenceC.on_update.toUpperCase();
269
+ ret[fkReference.constraint_name].onDelete = fkReferenceC.on_delete.toUpperCase();
270
+ }
225
271
  }
226
272
  }
227
273
 
@@ -617,7 +617,7 @@ const createEntityManager = db => {
617
617
  }
618
618
 
619
619
  if (attribute.joinColumn && attribute.owner) {
620
- // handled in the row itslef
620
+ // handled in the row itself
621
621
  continue;
622
622
  }
623
623
 
package/lib/index.d.ts CHANGED
@@ -2,16 +2,37 @@ import { LifecycleProvider } from './lifecycles';
2
2
  import { MigrationProvider } from './migrations';
3
3
  import { SchemaProvideer } from './schema';
4
4
 
5
- type BooleanWhere<T> = {
5
+ type LogicalOperators<T> = {
6
6
  $and?: WhereParams<T>[];
7
7
  $or?: WhereParams<T>[];
8
8
  $not?: WhereParams<T>;
9
9
  };
10
10
 
11
- type WhereParams<T> = {
12
- [K in keyof T]?: T[K];
11
+ type AttributeOperators<T, K extends keyof T> = {
12
+ $eq?: T[K] | Array<T[K]>;
13
+ $ne?: T[K] | Array<T[K]>;
14
+ $in?: T[K][];
15
+ $notIn?: T[K][];
16
+ $lt?: T[K];
17
+ $lte?: T[K];
18
+ $gt?: T[K];
19
+ $gte?: T[K];
20
+ $between?: [T[K], T[K]];
21
+ $contains?: T[K];
22
+ $notContains?: T[K];
23
+ $containsi?: T[K];
24
+ $notContainsi?: T[K];
25
+ $startsWith?: T[K];
26
+ $endsWith?: T[K];
27
+ $null?: boolean;
28
+ $notNull?: boolean;
29
+ $not?: WhereParams<T> | AttributeOperators<T, K>;
30
+ };
31
+
32
+ export type WhereParams<T> = {
33
+ [K in keyof T]?: T[K] | T[K][] | AttributeOperators<T, K>;
13
34
  } &
14
- BooleanWhere<T>;
35
+ LogicalOperators<T>;
15
36
 
16
37
  type Sortables<T> = {
17
38
  // check sortable
package/lib/index.js CHANGED
@@ -58,6 +58,10 @@ class Database {
58
58
  return schema ? trx.schema.withSchema(schema) : trx.schema;
59
59
  }
60
60
 
61
+ transaction() {
62
+ return this.connection.transaction();
63
+ }
64
+
61
65
  queryBuilder(uid) {
62
66
  return this.entityManager.createQueryBuilder(uid);
63
67
  }
@@ -33,6 +33,7 @@ const createMetadata = (models = []) => {
33
33
  ...model.attributes,
34
34
  },
35
35
  lifecycles: model.lifecycles || {},
36
+ indexes: model.indexes || [],
36
37
  });
37
38
  }
38
39
 
@@ -34,7 +34,7 @@ const migrationResolver = ({ name, path, context }) => {
34
34
  };
35
35
 
36
36
  const createUmzugProvider = db => {
37
- const migrationDir = path.join(strapi.dirs.root, 'database/migrations');
37
+ const migrationDir = path.join(strapi.dirs.app.root, 'database/migrations');
38
38
 
39
39
  fse.ensureDirSync(migrationDir);
40
40
 
@@ -12,6 +12,7 @@ const createQueryBuilder = (uid, db) => {
12
12
  type: 'select',
13
13
  select: [],
14
14
  count: null,
15
+ max: null,
15
16
  first: false,
16
17
  data: null,
17
18
  where: [],
@@ -19,6 +20,8 @@ const createQueryBuilder = (uid, db) => {
19
20
  populate: null,
20
21
  limit: null,
21
22
  offset: null,
23
+ transaction: null,
24
+ forUpdate: false,
22
25
  orderBy: [],
23
26
  groupBy: [],
24
27
  };
@@ -75,6 +78,13 @@ const createQueryBuilder = (uid, db) => {
75
78
  return this;
76
79
  },
77
80
 
81
+ max(column) {
82
+ state.type = 'max';
83
+ state.max = column;
84
+
85
+ return this;
86
+ },
87
+
78
88
  where(where = {}) {
79
89
  if (!_.isPlainObject(where)) {
80
90
  throw new Error('Where must be an object');
@@ -115,6 +125,16 @@ const createQueryBuilder = (uid, db) => {
115
125
  return this;
116
126
  },
117
127
 
128
+ transacting(transaction) {
129
+ state.transaction = transaction;
130
+ return this;
131
+ },
132
+
133
+ forUpdate() {
134
+ state.forUpdate = true;
135
+ return this;
136
+ },
137
+
118
138
  init(params = {}) {
119
139
  const { _q, filters, where, select, limit, offset, orderBy, groupBy, populate } = params;
120
140
 
@@ -281,7 +301,15 @@ const createQueryBuilder = (uid, db) => {
281
301
  break;
282
302
  }
283
303
  case 'count': {
284
- qb.count({ count: state.count });
304
+ const dbColumnName =
305
+ state.count === '*' ? '*' : this.aliasColumn(helpers.toColumnName(meta, state.count));
306
+
307
+ qb.count({ count: dbColumnName });
308
+ break;
309
+ }
310
+ case 'max': {
311
+ const dbColumnName = this.aliasColumn(helpers.toColumnName(meta, state.max));
312
+ qb.max({ max: dbColumnName });
285
313
  break;
286
314
  }
287
315
  case 'insert': {
@@ -308,6 +336,14 @@ const createQueryBuilder = (uid, db) => {
308
336
  }
309
337
  }
310
338
 
339
+ if (state.transaction) {
340
+ qb.transacting(state.transaction);
341
+ }
342
+
343
+ if (state.forUpdate) {
344
+ qb.forUpdate();
345
+ }
346
+
311
347
  if (state.limit) {
312
348
  qb.limit(state.limit);
313
349
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/database",
3
- "version": "4.2.1-alpha.0",
3
+ "version": "4.3.0-beta.2",
4
4
  "description": "Strapi's database layer",
5
5
  "homepage": "https://strapi.io",
6
6
  "bugs": {
@@ -31,7 +31,7 @@
31
31
  "test:unit": "jest --verbose"
32
32
  },
33
33
  "dependencies": {
34
- "date-fns": "2.22.1",
34
+ "date-fns": "2.28.0",
35
35
  "debug": "4.3.1",
36
36
  "fs-extra": "10.0.0",
37
37
  "knex": "1.0.4",
@@ -42,5 +42,5 @@
42
42
  "node": ">=14.19.1 <=16.x.x",
43
43
  "npm": ">=6.0.0"
44
44
  },
45
- "gitHead": "90bd3b9e294a69cd9538bde4e2e853e6de60f238"
45
+ "gitHead": "42aba356ad1b0751584d3b375e83baa4a2c18f65"
46
46
  }