@strapi/database 5.0.0-beta.12 → 5.0.0-beta.14

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.mjs CHANGED
@@ -5,11 +5,11 @@ import _, { isNil, castArray, prop, omit, isInteger, snakeCase, partition, sumBy
5
5
  import crypto from "crypto";
6
6
  import crypto$1 from "node:crypto";
7
7
  import * as dateFns from "date-fns";
8
- import { isOperatorOfType, isOperator } from "@strapi/utils";
8
+ import { AsyncLocalStorage } from "node:async_hooks";
9
9
  import KnexBuilder from "knex/lib/query/querybuilder";
10
10
  import KnexRaw from "knex/lib/raw";
11
+ import { isOperatorOfType, isOperator } from "@strapi/utils";
11
12
  import { Readable } from "stream";
12
- import { AsyncLocalStorage } from "node:async_hooks";
13
13
  import _$1 from "lodash";
14
14
  import { Umzug } from "umzug";
15
15
  import { createId } from "@paralleldrive/cuid2";
@@ -2895,6 +2895,68 @@ const createField = (attribute) => {
2895
2895
  }
2896
2896
  throw new Error(`Undefined field for type ${type}`);
2897
2897
  };
2898
+ const storage = new AsyncLocalStorage();
2899
+ const transactionCtx = {
2900
+ async run(trx, cb) {
2901
+ const store = storage.getStore();
2902
+ return storage.run(
2903
+ {
2904
+ trx,
2905
+ // Fill with existing callbacks if nesting transactions
2906
+ commitCallbacks: store?.commitCallbacks || [],
2907
+ rollbackCallbacks: store?.rollbackCallbacks || []
2908
+ },
2909
+ cb
2910
+ );
2911
+ },
2912
+ get() {
2913
+ const store = storage.getStore();
2914
+ return store?.trx;
2915
+ },
2916
+ async commit(trx) {
2917
+ const store = storage.getStore();
2918
+ if (store?.trx) {
2919
+ store.trx = null;
2920
+ }
2921
+ await trx.commit();
2922
+ if (!store?.commitCallbacks.length) {
2923
+ return;
2924
+ }
2925
+ store.commitCallbacks.forEach((cb) => cb());
2926
+ store.commitCallbacks = [];
2927
+ },
2928
+ async rollback(trx) {
2929
+ const store = storage.getStore();
2930
+ if (store?.trx) {
2931
+ store.trx = null;
2932
+ }
2933
+ await trx.rollback();
2934
+ if (!store?.rollbackCallbacks.length) {
2935
+ return;
2936
+ }
2937
+ store.rollbackCallbacks.forEach((cb) => cb());
2938
+ store.rollbackCallbacks = [];
2939
+ },
2940
+ onCommit(cb) {
2941
+ const store = storage.getStore();
2942
+ if (store?.commitCallbacks) {
2943
+ store.commitCallbacks.push(cb);
2944
+ }
2945
+ },
2946
+ onRollback(cb) {
2947
+ const store = storage.getStore();
2948
+ if (store?.rollbackCallbacks) {
2949
+ store.rollbackCallbacks.push(cb);
2950
+ }
2951
+ }
2952
+ };
2953
+ function isKnexQuery(value) {
2954
+ return value instanceof KnexBuilder || value instanceof KnexRaw;
2955
+ }
2956
+ const addSchema = (db, tableName) => {
2957
+ const schemaName = db.getSchemaName();
2958
+ return schemaName ? `${schemaName}.${tableName}` : tableName;
2959
+ };
2898
2960
  const fromSingleRow = (meta, row) => {
2899
2961
  const { attributes } = meta;
2900
2962
  if (_.isNil(row)) {
@@ -3017,7 +3079,7 @@ const escapeQuery = (query, charsToEscape, escapeChar = "\\") => {
3017
3079
  ""
3018
3080
  );
3019
3081
  };
3020
- const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
3082
+ const createPivotJoin = (ctx, { alias: alias2, refAlias, joinTable, targetMeta }) => {
3021
3083
  const { qb } = ctx;
3022
3084
  const joinAlias = qb.getAlias();
3023
3085
  qb.join({
@@ -3025,7 +3087,7 @@ const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
3025
3087
  referencedTable: joinTable.name,
3026
3088
  referencedColumn: joinTable.joinColumn.name,
3027
3089
  rootColumn: joinTable.joinColumn.referencedColumn,
3028
- rootTable: alias,
3090
+ rootTable: alias2,
3029
3091
  on: joinTable.on
3030
3092
  });
3031
3093
  const subAlias = refAlias || qb.getAlias();
@@ -3038,7 +3100,7 @@ const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
3038
3100
  });
3039
3101
  return subAlias;
3040
3102
  };
3041
- const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3103
+ const createJoin = (ctx, { alias: alias2, refAlias, attributeName, attribute }) => {
3042
3104
  const { db, qb, uid } = ctx;
3043
3105
  if (attribute.type !== "relation") {
3044
3106
  throw new Error(`Cannot join on non relational field ${attributeName}`);
@@ -3054,7 +3116,7 @@ const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3054
3116
  referencedTable: targetMeta.tableName,
3055
3117
  referencedColumn: morphColumn.idColumn.name,
3056
3118
  rootColumn: morphColumn.idColumn.referencedColumn,
3057
- rootTable: alias,
3119
+ rootTable: alias2,
3058
3120
  on: {
3059
3121
  [morphColumn.typeColumn.name]: uid,
3060
3122
  ...morphColumn.on
@@ -3069,7 +3131,7 @@ const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3069
3131
  referencedTable: joinTable2.name,
3070
3132
  referencedColumn: joinTable2.morphColumn.idColumn.name,
3071
3133
  rootColumn: joinTable2.morphColumn.idColumn.referencedColumn,
3072
- rootTable: alias,
3134
+ rootTable: alias2,
3073
3135
  on: {
3074
3136
  [joinTable2.morphColumn.typeColumn.name]: uid,
3075
3137
  field: attributeName
@@ -3085,7 +3147,7 @@ const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3085
3147
  });
3086
3148
  return subAlias;
3087
3149
  }
3088
- return alias;
3150
+ return alias2;
3089
3151
  }
3090
3152
  const { joinColumn } = attribute;
3091
3153
  if (joinColumn) {
@@ -3095,20 +3157,20 @@ const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3095
3157
  referencedTable: targetMeta.tableName,
3096
3158
  referencedColumn: joinColumn.referencedColumn,
3097
3159
  rootColumn: joinColumn.name,
3098
- rootTable: alias
3160
+ rootTable: alias2
3099
3161
  });
3100
3162
  return subAlias;
3101
3163
  }
3102
3164
  const { joinTable } = attribute;
3103
3165
  if (joinTable) {
3104
- return createPivotJoin(ctx, { alias, refAlias, joinTable, targetMeta });
3166
+ return createPivotJoin(ctx, { alias: alias2, refAlias, joinTable, targetMeta });
3105
3167
  }
3106
- return alias;
3168
+ return alias2;
3107
3169
  };
3108
3170
  const applyJoin = (qb, join) => {
3109
3171
  const {
3110
3172
  method = "leftJoin",
3111
- alias,
3173
+ alias: alias2,
3112
3174
  referencedTable,
3113
3175
  referencedColumn,
3114
3176
  rootColumn,
@@ -3118,26 +3180,28 @@ const applyJoin = (qb, join) => {
3118
3180
  on,
3119
3181
  orderBy
3120
3182
  } = join;
3121
- qb[method](`${referencedTable} as ${alias}`, (inner) => {
3122
- inner.on(`${rootTable}.${rootColumn}`, `${alias}.${referencedColumn}`);
3183
+ qb[method](`${referencedTable} as ${alias2}`, (inner) => {
3184
+ inner.on(`${rootTable}.${rootColumn}`, `${alias2}.${referencedColumn}`);
3123
3185
  if (on) {
3124
3186
  for (const key of Object.keys(on)) {
3125
- inner.onVal(`${alias}.${key}`, on[key]);
3187
+ inner.onVal(`${alias2}.${key}`, on[key]);
3126
3188
  }
3127
3189
  }
3128
3190
  });
3129
3191
  if (orderBy) {
3130
3192
  Object.keys(orderBy).forEach((column) => {
3131
3193
  const direction = orderBy[column];
3132
- qb.orderBy(`${alias}.${column}`, direction);
3194
+ qb.orderBy(`${alias2}.${column}`, direction);
3133
3195
  });
3134
3196
  }
3135
3197
  };
3136
3198
  const applyJoins = (qb, joins) => {
3137
3199
  return joins.forEach((join) => applyJoin(qb, join));
3138
3200
  };
3201
+ const COL_STRAPI_ROW_NUMBER = "__strapi_row_number";
3202
+ const COL_STRAPI_ORDER_BY_PREFIX = "__strapi_order_by";
3139
3203
  const processOrderBy = (orderBy, ctx) => {
3140
- const { db, uid, qb, alias } = ctx;
3204
+ const { db, uid, qb, alias: alias2 } = ctx;
3141
3205
  const meta = db.metadata.get(uid);
3142
3206
  const { attributes } = meta;
3143
3207
  if (typeof orderBy === "string") {
@@ -3146,7 +3210,7 @@ const processOrderBy = (orderBy, ctx) => {
3146
3210
  throw new Error(`Attribute ${orderBy} not found on model ${uid}`);
3147
3211
  }
3148
3212
  const columnName = toColumnName(meta, orderBy);
3149
- return [{ column: qb.aliasColumn(columnName, alias) }];
3213
+ return [{ column: qb.aliasColumn(columnName, alias2) }];
3150
3214
  }
3151
3215
  if (Array.isArray(orderBy)) {
3152
3216
  return orderBy.flatMap((value) => processOrderBy(value, ctx));
@@ -3160,11 +3224,11 @@ const processOrderBy = (orderBy, ctx) => {
3160
3224
  }
3161
3225
  if (isScalar(attribute.type)) {
3162
3226
  const columnName = toColumnName(meta, key);
3163
- return { column: qb.aliasColumn(columnName, alias), order: direction };
3227
+ return { column: qb.aliasColumn(columnName, alias2), order: direction };
3164
3228
  }
3165
3229
  if (attribute.type === "relation" && "target" in attribute) {
3166
3230
  const subAlias = createJoin(ctx, {
3167
- alias: alias || qb.alias,
3231
+ alias: alias2 || qb.alias,
3168
3232
  attributeName: key,
3169
3233
  attribute
3170
3234
  });
@@ -3180,6 +3244,75 @@ const processOrderBy = (orderBy, ctx) => {
3180
3244
  }
3181
3245
  throw new Error("Invalid orderBy syntax");
3182
3246
  };
3247
+ const getStrapiOrderColumnAlias = (column) => {
3248
+ const trimmedColumnName = column.replaceAll(".", "_");
3249
+ return `${COL_STRAPI_ORDER_BY_PREFIX}__${trimmedColumnName}`;
3250
+ };
3251
+ const wrapWithDeepSort = (originalQuery, ctx) => {
3252
+ const { db, qb, uid } = ctx;
3253
+ const { tableName } = db.metadata.get(uid);
3254
+ const orderBy = _.cloneDeep(qb.state.orderBy);
3255
+ const resultQueryAlias = qb.getAlias();
3256
+ const aliasedTableName = qb.mustUseAlias() ? alias(resultQueryAlias, tableName) : tableName;
3257
+ const resultQuery = db.getConnection(aliasedTableName);
3258
+ const baseQuery = originalQuery.clone();
3259
+ const baseQueryAlias = qb.getAlias();
3260
+ baseQuery.clear("select").clear("order").clear("limit").clear("offset");
3261
+ baseQuery.select(
3262
+ // Always select the row id for future manipulation
3263
+ prefix(qb.alias, "id"),
3264
+ ...orderBy.map(
3265
+ (orderByClause) => alias(getStrapiOrderColumnAlias(orderByClause.column), orderByClause.column)
3266
+ )
3267
+ );
3268
+ const partitionedQueryAlias = qb.getAlias();
3269
+ const selectRowsAsNumberedPartitions = (partitionedQuery) => {
3270
+ const prefixedOrderBy = orderBy.map((orderByClause) => ({
3271
+ column: prefix(baseQueryAlias, getStrapiOrderColumnAlias(orderByClause.column)),
3272
+ order: orderByClause.order
3273
+ }));
3274
+ const orderByColumns = prefixedOrderBy.map(_.prop("column"));
3275
+ partitionedQuery.select(
3276
+ // Always select baseQuery.id
3277
+ prefix(baseQueryAlias, "id"),
3278
+ ...orderByColumns
3279
+ ).rowNumber(COL_STRAPI_ROW_NUMBER, (subQuery) => {
3280
+ for (const orderByClause of prefixedOrderBy) {
3281
+ subQuery.orderBy(orderByClause.column, orderByClause.order, "last");
3282
+ }
3283
+ subQuery.partitionBy(`${baseQueryAlias}.id`);
3284
+ }).from(baseQuery.as(baseQueryAlias)).as(partitionedQueryAlias);
3285
+ };
3286
+ const originalSelect = _.difference(
3287
+ qb.state.select,
3288
+ // Remove order by columns from the initial select
3289
+ qb.state.orderBy.map(_.prop("column"))
3290
+ ).map(prefix(resultQueryAlias));
3291
+ resultQuery.select(originalSelect).innerJoin(selectRowsAsNumberedPartitions, function() {
3292
+ this.on(`${partitionedQueryAlias}.id`, `${resultQueryAlias}.id`).andOnVal(`${partitionedQueryAlias}.${COL_STRAPI_ROW_NUMBER}`, "=", 1);
3293
+ });
3294
+ if (qb.state.limit) {
3295
+ resultQuery.limit(qb.state.limit);
3296
+ }
3297
+ if (qb.state.offset) {
3298
+ resultQuery.offset(qb.state.offset);
3299
+ }
3300
+ if (qb.state.first) {
3301
+ resultQuery.first();
3302
+ }
3303
+ resultQuery.orderBy([
3304
+ // Transform "order by" clause to their T alias and prefix them with T alias
3305
+ ...orderBy.map((orderByClause) => ({
3306
+ column: prefix(partitionedQueryAlias, getStrapiOrderColumnAlias(orderByClause.column)),
3307
+ order: orderByClause.order
3308
+ })),
3309
+ // Add T.id to the order by clause to get consistent results in case several rows have the exact same order
3310
+ { column: `${partitionedQueryAlias}.id`, order: "asc" }
3311
+ ]);
3312
+ return resultQuery;
3313
+ };
3314
+ const alias = _.curry((alias2, value) => `${value} as ${alias2}`);
3315
+ const prefix = _.curry((prefix2, value) => `${prefix2}.${value}`);
3183
3316
  const joinColPrefix = "__strapi";
3184
3317
  const XtoOne = async (input, ctx) => {
3185
3318
  const { attribute, attributeName, results, populateValue, targetMeta, isCount } = input;
@@ -3207,8 +3340,8 @@ const XtoOne = async (input, ctx) => {
3207
3340
  const { joinTable } = attribute;
3208
3341
  const qb2 = db.entityManager.createQueryBuilder(targetMeta.uid);
3209
3342
  const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
3210
- const alias = qb2.getAlias();
3211
- const joinColAlias = `${alias}.${joinColumnName}`;
3343
+ const alias2 = qb2.getAlias();
3344
+ const joinColAlias = `${alias2}.${joinColumnName}`;
3212
3345
  const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
3213
3346
  const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
3214
3347
  const referencedValues = _.uniq(
@@ -3222,7 +3355,7 @@ const XtoOne = async (input, ctx) => {
3222
3355
  return;
3223
3356
  }
3224
3357
  const rows2 = await qb2.init(populateValue).join({
3225
- alias,
3358
+ alias: alias2,
3226
3359
  referencedTable: joinTable.name,
3227
3360
  referencedColumn: joinTable.inverseJoinColumn.name,
3228
3361
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3248,7 +3381,7 @@ const XtoOne = async (input, ctx) => {
3248
3381
  return;
3249
3382
  }
3250
3383
  const rows = await qb2.init(populateValue).join({
3251
- alias,
3384
+ alias: alias2,
3252
3385
  referencedTable: joinTable.name,
3253
3386
  referencedColumn: joinTable.inverseJoinColumn.name,
3254
3387
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3288,8 +3421,8 @@ const oneToMany = async (input, ctx) => {
3288
3421
  const { joinTable } = attribute;
3289
3422
  const qb2 = db.entityManager.createQueryBuilder(targetMeta.uid);
3290
3423
  const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
3291
- const alias = qb2.getAlias();
3292
- const joinColAlias = `${alias}.${joinColumnName}`;
3424
+ const alias2 = qb2.getAlias();
3425
+ const joinColAlias = `${alias2}.${joinColumnName}`;
3293
3426
  const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
3294
3427
  const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
3295
3428
  const referencedValues = _.uniq(
@@ -3303,7 +3436,7 @@ const oneToMany = async (input, ctx) => {
3303
3436
  return;
3304
3437
  }
3305
3438
  const rows2 = await qb2.init(populateValue).join({
3306
- alias,
3439
+ alias: alias2,
3307
3440
  referencedTable: joinTable.name,
3308
3441
  referencedColumn: joinTable.inverseJoinColumn.name,
3309
3442
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3329,7 +3462,7 @@ const oneToMany = async (input, ctx) => {
3329
3462
  return;
3330
3463
  }
3331
3464
  const rows = await qb2.init(populateValue).join({
3332
- alias,
3465
+ alias: alias2,
3333
3466
  referencedTable: joinTable.name,
3334
3467
  referencedColumn: joinTable.inverseJoinColumn.name,
3335
3468
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3350,8 +3483,8 @@ const manyToMany = async (input, ctx) => {
3350
3483
  const { joinTable } = attribute;
3351
3484
  const populateQb = db.entityManager.createQueryBuilder(targetMeta.uid);
3352
3485
  const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
3353
- const alias = populateQb.getAlias();
3354
- const joinColAlias = `${alias}.${joinColumnName}`;
3486
+ const alias2 = populateQb.getAlias();
3487
+ const joinColAlias = `${alias2}.${joinColumnName}`;
3355
3488
  const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
3356
3489
  const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
3357
3490
  const referencedValues = _.uniq(
@@ -3365,7 +3498,7 @@ const manyToMany = async (input, ctx) => {
3365
3498
  return;
3366
3499
  }
3367
3500
  const rows2 = await populateQb.init(populateValue).join({
3368
- alias,
3501
+ alias: alias2,
3369
3502
  referencedTable: joinTable.name,
3370
3503
  referencedColumn: joinTable.inverseJoinColumn.name,
3371
3504
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3391,7 +3524,7 @@ const manyToMany = async (input, ctx) => {
3391
3524
  return;
3392
3525
  }
3393
3526
  const rows = await populateQb.init(populateValue).join({
3394
- alias,
3527
+ alias: alias2,
3395
3528
  referencedTable: joinTable.name,
3396
3529
  referencedColumn: joinTable.inverseJoinColumn.name,
3397
3530
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3442,9 +3575,9 @@ const morphX = async (input, ctx) => {
3442
3575
  return;
3443
3576
  }
3444
3577
  const qb = db.entityManager.createQueryBuilder(target);
3445
- const alias = qb.getAlias();
3578
+ const alias2 = qb.getAlias();
3446
3579
  const rows = await qb.init(populateValue).join({
3447
- alias,
3580
+ alias: alias2,
3448
3581
  referencedTable: joinTable.name,
3449
3582
  referencedColumn: joinColumn.name,
3450
3583
  rootColumn: joinColumn.referencedColumn,
@@ -3454,9 +3587,9 @@ const morphX = async (input, ctx) => {
3454
3587
  field: attributeName
3455
3588
  },
3456
3589
  orderBy: _.mapValues((v) => populateValue.ordering || v, joinTable.orderBy)
3457
- }).addSelect([`${alias}.${idColumn.name}`, `${alias}.${typeColumn.name}`]).where({
3458
- [`${alias}.${idColumn.name}`]: referencedValues,
3459
- [`${alias}.${typeColumn.name}`]: uid
3590
+ }).addSelect([`${alias2}.${idColumn.name}`, `${alias2}.${typeColumn.name}`]).where({
3591
+ [`${alias2}.${idColumn.name}`]: referencedValues,
3592
+ [`${alias2}.${typeColumn.name}`]: uid
3460
3593
  }).execute({ mapResults: false });
3461
3594
  const map2 = _.groupBy(idColumn.name)(rows);
3462
3595
  results.forEach((result) => {
@@ -3705,13 +3838,6 @@ const processPopulate = (populate, ctx) => {
3705
3838
  }
3706
3839
  return finalPopulate;
3707
3840
  };
3708
- function isKnexQuery(value) {
3709
- return value instanceof KnexBuilder || value instanceof KnexRaw;
3710
- }
3711
- const addSchema = (db, tableName) => {
3712
- const schemaName = db.getSchemaName();
3713
- return schemaName ? `${schemaName}.${tableName}` : tableName;
3714
- };
3715
3841
  const isRecord$1 = (value) => isPlainObject(value);
3716
3842
  const castValue = (value, attribute) => {
3717
3843
  if (!attribute) {
@@ -3753,8 +3879,8 @@ const processNested = (where, ctx) => {
3753
3879
  return processWhere(where, ctx);
3754
3880
  };
3755
3881
  const processRelationWhere = (where, ctx) => {
3756
- const { qb, alias } = ctx;
3757
- const idAlias = qb.aliasColumn("id", alias);
3882
+ const { qb, alias: alias2 } = ctx;
3883
+ const idAlias = qb.aliasColumn("id", alias2);
3758
3884
  if (!isRecord$1(where)) {
3759
3885
  return { [idAlias]: where };
3760
3886
  }
@@ -3784,7 +3910,7 @@ function processWhere(where, ctx) {
3784
3910
  if (isArray(where)) {
3785
3911
  return where.map((sub) => processWhere(sub, ctx));
3786
3912
  }
3787
- const { db, uid, qb, alias } = ctx;
3913
+ const { db, uid, qb, alias: alias2 } = ctx;
3788
3914
  const meta = db.metadata.get(uid);
3789
3915
  const filters = {};
3790
3916
  for (const key of Object.keys(where)) {
@@ -3807,12 +3933,12 @@ function processWhere(where, ctx) {
3807
3933
  }
3808
3934
  const attribute = meta.attributes[key];
3809
3935
  if (!attribute) {
3810
- filters[qb.aliasColumn(key, alias)] = processAttributeWhere(null, value);
3936
+ filters[qb.aliasColumn(key, alias2)] = processAttributeWhere(null, value);
3811
3937
  continue;
3812
3938
  }
3813
3939
  if (isRelation(attribute.type) && "target" in attribute) {
3814
3940
  const subAlias = createJoin(ctx, {
3815
- alias: alias || qb.alias,
3941
+ alias: alias2 || qb.alias,
3816
3942
  attributeName: key,
3817
3943
  attribute
3818
3944
  });
@@ -3827,7 +3953,7 @@ function processWhere(where, ctx) {
3827
3953
  }
3828
3954
  if (isScalar(attribute.type)) {
3829
3955
  const columnName = toColumnName(meta, key);
3830
- const aliasedColumnName = qb.aliasColumn(columnName, alias);
3956
+ const aliasedColumnName = qb.aliasColumn(columnName, alias2);
3831
3957
  filters[aliasedColumnName] = processAttributeWhere(attribute, value);
3832
3958
  continue;
3833
3959
  }
@@ -4055,7 +4181,7 @@ class ReadableStrapiQuery extends Readable {
4055
4181
  * Custom ._read() implementation
4056
4182
  *
4057
4183
  * NOTE: Here "size" means the number of entities to be read from the database.
4058
- * Not the actual byte size, as it would means that we need to return partial entities.
4184
+ * Not the actual byte size, as it would mean that we need to return partial entities.
4059
4185
  *
4060
4186
  */
4061
4187
  async _read(size) {
@@ -4118,61 +4244,6 @@ class ReadableStrapiQuery extends Readable {
4118
4244
  }
4119
4245
  }
4120
4246
  }
4121
- const storage = new AsyncLocalStorage();
4122
- const transactionCtx = {
4123
- async run(trx, cb) {
4124
- const store = storage.getStore();
4125
- return storage.run(
4126
- {
4127
- trx,
4128
- // Fill with existing callbacks if nesting transactions
4129
- commitCallbacks: store?.commitCallbacks || [],
4130
- rollbackCallbacks: store?.rollbackCallbacks || []
4131
- },
4132
- cb
4133
- );
4134
- },
4135
- get() {
4136
- const store = storage.getStore();
4137
- return store?.trx;
4138
- },
4139
- async commit(trx) {
4140
- const store = storage.getStore();
4141
- if (store?.trx) {
4142
- store.trx = null;
4143
- }
4144
- await trx.commit();
4145
- if (!store?.commitCallbacks.length) {
4146
- return;
4147
- }
4148
- store.commitCallbacks.forEach((cb) => cb());
4149
- store.commitCallbacks = [];
4150
- },
4151
- async rollback(trx) {
4152
- const store = storage.getStore();
4153
- if (store?.trx) {
4154
- store.trx = null;
4155
- }
4156
- await trx.rollback();
4157
- if (!store?.rollbackCallbacks.length) {
4158
- return;
4159
- }
4160
- store.rollbackCallbacks.forEach((cb) => cb());
4161
- store.rollbackCallbacks = [];
4162
- },
4163
- onCommit(cb) {
4164
- const store = storage.getStore();
4165
- if (store?.commitCallbacks) {
4166
- store.commitCallbacks.push(cb);
4167
- }
4168
- },
4169
- onRollback(cb) {
4170
- const store = storage.getStore();
4171
- if (store?.rollbackCallbacks) {
4172
- store.rollbackCallbacks.push(cb);
4173
- }
4174
- }
4175
- };
4176
4247
  const createQueryBuilder = (uid, db, initialState = {}) => {
4177
4248
  const meta = db.metadata.get(uid);
4178
4249
  const { tableName } = meta;
@@ -4205,9 +4276,9 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
4205
4276
  initialState
4206
4277
  );
4207
4278
  const getAlias = () => {
4208
- const alias = `t${state.aliasCounter}`;
4279
+ const alias2 = `t${state.aliasCounter}`;
4209
4280
  state.aliasCounter += 1;
4210
- return alias;
4281
+ return alias2;
4211
4282
  };
4212
4283
  return {
4213
4284
  alias: getAlias(),
@@ -4374,15 +4445,15 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
4374
4445
  mustUseAlias() {
4375
4446
  return ["select", "count"].includes(state.type);
4376
4447
  },
4377
- aliasColumn(key, alias) {
4448
+ aliasColumn(key, alias2) {
4378
4449
  if (typeof key !== "string") {
4379
4450
  return key;
4380
4451
  }
4381
4452
  if (key.indexOf(".") >= 0) {
4382
4453
  return key;
4383
4454
  }
4384
- if (!_.isNil(alias)) {
4385
- return `${alias}.${key}`;
4455
+ if (!_.isNil(alias2)) {
4456
+ return `${alias2}.${key}`;
4386
4457
  }
4387
4458
  return this.mustUseAlias() ? `${this.alias}.${key}` : key;
4388
4459
  },
@@ -4417,6 +4488,20 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
4417
4488
  shouldUseDistinct() {
4418
4489
  return state.joins.length > 0 && _.isEmpty(state.groupBy);
4419
4490
  },
4491
+ shouldUseDeepSort() {
4492
+ return state.orderBy.filter(({ column }) => column.indexOf(".") >= 0).filter(({ column }) => {
4493
+ const col = column.split(".");
4494
+ for (let i = 0; i < col.length - 1; i += 1) {
4495
+ const el = col[i];
4496
+ const isRelationAttribute = meta.attributes[el]?.type === "relation";
4497
+ const isAliasedRelation = Object.values(state.joins).map((join) => join.alias).includes(el);
4498
+ if (isRelationAttribute || isAliasedRelation) {
4499
+ return true;
4500
+ }
4501
+ }
4502
+ return false;
4503
+ }).length > 0;
4504
+ },
4420
4505
  processSelect() {
4421
4506
  state.select = state.select.map((field) => {
4422
4507
  if (isKnexQuery(field)) {
@@ -4534,6 +4619,9 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
4534
4619
  if (state.joins.length > 0) {
4535
4620
  applyJoins(qb, state.joins);
4536
4621
  }
4622
+ if (this.shouldUseDeepSort()) {
4623
+ return wrapWithDeepSort(qb, { qb: this, db, uid });
4624
+ }
4537
4625
  return qb;
4538
4626
  },
4539
4627
  async execute({ mapResults = true } = {}) {
@@ -4743,11 +4831,10 @@ const getDocumentSiblingIdsQuery = (tableName, id) => {
4743
4831
  if (!isContentType) {
4744
4832
  return [id];
4745
4833
  }
4746
- return (con) => {
4747
- con.from(tableName).select("id").where(
4748
- "document_id",
4749
- (con2) => con2.select("document_id").from(tableName).where("id", id)
4750
- );
4834
+ return function(query) {
4835
+ query.select("id").from(tableName).whereIn("document_id", (documentIDSubQuery) => {
4836
+ documentIDSubQuery.from(tableName).select("document_id").where("id", id);
4837
+ });
4751
4838
  };
4752
4839
  };
4753
4840
  const deletePreviousOneToAnyRelations = async ({
@@ -4765,11 +4852,7 @@ const deletePreviousOneToAnyRelations = async ({
4765
4852
  const { joinTable } = attribute;
4766
4853
  const { joinColumn, inverseJoinColumn } = joinTable;
4767
4854
  const con = db.getConnection();
4768
- await con.delete().from(joinTable.name).whereNotIn(
4769
- // @ts-expect-error - knex incorrectly expects a string array
4770
- joinColumn.name,
4771
- getDocumentSiblingIdsQuery(joinColumn.referencedTable, id)
4772
- ).whereIn(inverseJoinColumn.name, relIdsToadd).where(joinTable.on || {}).transacting(trx);
4855
+ await con.delete().from(joinTable.name).whereNotIn(joinColumn.name, getDocumentSiblingIdsQuery(joinColumn.referencedTable, id)).whereIn(inverseJoinColumn.name, relIdsToadd).where(joinTable.on || {}).transacting(trx);
4773
4856
  await cleanOrderColumns({ attribute, db, inverseRelIds: relIdsToadd, transaction: trx });
4774
4857
  };
4775
4858
  const deletePreviousAnyToOneRelations = async ({
@@ -4787,7 +4870,6 @@ const deletePreviousAnyToOneRelations = async ({
4787
4870
  }
4788
4871
  if (isManyToAny(attribute)) {
4789
4872
  const relsToDelete = await con.select(inverseJoinColumn.name).from(joinTable.name).where(joinColumn.name, id).whereNotIn(
4790
- // @ts-expect-error - knex incorrectly expects a string array
4791
4873
  inverseJoinColumn.name,
4792
4874
  getDocumentSiblingIdsQuery(inverseJoinColumn.referencedTable, relIdToadd)
4793
4875
  ).where(joinTable.on || {}).transacting(trx);
@@ -4799,7 +4881,6 @@ const deletePreviousAnyToOneRelations = async ({
4799
4881
  await cleanOrderColumns({ attribute, db, inverseRelIds: relIdsToDelete, transaction: trx });
4800
4882
  } else {
4801
4883
  await con.delete().from(joinTable.name).where(joinColumn.name, id).whereNotIn(
4802
- // @ts-expect-error - knex incorrectly expects a string array
4803
4884
  inverseJoinColumn.name,
4804
4885
  getDocumentSiblingIdsQuery(inverseJoinColumn.referencedTable, relIdToadd)
4805
4886
  ).where(joinTable.on || {}).transacting(trx);