@strapi/database 5.0.0-beta.1 → 5.0.0-beta.2
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 +757 -313
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +752 -308
- 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/populate/apply.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,8 +2296,8 @@ 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");
|
|
2299
|
+
const idColumnName = identifiers.getJoinColumnAttributeIdName("target");
|
|
2300
|
+
const typeColumnName = identifiers.getMorphColumnTypeName("target");
|
|
2171
2301
|
if ("morphColumn" in attribute && attribute.morphColumn) {
|
|
2172
2302
|
return;
|
|
2173
2303
|
}
|
|
@@ -2189,11 +2319,11 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2189
2319
|
if ("joinTable" in attribute && attribute.joinTable) {
|
|
2190
2320
|
return;
|
|
2191
2321
|
}
|
|
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);
|
|
2322
|
+
const joinTableName = identifiers.getMorphTableName(meta.tableName, attributeName);
|
|
2323
|
+
const joinColumnName = identifiers.getMorphColumnJoinTableIdName(_.snakeCase(meta.singularName));
|
|
2324
|
+
const idColumnName = identifiers.getMorphColumnAttributeIdName(attributeName);
|
|
2325
|
+
const typeColumnName = identifiers.getMorphColumnTypeName(attributeName);
|
|
2326
|
+
const fkIndexName = identifiers.getFkIndexName(joinTableName);
|
|
2197
2327
|
metadata.add({
|
|
2198
2328
|
singularName: joinTableName,
|
|
2199
2329
|
uid: joinTableName,
|
|
@@ -2206,7 +2336,9 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2206
2336
|
type: "integer",
|
|
2207
2337
|
column: {
|
|
2208
2338
|
unsigned: true
|
|
2209
|
-
}
|
|
2339
|
+
},
|
|
2340
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2341
|
+
columnName: joinColumnName
|
|
2210
2342
|
},
|
|
2211
2343
|
[idColumnName]: {
|
|
2212
2344
|
type: "integer",
|
|
@@ -2233,11 +2365,11 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2233
2365
|
columns: [joinColumnName]
|
|
2234
2366
|
},
|
|
2235
2367
|
{
|
|
2236
|
-
name:
|
|
2368
|
+
name: identifiers.getOrderIndexName(joinTableName),
|
|
2237
2369
|
columns: [ORDER]
|
|
2238
2370
|
},
|
|
2239
2371
|
{
|
|
2240
|
-
name:
|
|
2372
|
+
name: identifiers.getIdColumnIndexName(joinTableName),
|
|
2241
2373
|
columns: [idColumnName]
|
|
2242
2374
|
}
|
|
2243
2375
|
],
|
|
@@ -2246,7 +2378,7 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2246
2378
|
name: fkIndexName,
|
|
2247
2379
|
columns: [joinColumnName],
|
|
2248
2380
|
referencedColumns: [ID],
|
|
2249
|
-
referencedTable:
|
|
2381
|
+
referencedTable: meta.tableName,
|
|
2250
2382
|
onDelete: "CASCADE"
|
|
2251
2383
|
}
|
|
2252
2384
|
],
|
|
@@ -2293,12 +2425,12 @@ const createMorphMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2293
2425
|
throw new Error(`Morph target attribute not found. Looking for ${attribute.morphBy}`);
|
|
2294
2426
|
}
|
|
2295
2427
|
};
|
|
2296
|
-
const
|
|
2428
|
+
const createJoinColumn = (metadata, { attribute, attributeName }) => {
|
|
2297
2429
|
const targetMeta = metadata.get(attribute.target);
|
|
2298
2430
|
if (!targetMeta) {
|
|
2299
2431
|
throw new Error(`Unknown target ${attribute.target}`);
|
|
2300
2432
|
}
|
|
2301
|
-
const joinColumnName =
|
|
2433
|
+
const joinColumnName = identifiers.getJoinColumnAttributeIdName(_.snakeCase(attributeName));
|
|
2302
2434
|
const joinColumn = {
|
|
2303
2435
|
name: joinColumnName,
|
|
2304
2436
|
referencedColumn: ID,
|
|
@@ -2313,7 +2445,7 @@ const createJoinColum = (metadata, { attribute, attributeName }) => {
|
|
|
2313
2445
|
Object.assign(inverseAttribute, {
|
|
2314
2446
|
joinColumn: {
|
|
2315
2447
|
name: joinColumn.referencedColumn,
|
|
2316
|
-
referencedColumn:
|
|
2448
|
+
referencedColumn: joinColumnName
|
|
2317
2449
|
}
|
|
2318
2450
|
});
|
|
2319
2451
|
}
|
|
@@ -2326,21 +2458,26 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2326
2458
|
if ("joinTable" in attribute && attribute.joinTable) {
|
|
2327
2459
|
return;
|
|
2328
2460
|
}
|
|
2329
|
-
const joinTableName = getJoinTableName(
|
|
2330
|
-
|
|
2331
|
-
|
|
2461
|
+
const joinTableName = identifiers.getJoinTableName(
|
|
2462
|
+
_.snakeCase(meta.tableName),
|
|
2463
|
+
_.snakeCase(attributeName)
|
|
2464
|
+
);
|
|
2465
|
+
const joinColumnName = identifiers.getJoinColumnAttributeIdName(_.snakeCase(meta.singularName));
|
|
2466
|
+
let inverseJoinColumnName = identifiers.getJoinColumnAttributeIdName(
|
|
2467
|
+
_.snakeCase(targetMeta.singularName)
|
|
2468
|
+
);
|
|
2332
2469
|
if (joinColumnName === inverseJoinColumnName) {
|
|
2333
|
-
inverseJoinColumnName = getInverseJoinColumnAttributeIdName(
|
|
2334
|
-
targetMeta.singularName
|
|
2470
|
+
inverseJoinColumnName = identifiers.getInverseJoinColumnAttributeIdName(
|
|
2471
|
+
_.snakeCase(targetMeta.singularName)
|
|
2335
2472
|
);
|
|
2336
2473
|
}
|
|
2337
|
-
const orderColumnName = getOrderColumnName(targetMeta.singularName);
|
|
2338
|
-
let inverseOrderColumnName = getOrderColumnName(meta.singularName);
|
|
2474
|
+
const orderColumnName = identifiers.getOrderColumnName(_.snakeCase(targetMeta.singularName));
|
|
2475
|
+
let inverseOrderColumnName = identifiers.getOrderColumnName(_.snakeCase(meta.singularName));
|
|
2339
2476
|
if (attribute.relation === "manyToMany" && orderColumnName === inverseOrderColumnName) {
|
|
2340
|
-
inverseOrderColumnName = getInverseOrderColumnName(meta.singularName);
|
|
2477
|
+
inverseOrderColumnName = identifiers.getInverseOrderColumnName(_.snakeCase(meta.singularName));
|
|
2341
2478
|
}
|
|
2342
|
-
const fkIndexName = getFkIndexName(joinTableName);
|
|
2343
|
-
const invFkIndexName = getInverseFkIndexName(joinTableName);
|
|
2479
|
+
const fkIndexName = identifiers.getFkIndexName(joinTableName);
|
|
2480
|
+
const invFkIndexName = identifiers.getInverseFkIndexName(joinTableName);
|
|
2344
2481
|
const metadataSchema = {
|
|
2345
2482
|
singularName: joinTableName,
|
|
2346
2483
|
uid: joinTableName,
|
|
@@ -2353,13 +2490,17 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2353
2490
|
type: "integer",
|
|
2354
2491
|
column: {
|
|
2355
2492
|
unsigned: true
|
|
2356
|
-
}
|
|
2493
|
+
},
|
|
2494
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2495
|
+
columnName: joinColumnName
|
|
2357
2496
|
},
|
|
2358
2497
|
[inverseJoinColumnName]: {
|
|
2359
2498
|
type: "integer",
|
|
2360
2499
|
column: {
|
|
2361
2500
|
unsigned: true
|
|
2362
|
-
}
|
|
2501
|
+
},
|
|
2502
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2503
|
+
columnName: inverseJoinColumnName
|
|
2363
2504
|
}
|
|
2364
2505
|
// TODO: add extra pivot attributes -> user should use an intermediate entity
|
|
2365
2506
|
},
|
|
@@ -2373,7 +2514,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2373
2514
|
columns: [inverseJoinColumnName]
|
|
2374
2515
|
},
|
|
2375
2516
|
{
|
|
2376
|
-
name: getUniqueIndexName(joinTableName),
|
|
2517
|
+
name: identifiers.getUniqueIndexName(joinTableName),
|
|
2377
2518
|
columns: [joinColumnName, inverseJoinColumnName],
|
|
2378
2519
|
type: "unique"
|
|
2379
2520
|
}
|
|
@@ -2384,7 +2525,6 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2384
2525
|
columns: [joinColumnName],
|
|
2385
2526
|
referencedColumns: [ID],
|
|
2386
2527
|
referencedTable: meta.tableName,
|
|
2387
|
-
// TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
|
|
2388
2528
|
onDelete: "CASCADE"
|
|
2389
2529
|
},
|
|
2390
2530
|
{
|
|
@@ -2392,7 +2532,6 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2392
2532
|
columns: [inverseJoinColumnName],
|
|
2393
2533
|
referencedColumns: [ID],
|
|
2394
2534
|
referencedTable: targetMeta.tableName,
|
|
2395
|
-
// TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
|
|
2396
2535
|
onDelete: "CASCADE"
|
|
2397
2536
|
}
|
|
2398
2537
|
],
|
|
@@ -2422,8 +2561,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2422
2561
|
}
|
|
2423
2562
|
};
|
|
2424
2563
|
metadataSchema.indexes.push({
|
|
2425
|
-
name: getOrderFkIndexName(joinTableName),
|
|
2426
|
-
// TODO: should we send joinTableName as parts?
|
|
2564
|
+
name: identifiers.getOrderFkIndexName(joinTableName),
|
|
2427
2565
|
columns: [orderColumnName]
|
|
2428
2566
|
});
|
|
2429
2567
|
joinTable.orderColumnName = orderColumnName;
|
|
@@ -2438,7 +2576,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2438
2576
|
}
|
|
2439
2577
|
};
|
|
2440
2578
|
metadataSchema.indexes.push({
|
|
2441
|
-
name: getOrderInverseFkIndexName(joinTableName),
|
|
2579
|
+
name: identifiers.getOrderInverseFkIndexName(joinTableName),
|
|
2442
2580
|
columns: [inverseOrderColumnName]
|
|
2443
2581
|
});
|
|
2444
2582
|
joinTable.inverseOrderColumnName = inverseOrderColumnName;
|
|
@@ -2496,6 +2634,12 @@ const createRelation = (attributeName, attribute, meta, metadata) => {
|
|
|
2496
2634
|
}
|
|
2497
2635
|
};
|
|
2498
2636
|
class Metadata extends Map {
|
|
2637
|
+
// TODO: we expose the global identifiers in this way so that in the future we can instantiate our own
|
|
2638
|
+
// However, it should NOT be done until all the methods used by metadata can be part of this metadata object
|
|
2639
|
+
// and access this one; currently they all access the global identifiers directly.
|
|
2640
|
+
get identifiers() {
|
|
2641
|
+
return identifiers;
|
|
2642
|
+
}
|
|
2499
2643
|
get(key) {
|
|
2500
2644
|
if (!super.has(key)) {
|
|
2501
2645
|
throw new Error(`Metadata for "${key}" not found`);
|
|
@@ -2519,10 +2663,12 @@ class Metadata extends Map {
|
|
|
2519
2663
|
seenTables.set(meta.tableName, true);
|
|
2520
2664
|
}
|
|
2521
2665
|
}
|
|
2522
|
-
loadModels(models
|
|
2523
|
-
for (const model of
|
|
2666
|
+
loadModels(models) {
|
|
2667
|
+
for (const model of _.cloneDeep(models ?? [])) {
|
|
2668
|
+
const tableName = identifiers.getTableName(model.tableName);
|
|
2524
2669
|
this.add({
|
|
2525
2670
|
...model,
|
|
2671
|
+
tableName,
|
|
2526
2672
|
attributes: {
|
|
2527
2673
|
...model.attributes
|
|
2528
2674
|
},
|
|
@@ -2563,10 +2709,13 @@ class Metadata extends Map {
|
|
|
2563
2709
|
}
|
|
2564
2710
|
}
|
|
2565
2711
|
const createAttribute = (attributeName, attribute) => {
|
|
2566
|
-
|
|
2712
|
+
if ("columnName" in attribute && attribute.columnName) {
|
|
2713
|
+
return;
|
|
2714
|
+
}
|
|
2715
|
+
const columnName = identifiers.getColumnName(_.snakeCase(attributeName));
|
|
2567
2716
|
Object.assign(attribute, { columnName });
|
|
2568
2717
|
};
|
|
2569
|
-
const createMetadata = (models
|
|
2718
|
+
const createMetadata = (models) => {
|
|
2570
2719
|
const metadata = new Metadata();
|
|
2571
2720
|
if (models.length) {
|
|
2572
2721
|
metadata.loadModels(models);
|
|
@@ -3061,10 +3210,13 @@ const XtoOne = async (input, ctx) => {
|
|
|
3061
3210
|
rootTable: qb2.alias,
|
|
3062
3211
|
on: joinTable.on
|
|
3063
3212
|
}).select([joinColAlias, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3064
|
-
const map2 = rows2.reduce(
|
|
3065
|
-
map3
|
|
3066
|
-
|
|
3067
|
-
|
|
3213
|
+
const map2 = rows2.reduce(
|
|
3214
|
+
(map3, row) => {
|
|
3215
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3216
|
+
return map3;
|
|
3217
|
+
},
|
|
3218
|
+
{}
|
|
3219
|
+
);
|
|
3068
3220
|
results.forEach((result) => {
|
|
3069
3221
|
result[attributeName] = map2[result[referencedColumnName]] || { count: 0 };
|
|
3070
3222
|
});
|
|
@@ -3137,10 +3289,13 @@ const oneToMany = async (input, ctx) => {
|
|
|
3137
3289
|
rootTable: qb2.alias,
|
|
3138
3290
|
on: joinTable.on
|
|
3139
3291
|
}).select([joinColAlias, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3140
|
-
const map2 = rows2.reduce(
|
|
3141
|
-
map3
|
|
3142
|
-
|
|
3143
|
-
|
|
3292
|
+
const map2 = rows2.reduce(
|
|
3293
|
+
(map3, row) => {
|
|
3294
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3295
|
+
return map3;
|
|
3296
|
+
},
|
|
3297
|
+
{}
|
|
3298
|
+
);
|
|
3144
3299
|
results.forEach((result) => {
|
|
3145
3300
|
result[attributeName] = map2[result[referencedColumnName]] || { count: 0 };
|
|
3146
3301
|
});
|
|
@@ -3194,10 +3349,13 @@ const manyToMany = async (input, ctx) => {
|
|
|
3194
3349
|
rootTable: populateQb.alias,
|
|
3195
3350
|
on: joinTable.on
|
|
3196
3351
|
}).select([joinColAlias, populateQb.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3197
|
-
const map2 = rows2.reduce(
|
|
3198
|
-
map3
|
|
3199
|
-
|
|
3200
|
-
|
|
3352
|
+
const map2 = rows2.reduce(
|
|
3353
|
+
(map3, row) => {
|
|
3354
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3355
|
+
return map3;
|
|
3356
|
+
},
|
|
3357
|
+
{}
|
|
3358
|
+
);
|
|
3201
3359
|
results.forEach((result) => {
|
|
3202
3360
|
result[attributeName] = map2[result[referencedColumnName]] || { count: 0 };
|
|
3203
3361
|
});
|
|
@@ -3544,7 +3702,7 @@ const castValue = (value, attribute) => {
|
|
|
3544
3702
|
};
|
|
3545
3703
|
const processSingleAttributeWhere = (attribute, where, operator = "$eq") => {
|
|
3546
3704
|
if (!isRecord$1(where)) {
|
|
3547
|
-
if (utils
|
|
3705
|
+
if (utils.isOperatorOfType("cast", operator)) {
|
|
3548
3706
|
return castValue(where, attribute);
|
|
3549
3707
|
}
|
|
3550
3708
|
return where;
|
|
@@ -3552,7 +3710,7 @@ const processSingleAttributeWhere = (attribute, where, operator = "$eq") => {
|
|
|
3552
3710
|
const filters = {};
|
|
3553
3711
|
for (const key of Object.keys(where)) {
|
|
3554
3712
|
const value = where[key];
|
|
3555
|
-
if (!utils
|
|
3713
|
+
if (!utils.isOperatorOfType("where", key)) {
|
|
3556
3714
|
throw new Error(`Undefined attribute level operator ${key}`);
|
|
3557
3715
|
}
|
|
3558
3716
|
filters[key] = processAttributeWhere(attribute, value, key);
|
|
@@ -3583,7 +3741,7 @@ function processWhere(where, ctx) {
|
|
|
3583
3741
|
const filters = {};
|
|
3584
3742
|
for (const key of Object.keys(where)) {
|
|
3585
3743
|
const value = where[key];
|
|
3586
|
-
if (utils
|
|
3744
|
+
if (utils.isOperatorOfType("group", key) && Array.isArray(value)) {
|
|
3587
3745
|
filters[key] = value.map((sub) => processNested(sub, ctx));
|
|
3588
3746
|
continue;
|
|
3589
3747
|
}
|
|
@@ -3591,7 +3749,7 @@ function processWhere(where, ctx) {
|
|
|
3591
3749
|
filters[key] = processNested(value, ctx);
|
|
3592
3750
|
continue;
|
|
3593
3751
|
}
|
|
3594
|
-
if (utils
|
|
3752
|
+
if (utils.isOperatorOfType("where", key)) {
|
|
3595
3753
|
throw new Error(
|
|
3596
3754
|
`Only $and, $or and $not can only be used as root level operators. Found ${key}.`
|
|
3597
3755
|
);
|
|
@@ -3613,7 +3771,7 @@ function processWhere(where, ctx) {
|
|
|
3613
3771
|
alias: subAlias,
|
|
3614
3772
|
uid: attribute.target
|
|
3615
3773
|
});
|
|
3616
|
-
if (!isRecord$1(nestedWhere) || utils
|
|
3774
|
+
if (!isRecord$1(nestedWhere) || utils.isOperatorOfType("where", _.keys(nestedWhere)[0])) {
|
|
3617
3775
|
nestedWhere = { [qb.aliasColumn("id", subAlias)]: nestedWhere };
|
|
3618
3776
|
}
|
|
3619
3777
|
Object.assign(filters, nestedWhere);
|
|
@@ -3630,7 +3788,7 @@ function processWhere(where, ctx) {
|
|
|
3630
3788
|
return filters;
|
|
3631
3789
|
}
|
|
3632
3790
|
const applyOperator = (qb, column, operator, value) => {
|
|
3633
|
-
if (Array.isArray(value) && !utils
|
|
3791
|
+
if (Array.isArray(value) && !utils.isOperatorOfType("array", operator)) {
|
|
3634
3792
|
return qb.where((subQB) => {
|
|
3635
3793
|
value.forEach(
|
|
3636
3794
|
(subValue) => subQB.orWhere((innerQB) => {
|
|
@@ -4708,21 +4866,24 @@ const sortConnectArray = (connectArr, initialArr = [], strictSort = true) => {
|
|
|
4708
4866
|
(acc, rel) => ({ ...acc, [rel.id]: true }),
|
|
4709
4867
|
{}
|
|
4710
4868
|
);
|
|
4711
|
-
const mappedRelations = connectArr.reduce(
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4869
|
+
const mappedRelations = connectArr.reduce(
|
|
4870
|
+
(mapper, relation) => {
|
|
4871
|
+
const adjacentRelId = relation.position?.before || relation.position?.after;
|
|
4872
|
+
if (!adjacentRelId || !relationInInitialArray[adjacentRelId] && !mapper[adjacentRelId]) {
|
|
4873
|
+
needsSorting = true;
|
|
4874
|
+
}
|
|
4875
|
+
if (mapper[relation.id]) {
|
|
4876
|
+
throw new InvalidRelationError(
|
|
4877
|
+
`The relation with id ${relation.id} is already connected. You cannot connect the same relation twice.`
|
|
4878
|
+
);
|
|
4879
|
+
}
|
|
4880
|
+
return {
|
|
4881
|
+
[relation.id]: { ...relation, computed: false },
|
|
4882
|
+
...mapper
|
|
4883
|
+
};
|
|
4884
|
+
},
|
|
4885
|
+
{}
|
|
4886
|
+
);
|
|
4726
4887
|
if (!needsSorting)
|
|
4727
4888
|
return connectArr;
|
|
4728
4889
|
const computeRelation = (relation, relationsSeenInBranch) => {
|
|
@@ -4830,14 +4991,17 @@ const relationsOrderer = (initArr, idColumn, orderColumn, strict) => {
|
|
|
4830
4991
|
* Get a map between the relation id and its order
|
|
4831
4992
|
*/
|
|
4832
4993
|
getOrderMap() {
|
|
4833
|
-
return ___default$1.default(computedRelations).groupBy("order").reduce(
|
|
4834
|
-
|
|
4994
|
+
return ___default$1.default(computedRelations).groupBy("order").reduce(
|
|
4995
|
+
(acc, relations) => {
|
|
4996
|
+
if (relations[0]?.init)
|
|
4997
|
+
return acc;
|
|
4998
|
+
relations.forEach((relation, idx) => {
|
|
4999
|
+
acc[relation.id] = Math.floor(relation.order) + (idx + 1) / (relations.length + 1);
|
|
5000
|
+
});
|
|
4835
5001
|
return acc;
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
return acc;
|
|
4840
|
-
}, {});
|
|
5002
|
+
},
|
|
5003
|
+
{}
|
|
5004
|
+
);
|
|
4841
5005
|
}
|
|
4842
5006
|
};
|
|
4843
5007
|
};
|
|
@@ -5591,10 +5755,13 @@ const createEntityManager = (db) => {
|
|
|
5591
5755
|
const entry = await this.findOne(uid, {
|
|
5592
5756
|
select: ["id"],
|
|
5593
5757
|
where: { id: entity.id },
|
|
5594
|
-
populate: fieldsArr.reduce(
|
|
5595
|
-
acc
|
|
5596
|
-
|
|
5597
|
-
|
|
5758
|
+
populate: fieldsArr.reduce(
|
|
5759
|
+
(acc, field) => {
|
|
5760
|
+
acc[field] = populate || true;
|
|
5761
|
+
return acc;
|
|
5762
|
+
},
|
|
5763
|
+
{}
|
|
5764
|
+
)
|
|
5598
5765
|
});
|
|
5599
5766
|
if (!entry) {
|
|
5600
5767
|
return null;
|
|
@@ -5710,7 +5877,280 @@ const createUserMigrationProvider = (db) => {
|
|
|
5710
5877
|
}
|
|
5711
5878
|
};
|
|
5712
5879
|
};
|
|
5713
|
-
const
|
|
5880
|
+
const QUERIES = {
|
|
5881
|
+
async postgres(knex2, params) {
|
|
5882
|
+
const res = await knex2.raw(
|
|
5883
|
+
`
|
|
5884
|
+
SELECT :tableName:.id as id, string_agg(DISTINCT :inverseJoinColumn:::character varying, ',') as other_ids
|
|
5885
|
+
FROM :tableName:
|
|
5886
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5887
|
+
WHERE document_id IS NULL
|
|
5888
|
+
GROUP BY :tableName:.id, :joinColumn:
|
|
5889
|
+
LIMIT 1;
|
|
5890
|
+
`,
|
|
5891
|
+
params
|
|
5892
|
+
);
|
|
5893
|
+
return res.rows;
|
|
5894
|
+
},
|
|
5895
|
+
async mysql(knex2, params) {
|
|
5896
|
+
const [res] = await knex2.raw(
|
|
5897
|
+
`
|
|
5898
|
+
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids
|
|
5899
|
+
FROM :tableName:
|
|
5900
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5901
|
+
WHERE document_id IS NULL
|
|
5902
|
+
GROUP BY :tableName:.id, :joinColumn:
|
|
5903
|
+
LIMIT 1;
|
|
5904
|
+
`,
|
|
5905
|
+
params
|
|
5906
|
+
);
|
|
5907
|
+
return res;
|
|
5908
|
+
},
|
|
5909
|
+
async sqlite(knex2, params) {
|
|
5910
|
+
return knex2.raw(
|
|
5911
|
+
`
|
|
5912
|
+
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids
|
|
5913
|
+
FROM :tableName:
|
|
5914
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5915
|
+
WHERE document_id IS NULL
|
|
5916
|
+
GROUP BY :joinColumn:
|
|
5917
|
+
LIMIT 1;
|
|
5918
|
+
`,
|
|
5919
|
+
params
|
|
5920
|
+
);
|
|
5921
|
+
}
|
|
5922
|
+
};
|
|
5923
|
+
const getNextIdsToCreateDocumentId = async (db, knex2, {
|
|
5924
|
+
joinColumn,
|
|
5925
|
+
inverseJoinColumn,
|
|
5926
|
+
tableName,
|
|
5927
|
+
joinTableName
|
|
5928
|
+
}) => {
|
|
5929
|
+
const res = await QUERIES[db.dialect.client](knex2, {
|
|
5930
|
+
joinColumn,
|
|
5931
|
+
inverseJoinColumn,
|
|
5932
|
+
tableName,
|
|
5933
|
+
joinTableName
|
|
5934
|
+
});
|
|
5935
|
+
if (res.length > 0) {
|
|
5936
|
+
const row = res[0];
|
|
5937
|
+
const otherIds = row.other_ids ? row.other_ids.split(",").map((v) => parseInt(v, 10)) : [];
|
|
5938
|
+
return [row.id, ...otherIds];
|
|
5939
|
+
}
|
|
5940
|
+
return [];
|
|
5941
|
+
};
|
|
5942
|
+
const migrateDocumentIdsWithLocalizations = async (db, knex2, meta) => {
|
|
5943
|
+
const singularName = meta.singularName.toLowerCase();
|
|
5944
|
+
const joinColumn = identifiers.getJoinColumnAttributeIdName(singularName);
|
|
5945
|
+
const inverseJoinColumn = identifiers.getInverseJoinColumnAttributeIdName(singularName);
|
|
5946
|
+
let ids;
|
|
5947
|
+
do {
|
|
5948
|
+
ids = await getNextIdsToCreateDocumentId(db, knex2, {
|
|
5949
|
+
joinColumn,
|
|
5950
|
+
inverseJoinColumn,
|
|
5951
|
+
tableName: meta.tableName,
|
|
5952
|
+
joinTableName: identifiers.getJoinTableName(meta.tableName, `localizations`)
|
|
5953
|
+
});
|
|
5954
|
+
if (ids.length > 0) {
|
|
5955
|
+
await knex2(meta.tableName).update({ document_id: cuid2.createId() }).whereIn("id", ids);
|
|
5956
|
+
}
|
|
5957
|
+
} while (ids.length > 0);
|
|
5958
|
+
};
|
|
5959
|
+
const migrationDocumentIds = async (db, knex2, meta) => {
|
|
5960
|
+
let run = true;
|
|
5961
|
+
do {
|
|
5962
|
+
const updatedRows = await knex2(meta.tableName).update({ document_id: cuid2.createId() }).whereIn("id", (builder) => {
|
|
5963
|
+
return builder.whereNull("document_id").select("id").limit(1);
|
|
5964
|
+
});
|
|
5965
|
+
if (updatedRows <= 0) {
|
|
5966
|
+
run = false;
|
|
5967
|
+
}
|
|
5968
|
+
} while (run);
|
|
5969
|
+
};
|
|
5970
|
+
const createDocumentIdColumn = async (knex2, tableName) => {
|
|
5971
|
+
await knex2.schema.alterTable(tableName, (table) => {
|
|
5972
|
+
table.string("document_id");
|
|
5973
|
+
});
|
|
5974
|
+
};
|
|
5975
|
+
const hasLocalizationsJoinTable = async (knex2, tableName) => {
|
|
5976
|
+
const joinTableName = identifiers.getJoinTableName(tableName, "localizations");
|
|
5977
|
+
return knex2.schema.hasTable(joinTableName);
|
|
5978
|
+
};
|
|
5979
|
+
const createdDocumentId = {
|
|
5980
|
+
name: "5.0.0-02-created-document-id",
|
|
5981
|
+
async up(knex2, db) {
|
|
5982
|
+
for (const meta of db.metadata.values()) {
|
|
5983
|
+
const hasTable = await knex2.schema.hasTable(meta.tableName);
|
|
5984
|
+
if (!hasTable) {
|
|
5985
|
+
continue;
|
|
5986
|
+
}
|
|
5987
|
+
if ("documentId" in meta.attributes) {
|
|
5988
|
+
const hasDocumentIdColumn = await knex2.schema.hasColumn(meta.tableName, "document_id");
|
|
5989
|
+
if (hasDocumentIdColumn) {
|
|
5990
|
+
continue;
|
|
5991
|
+
}
|
|
5992
|
+
await createDocumentIdColumn(knex2, meta.tableName);
|
|
5993
|
+
if (await hasLocalizationsJoinTable(knex2, meta.tableName)) {
|
|
5994
|
+
await migrateDocumentIdsWithLocalizations(db, knex2, meta);
|
|
5995
|
+
} else {
|
|
5996
|
+
await migrationDocumentIds(db, knex2, meta);
|
|
5997
|
+
}
|
|
5998
|
+
}
|
|
5999
|
+
}
|
|
6000
|
+
},
|
|
6001
|
+
async down() {
|
|
6002
|
+
throw new Error("not implemented");
|
|
6003
|
+
}
|
|
6004
|
+
};
|
|
6005
|
+
const debug = createDebug__default.default("strapi::database::migration");
|
|
6006
|
+
const renameIdentifiersLongerThanMaxLength = {
|
|
6007
|
+
name: "5.0.0-rename-identifiers-longer-than-max-length",
|
|
6008
|
+
async up(knex2, db) {
|
|
6009
|
+
const md = db.metadata;
|
|
6010
|
+
const diffs = findDiffs(md);
|
|
6011
|
+
for (const indexDiff of diffs.indexes) {
|
|
6012
|
+
await renameIndex(knex2, db, indexDiff);
|
|
6013
|
+
}
|
|
6014
|
+
for (const columnDiff of diffs.columns) {
|
|
6015
|
+
const { full, short } = columnDiff;
|
|
6016
|
+
const tableName = full.tableName;
|
|
6017
|
+
const hasTable = await knex2.schema.hasTable(tableName);
|
|
6018
|
+
if (hasTable) {
|
|
6019
|
+
const hasColumn = await knex2.schema.hasColumn(tableName, full.columnName);
|
|
6020
|
+
if (hasColumn) {
|
|
6021
|
+
await knex2.schema.alterTable(tableName, async (table) => {
|
|
6022
|
+
debug(`renaming column ${full.columnName} to ${short.columnName}`);
|
|
6023
|
+
table.renameColumn(full.columnName, short.columnName);
|
|
6024
|
+
});
|
|
6025
|
+
}
|
|
6026
|
+
}
|
|
6027
|
+
}
|
|
6028
|
+
for (const tableDiff of diffs.tables) {
|
|
6029
|
+
const hasTable = await knex2.schema.hasTable(tableDiff.full.tableName);
|
|
6030
|
+
if (hasTable) {
|
|
6031
|
+
debug(`renaming table ${tableDiff.full.tableName} to ${tableDiff.short.tableName}`);
|
|
6032
|
+
await knex2.schema.renameTable(tableDiff.full.tableName, tableDiff.short.tableName);
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
},
|
|
6036
|
+
async down() {
|
|
6037
|
+
throw new Error("not implemented");
|
|
6038
|
+
}
|
|
6039
|
+
};
|
|
6040
|
+
const renameIndex = async (knex2, db, diff) => {
|
|
6041
|
+
const client = db.config.connection.client;
|
|
6042
|
+
const short = diff.short;
|
|
6043
|
+
const full = diff.full;
|
|
6044
|
+
if (full.indexName === short.indexName) {
|
|
6045
|
+
debug(`not renaming index ${full.indexName} because name hasn't changed`);
|
|
6046
|
+
return;
|
|
6047
|
+
}
|
|
6048
|
+
if (short.indexName.includes("_lnk_") || full.indexName.includes("_lnk_") || short.indexName.endsWith("fk") || full.indexName.endsWith("fk")) {
|
|
6049
|
+
return;
|
|
6050
|
+
}
|
|
6051
|
+
debug(`renaming index from ${full.indexName} to ${short.indexName}`);
|
|
6052
|
+
try {
|
|
6053
|
+
await knex2.transaction(async (trx) => {
|
|
6054
|
+
if (client === "mysql" || client === "mariadb") {
|
|
6055
|
+
await knex2.raw(
|
|
6056
|
+
`ALTER TABLE \`${full.tableName}\` RENAME INDEX \`${full.indexName}\` TO \`${short.indexName}\``
|
|
6057
|
+
).transacting(trx);
|
|
6058
|
+
} else if (client === "pg" || client === "postgres") {
|
|
6059
|
+
await knex2.raw(`ALTER INDEX "${full.indexName}" RENAME TO "${short.indexName}"`).transacting(trx);
|
|
6060
|
+
} else if (client === "sqlite" || client === "better") {
|
|
6061
|
+
} else {
|
|
6062
|
+
debug("No db client name matches, not creating index");
|
|
6063
|
+
}
|
|
6064
|
+
});
|
|
6065
|
+
} catch (err) {
|
|
6066
|
+
debug(`error creating index: ${JSON.stringify(err)}`);
|
|
6067
|
+
}
|
|
6068
|
+
};
|
|
6069
|
+
const findDiffs = (shortMap) => {
|
|
6070
|
+
const diffs = {
|
|
6071
|
+
tables: [],
|
|
6072
|
+
columns: [],
|
|
6073
|
+
indexes: []
|
|
6074
|
+
};
|
|
6075
|
+
const shortArr = Array.from(shortMap.entries());
|
|
6076
|
+
shortArr.forEach(([, shortObj], index2) => {
|
|
6077
|
+
const fullTableName = identifiers.getUnshortenedName(shortObj.tableName);
|
|
6078
|
+
if (!fullTableName) {
|
|
6079
|
+
throw new Error(`Missing full table name for ${shortObj.tableName}`);
|
|
6080
|
+
}
|
|
6081
|
+
if (shortObj.tableName !== fullTableName) {
|
|
6082
|
+
diffs.tables.push({
|
|
6083
|
+
full: {
|
|
6084
|
+
index: index2,
|
|
6085
|
+
key: "tableName",
|
|
6086
|
+
tableName: fullTableName
|
|
6087
|
+
},
|
|
6088
|
+
short: {
|
|
6089
|
+
index: index2,
|
|
6090
|
+
key: "tableName",
|
|
6091
|
+
tableName: shortObj.tableName
|
|
6092
|
+
}
|
|
6093
|
+
});
|
|
6094
|
+
}
|
|
6095
|
+
for (const attrKey in shortObj.attributes) {
|
|
6096
|
+
if (shortObj.attributes[attrKey].type === "relation") {
|
|
6097
|
+
continue;
|
|
6098
|
+
}
|
|
6099
|
+
const attr = shortObj.attributes[attrKey];
|
|
6100
|
+
const shortColumnName = attr.columnName;
|
|
6101
|
+
const longColumnName = identifiers.getUnshortenedName(shortColumnName);
|
|
6102
|
+
if (!shortColumnName || !longColumnName) {
|
|
6103
|
+
throw new Error(`missing column name(s) for attribute ${JSON.stringify(attr, null, 2)}`);
|
|
6104
|
+
}
|
|
6105
|
+
if (shortColumnName && longColumnName && shortColumnName !== longColumnName) {
|
|
6106
|
+
diffs.columns.push({
|
|
6107
|
+
short: {
|
|
6108
|
+
index: index2,
|
|
6109
|
+
tableName: fullTableName,
|
|
6110
|
+
// NOTE: this means that we must rename columns before tables
|
|
6111
|
+
key: `attributes.${attrKey}`,
|
|
6112
|
+
columnName: shortColumnName
|
|
6113
|
+
},
|
|
6114
|
+
full: {
|
|
6115
|
+
index: index2,
|
|
6116
|
+
tableName: fullTableName,
|
|
6117
|
+
key: `attributes.${attrKey}`,
|
|
6118
|
+
columnName: longColumnName
|
|
6119
|
+
}
|
|
6120
|
+
});
|
|
6121
|
+
}
|
|
6122
|
+
}
|
|
6123
|
+
for (const attrKey in shortObj.indexes) {
|
|
6124
|
+
const shortIndexName = shortObj.indexes[attrKey].name;
|
|
6125
|
+
const longIndexName = identifiers.getUnshortenedName(shortIndexName);
|
|
6126
|
+
if (!longIndexName) {
|
|
6127
|
+
throw new Error(`Missing full index name for ${shortIndexName}`);
|
|
6128
|
+
}
|
|
6129
|
+
if (shortIndexName && longIndexName && shortIndexName !== longIndexName) {
|
|
6130
|
+
diffs.indexes.push({
|
|
6131
|
+
short: {
|
|
6132
|
+
index: index2,
|
|
6133
|
+
tableName: fullTableName,
|
|
6134
|
+
// NOTE: this means that we must rename columns before tables
|
|
6135
|
+
key: `indexes.${attrKey}`,
|
|
6136
|
+
indexName: shortIndexName
|
|
6137
|
+
},
|
|
6138
|
+
full: {
|
|
6139
|
+
index: index2,
|
|
6140
|
+
tableName: fullTableName,
|
|
6141
|
+
key: `indexes.${attrKey}`,
|
|
6142
|
+
indexName: longIndexName
|
|
6143
|
+
}
|
|
6144
|
+
});
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
6147
|
+
});
|
|
6148
|
+
return diffs;
|
|
6149
|
+
};
|
|
6150
|
+
const internalMigrations = [
|
|
6151
|
+
renameIdentifiersLongerThanMaxLength,
|
|
6152
|
+
createdDocumentId
|
|
6153
|
+
];
|
|
5714
6154
|
const createInternalMigrationProvider = (db) => {
|
|
5715
6155
|
const context = { db };
|
|
5716
6156
|
const umzugProvider = new umzug.Umzug({
|
|
@@ -5936,8 +6376,14 @@ const validateBidirectionalRelations = async (db) => {
|
|
|
5936
6376
|
for (const { relation, invRelation } of invalidLinks) {
|
|
5937
6377
|
const modelMetadata = db.metadata.get(invRelation.target);
|
|
5938
6378
|
const invModelMetadata = db.metadata.get(relation.target);
|
|
5939
|
-
const joinTableName = getJoinTableName(
|
|
5940
|
-
|
|
6379
|
+
const joinTableName = identifiers.getJoinTableName(
|
|
6380
|
+
_.snakeCase(modelMetadata.tableName),
|
|
6381
|
+
_.snakeCase(invRelation.inversedBy)
|
|
6382
|
+
);
|
|
6383
|
+
const inverseJoinTableName = identifiers.getJoinTableName(
|
|
6384
|
+
_.snakeCase(invModelMetadata.tableName),
|
|
6385
|
+
_.snakeCase(relation.inversedBy)
|
|
6386
|
+
);
|
|
5941
6387
|
const joinTableEmpty = await isLinkTableEmpty(db, joinTableName);
|
|
5942
6388
|
const inverseJoinTableEmpty = await isLinkTableEmpty(db, inverseJoinTableName);
|
|
5943
6389
|
if (joinTableEmpty) {
|
|
@@ -5983,7 +6429,7 @@ class Database {
|
|
|
5983
6429
|
};
|
|
5984
6430
|
this.dialect = getDialect(this);
|
|
5985
6431
|
this.dialect.configure();
|
|
5986
|
-
this.metadata = createMetadata();
|
|
6432
|
+
this.metadata = createMetadata([]);
|
|
5987
6433
|
this.connection = createConnection(this.config.connection, {
|
|
5988
6434
|
pool: { afterCreate: afterCreate(this) }
|
|
5989
6435
|
});
|
|
@@ -6060,9 +6506,7 @@ class Database {
|
|
|
6060
6506
|
await this.connection.destroy();
|
|
6061
6507
|
}
|
|
6062
6508
|
}
|
|
6063
|
-
const utils = { identifiers };
|
|
6064
6509
|
exports.Database = Database;
|
|
6065
6510
|
exports.errors = index;
|
|
6066
6511
|
exports.isKnexQuery = isKnexQuery;
|
|
6067
|
-
exports.utils = utils;
|
|
6068
6512
|
//# sourceMappingURL=index.js.map
|