@strapi/database 5.0.0-beta.1 → 5.0.0-beta.11
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/entity-manager/index.d.ts.map +1 -1
- package/dist/entity-manager/morph-relations.d.ts +1 -1
- package/dist/entity-manager/morph-relations.d.ts.map +1 -1
- package/dist/entity-manager/regular-relations.d.ts +8 -8
- package/dist/entity-manager/regular-relations.d.ts.map +1 -1
- package/dist/entity-manager/relations-orderer.d.ts.map +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +944 -342
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +942 -340
- package/dist/index.mjs.map +1 -1
- package/dist/lifecycles/types.d.ts +1 -0
- package/dist/lifecycles/types.d.ts.map +1 -1
- package/dist/metadata/index.d.ts +1 -1
- package/dist/metadata/index.d.ts.map +1 -1
- package/dist/metadata/metadata.d.ts +2 -1
- package/dist/metadata/metadata.d.ts.map +1 -1
- package/dist/metadata/relations.d.ts.map +1 -1
- package/dist/migrations/common.d.ts +16 -2
- package/dist/migrations/common.d.ts.map +1 -1
- package/dist/migrations/index.d.ts +2 -2
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/internal-migrations/5.0.0-01-convert-identifiers-long-than-max-length.d.ts +3 -0
- package/dist/migrations/internal-migrations/5.0.0-01-convert-identifiers-long-than-max-length.d.ts.map +1 -0
- package/dist/migrations/internal-migrations/5.0.0-02-document-id.d.ts +3 -0
- package/dist/migrations/internal-migrations/5.0.0-02-document-id.d.ts.map +1 -0
- package/dist/migrations/internal-migrations/5.0.0-03-locale.d.ts +3 -0
- package/dist/migrations/internal-migrations/5.0.0-03-locale.d.ts.map +1 -0
- package/dist/migrations/internal-migrations/index.d.ts.map +1 -1
- package/dist/migrations/internal.d.ts +2 -2
- package/dist/migrations/internal.d.ts.map +1 -1
- package/dist/migrations/logger.d.ts +10 -0
- package/dist/migrations/logger.d.ts.map +1 -0
- package/dist/migrations/storage.d.ts.map +1 -1
- package/dist/migrations/users.d.ts +2 -2
- package/dist/migrations/users.d.ts.map +1 -1
- package/dist/query/helpers/join.d.ts.map +1 -1
- package/dist/query/helpers/populate/apply.d.ts.map +1 -1
- package/dist/query/helpers/where.d.ts +1 -0
- package/dist/query/helpers/where.d.ts.map +1 -1
- package/dist/schema/index.d.ts +0 -3
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/transaction-context.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/identifiers/hash.d.ts +31 -0
- package/dist/utils/identifiers/hash.d.ts.map +1 -0
- package/dist/utils/identifiers/index.d.ts +107 -47
- package/dist/utils/identifiers/index.d.ts.map +1 -1
- package/dist/utils/identifiers/types.d.ts +27 -0
- package/dist/utils/identifiers/types.d.ts.map +1 -0
- package/dist/validations/relations/bidirectional.d.ts.map +1 -1
- package/package.json +11 -8
- package/dist/utils/identifiers/shortener.d.ts +0 -73
- package/dist/utils/identifiers/shortener.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -7,13 +7,14 @@ 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
|
|
10
|
+
const utils = require("@strapi/utils");
|
|
11
11
|
const KnexBuilder = require("knex/lib/query/querybuilder");
|
|
12
12
|
const KnexRaw = require("knex/lib/raw");
|
|
13
13
|
const stream = require("stream");
|
|
14
14
|
const node_async_hooks = require("node:async_hooks");
|
|
15
15
|
const _$1 = require("lodash");
|
|
16
16
|
const umzug = require("umzug");
|
|
17
|
+
const cuid2 = require("@paralleldrive/cuid2");
|
|
17
18
|
const assert = require("assert");
|
|
18
19
|
const knex = require("knex");
|
|
19
20
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
@@ -1007,7 +1008,7 @@ const getDialect = (db) => {
|
|
|
1007
1008
|
const dialect = new constructor(db, dialectName);
|
|
1008
1009
|
return dialect;
|
|
1009
1010
|
};
|
|
1010
|
-
const debug$
|
|
1011
|
+
const debug$2 = createDebug__default.default("strapi::database");
|
|
1011
1012
|
const createSchemaBuilder = (db) => {
|
|
1012
1013
|
const helpers2 = createHelpers(db);
|
|
1013
1014
|
return {
|
|
@@ -1033,12 +1034,12 @@ const createSchemaBuilder = (db) => {
|
|
|
1033
1034
|
*/
|
|
1034
1035
|
async createTables(tables, trx) {
|
|
1035
1036
|
for (const table of tables) {
|
|
1036
|
-
debug$
|
|
1037
|
+
debug$2(`Creating table: ${table.name}`);
|
|
1037
1038
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1038
1039
|
await helpers2.createTable(schemaBuilder, table);
|
|
1039
1040
|
}
|
|
1040
1041
|
for (const table of tables) {
|
|
1041
|
-
debug$
|
|
1042
|
+
debug$2(`Creating table foreign keys: ${table.name}`);
|
|
1042
1043
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1043
1044
|
await helpers2.createTableForeignKeys(schemaBuilder, table);
|
|
1044
1045
|
}
|
|
@@ -1069,18 +1070,18 @@ const createSchemaBuilder = (db) => {
|
|
|
1069
1070
|
await this.createTables(schemaDiff.tables.added, trx);
|
|
1070
1071
|
if (forceMigration) {
|
|
1071
1072
|
for (const table of schemaDiff.tables.removed) {
|
|
1072
|
-
debug$
|
|
1073
|
+
debug$2(`Removing table foreign keys: ${table.name}`);
|
|
1073
1074
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1074
1075
|
await helpers2.dropTableForeignKeys(schemaBuilder, table);
|
|
1075
1076
|
}
|
|
1076
1077
|
for (const table of schemaDiff.tables.removed) {
|
|
1077
|
-
debug$
|
|
1078
|
+
debug$2(`Removing table: ${table.name}`);
|
|
1078
1079
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1079
1080
|
await helpers2.dropTable(schemaBuilder, table);
|
|
1080
1081
|
}
|
|
1081
1082
|
}
|
|
1082
1083
|
for (const table of schemaDiff.tables.updated) {
|
|
1083
|
-
debug$
|
|
1084
|
+
debug$2(`Updating table: ${table.name}`);
|
|
1084
1085
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1085
1086
|
await helpers2.alterTable(schemaBuilder, table);
|
|
1086
1087
|
}
|
|
@@ -1175,28 +1176,37 @@ const createHelpers = (db) => {
|
|
|
1175
1176
|
};
|
|
1176
1177
|
const alterTable = async (schemaBuilder, table) => {
|
|
1177
1178
|
await schemaBuilder.alterTable(table.name, (tableBuilder) => {
|
|
1178
|
-
for (const removedIndex of table.indexes.removed) {
|
|
1179
|
-
debug$1(`Dropping index ${removedIndex.name}`);
|
|
1180
|
-
dropIndex(tableBuilder, removedIndex);
|
|
1181
|
-
}
|
|
1182
|
-
for (const updateddIndex of table.indexes.updated) {
|
|
1183
|
-
debug$1(`Dropping updated index ${updateddIndex.name}`);
|
|
1184
|
-
dropIndex(tableBuilder, updateddIndex.object);
|
|
1185
|
-
}
|
|
1186
1179
|
for (const removedForeignKey of table.foreignKeys.removed) {
|
|
1187
|
-
debug$
|
|
1180
|
+
debug$2(`Dropping foreign key ${removedForeignKey.name} on ${table.name}`);
|
|
1188
1181
|
dropForeignKey(tableBuilder, removedForeignKey);
|
|
1189
1182
|
}
|
|
1190
1183
|
for (const updatedForeignKey of table.foreignKeys.updated) {
|
|
1191
|
-
debug$
|
|
1184
|
+
debug$2(`Dropping updated foreign key ${updatedForeignKey.name} on ${table.name}`);
|
|
1192
1185
|
dropForeignKey(tableBuilder, updatedForeignKey.object);
|
|
1193
1186
|
}
|
|
1194
1187
|
for (const removedColumn of table.columns.removed) {
|
|
1195
|
-
debug$
|
|
1188
|
+
debug$2(`Dropping column ${removedColumn.name} on ${table.name}`);
|
|
1196
1189
|
dropColumn(tableBuilder, removedColumn);
|
|
1197
1190
|
}
|
|
1191
|
+
const isMySQL = db.config.connection.client === "mysql";
|
|
1192
|
+
const ignoreForeignKeyNames = isMySQL ? [
|
|
1193
|
+
...table.foreignKeys.removed.map((fk) => fk.name),
|
|
1194
|
+
...table.foreignKeys.updated.map((fk) => fk.name)
|
|
1195
|
+
] : [];
|
|
1196
|
+
for (const removedIndex of table.indexes.removed) {
|
|
1197
|
+
if (!ignoreForeignKeyNames.includes(removedIndex.name)) {
|
|
1198
|
+
debug$2(`Dropping index ${removedIndex.name} on ${table.name}`);
|
|
1199
|
+
dropIndex(tableBuilder, removedIndex);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
for (const updatedIndex of table.indexes.updated) {
|
|
1203
|
+
if (!ignoreForeignKeyNames.includes(updatedIndex.name)) {
|
|
1204
|
+
debug$2(`Dropping updated index ${updatedIndex.name} on ${table.name}`);
|
|
1205
|
+
dropIndex(tableBuilder, updatedIndex.object);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1198
1208
|
for (const updatedColumn of table.columns.updated) {
|
|
1199
|
-
debug$
|
|
1209
|
+
debug$2(`Updating column ${updatedColumn.name} on ${table.name}`);
|
|
1200
1210
|
const { object } = updatedColumn;
|
|
1201
1211
|
if (object.type === "increments") {
|
|
1202
1212
|
createColumn2(tableBuilder, { ...object, type: "integer" }).alter();
|
|
@@ -1205,15 +1215,15 @@ const createHelpers = (db) => {
|
|
|
1205
1215
|
}
|
|
1206
1216
|
}
|
|
1207
1217
|
for (const updatedForeignKey of table.foreignKeys.updated) {
|
|
1208
|
-
debug$
|
|
1218
|
+
debug$2(`Recreating updated foreign key ${updatedForeignKey.name} on ${table.name}`);
|
|
1209
1219
|
createForeignKey(tableBuilder, updatedForeignKey.object);
|
|
1210
1220
|
}
|
|
1211
1221
|
for (const updatedIndex of table.indexes.updated) {
|
|
1212
|
-
debug$
|
|
1222
|
+
debug$2(`Recreating updated index ${updatedIndex.name} on ${table.name}`);
|
|
1213
1223
|
createIndex(tableBuilder, updatedIndex.object);
|
|
1214
1224
|
}
|
|
1215
1225
|
for (const addedColumn of table.columns.added) {
|
|
1216
|
-
debug$
|
|
1226
|
+
debug$2(`Creating column ${addedColumn.name} on ${table.name}`);
|
|
1217
1227
|
if (addedColumn.type === "increments" && !db.dialect.canAddIncrements()) {
|
|
1218
1228
|
tableBuilder.integer(addedColumn.name).unsigned();
|
|
1219
1229
|
tableBuilder.primary([addedColumn.name]);
|
|
@@ -1222,11 +1232,11 @@ const createHelpers = (db) => {
|
|
|
1222
1232
|
}
|
|
1223
1233
|
}
|
|
1224
1234
|
for (const addedForeignKey of table.foreignKeys.added) {
|
|
1225
|
-
debug$
|
|
1235
|
+
debug$2(`Creating foreign keys ${addedForeignKey.name} on ${table.name}`);
|
|
1226
1236
|
createForeignKey(tableBuilder, addedForeignKey);
|
|
1227
1237
|
}
|
|
1228
1238
|
for (const addedIndex of table.indexes.added) {
|
|
1229
|
-
debug$
|
|
1239
|
+
debug$2(`Creating index ${addedIndex.name} on ${table.name}`);
|
|
1230
1240
|
createIndex(tableBuilder, addedIndex);
|
|
1231
1241
|
}
|
|
1232
1242
|
});
|
|
@@ -1645,11 +1655,6 @@ const isScalar = (type) => SCALAR_TYPES.includes(type);
|
|
|
1645
1655
|
const isRelation = (type) => type === "relation";
|
|
1646
1656
|
const isScalarAttribute = (attribute) => isScalar(attribute.type);
|
|
1647
1657
|
const isRelationalAttribute = (attribute) => isRelation(attribute.type);
|
|
1648
|
-
const MAX_DB_IDENTIFIER_LENGTH = 0;
|
|
1649
|
-
const HASH_LENGTH = 5;
|
|
1650
|
-
const HASH_SEPARATOR = "";
|
|
1651
|
-
const IDENTIFIER_SEPARATOR = "_";
|
|
1652
|
-
const MIN_TOKEN_LENGTH = 3;
|
|
1653
1658
|
function createHash(data, len) {
|
|
1654
1659
|
if (!_.isInteger(len) || len <= 0) {
|
|
1655
1660
|
throw new Error(`createHash length must be a positive integer, received ${len}`);
|
|
@@ -1657,201 +1662,334 @@ function createHash(data, len) {
|
|
|
1657
1662
|
const hash = crypto__default$1.default.createHash("shake256", { outputLength: Math.ceil(len / 2) }).update(data);
|
|
1658
1663
|
return hash.digest("hex").substring(0, len);
|
|
1659
1664
|
}
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1665
|
+
const IDENTIFIER_MAX_LENGTH = 55;
|
|
1666
|
+
class Identifiers {
|
|
1667
|
+
ID_COLUMN = "id";
|
|
1668
|
+
ORDER_COLUMN = "order";
|
|
1669
|
+
FIELD_COLUMN = "field";
|
|
1670
|
+
HASH_LENGTH = 5;
|
|
1671
|
+
HASH_SEPARATOR = "";
|
|
1672
|
+
// no separator is needed, we will just attach hash directly to shortened name
|
|
1673
|
+
IDENTIFIER_SEPARATOR = "_";
|
|
1674
|
+
MIN_TOKEN_LENGTH = 3;
|
|
1675
|
+
// the min characters required at the beginning of a name part
|
|
1676
|
+
// Fixed compression map for suffixes and prefixes
|
|
1677
|
+
#replacementMap = {
|
|
1678
|
+
links: "lnk",
|
|
1679
|
+
order_inv_fk: "oifk",
|
|
1680
|
+
order: "ord",
|
|
1681
|
+
morphs: "mph",
|
|
1682
|
+
index: "idx",
|
|
1683
|
+
inv_fk: "ifk",
|
|
1684
|
+
order_fk: "ofk",
|
|
1685
|
+
id_column_index: "idix",
|
|
1686
|
+
order_index: "oidx",
|
|
1687
|
+
unique: "uq",
|
|
1688
|
+
primary: "pk"
|
|
1689
|
+
};
|
|
1690
|
+
#options;
|
|
1691
|
+
constructor(options) {
|
|
1692
|
+
this.#options = options;
|
|
1677
1693
|
}
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
function getNameFromTokens(nameTokens, maxLength = MAX_DB_IDENTIFIER_LENGTH) {
|
|
1681
|
-
if (!_.isInteger(maxLength) || maxLength < 0) {
|
|
1682
|
-
throw new Error("maxLength must be a positive integer or 0 (for unlimited length)");
|
|
1694
|
+
get replacementMap() {
|
|
1695
|
+
return this.#replacementMap;
|
|
1683
1696
|
}
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
return fullLengthName;
|
|
1697
|
+
get options() {
|
|
1698
|
+
return this.#options;
|
|
1687
1699
|
}
|
|
1688
|
-
|
|
1689
|
-
(
|
|
1690
|
-
|
|
1691
|
-
);
|
|
1692
|
-
const totalIncompressibleLength = _.sumBy((token) => token.name.length)(incompressible);
|
|
1693
|
-
const totalSeparatorsLength = nameTokens.length * IDENTIFIER_SEPARATOR.length - 1;
|
|
1694
|
-
const available = maxLength - totalIncompressibleLength - totalSeparatorsLength;
|
|
1695
|
-
const availablePerToken = Math.floor(available / compressible.length);
|
|
1696
|
-
if (totalIncompressibleLength + totalSeparatorsLength > maxLength || availablePerToken < MIN_TOKEN_LENGTH) {
|
|
1697
|
-
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1698
|
-
}
|
|
1699
|
-
let surplus = available % compressible.length;
|
|
1700
|
-
const minHashedLength = HASH_LENGTH + HASH_SEPARATOR.length + MIN_TOKEN_LENGTH;
|
|
1701
|
-
const totalLength = nameTokens.reduce((total, token) => {
|
|
1702
|
-
if (token.compressible) {
|
|
1703
|
-
if (token.name.length < availablePerToken) {
|
|
1704
|
-
return total + token.name.length;
|
|
1705
|
-
}
|
|
1706
|
-
return total + minHashedLength;
|
|
1707
|
-
}
|
|
1708
|
-
return total + token.name.length;
|
|
1709
|
-
}, nameTokens.length * IDENTIFIER_SEPARATOR.length - 1);
|
|
1710
|
-
if (maxLength < totalLength) {
|
|
1711
|
-
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1712
|
-
}
|
|
1713
|
-
let deficits = [];
|
|
1714
|
-
compressible.forEach((token) => {
|
|
1715
|
-
const actualLength = token.name.length;
|
|
1716
|
-
if (actualLength < availablePerToken) {
|
|
1717
|
-
surplus += availablePerToken - actualLength;
|
|
1718
|
-
token.allocatedLength = actualLength;
|
|
1719
|
-
} else {
|
|
1720
|
-
token.allocatedLength = availablePerToken;
|
|
1721
|
-
deficits.push(token);
|
|
1700
|
+
mapshortNames = (name) => {
|
|
1701
|
+
if (name in this.replacementMap) {
|
|
1702
|
+
return this.replacementMap[name];
|
|
1722
1703
|
}
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1704
|
+
return void 0;
|
|
1705
|
+
};
|
|
1706
|
+
// Generic name handler that must be used by all helper functions
|
|
1707
|
+
/**
|
|
1708
|
+
* TODO: we should be requiring snake_case inputs for all names here, but we
|
|
1709
|
+
* aren't and it will require some refactoring to make it work. Currently if
|
|
1710
|
+
* we get names 'myModel' and 'my_model' they would be converted to the same
|
|
1711
|
+
* final string my_model which generally works but is not entirely safe
|
|
1712
|
+
* */
|
|
1713
|
+
getName = (names, options) => {
|
|
1714
|
+
const tokens = ___default.default.castArray(names).map((name) => {
|
|
1715
|
+
return {
|
|
1716
|
+
name,
|
|
1717
|
+
compressible: true
|
|
1718
|
+
};
|
|
1719
|
+
});
|
|
1720
|
+
if (options?.suffix) {
|
|
1721
|
+
tokens.push({
|
|
1722
|
+
name: options.suffix,
|
|
1723
|
+
compressible: false,
|
|
1724
|
+
shortName: this.mapshortNames(options.suffix)
|
|
1725
|
+
});
|
|
1729
1726
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
break;
|
|
1727
|
+
if (options?.prefix) {
|
|
1728
|
+
tokens.unshift({
|
|
1729
|
+
name: options.prefix,
|
|
1730
|
+
compressible: false,
|
|
1731
|
+
shortName: this.mapshortNames(options.prefix)
|
|
1732
|
+
});
|
|
1737
1733
|
}
|
|
1738
|
-
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1734
|
+
return this.getNameFromTokens(tokens);
|
|
1735
|
+
};
|
|
1736
|
+
/**
|
|
1737
|
+
* TABLES
|
|
1738
|
+
*/
|
|
1739
|
+
getTableName = (name, options) => {
|
|
1740
|
+
return this.getName(name, options);
|
|
1741
|
+
};
|
|
1742
|
+
getJoinTableName = (collectionName, attributeName, options) => {
|
|
1743
|
+
return this.getName([collectionName, attributeName], {
|
|
1744
|
+
suffix: "links",
|
|
1745
|
+
...options
|
|
1746
|
+
});
|
|
1747
|
+
};
|
|
1748
|
+
getMorphTableName = (collectionName, attributeName, options) => {
|
|
1749
|
+
return this.getName([_.snakeCase(collectionName), _.snakeCase(attributeName)], {
|
|
1750
|
+
suffix: "morphs",
|
|
1751
|
+
...options
|
|
1752
|
+
});
|
|
1753
|
+
};
|
|
1754
|
+
/**
|
|
1755
|
+
* COLUMNS
|
|
1756
|
+
*/
|
|
1757
|
+
getColumnName = (attributeName, options) => {
|
|
1758
|
+
return this.getName(attributeName, options);
|
|
1759
|
+
};
|
|
1760
|
+
getJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1761
|
+
return this.getName(attributeName, { suffix: "id", ...options });
|
|
1762
|
+
};
|
|
1763
|
+
getInverseJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1764
|
+
return this.getName(_.snakeCase(attributeName), { suffix: "id", prefix: "inv", ...options });
|
|
1765
|
+
};
|
|
1766
|
+
getOrderColumnName = (singularName, options) => {
|
|
1767
|
+
return this.getName(singularName, { suffix: "order", ...options });
|
|
1768
|
+
};
|
|
1769
|
+
getInverseOrderColumnName = (singularName, options) => {
|
|
1770
|
+
return this.getName(singularName, {
|
|
1771
|
+
suffix: "order",
|
|
1772
|
+
prefix: "inv",
|
|
1773
|
+
...options
|
|
1774
|
+
});
|
|
1775
|
+
};
|
|
1776
|
+
/**
|
|
1777
|
+
* Morph Join Tables
|
|
1778
|
+
*/
|
|
1779
|
+
getMorphColumnJoinTableIdName = (singularName, options) => {
|
|
1780
|
+
return this.getName(_.snakeCase(singularName), { suffix: "id", ...options });
|
|
1781
|
+
};
|
|
1782
|
+
getMorphColumnAttributeIdName = (attributeName, options) => {
|
|
1783
|
+
return this.getName(_.snakeCase(attributeName), { suffix: "id", ...options });
|
|
1784
|
+
};
|
|
1785
|
+
getMorphColumnTypeName = (attributeName, options) => {
|
|
1786
|
+
return this.getName(_.snakeCase(attributeName), { suffix: "type", ...options });
|
|
1787
|
+
};
|
|
1788
|
+
/**
|
|
1789
|
+
* INDEXES
|
|
1790
|
+
* Note that these methods are generally used to reference full table names + attribute(s), which
|
|
1791
|
+
* may already be shortened strings rather than individual parts.
|
|
1792
|
+
* That is fine and expected to compress the previously incompressible parts of those strings,
|
|
1793
|
+
* because in these cases the relevant information is the table name and we can't really do
|
|
1794
|
+
* any better; shortening the individual parts again might make it even more confusing.
|
|
1795
|
+
*
|
|
1796
|
+
* So for example, the fk for the table `mytable_myattr4567d_localizations` will become
|
|
1797
|
+
* mytable_myattr4567d_loc63bf2_fk
|
|
1798
|
+
*/
|
|
1799
|
+
// base index types
|
|
1800
|
+
getIndexName = (names, options) => {
|
|
1801
|
+
return this.getName(names, { suffix: "index", ...options });
|
|
1802
|
+
};
|
|
1803
|
+
getFkIndexName = (names, options) => {
|
|
1804
|
+
return this.getName(names, { suffix: "fk", ...options });
|
|
1805
|
+
};
|
|
1806
|
+
getUniqueIndexName = (names, options) => {
|
|
1807
|
+
return this.getName(names, { suffix: "unique", ...options });
|
|
1808
|
+
};
|
|
1809
|
+
getPrimaryIndexName = (names, options) => {
|
|
1810
|
+
return this.getName(names, { suffix: "primary", ...options });
|
|
1811
|
+
};
|
|
1812
|
+
// custom index types
|
|
1813
|
+
getInverseFkIndexName = (names, options) => {
|
|
1814
|
+
return this.getName(names, { suffix: "inv_fk", ...options });
|
|
1815
|
+
};
|
|
1816
|
+
getOrderFkIndexName = (names, options) => {
|
|
1817
|
+
return this.getName(names, { suffix: "order_fk", ...options });
|
|
1818
|
+
};
|
|
1819
|
+
getOrderInverseFkIndexName = (names, options) => {
|
|
1820
|
+
return this.getName(names, { suffix: "order_inv_fk", ...options });
|
|
1821
|
+
};
|
|
1822
|
+
getIdColumnIndexName = (names, options) => {
|
|
1823
|
+
return this.getName(names, { suffix: "id_column_index", ...options });
|
|
1824
|
+
};
|
|
1825
|
+
getOrderIndexName = (names, options) => {
|
|
1826
|
+
return this.getName(names, { suffix: "order_index", ...options });
|
|
1827
|
+
};
|
|
1828
|
+
/**
|
|
1829
|
+
* Generates a string with a max length, appending a hash at the end if necessary to keep it unique
|
|
1830
|
+
*
|
|
1831
|
+
* @example
|
|
1832
|
+
* // if we have strings such as "longstring1" and "longstring2" with a max length of 9,
|
|
1833
|
+
* // we don't want to end up with "longstrin" and "longstrin"
|
|
1834
|
+
* // we want something such as "longs0b23" and "longs953f"
|
|
1835
|
+
* const token1 = generateToken("longstring1", 9); // "longs0b23"
|
|
1836
|
+
* const token2 = generateToken("longstring2", 9); // "longs953f"
|
|
1837
|
+
*
|
|
1838
|
+
* @param name - The base name
|
|
1839
|
+
* @param len - The desired length of the token.
|
|
1840
|
+
* @returns The generated token with hash.
|
|
1841
|
+
* @throws Error if the length is not a positive integer, or if the length is too short for the token.
|
|
1842
|
+
* @internal
|
|
1843
|
+
*/
|
|
1844
|
+
getShortenedName = (name, len) => {
|
|
1845
|
+
if (!_.isInteger(len) || len <= 0) {
|
|
1846
|
+
throw new Error(`tokenWithHash length must be a positive integer, received ${len}`);
|
|
1743
1847
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1848
|
+
if (name.length <= len) {
|
|
1849
|
+
return name;
|
|
1850
|
+
}
|
|
1851
|
+
if (len < this.MIN_TOKEN_LENGTH + this.HASH_LENGTH) {
|
|
1852
|
+
throw new Error(
|
|
1853
|
+
`length for part of identifier too short, minimum is hash length (${this.HASH_LENGTH}) plus min token length (${this.MIN_TOKEN_LENGTH}), received ${len} for token ${name}`
|
|
1854
|
+
);
|
|
1855
|
+
}
|
|
1856
|
+
const availableLength = len - this.HASH_LENGTH - this.HASH_SEPARATOR.length;
|
|
1857
|
+
if (availableLength < this.MIN_TOKEN_LENGTH) {
|
|
1858
|
+
throw new Error(
|
|
1859
|
+
`length for part of identifier minimum is less than min token length (${this.MIN_TOKEN_LENGTH}), received ${len} for token ${name}`
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1862
|
+
return `${name.substring(0, availableLength)}${this.HASH_SEPARATOR}${createHash(
|
|
1863
|
+
name,
|
|
1864
|
+
this.HASH_LENGTH
|
|
1865
|
+
)}`;
|
|
1866
|
+
};
|
|
1867
|
+
/**
|
|
1868
|
+
* Constructs a name from an array of name tokens within a specified maximum length. It ensures the final name does not exceed
|
|
1869
|
+
* this limit by selectively compressing tokens marked as compressible. If the name exceeds the maximum length and cannot be
|
|
1870
|
+
* compressed sufficiently, an error is thrown. This function supports dynamic adjustment of token lengths to fit within the
|
|
1871
|
+
* maxLength constraint (that is, it will always make use of all available space), while also ensuring the preservation of
|
|
1872
|
+
* incompressible tokens.
|
|
1873
|
+
* @internal
|
|
1874
|
+
*/
|
|
1875
|
+
getNameFromTokens = (nameTokens) => {
|
|
1876
|
+
const { maxLength } = this.options;
|
|
1877
|
+
if (!_.isInteger(maxLength) || maxLength < 0) {
|
|
1878
|
+
throw new Error("maxLength must be a positive integer or 0 (for unlimited length)");
|
|
1879
|
+
}
|
|
1880
|
+
const unshortenedName = nameTokens.map((token) => {
|
|
1881
|
+
return token.name;
|
|
1882
|
+
}).join(this.IDENTIFIER_SEPARATOR);
|
|
1883
|
+
if (maxLength === 0) {
|
|
1884
|
+
this.setUnshortenedName(unshortenedName, unshortenedName);
|
|
1885
|
+
return unshortenedName;
|
|
1886
|
+
}
|
|
1887
|
+
const fullLengthName = nameTokens.map((token) => {
|
|
1888
|
+
if (token.compressible) {
|
|
1889
|
+
return token.name;
|
|
1890
|
+
}
|
|
1891
|
+
return token.shortName ?? token.name;
|
|
1892
|
+
}).join(this.IDENTIFIER_SEPARATOR);
|
|
1893
|
+
if (fullLengthName.length <= maxLength) {
|
|
1894
|
+
this.setUnshortenedName(fullLengthName, unshortenedName);
|
|
1895
|
+
return fullLengthName;
|
|
1896
|
+
}
|
|
1897
|
+
const [compressible, incompressible] = _.partition(
|
|
1898
|
+
(token) => token.compressible,
|
|
1899
|
+
nameTokens
|
|
1749
1900
|
);
|
|
1750
|
-
|
|
1751
|
-
|
|
1901
|
+
const totalIncompressibleLength = _.sumBy(
|
|
1902
|
+
(token) => token.compressible === false && token.shortName !== void 0 ? token.shortName.length : token.name.length
|
|
1903
|
+
)(incompressible);
|
|
1904
|
+
const totalSeparatorsLength = nameTokens.length * this.IDENTIFIER_SEPARATOR.length - 1;
|
|
1905
|
+
const available = maxLength - totalIncompressibleLength - totalSeparatorsLength;
|
|
1906
|
+
const availablePerToken = Math.floor(available / compressible.length);
|
|
1907
|
+
if (totalIncompressibleLength + totalSeparatorsLength > maxLength || availablePerToken < this.MIN_TOKEN_LENGTH) {
|
|
1908
|
+
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1909
|
+
}
|
|
1910
|
+
let surplus = available % compressible.length;
|
|
1911
|
+
const minHashedLength = this.HASH_LENGTH + this.HASH_SEPARATOR.length + this.MIN_TOKEN_LENGTH;
|
|
1912
|
+
const totalLength = nameTokens.reduce(
|
|
1913
|
+
(total, token) => {
|
|
1914
|
+
if (token.compressible) {
|
|
1915
|
+
if (token.name.length < availablePerToken) {
|
|
1916
|
+
return total + token.name.length;
|
|
1917
|
+
}
|
|
1918
|
+
return total + minHashedLength;
|
|
1919
|
+
}
|
|
1920
|
+
const tokenName = token.shortName ?? token.name;
|
|
1921
|
+
return total + tokenName.length;
|
|
1922
|
+
},
|
|
1923
|
+
nameTokens.length * this.IDENTIFIER_SEPARATOR.length - 1
|
|
1924
|
+
);
|
|
1925
|
+
if (maxLength < totalLength) {
|
|
1926
|
+
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1927
|
+
}
|
|
1928
|
+
let deficits = [];
|
|
1929
|
+
compressible.forEach((token) => {
|
|
1930
|
+
const actualLength = token.name.length;
|
|
1931
|
+
if (actualLength < availablePerToken) {
|
|
1932
|
+
surplus += availablePerToken - actualLength;
|
|
1933
|
+
token.allocatedLength = actualLength;
|
|
1934
|
+
} else {
|
|
1935
|
+
token.allocatedLength = availablePerToken;
|
|
1936
|
+
deficits.push(token);
|
|
1937
|
+
}
|
|
1938
|
+
});
|
|
1939
|
+
function filterAndIncreaseLength(token) {
|
|
1940
|
+
if (token.allocatedLength < token.name.length && surplus > 0) {
|
|
1941
|
+
token.allocatedLength += 1;
|
|
1942
|
+
surplus -= 1;
|
|
1943
|
+
return token.allocatedLength < token.name.length;
|
|
1944
|
+
}
|
|
1945
|
+
return false;
|
|
1946
|
+
}
|
|
1947
|
+
let previousSurplus = surplus + 1;
|
|
1948
|
+
while (surplus > 0 && deficits.length > 0) {
|
|
1949
|
+
deficits = deficits.filter((token) => filterAndIncreaseLength(token));
|
|
1950
|
+
if (surplus === previousSurplus) {
|
|
1951
|
+
break;
|
|
1952
|
+
}
|
|
1953
|
+
previousSurplus = surplus;
|
|
1954
|
+
}
|
|
1955
|
+
const shortenedName = nameTokens.map((token) => {
|
|
1956
|
+
if (token.compressible && "allocatedLength" in token && token.allocatedLength !== void 0) {
|
|
1957
|
+
return this.getShortenedName(token.name, token.allocatedLength);
|
|
1958
|
+
}
|
|
1959
|
+
if (token.compressible === false && token.shortName) {
|
|
1960
|
+
return token.shortName;
|
|
1961
|
+
}
|
|
1962
|
+
return token.name;
|
|
1963
|
+
}).join(this.IDENTIFIER_SEPARATOR);
|
|
1964
|
+
if (shortenedName.length > maxLength) {
|
|
1965
|
+
throw new Error(
|
|
1966
|
+
`name shortening failed to generate a name of the correct maxLength; name ${shortenedName}`
|
|
1967
|
+
);
|
|
1968
|
+
}
|
|
1969
|
+
this.setUnshortenedName(shortenedName, unshortenedName);
|
|
1970
|
+
return shortenedName;
|
|
1971
|
+
};
|
|
1972
|
+
// We need to be able to find the full-length name for any shortened name, primarily for migration purposes
|
|
1973
|
+
// Therefore we store every name that passes through so we can retrieve the original later
|
|
1974
|
+
nameMap = /* @__PURE__ */ new Map();
|
|
1975
|
+
getUnshortenedName = (shortName) => {
|
|
1976
|
+
return this.nameMap.get(this.serializeKey(shortName)) ?? shortName;
|
|
1977
|
+
};
|
|
1978
|
+
setUnshortenedName = (shortName, fullName) => {
|
|
1979
|
+
if (this.nameMap.get(this.serializeKey(shortName)) && shortName === fullName) {
|
|
1980
|
+
return;
|
|
1981
|
+
}
|
|
1982
|
+
this.nameMap.set(this.serializeKey(shortName), fullName);
|
|
1983
|
+
};
|
|
1984
|
+
serializeKey = (shortName) => {
|
|
1985
|
+
return `${shortName}.${this.options.maxLength}`;
|
|
1986
|
+
};
|
|
1752
1987
|
}
|
|
1753
|
-
const
|
|
1754
|
-
const ORDER_COLUMN = "order";
|
|
1755
|
-
const FIELD_COLUMN = "field";
|
|
1756
|
-
const getName = (names, options) => {
|
|
1757
|
-
const tokens = ___default.default.castArray(names).map((name) => {
|
|
1758
|
-
return {
|
|
1759
|
-
name,
|
|
1760
|
-
compressible: true
|
|
1761
|
-
};
|
|
1762
|
-
});
|
|
1763
|
-
if (options?.suffix) {
|
|
1764
|
-
tokens.push({ name: options.suffix, compressible: false });
|
|
1765
|
-
}
|
|
1766
|
-
if (options?.prefix) {
|
|
1767
|
-
tokens.unshift({ name: options.prefix, compressible: false });
|
|
1768
|
-
}
|
|
1769
|
-
const maxLength = options?.maxLength ?? MAX_DB_IDENTIFIER_LENGTH;
|
|
1770
|
-
return getNameFromTokens(tokens, maxLength);
|
|
1771
|
-
};
|
|
1772
|
-
const getTableName = (name, options) => {
|
|
1773
|
-
return getName(name, options);
|
|
1774
|
-
};
|
|
1775
|
-
const getJoinTableName = (collectionName, attributeName, options) => {
|
|
1776
|
-
return getName([collectionName, attributeName], { suffix: "links", ...options });
|
|
1777
|
-
};
|
|
1778
|
-
const getMorphTableName = (collectionName, attributeName, options) => {
|
|
1779
|
-
return getName([collectionName, attributeName], { suffix: "morphs", ...options });
|
|
1780
|
-
};
|
|
1781
|
-
const getColumnName = (attributeName, options) => {
|
|
1782
|
-
return getName(attributeName, options);
|
|
1783
|
-
};
|
|
1784
|
-
const getJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1785
|
-
return getName(attributeName, { suffix: "id", ...options });
|
|
1786
|
-
};
|
|
1787
|
-
const getInverseJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1788
|
-
return getName(attributeName, { suffix: "id", prefix: "inv", ...options });
|
|
1789
|
-
};
|
|
1790
|
-
const getOrderColumnName = (singularName, options) => {
|
|
1791
|
-
return getName(singularName, { suffix: "order", ...options });
|
|
1792
|
-
};
|
|
1793
|
-
const getInverseOrderColumnName = (singularName, options) => {
|
|
1794
|
-
return getName(singularName, { suffix: "order", prefix: "inv", ...options });
|
|
1795
|
-
};
|
|
1796
|
-
const getMorphColumnJoinTableIdName = (singularName, options) => {
|
|
1797
|
-
return getName(singularName, { suffix: "id", ...options });
|
|
1798
|
-
};
|
|
1799
|
-
const getMorphColumnAttributeIdName = (attributeName, options) => {
|
|
1800
|
-
return getName(attributeName, { suffix: "id", ...options });
|
|
1801
|
-
};
|
|
1802
|
-
const getMorphColumnTypeName = (attributeName, options) => {
|
|
1803
|
-
return getName(attributeName, { suffix: "type", ...options });
|
|
1804
|
-
};
|
|
1805
|
-
const getIndexName = (names, options) => {
|
|
1806
|
-
return getName(names, { suffix: "index", ...options });
|
|
1807
|
-
};
|
|
1808
|
-
const getFkIndexName = (names, options) => {
|
|
1809
|
-
return getName(names, { suffix: "fk", ...options });
|
|
1810
|
-
};
|
|
1811
|
-
const getInverseFkIndexName = (names, options) => {
|
|
1812
|
-
return getName(names, { suffix: "inv_fk", ...options });
|
|
1813
|
-
};
|
|
1814
|
-
const getOrderFkIndexName = (names, options) => {
|
|
1815
|
-
return getName(names, { suffix: "order_fk", ...options });
|
|
1816
|
-
};
|
|
1817
|
-
const getOrderInverseFkIndexName = (names, options) => {
|
|
1818
|
-
return getName(names, { suffix: "order_inv_fk", ...options });
|
|
1819
|
-
};
|
|
1820
|
-
const getUniqueIndexName = (names, options) => {
|
|
1821
|
-
return getName(names, { suffix: "unique", ...options });
|
|
1822
|
-
};
|
|
1823
|
-
const getPrimaryIndexName = (names) => {
|
|
1824
|
-
return getName(names, { suffix: "primary" });
|
|
1825
|
-
};
|
|
1826
|
-
const identifiers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1827
|
-
__proto__: null,
|
|
1828
|
-
FIELD_COLUMN,
|
|
1829
|
-
ID_COLUMN,
|
|
1830
|
-
ORDER_COLUMN,
|
|
1831
|
-
getColumnName,
|
|
1832
|
-
getFkIndexName,
|
|
1833
|
-
getIndexName,
|
|
1834
|
-
getInverseFkIndexName,
|
|
1835
|
-
getInverseJoinColumnAttributeIdName,
|
|
1836
|
-
getInverseOrderColumnName,
|
|
1837
|
-
getJoinColumnAttributeIdName,
|
|
1838
|
-
getJoinTableName,
|
|
1839
|
-
getMorphColumnAttributeIdName,
|
|
1840
|
-
getMorphColumnJoinTableIdName,
|
|
1841
|
-
getMorphColumnTypeName,
|
|
1842
|
-
getMorphTableName,
|
|
1843
|
-
getName,
|
|
1844
|
-
getOrderColumnName,
|
|
1845
|
-
getOrderFkIndexName,
|
|
1846
|
-
getOrderInverseFkIndexName,
|
|
1847
|
-
getPrimaryIndexName,
|
|
1848
|
-
getTableName,
|
|
1849
|
-
getUniqueIndexName
|
|
1850
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
1988
|
+
const identifiers = new Identifiers({ maxLength: IDENTIFIER_MAX_LENGTH });
|
|
1851
1989
|
const createColumn = (name, attribute) => {
|
|
1852
1990
|
const { type, args = [], ...opts } = getColumnType(attribute);
|
|
1853
1991
|
return {
|
|
1854
|
-
name,
|
|
1992
|
+
name: identifiers.getName(name),
|
|
1855
1993
|
type,
|
|
1856
1994
|
args,
|
|
1857
1995
|
defaultTo: null,
|
|
@@ -1873,8 +2011,8 @@ const createTable = (meta) => {
|
|
|
1873
2011
|
if (attribute.type === "relation") {
|
|
1874
2012
|
if ("morphColumn" in attribute && attribute.morphColumn && attribute.owner) {
|
|
1875
2013
|
const { idColumn, typeColumn } = attribute.morphColumn;
|
|
1876
|
-
const idColumnName = getName(idColumn.name);
|
|
1877
|
-
const typeColumnName = getName(typeColumn.name);
|
|
2014
|
+
const idColumnName = identifiers.getName(idColumn.name);
|
|
2015
|
+
const typeColumnName = identifiers.getName(typeColumn.name);
|
|
1878
2016
|
table.columns.push(
|
|
1879
2017
|
createColumn(idColumnName, {
|
|
1880
2018
|
type: "integer",
|
|
@@ -1891,7 +2029,7 @@ const createTable = (meta) => {
|
|
|
1891
2029
|
referencedTable,
|
|
1892
2030
|
columnType = "integer"
|
|
1893
2031
|
} = attribute.joinColumn;
|
|
1894
|
-
const columnName = getName(columnNameFull);
|
|
2032
|
+
const columnName = identifiers.getName(columnNameFull);
|
|
1895
2033
|
const column = createColumn(columnName, {
|
|
1896
2034
|
// TODO: find the column type automatically, or allow passing all the column params
|
|
1897
2035
|
type: columnType,
|
|
@@ -1900,7 +2038,7 @@ const createTable = (meta) => {
|
|
|
1900
2038
|
}
|
|
1901
2039
|
});
|
|
1902
2040
|
table.columns.push(column);
|
|
1903
|
-
const fkName = getFkIndexName([table.name, columnName]);
|
|
2041
|
+
const fkName = identifiers.getFkIndexName([table.name, columnName]);
|
|
1904
2042
|
table.foreignKeys.push({
|
|
1905
2043
|
name: fkName,
|
|
1906
2044
|
columns: [column.name],
|
|
@@ -1915,19 +2053,19 @@ const createTable = (meta) => {
|
|
|
1915
2053
|
});
|
|
1916
2054
|
}
|
|
1917
2055
|
} else if (isScalarAttribute(attribute)) {
|
|
1918
|
-
const columnName = getName(attribute.columnName || key);
|
|
2056
|
+
const columnName = identifiers.getName(attribute.columnName || key);
|
|
1919
2057
|
const column = createColumn(columnName, attribute);
|
|
1920
2058
|
if (column.unique) {
|
|
1921
2059
|
table.indexes.push({
|
|
1922
2060
|
type: "unique",
|
|
1923
|
-
name: getUniqueIndexName([table.name, column.name]),
|
|
2061
|
+
name: identifiers.getUniqueIndexName([table.name, column.name]),
|
|
1924
2062
|
columns: [columnName]
|
|
1925
2063
|
});
|
|
1926
2064
|
}
|
|
1927
2065
|
if (column.primary) {
|
|
1928
2066
|
table.indexes.push({
|
|
1929
2067
|
type: "primary",
|
|
1930
|
-
name: getPrimaryIndexName([table.name, column.name]),
|
|
2068
|
+
name: identifiers.getPrimaryIndexName([table.name, column.name]),
|
|
1931
2069
|
columns: [columnName]
|
|
1932
2070
|
});
|
|
1933
2071
|
}
|
|
@@ -2027,12 +2165,13 @@ const metadataToSchema = (metadata) => {
|
|
|
2027
2165
|
});
|
|
2028
2166
|
return schema;
|
|
2029
2167
|
};
|
|
2030
|
-
const debug = createDebug__default.default("strapi::database");
|
|
2168
|
+
const debug$1 = createDebug__default.default("strapi::database");
|
|
2031
2169
|
const createSchemaProvider = (db) => {
|
|
2032
2170
|
const state = {};
|
|
2033
2171
|
return {
|
|
2034
2172
|
get schema() {
|
|
2035
2173
|
if (!state.schema) {
|
|
2174
|
+
debug$1("Converting metadata to database schema");
|
|
2036
2175
|
state.schema = metadataToSchema(db.metadata);
|
|
2037
2176
|
}
|
|
2038
2177
|
return state.schema;
|
|
@@ -2044,7 +2183,7 @@ const createSchemaProvider = (db) => {
|
|
|
2044
2183
|
* Drops the database schema
|
|
2045
2184
|
*/
|
|
2046
2185
|
async drop() {
|
|
2047
|
-
debug("Dropping database schema");
|
|
2186
|
+
debug$1("Dropping database schema");
|
|
2048
2187
|
const DBSchema = await db.dialect.schemaInspector.getSchema();
|
|
2049
2188
|
await this.builder.dropSchema(DBSchema);
|
|
2050
2189
|
},
|
|
@@ -2052,19 +2191,19 @@ const createSchemaProvider = (db) => {
|
|
|
2052
2191
|
* Creates the database schema
|
|
2053
2192
|
*/
|
|
2054
2193
|
async create() {
|
|
2055
|
-
debug("Created database schema");
|
|
2194
|
+
debug$1("Created database schema");
|
|
2056
2195
|
await this.builder.createSchema(this.schema);
|
|
2057
2196
|
},
|
|
2058
2197
|
/**
|
|
2059
2198
|
* Resets the database schema
|
|
2060
2199
|
*/
|
|
2061
2200
|
async reset() {
|
|
2062
|
-
debug("Resetting database schema");
|
|
2201
|
+
debug$1("Resetting database schema");
|
|
2063
2202
|
await this.drop();
|
|
2064
2203
|
await this.create();
|
|
2065
2204
|
},
|
|
2066
2205
|
async syncSchema() {
|
|
2067
|
-
debug("Synchronizing database schema");
|
|
2206
|
+
debug$1("Synchronizing database schema");
|
|
2068
2207
|
const DBSchema = await db.dialect.schemaInspector.getSchema();
|
|
2069
2208
|
const { status, diff } = await this.schemaDiff.diff(DBSchema, this.schema);
|
|
2070
2209
|
if (status === "CHANGED") {
|
|
@@ -2077,28 +2216,28 @@ const createSchemaProvider = (db) => {
|
|
|
2077
2216
|
// TODO: Allow keeping extra indexes / extra tables / extra columns (globally or on a per table basis)
|
|
2078
2217
|
async sync() {
|
|
2079
2218
|
if (await db.migrations.shouldRun()) {
|
|
2080
|
-
debug("Found migrations to run");
|
|
2219
|
+
debug$1("Found migrations to run");
|
|
2081
2220
|
await db.migrations.up();
|
|
2082
2221
|
return this.syncSchema();
|
|
2083
2222
|
}
|
|
2084
2223
|
const oldSchema = await this.schemaStorage.read();
|
|
2085
2224
|
if (!oldSchema) {
|
|
2086
|
-
debug("Schema not persisted yet");
|
|
2225
|
+
debug$1("Schema not persisted yet");
|
|
2087
2226
|
return this.syncSchema();
|
|
2088
2227
|
}
|
|
2089
2228
|
const { hash: oldHash } = oldSchema;
|
|
2090
2229
|
const hash = await this.schemaStorage.hashSchema(this.schema);
|
|
2091
2230
|
if (oldHash !== hash) {
|
|
2092
|
-
debug("Schema changed");
|
|
2231
|
+
debug$1("Schema changed");
|
|
2093
2232
|
return this.syncSchema();
|
|
2094
2233
|
}
|
|
2095
|
-
debug("Schema unchanged");
|
|
2234
|
+
debug$1("Schema unchanged");
|
|
2096
2235
|
}
|
|
2097
2236
|
};
|
|
2098
2237
|
};
|
|
2099
|
-
const ID = ID_COLUMN;
|
|
2100
|
-
const ORDER = ORDER_COLUMN;
|
|
2101
|
-
const FIELD = FIELD_COLUMN;
|
|
2238
|
+
const ID = identifiers.ID_COLUMN;
|
|
2239
|
+
const ORDER = identifiers.ORDER_COLUMN;
|
|
2240
|
+
const FIELD = identifiers.FIELD_COLUMN;
|
|
2102
2241
|
const hasInversedBy = (attr) => "inversedBy" in attr;
|
|
2103
2242
|
const hasMappedBy = (attr) => "mappedBy" in attr;
|
|
2104
2243
|
const isOneToAny = (attribute) => ["oneToOne", "oneToMany"].includes(attribute.relation);
|
|
@@ -2119,7 +2258,7 @@ const createOneToOne = (attributeName, attribute, meta, metadata) => {
|
|
|
2119
2258
|
meta
|
|
2120
2259
|
});
|
|
2121
2260
|
} else {
|
|
2122
|
-
|
|
2261
|
+
createJoinColumn(metadata, {
|
|
2123
2262
|
attribute,
|
|
2124
2263
|
attributeName,
|
|
2125
2264
|
meta
|
|
@@ -2149,7 +2288,7 @@ const createManyToOne = (attributeName, attribute, meta, metadata) => {
|
|
|
2149
2288
|
meta
|
|
2150
2289
|
});
|
|
2151
2290
|
} else {
|
|
2152
|
-
|
|
2291
|
+
createJoinColumn(metadata, {
|
|
2153
2292
|
attribute,
|
|
2154
2293
|
attributeName,
|
|
2155
2294
|
meta
|
|
@@ -2166,15 +2305,11 @@ const createManyToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2166
2305
|
}
|
|
2167
2306
|
};
|
|
2168
2307
|
const createMorphToOne = (attributeName, attribute) => {
|
|
2169
|
-
const idColumnName = getJoinColumnAttributeIdName("target");
|
|
2170
|
-
const typeColumnName = getMorphColumnTypeName("target");
|
|
2171
|
-
if ("morphColumn" in attribute && attribute.morphColumn) {
|
|
2172
|
-
return;
|
|
2173
|
-
}
|
|
2308
|
+
const idColumnName = identifiers.getJoinColumnAttributeIdName("target");
|
|
2309
|
+
const typeColumnName = identifiers.getMorphColumnTypeName("target");
|
|
2174
2310
|
Object.assign(attribute, {
|
|
2175
2311
|
owner: true,
|
|
2176
|
-
morphColumn: {
|
|
2177
|
-
// TODO: add referenced column
|
|
2312
|
+
morphColumn: attribute.morphColumn ?? {
|
|
2178
2313
|
typeColumn: {
|
|
2179
2314
|
name: typeColumnName
|
|
2180
2315
|
},
|
|
@@ -2189,11 +2324,11 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2189
2324
|
if ("joinTable" in attribute && attribute.joinTable) {
|
|
2190
2325
|
return;
|
|
2191
2326
|
}
|
|
2192
|
-
const joinTableName = getMorphTableName(meta.tableName, attributeName);
|
|
2193
|
-
const joinColumnName = getMorphColumnJoinTableIdName(meta.singularName);
|
|
2194
|
-
const idColumnName = getMorphColumnAttributeIdName(attributeName);
|
|
2195
|
-
const typeColumnName = getMorphColumnTypeName(attributeName);
|
|
2196
|
-
const fkIndexName = getFkIndexName(joinTableName);
|
|
2327
|
+
const joinTableName = identifiers.getMorphTableName(meta.tableName, attributeName);
|
|
2328
|
+
const joinColumnName = identifiers.getMorphColumnJoinTableIdName(_.snakeCase(meta.singularName));
|
|
2329
|
+
const idColumnName = identifiers.getMorphColumnAttributeIdName(attributeName);
|
|
2330
|
+
const typeColumnName = identifiers.getMorphColumnTypeName(attributeName);
|
|
2331
|
+
const fkIndexName = identifiers.getFkIndexName(joinTableName);
|
|
2197
2332
|
metadata.add({
|
|
2198
2333
|
singularName: joinTableName,
|
|
2199
2334
|
uid: joinTableName,
|
|
@@ -2206,7 +2341,9 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2206
2341
|
type: "integer",
|
|
2207
2342
|
column: {
|
|
2208
2343
|
unsigned: true
|
|
2209
|
-
}
|
|
2344
|
+
},
|
|
2345
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2346
|
+
columnName: joinColumnName
|
|
2210
2347
|
},
|
|
2211
2348
|
[idColumnName]: {
|
|
2212
2349
|
type: "integer",
|
|
@@ -2233,11 +2370,11 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2233
2370
|
columns: [joinColumnName]
|
|
2234
2371
|
},
|
|
2235
2372
|
{
|
|
2236
|
-
name:
|
|
2373
|
+
name: identifiers.getOrderIndexName(joinTableName),
|
|
2237
2374
|
columns: [ORDER]
|
|
2238
2375
|
},
|
|
2239
2376
|
{
|
|
2240
|
-
name:
|
|
2377
|
+
name: identifiers.getIdColumnIndexName(joinTableName),
|
|
2241
2378
|
columns: [idColumnName]
|
|
2242
2379
|
}
|
|
2243
2380
|
],
|
|
@@ -2246,7 +2383,7 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2246
2383
|
name: fkIndexName,
|
|
2247
2384
|
columns: [joinColumnName],
|
|
2248
2385
|
referencedColumns: [ID],
|
|
2249
|
-
referencedTable:
|
|
2386
|
+
referencedTable: meta.tableName,
|
|
2250
2387
|
onDelete: "CASCADE"
|
|
2251
2388
|
}
|
|
2252
2389
|
],
|
|
@@ -2293,12 +2430,12 @@ const createMorphMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2293
2430
|
throw new Error(`Morph target attribute not found. Looking for ${attribute.morphBy}`);
|
|
2294
2431
|
}
|
|
2295
2432
|
};
|
|
2296
|
-
const
|
|
2433
|
+
const createJoinColumn = (metadata, { attribute, attributeName }) => {
|
|
2297
2434
|
const targetMeta = metadata.get(attribute.target);
|
|
2298
2435
|
if (!targetMeta) {
|
|
2299
2436
|
throw new Error(`Unknown target ${attribute.target}`);
|
|
2300
2437
|
}
|
|
2301
|
-
const joinColumnName =
|
|
2438
|
+
const joinColumnName = identifiers.getJoinColumnAttributeIdName(_.snakeCase(attributeName));
|
|
2302
2439
|
const joinColumn = {
|
|
2303
2440
|
name: joinColumnName,
|
|
2304
2441
|
referencedColumn: ID,
|
|
@@ -2313,7 +2450,7 @@ const createJoinColum = (metadata, { attribute, attributeName }) => {
|
|
|
2313
2450
|
Object.assign(inverseAttribute, {
|
|
2314
2451
|
joinColumn: {
|
|
2315
2452
|
name: joinColumn.referencedColumn,
|
|
2316
|
-
referencedColumn:
|
|
2453
|
+
referencedColumn: joinColumnName
|
|
2317
2454
|
}
|
|
2318
2455
|
});
|
|
2319
2456
|
}
|
|
@@ -2326,21 +2463,26 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2326
2463
|
if ("joinTable" in attribute && attribute.joinTable) {
|
|
2327
2464
|
return;
|
|
2328
2465
|
}
|
|
2329
|
-
const joinTableName = getJoinTableName(
|
|
2330
|
-
|
|
2331
|
-
|
|
2466
|
+
const joinTableName = identifiers.getJoinTableName(
|
|
2467
|
+
_.snakeCase(meta.tableName),
|
|
2468
|
+
_.snakeCase(attributeName)
|
|
2469
|
+
);
|
|
2470
|
+
const joinColumnName = identifiers.getJoinColumnAttributeIdName(_.snakeCase(meta.singularName));
|
|
2471
|
+
let inverseJoinColumnName = identifiers.getJoinColumnAttributeIdName(
|
|
2472
|
+
_.snakeCase(targetMeta.singularName)
|
|
2473
|
+
);
|
|
2332
2474
|
if (joinColumnName === inverseJoinColumnName) {
|
|
2333
|
-
inverseJoinColumnName = getInverseJoinColumnAttributeIdName(
|
|
2334
|
-
targetMeta.singularName
|
|
2475
|
+
inverseJoinColumnName = identifiers.getInverseJoinColumnAttributeIdName(
|
|
2476
|
+
_.snakeCase(targetMeta.singularName)
|
|
2335
2477
|
);
|
|
2336
2478
|
}
|
|
2337
|
-
const orderColumnName = getOrderColumnName(targetMeta.singularName);
|
|
2338
|
-
let inverseOrderColumnName = getOrderColumnName(meta.singularName);
|
|
2479
|
+
const orderColumnName = identifiers.getOrderColumnName(_.snakeCase(targetMeta.singularName));
|
|
2480
|
+
let inverseOrderColumnName = identifiers.getOrderColumnName(_.snakeCase(meta.singularName));
|
|
2339
2481
|
if (attribute.relation === "manyToMany" && orderColumnName === inverseOrderColumnName) {
|
|
2340
|
-
inverseOrderColumnName = getInverseOrderColumnName(meta.singularName);
|
|
2482
|
+
inverseOrderColumnName = identifiers.getInverseOrderColumnName(_.snakeCase(meta.singularName));
|
|
2341
2483
|
}
|
|
2342
|
-
const fkIndexName = getFkIndexName(joinTableName);
|
|
2343
|
-
const invFkIndexName = getInverseFkIndexName(joinTableName);
|
|
2484
|
+
const fkIndexName = identifiers.getFkIndexName(joinTableName);
|
|
2485
|
+
const invFkIndexName = identifiers.getInverseFkIndexName(joinTableName);
|
|
2344
2486
|
const metadataSchema = {
|
|
2345
2487
|
singularName: joinTableName,
|
|
2346
2488
|
uid: joinTableName,
|
|
@@ -2353,13 +2495,17 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2353
2495
|
type: "integer",
|
|
2354
2496
|
column: {
|
|
2355
2497
|
unsigned: true
|
|
2356
|
-
}
|
|
2498
|
+
},
|
|
2499
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2500
|
+
columnName: joinColumnName
|
|
2357
2501
|
},
|
|
2358
2502
|
[inverseJoinColumnName]: {
|
|
2359
2503
|
type: "integer",
|
|
2360
2504
|
column: {
|
|
2361
2505
|
unsigned: true
|
|
2362
|
-
}
|
|
2506
|
+
},
|
|
2507
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2508
|
+
columnName: inverseJoinColumnName
|
|
2363
2509
|
}
|
|
2364
2510
|
// TODO: add extra pivot attributes -> user should use an intermediate entity
|
|
2365
2511
|
},
|
|
@@ -2373,7 +2519,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2373
2519
|
columns: [inverseJoinColumnName]
|
|
2374
2520
|
},
|
|
2375
2521
|
{
|
|
2376
|
-
name: getUniqueIndexName(joinTableName),
|
|
2522
|
+
name: identifiers.getUniqueIndexName(joinTableName),
|
|
2377
2523
|
columns: [joinColumnName, inverseJoinColumnName],
|
|
2378
2524
|
type: "unique"
|
|
2379
2525
|
}
|
|
@@ -2384,7 +2530,6 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2384
2530
|
columns: [joinColumnName],
|
|
2385
2531
|
referencedColumns: [ID],
|
|
2386
2532
|
referencedTable: meta.tableName,
|
|
2387
|
-
// TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
|
|
2388
2533
|
onDelete: "CASCADE"
|
|
2389
2534
|
},
|
|
2390
2535
|
{
|
|
@@ -2392,7 +2537,6 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2392
2537
|
columns: [inverseJoinColumnName],
|
|
2393
2538
|
referencedColumns: [ID],
|
|
2394
2539
|
referencedTable: targetMeta.tableName,
|
|
2395
|
-
// TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
|
|
2396
2540
|
onDelete: "CASCADE"
|
|
2397
2541
|
}
|
|
2398
2542
|
],
|
|
@@ -2422,8 +2566,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2422
2566
|
}
|
|
2423
2567
|
};
|
|
2424
2568
|
metadataSchema.indexes.push({
|
|
2425
|
-
name: getOrderFkIndexName(joinTableName),
|
|
2426
|
-
// TODO: should we send joinTableName as parts?
|
|
2569
|
+
name: identifiers.getOrderFkIndexName(joinTableName),
|
|
2427
2570
|
columns: [orderColumnName]
|
|
2428
2571
|
});
|
|
2429
2572
|
joinTable.orderColumnName = orderColumnName;
|
|
@@ -2438,7 +2581,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2438
2581
|
}
|
|
2439
2582
|
};
|
|
2440
2583
|
metadataSchema.indexes.push({
|
|
2441
|
-
name: getOrderInverseFkIndexName(joinTableName),
|
|
2584
|
+
name: identifiers.getOrderInverseFkIndexName(joinTableName),
|
|
2442
2585
|
columns: [inverseOrderColumnName]
|
|
2443
2586
|
});
|
|
2444
2587
|
joinTable.inverseOrderColumnName = inverseOrderColumnName;
|
|
@@ -2496,6 +2639,12 @@ const createRelation = (attributeName, attribute, meta, metadata) => {
|
|
|
2496
2639
|
}
|
|
2497
2640
|
};
|
|
2498
2641
|
class Metadata extends Map {
|
|
2642
|
+
// TODO: we expose the global identifiers in this way so that in the future we can instantiate our own
|
|
2643
|
+
// However, it should NOT be done until all the methods used by metadata can be part of this metadata object
|
|
2644
|
+
// and access this one; currently they all access the global identifiers directly.
|
|
2645
|
+
get identifiers() {
|
|
2646
|
+
return identifiers;
|
|
2647
|
+
}
|
|
2499
2648
|
get(key) {
|
|
2500
2649
|
if (!super.has(key)) {
|
|
2501
2650
|
throw new Error(`Metadata for "${key}" not found`);
|
|
@@ -2519,10 +2668,12 @@ class Metadata extends Map {
|
|
|
2519
2668
|
seenTables.set(meta.tableName, true);
|
|
2520
2669
|
}
|
|
2521
2670
|
}
|
|
2522
|
-
loadModels(models
|
|
2523
|
-
for (const model of
|
|
2671
|
+
loadModels(models) {
|
|
2672
|
+
for (const model of _.cloneDeep(models ?? [])) {
|
|
2673
|
+
const tableName = identifiers.getTableName(model.tableName);
|
|
2524
2674
|
this.add({
|
|
2525
2675
|
...model,
|
|
2676
|
+
tableName,
|
|
2526
2677
|
attributes: {
|
|
2527
2678
|
...model.attributes
|
|
2528
2679
|
},
|
|
@@ -2563,10 +2714,13 @@ class Metadata extends Map {
|
|
|
2563
2714
|
}
|
|
2564
2715
|
}
|
|
2565
2716
|
const createAttribute = (attributeName, attribute) => {
|
|
2566
|
-
|
|
2717
|
+
if ("columnName" in attribute && attribute.columnName) {
|
|
2718
|
+
return;
|
|
2719
|
+
}
|
|
2720
|
+
const columnName = identifiers.getColumnName(_.snakeCase(attributeName));
|
|
2567
2721
|
Object.assign(attribute, { columnName });
|
|
2568
2722
|
};
|
|
2569
|
-
const createMetadata = (models
|
|
2723
|
+
const createMetadata = (models) => {
|
|
2570
2724
|
const metadata = new Metadata();
|
|
2571
2725
|
if (models.length) {
|
|
2572
2726
|
metadata.loadModels(models);
|
|
@@ -2917,11 +3071,54 @@ const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
|
|
|
2917
3071
|
return subAlias;
|
|
2918
3072
|
};
|
|
2919
3073
|
const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
|
|
2920
|
-
const { db, qb } = ctx;
|
|
3074
|
+
const { db, qb, uid } = ctx;
|
|
2921
3075
|
if (attribute.type !== "relation") {
|
|
2922
3076
|
throw new Error(`Cannot join on non relational field ${attributeName}`);
|
|
2923
3077
|
}
|
|
2924
3078
|
const targetMeta = db.metadata.get(attribute.target);
|
|
3079
|
+
if (["morphOne", "morphMany"].includes(attribute.relation)) {
|
|
3080
|
+
const targetAttribute = targetMeta.attributes[attribute.morphBy];
|
|
3081
|
+
const { joinTable: joinTable2, morphColumn } = targetAttribute;
|
|
3082
|
+
if (morphColumn) {
|
|
3083
|
+
const subAlias = refAlias || qb.getAlias();
|
|
3084
|
+
qb.join({
|
|
3085
|
+
alias: subAlias,
|
|
3086
|
+
referencedTable: targetMeta.tableName,
|
|
3087
|
+
referencedColumn: morphColumn.idColumn.name,
|
|
3088
|
+
rootColumn: morphColumn.idColumn.referencedColumn,
|
|
3089
|
+
rootTable: alias,
|
|
3090
|
+
on: {
|
|
3091
|
+
[morphColumn.typeColumn.name]: uid,
|
|
3092
|
+
...morphColumn.on
|
|
3093
|
+
}
|
|
3094
|
+
});
|
|
3095
|
+
return subAlias;
|
|
3096
|
+
}
|
|
3097
|
+
if (joinTable2) {
|
|
3098
|
+
const joinAlias = qb.getAlias();
|
|
3099
|
+
qb.join({
|
|
3100
|
+
alias: joinAlias,
|
|
3101
|
+
referencedTable: joinTable2.name,
|
|
3102
|
+
referencedColumn: joinTable2.morphColumn.idColumn.name,
|
|
3103
|
+
rootColumn: joinTable2.morphColumn.idColumn.referencedColumn,
|
|
3104
|
+
rootTable: alias,
|
|
3105
|
+
on: {
|
|
3106
|
+
[joinTable2.morphColumn.typeColumn.name]: uid,
|
|
3107
|
+
field: attributeName
|
|
3108
|
+
}
|
|
3109
|
+
});
|
|
3110
|
+
const subAlias = refAlias || qb.getAlias();
|
|
3111
|
+
qb.join({
|
|
3112
|
+
alias: subAlias,
|
|
3113
|
+
referencedTable: targetMeta.tableName,
|
|
3114
|
+
referencedColumn: joinTable2.joinColumn.referencedColumn,
|
|
3115
|
+
rootColumn: joinTable2.joinColumn.name,
|
|
3116
|
+
rootTable: joinAlias
|
|
3117
|
+
});
|
|
3118
|
+
return subAlias;
|
|
3119
|
+
}
|
|
3120
|
+
return alias;
|
|
3121
|
+
}
|
|
2925
3122
|
const { joinColumn } = attribute;
|
|
2926
3123
|
if (joinColumn) {
|
|
2927
3124
|
const subAlias = refAlias || qb.getAlias();
|
|
@@ -3015,6 +3212,7 @@ const processOrderBy = (orderBy, ctx) => {
|
|
|
3015
3212
|
}
|
|
3016
3213
|
throw new Error("Invalid orderBy syntax");
|
|
3017
3214
|
};
|
|
3215
|
+
const joinColPrefix = "__strapi";
|
|
3018
3216
|
const XtoOne = async (input, ctx) => {
|
|
3019
3217
|
const { attribute, attributeName, results, populateValue, targetMeta, isCount } = input;
|
|
3020
3218
|
const { db, qb } = ctx;
|
|
@@ -3043,6 +3241,8 @@ const XtoOne = async (input, ctx) => {
|
|
|
3043
3241
|
const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
|
|
3044
3242
|
const alias = qb2.getAlias();
|
|
3045
3243
|
const joinColAlias = `${alias}.${joinColumnName}`;
|
|
3244
|
+
const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
|
|
3245
|
+
const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
|
|
3046
3246
|
const referencedValues = ___default.default.uniq(
|
|
3047
3247
|
results.map((r) => r[referencedColumnName]).filter((value) => !___default.default.isNil(value))
|
|
3048
3248
|
);
|
|
@@ -3061,10 +3261,13 @@ const XtoOne = async (input, ctx) => {
|
|
|
3061
3261
|
rootTable: qb2.alias,
|
|
3062
3262
|
on: joinTable.on
|
|
3063
3263
|
}).select([joinColAlias, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3064
|
-
const map2 = rows2.reduce(
|
|
3065
|
-
map3
|
|
3066
|
-
|
|
3067
|
-
|
|
3264
|
+
const map2 = rows2.reduce(
|
|
3265
|
+
(map3, row) => {
|
|
3266
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3267
|
+
return map3;
|
|
3268
|
+
},
|
|
3269
|
+
{}
|
|
3270
|
+
);
|
|
3068
3271
|
results.forEach((result) => {
|
|
3069
3272
|
result[attributeName] = map2[result[referencedColumnName]] || { count: 0 };
|
|
3070
3273
|
});
|
|
@@ -3084,8 +3287,8 @@ const XtoOne = async (input, ctx) => {
|
|
|
3084
3287
|
rootTable: qb2.alias,
|
|
3085
3288
|
on: joinTable.on,
|
|
3086
3289
|
orderBy: joinTable.orderBy
|
|
3087
|
-
}).addSelect(
|
|
3088
|
-
const map = ___default.default.groupBy(
|
|
3290
|
+
}).addSelect(joinColSelect).where({ [joinColAlias]: referencedValues }).execute({ mapResults: false });
|
|
3291
|
+
const map = ___default.default.groupBy(joinColRenameAs)(rows);
|
|
3089
3292
|
results.forEach((result) => {
|
|
3090
3293
|
result[attributeName] = fromTargetRow(___default.default.first(map[result[referencedColumnName]]));
|
|
3091
3294
|
});
|
|
@@ -3119,6 +3322,8 @@ const oneToMany = async (input, ctx) => {
|
|
|
3119
3322
|
const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
|
|
3120
3323
|
const alias = qb2.getAlias();
|
|
3121
3324
|
const joinColAlias = `${alias}.${joinColumnName}`;
|
|
3325
|
+
const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
|
|
3326
|
+
const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
|
|
3122
3327
|
const referencedValues = ___default.default.uniq(
|
|
3123
3328
|
results.map((r) => r[referencedColumnName]).filter((value) => !___default.default.isNil(value))
|
|
3124
3329
|
);
|
|
@@ -3136,11 +3341,14 @@ const oneToMany = async (input, ctx) => {
|
|
|
3136
3341
|
rootColumn: joinTable.inverseJoinColumn.referencedColumn,
|
|
3137
3342
|
rootTable: qb2.alias,
|
|
3138
3343
|
on: joinTable.on
|
|
3139
|
-
}).select([
|
|
3140
|
-
const map2 = rows2.reduce(
|
|
3141
|
-
map3
|
|
3142
|
-
|
|
3143
|
-
|
|
3344
|
+
}).select([joinColSelect, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3345
|
+
const map2 = rows2.reduce(
|
|
3346
|
+
(map3, row) => {
|
|
3347
|
+
map3[row[joinColRenameAs]] = { count: Number(row.count) };
|
|
3348
|
+
return map3;
|
|
3349
|
+
},
|
|
3350
|
+
{}
|
|
3351
|
+
);
|
|
3144
3352
|
results.forEach((result) => {
|
|
3145
3353
|
result[attributeName] = map2[result[referencedColumnName]] || { count: 0 };
|
|
3146
3354
|
});
|
|
@@ -3160,8 +3368,8 @@ const oneToMany = async (input, ctx) => {
|
|
|
3160
3368
|
rootTable: qb2.alias,
|
|
3161
3369
|
on: joinTable.on,
|
|
3162
3370
|
orderBy: ___default.default.mapValues((v) => populateValue.ordering || v, joinTable.orderBy)
|
|
3163
|
-
}).addSelect(
|
|
3164
|
-
const map = ___default.default.groupBy(
|
|
3371
|
+
}).addSelect(joinColSelect).where({ [joinColAlias]: referencedValues }).execute({ mapResults: false });
|
|
3372
|
+
const map = ___default.default.groupBy(joinColRenameAs)(rows);
|
|
3165
3373
|
results.forEach((r) => {
|
|
3166
3374
|
r[attributeName] = fromTargetRow(map[r[referencedColumnName]] || []);
|
|
3167
3375
|
});
|
|
@@ -3176,6 +3384,8 @@ const manyToMany = async (input, ctx) => {
|
|
|
3176
3384
|
const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
|
|
3177
3385
|
const alias = populateQb.getAlias();
|
|
3178
3386
|
const joinColAlias = `${alias}.${joinColumnName}`;
|
|
3387
|
+
const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
|
|
3388
|
+
const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
|
|
3179
3389
|
const referencedValues = ___default.default.uniq(
|
|
3180
3390
|
results.map((r) => r[referencedColumnName]).filter((value) => !___default.default.isNil(value))
|
|
3181
3391
|
);
|
|
@@ -3194,10 +3404,13 @@ const manyToMany = async (input, ctx) => {
|
|
|
3194
3404
|
rootTable: populateQb.alias,
|
|
3195
3405
|
on: joinTable.on
|
|
3196
3406
|
}).select([joinColAlias, populateQb.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3197
|
-
const map2 = rows2.reduce(
|
|
3198
|
-
map3
|
|
3199
|
-
|
|
3200
|
-
|
|
3407
|
+
const map2 = rows2.reduce(
|
|
3408
|
+
(map3, row) => {
|
|
3409
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3410
|
+
return map3;
|
|
3411
|
+
},
|
|
3412
|
+
{}
|
|
3413
|
+
);
|
|
3201
3414
|
results.forEach((result) => {
|
|
3202
3415
|
result[attributeName] = map2[result[referencedColumnName]] || { count: 0 };
|
|
3203
3416
|
});
|
|
@@ -3217,8 +3430,8 @@ const manyToMany = async (input, ctx) => {
|
|
|
3217
3430
|
rootTable: populateQb.alias,
|
|
3218
3431
|
on: joinTable.on,
|
|
3219
3432
|
orderBy: ___default.default.mapValues((v) => populateValue.ordering || v, joinTable.orderBy)
|
|
3220
|
-
}).addSelect(
|
|
3221
|
-
const map = ___default.default.groupBy(
|
|
3433
|
+
}).addSelect(joinColSelect).where({ [joinColAlias]: referencedValues }).execute({ mapResults: false });
|
|
3434
|
+
const map = ___default.default.groupBy(joinColRenameAs)(rows);
|
|
3222
3435
|
results.forEach((result) => {
|
|
3223
3436
|
result[attributeName] = fromTargetRow(map[result[referencedColumnName]] || []);
|
|
3224
3437
|
});
|
|
@@ -3544,7 +3757,7 @@ const castValue = (value, attribute) => {
|
|
|
3544
3757
|
};
|
|
3545
3758
|
const processSingleAttributeWhere = (attribute, where, operator = "$eq") => {
|
|
3546
3759
|
if (!isRecord$1(where)) {
|
|
3547
|
-
if (utils
|
|
3760
|
+
if (utils.isOperatorOfType("cast", operator)) {
|
|
3548
3761
|
return castValue(where, attribute);
|
|
3549
3762
|
}
|
|
3550
3763
|
return where;
|
|
@@ -3552,7 +3765,7 @@ const processSingleAttributeWhere = (attribute, where, operator = "$eq") => {
|
|
|
3552
3765
|
const filters = {};
|
|
3553
3766
|
for (const key of Object.keys(where)) {
|
|
3554
3767
|
const value = where[key];
|
|
3555
|
-
if (!utils
|
|
3768
|
+
if (!utils.isOperatorOfType("where", key)) {
|
|
3556
3769
|
throw new Error(`Undefined attribute level operator ${key}`);
|
|
3557
3770
|
}
|
|
3558
3771
|
filters[key] = processAttributeWhere(attribute, value, key);
|
|
@@ -3571,6 +3784,31 @@ const processNested = (where, ctx) => {
|
|
|
3571
3784
|
}
|
|
3572
3785
|
return processWhere(where, ctx);
|
|
3573
3786
|
};
|
|
3787
|
+
const processRelationWhere = (where, ctx) => {
|
|
3788
|
+
const { qb, alias } = ctx;
|
|
3789
|
+
const idAlias = qb.aliasColumn("id", alias);
|
|
3790
|
+
if (!isRecord$1(where)) {
|
|
3791
|
+
return { [idAlias]: where };
|
|
3792
|
+
}
|
|
3793
|
+
const keys = Object.keys(where);
|
|
3794
|
+
const operatorKeys = keys.filter((key) => utils.isOperator(key));
|
|
3795
|
+
if (operatorKeys.length > 0 && operatorKeys.length !== keys.length) {
|
|
3796
|
+
throw new Error(`Operator and non-operator keys cannot be mixed in a relation where clause`);
|
|
3797
|
+
}
|
|
3798
|
+
if (operatorKeys.length > 1) {
|
|
3799
|
+
throw new Error(
|
|
3800
|
+
`Only one operator key is allowed in a relation where clause, but found: ${operatorKeys}`
|
|
3801
|
+
);
|
|
3802
|
+
}
|
|
3803
|
+
if (operatorKeys.length === 1) {
|
|
3804
|
+
const operator = operatorKeys[0];
|
|
3805
|
+
if (utils.isOperatorOfType("group", operator)) {
|
|
3806
|
+
return processWhere(where, ctx);
|
|
3807
|
+
}
|
|
3808
|
+
return { [idAlias]: { [operator]: processNested(where[operator], ctx) } };
|
|
3809
|
+
}
|
|
3810
|
+
return processWhere(where, ctx);
|
|
3811
|
+
};
|
|
3574
3812
|
function processWhere(where, ctx) {
|
|
3575
3813
|
if (!_.isArray(where) && !isRecord$1(where)) {
|
|
3576
3814
|
throw new Error("Where must be an array or an object");
|
|
@@ -3583,7 +3821,10 @@ function processWhere(where, ctx) {
|
|
|
3583
3821
|
const filters = {};
|
|
3584
3822
|
for (const key of Object.keys(where)) {
|
|
3585
3823
|
const value = where[key];
|
|
3586
|
-
if (utils
|
|
3824
|
+
if (utils.isOperatorOfType("group", key)) {
|
|
3825
|
+
if (!Array.isArray(value)) {
|
|
3826
|
+
throw new Error(`Operator ${key} must be an array`);
|
|
3827
|
+
}
|
|
3587
3828
|
filters[key] = value.map((sub) => processNested(sub, ctx));
|
|
3588
3829
|
continue;
|
|
3589
3830
|
}
|
|
@@ -3591,7 +3832,7 @@ function processWhere(where, ctx) {
|
|
|
3591
3832
|
filters[key] = processNested(value, ctx);
|
|
3592
3833
|
continue;
|
|
3593
3834
|
}
|
|
3594
|
-
if (utils
|
|
3835
|
+
if (utils.isOperatorOfType("where", key)) {
|
|
3595
3836
|
throw new Error(
|
|
3596
3837
|
`Only $and, $or and $not can only be used as root level operators. Found ${key}.`
|
|
3597
3838
|
);
|
|
@@ -3607,15 +3848,12 @@ function processWhere(where, ctx) {
|
|
|
3607
3848
|
attributeName: key,
|
|
3608
3849
|
attribute
|
|
3609
3850
|
});
|
|
3610
|
-
|
|
3851
|
+
const nestedWhere = processRelationWhere(value, {
|
|
3611
3852
|
db,
|
|
3612
3853
|
qb,
|
|
3613
3854
|
alias: subAlias,
|
|
3614
3855
|
uid: attribute.target
|
|
3615
3856
|
});
|
|
3616
|
-
if (!isRecord$1(nestedWhere) || utils$1.isOperatorOfType("where", _.keys(nestedWhere)[0])) {
|
|
3617
|
-
nestedWhere = { [qb.aliasColumn("id", subAlias)]: nestedWhere };
|
|
3618
|
-
}
|
|
3619
3857
|
Object.assign(filters, nestedWhere);
|
|
3620
3858
|
continue;
|
|
3621
3859
|
}
|
|
@@ -3630,7 +3868,7 @@ function processWhere(where, ctx) {
|
|
|
3630
3868
|
return filters;
|
|
3631
3869
|
}
|
|
3632
3870
|
const applyOperator = (qb, column, operator, value) => {
|
|
3633
|
-
if (Array.isArray(value) && !utils
|
|
3871
|
+
if (Array.isArray(value) && !utils.isOperatorOfType("array", operator)) {
|
|
3634
3872
|
return qb.where((subQB) => {
|
|
3635
3873
|
value.forEach(
|
|
3636
3874
|
(subValue) => subQB.orWhere((innerQB) => {
|
|
@@ -3768,8 +4006,8 @@ const applyWhereToColumn = (qb, column, columnWhere) => {
|
|
|
3768
4006
|
}
|
|
3769
4007
|
return qb.where(column, columnWhere);
|
|
3770
4008
|
}
|
|
3771
|
-
const
|
|
3772
|
-
|
|
4009
|
+
const keys = Object.keys(columnWhere);
|
|
4010
|
+
keys.forEach((operator) => {
|
|
3773
4011
|
const value = columnWhere[operator];
|
|
3774
4012
|
applyOperator(qb, column, operator, value);
|
|
3775
4013
|
});
|
|
@@ -4708,21 +4946,24 @@ const sortConnectArray = (connectArr, initialArr = [], strictSort = true) => {
|
|
|
4708
4946
|
(acc, rel) => ({ ...acc, [rel.id]: true }),
|
|
4709
4947
|
{}
|
|
4710
4948
|
);
|
|
4711
|
-
const mappedRelations = connectArr.reduce(
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4949
|
+
const mappedRelations = connectArr.reduce(
|
|
4950
|
+
(mapper, relation) => {
|
|
4951
|
+
const adjacentRelId = relation.position?.before || relation.position?.after;
|
|
4952
|
+
if (!adjacentRelId || !relationInInitialArray[adjacentRelId] && !mapper[adjacentRelId]) {
|
|
4953
|
+
needsSorting = true;
|
|
4954
|
+
}
|
|
4955
|
+
if (mapper[relation.id]) {
|
|
4956
|
+
throw new InvalidRelationError(
|
|
4957
|
+
`The relation with id ${relation.id} is already connected. You cannot connect the same relation twice.`
|
|
4958
|
+
);
|
|
4959
|
+
}
|
|
4960
|
+
return {
|
|
4961
|
+
[relation.id]: { ...relation, computed: false },
|
|
4962
|
+
...mapper
|
|
4963
|
+
};
|
|
4964
|
+
},
|
|
4965
|
+
{}
|
|
4966
|
+
);
|
|
4726
4967
|
if (!needsSorting)
|
|
4727
4968
|
return connectArr;
|
|
4728
4969
|
const computeRelation = (relation, relationsSeenInBranch) => {
|
|
@@ -4830,14 +5071,17 @@ const relationsOrderer = (initArr, idColumn, orderColumn, strict) => {
|
|
|
4830
5071
|
* Get a map between the relation id and its order
|
|
4831
5072
|
*/
|
|
4832
5073
|
getOrderMap() {
|
|
4833
|
-
return ___default$1.default(computedRelations).groupBy("order").reduce(
|
|
4834
|
-
|
|
5074
|
+
return ___default$1.default(computedRelations).groupBy("order").reduce(
|
|
5075
|
+
(acc, relations) => {
|
|
5076
|
+
if (relations[0]?.init)
|
|
5077
|
+
return acc;
|
|
5078
|
+
relations.forEach((relation, idx) => {
|
|
5079
|
+
acc[relation.id] = Math.floor(relation.order) + (idx + 1) / (relations.length + 1);
|
|
5080
|
+
});
|
|
4835
5081
|
return acc;
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
return acc;
|
|
4840
|
-
}, {});
|
|
5082
|
+
},
|
|
5083
|
+
{}
|
|
5084
|
+
);
|
|
4841
5085
|
}
|
|
4842
5086
|
};
|
|
4843
5087
|
};
|
|
@@ -5591,10 +5835,13 @@ const createEntityManager = (db) => {
|
|
|
5591
5835
|
const entry = await this.findOne(uid, {
|
|
5592
5836
|
select: ["id"],
|
|
5593
5837
|
where: { id: entity.id },
|
|
5594
|
-
populate: fieldsArr.reduce(
|
|
5595
|
-
acc
|
|
5596
|
-
|
|
5597
|
-
|
|
5838
|
+
populate: fieldsArr.reduce(
|
|
5839
|
+
(acc, field) => {
|
|
5840
|
+
acc[field] = populate || true;
|
|
5841
|
+
return acc;
|
|
5842
|
+
},
|
|
5843
|
+
{}
|
|
5844
|
+
)
|
|
5598
5845
|
});
|
|
5599
5846
|
if (!entry) {
|
|
5600
5847
|
return null;
|
|
@@ -5660,7 +5907,22 @@ const createStorage = (opts) => {
|
|
|
5660
5907
|
};
|
|
5661
5908
|
};
|
|
5662
5909
|
const wrapTransaction = (db) => (fn) => () => {
|
|
5663
|
-
return db.
|
|
5910
|
+
return db.transaction(({ trx }) => Promise.resolve(fn(trx, db)));
|
|
5911
|
+
};
|
|
5912
|
+
const transformLogMessage = (level, message) => {
|
|
5913
|
+
if (typeof message === "string") {
|
|
5914
|
+
return { level, message };
|
|
5915
|
+
}
|
|
5916
|
+
if (typeof message === "object" && message !== null) {
|
|
5917
|
+
if ("event" in message && "name" in message) {
|
|
5918
|
+
return {
|
|
5919
|
+
level,
|
|
5920
|
+
message: `[internal migration]: ${message.event} ${message?.name}`,
|
|
5921
|
+
timestamp: Date.now()
|
|
5922
|
+
};
|
|
5923
|
+
}
|
|
5924
|
+
}
|
|
5925
|
+
return "";
|
|
5664
5926
|
};
|
|
5665
5927
|
const migrationResolver = ({ name, path: path2, context }) => {
|
|
5666
5928
|
const { db } = context;
|
|
@@ -5690,7 +5952,20 @@ const createUserMigrationProvider = (db) => {
|
|
|
5690
5952
|
const context = { db };
|
|
5691
5953
|
const umzugProvider = new umzug.Umzug({
|
|
5692
5954
|
storage: createStorage({ db, tableName: "strapi_migrations" }),
|
|
5693
|
-
logger:
|
|
5955
|
+
logger: {
|
|
5956
|
+
info(message) {
|
|
5957
|
+
db.logger.info(transformLogMessage("info", message));
|
|
5958
|
+
},
|
|
5959
|
+
warn(message) {
|
|
5960
|
+
db.logger.warn(transformLogMessage("warn", message));
|
|
5961
|
+
},
|
|
5962
|
+
error(message) {
|
|
5963
|
+
db.logger.error(transformLogMessage("error", message));
|
|
5964
|
+
},
|
|
5965
|
+
debug(message) {
|
|
5966
|
+
db.logger.debug(transformLogMessage("debug", message));
|
|
5967
|
+
}
|
|
5968
|
+
},
|
|
5694
5969
|
context,
|
|
5695
5970
|
migrations: {
|
|
5696
5971
|
glob: ["*.{js,sql}", { cwd: dir }],
|
|
@@ -5710,14 +5985,327 @@ const createUserMigrationProvider = (db) => {
|
|
|
5710
5985
|
}
|
|
5711
5986
|
};
|
|
5712
5987
|
};
|
|
5713
|
-
const
|
|
5988
|
+
const QUERIES = {
|
|
5989
|
+
async postgres(knex2, params) {
|
|
5990
|
+
const res = await knex2.raw(
|
|
5991
|
+
`
|
|
5992
|
+
SELECT :tableName:.id as id, string_agg(DISTINCT :inverseJoinColumn:::character varying, ',') as other_ids
|
|
5993
|
+
FROM :tableName:
|
|
5994
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5995
|
+
WHERE document_id IS NULL
|
|
5996
|
+
GROUP BY :tableName:.id, :joinColumn:
|
|
5997
|
+
LIMIT 1;
|
|
5998
|
+
`,
|
|
5999
|
+
params
|
|
6000
|
+
);
|
|
6001
|
+
return res.rows;
|
|
6002
|
+
},
|
|
6003
|
+
async mysql(knex2, params) {
|
|
6004
|
+
const [res] = await knex2.raw(
|
|
6005
|
+
`
|
|
6006
|
+
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids
|
|
6007
|
+
FROM :tableName:
|
|
6008
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
6009
|
+
WHERE document_id IS NULL
|
|
6010
|
+
GROUP BY :tableName:.id, :joinColumn:
|
|
6011
|
+
LIMIT 1;
|
|
6012
|
+
`,
|
|
6013
|
+
params
|
|
6014
|
+
);
|
|
6015
|
+
return res;
|
|
6016
|
+
},
|
|
6017
|
+
async sqlite(knex2, params) {
|
|
6018
|
+
return knex2.raw(
|
|
6019
|
+
`
|
|
6020
|
+
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids
|
|
6021
|
+
FROM :tableName:
|
|
6022
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
6023
|
+
WHERE document_id IS NULL
|
|
6024
|
+
GROUP BY :joinColumn:
|
|
6025
|
+
LIMIT 1;
|
|
6026
|
+
`,
|
|
6027
|
+
params
|
|
6028
|
+
);
|
|
6029
|
+
}
|
|
6030
|
+
};
|
|
6031
|
+
const getNextIdsToCreateDocumentId = async (db, knex2, {
|
|
6032
|
+
joinColumn,
|
|
6033
|
+
inverseJoinColumn,
|
|
6034
|
+
tableName,
|
|
6035
|
+
joinTableName
|
|
6036
|
+
}) => {
|
|
6037
|
+
const res = await QUERIES[db.dialect.client](knex2, {
|
|
6038
|
+
joinColumn,
|
|
6039
|
+
inverseJoinColumn,
|
|
6040
|
+
tableName,
|
|
6041
|
+
joinTableName
|
|
6042
|
+
});
|
|
6043
|
+
if (res.length > 0) {
|
|
6044
|
+
const row = res[0];
|
|
6045
|
+
const otherIds = row.other_ids ? row.other_ids.split(",").map((v) => parseInt(v, 10)) : [];
|
|
6046
|
+
return [row.id, ...otherIds];
|
|
6047
|
+
}
|
|
6048
|
+
return [];
|
|
6049
|
+
};
|
|
6050
|
+
const migrateDocumentIdsWithLocalizations = async (db, knex2, meta) => {
|
|
6051
|
+
const singularName = meta.singularName.toLowerCase();
|
|
6052
|
+
const joinColumn = identifiers.getJoinColumnAttributeIdName(singularName);
|
|
6053
|
+
const inverseJoinColumn = identifiers.getInverseJoinColumnAttributeIdName(singularName);
|
|
6054
|
+
let ids;
|
|
6055
|
+
do {
|
|
6056
|
+
ids = await getNextIdsToCreateDocumentId(db, knex2, {
|
|
6057
|
+
joinColumn,
|
|
6058
|
+
inverseJoinColumn,
|
|
6059
|
+
tableName: meta.tableName,
|
|
6060
|
+
joinTableName: identifiers.getJoinTableName(meta.tableName, `localizations`)
|
|
6061
|
+
});
|
|
6062
|
+
if (ids.length > 0) {
|
|
6063
|
+
await knex2(meta.tableName).update({ document_id: cuid2.createId() }).whereIn("id", ids);
|
|
6064
|
+
}
|
|
6065
|
+
} while (ids.length > 0);
|
|
6066
|
+
};
|
|
6067
|
+
const migrationDocumentIds = async (db, knex2, meta) => {
|
|
6068
|
+
let updatedRows;
|
|
6069
|
+
do {
|
|
6070
|
+
updatedRows = await knex2(meta.tableName).update({ document_id: cuid2.createId() }).whereIn(
|
|
6071
|
+
"id",
|
|
6072
|
+
knex2(meta.tableName).select("id").from(knex2(meta.tableName).select("id").whereNull("document_id").limit(1).as("sub_query"))
|
|
6073
|
+
);
|
|
6074
|
+
} while (updatedRows > 0);
|
|
6075
|
+
};
|
|
6076
|
+
const createDocumentIdColumn = async (knex2, tableName) => {
|
|
6077
|
+
await knex2.schema.alterTable(tableName, (table) => {
|
|
6078
|
+
table.string("document_id");
|
|
6079
|
+
});
|
|
6080
|
+
};
|
|
6081
|
+
const hasLocalizationsJoinTable = async (knex2, tableName) => {
|
|
6082
|
+
const joinTableName = identifiers.getJoinTableName(tableName, "localizations");
|
|
6083
|
+
return knex2.schema.hasTable(joinTableName);
|
|
6084
|
+
};
|
|
6085
|
+
const createdDocumentId = {
|
|
6086
|
+
name: "5.0.0-02-created-document-id",
|
|
6087
|
+
async up(knex2, db) {
|
|
6088
|
+
for (const meta of db.metadata.values()) {
|
|
6089
|
+
const hasTable = await knex2.schema.hasTable(meta.tableName);
|
|
6090
|
+
if (!hasTable) {
|
|
6091
|
+
continue;
|
|
6092
|
+
}
|
|
6093
|
+
if ("documentId" in meta.attributes) {
|
|
6094
|
+
const hasDocumentIdColumn = await knex2.schema.hasColumn(meta.tableName, "document_id");
|
|
6095
|
+
if (hasDocumentIdColumn) {
|
|
6096
|
+
continue;
|
|
6097
|
+
}
|
|
6098
|
+
await createDocumentIdColumn(knex2, meta.tableName);
|
|
6099
|
+
if (await hasLocalizationsJoinTable(knex2, meta.tableName)) {
|
|
6100
|
+
await migrateDocumentIdsWithLocalizations(db, knex2, meta);
|
|
6101
|
+
} else {
|
|
6102
|
+
await migrationDocumentIds(db, knex2, meta);
|
|
6103
|
+
}
|
|
6104
|
+
}
|
|
6105
|
+
}
|
|
6106
|
+
},
|
|
6107
|
+
async down() {
|
|
6108
|
+
throw new Error("not implemented");
|
|
6109
|
+
}
|
|
6110
|
+
};
|
|
6111
|
+
const debug = createDebug__default.default("strapi::database::migration");
|
|
6112
|
+
const renameIdentifiersLongerThanMaxLength = {
|
|
6113
|
+
name: "5.0.0-rename-identifiers-longer-than-max-length",
|
|
6114
|
+
async up(knex2, db) {
|
|
6115
|
+
const md = db.metadata;
|
|
6116
|
+
const diffs = findDiffs(md);
|
|
6117
|
+
for (const indexDiff of diffs.indexes) {
|
|
6118
|
+
await renameIndex(knex2, db, indexDiff);
|
|
6119
|
+
}
|
|
6120
|
+
for (const columnDiff of diffs.columns) {
|
|
6121
|
+
const { full, short } = columnDiff;
|
|
6122
|
+
const tableName = full.tableName;
|
|
6123
|
+
const hasTable = await knex2.schema.hasTable(tableName);
|
|
6124
|
+
if (hasTable) {
|
|
6125
|
+
const hasColumn = await knex2.schema.hasColumn(tableName, full.columnName);
|
|
6126
|
+
if (hasColumn) {
|
|
6127
|
+
await knex2.schema.alterTable(tableName, async (table) => {
|
|
6128
|
+
debug(`renaming column ${full.columnName} to ${short.columnName}`);
|
|
6129
|
+
table.renameColumn(full.columnName, short.columnName);
|
|
6130
|
+
});
|
|
6131
|
+
}
|
|
6132
|
+
}
|
|
6133
|
+
}
|
|
6134
|
+
for (const tableDiff of diffs.tables) {
|
|
6135
|
+
const hasTable = await knex2.schema.hasTable(tableDiff.full.tableName);
|
|
6136
|
+
if (hasTable) {
|
|
6137
|
+
debug(`renaming table ${tableDiff.full.tableName} to ${tableDiff.short.tableName}`);
|
|
6138
|
+
await knex2.schema.renameTable(tableDiff.full.tableName, tableDiff.short.tableName);
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
},
|
|
6142
|
+
async down() {
|
|
6143
|
+
throw new Error("not implemented");
|
|
6144
|
+
}
|
|
6145
|
+
};
|
|
6146
|
+
const renameIndex = async (knex2, db, diff) => {
|
|
6147
|
+
const client = db.config.connection.client;
|
|
6148
|
+
const short = diff.short;
|
|
6149
|
+
const full = diff.full;
|
|
6150
|
+
if (full.indexName === short.indexName) {
|
|
6151
|
+
debug(`not renaming index ${full.indexName} because name hasn't changed`);
|
|
6152
|
+
return;
|
|
6153
|
+
}
|
|
6154
|
+
if (short.indexName.includes("_lnk_") || full.indexName.includes("_lnk_") || short.indexName.endsWith("fk") || full.indexName.endsWith("fk")) {
|
|
6155
|
+
return;
|
|
6156
|
+
}
|
|
6157
|
+
debug(`renaming index from ${full.indexName} to ${short.indexName}`);
|
|
6158
|
+
try {
|
|
6159
|
+
await knex2.transaction(async (trx) => {
|
|
6160
|
+
if (client === "mysql" || client === "mariadb") {
|
|
6161
|
+
await knex2.raw(
|
|
6162
|
+
`ALTER TABLE \`${full.tableName}\` RENAME INDEX \`${full.indexName}\` TO \`${short.indexName}\``
|
|
6163
|
+
).transacting(trx);
|
|
6164
|
+
} else if (client === "pg" || client === "postgres") {
|
|
6165
|
+
await knex2.raw(`ALTER INDEX "${full.indexName}" RENAME TO "${short.indexName}"`).transacting(trx);
|
|
6166
|
+
} else if (client === "sqlite" || client === "better") {
|
|
6167
|
+
} else {
|
|
6168
|
+
debug("No db client name matches, not creating index");
|
|
6169
|
+
}
|
|
6170
|
+
});
|
|
6171
|
+
} catch (err) {
|
|
6172
|
+
debug(`error creating index: ${JSON.stringify(err)}`);
|
|
6173
|
+
}
|
|
6174
|
+
};
|
|
6175
|
+
const findDiffs = (shortMap) => {
|
|
6176
|
+
const diffs = {
|
|
6177
|
+
tables: [],
|
|
6178
|
+
columns: [],
|
|
6179
|
+
indexes: []
|
|
6180
|
+
};
|
|
6181
|
+
const shortArr = Array.from(shortMap.entries());
|
|
6182
|
+
shortArr.forEach(([, shortObj], index2) => {
|
|
6183
|
+
const fullTableName = identifiers.getUnshortenedName(shortObj.tableName);
|
|
6184
|
+
if (!fullTableName) {
|
|
6185
|
+
throw new Error(`Missing full table name for ${shortObj.tableName}`);
|
|
6186
|
+
}
|
|
6187
|
+
if (shortObj.tableName !== fullTableName) {
|
|
6188
|
+
diffs.tables.push({
|
|
6189
|
+
full: {
|
|
6190
|
+
index: index2,
|
|
6191
|
+
key: "tableName",
|
|
6192
|
+
tableName: fullTableName
|
|
6193
|
+
},
|
|
6194
|
+
short: {
|
|
6195
|
+
index: index2,
|
|
6196
|
+
key: "tableName",
|
|
6197
|
+
tableName: shortObj.tableName
|
|
6198
|
+
}
|
|
6199
|
+
});
|
|
6200
|
+
}
|
|
6201
|
+
for (const attrKey in shortObj.attributes) {
|
|
6202
|
+
if (shortObj.attributes[attrKey].type === "relation") {
|
|
6203
|
+
continue;
|
|
6204
|
+
}
|
|
6205
|
+
const attr = shortObj.attributes[attrKey];
|
|
6206
|
+
const shortColumnName = attr.columnName;
|
|
6207
|
+
const longColumnName = identifiers.getUnshortenedName(shortColumnName);
|
|
6208
|
+
if (!shortColumnName || !longColumnName) {
|
|
6209
|
+
throw new Error(`missing column name(s) for attribute ${JSON.stringify(attr, null, 2)}`);
|
|
6210
|
+
}
|
|
6211
|
+
if (shortColumnName && longColumnName && shortColumnName !== longColumnName) {
|
|
6212
|
+
diffs.columns.push({
|
|
6213
|
+
short: {
|
|
6214
|
+
index: index2,
|
|
6215
|
+
tableName: fullTableName,
|
|
6216
|
+
// NOTE: this means that we must rename columns before tables
|
|
6217
|
+
key: `attributes.${attrKey}`,
|
|
6218
|
+
columnName: shortColumnName
|
|
6219
|
+
},
|
|
6220
|
+
full: {
|
|
6221
|
+
index: index2,
|
|
6222
|
+
tableName: fullTableName,
|
|
6223
|
+
key: `attributes.${attrKey}`,
|
|
6224
|
+
columnName: longColumnName
|
|
6225
|
+
}
|
|
6226
|
+
});
|
|
6227
|
+
}
|
|
6228
|
+
}
|
|
6229
|
+
for (const attrKey in shortObj.indexes) {
|
|
6230
|
+
const shortIndexName = shortObj.indexes[attrKey].name;
|
|
6231
|
+
const longIndexName = identifiers.getUnshortenedName(shortIndexName);
|
|
6232
|
+
if (!longIndexName) {
|
|
6233
|
+
throw new Error(`Missing full index name for ${shortIndexName}`);
|
|
6234
|
+
}
|
|
6235
|
+
if (shortIndexName && longIndexName && shortIndexName !== longIndexName) {
|
|
6236
|
+
diffs.indexes.push({
|
|
6237
|
+
short: {
|
|
6238
|
+
index: index2,
|
|
6239
|
+
tableName: fullTableName,
|
|
6240
|
+
// NOTE: this means that we must rename columns before tables
|
|
6241
|
+
key: `indexes.${attrKey}`,
|
|
6242
|
+
indexName: shortIndexName
|
|
6243
|
+
},
|
|
6244
|
+
full: {
|
|
6245
|
+
index: index2,
|
|
6246
|
+
tableName: fullTableName,
|
|
6247
|
+
key: `indexes.${attrKey}`,
|
|
6248
|
+
indexName: longIndexName
|
|
6249
|
+
}
|
|
6250
|
+
});
|
|
6251
|
+
}
|
|
6252
|
+
}
|
|
6253
|
+
});
|
|
6254
|
+
return diffs;
|
|
6255
|
+
};
|
|
6256
|
+
const createLocaleColumn = async (db, tableName) => {
|
|
6257
|
+
await db.schema.alterTable(tableName, (table) => {
|
|
6258
|
+
table.string("locale");
|
|
6259
|
+
});
|
|
6260
|
+
};
|
|
6261
|
+
const createdLocale = {
|
|
6262
|
+
name: "5.0.0-03-created-locale",
|
|
6263
|
+
async up(knex2, db) {
|
|
6264
|
+
for (const meta of db.metadata.values()) {
|
|
6265
|
+
const hasTable = await knex2.schema.hasTable(meta.tableName);
|
|
6266
|
+
if (!hasTable) {
|
|
6267
|
+
continue;
|
|
6268
|
+
}
|
|
6269
|
+
const uid = meta.uid;
|
|
6270
|
+
const model = strapi.getModel(uid);
|
|
6271
|
+
if (!model) {
|
|
6272
|
+
continue;
|
|
6273
|
+
}
|
|
6274
|
+
if (_.isNil(meta.attributes.locale)) {
|
|
6275
|
+
await createLocaleColumn(knex2, meta.tableName);
|
|
6276
|
+
}
|
|
6277
|
+
}
|
|
6278
|
+
},
|
|
6279
|
+
async down() {
|
|
6280
|
+
throw new Error("not implemented");
|
|
6281
|
+
}
|
|
6282
|
+
};
|
|
6283
|
+
const internalMigrations = [
|
|
6284
|
+
renameIdentifiersLongerThanMaxLength,
|
|
6285
|
+
createdDocumentId,
|
|
6286
|
+
createdLocale
|
|
6287
|
+
];
|
|
5714
6288
|
const createInternalMigrationProvider = (db) => {
|
|
5715
6289
|
const context = { db };
|
|
6290
|
+
const migrations = [...internalMigrations];
|
|
5716
6291
|
const umzugProvider = new umzug.Umzug({
|
|
5717
6292
|
storage: createStorage({ db, tableName: "strapi_migrations_internal" }),
|
|
5718
|
-
logger:
|
|
6293
|
+
logger: {
|
|
6294
|
+
info(message) {
|
|
6295
|
+
db.logger.debug(transformLogMessage("info", message));
|
|
6296
|
+
},
|
|
6297
|
+
warn(message) {
|
|
6298
|
+
db.logger.warn(transformLogMessage("warn", message));
|
|
6299
|
+
},
|
|
6300
|
+
error(message) {
|
|
6301
|
+
db.logger.error(transformLogMessage("error", message));
|
|
6302
|
+
},
|
|
6303
|
+
debug(message) {
|
|
6304
|
+
db.logger.debug(transformLogMessage("debug", message));
|
|
6305
|
+
}
|
|
6306
|
+
},
|
|
5719
6307
|
context,
|
|
5720
|
-
migrations:
|
|
6308
|
+
migrations: () => migrations.map((migration) => {
|
|
5721
6309
|
return {
|
|
5722
6310
|
name: migration.name,
|
|
5723
6311
|
up: wrapTransaction(context.db)(migration.up),
|
|
@@ -5726,6 +6314,9 @@ const createInternalMigrationProvider = (db) => {
|
|
|
5726
6314
|
})
|
|
5727
6315
|
});
|
|
5728
6316
|
return {
|
|
6317
|
+
async register(migration) {
|
|
6318
|
+
migrations.push(migration);
|
|
6319
|
+
},
|
|
5729
6320
|
async shouldRun() {
|
|
5730
6321
|
const pendingMigrations = await umzugProvider.pending();
|
|
5731
6322
|
return pendingMigrations.length > 0;
|
|
@@ -5739,8 +6330,13 @@ const createInternalMigrationProvider = (db) => {
|
|
|
5739
6330
|
};
|
|
5740
6331
|
};
|
|
5741
6332
|
const createMigrationsProvider = (db) => {
|
|
5742
|
-
const
|
|
6333
|
+
const userProvider = createUserMigrationProvider(db);
|
|
6334
|
+
const internalProvider = createInternalMigrationProvider(db);
|
|
6335
|
+
const providers = [userProvider, internalProvider];
|
|
5743
6336
|
return {
|
|
6337
|
+
providers: {
|
|
6338
|
+
internal: internalProvider
|
|
6339
|
+
},
|
|
5744
6340
|
async shouldRun() {
|
|
5745
6341
|
const shouldRunResponses = await Promise.all(
|
|
5746
6342
|
providers.map((provider) => provider.shouldRun())
|
|
@@ -5936,8 +6532,14 @@ const validateBidirectionalRelations = async (db) => {
|
|
|
5936
6532
|
for (const { relation, invRelation } of invalidLinks) {
|
|
5937
6533
|
const modelMetadata = db.metadata.get(invRelation.target);
|
|
5938
6534
|
const invModelMetadata = db.metadata.get(relation.target);
|
|
5939
|
-
const joinTableName = getJoinTableName(
|
|
5940
|
-
|
|
6535
|
+
const joinTableName = identifiers.getJoinTableName(
|
|
6536
|
+
_.snakeCase(modelMetadata.tableName),
|
|
6537
|
+
_.snakeCase(invRelation.inversedBy)
|
|
6538
|
+
);
|
|
6539
|
+
const inverseJoinTableName = identifiers.getJoinTableName(
|
|
6540
|
+
_.snakeCase(invModelMetadata.tableName),
|
|
6541
|
+
_.snakeCase(relation.inversedBy)
|
|
6542
|
+
);
|
|
5941
6543
|
const joinTableEmpty = await isLinkTableEmpty(db, joinTableName);
|
|
5942
6544
|
const inverseJoinTableEmpty = await isLinkTableEmpty(db, inverseJoinTableName);
|
|
5943
6545
|
if (joinTableEmpty) {
|
|
@@ -5972,6 +6574,7 @@ class Database {
|
|
|
5972
6574
|
migrations;
|
|
5973
6575
|
lifecycles;
|
|
5974
6576
|
entityManager;
|
|
6577
|
+
logger;
|
|
5975
6578
|
constructor(config) {
|
|
5976
6579
|
this.config = {
|
|
5977
6580
|
...config,
|
|
@@ -5983,7 +6586,7 @@ class Database {
|
|
|
5983
6586
|
};
|
|
5984
6587
|
this.dialect = getDialect(this);
|
|
5985
6588
|
this.dialect.configure();
|
|
5986
|
-
this.metadata = createMetadata();
|
|
6589
|
+
this.metadata = createMetadata([]);
|
|
5987
6590
|
this.connection = createConnection(this.config.connection, {
|
|
5988
6591
|
pool: { afterCreate: afterCreate(this) }
|
|
5989
6592
|
});
|
|
@@ -5991,6 +6594,7 @@ class Database {
|
|
|
5991
6594
|
this.migrations = createMigrationsProvider(this);
|
|
5992
6595
|
this.lifecycles = createLifecyclesProvider(this);
|
|
5993
6596
|
this.entityManager = createEntityManager(this);
|
|
6597
|
+
this.logger = config.logger ?? console;
|
|
5994
6598
|
}
|
|
5995
6599
|
async init({ models }) {
|
|
5996
6600
|
this.metadata.loadModels(models);
|
|
@@ -6060,9 +6664,7 @@ class Database {
|
|
|
6060
6664
|
await this.connection.destroy();
|
|
6061
6665
|
}
|
|
6062
6666
|
}
|
|
6063
|
-
const utils = { identifiers };
|
|
6064
6667
|
exports.Database = Database;
|
|
6065
6668
|
exports.errors = index;
|
|
6066
6669
|
exports.isKnexQuery = isKnexQuery;
|
|
6067
|
-
exports.utils = utils;
|
|
6068
6670
|
//# sourceMappingURL=index.js.map
|