@strapi/database 5.0.0-beta.9 → 5.0.0-rc.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.
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";
@@ -1087,7 +1087,7 @@ const createHelpers = (db) => {
1087
1087
  }
1088
1088
  }
1089
1089
  };
1090
- const dropIndex = (tableBuilder, index2) => {
1090
+ const dropIndex2 = (tableBuilder, index2) => {
1091
1091
  if (!db.config.settings?.forceMigration) {
1092
1092
  return;
1093
1093
  }
@@ -1164,13 +1164,13 @@ const createHelpers = (db) => {
1164
1164
  for (const removedIndex of table.indexes.removed) {
1165
1165
  if (!ignoreForeignKeyNames.includes(removedIndex.name)) {
1166
1166
  debug$2(`Dropping index ${removedIndex.name} on ${table.name}`);
1167
- dropIndex(tableBuilder, removedIndex);
1167
+ dropIndex2(tableBuilder, removedIndex);
1168
1168
  }
1169
1169
  }
1170
1170
  for (const updatedIndex of table.indexes.updated) {
1171
1171
  if (!ignoreForeignKeyNames.includes(updatedIndex.name)) {
1172
1172
  debug$2(`Dropping updated index ${updatedIndex.name} on ${table.name}`);
1173
- dropIndex(tableBuilder, updatedIndex.object);
1173
+ dropIndex2(tableBuilder, updatedIndex.object);
1174
1174
  }
1175
1175
  }
1176
1176
  for (const updatedColumn of table.columns.updated) {
@@ -1356,27 +1356,28 @@ const createSchemaDiff = (db) => {
1356
1356
  }
1357
1357
  };
1358
1358
  };
1359
- const diffTableColumns = (srcTable, destTable) => {
1359
+ const diffTableColumns = (diffCtx) => {
1360
+ const { databaseTable, userSchemaTable, previousTable } = diffCtx;
1360
1361
  const addedColumns = [];
1361
1362
  const updatedColumns = [];
1362
1363
  const unchangedColumns = [];
1363
1364
  const removedColumns = [];
1364
- for (const destColumn of destTable.columns) {
1365
- const srcColumn = helpers.findColumn(srcTable, destColumn.name);
1366
- if (srcColumn) {
1367
- const { status, diff } = diffColumns(srcColumn, destColumn);
1365
+ for (const userSchemaColumn of userSchemaTable.columns) {
1366
+ const databaseColumn = helpers.findColumn(databaseTable, userSchemaColumn.name);
1367
+ if (databaseColumn) {
1368
+ const { status, diff } = diffColumns(databaseColumn, userSchemaColumn);
1368
1369
  if (status === statuses.CHANGED) {
1369
1370
  updatedColumns.push(diff);
1370
1371
  } else {
1371
- unchangedColumns.push(srcColumn);
1372
+ unchangedColumns.push(databaseColumn);
1372
1373
  }
1373
1374
  } else {
1374
- addedColumns.push(destColumn);
1375
+ addedColumns.push(userSchemaColumn);
1375
1376
  }
1376
1377
  }
1377
- for (const srcColumn of srcTable.columns) {
1378
- if (!helpers.hasColumn(destTable, srcColumn.name)) {
1379
- removedColumns.push(srcColumn);
1378
+ for (const databaseColumn of databaseTable.columns) {
1379
+ if (!helpers.hasColumn(userSchemaTable, databaseColumn.name) && previousTable && helpers.hasColumn(previousTable, databaseColumn.name)) {
1380
+ removedColumns.push(databaseColumn);
1380
1381
  }
1381
1382
  }
1382
1383
  const hasChanged = [addedColumns, updatedColumns, removedColumns].some((arr) => arr.length > 0);
@@ -1390,27 +1391,28 @@ const createSchemaDiff = (db) => {
1390
1391
  }
1391
1392
  };
1392
1393
  };
1393
- const diffTableIndexes = (srcTable, destTable) => {
1394
+ const diffTableIndexes = (diffCtx) => {
1395
+ const { databaseTable, userSchemaTable, previousTable } = diffCtx;
1394
1396
  const addedIndexes = [];
1395
1397
  const updatedIndexes = [];
1396
1398
  const unchangedIndexes = [];
1397
1399
  const removedIndexes = [];
1398
- for (const destIndex of destTable.indexes) {
1399
- const srcIndex = helpers.findIndex(srcTable, destIndex.name);
1400
- if (srcIndex) {
1401
- const { status, diff } = diffIndexes(srcIndex, destIndex);
1400
+ for (const userSchemaIndex of userSchemaTable.indexes) {
1401
+ const databaseIndex = helpers.findIndex(databaseTable, userSchemaIndex.name);
1402
+ if (databaseIndex) {
1403
+ const { status, diff } = diffIndexes(databaseIndex, userSchemaIndex);
1402
1404
  if (status === statuses.CHANGED) {
1403
1405
  updatedIndexes.push(diff);
1404
1406
  } else {
1405
- unchangedIndexes.push(srcIndex);
1407
+ unchangedIndexes.push(databaseIndex);
1406
1408
  }
1407
1409
  } else {
1408
- addedIndexes.push(destIndex);
1410
+ addedIndexes.push(userSchemaIndex);
1409
1411
  }
1410
1412
  }
1411
- for (const srcIndex of srcTable.indexes) {
1412
- if (!helpers.hasIndex(destTable, srcIndex.name)) {
1413
- removedIndexes.push(srcIndex);
1413
+ for (const databaseIndex of databaseTable.indexes) {
1414
+ if (!helpers.hasIndex(userSchemaTable, databaseIndex.name) && previousTable && helpers.hasIndex(previousTable, databaseIndex.name)) {
1415
+ removedIndexes.push(databaseIndex);
1414
1416
  }
1415
1417
  }
1416
1418
  const hasChanged = [addedIndexes, updatedIndexes, removedIndexes].some((arr) => arr.length > 0);
@@ -1424,7 +1426,8 @@ const createSchemaDiff = (db) => {
1424
1426
  }
1425
1427
  };
1426
1428
  };
1427
- const diffTableForeignKeys = (srcTable, destTable) => {
1429
+ const diffTableForeignKeys = (diffCtx) => {
1430
+ const { databaseTable, userSchemaTable, previousTable } = diffCtx;
1428
1431
  const addedForeignKeys = [];
1429
1432
  const updatedForeignKeys = [];
1430
1433
  const unchangedForeignKeys = [];
@@ -1440,22 +1443,22 @@ const createSchemaDiff = (db) => {
1440
1443
  }
1441
1444
  };
1442
1445
  }
1443
- for (const destForeignKey of destTable.foreignKeys) {
1444
- const srcForeignKey = helpers.findForeignKey(srcTable, destForeignKey.name);
1445
- if (srcForeignKey) {
1446
- const { status, diff } = diffForeignKeys(srcForeignKey, destForeignKey);
1446
+ for (const userSchemaForeignKeys of userSchemaTable.foreignKeys) {
1447
+ const databaseForeignKeys = helpers.findForeignKey(databaseTable, userSchemaForeignKeys.name);
1448
+ if (databaseForeignKeys) {
1449
+ const { status, diff } = diffForeignKeys(databaseForeignKeys, userSchemaForeignKeys);
1447
1450
  if (status === statuses.CHANGED) {
1448
1451
  updatedForeignKeys.push(diff);
1449
1452
  } else {
1450
- unchangedForeignKeys.push(srcForeignKey);
1453
+ unchangedForeignKeys.push(databaseForeignKeys);
1451
1454
  }
1452
1455
  } else {
1453
- addedForeignKeys.push(destForeignKey);
1456
+ addedForeignKeys.push(userSchemaForeignKeys);
1454
1457
  }
1455
1458
  }
1456
- for (const srcForeignKey of srcTable.foreignKeys) {
1457
- if (!helpers.hasForeignKey(destTable, srcForeignKey.name)) {
1458
- removedForeignKeys.push(srcForeignKey);
1459
+ for (const databaseForeignKeys of databaseTable.foreignKeys) {
1460
+ if (!helpers.hasForeignKey(userSchemaTable, databaseForeignKeys.name) && previousTable && helpers.hasForeignKey(previousTable, databaseForeignKeys.name)) {
1461
+ removedForeignKeys.push(databaseForeignKeys);
1459
1462
  }
1460
1463
  }
1461
1464
  const hasChanged = [addedForeignKeys, updatedForeignKeys, removedForeignKeys].some(
@@ -1471,37 +1474,44 @@ const createSchemaDiff = (db) => {
1471
1474
  }
1472
1475
  };
1473
1476
  };
1474
- const diffTables = (srcTable, destTable) => {
1475
- const columnsDiff = diffTableColumns(srcTable, destTable);
1476
- const indexesDiff = diffTableIndexes(srcTable, destTable);
1477
- const foreignKeysDiff = diffTableForeignKeys(srcTable, destTable);
1477
+ const diffTables = (diffCtx) => {
1478
+ const { databaseTable } = diffCtx;
1479
+ const columnsDiff = diffTableColumns(diffCtx);
1480
+ const indexesDiff = diffTableIndexes(diffCtx);
1481
+ const foreignKeysDiff = diffTableForeignKeys(diffCtx);
1478
1482
  const hasChanged = [columnsDiff, indexesDiff, foreignKeysDiff].some(hasChangedStatus);
1479
1483
  return {
1480
1484
  status: hasChanged ? statuses.CHANGED : statuses.UNCHANGED,
1481
1485
  diff: {
1482
- name: srcTable.name,
1486
+ name: databaseTable.name,
1483
1487
  indexes: indexesDiff.diff,
1484
1488
  foreignKeys: foreignKeysDiff.diff,
1485
1489
  columns: columnsDiff.diff
1486
1490
  }
1487
1491
  };
1488
1492
  };
1489
- const diffSchemas = async (srcSchema, destSchema) => {
1493
+ const diffSchemas = async (schemaDiffCtx) => {
1494
+ const { previousSchema, databaseSchema, userSchema } = schemaDiffCtx;
1490
1495
  const addedTables = [];
1491
1496
  const updatedTables = [];
1492
1497
  const unchangedTables = [];
1493
1498
  const removedTables = [];
1494
- for (const destTable of destSchema.tables) {
1495
- const srcTable = helpers.findTable(srcSchema, destTable.name);
1496
- if (srcTable) {
1497
- const { status, diff } = diffTables(srcTable, destTable);
1499
+ for (const userSchemaTable of userSchema.tables) {
1500
+ const databaseTable = helpers.findTable(databaseSchema, userSchemaTable.name);
1501
+ const previousTable = previousSchema && helpers.findTable(previousSchema, userSchemaTable.name);
1502
+ if (databaseTable) {
1503
+ const { status, diff } = diffTables({
1504
+ previousTable,
1505
+ databaseTable,
1506
+ userSchemaTable
1507
+ });
1498
1508
  if (status === statuses.CHANGED) {
1499
1509
  updatedTables.push(diff);
1500
1510
  } else {
1501
- unchangedTables.push(srcTable);
1511
+ unchangedTables.push(databaseTable);
1502
1512
  }
1503
1513
  } else {
1504
- addedTables.push(destTable);
1514
+ addedTables.push(userSchemaTable);
1505
1515
  }
1506
1516
  }
1507
1517
  const parsePersistedTable = (persistedTable) => {
@@ -1510,23 +1520,34 @@ const createSchemaDiff = (db) => {
1510
1520
  }
1511
1521
  return persistedTable.name;
1512
1522
  };
1513
- const persistedTables = helpers.hasTable(srcSchema, "strapi_core_store_settings") ? await strapi.store.get({
1514
- type: "core",
1515
- key: "persisted_tables"
1516
- }) ?? [] : [];
1523
+ const persistedTables = helpers.hasTable(databaseSchema, "strapi_core_store_settings") ? (
1524
+ // TODO: replace with low level db query instead
1525
+ await strapi.store.get({
1526
+ type: "core",
1527
+ key: "persisted_tables"
1528
+ }) ?? []
1529
+ ) : [];
1517
1530
  const reservedTables = [...RESERVED_TABLE_NAMES, ...persistedTables.map(parsePersistedTable)];
1518
- for (const srcTable of srcSchema.tables) {
1519
- if (!helpers.hasTable(destSchema, srcTable.name) && !reservedTables.includes(srcTable.name)) {
1531
+ for (const databaseTable of databaseSchema.tables) {
1532
+ const isInUserSchema = helpers.hasTable(userSchema, databaseTable.name);
1533
+ const wasTracked = previousSchema && helpers.hasTable(previousSchema, databaseTable.name);
1534
+ const isReserved = reservedTables.includes(databaseTable.name);
1535
+ if (!isInUserSchema && !wasTracked) {
1536
+ continue;
1537
+ }
1538
+ if (!isInUserSchema && wasTracked && !isReserved) {
1520
1539
  const dependencies = persistedTables.filter((table) => {
1521
1540
  const dependsOn = table?.dependsOn;
1522
1541
  if (!_.isArray(dependsOn)) {
1523
1542
  return;
1524
1543
  }
1525
- return dependsOn.some((table2) => table2.name === srcTable.name);
1544
+ return dependsOn.some((table2) => table2.name === databaseTable.name);
1526
1545
  }).map((dependsOnTable) => {
1527
- return srcSchema.tables.find((srcTable2) => srcTable2.name === dependsOnTable.name);
1546
+ return databaseSchema.tables.find(
1547
+ (databaseTable2) => databaseTable2.name === dependsOnTable.name
1548
+ );
1528
1549
  }).filter((table) => !_.isNil(table));
1529
- removedTables.push(srcTable, ...dependencies);
1550
+ removedTables.push(databaseTable, ...dependencies);
1530
1551
  }
1531
1552
  }
1532
1553
  const hasChanged = [addedTables, updatedTables, removedTables].some((arr) => arr.length > 0);
@@ -2172,8 +2193,13 @@ const createSchemaProvider = (db) => {
2172
2193
  },
2173
2194
  async syncSchema() {
2174
2195
  debug$1("Synchronizing database schema");
2175
- const DBSchema = await db.dialect.schemaInspector.getSchema();
2176
- const { status, diff } = await this.schemaDiff.diff(DBSchema, this.schema);
2196
+ const databaseSchema = await db.dialect.schemaInspector.getSchema();
2197
+ const storedSchema = await this.schemaStorage.read();
2198
+ const { status, diff } = await this.schemaDiff.diff({
2199
+ previousSchema: storedSchema?.schema,
2200
+ databaseSchema,
2201
+ userSchema: this.schema
2202
+ });
2177
2203
  if (status === "CHANGED") {
2178
2204
  await this.builder.updateSchema(diff);
2179
2205
  }
@@ -2895,6 +2921,68 @@ const createField = (attribute) => {
2895
2921
  }
2896
2922
  throw new Error(`Undefined field for type ${type}`);
2897
2923
  };
2924
+ const storage = new AsyncLocalStorage();
2925
+ const transactionCtx = {
2926
+ async run(trx, cb) {
2927
+ const store = storage.getStore();
2928
+ return storage.run(
2929
+ {
2930
+ trx,
2931
+ // Fill with existing callbacks if nesting transactions
2932
+ commitCallbacks: store?.commitCallbacks || [],
2933
+ rollbackCallbacks: store?.rollbackCallbacks || []
2934
+ },
2935
+ cb
2936
+ );
2937
+ },
2938
+ get() {
2939
+ const store = storage.getStore();
2940
+ return store?.trx;
2941
+ },
2942
+ async commit(trx) {
2943
+ const store = storage.getStore();
2944
+ if (store?.trx) {
2945
+ store.trx = null;
2946
+ }
2947
+ await trx.commit();
2948
+ if (!store?.commitCallbacks.length) {
2949
+ return;
2950
+ }
2951
+ store.commitCallbacks.forEach((cb) => cb());
2952
+ store.commitCallbacks = [];
2953
+ },
2954
+ async rollback(trx) {
2955
+ const store = storage.getStore();
2956
+ if (store?.trx) {
2957
+ store.trx = null;
2958
+ }
2959
+ await trx.rollback();
2960
+ if (!store?.rollbackCallbacks.length) {
2961
+ return;
2962
+ }
2963
+ store.rollbackCallbacks.forEach((cb) => cb());
2964
+ store.rollbackCallbacks = [];
2965
+ },
2966
+ onCommit(cb) {
2967
+ const store = storage.getStore();
2968
+ if (store?.commitCallbacks) {
2969
+ store.commitCallbacks.push(cb);
2970
+ }
2971
+ },
2972
+ onRollback(cb) {
2973
+ const store = storage.getStore();
2974
+ if (store?.rollbackCallbacks) {
2975
+ store.rollbackCallbacks.push(cb);
2976
+ }
2977
+ }
2978
+ };
2979
+ function isKnexQuery(value) {
2980
+ return value instanceof KnexBuilder || value instanceof KnexRaw;
2981
+ }
2982
+ const addSchema = (db, tableName) => {
2983
+ const schemaName = db.getSchemaName();
2984
+ return schemaName ? `${schemaName}.${tableName}` : tableName;
2985
+ };
2898
2986
  const fromSingleRow = (meta, row) => {
2899
2987
  const { attributes } = meta;
2900
2988
  if (_.isNil(row)) {
@@ -3017,7 +3105,7 @@ const escapeQuery = (query, charsToEscape, escapeChar = "\\") => {
3017
3105
  ""
3018
3106
  );
3019
3107
  };
3020
- const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
3108
+ const createPivotJoin = (ctx, { alias: alias2, refAlias, joinTable, targetMeta }) => {
3021
3109
  const { qb } = ctx;
3022
3110
  const joinAlias = qb.getAlias();
3023
3111
  qb.join({
@@ -3025,7 +3113,7 @@ const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
3025
3113
  referencedTable: joinTable.name,
3026
3114
  referencedColumn: joinTable.joinColumn.name,
3027
3115
  rootColumn: joinTable.joinColumn.referencedColumn,
3028
- rootTable: alias,
3116
+ rootTable: alias2,
3029
3117
  on: joinTable.on
3030
3118
  });
3031
3119
  const subAlias = refAlias || qb.getAlias();
@@ -3038,7 +3126,7 @@ const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
3038
3126
  });
3039
3127
  return subAlias;
3040
3128
  };
3041
- const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3129
+ const createJoin = (ctx, { alias: alias2, refAlias, attributeName, attribute }) => {
3042
3130
  const { db, qb, uid } = ctx;
3043
3131
  if (attribute.type !== "relation") {
3044
3132
  throw new Error(`Cannot join on non relational field ${attributeName}`);
@@ -3054,7 +3142,7 @@ const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3054
3142
  referencedTable: targetMeta.tableName,
3055
3143
  referencedColumn: morphColumn.idColumn.name,
3056
3144
  rootColumn: morphColumn.idColumn.referencedColumn,
3057
- rootTable: alias,
3145
+ rootTable: alias2,
3058
3146
  on: {
3059
3147
  [morphColumn.typeColumn.name]: uid,
3060
3148
  ...morphColumn.on
@@ -3069,7 +3157,7 @@ const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3069
3157
  referencedTable: joinTable2.name,
3070
3158
  referencedColumn: joinTable2.morphColumn.idColumn.name,
3071
3159
  rootColumn: joinTable2.morphColumn.idColumn.referencedColumn,
3072
- rootTable: alias,
3160
+ rootTable: alias2,
3073
3161
  on: {
3074
3162
  [joinTable2.morphColumn.typeColumn.name]: uid,
3075
3163
  field: attributeName
@@ -3085,7 +3173,7 @@ const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3085
3173
  });
3086
3174
  return subAlias;
3087
3175
  }
3088
- return alias;
3176
+ return alias2;
3089
3177
  }
3090
3178
  const { joinColumn } = attribute;
3091
3179
  if (joinColumn) {
@@ -3095,20 +3183,20 @@ const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
3095
3183
  referencedTable: targetMeta.tableName,
3096
3184
  referencedColumn: joinColumn.referencedColumn,
3097
3185
  rootColumn: joinColumn.name,
3098
- rootTable: alias
3186
+ rootTable: alias2
3099
3187
  });
3100
3188
  return subAlias;
3101
3189
  }
3102
3190
  const { joinTable } = attribute;
3103
3191
  if (joinTable) {
3104
- return createPivotJoin(ctx, { alias, refAlias, joinTable, targetMeta });
3192
+ return createPivotJoin(ctx, { alias: alias2, refAlias, joinTable, targetMeta });
3105
3193
  }
3106
- return alias;
3194
+ return alias2;
3107
3195
  };
3108
3196
  const applyJoin = (qb, join) => {
3109
3197
  const {
3110
3198
  method = "leftJoin",
3111
- alias,
3199
+ alias: alias2,
3112
3200
  referencedTable,
3113
3201
  referencedColumn,
3114
3202
  rootColumn,
@@ -3118,26 +3206,28 @@ const applyJoin = (qb, join) => {
3118
3206
  on,
3119
3207
  orderBy
3120
3208
  } = join;
3121
- qb[method](`${referencedTable} as ${alias}`, (inner) => {
3122
- inner.on(`${rootTable}.${rootColumn}`, `${alias}.${referencedColumn}`);
3209
+ qb[method](`${referencedTable} as ${alias2}`, (inner) => {
3210
+ inner.on(`${rootTable}.${rootColumn}`, `${alias2}.${referencedColumn}`);
3123
3211
  if (on) {
3124
3212
  for (const key of Object.keys(on)) {
3125
- inner.onVal(`${alias}.${key}`, on[key]);
3213
+ inner.onVal(`${alias2}.${key}`, on[key]);
3126
3214
  }
3127
3215
  }
3128
3216
  });
3129
3217
  if (orderBy) {
3130
3218
  Object.keys(orderBy).forEach((column) => {
3131
3219
  const direction = orderBy[column];
3132
- qb.orderBy(`${alias}.${column}`, direction);
3220
+ qb.orderBy(`${alias2}.${column}`, direction);
3133
3221
  });
3134
3222
  }
3135
3223
  };
3136
3224
  const applyJoins = (qb, joins) => {
3137
3225
  return joins.forEach((join) => applyJoin(qb, join));
3138
3226
  };
3227
+ const COL_STRAPI_ROW_NUMBER = "__strapi_row_number";
3228
+ const COL_STRAPI_ORDER_BY_PREFIX = "__strapi_order_by";
3139
3229
  const processOrderBy = (orderBy, ctx) => {
3140
- const { db, uid, qb, alias } = ctx;
3230
+ const { db, uid, qb, alias: alias2 } = ctx;
3141
3231
  const meta = db.metadata.get(uid);
3142
3232
  const { attributes } = meta;
3143
3233
  if (typeof orderBy === "string") {
@@ -3146,7 +3236,7 @@ const processOrderBy = (orderBy, ctx) => {
3146
3236
  throw new Error(`Attribute ${orderBy} not found on model ${uid}`);
3147
3237
  }
3148
3238
  const columnName = toColumnName(meta, orderBy);
3149
- return [{ column: qb.aliasColumn(columnName, alias) }];
3239
+ return [{ column: qb.aliasColumn(columnName, alias2) }];
3150
3240
  }
3151
3241
  if (Array.isArray(orderBy)) {
3152
3242
  return orderBy.flatMap((value) => processOrderBy(value, ctx));
@@ -3160,11 +3250,11 @@ const processOrderBy = (orderBy, ctx) => {
3160
3250
  }
3161
3251
  if (isScalar(attribute.type)) {
3162
3252
  const columnName = toColumnName(meta, key);
3163
- return { column: qb.aliasColumn(columnName, alias), order: direction };
3253
+ return { column: qb.aliasColumn(columnName, alias2), order: direction };
3164
3254
  }
3165
3255
  if (attribute.type === "relation" && "target" in attribute) {
3166
3256
  const subAlias = createJoin(ctx, {
3167
- alias: alias || qb.alias,
3257
+ alias: alias2 || qb.alias,
3168
3258
  attributeName: key,
3169
3259
  attribute
3170
3260
  });
@@ -3180,6 +3270,75 @@ const processOrderBy = (orderBy, ctx) => {
3180
3270
  }
3181
3271
  throw new Error("Invalid orderBy syntax");
3182
3272
  };
3273
+ const getStrapiOrderColumnAlias = (column) => {
3274
+ const trimmedColumnName = column.replaceAll(".", "_");
3275
+ return `${COL_STRAPI_ORDER_BY_PREFIX}__${trimmedColumnName}`;
3276
+ };
3277
+ const wrapWithDeepSort = (originalQuery, ctx) => {
3278
+ const { db, qb, uid } = ctx;
3279
+ const { tableName } = db.metadata.get(uid);
3280
+ const orderBy = _.cloneDeep(qb.state.orderBy);
3281
+ const resultQueryAlias = qb.getAlias();
3282
+ const aliasedTableName = qb.mustUseAlias() ? alias(resultQueryAlias, tableName) : tableName;
3283
+ const resultQuery = db.getConnection(aliasedTableName);
3284
+ const baseQuery = originalQuery.clone();
3285
+ const baseQueryAlias = qb.getAlias();
3286
+ baseQuery.clear("select").clear("order").clear("limit").clear("offset");
3287
+ baseQuery.select(
3288
+ // Always select the row id for future manipulation
3289
+ prefix(qb.alias, "id"),
3290
+ ...orderBy.map(
3291
+ (orderByClause) => alias(getStrapiOrderColumnAlias(orderByClause.column), orderByClause.column)
3292
+ )
3293
+ );
3294
+ const partitionedQueryAlias = qb.getAlias();
3295
+ const selectRowsAsNumberedPartitions = (partitionedQuery) => {
3296
+ const prefixedOrderBy = orderBy.map((orderByClause) => ({
3297
+ column: prefix(baseQueryAlias, getStrapiOrderColumnAlias(orderByClause.column)),
3298
+ order: orderByClause.order
3299
+ }));
3300
+ const orderByColumns = prefixedOrderBy.map(_.prop("column"));
3301
+ partitionedQuery.select(
3302
+ // Always select baseQuery.id
3303
+ prefix(baseQueryAlias, "id"),
3304
+ ...orderByColumns
3305
+ ).rowNumber(COL_STRAPI_ROW_NUMBER, (subQuery) => {
3306
+ for (const orderByClause of prefixedOrderBy) {
3307
+ subQuery.orderBy(orderByClause.column, orderByClause.order, "last");
3308
+ }
3309
+ subQuery.partitionBy(`${baseQueryAlias}.id`);
3310
+ }).from(baseQuery.as(baseQueryAlias)).as(partitionedQueryAlias);
3311
+ };
3312
+ const originalSelect = _.difference(
3313
+ qb.state.select,
3314
+ // Remove order by columns from the initial select
3315
+ qb.state.orderBy.map(_.prop("column"))
3316
+ ).map(prefix(resultQueryAlias));
3317
+ resultQuery.select(originalSelect).innerJoin(selectRowsAsNumberedPartitions, function() {
3318
+ this.on(`${partitionedQueryAlias}.id`, `${resultQueryAlias}.id`).andOnVal(`${partitionedQueryAlias}.${COL_STRAPI_ROW_NUMBER}`, "=", 1);
3319
+ });
3320
+ if (qb.state.limit) {
3321
+ resultQuery.limit(qb.state.limit);
3322
+ }
3323
+ if (qb.state.offset) {
3324
+ resultQuery.offset(qb.state.offset);
3325
+ }
3326
+ if (qb.state.first) {
3327
+ resultQuery.first();
3328
+ }
3329
+ resultQuery.orderBy([
3330
+ // Transform "order by" clause to their T alias and prefix them with T alias
3331
+ ...orderBy.map((orderByClause) => ({
3332
+ column: prefix(partitionedQueryAlias, getStrapiOrderColumnAlias(orderByClause.column)),
3333
+ order: orderByClause.order
3334
+ })),
3335
+ // Add T.id to the order by clause to get consistent results in case several rows have the exact same order
3336
+ { column: `${partitionedQueryAlias}.id`, order: "asc" }
3337
+ ]);
3338
+ return resultQuery;
3339
+ };
3340
+ const alias = _.curry((alias2, value) => `${value} as ${alias2}`);
3341
+ const prefix = _.curry((prefix2, value) => `${prefix2}.${value}`);
3183
3342
  const joinColPrefix = "__strapi";
3184
3343
  const XtoOne = async (input, ctx) => {
3185
3344
  const { attribute, attributeName, results, populateValue, targetMeta, isCount } = input;
@@ -3207,8 +3366,8 @@ const XtoOne = async (input, ctx) => {
3207
3366
  const { joinTable } = attribute;
3208
3367
  const qb2 = db.entityManager.createQueryBuilder(targetMeta.uid);
3209
3368
  const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
3210
- const alias = qb2.getAlias();
3211
- const joinColAlias = `${alias}.${joinColumnName}`;
3369
+ const alias2 = qb2.getAlias();
3370
+ const joinColAlias = `${alias2}.${joinColumnName}`;
3212
3371
  const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
3213
3372
  const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
3214
3373
  const referencedValues = _.uniq(
@@ -3222,7 +3381,7 @@ const XtoOne = async (input, ctx) => {
3222
3381
  return;
3223
3382
  }
3224
3383
  const rows2 = await qb2.init(populateValue).join({
3225
- alias,
3384
+ alias: alias2,
3226
3385
  referencedTable: joinTable.name,
3227
3386
  referencedColumn: joinTable.inverseJoinColumn.name,
3228
3387
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3248,7 +3407,7 @@ const XtoOne = async (input, ctx) => {
3248
3407
  return;
3249
3408
  }
3250
3409
  const rows = await qb2.init(populateValue).join({
3251
- alias,
3410
+ alias: alias2,
3252
3411
  referencedTable: joinTable.name,
3253
3412
  referencedColumn: joinTable.inverseJoinColumn.name,
3254
3413
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3288,8 +3447,8 @@ const oneToMany = async (input, ctx) => {
3288
3447
  const { joinTable } = attribute;
3289
3448
  const qb2 = db.entityManager.createQueryBuilder(targetMeta.uid);
3290
3449
  const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
3291
- const alias = qb2.getAlias();
3292
- const joinColAlias = `${alias}.${joinColumnName}`;
3450
+ const alias2 = qb2.getAlias();
3451
+ const joinColAlias = `${alias2}.${joinColumnName}`;
3293
3452
  const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
3294
3453
  const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
3295
3454
  const referencedValues = _.uniq(
@@ -3303,7 +3462,7 @@ const oneToMany = async (input, ctx) => {
3303
3462
  return;
3304
3463
  }
3305
3464
  const rows2 = await qb2.init(populateValue).join({
3306
- alias,
3465
+ alias: alias2,
3307
3466
  referencedTable: joinTable.name,
3308
3467
  referencedColumn: joinTable.inverseJoinColumn.name,
3309
3468
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3329,7 +3488,7 @@ const oneToMany = async (input, ctx) => {
3329
3488
  return;
3330
3489
  }
3331
3490
  const rows = await qb2.init(populateValue).join({
3332
- alias,
3491
+ alias: alias2,
3333
3492
  referencedTable: joinTable.name,
3334
3493
  referencedColumn: joinTable.inverseJoinColumn.name,
3335
3494
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3350,8 +3509,8 @@ const manyToMany = async (input, ctx) => {
3350
3509
  const { joinTable } = attribute;
3351
3510
  const populateQb = db.entityManager.createQueryBuilder(targetMeta.uid);
3352
3511
  const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
3353
- const alias = populateQb.getAlias();
3354
- const joinColAlias = `${alias}.${joinColumnName}`;
3512
+ const alias2 = populateQb.getAlias();
3513
+ const joinColAlias = `${alias2}.${joinColumnName}`;
3355
3514
  const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
3356
3515
  const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
3357
3516
  const referencedValues = _.uniq(
@@ -3365,7 +3524,7 @@ const manyToMany = async (input, ctx) => {
3365
3524
  return;
3366
3525
  }
3367
3526
  const rows2 = await populateQb.init(populateValue).join({
3368
- alias,
3527
+ alias: alias2,
3369
3528
  referencedTable: joinTable.name,
3370
3529
  referencedColumn: joinTable.inverseJoinColumn.name,
3371
3530
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3391,7 +3550,7 @@ const manyToMany = async (input, ctx) => {
3391
3550
  return;
3392
3551
  }
3393
3552
  const rows = await populateQb.init(populateValue).join({
3394
- alias,
3553
+ alias: alias2,
3395
3554
  referencedTable: joinTable.name,
3396
3555
  referencedColumn: joinTable.inverseJoinColumn.name,
3397
3556
  rootColumn: joinTable.inverseJoinColumn.referencedColumn,
@@ -3442,9 +3601,9 @@ const morphX = async (input, ctx) => {
3442
3601
  return;
3443
3602
  }
3444
3603
  const qb = db.entityManager.createQueryBuilder(target);
3445
- const alias = qb.getAlias();
3604
+ const alias2 = qb.getAlias();
3446
3605
  const rows = await qb.init(populateValue).join({
3447
- alias,
3606
+ alias: alias2,
3448
3607
  referencedTable: joinTable.name,
3449
3608
  referencedColumn: joinColumn.name,
3450
3609
  rootColumn: joinColumn.referencedColumn,
@@ -3454,9 +3613,9 @@ const morphX = async (input, ctx) => {
3454
3613
  field: attributeName
3455
3614
  },
3456
3615
  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
3616
+ }).addSelect([`${alias2}.${idColumn.name}`, `${alias2}.${typeColumn.name}`]).where({
3617
+ [`${alias2}.${idColumn.name}`]: referencedValues,
3618
+ [`${alias2}.${typeColumn.name}`]: uid
3460
3619
  }).execute({ mapResults: false });
3461
3620
  const map2 = _.groupBy(idColumn.name)(rows);
3462
3621
  results.forEach((result) => {
@@ -3705,13 +3864,6 @@ const processPopulate = (populate, ctx) => {
3705
3864
  }
3706
3865
  return finalPopulate;
3707
3866
  };
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
3867
  const isRecord$1 = (value) => isPlainObject(value);
3716
3868
  const castValue = (value, attribute) => {
3717
3869
  if (!attribute) {
@@ -3753,8 +3905,8 @@ const processNested = (where, ctx) => {
3753
3905
  return processWhere(where, ctx);
3754
3906
  };
3755
3907
  const processRelationWhere = (where, ctx) => {
3756
- const { qb, alias } = ctx;
3757
- const idAlias = qb.aliasColumn("id", alias);
3908
+ const { qb, alias: alias2 } = ctx;
3909
+ const idAlias = qb.aliasColumn("id", alias2);
3758
3910
  if (!isRecord$1(where)) {
3759
3911
  return { [idAlias]: where };
3760
3912
  }
@@ -3784,7 +3936,7 @@ function processWhere(where, ctx) {
3784
3936
  if (isArray(where)) {
3785
3937
  return where.map((sub) => processWhere(sub, ctx));
3786
3938
  }
3787
- const { db, uid, qb, alias } = ctx;
3939
+ const { db, uid, qb, alias: alias2 } = ctx;
3788
3940
  const meta = db.metadata.get(uid);
3789
3941
  const filters = {};
3790
3942
  for (const key of Object.keys(where)) {
@@ -3807,12 +3959,12 @@ function processWhere(where, ctx) {
3807
3959
  }
3808
3960
  const attribute = meta.attributes[key];
3809
3961
  if (!attribute) {
3810
- filters[qb.aliasColumn(key, alias)] = processAttributeWhere(null, value);
3962
+ filters[qb.aliasColumn(key, alias2)] = processAttributeWhere(null, value);
3811
3963
  continue;
3812
3964
  }
3813
3965
  if (isRelation(attribute.type) && "target" in attribute) {
3814
3966
  const subAlias = createJoin(ctx, {
3815
- alias: alias || qb.alias,
3967
+ alias: alias2 || qb.alias,
3816
3968
  attributeName: key,
3817
3969
  attribute
3818
3970
  });
@@ -3827,7 +3979,7 @@ function processWhere(where, ctx) {
3827
3979
  }
3828
3980
  if (isScalar(attribute.type)) {
3829
3981
  const columnName = toColumnName(meta, key);
3830
- const aliasedColumnName = qb.aliasColumn(columnName, alias);
3982
+ const aliasedColumnName = qb.aliasColumn(columnName, alias2);
3831
3983
  filters[aliasedColumnName] = processAttributeWhere(attribute, value);
3832
3984
  continue;
3833
3985
  }
@@ -4055,7 +4207,7 @@ class ReadableStrapiQuery extends Readable {
4055
4207
  * Custom ._read() implementation
4056
4208
  *
4057
4209
  * 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.
4210
+ * Not the actual byte size, as it would mean that we need to return partial entities.
4059
4211
  *
4060
4212
  */
4061
4213
  async _read(size) {
@@ -4118,61 +4270,6 @@ class ReadableStrapiQuery extends Readable {
4118
4270
  }
4119
4271
  }
4120
4272
  }
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
4273
  const createQueryBuilder = (uid, db, initialState = {}) => {
4177
4274
  const meta = db.metadata.get(uid);
4178
4275
  const { tableName } = meta;
@@ -4205,9 +4302,9 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
4205
4302
  initialState
4206
4303
  );
4207
4304
  const getAlias = () => {
4208
- const alias = `t${state.aliasCounter}`;
4305
+ const alias2 = `t${state.aliasCounter}`;
4209
4306
  state.aliasCounter += 1;
4210
- return alias;
4307
+ return alias2;
4211
4308
  };
4212
4309
  return {
4213
4310
  alias: getAlias(),
@@ -4374,15 +4471,15 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
4374
4471
  mustUseAlias() {
4375
4472
  return ["select", "count"].includes(state.type);
4376
4473
  },
4377
- aliasColumn(key, alias) {
4474
+ aliasColumn(key, alias2) {
4378
4475
  if (typeof key !== "string") {
4379
4476
  return key;
4380
4477
  }
4381
4478
  if (key.indexOf(".") >= 0) {
4382
4479
  return key;
4383
4480
  }
4384
- if (!_.isNil(alias)) {
4385
- return `${alias}.${key}`;
4481
+ if (!_.isNil(alias2)) {
4482
+ return `${alias2}.${key}`;
4386
4483
  }
4387
4484
  return this.mustUseAlias() ? `${this.alias}.${key}` : key;
4388
4485
  },
@@ -4417,6 +4514,20 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
4417
4514
  shouldUseDistinct() {
4418
4515
  return state.joins.length > 0 && _.isEmpty(state.groupBy);
4419
4516
  },
4517
+ shouldUseDeepSort() {
4518
+ return state.orderBy.filter(({ column }) => column.indexOf(".") >= 0).filter(({ column }) => {
4519
+ const col = column.split(".");
4520
+ for (let i = 0; i < col.length - 1; i += 1) {
4521
+ const el = col[i];
4522
+ const isRelationAttribute = meta.attributes[el]?.type === "relation";
4523
+ const isAliasedRelation = Object.values(state.joins).map((join) => join.alias).includes(el);
4524
+ if (isRelationAttribute || isAliasedRelation) {
4525
+ return true;
4526
+ }
4527
+ }
4528
+ return false;
4529
+ }).length > 0;
4530
+ },
4420
4531
  processSelect() {
4421
4532
  state.select = state.select.map((field) => {
4422
4533
  if (isKnexQuery(field)) {
@@ -4534,6 +4645,9 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
4534
4645
  if (state.joins.length > 0) {
4535
4646
  applyJoins(qb, state.joins);
4536
4647
  }
4648
+ if (this.shouldUseDeepSort()) {
4649
+ return wrapWithDeepSort(qb, { qb: this, db, uid });
4650
+ }
4537
4651
  return qb;
4538
4652
  },
4539
4653
  async execute({ mapResults = true } = {}) {
@@ -4735,7 +4849,7 @@ const deleteRelatedMorphOneRelationsAfterMorphToManyUpdate = async (rows, {
4735
4849
  await createQueryBuilder(joinTable.name, db).delete().where({ $or: orWhere }).transacting(trx).execute();
4736
4850
  }
4737
4851
  };
4738
- const getDocumentSiblingIdsQuery = (con, tableName, id) => {
4852
+ const getDocumentSiblingIdsQuery = (tableName, id) => {
4739
4853
  const models = Array.from(strapi.db.metadata.values());
4740
4854
  const isContentType = models.find((model) => {
4741
4855
  return model.tableName === tableName && model.attributes.documentId;
@@ -4743,10 +4857,11 @@ const getDocumentSiblingIdsQuery = (con, tableName, id) => {
4743
4857
  if (!isContentType) {
4744
4858
  return [id];
4745
4859
  }
4746
- return con.from(tableName).select("id").where(
4747
- "document_id",
4748
- con.from(tableName).select("document_id").where("id", id)
4749
- );
4860
+ return function(query) {
4861
+ query.select("id").from(tableName).whereIn("document_id", (documentIDSubQuery) => {
4862
+ documentIDSubQuery.from(tableName).select("document_id").where("id", id);
4863
+ });
4864
+ };
4750
4865
  };
4751
4866
  const deletePreviousOneToAnyRelations = async ({
4752
4867
  id,
@@ -4763,7 +4878,7 @@ const deletePreviousOneToAnyRelations = async ({
4763
4878
  const { joinTable } = attribute;
4764
4879
  const { joinColumn, inverseJoinColumn } = joinTable;
4765
4880
  const con = db.getConnection();
4766
- await con.delete().from(joinTable.name).whereNotIn(joinColumn.name, getDocumentSiblingIdsQuery(con, joinColumn.referencedTable, id)).whereIn(inverseJoinColumn.name, relIdsToadd).where(joinTable.on || {}).transacting(trx);
4881
+ await con.delete().from(joinTable.name).whereNotIn(joinColumn.name, getDocumentSiblingIdsQuery(joinColumn.referencedTable, id)).whereIn(inverseJoinColumn.name, relIdsToadd).where(joinTable.on || {}).transacting(trx);
4767
4882
  await cleanOrderColumns({ attribute, db, inverseRelIds: relIdsToadd, transaction: trx });
4768
4883
  };
4769
4884
  const deletePreviousAnyToOneRelations = async ({
@@ -4782,7 +4897,7 @@ const deletePreviousAnyToOneRelations = async ({
4782
4897
  if (isManyToAny(attribute)) {
4783
4898
  const relsToDelete = await con.select(inverseJoinColumn.name).from(joinTable.name).where(joinColumn.name, id).whereNotIn(
4784
4899
  inverseJoinColumn.name,
4785
- getDocumentSiblingIdsQuery(con, inverseJoinColumn.referencedTable, relIdToadd)
4900
+ getDocumentSiblingIdsQuery(inverseJoinColumn.referencedTable, relIdToadd)
4786
4901
  ).where(joinTable.on || {}).transacting(trx);
4787
4902
  const relIdsToDelete = map(inverseJoinColumn.name, relsToDelete);
4788
4903
  await createQueryBuilder(joinTable.name, db).delete().where({
@@ -4793,7 +4908,7 @@ const deletePreviousAnyToOneRelations = async ({
4793
4908
  } else {
4794
4909
  await con.delete().from(joinTable.name).where(joinColumn.name, id).whereNotIn(
4795
4910
  inverseJoinColumn.name,
4796
- getDocumentSiblingIdsQuery(con, inverseJoinColumn.referencedTable, relIdToadd)
4911
+ getDocumentSiblingIdsQuery(inverseJoinColumn.referencedTable, relIdToadd)
4797
4912
  ).where(joinTable.on || {}).transacting(trx);
4798
4913
  }
4799
4914
  };
@@ -5877,6 +5992,21 @@ const createStorage = (opts) => {
5877
5992
  const wrapTransaction = (db) => (fn) => () => {
5878
5993
  return db.transaction(({ trx }) => Promise.resolve(fn(trx, db)));
5879
5994
  };
5995
+ const transformLogMessage = (level, message) => {
5996
+ if (typeof message === "string") {
5997
+ return { level, message };
5998
+ }
5999
+ if (typeof message === "object" && message !== null) {
6000
+ if ("event" in message && "name" in message) {
6001
+ return {
6002
+ level,
6003
+ message: `[internal migration]: ${message.event} ${message?.name}`,
6004
+ timestamp: Date.now()
6005
+ };
6006
+ }
6007
+ }
6008
+ return "";
6009
+ };
5880
6010
  const migrationResolver = ({ name, path: path2, context }) => {
5881
6011
  const { db } = context;
5882
6012
  if (!path2) {
@@ -5905,7 +6035,20 @@ const createUserMigrationProvider = (db) => {
5905
6035
  const context = { db };
5906
6036
  const umzugProvider = new Umzug({
5907
6037
  storage: createStorage({ db, tableName: "strapi_migrations" }),
5908
- logger: console,
6038
+ logger: {
6039
+ info(message) {
6040
+ db.logger.info(transformLogMessage("info", message));
6041
+ },
6042
+ warn(message) {
6043
+ db.logger.warn(transformLogMessage("warn", message));
6044
+ },
6045
+ error(message) {
6046
+ db.logger.error(transformLogMessage("error", message));
6047
+ },
6048
+ debug(message) {
6049
+ db.logger.debug(transformLogMessage("debug", message));
6050
+ }
6051
+ },
5909
6052
  context,
5910
6053
  migrations: {
5911
6054
  glob: ["*.{js,sql}", { cwd: dir }],
@@ -6211,7 +6354,8 @@ const createdLocale = {
6211
6354
  if (!model) {
6212
6355
  continue;
6213
6356
  }
6214
- if (isNil(meta.attributes.locale)) {
6357
+ const hasLocaleColumn = await knex2.schema.hasColumn(meta.tableName, "locale");
6358
+ if (meta.attributes.locale && !hasLocaleColumn) {
6215
6359
  await createLocaleColumn(knex2, meta.tableName);
6216
6360
  }
6217
6361
  }
@@ -6220,17 +6364,87 @@ const createdLocale = {
6220
6364
  throw new Error("not implemented");
6221
6365
  }
6222
6366
  };
6367
+ const createPublishedAtColumn = async (db, tableName) => {
6368
+ await db.schema.alterTable(tableName, (table) => {
6369
+ table.string("published_at");
6370
+ });
6371
+ };
6372
+ const createdPublishedAt = {
6373
+ name: "5.0.0-04-created-published-at",
6374
+ async up(knex2, db) {
6375
+ for (const meta of db.metadata.values()) {
6376
+ const hasTable = await knex2.schema.hasTable(meta.tableName);
6377
+ if (!hasTable) {
6378
+ continue;
6379
+ }
6380
+ const uid = meta.uid;
6381
+ const model = strapi.getModel(uid);
6382
+ if (!model) {
6383
+ continue;
6384
+ }
6385
+ const hasPublishedAtColumn = await knex2.schema.hasColumn(meta.tableName, "published_at");
6386
+ if (meta.attributes.publishedAt && !hasPublishedAtColumn) {
6387
+ await createPublishedAtColumn(knex2, meta.tableName);
6388
+ }
6389
+ }
6390
+ },
6391
+ async down() {
6392
+ throw new Error("not implemented");
6393
+ }
6394
+ };
6395
+ const dropIndex = async (knex2, tableName, columnName) => {
6396
+ try {
6397
+ await knex2.schema.alterTable(tableName, (table) => {
6398
+ table.dropUnique([columnName], `${tableName}_${columnName}_unique`);
6399
+ });
6400
+ } catch (error) {
6401
+ }
6402
+ };
6403
+ const dropSlugFieldsIndex = {
6404
+ name: "5.0.0-05-drop-slug-fields-index",
6405
+ async up(knex2, db) {
6406
+ for (const meta of db.metadata.values()) {
6407
+ const hasTable = await knex2.schema.hasTable(meta.tableName);
6408
+ if (!hasTable) {
6409
+ continue;
6410
+ }
6411
+ for (const attribute of Object.values(meta.attributes)) {
6412
+ if (attribute.type === "uid" && attribute.columnName) {
6413
+ await dropIndex(knex2, meta.tableName, attribute.columnName);
6414
+ }
6415
+ }
6416
+ }
6417
+ },
6418
+ async down() {
6419
+ throw new Error("not implemented");
6420
+ }
6421
+ };
6223
6422
  const internalMigrations = [
6224
6423
  renameIdentifiersLongerThanMaxLength,
6225
6424
  createdDocumentId,
6226
- createdLocale
6425
+ createdLocale,
6426
+ createdPublishedAt,
6427
+ dropSlugFieldsIndex
6227
6428
  ];
6228
6429
  const createInternalMigrationProvider = (db) => {
6229
6430
  const context = { db };
6230
6431
  const migrations = [...internalMigrations];
6231
6432
  const umzugProvider = new Umzug({
6232
6433
  storage: createStorage({ db, tableName: "strapi_migrations_internal" }),
6233
- logger: console,
6434
+ logger: {
6435
+ info(message) {
6436
+ db.logger.debug(transformLogMessage("info", message));
6437
+ },
6438
+ warn(message) {
6439
+ db.logger.warn(transformLogMessage("warn", message));
6440
+ },
6441
+ error(message) {
6442
+ db.logger.error(transformLogMessage("error", message));
6443
+ },
6444
+ debug(message) {
6445
+ db.logger.debug(transformLogMessage("debug", message));
6446
+ }
6447
+ },
6234
6448
  context,
6235
6449
  migrations: () => migrations.map((migration) => {
6236
6450
  return {
@@ -6501,6 +6715,7 @@ class Database {
6501
6715
  migrations;
6502
6716
  lifecycles;
6503
6717
  entityManager;
6718
+ logger;
6504
6719
  constructor(config) {
6505
6720
  this.config = {
6506
6721
  ...config,
@@ -6520,6 +6735,7 @@ class Database {
6520
6735
  this.migrations = createMigrationsProvider(this);
6521
6736
  this.lifecycles = createLifecyclesProvider(this);
6522
6737
  this.entityManager = createEntityManager(this);
6738
+ this.logger = config.logger ?? console;
6523
6739
  }
6524
6740
  async init({ models }) {
6525
6741
  this.metadata.loadModels(models);