@strapi/database 5.0.0-beta.1 → 5.0.0-beta.3
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/relations-orderer.d.ts.map +1 -1
- package/dist/index.d.ts +3 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +829 -324
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +827 -322
- 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/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/index.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/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 +7 -6
- 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
|
}
|
|
@@ -1176,27 +1177,27 @@ const createHelpers = (db) => {
|
|
|
1176
1177
|
const alterTable = async (schemaBuilder, table) => {
|
|
1177
1178
|
await schemaBuilder.alterTable(table.name, (tableBuilder) => {
|
|
1178
1179
|
for (const removedIndex of table.indexes.removed) {
|
|
1179
|
-
debug$
|
|
1180
|
+
debug$2(`Dropping index ${removedIndex.name} on ${table.name}`);
|
|
1180
1181
|
dropIndex(tableBuilder, removedIndex);
|
|
1181
1182
|
}
|
|
1182
1183
|
for (const updateddIndex of table.indexes.updated) {
|
|
1183
|
-
debug$
|
|
1184
|
+
debug$2(`Dropping updated index ${updateddIndex.name} on ${table.name}`);
|
|
1184
1185
|
dropIndex(tableBuilder, updateddIndex.object);
|
|
1185
1186
|
}
|
|
1186
1187
|
for (const removedForeignKey of table.foreignKeys.removed) {
|
|
1187
|
-
debug$
|
|
1188
|
+
debug$2(`Dropping foreign key ${removedForeignKey.name} on ${table.name}`);
|
|
1188
1189
|
dropForeignKey(tableBuilder, removedForeignKey);
|
|
1189
1190
|
}
|
|
1190
1191
|
for (const updatedForeignKey of table.foreignKeys.updated) {
|
|
1191
|
-
debug$
|
|
1192
|
+
debug$2(`Dropping updated foreign key ${updatedForeignKey.name} on ${table.name}`);
|
|
1192
1193
|
dropForeignKey(tableBuilder, updatedForeignKey.object);
|
|
1193
1194
|
}
|
|
1194
1195
|
for (const removedColumn of table.columns.removed) {
|
|
1195
|
-
debug$
|
|
1196
|
+
debug$2(`Dropping column ${removedColumn.name} on ${table.name}`);
|
|
1196
1197
|
dropColumn(tableBuilder, removedColumn);
|
|
1197
1198
|
}
|
|
1198
1199
|
for (const updatedColumn of table.columns.updated) {
|
|
1199
|
-
debug$
|
|
1200
|
+
debug$2(`Updating column ${updatedColumn.name} on ${table.name}`);
|
|
1200
1201
|
const { object } = updatedColumn;
|
|
1201
1202
|
if (object.type === "increments") {
|
|
1202
1203
|
createColumn2(tableBuilder, { ...object, type: "integer" }).alter();
|
|
@@ -1205,15 +1206,15 @@ const createHelpers = (db) => {
|
|
|
1205
1206
|
}
|
|
1206
1207
|
}
|
|
1207
1208
|
for (const updatedForeignKey of table.foreignKeys.updated) {
|
|
1208
|
-
debug$
|
|
1209
|
+
debug$2(`Recreating updated foreign key ${updatedForeignKey.name} on ${table.name}`);
|
|
1209
1210
|
createForeignKey(tableBuilder, updatedForeignKey.object);
|
|
1210
1211
|
}
|
|
1211
1212
|
for (const updatedIndex of table.indexes.updated) {
|
|
1212
|
-
debug$
|
|
1213
|
+
debug$2(`Recreating updated index ${updatedIndex.name} on ${table.name}`);
|
|
1213
1214
|
createIndex(tableBuilder, updatedIndex.object);
|
|
1214
1215
|
}
|
|
1215
1216
|
for (const addedColumn of table.columns.added) {
|
|
1216
|
-
debug$
|
|
1217
|
+
debug$2(`Creating column ${addedColumn.name} on ${table.name}`);
|
|
1217
1218
|
if (addedColumn.type === "increments" && !db.dialect.canAddIncrements()) {
|
|
1218
1219
|
tableBuilder.integer(addedColumn.name).unsigned();
|
|
1219
1220
|
tableBuilder.primary([addedColumn.name]);
|
|
@@ -1222,11 +1223,11 @@ const createHelpers = (db) => {
|
|
|
1222
1223
|
}
|
|
1223
1224
|
}
|
|
1224
1225
|
for (const addedForeignKey of table.foreignKeys.added) {
|
|
1225
|
-
debug$
|
|
1226
|
+
debug$2(`Creating foreign keys ${addedForeignKey.name} on ${table.name}`);
|
|
1226
1227
|
createForeignKey(tableBuilder, addedForeignKey);
|
|
1227
1228
|
}
|
|
1228
1229
|
for (const addedIndex of table.indexes.added) {
|
|
1229
|
-
debug$
|
|
1230
|
+
debug$2(`Creating index ${addedIndex.name} on ${table.name}`);
|
|
1230
1231
|
createIndex(tableBuilder, addedIndex);
|
|
1231
1232
|
}
|
|
1232
1233
|
});
|
|
@@ -1645,11 +1646,6 @@ const isScalar = (type) => SCALAR_TYPES.includes(type);
|
|
|
1645
1646
|
const isRelation = (type) => type === "relation";
|
|
1646
1647
|
const isScalarAttribute = (attribute) => isScalar(attribute.type);
|
|
1647
1648
|
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
1649
|
function createHash(data, len) {
|
|
1654
1650
|
if (!_.isInteger(len) || len <= 0) {
|
|
1655
1651
|
throw new Error(`createHash length must be a positive integer, received ${len}`);
|
|
@@ -1657,201 +1653,334 @@ function createHash(data, len) {
|
|
|
1657
1653
|
const hash = crypto__default$1.default.createHash("shake256", { outputLength: Math.ceil(len / 2) }).update(data);
|
|
1658
1654
|
return hash.digest("hex").substring(0, len);
|
|
1659
1655
|
}
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1656
|
+
const IDENTIFIER_MAX_LENGTH = 55;
|
|
1657
|
+
class Identifiers {
|
|
1658
|
+
ID_COLUMN = "id";
|
|
1659
|
+
ORDER_COLUMN = "order";
|
|
1660
|
+
FIELD_COLUMN = "field";
|
|
1661
|
+
HASH_LENGTH = 5;
|
|
1662
|
+
HASH_SEPARATOR = "";
|
|
1663
|
+
// no separator is needed, we will just attach hash directly to shortened name
|
|
1664
|
+
IDENTIFIER_SEPARATOR = "_";
|
|
1665
|
+
MIN_TOKEN_LENGTH = 3;
|
|
1666
|
+
// the min characters required at the beginning of a name part
|
|
1667
|
+
// Fixed compression map for suffixes and prefixes
|
|
1668
|
+
#replacementMap = {
|
|
1669
|
+
links: "lnk",
|
|
1670
|
+
order_inv_fk: "oifk",
|
|
1671
|
+
order: "ord",
|
|
1672
|
+
morphs: "mph",
|
|
1673
|
+
index: "idx",
|
|
1674
|
+
inv_fk: "ifk",
|
|
1675
|
+
order_fk: "ofk",
|
|
1676
|
+
id_column_index: "idix",
|
|
1677
|
+
order_index: "oidx",
|
|
1678
|
+
unique: "uq",
|
|
1679
|
+
primary: "pk"
|
|
1680
|
+
};
|
|
1681
|
+
#options;
|
|
1682
|
+
constructor(options) {
|
|
1683
|
+
this.#options = options;
|
|
1677
1684
|
}
|
|
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)");
|
|
1685
|
+
get replacementMap() {
|
|
1686
|
+
return this.#replacementMap;
|
|
1683
1687
|
}
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
return fullLengthName;
|
|
1688
|
+
get options() {
|
|
1689
|
+
return this.#options;
|
|
1687
1690
|
}
|
|
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);
|
|
1691
|
+
mapshortNames = (name) => {
|
|
1692
|
+
if (name in this.replacementMap) {
|
|
1693
|
+
return this.replacementMap[name];
|
|
1722
1694
|
}
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1695
|
+
return void 0;
|
|
1696
|
+
};
|
|
1697
|
+
// Generic name handler that must be used by all helper functions
|
|
1698
|
+
/**
|
|
1699
|
+
* TODO: we should be requiring snake_case inputs for all names here, but we
|
|
1700
|
+
* aren't and it will require some refactoring to make it work. Currently if
|
|
1701
|
+
* we get names 'myModel' and 'my_model' they would be converted to the same
|
|
1702
|
+
* final string my_model which generally works but is not entirely safe
|
|
1703
|
+
* */
|
|
1704
|
+
getName = (names, options) => {
|
|
1705
|
+
const tokens = ___default.default.castArray(names).map((name) => {
|
|
1706
|
+
return {
|
|
1707
|
+
name,
|
|
1708
|
+
compressible: true
|
|
1709
|
+
};
|
|
1710
|
+
});
|
|
1711
|
+
if (options?.suffix) {
|
|
1712
|
+
tokens.push({
|
|
1713
|
+
name: options.suffix,
|
|
1714
|
+
compressible: false,
|
|
1715
|
+
shortName: this.mapshortNames(options.suffix)
|
|
1716
|
+
});
|
|
1729
1717
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
break;
|
|
1718
|
+
if (options?.prefix) {
|
|
1719
|
+
tokens.unshift({
|
|
1720
|
+
name: options.prefix,
|
|
1721
|
+
compressible: false,
|
|
1722
|
+
shortName: this.mapshortNames(options.prefix)
|
|
1723
|
+
});
|
|
1737
1724
|
}
|
|
1738
|
-
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1725
|
+
return this.getNameFromTokens(tokens);
|
|
1726
|
+
};
|
|
1727
|
+
/**
|
|
1728
|
+
* TABLES
|
|
1729
|
+
*/
|
|
1730
|
+
getTableName = (name, options) => {
|
|
1731
|
+
return this.getName(name, options);
|
|
1732
|
+
};
|
|
1733
|
+
getJoinTableName = (collectionName, attributeName, options) => {
|
|
1734
|
+
return this.getName([collectionName, attributeName], {
|
|
1735
|
+
suffix: "links",
|
|
1736
|
+
...options
|
|
1737
|
+
});
|
|
1738
|
+
};
|
|
1739
|
+
getMorphTableName = (collectionName, attributeName, options) => {
|
|
1740
|
+
return this.getName([_.snakeCase(collectionName), _.snakeCase(attributeName)], {
|
|
1741
|
+
suffix: "morphs",
|
|
1742
|
+
...options
|
|
1743
|
+
});
|
|
1744
|
+
};
|
|
1745
|
+
/**
|
|
1746
|
+
* COLUMNS
|
|
1747
|
+
*/
|
|
1748
|
+
getColumnName = (attributeName, options) => {
|
|
1749
|
+
return this.getName(attributeName, options);
|
|
1750
|
+
};
|
|
1751
|
+
getJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1752
|
+
return this.getName(attributeName, { suffix: "id", ...options });
|
|
1753
|
+
};
|
|
1754
|
+
getInverseJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1755
|
+
return this.getName(_.snakeCase(attributeName), { suffix: "id", prefix: "inv", ...options });
|
|
1756
|
+
};
|
|
1757
|
+
getOrderColumnName = (singularName, options) => {
|
|
1758
|
+
return this.getName(singularName, { suffix: "order", ...options });
|
|
1759
|
+
};
|
|
1760
|
+
getInverseOrderColumnName = (singularName, options) => {
|
|
1761
|
+
return this.getName(singularName, {
|
|
1762
|
+
suffix: "order",
|
|
1763
|
+
prefix: "inv",
|
|
1764
|
+
...options
|
|
1765
|
+
});
|
|
1766
|
+
};
|
|
1767
|
+
/**
|
|
1768
|
+
* Morph Join Tables
|
|
1769
|
+
*/
|
|
1770
|
+
getMorphColumnJoinTableIdName = (singularName, options) => {
|
|
1771
|
+
return this.getName(_.snakeCase(singularName), { suffix: "id", ...options });
|
|
1772
|
+
};
|
|
1773
|
+
getMorphColumnAttributeIdName = (attributeName, options) => {
|
|
1774
|
+
return this.getName(_.snakeCase(attributeName), { suffix: "id", ...options });
|
|
1775
|
+
};
|
|
1776
|
+
getMorphColumnTypeName = (attributeName, options) => {
|
|
1777
|
+
return this.getName(_.snakeCase(attributeName), { suffix: "type", ...options });
|
|
1778
|
+
};
|
|
1779
|
+
/**
|
|
1780
|
+
* INDEXES
|
|
1781
|
+
* Note that these methods are generally used to reference full table names + attribute(s), which
|
|
1782
|
+
* may already be shortened strings rather than individual parts.
|
|
1783
|
+
* That is fine and expected to compress the previously incompressible parts of those strings,
|
|
1784
|
+
* because in these cases the relevant information is the table name and we can't really do
|
|
1785
|
+
* any better; shortening the individual parts again might make it even more confusing.
|
|
1786
|
+
*
|
|
1787
|
+
* So for example, the fk for the table `mytable_myattr4567d_localizations` will become
|
|
1788
|
+
* mytable_myattr4567d_loc63bf2_fk
|
|
1789
|
+
*/
|
|
1790
|
+
// base index types
|
|
1791
|
+
getIndexName = (names, options) => {
|
|
1792
|
+
return this.getName(names, { suffix: "index", ...options });
|
|
1793
|
+
};
|
|
1794
|
+
getFkIndexName = (names, options) => {
|
|
1795
|
+
return this.getName(names, { suffix: "fk", ...options });
|
|
1796
|
+
};
|
|
1797
|
+
getUniqueIndexName = (names, options) => {
|
|
1798
|
+
return this.getName(names, { suffix: "unique", ...options });
|
|
1799
|
+
};
|
|
1800
|
+
getPrimaryIndexName = (names, options) => {
|
|
1801
|
+
return this.getName(names, { suffix: "primary", ...options });
|
|
1802
|
+
};
|
|
1803
|
+
// custom index types
|
|
1804
|
+
getInverseFkIndexName = (names, options) => {
|
|
1805
|
+
return this.getName(names, { suffix: "inv_fk", ...options });
|
|
1806
|
+
};
|
|
1807
|
+
getOrderFkIndexName = (names, options) => {
|
|
1808
|
+
return this.getName(names, { suffix: "order_fk", ...options });
|
|
1809
|
+
};
|
|
1810
|
+
getOrderInverseFkIndexName = (names, options) => {
|
|
1811
|
+
return this.getName(names, { suffix: "order_inv_fk", ...options });
|
|
1812
|
+
};
|
|
1813
|
+
getIdColumnIndexName = (names, options) => {
|
|
1814
|
+
return this.getName(names, { suffix: "id_column_index", ...options });
|
|
1815
|
+
};
|
|
1816
|
+
getOrderIndexName = (names, options) => {
|
|
1817
|
+
return this.getName(names, { suffix: "order_index", ...options });
|
|
1818
|
+
};
|
|
1819
|
+
/**
|
|
1820
|
+
* Generates a string with a max length, appending a hash at the end if necessary to keep it unique
|
|
1821
|
+
*
|
|
1822
|
+
* @example
|
|
1823
|
+
* // if we have strings such as "longstring1" and "longstring2" with a max length of 9,
|
|
1824
|
+
* // we don't want to end up with "longstrin" and "longstrin"
|
|
1825
|
+
* // we want something such as "longs0b23" and "longs953f"
|
|
1826
|
+
* const token1 = generateToken("longstring1", 9); // "longs0b23"
|
|
1827
|
+
* const token2 = generateToken("longstring2", 9); // "longs953f"
|
|
1828
|
+
*
|
|
1829
|
+
* @param name - The base name
|
|
1830
|
+
* @param len - The desired length of the token.
|
|
1831
|
+
* @returns The generated token with hash.
|
|
1832
|
+
* @throws Error if the length is not a positive integer, or if the length is too short for the token.
|
|
1833
|
+
* @internal
|
|
1834
|
+
*/
|
|
1835
|
+
getShortenedName = (name, len) => {
|
|
1836
|
+
if (!_.isInteger(len) || len <= 0) {
|
|
1837
|
+
throw new Error(`tokenWithHash length must be a positive integer, received ${len}`);
|
|
1743
1838
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1839
|
+
if (name.length <= len) {
|
|
1840
|
+
return name;
|
|
1841
|
+
}
|
|
1842
|
+
if (len < this.MIN_TOKEN_LENGTH + this.HASH_LENGTH) {
|
|
1843
|
+
throw new Error(
|
|
1844
|
+
`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}`
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1847
|
+
const availableLength = len - this.HASH_LENGTH - this.HASH_SEPARATOR.length;
|
|
1848
|
+
if (availableLength < this.MIN_TOKEN_LENGTH) {
|
|
1849
|
+
throw new Error(
|
|
1850
|
+
`length for part of identifier minimum is less than min token length (${this.MIN_TOKEN_LENGTH}), received ${len} for token ${name}`
|
|
1851
|
+
);
|
|
1852
|
+
}
|
|
1853
|
+
return `${name.substring(0, availableLength)}${this.HASH_SEPARATOR}${createHash(
|
|
1854
|
+
name,
|
|
1855
|
+
this.HASH_LENGTH
|
|
1856
|
+
)}`;
|
|
1857
|
+
};
|
|
1858
|
+
/**
|
|
1859
|
+
* Constructs a name from an array of name tokens within a specified maximum length. It ensures the final name does not exceed
|
|
1860
|
+
* this limit by selectively compressing tokens marked as compressible. If the name exceeds the maximum length and cannot be
|
|
1861
|
+
* compressed sufficiently, an error is thrown. This function supports dynamic adjustment of token lengths to fit within the
|
|
1862
|
+
* maxLength constraint (that is, it will always make use of all available space), while also ensuring the preservation of
|
|
1863
|
+
* incompressible tokens.
|
|
1864
|
+
* @internal
|
|
1865
|
+
*/
|
|
1866
|
+
getNameFromTokens = (nameTokens) => {
|
|
1867
|
+
const { maxLength } = this.options;
|
|
1868
|
+
if (!_.isInteger(maxLength) || maxLength < 0) {
|
|
1869
|
+
throw new Error("maxLength must be a positive integer or 0 (for unlimited length)");
|
|
1870
|
+
}
|
|
1871
|
+
const unshortenedName = nameTokens.map((token) => {
|
|
1872
|
+
return token.name;
|
|
1873
|
+
}).join(this.IDENTIFIER_SEPARATOR);
|
|
1874
|
+
if (maxLength === 0) {
|
|
1875
|
+
this.setUnshortenedName(unshortenedName, unshortenedName);
|
|
1876
|
+
return unshortenedName;
|
|
1877
|
+
}
|
|
1878
|
+
const fullLengthName = nameTokens.map((token) => {
|
|
1879
|
+
if (token.compressible) {
|
|
1880
|
+
return token.name;
|
|
1881
|
+
}
|
|
1882
|
+
return token.shortName ?? token.name;
|
|
1883
|
+
}).join(this.IDENTIFIER_SEPARATOR);
|
|
1884
|
+
if (fullLengthName.length <= maxLength) {
|
|
1885
|
+
this.setUnshortenedName(fullLengthName, unshortenedName);
|
|
1886
|
+
return fullLengthName;
|
|
1887
|
+
}
|
|
1888
|
+
const [compressible, incompressible] = _.partition(
|
|
1889
|
+
(token) => token.compressible,
|
|
1890
|
+
nameTokens
|
|
1749
1891
|
);
|
|
1750
|
-
|
|
1751
|
-
|
|
1892
|
+
const totalIncompressibleLength = _.sumBy(
|
|
1893
|
+
(token) => token.compressible === false && token.shortName !== void 0 ? token.shortName.length : token.name.length
|
|
1894
|
+
)(incompressible);
|
|
1895
|
+
const totalSeparatorsLength = nameTokens.length * this.IDENTIFIER_SEPARATOR.length - 1;
|
|
1896
|
+
const available = maxLength - totalIncompressibleLength - totalSeparatorsLength;
|
|
1897
|
+
const availablePerToken = Math.floor(available / compressible.length);
|
|
1898
|
+
if (totalIncompressibleLength + totalSeparatorsLength > maxLength || availablePerToken < this.MIN_TOKEN_LENGTH) {
|
|
1899
|
+
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1900
|
+
}
|
|
1901
|
+
let surplus = available % compressible.length;
|
|
1902
|
+
const minHashedLength = this.HASH_LENGTH + this.HASH_SEPARATOR.length + this.MIN_TOKEN_LENGTH;
|
|
1903
|
+
const totalLength = nameTokens.reduce(
|
|
1904
|
+
(total, token) => {
|
|
1905
|
+
if (token.compressible) {
|
|
1906
|
+
if (token.name.length < availablePerToken) {
|
|
1907
|
+
return total + token.name.length;
|
|
1908
|
+
}
|
|
1909
|
+
return total + minHashedLength;
|
|
1910
|
+
}
|
|
1911
|
+
const tokenName = token.shortName ?? token.name;
|
|
1912
|
+
return total + tokenName.length;
|
|
1913
|
+
},
|
|
1914
|
+
nameTokens.length * this.IDENTIFIER_SEPARATOR.length - 1
|
|
1915
|
+
);
|
|
1916
|
+
if (maxLength < totalLength) {
|
|
1917
|
+
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1918
|
+
}
|
|
1919
|
+
let deficits = [];
|
|
1920
|
+
compressible.forEach((token) => {
|
|
1921
|
+
const actualLength = token.name.length;
|
|
1922
|
+
if (actualLength < availablePerToken) {
|
|
1923
|
+
surplus += availablePerToken - actualLength;
|
|
1924
|
+
token.allocatedLength = actualLength;
|
|
1925
|
+
} else {
|
|
1926
|
+
token.allocatedLength = availablePerToken;
|
|
1927
|
+
deficits.push(token);
|
|
1928
|
+
}
|
|
1929
|
+
});
|
|
1930
|
+
function filterAndIncreaseLength(token) {
|
|
1931
|
+
if (token.allocatedLength < token.name.length && surplus > 0) {
|
|
1932
|
+
token.allocatedLength += 1;
|
|
1933
|
+
surplus -= 1;
|
|
1934
|
+
return token.allocatedLength < token.name.length;
|
|
1935
|
+
}
|
|
1936
|
+
return false;
|
|
1937
|
+
}
|
|
1938
|
+
let previousSurplus = surplus + 1;
|
|
1939
|
+
while (surplus > 0 && deficits.length > 0) {
|
|
1940
|
+
deficits = deficits.filter((token) => filterAndIncreaseLength(token));
|
|
1941
|
+
if (surplus === previousSurplus) {
|
|
1942
|
+
break;
|
|
1943
|
+
}
|
|
1944
|
+
previousSurplus = surplus;
|
|
1945
|
+
}
|
|
1946
|
+
const shortenedName = nameTokens.map((token) => {
|
|
1947
|
+
if (token.compressible && "allocatedLength" in token && token.allocatedLength !== void 0) {
|
|
1948
|
+
return this.getShortenedName(token.name, token.allocatedLength);
|
|
1949
|
+
}
|
|
1950
|
+
if (token.compressible === false && token.shortName) {
|
|
1951
|
+
return token.shortName;
|
|
1952
|
+
}
|
|
1953
|
+
return token.name;
|
|
1954
|
+
}).join(this.IDENTIFIER_SEPARATOR);
|
|
1955
|
+
if (shortenedName.length > maxLength) {
|
|
1956
|
+
throw new Error(
|
|
1957
|
+
`name shortening failed to generate a name of the correct maxLength; name ${shortenedName}`
|
|
1958
|
+
);
|
|
1959
|
+
}
|
|
1960
|
+
this.setUnshortenedName(shortenedName, unshortenedName);
|
|
1961
|
+
return shortenedName;
|
|
1962
|
+
};
|
|
1963
|
+
// We need to be able to find the full-length name for any shortened name, primarily for migration purposes
|
|
1964
|
+
// Therefore we store every name that passes through so we can retrieve the original later
|
|
1965
|
+
nameMap = /* @__PURE__ */ new Map();
|
|
1966
|
+
getUnshortenedName = (shortName) => {
|
|
1967
|
+
return this.nameMap.get(this.serializeKey(shortName)) ?? shortName;
|
|
1968
|
+
};
|
|
1969
|
+
setUnshortenedName = (shortName, fullName) => {
|
|
1970
|
+
if (this.nameMap.get(this.serializeKey(shortName)) && shortName === fullName) {
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
this.nameMap.set(this.serializeKey(shortName), fullName);
|
|
1974
|
+
};
|
|
1975
|
+
serializeKey = (shortName) => {
|
|
1976
|
+
return `${shortName}.${this.options.maxLength}`;
|
|
1977
|
+
};
|
|
1752
1978
|
}
|
|
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" }));
|
|
1979
|
+
const identifiers = new Identifiers({ maxLength: IDENTIFIER_MAX_LENGTH });
|
|
1851
1980
|
const createColumn = (name, attribute) => {
|
|
1852
1981
|
const { type, args = [], ...opts } = getColumnType(attribute);
|
|
1853
1982
|
return {
|
|
1854
|
-
name,
|
|
1983
|
+
name: identifiers.getName(name),
|
|
1855
1984
|
type,
|
|
1856
1985
|
args,
|
|
1857
1986
|
defaultTo: null,
|
|
@@ -1873,8 +2002,8 @@ const createTable = (meta) => {
|
|
|
1873
2002
|
if (attribute.type === "relation") {
|
|
1874
2003
|
if ("morphColumn" in attribute && attribute.morphColumn && attribute.owner) {
|
|
1875
2004
|
const { idColumn, typeColumn } = attribute.morphColumn;
|
|
1876
|
-
const idColumnName = getName(idColumn.name);
|
|
1877
|
-
const typeColumnName = getName(typeColumn.name);
|
|
2005
|
+
const idColumnName = identifiers.getName(idColumn.name);
|
|
2006
|
+
const typeColumnName = identifiers.getName(typeColumn.name);
|
|
1878
2007
|
table.columns.push(
|
|
1879
2008
|
createColumn(idColumnName, {
|
|
1880
2009
|
type: "integer",
|
|
@@ -1891,7 +2020,7 @@ const createTable = (meta) => {
|
|
|
1891
2020
|
referencedTable,
|
|
1892
2021
|
columnType = "integer"
|
|
1893
2022
|
} = attribute.joinColumn;
|
|
1894
|
-
const columnName = getName(columnNameFull);
|
|
2023
|
+
const columnName = identifiers.getName(columnNameFull);
|
|
1895
2024
|
const column = createColumn(columnName, {
|
|
1896
2025
|
// TODO: find the column type automatically, or allow passing all the column params
|
|
1897
2026
|
type: columnType,
|
|
@@ -1900,7 +2029,7 @@ const createTable = (meta) => {
|
|
|
1900
2029
|
}
|
|
1901
2030
|
});
|
|
1902
2031
|
table.columns.push(column);
|
|
1903
|
-
const fkName = getFkIndexName([table.name, columnName]);
|
|
2032
|
+
const fkName = identifiers.getFkIndexName([table.name, columnName]);
|
|
1904
2033
|
table.foreignKeys.push({
|
|
1905
2034
|
name: fkName,
|
|
1906
2035
|
columns: [column.name],
|
|
@@ -1915,19 +2044,19 @@ const createTable = (meta) => {
|
|
|
1915
2044
|
});
|
|
1916
2045
|
}
|
|
1917
2046
|
} else if (isScalarAttribute(attribute)) {
|
|
1918
|
-
const columnName = getName(attribute.columnName || key);
|
|
2047
|
+
const columnName = identifiers.getName(attribute.columnName || key);
|
|
1919
2048
|
const column = createColumn(columnName, attribute);
|
|
1920
2049
|
if (column.unique) {
|
|
1921
2050
|
table.indexes.push({
|
|
1922
2051
|
type: "unique",
|
|
1923
|
-
name: getUniqueIndexName([table.name, column.name]),
|
|
2052
|
+
name: identifiers.getUniqueIndexName([table.name, column.name]),
|
|
1924
2053
|
columns: [columnName]
|
|
1925
2054
|
});
|
|
1926
2055
|
}
|
|
1927
2056
|
if (column.primary) {
|
|
1928
2057
|
table.indexes.push({
|
|
1929
2058
|
type: "primary",
|
|
1930
|
-
name: getPrimaryIndexName([table.name, column.name]),
|
|
2059
|
+
name: identifiers.getPrimaryIndexName([table.name, column.name]),
|
|
1931
2060
|
columns: [columnName]
|
|
1932
2061
|
});
|
|
1933
2062
|
}
|
|
@@ -2027,12 +2156,13 @@ const metadataToSchema = (metadata) => {
|
|
|
2027
2156
|
});
|
|
2028
2157
|
return schema;
|
|
2029
2158
|
};
|
|
2030
|
-
const debug = createDebug__default.default("strapi::database");
|
|
2159
|
+
const debug$1 = createDebug__default.default("strapi::database");
|
|
2031
2160
|
const createSchemaProvider = (db) => {
|
|
2032
2161
|
const state = {};
|
|
2033
2162
|
return {
|
|
2034
2163
|
get schema() {
|
|
2035
2164
|
if (!state.schema) {
|
|
2165
|
+
debug$1("Converting metadata to database schema");
|
|
2036
2166
|
state.schema = metadataToSchema(db.metadata);
|
|
2037
2167
|
}
|
|
2038
2168
|
return state.schema;
|
|
@@ -2044,7 +2174,7 @@ const createSchemaProvider = (db) => {
|
|
|
2044
2174
|
* Drops the database schema
|
|
2045
2175
|
*/
|
|
2046
2176
|
async drop() {
|
|
2047
|
-
debug("Dropping database schema");
|
|
2177
|
+
debug$1("Dropping database schema");
|
|
2048
2178
|
const DBSchema = await db.dialect.schemaInspector.getSchema();
|
|
2049
2179
|
await this.builder.dropSchema(DBSchema);
|
|
2050
2180
|
},
|
|
@@ -2052,19 +2182,19 @@ const createSchemaProvider = (db) => {
|
|
|
2052
2182
|
* Creates the database schema
|
|
2053
2183
|
*/
|
|
2054
2184
|
async create() {
|
|
2055
|
-
debug("Created database schema");
|
|
2185
|
+
debug$1("Created database schema");
|
|
2056
2186
|
await this.builder.createSchema(this.schema);
|
|
2057
2187
|
},
|
|
2058
2188
|
/**
|
|
2059
2189
|
* Resets the database schema
|
|
2060
2190
|
*/
|
|
2061
2191
|
async reset() {
|
|
2062
|
-
debug("Resetting database schema");
|
|
2192
|
+
debug$1("Resetting database schema");
|
|
2063
2193
|
await this.drop();
|
|
2064
2194
|
await this.create();
|
|
2065
2195
|
},
|
|
2066
2196
|
async syncSchema() {
|
|
2067
|
-
debug("Synchronizing database schema");
|
|
2197
|
+
debug$1("Synchronizing database schema");
|
|
2068
2198
|
const DBSchema = await db.dialect.schemaInspector.getSchema();
|
|
2069
2199
|
const { status, diff } = await this.schemaDiff.diff(DBSchema, this.schema);
|
|
2070
2200
|
if (status === "CHANGED") {
|
|
@@ -2077,28 +2207,28 @@ const createSchemaProvider = (db) => {
|
|
|
2077
2207
|
// TODO: Allow keeping extra indexes / extra tables / extra columns (globally or on a per table basis)
|
|
2078
2208
|
async sync() {
|
|
2079
2209
|
if (await db.migrations.shouldRun()) {
|
|
2080
|
-
debug("Found migrations to run");
|
|
2210
|
+
debug$1("Found migrations to run");
|
|
2081
2211
|
await db.migrations.up();
|
|
2082
2212
|
return this.syncSchema();
|
|
2083
2213
|
}
|
|
2084
2214
|
const oldSchema = await this.schemaStorage.read();
|
|
2085
2215
|
if (!oldSchema) {
|
|
2086
|
-
debug("Schema not persisted yet");
|
|
2216
|
+
debug$1("Schema not persisted yet");
|
|
2087
2217
|
return this.syncSchema();
|
|
2088
2218
|
}
|
|
2089
2219
|
const { hash: oldHash } = oldSchema;
|
|
2090
2220
|
const hash = await this.schemaStorage.hashSchema(this.schema);
|
|
2091
2221
|
if (oldHash !== hash) {
|
|
2092
|
-
debug("Schema changed");
|
|
2222
|
+
debug$1("Schema changed");
|
|
2093
2223
|
return this.syncSchema();
|
|
2094
2224
|
}
|
|
2095
|
-
debug("Schema unchanged");
|
|
2225
|
+
debug$1("Schema unchanged");
|
|
2096
2226
|
}
|
|
2097
2227
|
};
|
|
2098
2228
|
};
|
|
2099
|
-
const ID = ID_COLUMN;
|
|
2100
|
-
const ORDER = ORDER_COLUMN;
|
|
2101
|
-
const FIELD = FIELD_COLUMN;
|
|
2229
|
+
const ID = identifiers.ID_COLUMN;
|
|
2230
|
+
const ORDER = identifiers.ORDER_COLUMN;
|
|
2231
|
+
const FIELD = identifiers.FIELD_COLUMN;
|
|
2102
2232
|
const hasInversedBy = (attr) => "inversedBy" in attr;
|
|
2103
2233
|
const hasMappedBy = (attr) => "mappedBy" in attr;
|
|
2104
2234
|
const isOneToAny = (attribute) => ["oneToOne", "oneToMany"].includes(attribute.relation);
|
|
@@ -2119,7 +2249,7 @@ const createOneToOne = (attributeName, attribute, meta, metadata) => {
|
|
|
2119
2249
|
meta
|
|
2120
2250
|
});
|
|
2121
2251
|
} else {
|
|
2122
|
-
|
|
2252
|
+
createJoinColumn(metadata, {
|
|
2123
2253
|
attribute,
|
|
2124
2254
|
attributeName,
|
|
2125
2255
|
meta
|
|
@@ -2149,7 +2279,7 @@ const createManyToOne = (attributeName, attribute, meta, metadata) => {
|
|
|
2149
2279
|
meta
|
|
2150
2280
|
});
|
|
2151
2281
|
} else {
|
|
2152
|
-
|
|
2282
|
+
createJoinColumn(metadata, {
|
|
2153
2283
|
attribute,
|
|
2154
2284
|
attributeName,
|
|
2155
2285
|
meta
|
|
@@ -2166,15 +2296,11 @@ const createManyToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2166
2296
|
}
|
|
2167
2297
|
};
|
|
2168
2298
|
const createMorphToOne = (attributeName, attribute) => {
|
|
2169
|
-
const idColumnName = getJoinColumnAttributeIdName("target");
|
|
2170
|
-
const typeColumnName = getMorphColumnTypeName("target");
|
|
2171
|
-
if ("morphColumn" in attribute && attribute.morphColumn) {
|
|
2172
|
-
return;
|
|
2173
|
-
}
|
|
2299
|
+
const idColumnName = identifiers.getJoinColumnAttributeIdName("target");
|
|
2300
|
+
const typeColumnName = identifiers.getMorphColumnTypeName("target");
|
|
2174
2301
|
Object.assign(attribute, {
|
|
2175
2302
|
owner: true,
|
|
2176
|
-
morphColumn: {
|
|
2177
|
-
// TODO: add referenced column
|
|
2303
|
+
morphColumn: attribute.morphColumn ?? {
|
|
2178
2304
|
typeColumn: {
|
|
2179
2305
|
name: typeColumnName
|
|
2180
2306
|
},
|
|
@@ -2189,11 +2315,11 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2189
2315
|
if ("joinTable" in attribute && attribute.joinTable) {
|
|
2190
2316
|
return;
|
|
2191
2317
|
}
|
|
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);
|
|
2318
|
+
const joinTableName = identifiers.getMorphTableName(meta.tableName, attributeName);
|
|
2319
|
+
const joinColumnName = identifiers.getMorphColumnJoinTableIdName(_.snakeCase(meta.singularName));
|
|
2320
|
+
const idColumnName = identifiers.getMorphColumnAttributeIdName(attributeName);
|
|
2321
|
+
const typeColumnName = identifiers.getMorphColumnTypeName(attributeName);
|
|
2322
|
+
const fkIndexName = identifiers.getFkIndexName(joinTableName);
|
|
2197
2323
|
metadata.add({
|
|
2198
2324
|
singularName: joinTableName,
|
|
2199
2325
|
uid: joinTableName,
|
|
@@ -2206,7 +2332,9 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2206
2332
|
type: "integer",
|
|
2207
2333
|
column: {
|
|
2208
2334
|
unsigned: true
|
|
2209
|
-
}
|
|
2335
|
+
},
|
|
2336
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2337
|
+
columnName: joinColumnName
|
|
2210
2338
|
},
|
|
2211
2339
|
[idColumnName]: {
|
|
2212
2340
|
type: "integer",
|
|
@@ -2233,11 +2361,11 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2233
2361
|
columns: [joinColumnName]
|
|
2234
2362
|
},
|
|
2235
2363
|
{
|
|
2236
|
-
name:
|
|
2364
|
+
name: identifiers.getOrderIndexName(joinTableName),
|
|
2237
2365
|
columns: [ORDER]
|
|
2238
2366
|
},
|
|
2239
2367
|
{
|
|
2240
|
-
name:
|
|
2368
|
+
name: identifiers.getIdColumnIndexName(joinTableName),
|
|
2241
2369
|
columns: [idColumnName]
|
|
2242
2370
|
}
|
|
2243
2371
|
],
|
|
@@ -2246,7 +2374,7 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2246
2374
|
name: fkIndexName,
|
|
2247
2375
|
columns: [joinColumnName],
|
|
2248
2376
|
referencedColumns: [ID],
|
|
2249
|
-
referencedTable:
|
|
2377
|
+
referencedTable: meta.tableName,
|
|
2250
2378
|
onDelete: "CASCADE"
|
|
2251
2379
|
}
|
|
2252
2380
|
],
|
|
@@ -2293,12 +2421,12 @@ const createMorphMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2293
2421
|
throw new Error(`Morph target attribute not found. Looking for ${attribute.morphBy}`);
|
|
2294
2422
|
}
|
|
2295
2423
|
};
|
|
2296
|
-
const
|
|
2424
|
+
const createJoinColumn = (metadata, { attribute, attributeName }) => {
|
|
2297
2425
|
const targetMeta = metadata.get(attribute.target);
|
|
2298
2426
|
if (!targetMeta) {
|
|
2299
2427
|
throw new Error(`Unknown target ${attribute.target}`);
|
|
2300
2428
|
}
|
|
2301
|
-
const joinColumnName =
|
|
2429
|
+
const joinColumnName = identifiers.getJoinColumnAttributeIdName(_.snakeCase(attributeName));
|
|
2302
2430
|
const joinColumn = {
|
|
2303
2431
|
name: joinColumnName,
|
|
2304
2432
|
referencedColumn: ID,
|
|
@@ -2313,7 +2441,7 @@ const createJoinColum = (metadata, { attribute, attributeName }) => {
|
|
|
2313
2441
|
Object.assign(inverseAttribute, {
|
|
2314
2442
|
joinColumn: {
|
|
2315
2443
|
name: joinColumn.referencedColumn,
|
|
2316
|
-
referencedColumn:
|
|
2444
|
+
referencedColumn: joinColumnName
|
|
2317
2445
|
}
|
|
2318
2446
|
});
|
|
2319
2447
|
}
|
|
@@ -2326,21 +2454,26 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2326
2454
|
if ("joinTable" in attribute && attribute.joinTable) {
|
|
2327
2455
|
return;
|
|
2328
2456
|
}
|
|
2329
|
-
const joinTableName = getJoinTableName(
|
|
2330
|
-
|
|
2331
|
-
|
|
2457
|
+
const joinTableName = identifiers.getJoinTableName(
|
|
2458
|
+
_.snakeCase(meta.tableName),
|
|
2459
|
+
_.snakeCase(attributeName)
|
|
2460
|
+
);
|
|
2461
|
+
const joinColumnName = identifiers.getJoinColumnAttributeIdName(_.snakeCase(meta.singularName));
|
|
2462
|
+
let inverseJoinColumnName = identifiers.getJoinColumnAttributeIdName(
|
|
2463
|
+
_.snakeCase(targetMeta.singularName)
|
|
2464
|
+
);
|
|
2332
2465
|
if (joinColumnName === inverseJoinColumnName) {
|
|
2333
|
-
inverseJoinColumnName = getInverseJoinColumnAttributeIdName(
|
|
2334
|
-
targetMeta.singularName
|
|
2466
|
+
inverseJoinColumnName = identifiers.getInverseJoinColumnAttributeIdName(
|
|
2467
|
+
_.snakeCase(targetMeta.singularName)
|
|
2335
2468
|
);
|
|
2336
2469
|
}
|
|
2337
|
-
const orderColumnName = getOrderColumnName(targetMeta.singularName);
|
|
2338
|
-
let inverseOrderColumnName = getOrderColumnName(meta.singularName);
|
|
2470
|
+
const orderColumnName = identifiers.getOrderColumnName(_.snakeCase(targetMeta.singularName));
|
|
2471
|
+
let inverseOrderColumnName = identifiers.getOrderColumnName(_.snakeCase(meta.singularName));
|
|
2339
2472
|
if (attribute.relation === "manyToMany" && orderColumnName === inverseOrderColumnName) {
|
|
2340
|
-
inverseOrderColumnName = getInverseOrderColumnName(meta.singularName);
|
|
2473
|
+
inverseOrderColumnName = identifiers.getInverseOrderColumnName(_.snakeCase(meta.singularName));
|
|
2341
2474
|
}
|
|
2342
|
-
const fkIndexName = getFkIndexName(joinTableName);
|
|
2343
|
-
const invFkIndexName = getInverseFkIndexName(joinTableName);
|
|
2475
|
+
const fkIndexName = identifiers.getFkIndexName(joinTableName);
|
|
2476
|
+
const invFkIndexName = identifiers.getInverseFkIndexName(joinTableName);
|
|
2344
2477
|
const metadataSchema = {
|
|
2345
2478
|
singularName: joinTableName,
|
|
2346
2479
|
uid: joinTableName,
|
|
@@ -2353,13 +2486,17 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2353
2486
|
type: "integer",
|
|
2354
2487
|
column: {
|
|
2355
2488
|
unsigned: true
|
|
2356
|
-
}
|
|
2489
|
+
},
|
|
2490
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2491
|
+
columnName: joinColumnName
|
|
2357
2492
|
},
|
|
2358
2493
|
[inverseJoinColumnName]: {
|
|
2359
2494
|
type: "integer",
|
|
2360
2495
|
column: {
|
|
2361
2496
|
unsigned: true
|
|
2362
|
-
}
|
|
2497
|
+
},
|
|
2498
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2499
|
+
columnName: inverseJoinColumnName
|
|
2363
2500
|
}
|
|
2364
2501
|
// TODO: add extra pivot attributes -> user should use an intermediate entity
|
|
2365
2502
|
},
|
|
@@ -2373,7 +2510,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2373
2510
|
columns: [inverseJoinColumnName]
|
|
2374
2511
|
},
|
|
2375
2512
|
{
|
|
2376
|
-
name: getUniqueIndexName(joinTableName),
|
|
2513
|
+
name: identifiers.getUniqueIndexName(joinTableName),
|
|
2377
2514
|
columns: [joinColumnName, inverseJoinColumnName],
|
|
2378
2515
|
type: "unique"
|
|
2379
2516
|
}
|
|
@@ -2384,7 +2521,6 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2384
2521
|
columns: [joinColumnName],
|
|
2385
2522
|
referencedColumns: [ID],
|
|
2386
2523
|
referencedTable: meta.tableName,
|
|
2387
|
-
// TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
|
|
2388
2524
|
onDelete: "CASCADE"
|
|
2389
2525
|
},
|
|
2390
2526
|
{
|
|
@@ -2392,7 +2528,6 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2392
2528
|
columns: [inverseJoinColumnName],
|
|
2393
2529
|
referencedColumns: [ID],
|
|
2394
2530
|
referencedTable: targetMeta.tableName,
|
|
2395
|
-
// TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
|
|
2396
2531
|
onDelete: "CASCADE"
|
|
2397
2532
|
}
|
|
2398
2533
|
],
|
|
@@ -2422,8 +2557,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2422
2557
|
}
|
|
2423
2558
|
};
|
|
2424
2559
|
metadataSchema.indexes.push({
|
|
2425
|
-
name: getOrderFkIndexName(joinTableName),
|
|
2426
|
-
// TODO: should we send joinTableName as parts?
|
|
2560
|
+
name: identifiers.getOrderFkIndexName(joinTableName),
|
|
2427
2561
|
columns: [orderColumnName]
|
|
2428
2562
|
});
|
|
2429
2563
|
joinTable.orderColumnName = orderColumnName;
|
|
@@ -2438,7 +2572,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2438
2572
|
}
|
|
2439
2573
|
};
|
|
2440
2574
|
metadataSchema.indexes.push({
|
|
2441
|
-
name: getOrderInverseFkIndexName(joinTableName),
|
|
2575
|
+
name: identifiers.getOrderInverseFkIndexName(joinTableName),
|
|
2442
2576
|
columns: [inverseOrderColumnName]
|
|
2443
2577
|
});
|
|
2444
2578
|
joinTable.inverseOrderColumnName = inverseOrderColumnName;
|
|
@@ -2496,6 +2630,12 @@ const createRelation = (attributeName, attribute, meta, metadata) => {
|
|
|
2496
2630
|
}
|
|
2497
2631
|
};
|
|
2498
2632
|
class Metadata extends Map {
|
|
2633
|
+
// TODO: we expose the global identifiers in this way so that in the future we can instantiate our own
|
|
2634
|
+
// However, it should NOT be done until all the methods used by metadata can be part of this metadata object
|
|
2635
|
+
// and access this one; currently they all access the global identifiers directly.
|
|
2636
|
+
get identifiers() {
|
|
2637
|
+
return identifiers;
|
|
2638
|
+
}
|
|
2499
2639
|
get(key) {
|
|
2500
2640
|
if (!super.has(key)) {
|
|
2501
2641
|
throw new Error(`Metadata for "${key}" not found`);
|
|
@@ -2519,10 +2659,12 @@ class Metadata extends Map {
|
|
|
2519
2659
|
seenTables.set(meta.tableName, true);
|
|
2520
2660
|
}
|
|
2521
2661
|
}
|
|
2522
|
-
loadModels(models
|
|
2523
|
-
for (const model of
|
|
2662
|
+
loadModels(models) {
|
|
2663
|
+
for (const model of _.cloneDeep(models ?? [])) {
|
|
2664
|
+
const tableName = identifiers.getTableName(model.tableName);
|
|
2524
2665
|
this.add({
|
|
2525
2666
|
...model,
|
|
2667
|
+
tableName,
|
|
2526
2668
|
attributes: {
|
|
2527
2669
|
...model.attributes
|
|
2528
2670
|
},
|
|
@@ -2563,10 +2705,13 @@ class Metadata extends Map {
|
|
|
2563
2705
|
}
|
|
2564
2706
|
}
|
|
2565
2707
|
const createAttribute = (attributeName, attribute) => {
|
|
2566
|
-
|
|
2708
|
+
if ("columnName" in attribute && attribute.columnName) {
|
|
2709
|
+
return;
|
|
2710
|
+
}
|
|
2711
|
+
const columnName = identifiers.getColumnName(_.snakeCase(attributeName));
|
|
2567
2712
|
Object.assign(attribute, { columnName });
|
|
2568
2713
|
};
|
|
2569
|
-
const createMetadata = (models
|
|
2714
|
+
const createMetadata = (models) => {
|
|
2570
2715
|
const metadata = new Metadata();
|
|
2571
2716
|
if (models.length) {
|
|
2572
2717
|
metadata.loadModels(models);
|
|
@@ -2917,11 +3062,54 @@ const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
|
|
|
2917
3062
|
return subAlias;
|
|
2918
3063
|
};
|
|
2919
3064
|
const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
|
|
2920
|
-
const { db, qb } = ctx;
|
|
3065
|
+
const { db, qb, uid } = ctx;
|
|
2921
3066
|
if (attribute.type !== "relation") {
|
|
2922
3067
|
throw new Error(`Cannot join on non relational field ${attributeName}`);
|
|
2923
3068
|
}
|
|
2924
3069
|
const targetMeta = db.metadata.get(attribute.target);
|
|
3070
|
+
if (["morphOne", "morphMany"].includes(attribute.relation)) {
|
|
3071
|
+
const targetAttribute = targetMeta.attributes[attribute.morphBy];
|
|
3072
|
+
const { joinTable: joinTable2, morphColumn } = targetAttribute;
|
|
3073
|
+
if (morphColumn) {
|
|
3074
|
+
const subAlias = refAlias || qb.getAlias();
|
|
3075
|
+
qb.join({
|
|
3076
|
+
alias: subAlias,
|
|
3077
|
+
referencedTable: targetMeta.tableName,
|
|
3078
|
+
referencedColumn: morphColumn.idColumn.name,
|
|
3079
|
+
rootColumn: morphColumn.idColumn.referencedColumn,
|
|
3080
|
+
rootTable: alias,
|
|
3081
|
+
on: {
|
|
3082
|
+
[morphColumn.typeColumn.name]: uid,
|
|
3083
|
+
...morphColumn.on
|
|
3084
|
+
}
|
|
3085
|
+
});
|
|
3086
|
+
return subAlias;
|
|
3087
|
+
}
|
|
3088
|
+
if (joinTable2) {
|
|
3089
|
+
const joinAlias = qb.getAlias();
|
|
3090
|
+
qb.join({
|
|
3091
|
+
alias: joinAlias,
|
|
3092
|
+
referencedTable: joinTable2.name,
|
|
3093
|
+
referencedColumn: joinTable2.morphColumn.idColumn.name,
|
|
3094
|
+
rootColumn: joinTable2.morphColumn.idColumn.referencedColumn,
|
|
3095
|
+
rootTable: alias,
|
|
3096
|
+
on: {
|
|
3097
|
+
[joinTable2.morphColumn.typeColumn.name]: uid,
|
|
3098
|
+
field: attributeName
|
|
3099
|
+
}
|
|
3100
|
+
});
|
|
3101
|
+
const subAlias = refAlias || qb.getAlias();
|
|
3102
|
+
qb.join({
|
|
3103
|
+
alias: subAlias,
|
|
3104
|
+
referencedTable: targetMeta.tableName,
|
|
3105
|
+
referencedColumn: joinTable2.joinColumn.referencedColumn,
|
|
3106
|
+
rootColumn: joinTable2.joinColumn.name,
|
|
3107
|
+
rootTable: joinAlias
|
|
3108
|
+
});
|
|
3109
|
+
return subAlias;
|
|
3110
|
+
}
|
|
3111
|
+
return alias;
|
|
3112
|
+
}
|
|
2925
3113
|
const { joinColumn } = attribute;
|
|
2926
3114
|
if (joinColumn) {
|
|
2927
3115
|
const subAlias = refAlias || qb.getAlias();
|
|
@@ -3061,10 +3249,13 @@ const XtoOne = async (input, ctx) => {
|
|
|
3061
3249
|
rootTable: qb2.alias,
|
|
3062
3250
|
on: joinTable.on
|
|
3063
3251
|
}).select([joinColAlias, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3064
|
-
const map2 = rows2.reduce(
|
|
3065
|
-
map3
|
|
3066
|
-
|
|
3067
|
-
|
|
3252
|
+
const map2 = rows2.reduce(
|
|
3253
|
+
(map3, row) => {
|
|
3254
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3255
|
+
return map3;
|
|
3256
|
+
},
|
|
3257
|
+
{}
|
|
3258
|
+
);
|
|
3068
3259
|
results.forEach((result) => {
|
|
3069
3260
|
result[attributeName] = map2[result[referencedColumnName]] || { count: 0 };
|
|
3070
3261
|
});
|
|
@@ -3137,10 +3328,13 @@ const oneToMany = async (input, ctx) => {
|
|
|
3137
3328
|
rootTable: qb2.alias,
|
|
3138
3329
|
on: joinTable.on
|
|
3139
3330
|
}).select([joinColAlias, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3140
|
-
const map2 = rows2.reduce(
|
|
3141
|
-
map3
|
|
3142
|
-
|
|
3143
|
-
|
|
3331
|
+
const map2 = rows2.reduce(
|
|
3332
|
+
(map3, row) => {
|
|
3333
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3334
|
+
return map3;
|
|
3335
|
+
},
|
|
3336
|
+
{}
|
|
3337
|
+
);
|
|
3144
3338
|
results.forEach((result) => {
|
|
3145
3339
|
result[attributeName] = map2[result[referencedColumnName]] || { count: 0 };
|
|
3146
3340
|
});
|
|
@@ -3194,10 +3388,13 @@ const manyToMany = async (input, ctx) => {
|
|
|
3194
3388
|
rootTable: populateQb.alias,
|
|
3195
3389
|
on: joinTable.on
|
|
3196
3390
|
}).select([joinColAlias, populateQb.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3197
|
-
const map2 = rows2.reduce(
|
|
3198
|
-
map3
|
|
3199
|
-
|
|
3200
|
-
|
|
3391
|
+
const map2 = rows2.reduce(
|
|
3392
|
+
(map3, row) => {
|
|
3393
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3394
|
+
return map3;
|
|
3395
|
+
},
|
|
3396
|
+
{}
|
|
3397
|
+
);
|
|
3201
3398
|
results.forEach((result) => {
|
|
3202
3399
|
result[attributeName] = map2[result[referencedColumnName]] || { count: 0 };
|
|
3203
3400
|
});
|
|
@@ -3544,7 +3741,7 @@ const castValue = (value, attribute) => {
|
|
|
3544
3741
|
};
|
|
3545
3742
|
const processSingleAttributeWhere = (attribute, where, operator = "$eq") => {
|
|
3546
3743
|
if (!isRecord$1(where)) {
|
|
3547
|
-
if (utils
|
|
3744
|
+
if (utils.isOperatorOfType("cast", operator)) {
|
|
3548
3745
|
return castValue(where, attribute);
|
|
3549
3746
|
}
|
|
3550
3747
|
return where;
|
|
@@ -3552,7 +3749,7 @@ const processSingleAttributeWhere = (attribute, where, operator = "$eq") => {
|
|
|
3552
3749
|
const filters = {};
|
|
3553
3750
|
for (const key of Object.keys(where)) {
|
|
3554
3751
|
const value = where[key];
|
|
3555
|
-
if (!utils
|
|
3752
|
+
if (!utils.isOperatorOfType("where", key)) {
|
|
3556
3753
|
throw new Error(`Undefined attribute level operator ${key}`);
|
|
3557
3754
|
}
|
|
3558
3755
|
filters[key] = processAttributeWhere(attribute, value, key);
|
|
@@ -3571,6 +3768,28 @@ const processNested = (where, ctx) => {
|
|
|
3571
3768
|
}
|
|
3572
3769
|
return processWhere(where, ctx);
|
|
3573
3770
|
};
|
|
3771
|
+
const processRelationWhere = (where, ctx) => {
|
|
3772
|
+
const { qb, alias } = ctx;
|
|
3773
|
+
const idAlias = qb.aliasColumn("id", alias);
|
|
3774
|
+
if (!isRecord$1(where)) {
|
|
3775
|
+
return { [idAlias]: where };
|
|
3776
|
+
}
|
|
3777
|
+
const keys = Object.keys(where);
|
|
3778
|
+
const operatorKeys = keys.filter((key) => utils.isOperator(key));
|
|
3779
|
+
if (operatorKeys.length > 0 && operatorKeys.length !== keys.length) {
|
|
3780
|
+
throw new Error(`Operator and non-operator keys cannot be mixed in a relation where clause`);
|
|
3781
|
+
}
|
|
3782
|
+
if (operatorKeys.length > 1) {
|
|
3783
|
+
throw new Error(
|
|
3784
|
+
`Only one operator key is allowed in a relation where clause, but found: ${operatorKeys}`
|
|
3785
|
+
);
|
|
3786
|
+
}
|
|
3787
|
+
if (operatorKeys.length === 1) {
|
|
3788
|
+
const operator = operatorKeys[0];
|
|
3789
|
+
return { [idAlias]: { [operator]: processNested(where[operator], ctx) } };
|
|
3790
|
+
}
|
|
3791
|
+
return processWhere(where, ctx);
|
|
3792
|
+
};
|
|
3574
3793
|
function processWhere(where, ctx) {
|
|
3575
3794
|
if (!_.isArray(where) && !isRecord$1(where)) {
|
|
3576
3795
|
throw new Error("Where must be an array or an object");
|
|
@@ -3583,7 +3802,10 @@ function processWhere(where, ctx) {
|
|
|
3583
3802
|
const filters = {};
|
|
3584
3803
|
for (const key of Object.keys(where)) {
|
|
3585
3804
|
const value = where[key];
|
|
3586
|
-
if (utils
|
|
3805
|
+
if (utils.isOperatorOfType("group", key)) {
|
|
3806
|
+
if (!Array.isArray(value)) {
|
|
3807
|
+
throw new Error(`Operator ${key} must be an array`);
|
|
3808
|
+
}
|
|
3587
3809
|
filters[key] = value.map((sub) => processNested(sub, ctx));
|
|
3588
3810
|
continue;
|
|
3589
3811
|
}
|
|
@@ -3591,7 +3813,7 @@ function processWhere(where, ctx) {
|
|
|
3591
3813
|
filters[key] = processNested(value, ctx);
|
|
3592
3814
|
continue;
|
|
3593
3815
|
}
|
|
3594
|
-
if (utils
|
|
3816
|
+
if (utils.isOperatorOfType("where", key)) {
|
|
3595
3817
|
throw new Error(
|
|
3596
3818
|
`Only $and, $or and $not can only be used as root level operators. Found ${key}.`
|
|
3597
3819
|
);
|
|
@@ -3607,15 +3829,12 @@ function processWhere(where, ctx) {
|
|
|
3607
3829
|
attributeName: key,
|
|
3608
3830
|
attribute
|
|
3609
3831
|
});
|
|
3610
|
-
|
|
3832
|
+
const nestedWhere = processRelationWhere(value, {
|
|
3611
3833
|
db,
|
|
3612
3834
|
qb,
|
|
3613
3835
|
alias: subAlias,
|
|
3614
3836
|
uid: attribute.target
|
|
3615
3837
|
});
|
|
3616
|
-
if (!isRecord$1(nestedWhere) || utils$1.isOperatorOfType("where", _.keys(nestedWhere)[0])) {
|
|
3617
|
-
nestedWhere = { [qb.aliasColumn("id", subAlias)]: nestedWhere };
|
|
3618
|
-
}
|
|
3619
3838
|
Object.assign(filters, nestedWhere);
|
|
3620
3839
|
continue;
|
|
3621
3840
|
}
|
|
@@ -3630,7 +3849,7 @@ function processWhere(where, ctx) {
|
|
|
3630
3849
|
return filters;
|
|
3631
3850
|
}
|
|
3632
3851
|
const applyOperator = (qb, column, operator, value) => {
|
|
3633
|
-
if (Array.isArray(value) && !utils
|
|
3852
|
+
if (Array.isArray(value) && !utils.isOperatorOfType("array", operator)) {
|
|
3634
3853
|
return qb.where((subQB) => {
|
|
3635
3854
|
value.forEach(
|
|
3636
3855
|
(subValue) => subQB.orWhere((innerQB) => {
|
|
@@ -3768,8 +3987,8 @@ const applyWhereToColumn = (qb, column, columnWhere) => {
|
|
|
3768
3987
|
}
|
|
3769
3988
|
return qb.where(column, columnWhere);
|
|
3770
3989
|
}
|
|
3771
|
-
const
|
|
3772
|
-
|
|
3990
|
+
const keys = Object.keys(columnWhere);
|
|
3991
|
+
keys.forEach((operator) => {
|
|
3773
3992
|
const value = columnWhere[operator];
|
|
3774
3993
|
applyOperator(qb, column, operator, value);
|
|
3775
3994
|
});
|
|
@@ -4708,21 +4927,24 @@ const sortConnectArray = (connectArr, initialArr = [], strictSort = true) => {
|
|
|
4708
4927
|
(acc, rel) => ({ ...acc, [rel.id]: true }),
|
|
4709
4928
|
{}
|
|
4710
4929
|
);
|
|
4711
|
-
const mappedRelations = connectArr.reduce(
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4930
|
+
const mappedRelations = connectArr.reduce(
|
|
4931
|
+
(mapper, relation) => {
|
|
4932
|
+
const adjacentRelId = relation.position?.before || relation.position?.after;
|
|
4933
|
+
if (!adjacentRelId || !relationInInitialArray[adjacentRelId] && !mapper[adjacentRelId]) {
|
|
4934
|
+
needsSorting = true;
|
|
4935
|
+
}
|
|
4936
|
+
if (mapper[relation.id]) {
|
|
4937
|
+
throw new InvalidRelationError(
|
|
4938
|
+
`The relation with id ${relation.id} is already connected. You cannot connect the same relation twice.`
|
|
4939
|
+
);
|
|
4940
|
+
}
|
|
4941
|
+
return {
|
|
4942
|
+
[relation.id]: { ...relation, computed: false },
|
|
4943
|
+
...mapper
|
|
4944
|
+
};
|
|
4945
|
+
},
|
|
4946
|
+
{}
|
|
4947
|
+
);
|
|
4726
4948
|
if (!needsSorting)
|
|
4727
4949
|
return connectArr;
|
|
4728
4950
|
const computeRelation = (relation, relationsSeenInBranch) => {
|
|
@@ -4830,14 +5052,17 @@ const relationsOrderer = (initArr, idColumn, orderColumn, strict) => {
|
|
|
4830
5052
|
* Get a map between the relation id and its order
|
|
4831
5053
|
*/
|
|
4832
5054
|
getOrderMap() {
|
|
4833
|
-
return ___default$1.default(computedRelations).groupBy("order").reduce(
|
|
4834
|
-
|
|
5055
|
+
return ___default$1.default(computedRelations).groupBy("order").reduce(
|
|
5056
|
+
(acc, relations) => {
|
|
5057
|
+
if (relations[0]?.init)
|
|
5058
|
+
return acc;
|
|
5059
|
+
relations.forEach((relation, idx) => {
|
|
5060
|
+
acc[relation.id] = Math.floor(relation.order) + (idx + 1) / (relations.length + 1);
|
|
5061
|
+
});
|
|
4835
5062
|
return acc;
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
return acc;
|
|
4840
|
-
}, {});
|
|
5063
|
+
},
|
|
5064
|
+
{}
|
|
5065
|
+
);
|
|
4841
5066
|
}
|
|
4842
5067
|
};
|
|
4843
5068
|
};
|
|
@@ -5591,10 +5816,13 @@ const createEntityManager = (db) => {
|
|
|
5591
5816
|
const entry = await this.findOne(uid, {
|
|
5592
5817
|
select: ["id"],
|
|
5593
5818
|
where: { id: entity.id },
|
|
5594
|
-
populate: fieldsArr.reduce(
|
|
5595
|
-
acc
|
|
5596
|
-
|
|
5597
|
-
|
|
5819
|
+
populate: fieldsArr.reduce(
|
|
5820
|
+
(acc, field) => {
|
|
5821
|
+
acc[field] = populate || true;
|
|
5822
|
+
return acc;
|
|
5823
|
+
},
|
|
5824
|
+
{}
|
|
5825
|
+
)
|
|
5598
5826
|
});
|
|
5599
5827
|
if (!entry) {
|
|
5600
5828
|
return null;
|
|
@@ -5710,7 +5938,280 @@ const createUserMigrationProvider = (db) => {
|
|
|
5710
5938
|
}
|
|
5711
5939
|
};
|
|
5712
5940
|
};
|
|
5713
|
-
const
|
|
5941
|
+
const QUERIES = {
|
|
5942
|
+
async postgres(knex2, params) {
|
|
5943
|
+
const res = await knex2.raw(
|
|
5944
|
+
`
|
|
5945
|
+
SELECT :tableName:.id as id, string_agg(DISTINCT :inverseJoinColumn:::character varying, ',') as other_ids
|
|
5946
|
+
FROM :tableName:
|
|
5947
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5948
|
+
WHERE document_id IS NULL
|
|
5949
|
+
GROUP BY :tableName:.id, :joinColumn:
|
|
5950
|
+
LIMIT 1;
|
|
5951
|
+
`,
|
|
5952
|
+
params
|
|
5953
|
+
);
|
|
5954
|
+
return res.rows;
|
|
5955
|
+
},
|
|
5956
|
+
async mysql(knex2, params) {
|
|
5957
|
+
const [res] = await knex2.raw(
|
|
5958
|
+
`
|
|
5959
|
+
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids
|
|
5960
|
+
FROM :tableName:
|
|
5961
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5962
|
+
WHERE document_id IS NULL
|
|
5963
|
+
GROUP BY :tableName:.id, :joinColumn:
|
|
5964
|
+
LIMIT 1;
|
|
5965
|
+
`,
|
|
5966
|
+
params
|
|
5967
|
+
);
|
|
5968
|
+
return res;
|
|
5969
|
+
},
|
|
5970
|
+
async sqlite(knex2, params) {
|
|
5971
|
+
return knex2.raw(
|
|
5972
|
+
`
|
|
5973
|
+
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids
|
|
5974
|
+
FROM :tableName:
|
|
5975
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5976
|
+
WHERE document_id IS NULL
|
|
5977
|
+
GROUP BY :joinColumn:
|
|
5978
|
+
LIMIT 1;
|
|
5979
|
+
`,
|
|
5980
|
+
params
|
|
5981
|
+
);
|
|
5982
|
+
}
|
|
5983
|
+
};
|
|
5984
|
+
const getNextIdsToCreateDocumentId = async (db, knex2, {
|
|
5985
|
+
joinColumn,
|
|
5986
|
+
inverseJoinColumn,
|
|
5987
|
+
tableName,
|
|
5988
|
+
joinTableName
|
|
5989
|
+
}) => {
|
|
5990
|
+
const res = await QUERIES[db.dialect.client](knex2, {
|
|
5991
|
+
joinColumn,
|
|
5992
|
+
inverseJoinColumn,
|
|
5993
|
+
tableName,
|
|
5994
|
+
joinTableName
|
|
5995
|
+
});
|
|
5996
|
+
if (res.length > 0) {
|
|
5997
|
+
const row = res[0];
|
|
5998
|
+
const otherIds = row.other_ids ? row.other_ids.split(",").map((v) => parseInt(v, 10)) : [];
|
|
5999
|
+
return [row.id, ...otherIds];
|
|
6000
|
+
}
|
|
6001
|
+
return [];
|
|
6002
|
+
};
|
|
6003
|
+
const migrateDocumentIdsWithLocalizations = async (db, knex2, meta) => {
|
|
6004
|
+
const singularName = meta.singularName.toLowerCase();
|
|
6005
|
+
const joinColumn = identifiers.getJoinColumnAttributeIdName(singularName);
|
|
6006
|
+
const inverseJoinColumn = identifiers.getInverseJoinColumnAttributeIdName(singularName);
|
|
6007
|
+
let ids;
|
|
6008
|
+
do {
|
|
6009
|
+
ids = await getNextIdsToCreateDocumentId(db, knex2, {
|
|
6010
|
+
joinColumn,
|
|
6011
|
+
inverseJoinColumn,
|
|
6012
|
+
tableName: meta.tableName,
|
|
6013
|
+
joinTableName: identifiers.getJoinTableName(meta.tableName, `localizations`)
|
|
6014
|
+
});
|
|
6015
|
+
if (ids.length > 0) {
|
|
6016
|
+
await knex2(meta.tableName).update({ document_id: cuid2.createId() }).whereIn("id", ids);
|
|
6017
|
+
}
|
|
6018
|
+
} while (ids.length > 0);
|
|
6019
|
+
};
|
|
6020
|
+
const migrationDocumentIds = async (db, knex2, meta) => {
|
|
6021
|
+
let run = true;
|
|
6022
|
+
do {
|
|
6023
|
+
const updatedRows = await knex2(meta.tableName).update({ document_id: cuid2.createId() }).whereIn("id", (builder) => {
|
|
6024
|
+
return builder.whereNull("document_id").select("id").limit(1);
|
|
6025
|
+
});
|
|
6026
|
+
if (updatedRows <= 0) {
|
|
6027
|
+
run = false;
|
|
6028
|
+
}
|
|
6029
|
+
} while (run);
|
|
6030
|
+
};
|
|
6031
|
+
const createDocumentIdColumn = async (knex2, tableName) => {
|
|
6032
|
+
await knex2.schema.alterTable(tableName, (table) => {
|
|
6033
|
+
table.string("document_id");
|
|
6034
|
+
});
|
|
6035
|
+
};
|
|
6036
|
+
const hasLocalizationsJoinTable = async (knex2, tableName) => {
|
|
6037
|
+
const joinTableName = identifiers.getJoinTableName(tableName, "localizations");
|
|
6038
|
+
return knex2.schema.hasTable(joinTableName);
|
|
6039
|
+
};
|
|
6040
|
+
const createdDocumentId = {
|
|
6041
|
+
name: "5.0.0-02-created-document-id",
|
|
6042
|
+
async up(knex2, db) {
|
|
6043
|
+
for (const meta of db.metadata.values()) {
|
|
6044
|
+
const hasTable = await knex2.schema.hasTable(meta.tableName);
|
|
6045
|
+
if (!hasTable) {
|
|
6046
|
+
continue;
|
|
6047
|
+
}
|
|
6048
|
+
if ("documentId" in meta.attributes) {
|
|
6049
|
+
const hasDocumentIdColumn = await knex2.schema.hasColumn(meta.tableName, "document_id");
|
|
6050
|
+
if (hasDocumentIdColumn) {
|
|
6051
|
+
continue;
|
|
6052
|
+
}
|
|
6053
|
+
await createDocumentIdColumn(knex2, meta.tableName);
|
|
6054
|
+
if (await hasLocalizationsJoinTable(knex2, meta.tableName)) {
|
|
6055
|
+
await migrateDocumentIdsWithLocalizations(db, knex2, meta);
|
|
6056
|
+
} else {
|
|
6057
|
+
await migrationDocumentIds(db, knex2, meta);
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
},
|
|
6062
|
+
async down() {
|
|
6063
|
+
throw new Error("not implemented");
|
|
6064
|
+
}
|
|
6065
|
+
};
|
|
6066
|
+
const debug = createDebug__default.default("strapi::database::migration");
|
|
6067
|
+
const renameIdentifiersLongerThanMaxLength = {
|
|
6068
|
+
name: "5.0.0-rename-identifiers-longer-than-max-length",
|
|
6069
|
+
async up(knex2, db) {
|
|
6070
|
+
const md = db.metadata;
|
|
6071
|
+
const diffs = findDiffs(md);
|
|
6072
|
+
for (const indexDiff of diffs.indexes) {
|
|
6073
|
+
await renameIndex(knex2, db, indexDiff);
|
|
6074
|
+
}
|
|
6075
|
+
for (const columnDiff of diffs.columns) {
|
|
6076
|
+
const { full, short } = columnDiff;
|
|
6077
|
+
const tableName = full.tableName;
|
|
6078
|
+
const hasTable = await knex2.schema.hasTable(tableName);
|
|
6079
|
+
if (hasTable) {
|
|
6080
|
+
const hasColumn = await knex2.schema.hasColumn(tableName, full.columnName);
|
|
6081
|
+
if (hasColumn) {
|
|
6082
|
+
await knex2.schema.alterTable(tableName, async (table) => {
|
|
6083
|
+
debug(`renaming column ${full.columnName} to ${short.columnName}`);
|
|
6084
|
+
table.renameColumn(full.columnName, short.columnName);
|
|
6085
|
+
});
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
}
|
|
6089
|
+
for (const tableDiff of diffs.tables) {
|
|
6090
|
+
const hasTable = await knex2.schema.hasTable(tableDiff.full.tableName);
|
|
6091
|
+
if (hasTable) {
|
|
6092
|
+
debug(`renaming table ${tableDiff.full.tableName} to ${tableDiff.short.tableName}`);
|
|
6093
|
+
await knex2.schema.renameTable(tableDiff.full.tableName, tableDiff.short.tableName);
|
|
6094
|
+
}
|
|
6095
|
+
}
|
|
6096
|
+
},
|
|
6097
|
+
async down() {
|
|
6098
|
+
throw new Error("not implemented");
|
|
6099
|
+
}
|
|
6100
|
+
};
|
|
6101
|
+
const renameIndex = async (knex2, db, diff) => {
|
|
6102
|
+
const client = db.config.connection.client;
|
|
6103
|
+
const short = diff.short;
|
|
6104
|
+
const full = diff.full;
|
|
6105
|
+
if (full.indexName === short.indexName) {
|
|
6106
|
+
debug(`not renaming index ${full.indexName} because name hasn't changed`);
|
|
6107
|
+
return;
|
|
6108
|
+
}
|
|
6109
|
+
if (short.indexName.includes("_lnk_") || full.indexName.includes("_lnk_") || short.indexName.endsWith("fk") || full.indexName.endsWith("fk")) {
|
|
6110
|
+
return;
|
|
6111
|
+
}
|
|
6112
|
+
debug(`renaming index from ${full.indexName} to ${short.indexName}`);
|
|
6113
|
+
try {
|
|
6114
|
+
await knex2.transaction(async (trx) => {
|
|
6115
|
+
if (client === "mysql" || client === "mariadb") {
|
|
6116
|
+
await knex2.raw(
|
|
6117
|
+
`ALTER TABLE \`${full.tableName}\` RENAME INDEX \`${full.indexName}\` TO \`${short.indexName}\``
|
|
6118
|
+
).transacting(trx);
|
|
6119
|
+
} else if (client === "pg" || client === "postgres") {
|
|
6120
|
+
await knex2.raw(`ALTER INDEX "${full.indexName}" RENAME TO "${short.indexName}"`).transacting(trx);
|
|
6121
|
+
} else if (client === "sqlite" || client === "better") {
|
|
6122
|
+
} else {
|
|
6123
|
+
debug("No db client name matches, not creating index");
|
|
6124
|
+
}
|
|
6125
|
+
});
|
|
6126
|
+
} catch (err) {
|
|
6127
|
+
debug(`error creating index: ${JSON.stringify(err)}`);
|
|
6128
|
+
}
|
|
6129
|
+
};
|
|
6130
|
+
const findDiffs = (shortMap) => {
|
|
6131
|
+
const diffs = {
|
|
6132
|
+
tables: [],
|
|
6133
|
+
columns: [],
|
|
6134
|
+
indexes: []
|
|
6135
|
+
};
|
|
6136
|
+
const shortArr = Array.from(shortMap.entries());
|
|
6137
|
+
shortArr.forEach(([, shortObj], index2) => {
|
|
6138
|
+
const fullTableName = identifiers.getUnshortenedName(shortObj.tableName);
|
|
6139
|
+
if (!fullTableName) {
|
|
6140
|
+
throw new Error(`Missing full table name for ${shortObj.tableName}`);
|
|
6141
|
+
}
|
|
6142
|
+
if (shortObj.tableName !== fullTableName) {
|
|
6143
|
+
diffs.tables.push({
|
|
6144
|
+
full: {
|
|
6145
|
+
index: index2,
|
|
6146
|
+
key: "tableName",
|
|
6147
|
+
tableName: fullTableName
|
|
6148
|
+
},
|
|
6149
|
+
short: {
|
|
6150
|
+
index: index2,
|
|
6151
|
+
key: "tableName",
|
|
6152
|
+
tableName: shortObj.tableName
|
|
6153
|
+
}
|
|
6154
|
+
});
|
|
6155
|
+
}
|
|
6156
|
+
for (const attrKey in shortObj.attributes) {
|
|
6157
|
+
if (shortObj.attributes[attrKey].type === "relation") {
|
|
6158
|
+
continue;
|
|
6159
|
+
}
|
|
6160
|
+
const attr = shortObj.attributes[attrKey];
|
|
6161
|
+
const shortColumnName = attr.columnName;
|
|
6162
|
+
const longColumnName = identifiers.getUnshortenedName(shortColumnName);
|
|
6163
|
+
if (!shortColumnName || !longColumnName) {
|
|
6164
|
+
throw new Error(`missing column name(s) for attribute ${JSON.stringify(attr, null, 2)}`);
|
|
6165
|
+
}
|
|
6166
|
+
if (shortColumnName && longColumnName && shortColumnName !== longColumnName) {
|
|
6167
|
+
diffs.columns.push({
|
|
6168
|
+
short: {
|
|
6169
|
+
index: index2,
|
|
6170
|
+
tableName: fullTableName,
|
|
6171
|
+
// NOTE: this means that we must rename columns before tables
|
|
6172
|
+
key: `attributes.${attrKey}`,
|
|
6173
|
+
columnName: shortColumnName
|
|
6174
|
+
},
|
|
6175
|
+
full: {
|
|
6176
|
+
index: index2,
|
|
6177
|
+
tableName: fullTableName,
|
|
6178
|
+
key: `attributes.${attrKey}`,
|
|
6179
|
+
columnName: longColumnName
|
|
6180
|
+
}
|
|
6181
|
+
});
|
|
6182
|
+
}
|
|
6183
|
+
}
|
|
6184
|
+
for (const attrKey in shortObj.indexes) {
|
|
6185
|
+
const shortIndexName = shortObj.indexes[attrKey].name;
|
|
6186
|
+
const longIndexName = identifiers.getUnshortenedName(shortIndexName);
|
|
6187
|
+
if (!longIndexName) {
|
|
6188
|
+
throw new Error(`Missing full index name for ${shortIndexName}`);
|
|
6189
|
+
}
|
|
6190
|
+
if (shortIndexName && longIndexName && shortIndexName !== longIndexName) {
|
|
6191
|
+
diffs.indexes.push({
|
|
6192
|
+
short: {
|
|
6193
|
+
index: index2,
|
|
6194
|
+
tableName: fullTableName,
|
|
6195
|
+
// NOTE: this means that we must rename columns before tables
|
|
6196
|
+
key: `indexes.${attrKey}`,
|
|
6197
|
+
indexName: shortIndexName
|
|
6198
|
+
},
|
|
6199
|
+
full: {
|
|
6200
|
+
index: index2,
|
|
6201
|
+
tableName: fullTableName,
|
|
6202
|
+
key: `indexes.${attrKey}`,
|
|
6203
|
+
indexName: longIndexName
|
|
6204
|
+
}
|
|
6205
|
+
});
|
|
6206
|
+
}
|
|
6207
|
+
}
|
|
6208
|
+
});
|
|
6209
|
+
return diffs;
|
|
6210
|
+
};
|
|
6211
|
+
const internalMigrations = [
|
|
6212
|
+
renameIdentifiersLongerThanMaxLength,
|
|
6213
|
+
createdDocumentId
|
|
6214
|
+
];
|
|
5714
6215
|
const createInternalMigrationProvider = (db) => {
|
|
5715
6216
|
const context = { db };
|
|
5716
6217
|
const umzugProvider = new umzug.Umzug({
|
|
@@ -5936,8 +6437,14 @@ const validateBidirectionalRelations = async (db) => {
|
|
|
5936
6437
|
for (const { relation, invRelation } of invalidLinks) {
|
|
5937
6438
|
const modelMetadata = db.metadata.get(invRelation.target);
|
|
5938
6439
|
const invModelMetadata = db.metadata.get(relation.target);
|
|
5939
|
-
const joinTableName = getJoinTableName(
|
|
5940
|
-
|
|
6440
|
+
const joinTableName = identifiers.getJoinTableName(
|
|
6441
|
+
_.snakeCase(modelMetadata.tableName),
|
|
6442
|
+
_.snakeCase(invRelation.inversedBy)
|
|
6443
|
+
);
|
|
6444
|
+
const inverseJoinTableName = identifiers.getJoinTableName(
|
|
6445
|
+
_.snakeCase(invModelMetadata.tableName),
|
|
6446
|
+
_.snakeCase(relation.inversedBy)
|
|
6447
|
+
);
|
|
5941
6448
|
const joinTableEmpty = await isLinkTableEmpty(db, joinTableName);
|
|
5942
6449
|
const inverseJoinTableEmpty = await isLinkTableEmpty(db, inverseJoinTableName);
|
|
5943
6450
|
if (joinTableEmpty) {
|
|
@@ -5983,7 +6490,7 @@ class Database {
|
|
|
5983
6490
|
};
|
|
5984
6491
|
this.dialect = getDialect(this);
|
|
5985
6492
|
this.dialect.configure();
|
|
5986
|
-
this.metadata = createMetadata();
|
|
6493
|
+
this.metadata = createMetadata([]);
|
|
5987
6494
|
this.connection = createConnection(this.config.connection, {
|
|
5988
6495
|
pool: { afterCreate: afterCreate(this) }
|
|
5989
6496
|
});
|
|
@@ -6060,9 +6567,7 @@ class Database {
|
|
|
6060
6567
|
await this.connection.destroy();
|
|
6061
6568
|
}
|
|
6062
6569
|
}
|
|
6063
|
-
const utils = { identifiers };
|
|
6064
6570
|
exports.Database = Database;
|
|
6065
6571
|
exports.errors = index;
|
|
6066
6572
|
exports.isKnexQuery = isKnexQuery;
|
|
6067
|
-
exports.utils = utils;
|
|
6068
6573
|
//# sourceMappingURL=index.js.map
|