@strapi/database 5.0.0-beta.0 → 5.0.0-beta.10
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 +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +899 -340
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +897 -338
- 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/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 +9 -7
- package/dist/utils/identifiers/shortener.d.ts +0 -73
- package/dist/utils/identifiers/shortener.d.ts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fse from "fs-extra";
|
|
3
3
|
import createDebug from "debug";
|
|
4
|
-
import _, { isNil, castArray, prop, omit, isInteger, snakeCase, partition, sumBy, toString, toNumber, isString as isString$1, padCharsEnd, isArray,
|
|
4
|
+
import _, { isNil, castArray, prop, omit, isInteger, snakeCase, partition, sumBy, cloneDeep, toString, toNumber, isString as isString$1, padCharsEnd, isArray, isPlainObject, isFinite, groupBy, pipe, mapValues, map, isEmpty, maxBy, pick, has, uniqBy, isNull, differenceWith, isEqual, compact, difference, isObject, isNumber as isNumber$1, isUndefined, uniqWith } from "lodash/fp";
|
|
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 } from "@strapi/utils";
|
|
8
|
+
import { isOperatorOfType, isOperator } from "@strapi/utils";
|
|
9
9
|
import KnexBuilder from "knex/lib/query/querybuilder";
|
|
10
10
|
import KnexRaw from "knex/lib/raw";
|
|
11
11
|
import { Readable } from "stream";
|
|
12
12
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
13
13
|
import _$1 from "lodash";
|
|
14
14
|
import { Umzug } from "umzug";
|
|
15
|
+
import { createId } from "@paralleldrive/cuid2";
|
|
15
16
|
import { strict } from "assert";
|
|
16
17
|
import knex from "knex";
|
|
17
18
|
class Dialect {
|
|
@@ -975,7 +976,7 @@ const getDialect = (db) => {
|
|
|
975
976
|
const dialect = new constructor(db, dialectName);
|
|
976
977
|
return dialect;
|
|
977
978
|
};
|
|
978
|
-
const debug$
|
|
979
|
+
const debug$2 = createDebug("strapi::database");
|
|
979
980
|
const createSchemaBuilder = (db) => {
|
|
980
981
|
const helpers2 = createHelpers(db);
|
|
981
982
|
return {
|
|
@@ -1001,12 +1002,12 @@ const createSchemaBuilder = (db) => {
|
|
|
1001
1002
|
*/
|
|
1002
1003
|
async createTables(tables, trx) {
|
|
1003
1004
|
for (const table of tables) {
|
|
1004
|
-
debug$
|
|
1005
|
+
debug$2(`Creating table: ${table.name}`);
|
|
1005
1006
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1006
1007
|
await helpers2.createTable(schemaBuilder, table);
|
|
1007
1008
|
}
|
|
1008
1009
|
for (const table of tables) {
|
|
1009
|
-
debug$
|
|
1010
|
+
debug$2(`Creating table foreign keys: ${table.name}`);
|
|
1010
1011
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1011
1012
|
await helpers2.createTableForeignKeys(schemaBuilder, table);
|
|
1012
1013
|
}
|
|
@@ -1037,18 +1038,18 @@ const createSchemaBuilder = (db) => {
|
|
|
1037
1038
|
await this.createTables(schemaDiff.tables.added, trx);
|
|
1038
1039
|
if (forceMigration) {
|
|
1039
1040
|
for (const table of schemaDiff.tables.removed) {
|
|
1040
|
-
debug$
|
|
1041
|
+
debug$2(`Removing table foreign keys: ${table.name}`);
|
|
1041
1042
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1042
1043
|
await helpers2.dropTableForeignKeys(schemaBuilder, table);
|
|
1043
1044
|
}
|
|
1044
1045
|
for (const table of schemaDiff.tables.removed) {
|
|
1045
|
-
debug$
|
|
1046
|
+
debug$2(`Removing table: ${table.name}`);
|
|
1046
1047
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1047
1048
|
await helpers2.dropTable(schemaBuilder, table);
|
|
1048
1049
|
}
|
|
1049
1050
|
}
|
|
1050
1051
|
for (const table of schemaDiff.tables.updated) {
|
|
1051
|
-
debug$
|
|
1052
|
+
debug$2(`Updating table: ${table.name}`);
|
|
1052
1053
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1053
1054
|
await helpers2.alterTable(schemaBuilder, table);
|
|
1054
1055
|
}
|
|
@@ -1143,28 +1144,37 @@ const createHelpers = (db) => {
|
|
|
1143
1144
|
};
|
|
1144
1145
|
const alterTable = async (schemaBuilder, table) => {
|
|
1145
1146
|
await schemaBuilder.alterTable(table.name, (tableBuilder) => {
|
|
1146
|
-
for (const removedIndex of table.indexes.removed) {
|
|
1147
|
-
debug$1(`Dropping index ${removedIndex.name}`);
|
|
1148
|
-
dropIndex(tableBuilder, removedIndex);
|
|
1149
|
-
}
|
|
1150
|
-
for (const updateddIndex of table.indexes.updated) {
|
|
1151
|
-
debug$1(`Dropping updated index ${updateddIndex.name}`);
|
|
1152
|
-
dropIndex(tableBuilder, updateddIndex.object);
|
|
1153
|
-
}
|
|
1154
1147
|
for (const removedForeignKey of table.foreignKeys.removed) {
|
|
1155
|
-
debug$
|
|
1148
|
+
debug$2(`Dropping foreign key ${removedForeignKey.name} on ${table.name}`);
|
|
1156
1149
|
dropForeignKey(tableBuilder, removedForeignKey);
|
|
1157
1150
|
}
|
|
1158
1151
|
for (const updatedForeignKey of table.foreignKeys.updated) {
|
|
1159
|
-
debug$
|
|
1152
|
+
debug$2(`Dropping updated foreign key ${updatedForeignKey.name} on ${table.name}`);
|
|
1160
1153
|
dropForeignKey(tableBuilder, updatedForeignKey.object);
|
|
1161
1154
|
}
|
|
1162
1155
|
for (const removedColumn of table.columns.removed) {
|
|
1163
|
-
debug$
|
|
1156
|
+
debug$2(`Dropping column ${removedColumn.name} on ${table.name}`);
|
|
1164
1157
|
dropColumn(tableBuilder, removedColumn);
|
|
1165
1158
|
}
|
|
1159
|
+
const isMySQL = db.config.connection.client === "mysql";
|
|
1160
|
+
const ignoreForeignKeyNames = isMySQL ? [
|
|
1161
|
+
...table.foreignKeys.removed.map((fk) => fk.name),
|
|
1162
|
+
...table.foreignKeys.updated.map((fk) => fk.name)
|
|
1163
|
+
] : [];
|
|
1164
|
+
for (const removedIndex of table.indexes.removed) {
|
|
1165
|
+
if (!ignoreForeignKeyNames.includes(removedIndex.name)) {
|
|
1166
|
+
debug$2(`Dropping index ${removedIndex.name} on ${table.name}`);
|
|
1167
|
+
dropIndex(tableBuilder, removedIndex);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
for (const updatedIndex of table.indexes.updated) {
|
|
1171
|
+
if (!ignoreForeignKeyNames.includes(updatedIndex.name)) {
|
|
1172
|
+
debug$2(`Dropping updated index ${updatedIndex.name} on ${table.name}`);
|
|
1173
|
+
dropIndex(tableBuilder, updatedIndex.object);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1166
1176
|
for (const updatedColumn of table.columns.updated) {
|
|
1167
|
-
debug$
|
|
1177
|
+
debug$2(`Updating column ${updatedColumn.name} on ${table.name}`);
|
|
1168
1178
|
const { object } = updatedColumn;
|
|
1169
1179
|
if (object.type === "increments") {
|
|
1170
1180
|
createColumn2(tableBuilder, { ...object, type: "integer" }).alter();
|
|
@@ -1173,15 +1183,15 @@ const createHelpers = (db) => {
|
|
|
1173
1183
|
}
|
|
1174
1184
|
}
|
|
1175
1185
|
for (const updatedForeignKey of table.foreignKeys.updated) {
|
|
1176
|
-
debug$
|
|
1186
|
+
debug$2(`Recreating updated foreign key ${updatedForeignKey.name} on ${table.name}`);
|
|
1177
1187
|
createForeignKey(tableBuilder, updatedForeignKey.object);
|
|
1178
1188
|
}
|
|
1179
1189
|
for (const updatedIndex of table.indexes.updated) {
|
|
1180
|
-
debug$
|
|
1190
|
+
debug$2(`Recreating updated index ${updatedIndex.name} on ${table.name}`);
|
|
1181
1191
|
createIndex(tableBuilder, updatedIndex.object);
|
|
1182
1192
|
}
|
|
1183
1193
|
for (const addedColumn of table.columns.added) {
|
|
1184
|
-
debug$
|
|
1194
|
+
debug$2(`Creating column ${addedColumn.name} on ${table.name}`);
|
|
1185
1195
|
if (addedColumn.type === "increments" && !db.dialect.canAddIncrements()) {
|
|
1186
1196
|
tableBuilder.integer(addedColumn.name).unsigned();
|
|
1187
1197
|
tableBuilder.primary([addedColumn.name]);
|
|
@@ -1190,11 +1200,11 @@ const createHelpers = (db) => {
|
|
|
1190
1200
|
}
|
|
1191
1201
|
}
|
|
1192
1202
|
for (const addedForeignKey of table.foreignKeys.added) {
|
|
1193
|
-
debug$
|
|
1203
|
+
debug$2(`Creating foreign keys ${addedForeignKey.name} on ${table.name}`);
|
|
1194
1204
|
createForeignKey(tableBuilder, addedForeignKey);
|
|
1195
1205
|
}
|
|
1196
1206
|
for (const addedIndex of table.indexes.added) {
|
|
1197
|
-
debug$
|
|
1207
|
+
debug$2(`Creating index ${addedIndex.name} on ${table.name}`);
|
|
1198
1208
|
createIndex(tableBuilder, addedIndex);
|
|
1199
1209
|
}
|
|
1200
1210
|
});
|
|
@@ -1613,11 +1623,6 @@ const isScalar = (type) => SCALAR_TYPES.includes(type);
|
|
|
1613
1623
|
const isRelation = (type) => type === "relation";
|
|
1614
1624
|
const isScalarAttribute = (attribute) => isScalar(attribute.type);
|
|
1615
1625
|
const isRelationalAttribute = (attribute) => isRelation(attribute.type);
|
|
1616
|
-
const MAX_DB_IDENTIFIER_LENGTH = 0;
|
|
1617
|
-
const HASH_LENGTH = 5;
|
|
1618
|
-
const HASH_SEPARATOR = "";
|
|
1619
|
-
const IDENTIFIER_SEPARATOR = "_";
|
|
1620
|
-
const MIN_TOKEN_LENGTH = 3;
|
|
1621
1626
|
function createHash(data, len) {
|
|
1622
1627
|
if (!isInteger(len) || len <= 0) {
|
|
1623
1628
|
throw new Error(`createHash length must be a positive integer, received ${len}`);
|
|
@@ -1625,201 +1630,334 @@ function createHash(data, len) {
|
|
|
1625
1630
|
const hash = crypto$1.createHash("shake256", { outputLength: Math.ceil(len / 2) }).update(data);
|
|
1626
1631
|
return hash.digest("hex").substring(0, len);
|
|
1627
1632
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1633
|
+
const IDENTIFIER_MAX_LENGTH = 55;
|
|
1634
|
+
class Identifiers {
|
|
1635
|
+
ID_COLUMN = "id";
|
|
1636
|
+
ORDER_COLUMN = "order";
|
|
1637
|
+
FIELD_COLUMN = "field";
|
|
1638
|
+
HASH_LENGTH = 5;
|
|
1639
|
+
HASH_SEPARATOR = "";
|
|
1640
|
+
// no separator is needed, we will just attach hash directly to shortened name
|
|
1641
|
+
IDENTIFIER_SEPARATOR = "_";
|
|
1642
|
+
MIN_TOKEN_LENGTH = 3;
|
|
1643
|
+
// the min characters required at the beginning of a name part
|
|
1644
|
+
// Fixed compression map for suffixes and prefixes
|
|
1645
|
+
#replacementMap = {
|
|
1646
|
+
links: "lnk",
|
|
1647
|
+
order_inv_fk: "oifk",
|
|
1648
|
+
order: "ord",
|
|
1649
|
+
morphs: "mph",
|
|
1650
|
+
index: "idx",
|
|
1651
|
+
inv_fk: "ifk",
|
|
1652
|
+
order_fk: "ofk",
|
|
1653
|
+
id_column_index: "idix",
|
|
1654
|
+
order_index: "oidx",
|
|
1655
|
+
unique: "uq",
|
|
1656
|
+
primary: "pk"
|
|
1657
|
+
};
|
|
1658
|
+
#options;
|
|
1659
|
+
constructor(options) {
|
|
1660
|
+
this.#options = options;
|
|
1645
1661
|
}
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
function getNameFromTokens(nameTokens, maxLength = MAX_DB_IDENTIFIER_LENGTH) {
|
|
1649
|
-
if (!isInteger(maxLength) || maxLength < 0) {
|
|
1650
|
-
throw new Error("maxLength must be a positive integer or 0 (for unlimited length)");
|
|
1662
|
+
get replacementMap() {
|
|
1663
|
+
return this.#replacementMap;
|
|
1651
1664
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
return fullLengthName;
|
|
1665
|
+
get options() {
|
|
1666
|
+
return this.#options;
|
|
1655
1667
|
}
|
|
1656
|
-
|
|
1657
|
-
(
|
|
1658
|
-
|
|
1659
|
-
);
|
|
1660
|
-
const totalIncompressibleLength = sumBy((token) => token.name.length)(incompressible);
|
|
1661
|
-
const totalSeparatorsLength = nameTokens.length * IDENTIFIER_SEPARATOR.length - 1;
|
|
1662
|
-
const available = maxLength - totalIncompressibleLength - totalSeparatorsLength;
|
|
1663
|
-
const availablePerToken = Math.floor(available / compressible.length);
|
|
1664
|
-
if (totalIncompressibleLength + totalSeparatorsLength > maxLength || availablePerToken < MIN_TOKEN_LENGTH) {
|
|
1665
|
-
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1666
|
-
}
|
|
1667
|
-
let surplus = available % compressible.length;
|
|
1668
|
-
const minHashedLength = HASH_LENGTH + HASH_SEPARATOR.length + MIN_TOKEN_LENGTH;
|
|
1669
|
-
const totalLength = nameTokens.reduce((total, token) => {
|
|
1670
|
-
if (token.compressible) {
|
|
1671
|
-
if (token.name.length < availablePerToken) {
|
|
1672
|
-
return total + token.name.length;
|
|
1673
|
-
}
|
|
1674
|
-
return total + minHashedLength;
|
|
1675
|
-
}
|
|
1676
|
-
return total + token.name.length;
|
|
1677
|
-
}, nameTokens.length * IDENTIFIER_SEPARATOR.length - 1);
|
|
1678
|
-
if (maxLength < totalLength) {
|
|
1679
|
-
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1680
|
-
}
|
|
1681
|
-
let deficits = [];
|
|
1682
|
-
compressible.forEach((token) => {
|
|
1683
|
-
const actualLength = token.name.length;
|
|
1684
|
-
if (actualLength < availablePerToken) {
|
|
1685
|
-
surplus += availablePerToken - actualLength;
|
|
1686
|
-
token.allocatedLength = actualLength;
|
|
1687
|
-
} else {
|
|
1688
|
-
token.allocatedLength = availablePerToken;
|
|
1689
|
-
deficits.push(token);
|
|
1668
|
+
mapshortNames = (name) => {
|
|
1669
|
+
if (name in this.replacementMap) {
|
|
1670
|
+
return this.replacementMap[name];
|
|
1690
1671
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1672
|
+
return void 0;
|
|
1673
|
+
};
|
|
1674
|
+
// Generic name handler that must be used by all helper functions
|
|
1675
|
+
/**
|
|
1676
|
+
* TODO: we should be requiring snake_case inputs for all names here, but we
|
|
1677
|
+
* aren't and it will require some refactoring to make it work. Currently if
|
|
1678
|
+
* we get names 'myModel' and 'my_model' they would be converted to the same
|
|
1679
|
+
* final string my_model which generally works but is not entirely safe
|
|
1680
|
+
* */
|
|
1681
|
+
getName = (names, options) => {
|
|
1682
|
+
const tokens = _.castArray(names).map((name) => {
|
|
1683
|
+
return {
|
|
1684
|
+
name,
|
|
1685
|
+
compressible: true
|
|
1686
|
+
};
|
|
1687
|
+
});
|
|
1688
|
+
if (options?.suffix) {
|
|
1689
|
+
tokens.push({
|
|
1690
|
+
name: options.suffix,
|
|
1691
|
+
compressible: false,
|
|
1692
|
+
shortName: this.mapshortNames(options.suffix)
|
|
1693
|
+
});
|
|
1697
1694
|
}
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
break;
|
|
1695
|
+
if (options?.prefix) {
|
|
1696
|
+
tokens.unshift({
|
|
1697
|
+
name: options.prefix,
|
|
1698
|
+
compressible: false,
|
|
1699
|
+
shortName: this.mapshortNames(options.prefix)
|
|
1700
|
+
});
|
|
1705
1701
|
}
|
|
1706
|
-
|
|
1707
|
-
}
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1702
|
+
return this.getNameFromTokens(tokens);
|
|
1703
|
+
};
|
|
1704
|
+
/**
|
|
1705
|
+
* TABLES
|
|
1706
|
+
*/
|
|
1707
|
+
getTableName = (name, options) => {
|
|
1708
|
+
return this.getName(name, options);
|
|
1709
|
+
};
|
|
1710
|
+
getJoinTableName = (collectionName, attributeName, options) => {
|
|
1711
|
+
return this.getName([collectionName, attributeName], {
|
|
1712
|
+
suffix: "links",
|
|
1713
|
+
...options
|
|
1714
|
+
});
|
|
1715
|
+
};
|
|
1716
|
+
getMorphTableName = (collectionName, attributeName, options) => {
|
|
1717
|
+
return this.getName([snakeCase(collectionName), snakeCase(attributeName)], {
|
|
1718
|
+
suffix: "morphs",
|
|
1719
|
+
...options
|
|
1720
|
+
});
|
|
1721
|
+
};
|
|
1722
|
+
/**
|
|
1723
|
+
* COLUMNS
|
|
1724
|
+
*/
|
|
1725
|
+
getColumnName = (attributeName, options) => {
|
|
1726
|
+
return this.getName(attributeName, options);
|
|
1727
|
+
};
|
|
1728
|
+
getJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1729
|
+
return this.getName(attributeName, { suffix: "id", ...options });
|
|
1730
|
+
};
|
|
1731
|
+
getInverseJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1732
|
+
return this.getName(snakeCase(attributeName), { suffix: "id", prefix: "inv", ...options });
|
|
1733
|
+
};
|
|
1734
|
+
getOrderColumnName = (singularName, options) => {
|
|
1735
|
+
return this.getName(singularName, { suffix: "order", ...options });
|
|
1736
|
+
};
|
|
1737
|
+
getInverseOrderColumnName = (singularName, options) => {
|
|
1738
|
+
return this.getName(singularName, {
|
|
1739
|
+
suffix: "order",
|
|
1740
|
+
prefix: "inv",
|
|
1741
|
+
...options
|
|
1742
|
+
});
|
|
1743
|
+
};
|
|
1744
|
+
/**
|
|
1745
|
+
* Morph Join Tables
|
|
1746
|
+
*/
|
|
1747
|
+
getMorphColumnJoinTableIdName = (singularName, options) => {
|
|
1748
|
+
return this.getName(snakeCase(singularName), { suffix: "id", ...options });
|
|
1749
|
+
};
|
|
1750
|
+
getMorphColumnAttributeIdName = (attributeName, options) => {
|
|
1751
|
+
return this.getName(snakeCase(attributeName), { suffix: "id", ...options });
|
|
1752
|
+
};
|
|
1753
|
+
getMorphColumnTypeName = (attributeName, options) => {
|
|
1754
|
+
return this.getName(snakeCase(attributeName), { suffix: "type", ...options });
|
|
1755
|
+
};
|
|
1756
|
+
/**
|
|
1757
|
+
* INDEXES
|
|
1758
|
+
* Note that these methods are generally used to reference full table names + attribute(s), which
|
|
1759
|
+
* may already be shortened strings rather than individual parts.
|
|
1760
|
+
* That is fine and expected to compress the previously incompressible parts of those strings,
|
|
1761
|
+
* because in these cases the relevant information is the table name and we can't really do
|
|
1762
|
+
* any better; shortening the individual parts again might make it even more confusing.
|
|
1763
|
+
*
|
|
1764
|
+
* So for example, the fk for the table `mytable_myattr4567d_localizations` will become
|
|
1765
|
+
* mytable_myattr4567d_loc63bf2_fk
|
|
1766
|
+
*/
|
|
1767
|
+
// base index types
|
|
1768
|
+
getIndexName = (names, options) => {
|
|
1769
|
+
return this.getName(names, { suffix: "index", ...options });
|
|
1770
|
+
};
|
|
1771
|
+
getFkIndexName = (names, options) => {
|
|
1772
|
+
return this.getName(names, { suffix: "fk", ...options });
|
|
1773
|
+
};
|
|
1774
|
+
getUniqueIndexName = (names, options) => {
|
|
1775
|
+
return this.getName(names, { suffix: "unique", ...options });
|
|
1776
|
+
};
|
|
1777
|
+
getPrimaryIndexName = (names, options) => {
|
|
1778
|
+
return this.getName(names, { suffix: "primary", ...options });
|
|
1779
|
+
};
|
|
1780
|
+
// custom index types
|
|
1781
|
+
getInverseFkIndexName = (names, options) => {
|
|
1782
|
+
return this.getName(names, { suffix: "inv_fk", ...options });
|
|
1783
|
+
};
|
|
1784
|
+
getOrderFkIndexName = (names, options) => {
|
|
1785
|
+
return this.getName(names, { suffix: "order_fk", ...options });
|
|
1786
|
+
};
|
|
1787
|
+
getOrderInverseFkIndexName = (names, options) => {
|
|
1788
|
+
return this.getName(names, { suffix: "order_inv_fk", ...options });
|
|
1789
|
+
};
|
|
1790
|
+
getIdColumnIndexName = (names, options) => {
|
|
1791
|
+
return this.getName(names, { suffix: "id_column_index", ...options });
|
|
1792
|
+
};
|
|
1793
|
+
getOrderIndexName = (names, options) => {
|
|
1794
|
+
return this.getName(names, { suffix: "order_index", ...options });
|
|
1795
|
+
};
|
|
1796
|
+
/**
|
|
1797
|
+
* Generates a string with a max length, appending a hash at the end if necessary to keep it unique
|
|
1798
|
+
*
|
|
1799
|
+
* @example
|
|
1800
|
+
* // if we have strings such as "longstring1" and "longstring2" with a max length of 9,
|
|
1801
|
+
* // we don't want to end up with "longstrin" and "longstrin"
|
|
1802
|
+
* // we want something such as "longs0b23" and "longs953f"
|
|
1803
|
+
* const token1 = generateToken("longstring1", 9); // "longs0b23"
|
|
1804
|
+
* const token2 = generateToken("longstring2", 9); // "longs953f"
|
|
1805
|
+
*
|
|
1806
|
+
* @param name - The base name
|
|
1807
|
+
* @param len - The desired length of the token.
|
|
1808
|
+
* @returns The generated token with hash.
|
|
1809
|
+
* @throws Error if the length is not a positive integer, or if the length is too short for the token.
|
|
1810
|
+
* @internal
|
|
1811
|
+
*/
|
|
1812
|
+
getShortenedName = (name, len) => {
|
|
1813
|
+
if (!isInteger(len) || len <= 0) {
|
|
1814
|
+
throw new Error(`tokenWithHash length must be a positive integer, received ${len}`);
|
|
1711
1815
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1816
|
+
if (name.length <= len) {
|
|
1817
|
+
return name;
|
|
1818
|
+
}
|
|
1819
|
+
if (len < this.MIN_TOKEN_LENGTH + this.HASH_LENGTH) {
|
|
1820
|
+
throw new Error(
|
|
1821
|
+
`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}`
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1824
|
+
const availableLength = len - this.HASH_LENGTH - this.HASH_SEPARATOR.length;
|
|
1825
|
+
if (availableLength < this.MIN_TOKEN_LENGTH) {
|
|
1826
|
+
throw new Error(
|
|
1827
|
+
`length for part of identifier minimum is less than min token length (${this.MIN_TOKEN_LENGTH}), received ${len} for token ${name}`
|
|
1828
|
+
);
|
|
1829
|
+
}
|
|
1830
|
+
return `${name.substring(0, availableLength)}${this.HASH_SEPARATOR}${createHash(
|
|
1831
|
+
name,
|
|
1832
|
+
this.HASH_LENGTH
|
|
1833
|
+
)}`;
|
|
1834
|
+
};
|
|
1835
|
+
/**
|
|
1836
|
+
* Constructs a name from an array of name tokens within a specified maximum length. It ensures the final name does not exceed
|
|
1837
|
+
* this limit by selectively compressing tokens marked as compressible. If the name exceeds the maximum length and cannot be
|
|
1838
|
+
* compressed sufficiently, an error is thrown. This function supports dynamic adjustment of token lengths to fit within the
|
|
1839
|
+
* maxLength constraint (that is, it will always make use of all available space), while also ensuring the preservation of
|
|
1840
|
+
* incompressible tokens.
|
|
1841
|
+
* @internal
|
|
1842
|
+
*/
|
|
1843
|
+
getNameFromTokens = (nameTokens) => {
|
|
1844
|
+
const { maxLength } = this.options;
|
|
1845
|
+
if (!isInteger(maxLength) || maxLength < 0) {
|
|
1846
|
+
throw new Error("maxLength must be a positive integer or 0 (for unlimited length)");
|
|
1847
|
+
}
|
|
1848
|
+
const unshortenedName = nameTokens.map((token) => {
|
|
1849
|
+
return token.name;
|
|
1850
|
+
}).join(this.IDENTIFIER_SEPARATOR);
|
|
1851
|
+
if (maxLength === 0) {
|
|
1852
|
+
this.setUnshortenedName(unshortenedName, unshortenedName);
|
|
1853
|
+
return unshortenedName;
|
|
1854
|
+
}
|
|
1855
|
+
const fullLengthName = nameTokens.map((token) => {
|
|
1856
|
+
if (token.compressible) {
|
|
1857
|
+
return token.name;
|
|
1858
|
+
}
|
|
1859
|
+
return token.shortName ?? token.name;
|
|
1860
|
+
}).join(this.IDENTIFIER_SEPARATOR);
|
|
1861
|
+
if (fullLengthName.length <= maxLength) {
|
|
1862
|
+
this.setUnshortenedName(fullLengthName, unshortenedName);
|
|
1863
|
+
return fullLengthName;
|
|
1864
|
+
}
|
|
1865
|
+
const [compressible, incompressible] = partition(
|
|
1866
|
+
(token) => token.compressible,
|
|
1867
|
+
nameTokens
|
|
1717
1868
|
);
|
|
1718
|
-
|
|
1719
|
-
|
|
1869
|
+
const totalIncompressibleLength = sumBy(
|
|
1870
|
+
(token) => token.compressible === false && token.shortName !== void 0 ? token.shortName.length : token.name.length
|
|
1871
|
+
)(incompressible);
|
|
1872
|
+
const totalSeparatorsLength = nameTokens.length * this.IDENTIFIER_SEPARATOR.length - 1;
|
|
1873
|
+
const available = maxLength - totalIncompressibleLength - totalSeparatorsLength;
|
|
1874
|
+
const availablePerToken = Math.floor(available / compressible.length);
|
|
1875
|
+
if (totalIncompressibleLength + totalSeparatorsLength > maxLength || availablePerToken < this.MIN_TOKEN_LENGTH) {
|
|
1876
|
+
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1877
|
+
}
|
|
1878
|
+
let surplus = available % compressible.length;
|
|
1879
|
+
const minHashedLength = this.HASH_LENGTH + this.HASH_SEPARATOR.length + this.MIN_TOKEN_LENGTH;
|
|
1880
|
+
const totalLength = nameTokens.reduce(
|
|
1881
|
+
(total, token) => {
|
|
1882
|
+
if (token.compressible) {
|
|
1883
|
+
if (token.name.length < availablePerToken) {
|
|
1884
|
+
return total + token.name.length;
|
|
1885
|
+
}
|
|
1886
|
+
return total + minHashedLength;
|
|
1887
|
+
}
|
|
1888
|
+
const tokenName = token.shortName ?? token.name;
|
|
1889
|
+
return total + tokenName.length;
|
|
1890
|
+
},
|
|
1891
|
+
nameTokens.length * this.IDENTIFIER_SEPARATOR.length - 1
|
|
1892
|
+
);
|
|
1893
|
+
if (maxLength < totalLength) {
|
|
1894
|
+
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1895
|
+
}
|
|
1896
|
+
let deficits = [];
|
|
1897
|
+
compressible.forEach((token) => {
|
|
1898
|
+
const actualLength = token.name.length;
|
|
1899
|
+
if (actualLength < availablePerToken) {
|
|
1900
|
+
surplus += availablePerToken - actualLength;
|
|
1901
|
+
token.allocatedLength = actualLength;
|
|
1902
|
+
} else {
|
|
1903
|
+
token.allocatedLength = availablePerToken;
|
|
1904
|
+
deficits.push(token);
|
|
1905
|
+
}
|
|
1906
|
+
});
|
|
1907
|
+
function filterAndIncreaseLength(token) {
|
|
1908
|
+
if (token.allocatedLength < token.name.length && surplus > 0) {
|
|
1909
|
+
token.allocatedLength += 1;
|
|
1910
|
+
surplus -= 1;
|
|
1911
|
+
return token.allocatedLength < token.name.length;
|
|
1912
|
+
}
|
|
1913
|
+
return false;
|
|
1914
|
+
}
|
|
1915
|
+
let previousSurplus = surplus + 1;
|
|
1916
|
+
while (surplus > 0 && deficits.length > 0) {
|
|
1917
|
+
deficits = deficits.filter((token) => filterAndIncreaseLength(token));
|
|
1918
|
+
if (surplus === previousSurplus) {
|
|
1919
|
+
break;
|
|
1920
|
+
}
|
|
1921
|
+
previousSurplus = surplus;
|
|
1922
|
+
}
|
|
1923
|
+
const shortenedName = nameTokens.map((token) => {
|
|
1924
|
+
if (token.compressible && "allocatedLength" in token && token.allocatedLength !== void 0) {
|
|
1925
|
+
return this.getShortenedName(token.name, token.allocatedLength);
|
|
1926
|
+
}
|
|
1927
|
+
if (token.compressible === false && token.shortName) {
|
|
1928
|
+
return token.shortName;
|
|
1929
|
+
}
|
|
1930
|
+
return token.name;
|
|
1931
|
+
}).join(this.IDENTIFIER_SEPARATOR);
|
|
1932
|
+
if (shortenedName.length > maxLength) {
|
|
1933
|
+
throw new Error(
|
|
1934
|
+
`name shortening failed to generate a name of the correct maxLength; name ${shortenedName}`
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
this.setUnshortenedName(shortenedName, unshortenedName);
|
|
1938
|
+
return shortenedName;
|
|
1939
|
+
};
|
|
1940
|
+
// We need to be able to find the full-length name for any shortened name, primarily for migration purposes
|
|
1941
|
+
// Therefore we store every name that passes through so we can retrieve the original later
|
|
1942
|
+
nameMap = /* @__PURE__ */ new Map();
|
|
1943
|
+
getUnshortenedName = (shortName) => {
|
|
1944
|
+
return this.nameMap.get(this.serializeKey(shortName)) ?? shortName;
|
|
1945
|
+
};
|
|
1946
|
+
setUnshortenedName = (shortName, fullName) => {
|
|
1947
|
+
if (this.nameMap.get(this.serializeKey(shortName)) && shortName === fullName) {
|
|
1948
|
+
return;
|
|
1949
|
+
}
|
|
1950
|
+
this.nameMap.set(this.serializeKey(shortName), fullName);
|
|
1951
|
+
};
|
|
1952
|
+
serializeKey = (shortName) => {
|
|
1953
|
+
return `${shortName}.${this.options.maxLength}`;
|
|
1954
|
+
};
|
|
1720
1955
|
}
|
|
1721
|
-
const
|
|
1722
|
-
const ORDER_COLUMN = "order";
|
|
1723
|
-
const FIELD_COLUMN = "field";
|
|
1724
|
-
const getName = (names, options) => {
|
|
1725
|
-
const tokens = _.castArray(names).map((name) => {
|
|
1726
|
-
return {
|
|
1727
|
-
name,
|
|
1728
|
-
compressible: true
|
|
1729
|
-
};
|
|
1730
|
-
});
|
|
1731
|
-
if (options?.suffix) {
|
|
1732
|
-
tokens.push({ name: options.suffix, compressible: false });
|
|
1733
|
-
}
|
|
1734
|
-
if (options?.prefix) {
|
|
1735
|
-
tokens.unshift({ name: options.prefix, compressible: false });
|
|
1736
|
-
}
|
|
1737
|
-
const maxLength = options?.maxLength ?? MAX_DB_IDENTIFIER_LENGTH;
|
|
1738
|
-
return getNameFromTokens(tokens, maxLength);
|
|
1739
|
-
};
|
|
1740
|
-
const getTableName = (name, options) => {
|
|
1741
|
-
return getName(name, options);
|
|
1742
|
-
};
|
|
1743
|
-
const getJoinTableName = (collectionName, attributeName, options) => {
|
|
1744
|
-
return getName([collectionName, attributeName], { suffix: "links", ...options });
|
|
1745
|
-
};
|
|
1746
|
-
const getMorphTableName = (collectionName, attributeName, options) => {
|
|
1747
|
-
return getName([collectionName, attributeName], { suffix: "morphs", ...options });
|
|
1748
|
-
};
|
|
1749
|
-
const getColumnName = (attributeName, options) => {
|
|
1750
|
-
return getName(attributeName, options);
|
|
1751
|
-
};
|
|
1752
|
-
const getJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1753
|
-
return getName(attributeName, { suffix: "id", ...options });
|
|
1754
|
-
};
|
|
1755
|
-
const getInverseJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1756
|
-
return getName(attributeName, { suffix: "id", prefix: "inv", ...options });
|
|
1757
|
-
};
|
|
1758
|
-
const getOrderColumnName = (singularName, options) => {
|
|
1759
|
-
return getName(singularName, { suffix: "order", ...options });
|
|
1760
|
-
};
|
|
1761
|
-
const getInverseOrderColumnName = (singularName, options) => {
|
|
1762
|
-
return getName(singularName, { suffix: "order", prefix: "inv", ...options });
|
|
1763
|
-
};
|
|
1764
|
-
const getMorphColumnJoinTableIdName = (singularName, options) => {
|
|
1765
|
-
return getName(singularName, { suffix: "id", ...options });
|
|
1766
|
-
};
|
|
1767
|
-
const getMorphColumnAttributeIdName = (attributeName, options) => {
|
|
1768
|
-
return getName(attributeName, { suffix: "id", ...options });
|
|
1769
|
-
};
|
|
1770
|
-
const getMorphColumnTypeName = (attributeName, options) => {
|
|
1771
|
-
return getName(attributeName, { suffix: "type", ...options });
|
|
1772
|
-
};
|
|
1773
|
-
const getIndexName = (names, options) => {
|
|
1774
|
-
return getName(names, { suffix: "index", ...options });
|
|
1775
|
-
};
|
|
1776
|
-
const getFkIndexName = (names, options) => {
|
|
1777
|
-
return getName(names, { suffix: "fk", ...options });
|
|
1778
|
-
};
|
|
1779
|
-
const getInverseFkIndexName = (names, options) => {
|
|
1780
|
-
return getName(names, { suffix: "inv_fk", ...options });
|
|
1781
|
-
};
|
|
1782
|
-
const getOrderFkIndexName = (names, options) => {
|
|
1783
|
-
return getName(names, { suffix: "order_fk", ...options });
|
|
1784
|
-
};
|
|
1785
|
-
const getOrderInverseFkIndexName = (names, options) => {
|
|
1786
|
-
return getName(names, { suffix: "order_inv_fk", ...options });
|
|
1787
|
-
};
|
|
1788
|
-
const getUniqueIndexName = (names, options) => {
|
|
1789
|
-
return getName(names, { suffix: "unique", ...options });
|
|
1790
|
-
};
|
|
1791
|
-
const getPrimaryIndexName = (names) => {
|
|
1792
|
-
return getName(names, { suffix: "primary" });
|
|
1793
|
-
};
|
|
1794
|
-
const identifiers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1795
|
-
__proto__: null,
|
|
1796
|
-
FIELD_COLUMN,
|
|
1797
|
-
ID_COLUMN,
|
|
1798
|
-
ORDER_COLUMN,
|
|
1799
|
-
getColumnName,
|
|
1800
|
-
getFkIndexName,
|
|
1801
|
-
getIndexName,
|
|
1802
|
-
getInverseFkIndexName,
|
|
1803
|
-
getInverseJoinColumnAttributeIdName,
|
|
1804
|
-
getInverseOrderColumnName,
|
|
1805
|
-
getJoinColumnAttributeIdName,
|
|
1806
|
-
getJoinTableName,
|
|
1807
|
-
getMorphColumnAttributeIdName,
|
|
1808
|
-
getMorphColumnJoinTableIdName,
|
|
1809
|
-
getMorphColumnTypeName,
|
|
1810
|
-
getMorphTableName,
|
|
1811
|
-
getName,
|
|
1812
|
-
getOrderColumnName,
|
|
1813
|
-
getOrderFkIndexName,
|
|
1814
|
-
getOrderInverseFkIndexName,
|
|
1815
|
-
getPrimaryIndexName,
|
|
1816
|
-
getTableName,
|
|
1817
|
-
getUniqueIndexName
|
|
1818
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
1956
|
+
const identifiers = new Identifiers({ maxLength: IDENTIFIER_MAX_LENGTH });
|
|
1819
1957
|
const createColumn = (name, attribute) => {
|
|
1820
1958
|
const { type, args = [], ...opts } = getColumnType(attribute);
|
|
1821
1959
|
return {
|
|
1822
|
-
name,
|
|
1960
|
+
name: identifiers.getName(name),
|
|
1823
1961
|
type,
|
|
1824
1962
|
args,
|
|
1825
1963
|
defaultTo: null,
|
|
@@ -1841,8 +1979,8 @@ const createTable = (meta) => {
|
|
|
1841
1979
|
if (attribute.type === "relation") {
|
|
1842
1980
|
if ("morphColumn" in attribute && attribute.morphColumn && attribute.owner) {
|
|
1843
1981
|
const { idColumn, typeColumn } = attribute.morphColumn;
|
|
1844
|
-
const idColumnName = getName(idColumn.name);
|
|
1845
|
-
const typeColumnName = getName(typeColumn.name);
|
|
1982
|
+
const idColumnName = identifiers.getName(idColumn.name);
|
|
1983
|
+
const typeColumnName = identifiers.getName(typeColumn.name);
|
|
1846
1984
|
table.columns.push(
|
|
1847
1985
|
createColumn(idColumnName, {
|
|
1848
1986
|
type: "integer",
|
|
@@ -1859,7 +1997,7 @@ const createTable = (meta) => {
|
|
|
1859
1997
|
referencedTable,
|
|
1860
1998
|
columnType = "integer"
|
|
1861
1999
|
} = attribute.joinColumn;
|
|
1862
|
-
const columnName = getName(columnNameFull);
|
|
2000
|
+
const columnName = identifiers.getName(columnNameFull);
|
|
1863
2001
|
const column = createColumn(columnName, {
|
|
1864
2002
|
// TODO: find the column type automatically, or allow passing all the column params
|
|
1865
2003
|
type: columnType,
|
|
@@ -1868,7 +2006,7 @@ const createTable = (meta) => {
|
|
|
1868
2006
|
}
|
|
1869
2007
|
});
|
|
1870
2008
|
table.columns.push(column);
|
|
1871
|
-
const fkName = getFkIndexName([table.name, columnName]);
|
|
2009
|
+
const fkName = identifiers.getFkIndexName([table.name, columnName]);
|
|
1872
2010
|
table.foreignKeys.push({
|
|
1873
2011
|
name: fkName,
|
|
1874
2012
|
columns: [column.name],
|
|
@@ -1883,19 +2021,19 @@ const createTable = (meta) => {
|
|
|
1883
2021
|
});
|
|
1884
2022
|
}
|
|
1885
2023
|
} else if (isScalarAttribute(attribute)) {
|
|
1886
|
-
const columnName = getName(attribute.columnName || key);
|
|
2024
|
+
const columnName = identifiers.getName(attribute.columnName || key);
|
|
1887
2025
|
const column = createColumn(columnName, attribute);
|
|
1888
2026
|
if (column.unique) {
|
|
1889
2027
|
table.indexes.push({
|
|
1890
2028
|
type: "unique",
|
|
1891
|
-
name: getUniqueIndexName([table.name, column.name]),
|
|
2029
|
+
name: identifiers.getUniqueIndexName([table.name, column.name]),
|
|
1892
2030
|
columns: [columnName]
|
|
1893
2031
|
});
|
|
1894
2032
|
}
|
|
1895
2033
|
if (column.primary) {
|
|
1896
2034
|
table.indexes.push({
|
|
1897
2035
|
type: "primary",
|
|
1898
|
-
name: getPrimaryIndexName([table.name, column.name]),
|
|
2036
|
+
name: identifiers.getPrimaryIndexName([table.name, column.name]),
|
|
1899
2037
|
columns: [columnName]
|
|
1900
2038
|
});
|
|
1901
2039
|
}
|
|
@@ -1995,12 +2133,13 @@ const metadataToSchema = (metadata) => {
|
|
|
1995
2133
|
});
|
|
1996
2134
|
return schema;
|
|
1997
2135
|
};
|
|
1998
|
-
const debug = createDebug("strapi::database");
|
|
2136
|
+
const debug$1 = createDebug("strapi::database");
|
|
1999
2137
|
const createSchemaProvider = (db) => {
|
|
2000
2138
|
const state = {};
|
|
2001
2139
|
return {
|
|
2002
2140
|
get schema() {
|
|
2003
2141
|
if (!state.schema) {
|
|
2142
|
+
debug$1("Converting metadata to database schema");
|
|
2004
2143
|
state.schema = metadataToSchema(db.metadata);
|
|
2005
2144
|
}
|
|
2006
2145
|
return state.schema;
|
|
@@ -2012,7 +2151,7 @@ const createSchemaProvider = (db) => {
|
|
|
2012
2151
|
* Drops the database schema
|
|
2013
2152
|
*/
|
|
2014
2153
|
async drop() {
|
|
2015
|
-
debug("Dropping database schema");
|
|
2154
|
+
debug$1("Dropping database schema");
|
|
2016
2155
|
const DBSchema = await db.dialect.schemaInspector.getSchema();
|
|
2017
2156
|
await this.builder.dropSchema(DBSchema);
|
|
2018
2157
|
},
|
|
@@ -2020,19 +2159,19 @@ const createSchemaProvider = (db) => {
|
|
|
2020
2159
|
* Creates the database schema
|
|
2021
2160
|
*/
|
|
2022
2161
|
async create() {
|
|
2023
|
-
debug("Created database schema");
|
|
2162
|
+
debug$1("Created database schema");
|
|
2024
2163
|
await this.builder.createSchema(this.schema);
|
|
2025
2164
|
},
|
|
2026
2165
|
/**
|
|
2027
2166
|
* Resets the database schema
|
|
2028
2167
|
*/
|
|
2029
2168
|
async reset() {
|
|
2030
|
-
debug("Resetting database schema");
|
|
2169
|
+
debug$1("Resetting database schema");
|
|
2031
2170
|
await this.drop();
|
|
2032
2171
|
await this.create();
|
|
2033
2172
|
},
|
|
2034
2173
|
async syncSchema() {
|
|
2035
|
-
debug("Synchronizing database schema");
|
|
2174
|
+
debug$1("Synchronizing database schema");
|
|
2036
2175
|
const DBSchema = await db.dialect.schemaInspector.getSchema();
|
|
2037
2176
|
const { status, diff } = await this.schemaDiff.diff(DBSchema, this.schema);
|
|
2038
2177
|
if (status === "CHANGED") {
|
|
@@ -2045,28 +2184,28 @@ const createSchemaProvider = (db) => {
|
|
|
2045
2184
|
// TODO: Allow keeping extra indexes / extra tables / extra columns (globally or on a per table basis)
|
|
2046
2185
|
async sync() {
|
|
2047
2186
|
if (await db.migrations.shouldRun()) {
|
|
2048
|
-
debug("Found migrations to run");
|
|
2187
|
+
debug$1("Found migrations to run");
|
|
2049
2188
|
await db.migrations.up();
|
|
2050
2189
|
return this.syncSchema();
|
|
2051
2190
|
}
|
|
2052
2191
|
const oldSchema = await this.schemaStorage.read();
|
|
2053
2192
|
if (!oldSchema) {
|
|
2054
|
-
debug("Schema not persisted yet");
|
|
2193
|
+
debug$1("Schema not persisted yet");
|
|
2055
2194
|
return this.syncSchema();
|
|
2056
2195
|
}
|
|
2057
2196
|
const { hash: oldHash } = oldSchema;
|
|
2058
2197
|
const hash = await this.schemaStorage.hashSchema(this.schema);
|
|
2059
2198
|
if (oldHash !== hash) {
|
|
2060
|
-
debug("Schema changed");
|
|
2199
|
+
debug$1("Schema changed");
|
|
2061
2200
|
return this.syncSchema();
|
|
2062
2201
|
}
|
|
2063
|
-
debug("Schema unchanged");
|
|
2202
|
+
debug$1("Schema unchanged");
|
|
2064
2203
|
}
|
|
2065
2204
|
};
|
|
2066
2205
|
};
|
|
2067
|
-
const ID = ID_COLUMN;
|
|
2068
|
-
const ORDER = ORDER_COLUMN;
|
|
2069
|
-
const FIELD = FIELD_COLUMN;
|
|
2206
|
+
const ID = identifiers.ID_COLUMN;
|
|
2207
|
+
const ORDER = identifiers.ORDER_COLUMN;
|
|
2208
|
+
const FIELD = identifiers.FIELD_COLUMN;
|
|
2070
2209
|
const hasInversedBy = (attr) => "inversedBy" in attr;
|
|
2071
2210
|
const hasMappedBy = (attr) => "mappedBy" in attr;
|
|
2072
2211
|
const isOneToAny = (attribute) => ["oneToOne", "oneToMany"].includes(attribute.relation);
|
|
@@ -2087,7 +2226,7 @@ const createOneToOne = (attributeName, attribute, meta, metadata) => {
|
|
|
2087
2226
|
meta
|
|
2088
2227
|
});
|
|
2089
2228
|
} else {
|
|
2090
|
-
|
|
2229
|
+
createJoinColumn(metadata, {
|
|
2091
2230
|
attribute,
|
|
2092
2231
|
attributeName,
|
|
2093
2232
|
meta
|
|
@@ -2117,7 +2256,7 @@ const createManyToOne = (attributeName, attribute, meta, metadata) => {
|
|
|
2117
2256
|
meta
|
|
2118
2257
|
});
|
|
2119
2258
|
} else {
|
|
2120
|
-
|
|
2259
|
+
createJoinColumn(metadata, {
|
|
2121
2260
|
attribute,
|
|
2122
2261
|
attributeName,
|
|
2123
2262
|
meta
|
|
@@ -2134,15 +2273,11 @@ const createManyToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2134
2273
|
}
|
|
2135
2274
|
};
|
|
2136
2275
|
const createMorphToOne = (attributeName, attribute) => {
|
|
2137
|
-
const idColumnName = getJoinColumnAttributeIdName("target");
|
|
2138
|
-
const typeColumnName = getMorphColumnTypeName("target");
|
|
2139
|
-
if ("morphColumn" in attribute && attribute.morphColumn) {
|
|
2140
|
-
return;
|
|
2141
|
-
}
|
|
2276
|
+
const idColumnName = identifiers.getJoinColumnAttributeIdName("target");
|
|
2277
|
+
const typeColumnName = identifiers.getMorphColumnTypeName("target");
|
|
2142
2278
|
Object.assign(attribute, {
|
|
2143
2279
|
owner: true,
|
|
2144
|
-
morphColumn: {
|
|
2145
|
-
// TODO: add referenced column
|
|
2280
|
+
morphColumn: attribute.morphColumn ?? {
|
|
2146
2281
|
typeColumn: {
|
|
2147
2282
|
name: typeColumnName
|
|
2148
2283
|
},
|
|
@@ -2157,11 +2292,11 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2157
2292
|
if ("joinTable" in attribute && attribute.joinTable) {
|
|
2158
2293
|
return;
|
|
2159
2294
|
}
|
|
2160
|
-
const joinTableName = getMorphTableName(meta.tableName, attributeName);
|
|
2161
|
-
const joinColumnName = getMorphColumnJoinTableIdName(meta.singularName);
|
|
2162
|
-
const idColumnName = getMorphColumnAttributeIdName(attributeName);
|
|
2163
|
-
const typeColumnName = getMorphColumnTypeName(attributeName);
|
|
2164
|
-
const fkIndexName = getFkIndexName(joinTableName);
|
|
2295
|
+
const joinTableName = identifiers.getMorphTableName(meta.tableName, attributeName);
|
|
2296
|
+
const joinColumnName = identifiers.getMorphColumnJoinTableIdName(snakeCase(meta.singularName));
|
|
2297
|
+
const idColumnName = identifiers.getMorphColumnAttributeIdName(attributeName);
|
|
2298
|
+
const typeColumnName = identifiers.getMorphColumnTypeName(attributeName);
|
|
2299
|
+
const fkIndexName = identifiers.getFkIndexName(joinTableName);
|
|
2165
2300
|
metadata.add({
|
|
2166
2301
|
singularName: joinTableName,
|
|
2167
2302
|
uid: joinTableName,
|
|
@@ -2174,7 +2309,9 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2174
2309
|
type: "integer",
|
|
2175
2310
|
column: {
|
|
2176
2311
|
unsigned: true
|
|
2177
|
-
}
|
|
2312
|
+
},
|
|
2313
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2314
|
+
columnName: joinColumnName
|
|
2178
2315
|
},
|
|
2179
2316
|
[idColumnName]: {
|
|
2180
2317
|
type: "integer",
|
|
@@ -2201,11 +2338,11 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2201
2338
|
columns: [joinColumnName]
|
|
2202
2339
|
},
|
|
2203
2340
|
{
|
|
2204
|
-
name:
|
|
2341
|
+
name: identifiers.getOrderIndexName(joinTableName),
|
|
2205
2342
|
columns: [ORDER]
|
|
2206
2343
|
},
|
|
2207
2344
|
{
|
|
2208
|
-
name:
|
|
2345
|
+
name: identifiers.getIdColumnIndexName(joinTableName),
|
|
2209
2346
|
columns: [idColumnName]
|
|
2210
2347
|
}
|
|
2211
2348
|
],
|
|
@@ -2214,7 +2351,7 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2214
2351
|
name: fkIndexName,
|
|
2215
2352
|
columns: [joinColumnName],
|
|
2216
2353
|
referencedColumns: [ID],
|
|
2217
|
-
referencedTable:
|
|
2354
|
+
referencedTable: meta.tableName,
|
|
2218
2355
|
onDelete: "CASCADE"
|
|
2219
2356
|
}
|
|
2220
2357
|
],
|
|
@@ -2261,12 +2398,12 @@ const createMorphMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2261
2398
|
throw new Error(`Morph target attribute not found. Looking for ${attribute.morphBy}`);
|
|
2262
2399
|
}
|
|
2263
2400
|
};
|
|
2264
|
-
const
|
|
2401
|
+
const createJoinColumn = (metadata, { attribute, attributeName }) => {
|
|
2265
2402
|
const targetMeta = metadata.get(attribute.target);
|
|
2266
2403
|
if (!targetMeta) {
|
|
2267
2404
|
throw new Error(`Unknown target ${attribute.target}`);
|
|
2268
2405
|
}
|
|
2269
|
-
const joinColumnName =
|
|
2406
|
+
const joinColumnName = identifiers.getJoinColumnAttributeIdName(snakeCase(attributeName));
|
|
2270
2407
|
const joinColumn = {
|
|
2271
2408
|
name: joinColumnName,
|
|
2272
2409
|
referencedColumn: ID,
|
|
@@ -2281,7 +2418,7 @@ const createJoinColum = (metadata, { attribute, attributeName }) => {
|
|
|
2281
2418
|
Object.assign(inverseAttribute, {
|
|
2282
2419
|
joinColumn: {
|
|
2283
2420
|
name: joinColumn.referencedColumn,
|
|
2284
|
-
referencedColumn:
|
|
2421
|
+
referencedColumn: joinColumnName
|
|
2285
2422
|
}
|
|
2286
2423
|
});
|
|
2287
2424
|
}
|
|
@@ -2294,21 +2431,26 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2294
2431
|
if ("joinTable" in attribute && attribute.joinTable) {
|
|
2295
2432
|
return;
|
|
2296
2433
|
}
|
|
2297
|
-
const joinTableName = getJoinTableName(
|
|
2298
|
-
|
|
2299
|
-
|
|
2434
|
+
const joinTableName = identifiers.getJoinTableName(
|
|
2435
|
+
snakeCase(meta.tableName),
|
|
2436
|
+
snakeCase(attributeName)
|
|
2437
|
+
);
|
|
2438
|
+
const joinColumnName = identifiers.getJoinColumnAttributeIdName(snakeCase(meta.singularName));
|
|
2439
|
+
let inverseJoinColumnName = identifiers.getJoinColumnAttributeIdName(
|
|
2440
|
+
snakeCase(targetMeta.singularName)
|
|
2441
|
+
);
|
|
2300
2442
|
if (joinColumnName === inverseJoinColumnName) {
|
|
2301
|
-
inverseJoinColumnName = getInverseJoinColumnAttributeIdName(
|
|
2302
|
-
targetMeta.singularName
|
|
2443
|
+
inverseJoinColumnName = identifiers.getInverseJoinColumnAttributeIdName(
|
|
2444
|
+
snakeCase(targetMeta.singularName)
|
|
2303
2445
|
);
|
|
2304
2446
|
}
|
|
2305
|
-
const orderColumnName = getOrderColumnName(targetMeta.singularName);
|
|
2306
|
-
let inverseOrderColumnName = getOrderColumnName(meta.singularName);
|
|
2447
|
+
const orderColumnName = identifiers.getOrderColumnName(snakeCase(targetMeta.singularName));
|
|
2448
|
+
let inverseOrderColumnName = identifiers.getOrderColumnName(snakeCase(meta.singularName));
|
|
2307
2449
|
if (attribute.relation === "manyToMany" && orderColumnName === inverseOrderColumnName) {
|
|
2308
|
-
inverseOrderColumnName = getInverseOrderColumnName(meta.singularName);
|
|
2450
|
+
inverseOrderColumnName = identifiers.getInverseOrderColumnName(snakeCase(meta.singularName));
|
|
2309
2451
|
}
|
|
2310
|
-
const fkIndexName = getFkIndexName(joinTableName);
|
|
2311
|
-
const invFkIndexName = getInverseFkIndexName(joinTableName);
|
|
2452
|
+
const fkIndexName = identifiers.getFkIndexName(joinTableName);
|
|
2453
|
+
const invFkIndexName = identifiers.getInverseFkIndexName(joinTableName);
|
|
2312
2454
|
const metadataSchema = {
|
|
2313
2455
|
singularName: joinTableName,
|
|
2314
2456
|
uid: joinTableName,
|
|
@@ -2321,13 +2463,17 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2321
2463
|
type: "integer",
|
|
2322
2464
|
column: {
|
|
2323
2465
|
unsigned: true
|
|
2324
|
-
}
|
|
2466
|
+
},
|
|
2467
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2468
|
+
columnName: joinColumnName
|
|
2325
2469
|
},
|
|
2326
2470
|
[inverseJoinColumnName]: {
|
|
2327
2471
|
type: "integer",
|
|
2328
2472
|
column: {
|
|
2329
2473
|
unsigned: true
|
|
2330
|
-
}
|
|
2474
|
+
},
|
|
2475
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2476
|
+
columnName: inverseJoinColumnName
|
|
2331
2477
|
}
|
|
2332
2478
|
// TODO: add extra pivot attributes -> user should use an intermediate entity
|
|
2333
2479
|
},
|
|
@@ -2341,7 +2487,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2341
2487
|
columns: [inverseJoinColumnName]
|
|
2342
2488
|
},
|
|
2343
2489
|
{
|
|
2344
|
-
name: getUniqueIndexName(joinTableName),
|
|
2490
|
+
name: identifiers.getUniqueIndexName(joinTableName),
|
|
2345
2491
|
columns: [joinColumnName, inverseJoinColumnName],
|
|
2346
2492
|
type: "unique"
|
|
2347
2493
|
}
|
|
@@ -2352,7 +2498,6 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2352
2498
|
columns: [joinColumnName],
|
|
2353
2499
|
referencedColumns: [ID],
|
|
2354
2500
|
referencedTable: meta.tableName,
|
|
2355
|
-
// TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
|
|
2356
2501
|
onDelete: "CASCADE"
|
|
2357
2502
|
},
|
|
2358
2503
|
{
|
|
@@ -2360,7 +2505,6 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2360
2505
|
columns: [inverseJoinColumnName],
|
|
2361
2506
|
referencedColumns: [ID],
|
|
2362
2507
|
referencedTable: targetMeta.tableName,
|
|
2363
|
-
// TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
|
|
2364
2508
|
onDelete: "CASCADE"
|
|
2365
2509
|
}
|
|
2366
2510
|
],
|
|
@@ -2390,8 +2534,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2390
2534
|
}
|
|
2391
2535
|
};
|
|
2392
2536
|
metadataSchema.indexes.push({
|
|
2393
|
-
name: getOrderFkIndexName(joinTableName),
|
|
2394
|
-
// TODO: should we send joinTableName as parts?
|
|
2537
|
+
name: identifiers.getOrderFkIndexName(joinTableName),
|
|
2395
2538
|
columns: [orderColumnName]
|
|
2396
2539
|
});
|
|
2397
2540
|
joinTable.orderColumnName = orderColumnName;
|
|
@@ -2406,7 +2549,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2406
2549
|
}
|
|
2407
2550
|
};
|
|
2408
2551
|
metadataSchema.indexes.push({
|
|
2409
|
-
name: getOrderInverseFkIndexName(joinTableName),
|
|
2552
|
+
name: identifiers.getOrderInverseFkIndexName(joinTableName),
|
|
2410
2553
|
columns: [inverseOrderColumnName]
|
|
2411
2554
|
});
|
|
2412
2555
|
joinTable.inverseOrderColumnName = inverseOrderColumnName;
|
|
@@ -2464,6 +2607,12 @@ const createRelation = (attributeName, attribute, meta, metadata) => {
|
|
|
2464
2607
|
}
|
|
2465
2608
|
};
|
|
2466
2609
|
class Metadata extends Map {
|
|
2610
|
+
// TODO: we expose the global identifiers in this way so that in the future we can instantiate our own
|
|
2611
|
+
// However, it should NOT be done until all the methods used by metadata can be part of this metadata object
|
|
2612
|
+
// and access this one; currently they all access the global identifiers directly.
|
|
2613
|
+
get identifiers() {
|
|
2614
|
+
return identifiers;
|
|
2615
|
+
}
|
|
2467
2616
|
get(key) {
|
|
2468
2617
|
if (!super.has(key)) {
|
|
2469
2618
|
throw new Error(`Metadata for "${key}" not found`);
|
|
@@ -2487,10 +2636,12 @@ class Metadata extends Map {
|
|
|
2487
2636
|
seenTables.set(meta.tableName, true);
|
|
2488
2637
|
}
|
|
2489
2638
|
}
|
|
2490
|
-
loadModels(models
|
|
2491
|
-
for (const model of
|
|
2639
|
+
loadModels(models) {
|
|
2640
|
+
for (const model of cloneDeep(models ?? [])) {
|
|
2641
|
+
const tableName = identifiers.getTableName(model.tableName);
|
|
2492
2642
|
this.add({
|
|
2493
2643
|
...model,
|
|
2644
|
+
tableName,
|
|
2494
2645
|
attributes: {
|
|
2495
2646
|
...model.attributes
|
|
2496
2647
|
},
|
|
@@ -2531,10 +2682,13 @@ class Metadata extends Map {
|
|
|
2531
2682
|
}
|
|
2532
2683
|
}
|
|
2533
2684
|
const createAttribute = (attributeName, attribute) => {
|
|
2534
|
-
|
|
2685
|
+
if ("columnName" in attribute && attribute.columnName) {
|
|
2686
|
+
return;
|
|
2687
|
+
}
|
|
2688
|
+
const columnName = identifiers.getColumnName(snakeCase(attributeName));
|
|
2535
2689
|
Object.assign(attribute, { columnName });
|
|
2536
2690
|
};
|
|
2537
|
-
const createMetadata = (models
|
|
2691
|
+
const createMetadata = (models) => {
|
|
2538
2692
|
const metadata = new Metadata();
|
|
2539
2693
|
if (models.length) {
|
|
2540
2694
|
metadata.loadModels(models);
|
|
@@ -2885,11 +3039,54 @@ const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
|
|
|
2885
3039
|
return subAlias;
|
|
2886
3040
|
};
|
|
2887
3041
|
const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
|
|
2888
|
-
const { db, qb } = ctx;
|
|
3042
|
+
const { db, qb, uid } = ctx;
|
|
2889
3043
|
if (attribute.type !== "relation") {
|
|
2890
3044
|
throw new Error(`Cannot join on non relational field ${attributeName}`);
|
|
2891
3045
|
}
|
|
2892
3046
|
const targetMeta = db.metadata.get(attribute.target);
|
|
3047
|
+
if (["morphOne", "morphMany"].includes(attribute.relation)) {
|
|
3048
|
+
const targetAttribute = targetMeta.attributes[attribute.morphBy];
|
|
3049
|
+
const { joinTable: joinTable2, morphColumn } = targetAttribute;
|
|
3050
|
+
if (morphColumn) {
|
|
3051
|
+
const subAlias = refAlias || qb.getAlias();
|
|
3052
|
+
qb.join({
|
|
3053
|
+
alias: subAlias,
|
|
3054
|
+
referencedTable: targetMeta.tableName,
|
|
3055
|
+
referencedColumn: morphColumn.idColumn.name,
|
|
3056
|
+
rootColumn: morphColumn.idColumn.referencedColumn,
|
|
3057
|
+
rootTable: alias,
|
|
3058
|
+
on: {
|
|
3059
|
+
[morphColumn.typeColumn.name]: uid,
|
|
3060
|
+
...morphColumn.on
|
|
3061
|
+
}
|
|
3062
|
+
});
|
|
3063
|
+
return subAlias;
|
|
3064
|
+
}
|
|
3065
|
+
if (joinTable2) {
|
|
3066
|
+
const joinAlias = qb.getAlias();
|
|
3067
|
+
qb.join({
|
|
3068
|
+
alias: joinAlias,
|
|
3069
|
+
referencedTable: joinTable2.name,
|
|
3070
|
+
referencedColumn: joinTable2.morphColumn.idColumn.name,
|
|
3071
|
+
rootColumn: joinTable2.morphColumn.idColumn.referencedColumn,
|
|
3072
|
+
rootTable: alias,
|
|
3073
|
+
on: {
|
|
3074
|
+
[joinTable2.morphColumn.typeColumn.name]: uid,
|
|
3075
|
+
field: attributeName
|
|
3076
|
+
}
|
|
3077
|
+
});
|
|
3078
|
+
const subAlias = refAlias || qb.getAlias();
|
|
3079
|
+
qb.join({
|
|
3080
|
+
alias: subAlias,
|
|
3081
|
+
referencedTable: targetMeta.tableName,
|
|
3082
|
+
referencedColumn: joinTable2.joinColumn.referencedColumn,
|
|
3083
|
+
rootColumn: joinTable2.joinColumn.name,
|
|
3084
|
+
rootTable: joinAlias
|
|
3085
|
+
});
|
|
3086
|
+
return subAlias;
|
|
3087
|
+
}
|
|
3088
|
+
return alias;
|
|
3089
|
+
}
|
|
2893
3090
|
const { joinColumn } = attribute;
|
|
2894
3091
|
if (joinColumn) {
|
|
2895
3092
|
const subAlias = refAlias || qb.getAlias();
|
|
@@ -2983,6 +3180,7 @@ const processOrderBy = (orderBy, ctx) => {
|
|
|
2983
3180
|
}
|
|
2984
3181
|
throw new Error("Invalid orderBy syntax");
|
|
2985
3182
|
};
|
|
3183
|
+
const joinColPrefix = "__strapi";
|
|
2986
3184
|
const XtoOne = async (input, ctx) => {
|
|
2987
3185
|
const { attribute, attributeName, results, populateValue, targetMeta, isCount } = input;
|
|
2988
3186
|
const { db, qb } = ctx;
|
|
@@ -3011,6 +3209,8 @@ const XtoOne = async (input, ctx) => {
|
|
|
3011
3209
|
const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
|
|
3012
3210
|
const alias = qb2.getAlias();
|
|
3013
3211
|
const joinColAlias = `${alias}.${joinColumnName}`;
|
|
3212
|
+
const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
|
|
3213
|
+
const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
|
|
3014
3214
|
const referencedValues = _.uniq(
|
|
3015
3215
|
results.map((r) => r[referencedColumnName]).filter((value) => !_.isNil(value))
|
|
3016
3216
|
);
|
|
@@ -3029,10 +3229,13 @@ const XtoOne = async (input, ctx) => {
|
|
|
3029
3229
|
rootTable: qb2.alias,
|
|
3030
3230
|
on: joinTable.on
|
|
3031
3231
|
}).select([joinColAlias, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3032
|
-
const map22 = rows2.reduce(
|
|
3033
|
-
map3
|
|
3034
|
-
|
|
3035
|
-
|
|
3232
|
+
const map22 = rows2.reduce(
|
|
3233
|
+
(map3, row) => {
|
|
3234
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3235
|
+
return map3;
|
|
3236
|
+
},
|
|
3237
|
+
{}
|
|
3238
|
+
);
|
|
3036
3239
|
results.forEach((result) => {
|
|
3037
3240
|
result[attributeName] = map22[result[referencedColumnName]] || { count: 0 };
|
|
3038
3241
|
});
|
|
@@ -3052,8 +3255,8 @@ const XtoOne = async (input, ctx) => {
|
|
|
3052
3255
|
rootTable: qb2.alias,
|
|
3053
3256
|
on: joinTable.on,
|
|
3054
3257
|
orderBy: joinTable.orderBy
|
|
3055
|
-
}).addSelect(
|
|
3056
|
-
const map2 = _.groupBy(
|
|
3258
|
+
}).addSelect(joinColSelect).where({ [joinColAlias]: referencedValues }).execute({ mapResults: false });
|
|
3259
|
+
const map2 = _.groupBy(joinColRenameAs)(rows);
|
|
3057
3260
|
results.forEach((result) => {
|
|
3058
3261
|
result[attributeName] = fromTargetRow(_.first(map2[result[referencedColumnName]]));
|
|
3059
3262
|
});
|
|
@@ -3087,6 +3290,8 @@ const oneToMany = async (input, ctx) => {
|
|
|
3087
3290
|
const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
|
|
3088
3291
|
const alias = qb2.getAlias();
|
|
3089
3292
|
const joinColAlias = `${alias}.${joinColumnName}`;
|
|
3293
|
+
const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
|
|
3294
|
+
const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
|
|
3090
3295
|
const referencedValues = _.uniq(
|
|
3091
3296
|
results.map((r) => r[referencedColumnName]).filter((value) => !_.isNil(value))
|
|
3092
3297
|
);
|
|
@@ -3104,11 +3309,14 @@ const oneToMany = async (input, ctx) => {
|
|
|
3104
3309
|
rootColumn: joinTable.inverseJoinColumn.referencedColumn,
|
|
3105
3310
|
rootTable: qb2.alias,
|
|
3106
3311
|
on: joinTable.on
|
|
3107
|
-
}).select([
|
|
3108
|
-
const map22 = rows2.reduce(
|
|
3109
|
-
map3
|
|
3110
|
-
|
|
3111
|
-
|
|
3312
|
+
}).select([joinColSelect, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3313
|
+
const map22 = rows2.reduce(
|
|
3314
|
+
(map3, row) => {
|
|
3315
|
+
map3[row[joinColRenameAs]] = { count: Number(row.count) };
|
|
3316
|
+
return map3;
|
|
3317
|
+
},
|
|
3318
|
+
{}
|
|
3319
|
+
);
|
|
3112
3320
|
results.forEach((result) => {
|
|
3113
3321
|
result[attributeName] = map22[result[referencedColumnName]] || { count: 0 };
|
|
3114
3322
|
});
|
|
@@ -3128,8 +3336,8 @@ const oneToMany = async (input, ctx) => {
|
|
|
3128
3336
|
rootTable: qb2.alias,
|
|
3129
3337
|
on: joinTable.on,
|
|
3130
3338
|
orderBy: _.mapValues((v) => populateValue.ordering || v, joinTable.orderBy)
|
|
3131
|
-
}).addSelect(
|
|
3132
|
-
const map2 = _.groupBy(
|
|
3339
|
+
}).addSelect(joinColSelect).where({ [joinColAlias]: referencedValues }).execute({ mapResults: false });
|
|
3340
|
+
const map2 = _.groupBy(joinColRenameAs)(rows);
|
|
3133
3341
|
results.forEach((r) => {
|
|
3134
3342
|
r[attributeName] = fromTargetRow(map2[r[referencedColumnName]] || []);
|
|
3135
3343
|
});
|
|
@@ -3144,6 +3352,8 @@ const manyToMany = async (input, ctx) => {
|
|
|
3144
3352
|
const { name: joinColumnName, referencedColumn: referencedColumnName } = joinTable.joinColumn;
|
|
3145
3353
|
const alias = populateQb.getAlias();
|
|
3146
3354
|
const joinColAlias = `${alias}.${joinColumnName}`;
|
|
3355
|
+
const joinColRenameAs = `${joinColPrefix}${joinColumnName}`;
|
|
3356
|
+
const joinColSelect = `${joinColAlias} as ${joinColRenameAs}`;
|
|
3147
3357
|
const referencedValues = _.uniq(
|
|
3148
3358
|
results.map((r) => r[referencedColumnName]).filter((value) => !_.isNil(value))
|
|
3149
3359
|
);
|
|
@@ -3162,10 +3372,13 @@ const manyToMany = async (input, ctx) => {
|
|
|
3162
3372
|
rootTable: populateQb.alias,
|
|
3163
3373
|
on: joinTable.on
|
|
3164
3374
|
}).select([joinColAlias, populateQb.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3165
|
-
const map22 = rows2.reduce(
|
|
3166
|
-
map3
|
|
3167
|
-
|
|
3168
|
-
|
|
3375
|
+
const map22 = rows2.reduce(
|
|
3376
|
+
(map3, row) => {
|
|
3377
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3378
|
+
return map3;
|
|
3379
|
+
},
|
|
3380
|
+
{}
|
|
3381
|
+
);
|
|
3169
3382
|
results.forEach((result) => {
|
|
3170
3383
|
result[attributeName] = map22[result[referencedColumnName]] || { count: 0 };
|
|
3171
3384
|
});
|
|
@@ -3185,8 +3398,8 @@ const manyToMany = async (input, ctx) => {
|
|
|
3185
3398
|
rootTable: populateQb.alias,
|
|
3186
3399
|
on: joinTable.on,
|
|
3187
3400
|
orderBy: _.mapValues((v) => populateValue.ordering || v, joinTable.orderBy)
|
|
3188
|
-
}).addSelect(
|
|
3189
|
-
const map2 = _.groupBy(
|
|
3401
|
+
}).addSelect(joinColSelect).where({ [joinColAlias]: referencedValues }).execute({ mapResults: false });
|
|
3402
|
+
const map2 = _.groupBy(joinColRenameAs)(rows);
|
|
3190
3403
|
results.forEach((result) => {
|
|
3191
3404
|
result[attributeName] = fromTargetRow(map2[result[referencedColumnName]] || []);
|
|
3192
3405
|
});
|
|
@@ -3539,6 +3752,31 @@ const processNested = (where, ctx) => {
|
|
|
3539
3752
|
}
|
|
3540
3753
|
return processWhere(where, ctx);
|
|
3541
3754
|
};
|
|
3755
|
+
const processRelationWhere = (where, ctx) => {
|
|
3756
|
+
const { qb, alias } = ctx;
|
|
3757
|
+
const idAlias = qb.aliasColumn("id", alias);
|
|
3758
|
+
if (!isRecord$1(where)) {
|
|
3759
|
+
return { [idAlias]: where };
|
|
3760
|
+
}
|
|
3761
|
+
const keys = Object.keys(where);
|
|
3762
|
+
const operatorKeys = keys.filter((key) => isOperator(key));
|
|
3763
|
+
if (operatorKeys.length > 0 && operatorKeys.length !== keys.length) {
|
|
3764
|
+
throw new Error(`Operator and non-operator keys cannot be mixed in a relation where clause`);
|
|
3765
|
+
}
|
|
3766
|
+
if (operatorKeys.length > 1) {
|
|
3767
|
+
throw new Error(
|
|
3768
|
+
`Only one operator key is allowed in a relation where clause, but found: ${operatorKeys}`
|
|
3769
|
+
);
|
|
3770
|
+
}
|
|
3771
|
+
if (operatorKeys.length === 1) {
|
|
3772
|
+
const operator = operatorKeys[0];
|
|
3773
|
+
if (isOperatorOfType("group", operator)) {
|
|
3774
|
+
return processWhere(where, ctx);
|
|
3775
|
+
}
|
|
3776
|
+
return { [idAlias]: { [operator]: processNested(where[operator], ctx) } };
|
|
3777
|
+
}
|
|
3778
|
+
return processWhere(where, ctx);
|
|
3779
|
+
};
|
|
3542
3780
|
function processWhere(where, ctx) {
|
|
3543
3781
|
if (!isArray(where) && !isRecord$1(where)) {
|
|
3544
3782
|
throw new Error("Where must be an array or an object");
|
|
@@ -3551,7 +3789,10 @@ function processWhere(where, ctx) {
|
|
|
3551
3789
|
const filters = {};
|
|
3552
3790
|
for (const key of Object.keys(where)) {
|
|
3553
3791
|
const value = where[key];
|
|
3554
|
-
if (isOperatorOfType("group", key)
|
|
3792
|
+
if (isOperatorOfType("group", key)) {
|
|
3793
|
+
if (!Array.isArray(value)) {
|
|
3794
|
+
throw new Error(`Operator ${key} must be an array`);
|
|
3795
|
+
}
|
|
3555
3796
|
filters[key] = value.map((sub) => processNested(sub, ctx));
|
|
3556
3797
|
continue;
|
|
3557
3798
|
}
|
|
@@ -3575,15 +3816,12 @@ function processWhere(where, ctx) {
|
|
|
3575
3816
|
attributeName: key,
|
|
3576
3817
|
attribute
|
|
3577
3818
|
});
|
|
3578
|
-
|
|
3819
|
+
const nestedWhere = processRelationWhere(value, {
|
|
3579
3820
|
db,
|
|
3580
3821
|
qb,
|
|
3581
3822
|
alias: subAlias,
|
|
3582
3823
|
uid: attribute.target
|
|
3583
3824
|
});
|
|
3584
|
-
if (!isRecord$1(nestedWhere) || isOperatorOfType("where", keys(nestedWhere)[0])) {
|
|
3585
|
-
nestedWhere = { [qb.aliasColumn("id", subAlias)]: nestedWhere };
|
|
3586
|
-
}
|
|
3587
3825
|
Object.assign(filters, nestedWhere);
|
|
3588
3826
|
continue;
|
|
3589
3827
|
}
|
|
@@ -3736,8 +3974,8 @@ const applyWhereToColumn = (qb, column, columnWhere) => {
|
|
|
3736
3974
|
}
|
|
3737
3975
|
return qb.where(column, columnWhere);
|
|
3738
3976
|
}
|
|
3739
|
-
const
|
|
3740
|
-
|
|
3977
|
+
const keys = Object.keys(columnWhere);
|
|
3978
|
+
keys.forEach((operator) => {
|
|
3741
3979
|
const value = columnWhere[operator];
|
|
3742
3980
|
applyOperator(qb, column, operator, value);
|
|
3743
3981
|
});
|
|
@@ -4676,21 +4914,24 @@ const sortConnectArray = (connectArr, initialArr = [], strictSort = true) => {
|
|
|
4676
4914
|
(acc, rel) => ({ ...acc, [rel.id]: true }),
|
|
4677
4915
|
{}
|
|
4678
4916
|
);
|
|
4679
|
-
const mappedRelations = connectArr.reduce(
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4917
|
+
const mappedRelations = connectArr.reduce(
|
|
4918
|
+
(mapper, relation) => {
|
|
4919
|
+
const adjacentRelId = relation.position?.before || relation.position?.after;
|
|
4920
|
+
if (!adjacentRelId || !relationInInitialArray[adjacentRelId] && !mapper[adjacentRelId]) {
|
|
4921
|
+
needsSorting = true;
|
|
4922
|
+
}
|
|
4923
|
+
if (mapper[relation.id]) {
|
|
4924
|
+
throw new InvalidRelationError(
|
|
4925
|
+
`The relation with id ${relation.id} is already connected. You cannot connect the same relation twice.`
|
|
4926
|
+
);
|
|
4927
|
+
}
|
|
4928
|
+
return {
|
|
4929
|
+
[relation.id]: { ...relation, computed: false },
|
|
4930
|
+
...mapper
|
|
4931
|
+
};
|
|
4932
|
+
},
|
|
4933
|
+
{}
|
|
4934
|
+
);
|
|
4694
4935
|
if (!needsSorting)
|
|
4695
4936
|
return connectArr;
|
|
4696
4937
|
const computeRelation = (relation, relationsSeenInBranch) => {
|
|
@@ -4798,14 +5039,17 @@ const relationsOrderer = (initArr, idColumn, orderColumn, strict2) => {
|
|
|
4798
5039
|
* Get a map between the relation id and its order
|
|
4799
5040
|
*/
|
|
4800
5041
|
getOrderMap() {
|
|
4801
|
-
return _$1(computedRelations).groupBy("order").reduce(
|
|
4802
|
-
|
|
5042
|
+
return _$1(computedRelations).groupBy("order").reduce(
|
|
5043
|
+
(acc, relations) => {
|
|
5044
|
+
if (relations[0]?.init)
|
|
5045
|
+
return acc;
|
|
5046
|
+
relations.forEach((relation, idx) => {
|
|
5047
|
+
acc[relation.id] = Math.floor(relation.order) + (idx + 1) / (relations.length + 1);
|
|
5048
|
+
});
|
|
4803
5049
|
return acc;
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
return acc;
|
|
4808
|
-
}, {});
|
|
5050
|
+
},
|
|
5051
|
+
{}
|
|
5052
|
+
);
|
|
4809
5053
|
}
|
|
4810
5054
|
};
|
|
4811
5055
|
};
|
|
@@ -5559,10 +5803,13 @@ const createEntityManager = (db) => {
|
|
|
5559
5803
|
const entry = await this.findOne(uid, {
|
|
5560
5804
|
select: ["id"],
|
|
5561
5805
|
where: { id: entity.id },
|
|
5562
|
-
populate: fieldsArr.reduce(
|
|
5563
|
-
acc
|
|
5564
|
-
|
|
5565
|
-
|
|
5806
|
+
populate: fieldsArr.reduce(
|
|
5807
|
+
(acc, field) => {
|
|
5808
|
+
acc[field] = populate || true;
|
|
5809
|
+
return acc;
|
|
5810
|
+
},
|
|
5811
|
+
{}
|
|
5812
|
+
)
|
|
5566
5813
|
});
|
|
5567
5814
|
if (!entry) {
|
|
5568
5815
|
return null;
|
|
@@ -5628,7 +5875,7 @@ const createStorage = (opts) => {
|
|
|
5628
5875
|
};
|
|
5629
5876
|
};
|
|
5630
5877
|
const wrapTransaction = (db) => (fn) => () => {
|
|
5631
|
-
return db.
|
|
5878
|
+
return db.transaction(({ trx }) => Promise.resolve(fn(trx, db)));
|
|
5632
5879
|
};
|
|
5633
5880
|
const migrationResolver = ({ name, path: path2, context }) => {
|
|
5634
5881
|
const { db } = context;
|
|
@@ -5678,14 +5925,314 @@ const createUserMigrationProvider = (db) => {
|
|
|
5678
5925
|
}
|
|
5679
5926
|
};
|
|
5680
5927
|
};
|
|
5681
|
-
const
|
|
5928
|
+
const QUERIES = {
|
|
5929
|
+
async postgres(knex2, params) {
|
|
5930
|
+
const res = await knex2.raw(
|
|
5931
|
+
`
|
|
5932
|
+
SELECT :tableName:.id as id, string_agg(DISTINCT :inverseJoinColumn:::character varying, ',') as other_ids
|
|
5933
|
+
FROM :tableName:
|
|
5934
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5935
|
+
WHERE document_id IS NULL
|
|
5936
|
+
GROUP BY :tableName:.id, :joinColumn:
|
|
5937
|
+
LIMIT 1;
|
|
5938
|
+
`,
|
|
5939
|
+
params
|
|
5940
|
+
);
|
|
5941
|
+
return res.rows;
|
|
5942
|
+
},
|
|
5943
|
+
async mysql(knex2, params) {
|
|
5944
|
+
const [res] = await knex2.raw(
|
|
5945
|
+
`
|
|
5946
|
+
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids
|
|
5947
|
+
FROM :tableName:
|
|
5948
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5949
|
+
WHERE document_id IS NULL
|
|
5950
|
+
GROUP BY :tableName:.id, :joinColumn:
|
|
5951
|
+
LIMIT 1;
|
|
5952
|
+
`,
|
|
5953
|
+
params
|
|
5954
|
+
);
|
|
5955
|
+
return res;
|
|
5956
|
+
},
|
|
5957
|
+
async sqlite(knex2, params) {
|
|
5958
|
+
return knex2.raw(
|
|
5959
|
+
`
|
|
5960
|
+
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids
|
|
5961
|
+
FROM :tableName:
|
|
5962
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5963
|
+
WHERE document_id IS NULL
|
|
5964
|
+
GROUP BY :joinColumn:
|
|
5965
|
+
LIMIT 1;
|
|
5966
|
+
`,
|
|
5967
|
+
params
|
|
5968
|
+
);
|
|
5969
|
+
}
|
|
5970
|
+
};
|
|
5971
|
+
const getNextIdsToCreateDocumentId = async (db, knex2, {
|
|
5972
|
+
joinColumn,
|
|
5973
|
+
inverseJoinColumn,
|
|
5974
|
+
tableName,
|
|
5975
|
+
joinTableName
|
|
5976
|
+
}) => {
|
|
5977
|
+
const res = await QUERIES[db.dialect.client](knex2, {
|
|
5978
|
+
joinColumn,
|
|
5979
|
+
inverseJoinColumn,
|
|
5980
|
+
tableName,
|
|
5981
|
+
joinTableName
|
|
5982
|
+
});
|
|
5983
|
+
if (res.length > 0) {
|
|
5984
|
+
const row = res[0];
|
|
5985
|
+
const otherIds = row.other_ids ? row.other_ids.split(",").map((v) => parseInt(v, 10)) : [];
|
|
5986
|
+
return [row.id, ...otherIds];
|
|
5987
|
+
}
|
|
5988
|
+
return [];
|
|
5989
|
+
};
|
|
5990
|
+
const migrateDocumentIdsWithLocalizations = async (db, knex2, meta) => {
|
|
5991
|
+
const singularName = meta.singularName.toLowerCase();
|
|
5992
|
+
const joinColumn = identifiers.getJoinColumnAttributeIdName(singularName);
|
|
5993
|
+
const inverseJoinColumn = identifiers.getInverseJoinColumnAttributeIdName(singularName);
|
|
5994
|
+
let ids;
|
|
5995
|
+
do {
|
|
5996
|
+
ids = await getNextIdsToCreateDocumentId(db, knex2, {
|
|
5997
|
+
joinColumn,
|
|
5998
|
+
inverseJoinColumn,
|
|
5999
|
+
tableName: meta.tableName,
|
|
6000
|
+
joinTableName: identifiers.getJoinTableName(meta.tableName, `localizations`)
|
|
6001
|
+
});
|
|
6002
|
+
if (ids.length > 0) {
|
|
6003
|
+
await knex2(meta.tableName).update({ document_id: createId() }).whereIn("id", ids);
|
|
6004
|
+
}
|
|
6005
|
+
} while (ids.length > 0);
|
|
6006
|
+
};
|
|
6007
|
+
const migrationDocumentIds = async (db, knex2, meta) => {
|
|
6008
|
+
let updatedRows;
|
|
6009
|
+
do {
|
|
6010
|
+
updatedRows = await knex2(meta.tableName).update({ document_id: createId() }).whereIn(
|
|
6011
|
+
"id",
|
|
6012
|
+
knex2(meta.tableName).select("id").from(knex2(meta.tableName).select("id").whereNull("document_id").limit(1).as("sub_query"))
|
|
6013
|
+
);
|
|
6014
|
+
} while (updatedRows > 0);
|
|
6015
|
+
};
|
|
6016
|
+
const createDocumentIdColumn = async (knex2, tableName) => {
|
|
6017
|
+
await knex2.schema.alterTable(tableName, (table) => {
|
|
6018
|
+
table.string("document_id");
|
|
6019
|
+
});
|
|
6020
|
+
};
|
|
6021
|
+
const hasLocalizationsJoinTable = async (knex2, tableName) => {
|
|
6022
|
+
const joinTableName = identifiers.getJoinTableName(tableName, "localizations");
|
|
6023
|
+
return knex2.schema.hasTable(joinTableName);
|
|
6024
|
+
};
|
|
6025
|
+
const createdDocumentId = {
|
|
6026
|
+
name: "5.0.0-02-created-document-id",
|
|
6027
|
+
async up(knex2, db) {
|
|
6028
|
+
for (const meta of db.metadata.values()) {
|
|
6029
|
+
const hasTable = await knex2.schema.hasTable(meta.tableName);
|
|
6030
|
+
if (!hasTable) {
|
|
6031
|
+
continue;
|
|
6032
|
+
}
|
|
6033
|
+
if ("documentId" in meta.attributes) {
|
|
6034
|
+
const hasDocumentIdColumn = await knex2.schema.hasColumn(meta.tableName, "document_id");
|
|
6035
|
+
if (hasDocumentIdColumn) {
|
|
6036
|
+
continue;
|
|
6037
|
+
}
|
|
6038
|
+
await createDocumentIdColumn(knex2, meta.tableName);
|
|
6039
|
+
if (await hasLocalizationsJoinTable(knex2, meta.tableName)) {
|
|
6040
|
+
await migrateDocumentIdsWithLocalizations(db, knex2, meta);
|
|
6041
|
+
} else {
|
|
6042
|
+
await migrationDocumentIds(db, knex2, meta);
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
}
|
|
6046
|
+
},
|
|
6047
|
+
async down() {
|
|
6048
|
+
throw new Error("not implemented");
|
|
6049
|
+
}
|
|
6050
|
+
};
|
|
6051
|
+
const debug = createDebug("strapi::database::migration");
|
|
6052
|
+
const renameIdentifiersLongerThanMaxLength = {
|
|
6053
|
+
name: "5.0.0-rename-identifiers-longer-than-max-length",
|
|
6054
|
+
async up(knex2, db) {
|
|
6055
|
+
const md = db.metadata;
|
|
6056
|
+
const diffs = findDiffs(md);
|
|
6057
|
+
for (const indexDiff of diffs.indexes) {
|
|
6058
|
+
await renameIndex(knex2, db, indexDiff);
|
|
6059
|
+
}
|
|
6060
|
+
for (const columnDiff of diffs.columns) {
|
|
6061
|
+
const { full, short } = columnDiff;
|
|
6062
|
+
const tableName = full.tableName;
|
|
6063
|
+
const hasTable = await knex2.schema.hasTable(tableName);
|
|
6064
|
+
if (hasTable) {
|
|
6065
|
+
const hasColumn = await knex2.schema.hasColumn(tableName, full.columnName);
|
|
6066
|
+
if (hasColumn) {
|
|
6067
|
+
await knex2.schema.alterTable(tableName, async (table) => {
|
|
6068
|
+
debug(`renaming column ${full.columnName} to ${short.columnName}`);
|
|
6069
|
+
table.renameColumn(full.columnName, short.columnName);
|
|
6070
|
+
});
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6073
|
+
}
|
|
6074
|
+
for (const tableDiff of diffs.tables) {
|
|
6075
|
+
const hasTable = await knex2.schema.hasTable(tableDiff.full.tableName);
|
|
6076
|
+
if (hasTable) {
|
|
6077
|
+
debug(`renaming table ${tableDiff.full.tableName} to ${tableDiff.short.tableName}`);
|
|
6078
|
+
await knex2.schema.renameTable(tableDiff.full.tableName, tableDiff.short.tableName);
|
|
6079
|
+
}
|
|
6080
|
+
}
|
|
6081
|
+
},
|
|
6082
|
+
async down() {
|
|
6083
|
+
throw new Error("not implemented");
|
|
6084
|
+
}
|
|
6085
|
+
};
|
|
6086
|
+
const renameIndex = async (knex2, db, diff) => {
|
|
6087
|
+
const client = db.config.connection.client;
|
|
6088
|
+
const short = diff.short;
|
|
6089
|
+
const full = diff.full;
|
|
6090
|
+
if (full.indexName === short.indexName) {
|
|
6091
|
+
debug(`not renaming index ${full.indexName} because name hasn't changed`);
|
|
6092
|
+
return;
|
|
6093
|
+
}
|
|
6094
|
+
if (short.indexName.includes("_lnk_") || full.indexName.includes("_lnk_") || short.indexName.endsWith("fk") || full.indexName.endsWith("fk")) {
|
|
6095
|
+
return;
|
|
6096
|
+
}
|
|
6097
|
+
debug(`renaming index from ${full.indexName} to ${short.indexName}`);
|
|
6098
|
+
try {
|
|
6099
|
+
await knex2.transaction(async (trx) => {
|
|
6100
|
+
if (client === "mysql" || client === "mariadb") {
|
|
6101
|
+
await knex2.raw(
|
|
6102
|
+
`ALTER TABLE \`${full.tableName}\` RENAME INDEX \`${full.indexName}\` TO \`${short.indexName}\``
|
|
6103
|
+
).transacting(trx);
|
|
6104
|
+
} else if (client === "pg" || client === "postgres") {
|
|
6105
|
+
await knex2.raw(`ALTER INDEX "${full.indexName}" RENAME TO "${short.indexName}"`).transacting(trx);
|
|
6106
|
+
} else if (client === "sqlite" || client === "better") {
|
|
6107
|
+
} else {
|
|
6108
|
+
debug("No db client name matches, not creating index");
|
|
6109
|
+
}
|
|
6110
|
+
});
|
|
6111
|
+
} catch (err) {
|
|
6112
|
+
debug(`error creating index: ${JSON.stringify(err)}`);
|
|
6113
|
+
}
|
|
6114
|
+
};
|
|
6115
|
+
const findDiffs = (shortMap) => {
|
|
6116
|
+
const diffs = {
|
|
6117
|
+
tables: [],
|
|
6118
|
+
columns: [],
|
|
6119
|
+
indexes: []
|
|
6120
|
+
};
|
|
6121
|
+
const shortArr = Array.from(shortMap.entries());
|
|
6122
|
+
shortArr.forEach(([, shortObj], index2) => {
|
|
6123
|
+
const fullTableName = identifiers.getUnshortenedName(shortObj.tableName);
|
|
6124
|
+
if (!fullTableName) {
|
|
6125
|
+
throw new Error(`Missing full table name for ${shortObj.tableName}`);
|
|
6126
|
+
}
|
|
6127
|
+
if (shortObj.tableName !== fullTableName) {
|
|
6128
|
+
diffs.tables.push({
|
|
6129
|
+
full: {
|
|
6130
|
+
index: index2,
|
|
6131
|
+
key: "tableName",
|
|
6132
|
+
tableName: fullTableName
|
|
6133
|
+
},
|
|
6134
|
+
short: {
|
|
6135
|
+
index: index2,
|
|
6136
|
+
key: "tableName",
|
|
6137
|
+
tableName: shortObj.tableName
|
|
6138
|
+
}
|
|
6139
|
+
});
|
|
6140
|
+
}
|
|
6141
|
+
for (const attrKey in shortObj.attributes) {
|
|
6142
|
+
if (shortObj.attributes[attrKey].type === "relation") {
|
|
6143
|
+
continue;
|
|
6144
|
+
}
|
|
6145
|
+
const attr = shortObj.attributes[attrKey];
|
|
6146
|
+
const shortColumnName = attr.columnName;
|
|
6147
|
+
const longColumnName = identifiers.getUnshortenedName(shortColumnName);
|
|
6148
|
+
if (!shortColumnName || !longColumnName) {
|
|
6149
|
+
throw new Error(`missing column name(s) for attribute ${JSON.stringify(attr, null, 2)}`);
|
|
6150
|
+
}
|
|
6151
|
+
if (shortColumnName && longColumnName && shortColumnName !== longColumnName) {
|
|
6152
|
+
diffs.columns.push({
|
|
6153
|
+
short: {
|
|
6154
|
+
index: index2,
|
|
6155
|
+
tableName: fullTableName,
|
|
6156
|
+
// NOTE: this means that we must rename columns before tables
|
|
6157
|
+
key: `attributes.${attrKey}`,
|
|
6158
|
+
columnName: shortColumnName
|
|
6159
|
+
},
|
|
6160
|
+
full: {
|
|
6161
|
+
index: index2,
|
|
6162
|
+
tableName: fullTableName,
|
|
6163
|
+
key: `attributes.${attrKey}`,
|
|
6164
|
+
columnName: longColumnName
|
|
6165
|
+
}
|
|
6166
|
+
});
|
|
6167
|
+
}
|
|
6168
|
+
}
|
|
6169
|
+
for (const attrKey in shortObj.indexes) {
|
|
6170
|
+
const shortIndexName = shortObj.indexes[attrKey].name;
|
|
6171
|
+
const longIndexName = identifiers.getUnshortenedName(shortIndexName);
|
|
6172
|
+
if (!longIndexName) {
|
|
6173
|
+
throw new Error(`Missing full index name for ${shortIndexName}`);
|
|
6174
|
+
}
|
|
6175
|
+
if (shortIndexName && longIndexName && shortIndexName !== longIndexName) {
|
|
6176
|
+
diffs.indexes.push({
|
|
6177
|
+
short: {
|
|
6178
|
+
index: index2,
|
|
6179
|
+
tableName: fullTableName,
|
|
6180
|
+
// NOTE: this means that we must rename columns before tables
|
|
6181
|
+
key: `indexes.${attrKey}`,
|
|
6182
|
+
indexName: shortIndexName
|
|
6183
|
+
},
|
|
6184
|
+
full: {
|
|
6185
|
+
index: index2,
|
|
6186
|
+
tableName: fullTableName,
|
|
6187
|
+
key: `indexes.${attrKey}`,
|
|
6188
|
+
indexName: longIndexName
|
|
6189
|
+
}
|
|
6190
|
+
});
|
|
6191
|
+
}
|
|
6192
|
+
}
|
|
6193
|
+
});
|
|
6194
|
+
return diffs;
|
|
6195
|
+
};
|
|
6196
|
+
const createLocaleColumn = async (db, tableName) => {
|
|
6197
|
+
await db.schema.alterTable(tableName, (table) => {
|
|
6198
|
+
table.string("locale");
|
|
6199
|
+
});
|
|
6200
|
+
};
|
|
6201
|
+
const createdLocale = {
|
|
6202
|
+
name: "5.0.0-03-created-locale",
|
|
6203
|
+
async up(knex2, db) {
|
|
6204
|
+
for (const meta of db.metadata.values()) {
|
|
6205
|
+
const hasTable = await knex2.schema.hasTable(meta.tableName);
|
|
6206
|
+
if (!hasTable) {
|
|
6207
|
+
continue;
|
|
6208
|
+
}
|
|
6209
|
+
const uid = meta.uid;
|
|
6210
|
+
const model = strapi.getModel(uid);
|
|
6211
|
+
if (!model) {
|
|
6212
|
+
continue;
|
|
6213
|
+
}
|
|
6214
|
+
if (isNil(meta.attributes.locale)) {
|
|
6215
|
+
await createLocaleColumn(knex2, meta.tableName);
|
|
6216
|
+
}
|
|
6217
|
+
}
|
|
6218
|
+
},
|
|
6219
|
+
async down() {
|
|
6220
|
+
throw new Error("not implemented");
|
|
6221
|
+
}
|
|
6222
|
+
};
|
|
6223
|
+
const internalMigrations = [
|
|
6224
|
+
renameIdentifiersLongerThanMaxLength,
|
|
6225
|
+
createdDocumentId,
|
|
6226
|
+
createdLocale
|
|
6227
|
+
];
|
|
5682
6228
|
const createInternalMigrationProvider = (db) => {
|
|
5683
6229
|
const context = { db };
|
|
6230
|
+
const migrations = [...internalMigrations];
|
|
5684
6231
|
const umzugProvider = new Umzug({
|
|
5685
6232
|
storage: createStorage({ db, tableName: "strapi_migrations_internal" }),
|
|
5686
6233
|
logger: console,
|
|
5687
6234
|
context,
|
|
5688
|
-
migrations:
|
|
6235
|
+
migrations: () => migrations.map((migration) => {
|
|
5689
6236
|
return {
|
|
5690
6237
|
name: migration.name,
|
|
5691
6238
|
up: wrapTransaction(context.db)(migration.up),
|
|
@@ -5694,6 +6241,9 @@ const createInternalMigrationProvider = (db) => {
|
|
|
5694
6241
|
})
|
|
5695
6242
|
});
|
|
5696
6243
|
return {
|
|
6244
|
+
async register(migration) {
|
|
6245
|
+
migrations.push(migration);
|
|
6246
|
+
},
|
|
5697
6247
|
async shouldRun() {
|
|
5698
6248
|
const pendingMigrations = await umzugProvider.pending();
|
|
5699
6249
|
return pendingMigrations.length > 0;
|
|
@@ -5707,8 +6257,13 @@ const createInternalMigrationProvider = (db) => {
|
|
|
5707
6257
|
};
|
|
5708
6258
|
};
|
|
5709
6259
|
const createMigrationsProvider = (db) => {
|
|
5710
|
-
const
|
|
6260
|
+
const userProvider = createUserMigrationProvider(db);
|
|
6261
|
+
const internalProvider = createInternalMigrationProvider(db);
|
|
6262
|
+
const providers = [userProvider, internalProvider];
|
|
5711
6263
|
return {
|
|
6264
|
+
providers: {
|
|
6265
|
+
internal: internalProvider
|
|
6266
|
+
},
|
|
5712
6267
|
async shouldRun() {
|
|
5713
6268
|
const shouldRunResponses = await Promise.all(
|
|
5714
6269
|
providers.map((provider) => provider.shouldRun())
|
|
@@ -5904,8 +6459,14 @@ const validateBidirectionalRelations = async (db) => {
|
|
|
5904
6459
|
for (const { relation, invRelation } of invalidLinks) {
|
|
5905
6460
|
const modelMetadata = db.metadata.get(invRelation.target);
|
|
5906
6461
|
const invModelMetadata = db.metadata.get(relation.target);
|
|
5907
|
-
const joinTableName = getJoinTableName(
|
|
5908
|
-
|
|
6462
|
+
const joinTableName = identifiers.getJoinTableName(
|
|
6463
|
+
snakeCase(modelMetadata.tableName),
|
|
6464
|
+
snakeCase(invRelation.inversedBy)
|
|
6465
|
+
);
|
|
6466
|
+
const inverseJoinTableName = identifiers.getJoinTableName(
|
|
6467
|
+
snakeCase(invModelMetadata.tableName),
|
|
6468
|
+
snakeCase(relation.inversedBy)
|
|
6469
|
+
);
|
|
5909
6470
|
const joinTableEmpty = await isLinkTableEmpty(db, joinTableName);
|
|
5910
6471
|
const inverseJoinTableEmpty = await isLinkTableEmpty(db, inverseJoinTableName);
|
|
5911
6472
|
if (joinTableEmpty) {
|
|
@@ -5951,7 +6512,7 @@ class Database {
|
|
|
5951
6512
|
};
|
|
5952
6513
|
this.dialect = getDialect(this);
|
|
5953
6514
|
this.dialect.configure();
|
|
5954
|
-
this.metadata = createMetadata();
|
|
6515
|
+
this.metadata = createMetadata([]);
|
|
5955
6516
|
this.connection = createConnection(this.config.connection, {
|
|
5956
6517
|
pool: { afterCreate: afterCreate(this) }
|
|
5957
6518
|
});
|
|
@@ -6028,11 +6589,9 @@ class Database {
|
|
|
6028
6589
|
await this.connection.destroy();
|
|
6029
6590
|
}
|
|
6030
6591
|
}
|
|
6031
|
-
const utils = { identifiers };
|
|
6032
6592
|
export {
|
|
6033
6593
|
Database,
|
|
6034
6594
|
index as errors,
|
|
6035
|
-
isKnexQuery
|
|
6036
|
-
utils
|
|
6595
|
+
isKnexQuery
|
|
6037
6596
|
};
|
|
6038
6597
|
//# sourceMappingURL=index.mjs.map
|