@strapi/database 5.0.0-beta.8 → 5.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7,11 +7,11 @@ const _ = require("lodash/fp");
7
7
  const crypto = require("crypto");
8
8
  const crypto$1 = require("node:crypto");
9
9
  const dateFns = require("date-fns");
10
- const utils = require("@strapi/utils");
10
+ const node_async_hooks = require("node:async_hooks");
11
11
  const KnexBuilder = require("knex/lib/query/querybuilder");
12
12
  const KnexRaw = require("knex/lib/raw");
13
+ const utils = require("@strapi/utils");
13
14
  const stream = require("stream");
14
- const node_async_hooks = require("node:async_hooks");
15
15
  const _$1 = require("lodash");
16
16
  const umzug = require("umzug");
17
17
  const cuid2 = require("@paralleldrive/cuid2");
@@ -1119,7 +1119,7 @@ const createHelpers = (db) => {
1119
1119
  }
1120
1120
  }
1121
1121
  };
1122
- const dropIndex = (tableBuilder, index2) => {
1122
+ const dropIndex2 = (tableBuilder, index2) => {
1123
1123
  if (!db.config.settings?.forceMigration) {
1124
1124
  return;
1125
1125
  }
@@ -1196,13 +1196,13 @@ const createHelpers = (db) => {
1196
1196
  for (const removedIndex of table.indexes.removed) {
1197
1197
  if (!ignoreForeignKeyNames.includes(removedIndex.name)) {
1198
1198
  debug$2(`Dropping index ${removedIndex.name} on ${table.name}`);
1199
- dropIndex(tableBuilder, removedIndex);
1199
+ dropIndex2(tableBuilder, removedIndex);
1200
1200
  }
1201
1201
  }
1202
1202
  for (const updatedIndex of table.indexes.updated) {
1203
1203
  if (!ignoreForeignKeyNames.includes(updatedIndex.name)) {
1204
1204
  debug$2(`Dropping updated index ${updatedIndex.name} on ${table.name}`);
1205
- dropIndex(tableBuilder, updatedIndex.object);
1205
+ dropIndex2(tableBuilder, updatedIndex.object);
1206
1206
  }
1207
1207
  }
1208
1208
  for (const updatedColumn of table.columns.updated) {
@@ -2927,6 +2927,68 @@ const createField = (attribute) => {
2927
2927
  }
2928
2928
  throw new Error(`Undefined field for type ${type}`);
2929
2929
  };
2930
+ const storage = new node_async_hooks.AsyncLocalStorage();
2931
+ const transactionCtx = {
2932
+ async run(trx, cb) {
2933
+ const store = storage.getStore();
2934
+ return storage.run(
2935
+ {
2936
+ trx,
2937
+ // Fill with existing callbacks if nesting transactions
2938
+ commitCallbacks: store?.commitCallbacks || [],
2939
+ rollbackCallbacks: store?.rollbackCallbacks || []
2940
+ },
2941
+ cb
2942
+ );
2943
+ },
2944
+ get() {
2945
+ const store = storage.getStore();
2946
+ return store?.trx;
2947
+ },
2948
+ async commit(trx) {
2949
+ const store = storage.getStore();
2950
+ if (store?.trx) {
2951
+ store.trx = null;
2952
+ }
2953
+ await trx.commit();
2954
+ if (!store?.commitCallbacks.length) {
2955
+ return;
2956
+ }
2957
+ store.commitCallbacks.forEach((cb) => cb());
2958
+ store.commitCallbacks = [];
2959
+ },
2960
+ async rollback(trx) {
2961
+ const store = storage.getStore();
2962
+ if (store?.trx) {
2963
+ store.trx = null;
2964
+ }
2965
+ await trx.rollback();
2966
+ if (!store?.rollbackCallbacks.length) {
2967
+ return;
2968
+ }
2969
+ store.rollbackCallbacks.forEach((cb) => cb());
2970
+ store.rollbackCallbacks = [];
2971
+ },
2972
+ onCommit(cb) {
2973
+ const store = storage.getStore();
2974
+ if (store?.commitCallbacks) {
2975
+ store.commitCallbacks.push(cb);
2976
+ }
2977
+ },
2978
+ onRollback(cb) {
2979
+ const store = storage.getStore();
2980
+ if (store?.rollbackCallbacks) {
2981
+ store.rollbackCallbacks.push(cb);
2982
+ }
2983
+ }
2984
+ };
2985
+ function isKnexQuery(value) {
2986
+ return value instanceof KnexBuilder__default.default || value instanceof KnexRaw__default.default;
2987
+ }
2988
+ const addSchema = (db, tableName) => {
2989
+ const schemaName = db.getSchemaName();
2990
+ return schemaName ? `${schemaName}.${tableName}` : tableName;
2991
+ };
2930
2992
  const fromSingleRow = (meta, row) => {
2931
2993
  const { attributes } = meta;
2932
2994
  if (___default.default.isNil(row)) {
@@ -3049,7 +3111,7 @@ const escapeQuery = (query, charsToEscape, escapeChar = "\\") => {
3049
3111
  ""
3050
3112
  );
3051
3113
  };
3052
- const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
3114
+ const createPivotJoin = (ctx, { alias: alias2, refAlias, joinTable, targetMeta }) => {
3053
3115
  const { qb } = ctx;
3054
3116
  const joinAlias = qb.getAlias();
3055
3117
  qb.join({
@@ -3057,7 +3119,7 @@ const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
3057
3119
  referencedTable: joinTable.name,
3058
3120
  referencedColumn: joinTable.joinColumn.name,
3059
3121
  rootColumn: joinTable.joinColumn.referencedColumn,
3060
- rootTable: alias,
3122
+ rootTable: alias2,
3061
3123
  on: joinTable.on
3062
3124
  });
3063
3125
  const subAlias = refAlias || qb.getAlias();
@@ -3070,7 +3132,7 @@ const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
3070
3132
  });
3071
3133
  return subAlias;
3072
3134
  };
3073
- const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3135
+ const createJoin = (ctx, { alias: alias2, refAlias, attributeName, attribute }) => {
3074
3136
  const { db, qb, uid } = ctx;
3075
3137
  if (attribute.type !== "relation") {
3076
3138
  throw new Error(`Cannot join on non relational field ${attributeName}`);
@@ -3086,7 +3148,7 @@ const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3086
3148
  referencedTable: targetMeta.tableName,
3087
3149
  referencedColumn: morphColumn.idColumn.name,
3088
3150
  rootColumn: morphColumn.idColumn.referencedColumn,
3089
- rootTable: alias,
3151
+ rootTable: alias2,
3090
3152
  on: {
3091
3153
  [morphColumn.typeColumn.name]: uid,
3092
3154
  ...morphColumn.on
@@ -3101,7 +3163,7 @@ const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3101
3163
  referencedTable: joinTable2.name,
3102
3164
  referencedColumn: joinTable2.morphColumn.idColumn.name,
3103
3165
  rootColumn: joinTable2.morphColumn.idColumn.referencedColumn,
3104
- rootTable: alias,
3166
+ rootTable: alias2,
3105
3167
  on: {
3106
3168
  [joinTable2.morphColumn.typeColumn.name]: uid,
3107
3169
  field: attributeName
@@ -3117,7 +3179,7 @@ const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3117
3179
  });
3118
3180
  return subAlias;
3119
3181
  }
3120
- return alias;
3182
+ return alias2;
3121
3183
  }
3122
3184
  const { joinColumn } = attribute;
3123
3185
  if (joinColumn) {
@@ -3127,20 +3189,20 @@ const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3127
3189
  referencedTable: targetMeta.tableName,
3128
3190
  referencedColumn: joinColumn.referencedColumn,
3129
3191
  rootColumn: joinColumn.name,
3130
- rootTable: alias
3192
+ rootTable: alias2
3131
3193
  });
3132
3194
  return subAlias;
3133
3195
  }
3134
3196
  const { joinTable } = attribute;
3135
3197
  if (joinTable) {
3136
- return createPivotJoin(ctx, { alias, refAlias, joinTable, targetMeta });
3198
+ return createPivotJoin(ctx, { alias: alias2, refAlias, joinTable, targetMeta });
3137
3199
  }
3138
- return alias;
3200
+ return alias2;
3139
3201
  };
3140
3202
  const applyJoin = (qb, join) => {
3141
3203
  const {
3142
3204
  method = "leftJoin",
3143
- alias,
3205
+ alias: alias2,
3144
3206
  referencedTable,
3145
3207
  referencedColumn,
3146
3208
  rootColumn,
@@ -3150,26 +3212,28 @@ const applyJoin = (qb, join) => {
3150
3212
  on,
3151
3213
  orderBy
3152
3214
  } = join;
3153
- qb[method](`${referencedTable} as ${alias}`, (inner) => {
3154
- inner.on(`${rootTable}.${rootColumn}`, `${alias}.${referencedColumn}`);
3215
+ qb[method](`${referencedTable} as ${alias2}`, (inner) => {
3216
+ inner.on(`${rootTable}.${rootColumn}`, `${alias2}.${referencedColumn}`);
3155
3217
  if (on) {
3156
3218
  for (const key of Object.keys(on)) {
3157
- inner.onVal(`${alias}.${key}`, on[key]);
3219
+ inner.onVal(`${alias2}.${key}`, on[key]);
3158
3220
  }
3159
3221
  }
3160
3222
  });
3161
3223
  if (orderBy) {
3162
3224
  Object.keys(orderBy).forEach((column) => {
3163
3225
  const direction = orderBy[column];
3164
- qb.orderBy(`${alias}.${column}`, direction);
3226
+ qb.orderBy(`${alias2}.${column}`, direction);
3165
3227
  });
3166
3228
  }
3167
3229
  };
3168
3230
  const applyJoins = (qb, joins) => {
3169
3231
  return joins.forEach((join) => applyJoin(qb, join));
3170
3232
  };
3233
+ const COL_STRAPI_ROW_NUMBER = "__strapi_row_number";
3234
+ const COL_STRAPI_ORDER_BY_PREFIX = "__strapi_order_by";
3171
3235
  const processOrderBy = (orderBy, ctx) => {
3172
- const { db, uid, qb, alias } = ctx;
3236
+ const { db, uid, qb, alias: alias2 } = ctx;
3173
3237
  const meta = db.metadata.get(uid);
3174
3238
  const { attributes } = meta;
3175
3239
  if (typeof orderBy === "string") {
@@ -3178,7 +3242,7 @@ const processOrderBy = (orderBy, ctx) => {
3178
3242
  throw new Error(`Attribute ${orderBy} not found on model ${uid}`);
3179
3243
  }
3180
3244
  const columnName = toColumnName(meta, orderBy);
3181
- return [{ column: qb.aliasColumn(columnName, alias) }];
3245
+ return [{ column: qb.aliasColumn(columnName, alias2) }];
3182
3246
  }
3183
3247
  if (Array.isArray(orderBy)) {
3184
3248
  return orderBy.flatMap((value) => processOrderBy(value, ctx));
@@ -3192,11 +3256,11 @@ const processOrderBy = (orderBy, ctx) => {
3192
3256
  }
3193
3257
  if (isScalar(attribute.type)) {
3194
3258
  const columnName = toColumnName(meta, key);
3195
- return { column: qb.aliasColumn(columnName, alias), order: direction };
3259
+ return { column: qb.aliasColumn(columnName, alias2), order: direction };
3196
3260
  }
3197
3261
  if (attribute.type === "relation" && "target" in attribute) {
3198
3262
  const subAlias = createJoin(ctx, {
3199
- alias: alias || qb.alias,
3263
+ alias: alias2 || qb.alias,
3200
3264
  attributeName: key,
3201
3265
  attribute
3202
3266
  });
@@ -3212,6 +3276,76 @@ const processOrderBy = (orderBy, ctx) => {
3212
3276
  }
3213
3277
  throw new Error("Invalid orderBy syntax");
3214
3278
  };
3279
+ const getStrapiOrderColumnAlias = (column) => {
3280
+ const trimmedColumnName = column.replaceAll(".", "_");
3281
+ return `${COL_STRAPI_ORDER_BY_PREFIX}__${trimmedColumnName}`;
3282
+ };
3283
+ const wrapWithDeepSort = (originalQuery, ctx) => {
3284
+ const { db, qb, uid } = ctx;
3285
+ const { tableName } = db.metadata.get(uid);
3286
+ const orderBy = ___default.default.cloneDeep(qb.state.orderBy);
3287
+ const resultQueryAlias = qb.getAlias();
3288
+ const aliasedTableName = qb.mustUseAlias() ? alias(resultQueryAlias, tableName) : tableName;
3289
+ const resultQuery = db.getConnection(aliasedTableName);
3290
+ const baseQuery = originalQuery.clone();
3291
+ const baseQueryAlias = qb.getAlias();
3292
+ baseQuery.clear("select").clear("order").clear("limit").clear("offset");
3293
+ baseQuery.select(
3294
+ // Always select the row id for future manipulation
3295
+ prefix(qb.alias, "id"),
3296
+ ...orderBy.map(
3297
+ (orderByClause) => alias(getStrapiOrderColumnAlias(orderByClause.column), orderByClause.column)
3298
+ )
3299
+ );
3300
+ const partitionedQueryAlias = qb.getAlias();
3301
+ const selectRowsAsNumberedPartitions = (partitionedQuery) => {
3302
+ const prefixedOrderBy = orderBy.map((orderByClause) => ({
3303
+ column: prefix(baseQueryAlias, getStrapiOrderColumnAlias(orderByClause.column)),
3304
+ order: orderByClause.order
3305
+ }));
3306
+ const orderByColumns = prefixedOrderBy.map(___default.default.prop("column"));
3307
+ partitionedQuery.select(
3308
+ // Always select baseQuery.id
3309
+ prefix(baseQueryAlias, "id"),
3310
+ ...orderByColumns
3311
+ ).rowNumber(COL_STRAPI_ROW_NUMBER, (subQuery) => {
3312
+ for (const orderByClause of prefixedOrderBy) {
3313
+ subQuery.orderBy(orderByClause.column, orderByClause.order, "last");
3314
+ }
3315
+ subQuery.partitionBy(`${baseQueryAlias}.id`);
3316
+ }).from(baseQuery.as(baseQueryAlias)).as(partitionedQueryAlias);
3317
+ };
3318
+ const originalSelect = ___default.default.difference(
3319
+ qb.state.select,
3320
+ // Remove order by columns from the initial select
3321
+ qb.state.orderBy.map(___default.default.prop("column"))
3322
+ ).map(prefix(resultQueryAlias));
3323
+ resultQuery.select(originalSelect).innerJoin(selectRowsAsNumberedPartitions, function() {
3324
+ this.on(`${partitionedQueryAlias}.id`, `${resultQueryAlias}.id`).andOnVal(`${partitionedQueryAlias}.${COL_STRAPI_ROW_NUMBER}`, "=", 1);
3325
+ });
3326
+ if (qb.state.limit) {
3327
+ resultQuery.limit(qb.state.limit);
3328
+ }
3329
+ if (qb.state.offset) {
3330
+ resultQuery.offset(qb.state.offset);
3331
+ }
3332
+ if (qb.state.first) {
3333
+ resultQuery.first();
3334
+ }
3335
+ resultQuery.orderBy([
3336
+ // Transform "order by" clause to their T alias and prefix them with T alias
3337
+ ...orderBy.map((orderByClause) => ({
3338
+ column: prefix(partitionedQueryAlias, getStrapiOrderColumnAlias(orderByClause.column)),
3339
+ order: orderByClause.order
3340
+ })),
3341
+ // Add T.id to the order by clause to get consistent results in case several rows have the exact same order
3342
+ { column: `${partitionedQueryAlias}.id`, order: "asc" }
3343
+ ]);
3344
+ return resultQuery;
3345
+ };
3346
+ const alias = ___default.default.curry((alias2, value) => `${value} as ${alias2}`);
3347
+ const prefix = ___default.default.curry((prefix2, value) => `${prefix2}.${value}`);
3348
+ const joinColPrefix = "__strapi";
3215
3349
  const XtoOne = async (input, ctx) => {
3216
3350
  const { attribute, attributeName, results, populateValue, targetMeta, isCount } = input;
3217
3351
  const { db, qb } = ctx;
@@ -3238,8 +3372,10 @@ const XtoOne = async (input, ctx) => {
3238
3372
  const { joinTable } = attribute;
3239
3373
  const qb2 = db.entityManager.createQueryBuilder(targetMeta.uid);
3240
3374
  const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
3241
- const alias = qb2.getAlias();
3242
- const joinColAlias = `${alias}.${joinColumnName}`;
3375
+ const alias2 = qb2.getAlias();
3376
+ const joinColAlias = `${alias2}.${joinColumnName}`;
3377
+ const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
3378
+ const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
3243
3379
  const referencedValues = ___default.default.uniq(
3244
3380
  results.map((r) => r[referencedColumnName]).filter((value) => !___default.default.isNil(value))
3245
3381
  );
@@ -3251,7 +3387,7 @@ const XtoOne = async (input, ctx) => {
3251
3387
  return;
3252
3388
  }
3253
3389
  const rows2 = await qb2.init(populateValue).join({
3254
- alias,
3390
+ alias: alias2,
3255
3391
  referencedTable: joinTable.name,
3256
3392
  referencedColumn: joinTable.inverseJoinColumn.name,
3257
3393
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3277,15 +3413,15 @@ const XtoOne = async (input, ctx) => {
3277
3413
  return;
3278
3414
  }
3279
3415
  const rows = await qb2.init(populateValue).join({
3280
- alias,
3416
+ alias: alias2,
3281
3417
  referencedTable: joinTable.name,
3282
3418
  referencedColumn: joinTable.inverseJoinColumn.name,
3283
3419
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
3284
3420
  rootTable: qb2.alias,
3285
3421
  on: joinTable.on,
3286
3422
  orderBy: joinTable.orderBy
3287
- }).addSelect(joinColAlias).where({ [joinColAlias]: referencedValues }).execute({ mapResults: false });
3288
- const map = ___default.default.groupBy(joinColumnName)(rows);
3423
+ }).addSelect(joinColSelect).where({ [joinColAlias]: referencedValues }).execute({ mapResults: false });
3424
+ const map = ___default.default.groupBy(joinColRenameAs)(rows);
3289
3425
  results.forEach((result) => {
3290
3426
  result[attributeName] = fromTargetRow(___default.default.first(map[result[referencedColumnName]]));
3291
3427
  });
@@ -3317,8 +3453,10 @@ const oneToMany = async (input, ctx) => {
3317
3453
  const { joinTable } = attribute;
3318
3454
  const qb2 = db.entityManager.createQueryBuilder(targetMeta.uid);
3319
3455
  const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
3320
- const alias = qb2.getAlias();
3321
- const joinColAlias = `${alias}.${joinColumnName}`;
3456
+ const alias2 = qb2.getAlias();
3457
+ const joinColAlias = `${alias2}.${joinColumnName}`;
3458
+ const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
3459
+ const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
3322
3460
  const referencedValues = ___default.default.uniq(
3323
3461
  results.map((r) => r[referencedColumnName]).filter((value) => !___default.default.isNil(value))
3324
3462
  );
@@ -3330,16 +3468,16 @@ const oneToMany = async (input, ctx) => {
3330
3468
  return;
3331
3469
  }
3332
3470
  const rows2 = await qb2.init(populateValue).join({
3333
- alias,
3471
+ alias: alias2,
3334
3472
  referencedTable: joinTable.name,
3335
3473
  referencedColumn: joinTable.inverseJoinColumn.name,
3336
3474
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
3337
3475
  rootTable: qb2.alias,
3338
3476
  on: joinTable.on
3339
- }).select([joinColAlias, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
3477
+ }).select([joinColSelect, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
3340
3478
  const map2 = rows2.reduce(
3341
3479
  (map3, row) => {
3342
- map3[row[joinColumnName]] = { count: Number(row.count) };
3480
+ map3[row[joinColRenameAs]] = { count: Number(row.count) };
3343
3481
  return map3;
3344
3482
  },
3345
3483
  {}
@@ -3356,15 +3494,15 @@ const oneToMany = async (input, ctx) => {
3356
3494
  return;
3357
3495
  }
3358
3496
  const rows = await qb2.init(populateValue).join({
3359
- alias,
3497
+ alias: alias2,
3360
3498
  referencedTable: joinTable.name,
3361
3499
  referencedColumn: joinTable.inverseJoinColumn.name,
3362
3500
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
3363
3501
  rootTable: qb2.alias,
3364
3502
  on: joinTable.on,
3365
3503
  orderBy: ___default.default.mapValues((v) => populateValue.ordering || v, joinTable.orderBy)
3366
- }).addSelect(joinColAlias).where({ [joinColAlias]: referencedValues }).execute({ mapResults: false });
3367
- const map = ___default.default.groupBy(joinColumnName)(rows);
3504
+ }).addSelect(joinColSelect).where({ [joinColAlias]: referencedValues }).execute({ mapResults: false });
3505
+ const map = ___default.default.groupBy(joinColRenameAs)(rows);
3368
3506
  results.forEach((r) => {
3369
3507
  r[attributeName] = fromTargetRow(map[r[referencedColumnName]] || []);
3370
3508
  });
@@ -3377,8 +3515,10 @@ const manyToMany = async (input, ctx) => {
3377
3515
  const { joinTable } = attribute;
3378
3516
  const populateQb = db.entityManager.createQueryBuilder(targetMeta.uid);
3379
3517
  const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
3380
- const alias = populateQb.getAlias();
3381
- const joinColAlias = `${alias}.${joinColumnName}`;
3518
+ const alias2 = populateQb.getAlias();
3519
+ const joinColAlias = `${alias2}.${joinColumnName}`;
3520
+ const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
3521
+ const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
3382
3522
  const referencedValues = ___default.default.uniq(
3383
3523
  results.map((r) => r[referencedColumnName]).filter((value) => !___default.default.isNil(value))
3384
3524
  );
@@ -3390,7 +3530,7 @@ const manyToMany = async (input, ctx) => {
3390
3530
  return;
3391
3531
  }
3392
3532
  const rows2 = await populateQb.init(populateValue).join({
3393
- alias,
3533
+ alias: alias2,
3394
3534
  referencedTable: joinTable.name,
3395
3535
  referencedColumn: joinTable.inverseJoinColumn.name,
3396
3536
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3416,15 +3556,15 @@ const manyToMany = async (input, ctx) => {
3416
3556
  return;
3417
3557
  }
3418
3558
  const rows = await populateQb.init(populateValue).join({
3419
- alias,
3559
+ alias: alias2,
3420
3560
  referencedTable: joinTable.name,
3421
3561
  referencedColumn: joinTable.inverseJoinColumn.name,
3422
3562
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
3423
3563
  rootTable: populateQb.alias,
3424
3564
  on: joinTable.on,
3425
3565
  orderBy: ___default.default.mapValues((v) => populateValue.ordering || v, joinTable.orderBy)
3426
- }).addSelect(joinColAlias).where({ [joinColAlias]: referencedValues }).execute({ mapResults: false });
3427
- const map = ___default.default.groupBy(joinColumnName)(rows);
3566
+ }).addSelect(joinColSelect).where({ [joinColAlias]: referencedValues }).execute({ mapResults: false });
3567
+ const map = ___default.default.groupBy(joinColRenameAs)(rows);
3428
3568
  results.forEach((result) => {
3429
3569
  result[attributeName] = fromTargetRow(map[result[referencedColumnName]] || []);
3430
3570
  });
@@ -3467,9 +3607,9 @@ const morphX = async (input, ctx) => {
3467
3607
  return;
3468
3608
  }
3469
3609
  const qb = db.entityManager.createQueryBuilder(target);
3470
- const alias = qb.getAlias();
3610
+ const alias2 = qb.getAlias();
3471
3611
  const rows = await qb.init(populateValue).join({
3472
- alias,
3612
+ alias: alias2,
3473
3613
  referencedTable: joinTable.name,
3474
3614
  referencedColumn: joinColumn.name,
3475
3615
  rootColumn: joinColumn.referencedColumn,
@@ -3479,9 +3619,9 @@ const morphX = async (input, ctx) => {
3479
3619
  field: attributeName
3480
3620
  },
3481
3621
  orderBy: ___default.default.mapValues((v) => populateValue.ordering || v, joinTable.orderBy)
3482
- }).addSelect([`${alias}.${idColumn.name}`, `${alias}.${typeColumn.name}`]).where({
3483
- [`${alias}.${idColumn.name}`]: referencedValues,
3484
- [`${alias}.${typeColumn.name}`]: uid
3622
+ }).addSelect([`${alias2}.${idColumn.name}`, `${alias2}.${typeColumn.name}`]).where({
3623
+ [`${alias2}.${idColumn.name}`]: referencedValues,
3624
+ [`${alias2}.${typeColumn.name}`]: uid
3485
3625
  }).execute({ mapResults: false });
3486
3626
  const map = ___default.default.groupBy(idColumn.name)(rows);
3487
3627
  results.forEach((result) => {
@@ -3730,13 +3870,6 @@ const processPopulate = (populate, ctx) => {
3730
3870
  }
3731
3871
  return finalPopulate;
3732
3872
  };
3733
- function isKnexQuery(value) {
3734
- return value instanceof KnexBuilder__default.default || value instanceof KnexRaw__default.default;
3735
- }
3736
- const addSchema = (db, tableName) => {
3737
- const schemaName = db.getSchemaName();
3738
- return schemaName ? `${schemaName}.${tableName}` : tableName;
3739
- };
3740
3873
  const isRecord$1 = (value) => _.isPlainObject(value);
3741
3874
  const castValue = (value, attribute) => {
3742
3875
  if (!attribute) {
@@ -3778,8 +3911,8 @@ const processNested = (where, ctx) => {
3778
3911
  return processWhere(where, ctx);
3779
3912
  };
3780
3913
  const processRelationWhere = (where, ctx) => {
3781
- const { qb, alias } = ctx;
3782
- const idAlias = qb.aliasColumn("id", alias);
3914
+ const { qb, alias: alias2 } = ctx;
3915
+ const idAlias = qb.aliasColumn("id", alias2);
3783
3916
  if (!isRecord$1(where)) {
3784
3917
  return { [idAlias]: where };
3785
3918
  }
@@ -3795,6 +3928,9 @@ const processRelationWhere = (where, ctx) => {
3795
3928
  }
3796
3929
  if (operatorKeys.length === 1) {
3797
3930
  const operator = operatorKeys[0];
3931
+ if (utils.isOperatorOfType("group", operator)) {
3932
+ return processWhere(where, ctx);
3933
+ }
3798
3934
  return { [idAlias]: { [operator]: processNested(where[operator], ctx) } };
3799
3935
  }
3800
3936
  return processWhere(where, ctx);
@@ -3806,7 +3942,7 @@ function processWhere(where, ctx) {
3806
3942
  if (_.isArray(where)) {
3807
3943
  return where.map((sub) => processWhere(sub, ctx));
3808
3944
  }
3809
- const { db, uid, qb, alias } = ctx;
3945
+ const { db, uid, qb, alias: alias2 } = ctx;
3810
3946
  const meta = db.metadata.get(uid);
3811
3947
  const filters = {};
3812
3948
  for (const key of Object.keys(where)) {
@@ -3829,12 +3965,12 @@ function processWhere(where, ctx) {
3829
3965
  }
3830
3966
  const attribute = meta.attributes[key];
3831
3967
  if (!attribute) {
3832
- filters[qb.aliasColumn(key, alias)] = processAttributeWhere(null, value);
3968
+ filters[qb.aliasColumn(key, alias2)] = processAttributeWhere(null, value);
3833
3969
  continue;
3834
3970
  }
3835
3971
  if (isRelation(attribute.type) && "target" in attribute) {
3836
3972
  const subAlias = createJoin(ctx, {
3837
- alias: alias || qb.alias,
3973
+ alias: alias2 || qb.alias,
3838
3974
  attributeName: key,
3839
3975
  attribute
3840
3976
  });
@@ -3849,7 +3985,7 @@ function processWhere(where, ctx) {
3849
3985
  }
3850
3986
  if (isScalar(attribute.type)) {
3851
3987
  const columnName = toColumnName(meta, key);
3852
- const aliasedColumnName = qb.aliasColumn(columnName, alias);
3988
+ const aliasedColumnName = qb.aliasColumn(columnName, alias2);
3853
3989
  filters[aliasedColumnName] = processAttributeWhere(attribute, value);
3854
3990
  continue;
3855
3991
  }
@@ -4077,7 +4213,7 @@ class ReadableStrapiQuery extends stream.Readable {
4077
4213
  * Custom ._read() implementation
4078
4214
  *
4079
4215
  * NOTE: Here "size" means the number of entities to be read from the database.
4080
- * Not the actual byte size, as it would means that we need to return partial entities.
4216
+ * Not the actual byte size, as it would mean that we need to return partial entities.
4081
4217
  *
4082
4218
  */
4083
4219
  async _read(size) {
@@ -4140,61 +4276,6 @@ class ReadableStrapiQuery extends stream.Readable {
4140
4276
  }
4141
4277
  }
4142
4278
  }
4143
- const storage = new node_async_hooks.AsyncLocalStorage();
4144
- const transactionCtx = {
4145
- async run(trx, cb) {
4146
- const store = storage.getStore();
4147
- return storage.run(
4148
- {
4149
- trx,
4150
- // Fill with existing callbacks if nesting transactions
4151
- commitCallbacks: store?.commitCallbacks || [],
4152
- rollbackCallbacks: store?.rollbackCallbacks || []
4153
- },
4154
- cb
4155
- );
4156
- },
4157
- get() {
4158
- const store = storage.getStore();
4159
- return store?.trx;
4160
- },
4161
- async commit(trx) {
4162
- const store = storage.getStore();
4163
- if (store?.trx) {
4164
- store.trx = null;
4165
- }
4166
- await trx.commit();
4167
- if (!store?.commitCallbacks.length) {
4168
- return;
4169
- }
4170
- store.commitCallbacks.forEach((cb) => cb());
4171
- store.commitCallbacks = [];
4172
- },
4173
- async rollback(trx) {
4174
- const store = storage.getStore();
4175
- if (store?.trx) {
4176
- store.trx = null;
4177
- }
4178
- await trx.rollback();
4179
- if (!store?.rollbackCallbacks.length) {
4180
- return;
4181
- }
4182
- store.rollbackCallbacks.forEach((cb) => cb());
4183
- store.rollbackCallbacks = [];
4184
- },
4185
- onCommit(cb) {
4186
- const store = storage.getStore();
4187
- if (store?.commitCallbacks) {
4188
- store.commitCallbacks.push(cb);
4189
- }
4190
- },
4191
- onRollback(cb) {
4192
- const store = storage.getStore();
4193
- if (store?.rollbackCallbacks) {
4194
- store.rollbackCallbacks.push(cb);
4195
- }
4196
- }
4197
- };
4198
4279
  const createQueryBuilder = (uid, db, initialState = {}) => {
4199
4280
  const meta = db.metadata.get(uid);
4200
4281
  const { tableName } = meta;
@@ -4227,9 +4308,9 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
4227
4308
  initialState
4228
4309
  );
4229
4310
  const getAlias = () => {
4230
- const alias = `t${state.aliasCounter}`;
4311
+ const alias2 = `t${state.aliasCounter}`;
4231
4312
  state.aliasCounter += 1;
4232
- return alias;
4313
+ return alias2;
4233
4314
  };
4234
4315
  return {
4235
4316
  alias: getAlias(),
@@ -4396,15 +4477,15 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
4396
4477
  mustUseAlias() {
4397
4478
  return ["select", "count"].includes(state.type);
4398
4479
  },
4399
- aliasColumn(key, alias) {
4480
+ aliasColumn(key, alias2) {
4400
4481
  if (typeof key !== "string") {
4401
4482
  return key;
4402
4483
  }
4403
4484
  if (key.indexOf(".") >= 0) {
4404
4485
  return key;
4405
4486
  }
4406
- if (!___default.default.isNil(alias)) {
4407
- return `${alias}.${key}`;
4487
+ if (!___default.default.isNil(alias2)) {
4488
+ return `${alias2}.${key}`;
4408
4489
  }
4409
4490
  return this.mustUseAlias() ? `${this.alias}.${key}` : key;
4410
4491
  },
@@ -4439,6 +4520,20 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
4439
4520
  shouldUseDistinct() {
4440
4521
  return state.joins.length > 0 && ___default.default.isEmpty(state.groupBy);
4441
4522
  },
4523
+ shouldUseDeepSort() {
4524
+ return state.orderBy.filter(({ column }) => column.indexOf(".") >= 0).filter(({ column }) => {
4525
+ const col = column.split(".");
4526
+ for (let i = 0; i < col.length - 1; i += 1) {
4527
+ const el = col[i];
4528
+ const isRelationAttribute = meta.attributes[el]?.type === "relation";
4529
+ const isAliasedRelation = Object.values(state.joins).map((join) => join.alias).includes(el);
4530
+ if (isRelationAttribute || isAliasedRelation) {
4531
+ return true;
4532
+ }
4533
+ }
4534
+ return false;
4535
+ }).length > 0;
4536
+ },
4442
4537
  processSelect() {
4443
4538
  state.select = state.select.map((field) => {
4444
4539
  if (isKnexQuery(field)) {
@@ -4556,6 +4651,9 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
4556
4651
  if (state.joins.length > 0) {
4557
4652
  applyJoins(qb, state.joins);
4558
4653
  }
4654
+ if (this.shouldUseDeepSort()) {
4655
+ return wrapWithDeepSort(qb, { qb: this, db, uid });
4656
+ }
4559
4657
  return qb;
4560
4658
  },
4561
4659
  async execute({ mapResults = true } = {}) {
@@ -4757,7 +4855,7 @@ const deleteRelatedMorphOneRelationsAfterMorphToManyUpdate = async (rows, {
4757
4855
  await createQueryBuilder(joinTable.name, db).delete().where({ $or: orWhere }).transacting(trx).execute();
4758
4856
  }
4759
4857
  };
4760
- const getDocumentSiblingIdsQuery = (con, tableName, id) => {
4858
+ const getDocumentSiblingIdsQuery = (tableName, id) => {
4761
4859
  const models = Array.from(strapi.db.metadata.values());
4762
4860
  const isContentType = models.find((model) => {
4763
4861
  return model.tableName === tableName && model.attributes.documentId;
@@ -4765,10 +4863,11 @@ const getDocumentSiblingIdsQuery = (con, tableName, id) => {
4765
4863
  if (!isContentType) {
4766
4864
  return [id];
4767
4865
  }
4768
- return con.from(tableName).select("id").where(
4769
- "document_id",
4770
- con.from(tableName).select("document_id").where("id", id)
4771
- );
4866
+ return function(query) {
4867
+ query.select("id").from(tableName).whereIn("document_id", (documentIDSubQuery) => {
4868
+ documentIDSubQuery.from(tableName).select("document_id").where("id", id);
4869
+ });
4870
+ };
4772
4871
  };
4773
4872
  const deletePreviousOneToAnyRelations = async ({
4774
4873
  id,
@@ -4785,7 +4884,7 @@ const deletePreviousOneToAnyRelations = async ({
4785
4884
  const { joinTable } = attribute;
4786
4885
  const { joinColumn, inverseJoinColumn } = joinTable;
4787
4886
  const con = db.getConnection();
4788
- await con.delete().from(joinTable.name).whereNotIn(joinColumn.name, getDocumentSiblingIdsQuery(con, joinColumn.referencedTable, id)).whereIn(inverseJoinColumn.name, relIdsToadd).where(joinTable.on || {}).transacting(trx);
4887
+ await con.delete().from(joinTable.name).whereNotIn(joinColumn.name, getDocumentSiblingIdsQuery(joinColumn.referencedTable, id)).whereIn(inverseJoinColumn.name, relIdsToadd).where(joinTable.on || {}).transacting(trx);
4789
4888
  await cleanOrderColumns({ attribute, db, inverseRelIds: relIdsToadd, transaction: trx });
4790
4889
  };
4791
4890
  const deletePreviousAnyToOneRelations = async ({
@@ -4804,7 +4903,7 @@ const deletePreviousAnyToOneRelations = async ({
4804
4903
  if (isManyToAny(attribute)) {
4805
4904
  const relsToDelete = await con.select(inverseJoinColumn.name).from(joinTable.name).where(joinColumn.name, id).whereNotIn(
4806
4905
  inverseJoinColumn.name,
4807
- getDocumentSiblingIdsQuery(con, inverseJoinColumn.referencedTable, relIdToadd)
4906
+ getDocumentSiblingIdsQuery(inverseJoinColumn.referencedTable, relIdToadd)
4808
4907
  ).where(joinTable.on || {}).transacting(trx);
4809
4908
  const relIdsToDelete = _.map(inverseJoinColumn.name, relsToDelete);
4810
4909
  await createQueryBuilder(joinTable.name, db).delete().where({
@@ -4815,7 +4914,7 @@ const deletePreviousAnyToOneRelations = async ({
4815
4914
  } else {
4816
4915
  await con.delete().from(joinTable.name).where(joinColumn.name, id).whereNotIn(
4817
4916
  inverseJoinColumn.name,
4818
- getDocumentSiblingIdsQuery(con, inverseJoinColumn.referencedTable, relIdToadd)
4917
+ getDocumentSiblingIdsQuery(inverseJoinColumn.referencedTable, relIdToadd)
4819
4918
  ).where(joinTable.on || {}).transacting(trx);
4820
4919
  }
4821
4920
  };
@@ -5899,6 +5998,21 @@ const createStorage = (opts) => {
5899
5998
  const wrapTransaction = (db) => (fn) => () => {
5900
5999
  return db.transaction(({ trx }) => Promise.resolve(fn(trx, db)));
5901
6000
  };
6001
+ const transformLogMessage = (level, message) => {
6002
+ if (typeof message === "string") {
6003
+ return { level, message };
6004
+ }
6005
+ if (typeof message === "object" && message !== null) {
6006
+ if ("event" in message && "name" in message) {
6007
+ return {
6008
+ level,
6009
+ message: `[internal migration]: ${message.event} ${message?.name}`,
6010
+ timestamp: Date.now()
6011
+ };
6012
+ }
6013
+ }
6014
+ return "";
6015
+ };
5902
6016
  const migrationResolver = ({ name, path: path2, context }) => {
5903
6017
  const { db } = context;
5904
6018
  if (!path2) {
@@ -5927,7 +6041,20 @@ const createUserMigrationProvider = (db) => {
5927
6041
  const context = { db };
5928
6042
  const umzugProvider = new umzug.Umzug({
5929
6043
  storage: createStorage({ db, tableName: "strapi_migrations" }),
5930
- logger: console,
6044
+ logger: {
6045
+ info(message) {
6046
+ db.logger.info(transformLogMessage("info", message));
6047
+ },
6048
+ warn(message) {
6049
+ db.logger.warn(transformLogMessage("warn", message));
6050
+ },
6051
+ error(message) {
6052
+ db.logger.error(transformLogMessage("error", message));
6053
+ },
6054
+ debug(message) {
6055
+ db.logger.debug(transformLogMessage("debug", message));
6056
+ }
6057
+ },
5931
6058
  context,
5932
6059
  migrations: {
5933
6060
  glob: ["*.{js,sql}", { cwd: dir }],
@@ -6215,16 +6342,115 @@ const findDiffs = (shortMap) => {
6215
6342
  });
6216
6343
  return diffs;
6217
6344
  };
6345
+ const createLocaleColumn = async (db, tableName) => {
6346
+ await db.schema.alterTable(tableName, (table) => {
6347
+ table.string("locale");
6348
+ });
6349
+ };
6350
+ const createdLocale = {
6351
+ name: "5.0.0-03-created-locale",
6352
+ async up(knex2, db) {
6353
+ for (const meta of db.metadata.values()) {
6354
+ const hasTable = await knex2.schema.hasTable(meta.tableName);
6355
+ if (!hasTable) {
6356
+ continue;
6357
+ }
6358
+ const uid = meta.uid;
6359
+ const model = strapi.getModel(uid);
6360
+ if (!model) {
6361
+ continue;
6362
+ }
6363
+ const hasLocaleColumn = await knex2.schema.hasColumn(meta.tableName, "locale");
6364
+ if (meta.attributes.locale && !hasLocaleColumn) {
6365
+ await createLocaleColumn(knex2, meta.tableName);
6366
+ }
6367
+ }
6368
+ },
6369
+ async down() {
6370
+ throw new Error("not implemented");
6371
+ }
6372
+ };
6373
+ const createPublishedAtColumn = async (db, tableName) => {
6374
+ await db.schema.alterTable(tableName, (table) => {
6375
+ table.string("published_at");
6376
+ });
6377
+ };
6378
+ const createdPublishedAt = {
6379
+ name: "5.0.0-04-created-published-at",
6380
+ async up(knex2, db) {
6381
+ for (const meta of db.metadata.values()) {
6382
+ const hasTable = await knex2.schema.hasTable(meta.tableName);
6383
+ if (!hasTable) {
6384
+ continue;
6385
+ }
6386
+ const uid = meta.uid;
6387
+ const model = strapi.getModel(uid);
6388
+ if (!model) {
6389
+ continue;
6390
+ }
6391
+ const hasPublishedAtColumn = await knex2.schema.hasColumn(meta.tableName, "published_at");
6392
+ if (meta.attributes.publishedAt && !hasPublishedAtColumn) {
6393
+ await createPublishedAtColumn(knex2, meta.tableName);
6394
+ }
6395
+ }
6396
+ },
6397
+ async down() {
6398
+ throw new Error("not implemented");
6399
+ }
6400
+ };
6401
+ const dropIndex = async (knex2, tableName, columnName) => {
6402
+ try {
6403
+ await knex2.schema.alterTable(tableName, (table) => {
6404
+ table.dropUnique([columnName], `${tableName}_${columnName}_unique`);
6405
+ });
6406
+ } catch (error) {
6407
+ }
6408
+ };
6409
+ const dropSlugFieldsIndex = {
6410
+ name: "5.0.0-05-drop-slug-fields-index",
6411
+ async up(knex2, db) {
6412
+ for (const meta of db.metadata.values()) {
6413
+ const hasTable = await knex2.schema.hasTable(meta.tableName);
6414
+ if (!hasTable) {
6415
+ continue;
6416
+ }
6417
+ for (const attribute of Object.values(meta.attributes)) {
6418
+ if (attribute.type === "uid" && attribute.columnName) {
6419
+ await dropIndex(knex2, meta.tableName, attribute.columnName);
6420
+ }
6421
+ }
6422
+ }
6423
+ },
6424
+ async down() {
6425
+ throw new Error("not implemented");
6426
+ }
6427
+ };
6218
6428
  const internalMigrations = [
6219
6429
  renameIdentifiersLongerThanMaxLength,
6220
- createdDocumentId
6430
+ createdDocumentId,
6431
+ createdLocale,
6432
+ createdPublishedAt,
6433
+ dropSlugFieldsIndex
6221
6434
  ];
6222
6435
  const createInternalMigrationProvider = (db) => {
6223
6436
  const context = { db };
6224
6437
  const migrations = [...internalMigrations];
6225
6438
  const umzugProvider = new umzug.Umzug({
6226
6439
  storage: createStorage({ db, tableName: "strapi_migrations_internal" }),
6227
- logger: console,
6440
+ logger: {
6441
+ info(message) {
6442
+ db.logger.debug(transformLogMessage("info", message));
6443
+ },
6444
+ warn(message) {
6445
+ db.logger.warn(transformLogMessage("warn", message));
6446
+ },
6447
+ error(message) {
6448
+ db.logger.error(transformLogMessage("error", message));
6449
+ },
6450
+ debug(message) {
6451
+ db.logger.debug(transformLogMessage("debug", message));
6452
+ }
6453
+ },
6228
6454
  context,
6229
6455
  migrations: () => migrations.map((migration) => {
6230
6456
  return {
@@ -6495,6 +6721,7 @@ class Database {
6495
6721
  migrations;
6496
6722
  lifecycles;
6497
6723
  entityManager;
6724
+ logger;
6498
6725
  constructor(config) {
6499
6726
  this.config = {
6500
6727
  ...config,
@@ -6514,6 +6741,7 @@ class Database {
6514
6741
  this.migrations = createMigrationsProvider(this);
6515
6742
  this.lifecycles = createLifecyclesProvider(this);
6516
6743
  this.entityManager = createEntityManager(this);
6744
+ this.logger = config.logger ?? console;
6517
6745
  }
6518
6746
  async init({ models }) {
6519
6747
  this.metadata.loadModels(models);