@strapi/database 5.0.0-beta.0 → 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.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fse from "fs-extra";
|
|
3
3
|
import createDebug from "debug";
|
|
4
|
-
import _, { isNil, castArray, prop, omit, isInteger, snakeCase, partition, sumBy, toString, toNumber, isString as isString$1, padCharsEnd, isArray, keys, isPlainObject, isFinite, groupBy, pipe, mapValues, map, isEmpty, maxBy, pick, has, uniqBy, isNull, differenceWith, isEqual, compact, difference, isObject, isNumber as isNumber$1, isUndefined, uniqWith } from "lodash/fp";
|
|
4
|
+
import _, { isNil, castArray, prop, omit, isInteger, snakeCase, partition, sumBy, cloneDeep, toString, toNumber, isString as isString$1, padCharsEnd, isArray, keys, isPlainObject, isFinite, groupBy, pipe, mapValues, map, isEmpty, maxBy, pick, has, uniqBy, isNull, differenceWith, isEqual, compact, difference, isObject, isNumber as isNumber$1, isUndefined, uniqWith } from "lodash/fp";
|
|
5
5
|
import crypto from "crypto";
|
|
6
6
|
import crypto$1 from "node:crypto";
|
|
7
7
|
import * as dateFns from "date-fns";
|
|
@@ -12,6 +12,7 @@ import { Readable } from "stream";
|
|
|
12
12
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
13
13
|
import _$1 from "lodash";
|
|
14
14
|
import { Umzug } from "umzug";
|
|
15
|
+
import { createId } from "@paralleldrive/cuid2";
|
|
15
16
|
import { strict } from "assert";
|
|
16
17
|
import knex from "knex";
|
|
17
18
|
class Dialect {
|
|
@@ -975,7 +976,7 @@ const getDialect = (db) => {
|
|
|
975
976
|
const dialect = new constructor(db, dialectName);
|
|
976
977
|
return dialect;
|
|
977
978
|
};
|
|
978
|
-
const debug$
|
|
979
|
+
const debug$2 = createDebug("strapi::database");
|
|
979
980
|
const createSchemaBuilder = (db) => {
|
|
980
981
|
const helpers2 = createHelpers(db);
|
|
981
982
|
return {
|
|
@@ -1001,12 +1002,12 @@ const createSchemaBuilder = (db) => {
|
|
|
1001
1002
|
*/
|
|
1002
1003
|
async createTables(tables, trx) {
|
|
1003
1004
|
for (const table of tables) {
|
|
1004
|
-
debug$
|
|
1005
|
+
debug$2(`Creating table: ${table.name}`);
|
|
1005
1006
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1006
1007
|
await helpers2.createTable(schemaBuilder, table);
|
|
1007
1008
|
}
|
|
1008
1009
|
for (const table of tables) {
|
|
1009
|
-
debug$
|
|
1010
|
+
debug$2(`Creating table foreign keys: ${table.name}`);
|
|
1010
1011
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1011
1012
|
await helpers2.createTableForeignKeys(schemaBuilder, table);
|
|
1012
1013
|
}
|
|
@@ -1037,18 +1038,18 @@ const createSchemaBuilder = (db) => {
|
|
|
1037
1038
|
await this.createTables(schemaDiff.tables.added, trx);
|
|
1038
1039
|
if (forceMigration) {
|
|
1039
1040
|
for (const table of schemaDiff.tables.removed) {
|
|
1040
|
-
debug$
|
|
1041
|
+
debug$2(`Removing table foreign keys: ${table.name}`);
|
|
1041
1042
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1042
1043
|
await helpers2.dropTableForeignKeys(schemaBuilder, table);
|
|
1043
1044
|
}
|
|
1044
1045
|
for (const table of schemaDiff.tables.removed) {
|
|
1045
|
-
debug$
|
|
1046
|
+
debug$2(`Removing table: ${table.name}`);
|
|
1046
1047
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1047
1048
|
await helpers2.dropTable(schemaBuilder, table);
|
|
1048
1049
|
}
|
|
1049
1050
|
}
|
|
1050
1051
|
for (const table of schemaDiff.tables.updated) {
|
|
1051
|
-
debug$
|
|
1052
|
+
debug$2(`Updating table: ${table.name}`);
|
|
1052
1053
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1053
1054
|
await helpers2.alterTable(schemaBuilder, table);
|
|
1054
1055
|
}
|
|
@@ -1144,27 +1145,27 @@ const createHelpers = (db) => {
|
|
|
1144
1145
|
const alterTable = async (schemaBuilder, table) => {
|
|
1145
1146
|
await schemaBuilder.alterTable(table.name, (tableBuilder) => {
|
|
1146
1147
|
for (const removedIndex of table.indexes.removed) {
|
|
1147
|
-
debug$
|
|
1148
|
+
debug$2(`Dropping index ${removedIndex.name} on ${table.name}`);
|
|
1148
1149
|
dropIndex(tableBuilder, removedIndex);
|
|
1149
1150
|
}
|
|
1150
1151
|
for (const updateddIndex of table.indexes.updated) {
|
|
1151
|
-
debug$
|
|
1152
|
+
debug$2(`Dropping updated index ${updateddIndex.name} on ${table.name}`);
|
|
1152
1153
|
dropIndex(tableBuilder, updateddIndex.object);
|
|
1153
1154
|
}
|
|
1154
1155
|
for (const removedForeignKey of table.foreignKeys.removed) {
|
|
1155
|
-
debug$
|
|
1156
|
+
debug$2(`Dropping foreign key ${removedForeignKey.name} on ${table.name}`);
|
|
1156
1157
|
dropForeignKey(tableBuilder, removedForeignKey);
|
|
1157
1158
|
}
|
|
1158
1159
|
for (const updatedForeignKey of table.foreignKeys.updated) {
|
|
1159
|
-
debug$
|
|
1160
|
+
debug$2(`Dropping updated foreign key ${updatedForeignKey.name} on ${table.name}`);
|
|
1160
1161
|
dropForeignKey(tableBuilder, updatedForeignKey.object);
|
|
1161
1162
|
}
|
|
1162
1163
|
for (const removedColumn of table.columns.removed) {
|
|
1163
|
-
debug$
|
|
1164
|
+
debug$2(`Dropping column ${removedColumn.name} on ${table.name}`);
|
|
1164
1165
|
dropColumn(tableBuilder, removedColumn);
|
|
1165
1166
|
}
|
|
1166
1167
|
for (const updatedColumn of table.columns.updated) {
|
|
1167
|
-
debug$
|
|
1168
|
+
debug$2(`Updating column ${updatedColumn.name} on ${table.name}`);
|
|
1168
1169
|
const { object } = updatedColumn;
|
|
1169
1170
|
if (object.type === "increments") {
|
|
1170
1171
|
createColumn2(tableBuilder, { ...object, type: "integer" }).alter();
|
|
@@ -1173,15 +1174,15 @@ const createHelpers = (db) => {
|
|
|
1173
1174
|
}
|
|
1174
1175
|
}
|
|
1175
1176
|
for (const updatedForeignKey of table.foreignKeys.updated) {
|
|
1176
|
-
debug$
|
|
1177
|
+
debug$2(`Recreating updated foreign key ${updatedForeignKey.name} on ${table.name}`);
|
|
1177
1178
|
createForeignKey(tableBuilder, updatedForeignKey.object);
|
|
1178
1179
|
}
|
|
1179
1180
|
for (const updatedIndex of table.indexes.updated) {
|
|
1180
|
-
debug$
|
|
1181
|
+
debug$2(`Recreating updated index ${updatedIndex.name} on ${table.name}`);
|
|
1181
1182
|
createIndex(tableBuilder, updatedIndex.object);
|
|
1182
1183
|
}
|
|
1183
1184
|
for (const addedColumn of table.columns.added) {
|
|
1184
|
-
debug$
|
|
1185
|
+
debug$2(`Creating column ${addedColumn.name} on ${table.name}`);
|
|
1185
1186
|
if (addedColumn.type === "increments" && !db.dialect.canAddIncrements()) {
|
|
1186
1187
|
tableBuilder.integer(addedColumn.name).unsigned();
|
|
1187
1188
|
tableBuilder.primary([addedColumn.name]);
|
|
@@ -1190,11 +1191,11 @@ const createHelpers = (db) => {
|
|
|
1190
1191
|
}
|
|
1191
1192
|
}
|
|
1192
1193
|
for (const addedForeignKey of table.foreignKeys.added) {
|
|
1193
|
-
debug$
|
|
1194
|
+
debug$2(`Creating foreign keys ${addedForeignKey.name} on ${table.name}`);
|
|
1194
1195
|
createForeignKey(tableBuilder, addedForeignKey);
|
|
1195
1196
|
}
|
|
1196
1197
|
for (const addedIndex of table.indexes.added) {
|
|
1197
|
-
debug$
|
|
1198
|
+
debug$2(`Creating index ${addedIndex.name} on ${table.name}`);
|
|
1198
1199
|
createIndex(tableBuilder, addedIndex);
|
|
1199
1200
|
}
|
|
1200
1201
|
});
|
|
@@ -1613,11 +1614,6 @@ const isScalar = (type) => SCALAR_TYPES.includes(type);
|
|
|
1613
1614
|
const isRelation = (type) => type === "relation";
|
|
1614
1615
|
const isScalarAttribute = (attribute) => isScalar(attribute.type);
|
|
1615
1616
|
const isRelationalAttribute = (attribute) => isRelation(attribute.type);
|
|
1616
|
-
const MAX_DB_IDENTIFIER_LENGTH = 0;
|
|
1617
|
-
const HASH_LENGTH = 5;
|
|
1618
|
-
const HASH_SEPARATOR = "";
|
|
1619
|
-
const IDENTIFIER_SEPARATOR = "_";
|
|
1620
|
-
const MIN_TOKEN_LENGTH = 3;
|
|
1621
1617
|
function createHash(data, len) {
|
|
1622
1618
|
if (!isInteger(len) || len <= 0) {
|
|
1623
1619
|
throw new Error(`createHash length must be a positive integer, received ${len}`);
|
|
@@ -1625,201 +1621,334 @@ function createHash(data, len) {
|
|
|
1625
1621
|
const hash = crypto$1.createHash("shake256", { outputLength: Math.ceil(len / 2) }).update(data);
|
|
1626
1622
|
return hash.digest("hex").substring(0, len);
|
|
1627
1623
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1624
|
+
const IDENTIFIER_MAX_LENGTH = 55;
|
|
1625
|
+
class Identifiers {
|
|
1626
|
+
ID_COLUMN = "id";
|
|
1627
|
+
ORDER_COLUMN = "order";
|
|
1628
|
+
FIELD_COLUMN = "field";
|
|
1629
|
+
HASH_LENGTH = 5;
|
|
1630
|
+
HASH_SEPARATOR = "";
|
|
1631
|
+
// no separator is needed, we will just attach hash directly to shortened name
|
|
1632
|
+
IDENTIFIER_SEPARATOR = "_";
|
|
1633
|
+
MIN_TOKEN_LENGTH = 3;
|
|
1634
|
+
// the min characters required at the beginning of a name part
|
|
1635
|
+
// Fixed compression map for suffixes and prefixes
|
|
1636
|
+
#replacementMap = {
|
|
1637
|
+
links: "lnk",
|
|
1638
|
+
order_inv_fk: "oifk",
|
|
1639
|
+
order: "ord",
|
|
1640
|
+
morphs: "mph",
|
|
1641
|
+
index: "idx",
|
|
1642
|
+
inv_fk: "ifk",
|
|
1643
|
+
order_fk: "ofk",
|
|
1644
|
+
id_column_index: "idix",
|
|
1645
|
+
order_index: "oidx",
|
|
1646
|
+
unique: "uq",
|
|
1647
|
+
primary: "pk"
|
|
1648
|
+
};
|
|
1649
|
+
#options;
|
|
1650
|
+
constructor(options) {
|
|
1651
|
+
this.#options = options;
|
|
1645
1652
|
}
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
function getNameFromTokens(nameTokens, maxLength = MAX_DB_IDENTIFIER_LENGTH) {
|
|
1649
|
-
if (!isInteger(maxLength) || maxLength < 0) {
|
|
1650
|
-
throw new Error("maxLength must be a positive integer or 0 (for unlimited length)");
|
|
1653
|
+
get replacementMap() {
|
|
1654
|
+
return this.#replacementMap;
|
|
1651
1655
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
return fullLengthName;
|
|
1656
|
+
get options() {
|
|
1657
|
+
return this.#options;
|
|
1655
1658
|
}
|
|
1656
|
-
|
|
1657
|
-
(
|
|
1658
|
-
|
|
1659
|
-
);
|
|
1660
|
-
const totalIncompressibleLength = sumBy((token) => token.name.length)(incompressible);
|
|
1661
|
-
const totalSeparatorsLength = nameTokens.length * IDENTIFIER_SEPARATOR.length - 1;
|
|
1662
|
-
const available = maxLength - totalIncompressibleLength - totalSeparatorsLength;
|
|
1663
|
-
const availablePerToken = Math.floor(available / compressible.length);
|
|
1664
|
-
if (totalIncompressibleLength + totalSeparatorsLength > maxLength || availablePerToken < MIN_TOKEN_LENGTH) {
|
|
1665
|
-
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1666
|
-
}
|
|
1667
|
-
let surplus = available % compressible.length;
|
|
1668
|
-
const minHashedLength = HASH_LENGTH + HASH_SEPARATOR.length + MIN_TOKEN_LENGTH;
|
|
1669
|
-
const totalLength = nameTokens.reduce((total, token) => {
|
|
1670
|
-
if (token.compressible) {
|
|
1671
|
-
if (token.name.length < availablePerToken) {
|
|
1672
|
-
return total + token.name.length;
|
|
1673
|
-
}
|
|
1674
|
-
return total + minHashedLength;
|
|
1675
|
-
}
|
|
1676
|
-
return total + token.name.length;
|
|
1677
|
-
}, nameTokens.length * IDENTIFIER_SEPARATOR.length - 1);
|
|
1678
|
-
if (maxLength < totalLength) {
|
|
1679
|
-
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1680
|
-
}
|
|
1681
|
-
let deficits = [];
|
|
1682
|
-
compressible.forEach((token) => {
|
|
1683
|
-
const actualLength = token.name.length;
|
|
1684
|
-
if (actualLength < availablePerToken) {
|
|
1685
|
-
surplus += availablePerToken - actualLength;
|
|
1686
|
-
token.allocatedLength = actualLength;
|
|
1687
|
-
} else {
|
|
1688
|
-
token.allocatedLength = availablePerToken;
|
|
1689
|
-
deficits.push(token);
|
|
1659
|
+
mapshortNames = (name) => {
|
|
1660
|
+
if (name in this.replacementMap) {
|
|
1661
|
+
return this.replacementMap[name];
|
|
1690
1662
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1663
|
+
return void 0;
|
|
1664
|
+
};
|
|
1665
|
+
// Generic name handler that must be used by all helper functions
|
|
1666
|
+
/**
|
|
1667
|
+
* TODO: we should be requiring snake_case inputs for all names here, but we
|
|
1668
|
+
* aren't and it will require some refactoring to make it work. Currently if
|
|
1669
|
+
* we get names 'myModel' and 'my_model' they would be converted to the same
|
|
1670
|
+
* final string my_model which generally works but is not entirely safe
|
|
1671
|
+
* */
|
|
1672
|
+
getName = (names, options) => {
|
|
1673
|
+
const tokens = _.castArray(names).map((name) => {
|
|
1674
|
+
return {
|
|
1675
|
+
name,
|
|
1676
|
+
compressible: true
|
|
1677
|
+
};
|
|
1678
|
+
});
|
|
1679
|
+
if (options?.suffix) {
|
|
1680
|
+
tokens.push({
|
|
1681
|
+
name: options.suffix,
|
|
1682
|
+
compressible: false,
|
|
1683
|
+
shortName: this.mapshortNames(options.suffix)
|
|
1684
|
+
});
|
|
1697
1685
|
}
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
break;
|
|
1686
|
+
if (options?.prefix) {
|
|
1687
|
+
tokens.unshift({
|
|
1688
|
+
name: options.prefix,
|
|
1689
|
+
compressible: false,
|
|
1690
|
+
shortName: this.mapshortNames(options.prefix)
|
|
1691
|
+
});
|
|
1705
1692
|
}
|
|
1706
|
-
|
|
1707
|
-
}
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1693
|
+
return this.getNameFromTokens(tokens);
|
|
1694
|
+
};
|
|
1695
|
+
/**
|
|
1696
|
+
* TABLES
|
|
1697
|
+
*/
|
|
1698
|
+
getTableName = (name, options) => {
|
|
1699
|
+
return this.getName(name, options);
|
|
1700
|
+
};
|
|
1701
|
+
getJoinTableName = (collectionName, attributeName, options) => {
|
|
1702
|
+
return this.getName([collectionName, attributeName], {
|
|
1703
|
+
suffix: "links",
|
|
1704
|
+
...options
|
|
1705
|
+
});
|
|
1706
|
+
};
|
|
1707
|
+
getMorphTableName = (collectionName, attributeName, options) => {
|
|
1708
|
+
return this.getName([snakeCase(collectionName), snakeCase(attributeName)], {
|
|
1709
|
+
suffix: "morphs",
|
|
1710
|
+
...options
|
|
1711
|
+
});
|
|
1712
|
+
};
|
|
1713
|
+
/**
|
|
1714
|
+
* COLUMNS
|
|
1715
|
+
*/
|
|
1716
|
+
getColumnName = (attributeName, options) => {
|
|
1717
|
+
return this.getName(attributeName, options);
|
|
1718
|
+
};
|
|
1719
|
+
getJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1720
|
+
return this.getName(attributeName, { suffix: "id", ...options });
|
|
1721
|
+
};
|
|
1722
|
+
getInverseJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1723
|
+
return this.getName(snakeCase(attributeName), { suffix: "id", prefix: "inv", ...options });
|
|
1724
|
+
};
|
|
1725
|
+
getOrderColumnName = (singularName, options) => {
|
|
1726
|
+
return this.getName(singularName, { suffix: "order", ...options });
|
|
1727
|
+
};
|
|
1728
|
+
getInverseOrderColumnName = (singularName, options) => {
|
|
1729
|
+
return this.getName(singularName, {
|
|
1730
|
+
suffix: "order",
|
|
1731
|
+
prefix: "inv",
|
|
1732
|
+
...options
|
|
1733
|
+
});
|
|
1734
|
+
};
|
|
1735
|
+
/**
|
|
1736
|
+
* Morph Join Tables
|
|
1737
|
+
*/
|
|
1738
|
+
getMorphColumnJoinTableIdName = (singularName, options) => {
|
|
1739
|
+
return this.getName(snakeCase(singularName), { suffix: "id", ...options });
|
|
1740
|
+
};
|
|
1741
|
+
getMorphColumnAttributeIdName = (attributeName, options) => {
|
|
1742
|
+
return this.getName(snakeCase(attributeName), { suffix: "id", ...options });
|
|
1743
|
+
};
|
|
1744
|
+
getMorphColumnTypeName = (attributeName, options) => {
|
|
1745
|
+
return this.getName(snakeCase(attributeName), { suffix: "type", ...options });
|
|
1746
|
+
};
|
|
1747
|
+
/**
|
|
1748
|
+
* INDEXES
|
|
1749
|
+
* Note that these methods are generally used to reference full table names + attribute(s), which
|
|
1750
|
+
* may already be shortened strings rather than individual parts.
|
|
1751
|
+
* That is fine and expected to compress the previously incompressible parts of those strings,
|
|
1752
|
+
* because in these cases the relevant information is the table name and we can't really do
|
|
1753
|
+
* any better; shortening the individual parts again might make it even more confusing.
|
|
1754
|
+
*
|
|
1755
|
+
* So for example, the fk for the table `mytable_myattr4567d_localizations` will become
|
|
1756
|
+
* mytable_myattr4567d_loc63bf2_fk
|
|
1757
|
+
*/
|
|
1758
|
+
// base index types
|
|
1759
|
+
getIndexName = (names, options) => {
|
|
1760
|
+
return this.getName(names, { suffix: "index", ...options });
|
|
1761
|
+
};
|
|
1762
|
+
getFkIndexName = (names, options) => {
|
|
1763
|
+
return this.getName(names, { suffix: "fk", ...options });
|
|
1764
|
+
};
|
|
1765
|
+
getUniqueIndexName = (names, options) => {
|
|
1766
|
+
return this.getName(names, { suffix: "unique", ...options });
|
|
1767
|
+
};
|
|
1768
|
+
getPrimaryIndexName = (names, options) => {
|
|
1769
|
+
return this.getName(names, { suffix: "primary", ...options });
|
|
1770
|
+
};
|
|
1771
|
+
// custom index types
|
|
1772
|
+
getInverseFkIndexName = (names, options) => {
|
|
1773
|
+
return this.getName(names, { suffix: "inv_fk", ...options });
|
|
1774
|
+
};
|
|
1775
|
+
getOrderFkIndexName = (names, options) => {
|
|
1776
|
+
return this.getName(names, { suffix: "order_fk", ...options });
|
|
1777
|
+
};
|
|
1778
|
+
getOrderInverseFkIndexName = (names, options) => {
|
|
1779
|
+
return this.getName(names, { suffix: "order_inv_fk", ...options });
|
|
1780
|
+
};
|
|
1781
|
+
getIdColumnIndexName = (names, options) => {
|
|
1782
|
+
return this.getName(names, { suffix: "id_column_index", ...options });
|
|
1783
|
+
};
|
|
1784
|
+
getOrderIndexName = (names, options) => {
|
|
1785
|
+
return this.getName(names, { suffix: "order_index", ...options });
|
|
1786
|
+
};
|
|
1787
|
+
/**
|
|
1788
|
+
* Generates a string with a max length, appending a hash at the end if necessary to keep it unique
|
|
1789
|
+
*
|
|
1790
|
+
* @example
|
|
1791
|
+
* // if we have strings such as "longstring1" and "longstring2" with a max length of 9,
|
|
1792
|
+
* // we don't want to end up with "longstrin" and "longstrin"
|
|
1793
|
+
* // we want something such as "longs0b23" and "longs953f"
|
|
1794
|
+
* const token1 = generateToken("longstring1", 9); // "longs0b23"
|
|
1795
|
+
* const token2 = generateToken("longstring2", 9); // "longs953f"
|
|
1796
|
+
*
|
|
1797
|
+
* @param name - The base name
|
|
1798
|
+
* @param len - The desired length of the token.
|
|
1799
|
+
* @returns The generated token with hash.
|
|
1800
|
+
* @throws Error if the length is not a positive integer, or if the length is too short for the token.
|
|
1801
|
+
* @internal
|
|
1802
|
+
*/
|
|
1803
|
+
getShortenedName = (name, len) => {
|
|
1804
|
+
if (!isInteger(len) || len <= 0) {
|
|
1805
|
+
throw new Error(`tokenWithHash length must be a positive integer, received ${len}`);
|
|
1711
1806
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1807
|
+
if (name.length <= len) {
|
|
1808
|
+
return name;
|
|
1809
|
+
}
|
|
1810
|
+
if (len < this.MIN_TOKEN_LENGTH + this.HASH_LENGTH) {
|
|
1811
|
+
throw new Error(
|
|
1812
|
+
`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}`
|
|
1813
|
+
);
|
|
1814
|
+
}
|
|
1815
|
+
const availableLength = len - this.HASH_LENGTH - this.HASH_SEPARATOR.length;
|
|
1816
|
+
if (availableLength < this.MIN_TOKEN_LENGTH) {
|
|
1817
|
+
throw new Error(
|
|
1818
|
+
`length for part of identifier minimum is less than min token length (${this.MIN_TOKEN_LENGTH}), received ${len} for token ${name}`
|
|
1819
|
+
);
|
|
1820
|
+
}
|
|
1821
|
+
return `${name.substring(0, availableLength)}${this.HASH_SEPARATOR}${createHash(
|
|
1822
|
+
name,
|
|
1823
|
+
this.HASH_LENGTH
|
|
1824
|
+
)}`;
|
|
1825
|
+
};
|
|
1826
|
+
/**
|
|
1827
|
+
* Constructs a name from an array of name tokens within a specified maximum length. It ensures the final name does not exceed
|
|
1828
|
+
* this limit by selectively compressing tokens marked as compressible. If the name exceeds the maximum length and cannot be
|
|
1829
|
+
* compressed sufficiently, an error is thrown. This function supports dynamic adjustment of token lengths to fit within the
|
|
1830
|
+
* maxLength constraint (that is, it will always make use of all available space), while also ensuring the preservation of
|
|
1831
|
+
* incompressible tokens.
|
|
1832
|
+
* @internal
|
|
1833
|
+
*/
|
|
1834
|
+
getNameFromTokens = (nameTokens) => {
|
|
1835
|
+
const { maxLength } = this.options;
|
|
1836
|
+
if (!isInteger(maxLength) || maxLength < 0) {
|
|
1837
|
+
throw new Error("maxLength must be a positive integer or 0 (for unlimited length)");
|
|
1838
|
+
}
|
|
1839
|
+
const unshortenedName = nameTokens.map((token) => {
|
|
1840
|
+
return token.name;
|
|
1841
|
+
}).join(this.IDENTIFIER_SEPARATOR);
|
|
1842
|
+
if (maxLength === 0) {
|
|
1843
|
+
this.setUnshortenedName(unshortenedName, unshortenedName);
|
|
1844
|
+
return unshortenedName;
|
|
1845
|
+
}
|
|
1846
|
+
const fullLengthName = nameTokens.map((token) => {
|
|
1847
|
+
if (token.compressible) {
|
|
1848
|
+
return token.name;
|
|
1849
|
+
}
|
|
1850
|
+
return token.shortName ?? token.name;
|
|
1851
|
+
}).join(this.IDENTIFIER_SEPARATOR);
|
|
1852
|
+
if (fullLengthName.length <= maxLength) {
|
|
1853
|
+
this.setUnshortenedName(fullLengthName, unshortenedName);
|
|
1854
|
+
return fullLengthName;
|
|
1855
|
+
}
|
|
1856
|
+
const [compressible, incompressible] = partition(
|
|
1857
|
+
(token) => token.compressible,
|
|
1858
|
+
nameTokens
|
|
1717
1859
|
);
|
|
1718
|
-
|
|
1719
|
-
|
|
1860
|
+
const totalIncompressibleLength = sumBy(
|
|
1861
|
+
(token) => token.compressible === false && token.shortName !== void 0 ? token.shortName.length : token.name.length
|
|
1862
|
+
)(incompressible);
|
|
1863
|
+
const totalSeparatorsLength = nameTokens.length * this.IDENTIFIER_SEPARATOR.length - 1;
|
|
1864
|
+
const available = maxLength - totalIncompressibleLength - totalSeparatorsLength;
|
|
1865
|
+
const availablePerToken = Math.floor(available / compressible.length);
|
|
1866
|
+
if (totalIncompressibleLength + totalSeparatorsLength > maxLength || availablePerToken < this.MIN_TOKEN_LENGTH) {
|
|
1867
|
+
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1868
|
+
}
|
|
1869
|
+
let surplus = available % compressible.length;
|
|
1870
|
+
const minHashedLength = this.HASH_LENGTH + this.HASH_SEPARATOR.length + this.MIN_TOKEN_LENGTH;
|
|
1871
|
+
const totalLength = nameTokens.reduce(
|
|
1872
|
+
(total, token) => {
|
|
1873
|
+
if (token.compressible) {
|
|
1874
|
+
if (token.name.length < availablePerToken) {
|
|
1875
|
+
return total + token.name.length;
|
|
1876
|
+
}
|
|
1877
|
+
return total + minHashedLength;
|
|
1878
|
+
}
|
|
1879
|
+
const tokenName = token.shortName ?? token.name;
|
|
1880
|
+
return total + tokenName.length;
|
|
1881
|
+
},
|
|
1882
|
+
nameTokens.length * this.IDENTIFIER_SEPARATOR.length - 1
|
|
1883
|
+
);
|
|
1884
|
+
if (maxLength < totalLength) {
|
|
1885
|
+
throw new Error("Maximum length is too small to accommodate all tokens");
|
|
1886
|
+
}
|
|
1887
|
+
let deficits = [];
|
|
1888
|
+
compressible.forEach((token) => {
|
|
1889
|
+
const actualLength = token.name.length;
|
|
1890
|
+
if (actualLength < availablePerToken) {
|
|
1891
|
+
surplus += availablePerToken - actualLength;
|
|
1892
|
+
token.allocatedLength = actualLength;
|
|
1893
|
+
} else {
|
|
1894
|
+
token.allocatedLength = availablePerToken;
|
|
1895
|
+
deficits.push(token);
|
|
1896
|
+
}
|
|
1897
|
+
});
|
|
1898
|
+
function filterAndIncreaseLength(token) {
|
|
1899
|
+
if (token.allocatedLength < token.name.length && surplus > 0) {
|
|
1900
|
+
token.allocatedLength += 1;
|
|
1901
|
+
surplus -= 1;
|
|
1902
|
+
return token.allocatedLength < token.name.length;
|
|
1903
|
+
}
|
|
1904
|
+
return false;
|
|
1905
|
+
}
|
|
1906
|
+
let previousSurplus = surplus + 1;
|
|
1907
|
+
while (surplus > 0 && deficits.length > 0) {
|
|
1908
|
+
deficits = deficits.filter((token) => filterAndIncreaseLength(token));
|
|
1909
|
+
if (surplus === previousSurplus) {
|
|
1910
|
+
break;
|
|
1911
|
+
}
|
|
1912
|
+
previousSurplus = surplus;
|
|
1913
|
+
}
|
|
1914
|
+
const shortenedName = nameTokens.map((token) => {
|
|
1915
|
+
if (token.compressible && "allocatedLength" in token && token.allocatedLength !== void 0) {
|
|
1916
|
+
return this.getShortenedName(token.name, token.allocatedLength);
|
|
1917
|
+
}
|
|
1918
|
+
if (token.compressible === false && token.shortName) {
|
|
1919
|
+
return token.shortName;
|
|
1920
|
+
}
|
|
1921
|
+
return token.name;
|
|
1922
|
+
}).join(this.IDENTIFIER_SEPARATOR);
|
|
1923
|
+
if (shortenedName.length > maxLength) {
|
|
1924
|
+
throw new Error(
|
|
1925
|
+
`name shortening failed to generate a name of the correct maxLength; name ${shortenedName}`
|
|
1926
|
+
);
|
|
1927
|
+
}
|
|
1928
|
+
this.setUnshortenedName(shortenedName, unshortenedName);
|
|
1929
|
+
return shortenedName;
|
|
1930
|
+
};
|
|
1931
|
+
// We need to be able to find the full-length name for any shortened name, primarily for migration purposes
|
|
1932
|
+
// Therefore we store every name that passes through so we can retrieve the original later
|
|
1933
|
+
nameMap = /* @__PURE__ */ new Map();
|
|
1934
|
+
getUnshortenedName = (shortName) => {
|
|
1935
|
+
return this.nameMap.get(this.serializeKey(shortName)) ?? shortName;
|
|
1936
|
+
};
|
|
1937
|
+
setUnshortenedName = (shortName, fullName) => {
|
|
1938
|
+
if (this.nameMap.get(this.serializeKey(shortName)) && shortName === fullName) {
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
this.nameMap.set(this.serializeKey(shortName), fullName);
|
|
1942
|
+
};
|
|
1943
|
+
serializeKey = (shortName) => {
|
|
1944
|
+
return `${shortName}.${this.options.maxLength}`;
|
|
1945
|
+
};
|
|
1720
1946
|
}
|
|
1721
|
-
const
|
|
1722
|
-
const ORDER_COLUMN = "order";
|
|
1723
|
-
const FIELD_COLUMN = "field";
|
|
1724
|
-
const getName = (names, options) => {
|
|
1725
|
-
const tokens = _.castArray(names).map((name) => {
|
|
1726
|
-
return {
|
|
1727
|
-
name,
|
|
1728
|
-
compressible: true
|
|
1729
|
-
};
|
|
1730
|
-
});
|
|
1731
|
-
if (options?.suffix) {
|
|
1732
|
-
tokens.push({ name: options.suffix, compressible: false });
|
|
1733
|
-
}
|
|
1734
|
-
if (options?.prefix) {
|
|
1735
|
-
tokens.unshift({ name: options.prefix, compressible: false });
|
|
1736
|
-
}
|
|
1737
|
-
const maxLength = options?.maxLength ?? MAX_DB_IDENTIFIER_LENGTH;
|
|
1738
|
-
return getNameFromTokens(tokens, maxLength);
|
|
1739
|
-
};
|
|
1740
|
-
const getTableName = (name, options) => {
|
|
1741
|
-
return getName(name, options);
|
|
1742
|
-
};
|
|
1743
|
-
const getJoinTableName = (collectionName, attributeName, options) => {
|
|
1744
|
-
return getName([collectionName, attributeName], { suffix: "links", ...options });
|
|
1745
|
-
};
|
|
1746
|
-
const getMorphTableName = (collectionName, attributeName, options) => {
|
|
1747
|
-
return getName([collectionName, attributeName], { suffix: "morphs", ...options });
|
|
1748
|
-
};
|
|
1749
|
-
const getColumnName = (attributeName, options) => {
|
|
1750
|
-
return getName(attributeName, options);
|
|
1751
|
-
};
|
|
1752
|
-
const getJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1753
|
-
return getName(attributeName, { suffix: "id", ...options });
|
|
1754
|
-
};
|
|
1755
|
-
const getInverseJoinColumnAttributeIdName = (attributeName, options) => {
|
|
1756
|
-
return getName(attributeName, { suffix: "id", prefix: "inv", ...options });
|
|
1757
|
-
};
|
|
1758
|
-
const getOrderColumnName = (singularName, options) => {
|
|
1759
|
-
return getName(singularName, { suffix: "order", ...options });
|
|
1760
|
-
};
|
|
1761
|
-
const getInverseOrderColumnName = (singularName, options) => {
|
|
1762
|
-
return getName(singularName, { suffix: "order", prefix: "inv", ...options });
|
|
1763
|
-
};
|
|
1764
|
-
const getMorphColumnJoinTableIdName = (singularName, options) => {
|
|
1765
|
-
return getName(singularName, { suffix: "id", ...options });
|
|
1766
|
-
};
|
|
1767
|
-
const getMorphColumnAttributeIdName = (attributeName, options) => {
|
|
1768
|
-
return getName(attributeName, { suffix: "id", ...options });
|
|
1769
|
-
};
|
|
1770
|
-
const getMorphColumnTypeName = (attributeName, options) => {
|
|
1771
|
-
return getName(attributeName, { suffix: "type", ...options });
|
|
1772
|
-
};
|
|
1773
|
-
const getIndexName = (names, options) => {
|
|
1774
|
-
return getName(names, { suffix: "index", ...options });
|
|
1775
|
-
};
|
|
1776
|
-
const getFkIndexName = (names, options) => {
|
|
1777
|
-
return getName(names, { suffix: "fk", ...options });
|
|
1778
|
-
};
|
|
1779
|
-
const getInverseFkIndexName = (names, options) => {
|
|
1780
|
-
return getName(names, { suffix: "inv_fk", ...options });
|
|
1781
|
-
};
|
|
1782
|
-
const getOrderFkIndexName = (names, options) => {
|
|
1783
|
-
return getName(names, { suffix: "order_fk", ...options });
|
|
1784
|
-
};
|
|
1785
|
-
const getOrderInverseFkIndexName = (names, options) => {
|
|
1786
|
-
return getName(names, { suffix: "order_inv_fk", ...options });
|
|
1787
|
-
};
|
|
1788
|
-
const getUniqueIndexName = (names, options) => {
|
|
1789
|
-
return getName(names, { suffix: "unique", ...options });
|
|
1790
|
-
};
|
|
1791
|
-
const getPrimaryIndexName = (names) => {
|
|
1792
|
-
return getName(names, { suffix: "primary" });
|
|
1793
|
-
};
|
|
1794
|
-
const identifiers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1795
|
-
__proto__: null,
|
|
1796
|
-
FIELD_COLUMN,
|
|
1797
|
-
ID_COLUMN,
|
|
1798
|
-
ORDER_COLUMN,
|
|
1799
|
-
getColumnName,
|
|
1800
|
-
getFkIndexName,
|
|
1801
|
-
getIndexName,
|
|
1802
|
-
getInverseFkIndexName,
|
|
1803
|
-
getInverseJoinColumnAttributeIdName,
|
|
1804
|
-
getInverseOrderColumnName,
|
|
1805
|
-
getJoinColumnAttributeIdName,
|
|
1806
|
-
getJoinTableName,
|
|
1807
|
-
getMorphColumnAttributeIdName,
|
|
1808
|
-
getMorphColumnJoinTableIdName,
|
|
1809
|
-
getMorphColumnTypeName,
|
|
1810
|
-
getMorphTableName,
|
|
1811
|
-
getName,
|
|
1812
|
-
getOrderColumnName,
|
|
1813
|
-
getOrderFkIndexName,
|
|
1814
|
-
getOrderInverseFkIndexName,
|
|
1815
|
-
getPrimaryIndexName,
|
|
1816
|
-
getTableName,
|
|
1817
|
-
getUniqueIndexName
|
|
1818
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
1947
|
+
const identifiers = new Identifiers({ maxLength: IDENTIFIER_MAX_LENGTH });
|
|
1819
1948
|
const createColumn = (name, attribute) => {
|
|
1820
1949
|
const { type, args = [], ...opts } = getColumnType(attribute);
|
|
1821
1950
|
return {
|
|
1822
|
-
name,
|
|
1951
|
+
name: identifiers.getName(name),
|
|
1823
1952
|
type,
|
|
1824
1953
|
args,
|
|
1825
1954
|
defaultTo: null,
|
|
@@ -1841,8 +1970,8 @@ const createTable = (meta) => {
|
|
|
1841
1970
|
if (attribute.type === "relation") {
|
|
1842
1971
|
if ("morphColumn" in attribute && attribute.morphColumn && attribute.owner) {
|
|
1843
1972
|
const { idColumn, typeColumn } = attribute.morphColumn;
|
|
1844
|
-
const idColumnName = getName(idColumn.name);
|
|
1845
|
-
const typeColumnName = getName(typeColumn.name);
|
|
1973
|
+
const idColumnName = identifiers.getName(idColumn.name);
|
|
1974
|
+
const typeColumnName = identifiers.getName(typeColumn.name);
|
|
1846
1975
|
table.columns.push(
|
|
1847
1976
|
createColumn(idColumnName, {
|
|
1848
1977
|
type: "integer",
|
|
@@ -1859,7 +1988,7 @@ const createTable = (meta) => {
|
|
|
1859
1988
|
referencedTable,
|
|
1860
1989
|
columnType = "integer"
|
|
1861
1990
|
} = attribute.joinColumn;
|
|
1862
|
-
const columnName = getName(columnNameFull);
|
|
1991
|
+
const columnName = identifiers.getName(columnNameFull);
|
|
1863
1992
|
const column = createColumn(columnName, {
|
|
1864
1993
|
// TODO: find the column type automatically, or allow passing all the column params
|
|
1865
1994
|
type: columnType,
|
|
@@ -1868,7 +1997,7 @@ const createTable = (meta) => {
|
|
|
1868
1997
|
}
|
|
1869
1998
|
});
|
|
1870
1999
|
table.columns.push(column);
|
|
1871
|
-
const fkName = getFkIndexName([table.name, columnName]);
|
|
2000
|
+
const fkName = identifiers.getFkIndexName([table.name, columnName]);
|
|
1872
2001
|
table.foreignKeys.push({
|
|
1873
2002
|
name: fkName,
|
|
1874
2003
|
columns: [column.name],
|
|
@@ -1883,19 +2012,19 @@ const createTable = (meta) => {
|
|
|
1883
2012
|
});
|
|
1884
2013
|
}
|
|
1885
2014
|
} else if (isScalarAttribute(attribute)) {
|
|
1886
|
-
const columnName = getName(attribute.columnName || key);
|
|
2015
|
+
const columnName = identifiers.getName(attribute.columnName || key);
|
|
1887
2016
|
const column = createColumn(columnName, attribute);
|
|
1888
2017
|
if (column.unique) {
|
|
1889
2018
|
table.indexes.push({
|
|
1890
2019
|
type: "unique",
|
|
1891
|
-
name: getUniqueIndexName([table.name, column.name]),
|
|
2020
|
+
name: identifiers.getUniqueIndexName([table.name, column.name]),
|
|
1892
2021
|
columns: [columnName]
|
|
1893
2022
|
});
|
|
1894
2023
|
}
|
|
1895
2024
|
if (column.primary) {
|
|
1896
2025
|
table.indexes.push({
|
|
1897
2026
|
type: "primary",
|
|
1898
|
-
name: getPrimaryIndexName([table.name, column.name]),
|
|
2027
|
+
name: identifiers.getPrimaryIndexName([table.name, column.name]),
|
|
1899
2028
|
columns: [columnName]
|
|
1900
2029
|
});
|
|
1901
2030
|
}
|
|
@@ -1995,12 +2124,13 @@ const metadataToSchema = (metadata) => {
|
|
|
1995
2124
|
});
|
|
1996
2125
|
return schema;
|
|
1997
2126
|
};
|
|
1998
|
-
const debug = createDebug("strapi::database");
|
|
2127
|
+
const debug$1 = createDebug("strapi::database");
|
|
1999
2128
|
const createSchemaProvider = (db) => {
|
|
2000
2129
|
const state = {};
|
|
2001
2130
|
return {
|
|
2002
2131
|
get schema() {
|
|
2003
2132
|
if (!state.schema) {
|
|
2133
|
+
debug$1("Converting metadata to database schema");
|
|
2004
2134
|
state.schema = metadataToSchema(db.metadata);
|
|
2005
2135
|
}
|
|
2006
2136
|
return state.schema;
|
|
@@ -2012,7 +2142,7 @@ const createSchemaProvider = (db) => {
|
|
|
2012
2142
|
* Drops the database schema
|
|
2013
2143
|
*/
|
|
2014
2144
|
async drop() {
|
|
2015
|
-
debug("Dropping database schema");
|
|
2145
|
+
debug$1("Dropping database schema");
|
|
2016
2146
|
const DBSchema = await db.dialect.schemaInspector.getSchema();
|
|
2017
2147
|
await this.builder.dropSchema(DBSchema);
|
|
2018
2148
|
},
|
|
@@ -2020,19 +2150,19 @@ const createSchemaProvider = (db) => {
|
|
|
2020
2150
|
* Creates the database schema
|
|
2021
2151
|
*/
|
|
2022
2152
|
async create() {
|
|
2023
|
-
debug("Created database schema");
|
|
2153
|
+
debug$1("Created database schema");
|
|
2024
2154
|
await this.builder.createSchema(this.schema);
|
|
2025
2155
|
},
|
|
2026
2156
|
/**
|
|
2027
2157
|
* Resets the database schema
|
|
2028
2158
|
*/
|
|
2029
2159
|
async reset() {
|
|
2030
|
-
debug("Resetting database schema");
|
|
2160
|
+
debug$1("Resetting database schema");
|
|
2031
2161
|
await this.drop();
|
|
2032
2162
|
await this.create();
|
|
2033
2163
|
},
|
|
2034
2164
|
async syncSchema() {
|
|
2035
|
-
debug("Synchronizing database schema");
|
|
2165
|
+
debug$1("Synchronizing database schema");
|
|
2036
2166
|
const DBSchema = await db.dialect.schemaInspector.getSchema();
|
|
2037
2167
|
const { status, diff } = await this.schemaDiff.diff(DBSchema, this.schema);
|
|
2038
2168
|
if (status === "CHANGED") {
|
|
@@ -2045,28 +2175,28 @@ const createSchemaProvider = (db) => {
|
|
|
2045
2175
|
// TODO: Allow keeping extra indexes / extra tables / extra columns (globally or on a per table basis)
|
|
2046
2176
|
async sync() {
|
|
2047
2177
|
if (await db.migrations.shouldRun()) {
|
|
2048
|
-
debug("Found migrations to run");
|
|
2178
|
+
debug$1("Found migrations to run");
|
|
2049
2179
|
await db.migrations.up();
|
|
2050
2180
|
return this.syncSchema();
|
|
2051
2181
|
}
|
|
2052
2182
|
const oldSchema = await this.schemaStorage.read();
|
|
2053
2183
|
if (!oldSchema) {
|
|
2054
|
-
debug("Schema not persisted yet");
|
|
2184
|
+
debug$1("Schema not persisted yet");
|
|
2055
2185
|
return this.syncSchema();
|
|
2056
2186
|
}
|
|
2057
2187
|
const { hash: oldHash } = oldSchema;
|
|
2058
2188
|
const hash = await this.schemaStorage.hashSchema(this.schema);
|
|
2059
2189
|
if (oldHash !== hash) {
|
|
2060
|
-
debug("Schema changed");
|
|
2190
|
+
debug$1("Schema changed");
|
|
2061
2191
|
return this.syncSchema();
|
|
2062
2192
|
}
|
|
2063
|
-
debug("Schema unchanged");
|
|
2193
|
+
debug$1("Schema unchanged");
|
|
2064
2194
|
}
|
|
2065
2195
|
};
|
|
2066
2196
|
};
|
|
2067
|
-
const ID = ID_COLUMN;
|
|
2068
|
-
const ORDER = ORDER_COLUMN;
|
|
2069
|
-
const FIELD = FIELD_COLUMN;
|
|
2197
|
+
const ID = identifiers.ID_COLUMN;
|
|
2198
|
+
const ORDER = identifiers.ORDER_COLUMN;
|
|
2199
|
+
const FIELD = identifiers.FIELD_COLUMN;
|
|
2070
2200
|
const hasInversedBy = (attr) => "inversedBy" in attr;
|
|
2071
2201
|
const hasMappedBy = (attr) => "mappedBy" in attr;
|
|
2072
2202
|
const isOneToAny = (attribute) => ["oneToOne", "oneToMany"].includes(attribute.relation);
|
|
@@ -2087,7 +2217,7 @@ const createOneToOne = (attributeName, attribute, meta, metadata) => {
|
|
|
2087
2217
|
meta
|
|
2088
2218
|
});
|
|
2089
2219
|
} else {
|
|
2090
|
-
|
|
2220
|
+
createJoinColumn(metadata, {
|
|
2091
2221
|
attribute,
|
|
2092
2222
|
attributeName,
|
|
2093
2223
|
meta
|
|
@@ -2117,7 +2247,7 @@ const createManyToOne = (attributeName, attribute, meta, metadata) => {
|
|
|
2117
2247
|
meta
|
|
2118
2248
|
});
|
|
2119
2249
|
} else {
|
|
2120
|
-
|
|
2250
|
+
createJoinColumn(metadata, {
|
|
2121
2251
|
attribute,
|
|
2122
2252
|
attributeName,
|
|
2123
2253
|
meta
|
|
@@ -2134,8 +2264,8 @@ const createManyToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2134
2264
|
}
|
|
2135
2265
|
};
|
|
2136
2266
|
const createMorphToOne = (attributeName, attribute) => {
|
|
2137
|
-
const idColumnName = getJoinColumnAttributeIdName("target");
|
|
2138
|
-
const typeColumnName = getMorphColumnTypeName("target");
|
|
2267
|
+
const idColumnName = identifiers.getJoinColumnAttributeIdName("target");
|
|
2268
|
+
const typeColumnName = identifiers.getMorphColumnTypeName("target");
|
|
2139
2269
|
if ("morphColumn" in attribute && attribute.morphColumn) {
|
|
2140
2270
|
return;
|
|
2141
2271
|
}
|
|
@@ -2157,11 +2287,11 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2157
2287
|
if ("joinTable" in attribute && attribute.joinTable) {
|
|
2158
2288
|
return;
|
|
2159
2289
|
}
|
|
2160
|
-
const joinTableName = getMorphTableName(meta.tableName, attributeName);
|
|
2161
|
-
const joinColumnName = getMorphColumnJoinTableIdName(meta.singularName);
|
|
2162
|
-
const idColumnName = getMorphColumnAttributeIdName(attributeName);
|
|
2163
|
-
const typeColumnName = getMorphColumnTypeName(attributeName);
|
|
2164
|
-
const fkIndexName = getFkIndexName(joinTableName);
|
|
2290
|
+
const joinTableName = identifiers.getMorphTableName(meta.tableName, attributeName);
|
|
2291
|
+
const joinColumnName = identifiers.getMorphColumnJoinTableIdName(snakeCase(meta.singularName));
|
|
2292
|
+
const idColumnName = identifiers.getMorphColumnAttributeIdName(attributeName);
|
|
2293
|
+
const typeColumnName = identifiers.getMorphColumnTypeName(attributeName);
|
|
2294
|
+
const fkIndexName = identifiers.getFkIndexName(joinTableName);
|
|
2165
2295
|
metadata.add({
|
|
2166
2296
|
singularName: joinTableName,
|
|
2167
2297
|
uid: joinTableName,
|
|
@@ -2174,7 +2304,9 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2174
2304
|
type: "integer",
|
|
2175
2305
|
column: {
|
|
2176
2306
|
unsigned: true
|
|
2177
|
-
}
|
|
2307
|
+
},
|
|
2308
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2309
|
+
columnName: joinColumnName
|
|
2178
2310
|
},
|
|
2179
2311
|
[idColumnName]: {
|
|
2180
2312
|
type: "integer",
|
|
@@ -2201,11 +2333,11 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2201
2333
|
columns: [joinColumnName]
|
|
2202
2334
|
},
|
|
2203
2335
|
{
|
|
2204
|
-
name:
|
|
2336
|
+
name: identifiers.getOrderIndexName(joinTableName),
|
|
2205
2337
|
columns: [ORDER]
|
|
2206
2338
|
},
|
|
2207
2339
|
{
|
|
2208
|
-
name:
|
|
2340
|
+
name: identifiers.getIdColumnIndexName(joinTableName),
|
|
2209
2341
|
columns: [idColumnName]
|
|
2210
2342
|
}
|
|
2211
2343
|
],
|
|
@@ -2214,7 +2346,7 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2214
2346
|
name: fkIndexName,
|
|
2215
2347
|
columns: [joinColumnName],
|
|
2216
2348
|
referencedColumns: [ID],
|
|
2217
|
-
referencedTable:
|
|
2349
|
+
referencedTable: meta.tableName,
|
|
2218
2350
|
onDelete: "CASCADE"
|
|
2219
2351
|
}
|
|
2220
2352
|
],
|
|
@@ -2261,12 +2393,12 @@ const createMorphMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2261
2393
|
throw new Error(`Morph target attribute not found. Looking for ${attribute.morphBy}`);
|
|
2262
2394
|
}
|
|
2263
2395
|
};
|
|
2264
|
-
const
|
|
2396
|
+
const createJoinColumn = (metadata, { attribute, attributeName }) => {
|
|
2265
2397
|
const targetMeta = metadata.get(attribute.target);
|
|
2266
2398
|
if (!targetMeta) {
|
|
2267
2399
|
throw new Error(`Unknown target ${attribute.target}`);
|
|
2268
2400
|
}
|
|
2269
|
-
const joinColumnName =
|
|
2401
|
+
const joinColumnName = identifiers.getJoinColumnAttributeIdName(snakeCase(attributeName));
|
|
2270
2402
|
const joinColumn = {
|
|
2271
2403
|
name: joinColumnName,
|
|
2272
2404
|
referencedColumn: ID,
|
|
@@ -2281,7 +2413,7 @@ const createJoinColum = (metadata, { attribute, attributeName }) => {
|
|
|
2281
2413
|
Object.assign(inverseAttribute, {
|
|
2282
2414
|
joinColumn: {
|
|
2283
2415
|
name: joinColumn.referencedColumn,
|
|
2284
|
-
referencedColumn:
|
|
2416
|
+
referencedColumn: joinColumnName
|
|
2285
2417
|
}
|
|
2286
2418
|
});
|
|
2287
2419
|
}
|
|
@@ -2294,21 +2426,26 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2294
2426
|
if ("joinTable" in attribute && attribute.joinTable) {
|
|
2295
2427
|
return;
|
|
2296
2428
|
}
|
|
2297
|
-
const joinTableName = getJoinTableName(
|
|
2298
|
-
|
|
2299
|
-
|
|
2429
|
+
const joinTableName = identifiers.getJoinTableName(
|
|
2430
|
+
snakeCase(meta.tableName),
|
|
2431
|
+
snakeCase(attributeName)
|
|
2432
|
+
);
|
|
2433
|
+
const joinColumnName = identifiers.getJoinColumnAttributeIdName(snakeCase(meta.singularName));
|
|
2434
|
+
let inverseJoinColumnName = identifiers.getJoinColumnAttributeIdName(
|
|
2435
|
+
snakeCase(targetMeta.singularName)
|
|
2436
|
+
);
|
|
2300
2437
|
if (joinColumnName === inverseJoinColumnName) {
|
|
2301
|
-
inverseJoinColumnName = getInverseJoinColumnAttributeIdName(
|
|
2302
|
-
targetMeta.singularName
|
|
2438
|
+
inverseJoinColumnName = identifiers.getInverseJoinColumnAttributeIdName(
|
|
2439
|
+
snakeCase(targetMeta.singularName)
|
|
2303
2440
|
);
|
|
2304
2441
|
}
|
|
2305
|
-
const orderColumnName = getOrderColumnName(targetMeta.singularName);
|
|
2306
|
-
let inverseOrderColumnName = getOrderColumnName(meta.singularName);
|
|
2442
|
+
const orderColumnName = identifiers.getOrderColumnName(snakeCase(targetMeta.singularName));
|
|
2443
|
+
let inverseOrderColumnName = identifiers.getOrderColumnName(snakeCase(meta.singularName));
|
|
2307
2444
|
if (attribute.relation === "manyToMany" && orderColumnName === inverseOrderColumnName) {
|
|
2308
|
-
inverseOrderColumnName = getInverseOrderColumnName(meta.singularName);
|
|
2445
|
+
inverseOrderColumnName = identifiers.getInverseOrderColumnName(snakeCase(meta.singularName));
|
|
2309
2446
|
}
|
|
2310
|
-
const fkIndexName = getFkIndexName(joinTableName);
|
|
2311
|
-
const invFkIndexName = getInverseFkIndexName(joinTableName);
|
|
2447
|
+
const fkIndexName = identifiers.getFkIndexName(joinTableName);
|
|
2448
|
+
const invFkIndexName = identifiers.getInverseFkIndexName(joinTableName);
|
|
2312
2449
|
const metadataSchema = {
|
|
2313
2450
|
singularName: joinTableName,
|
|
2314
2451
|
uid: joinTableName,
|
|
@@ -2321,13 +2458,17 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2321
2458
|
type: "integer",
|
|
2322
2459
|
column: {
|
|
2323
2460
|
unsigned: true
|
|
2324
|
-
}
|
|
2461
|
+
},
|
|
2462
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2463
|
+
columnName: joinColumnName
|
|
2325
2464
|
},
|
|
2326
2465
|
[inverseJoinColumnName]: {
|
|
2327
2466
|
type: "integer",
|
|
2328
2467
|
column: {
|
|
2329
2468
|
unsigned: true
|
|
2330
|
-
}
|
|
2469
|
+
},
|
|
2470
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2471
|
+
columnName: inverseJoinColumnName
|
|
2331
2472
|
}
|
|
2332
2473
|
// TODO: add extra pivot attributes -> user should use an intermediate entity
|
|
2333
2474
|
},
|
|
@@ -2341,7 +2482,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2341
2482
|
columns: [inverseJoinColumnName]
|
|
2342
2483
|
},
|
|
2343
2484
|
{
|
|
2344
|
-
name: getUniqueIndexName(joinTableName),
|
|
2485
|
+
name: identifiers.getUniqueIndexName(joinTableName),
|
|
2345
2486
|
columns: [joinColumnName, inverseJoinColumnName],
|
|
2346
2487
|
type: "unique"
|
|
2347
2488
|
}
|
|
@@ -2352,7 +2493,6 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2352
2493
|
columns: [joinColumnName],
|
|
2353
2494
|
referencedColumns: [ID],
|
|
2354
2495
|
referencedTable: meta.tableName,
|
|
2355
|
-
// TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
|
|
2356
2496
|
onDelete: "CASCADE"
|
|
2357
2497
|
},
|
|
2358
2498
|
{
|
|
@@ -2360,7 +2500,6 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2360
2500
|
columns: [inverseJoinColumnName],
|
|
2361
2501
|
referencedColumns: [ID],
|
|
2362
2502
|
referencedTable: targetMeta.tableName,
|
|
2363
|
-
// TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
|
|
2364
2503
|
onDelete: "CASCADE"
|
|
2365
2504
|
}
|
|
2366
2505
|
],
|
|
@@ -2390,8 +2529,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2390
2529
|
}
|
|
2391
2530
|
};
|
|
2392
2531
|
metadataSchema.indexes.push({
|
|
2393
|
-
name: getOrderFkIndexName(joinTableName),
|
|
2394
|
-
// TODO: should we send joinTableName as parts?
|
|
2532
|
+
name: identifiers.getOrderFkIndexName(joinTableName),
|
|
2395
2533
|
columns: [orderColumnName]
|
|
2396
2534
|
});
|
|
2397
2535
|
joinTable.orderColumnName = orderColumnName;
|
|
@@ -2406,7 +2544,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2406
2544
|
}
|
|
2407
2545
|
};
|
|
2408
2546
|
metadataSchema.indexes.push({
|
|
2409
|
-
name: getOrderInverseFkIndexName(joinTableName),
|
|
2547
|
+
name: identifiers.getOrderInverseFkIndexName(joinTableName),
|
|
2410
2548
|
columns: [inverseOrderColumnName]
|
|
2411
2549
|
});
|
|
2412
2550
|
joinTable.inverseOrderColumnName = inverseOrderColumnName;
|
|
@@ -2464,6 +2602,12 @@ const createRelation = (attributeName, attribute, meta, metadata) => {
|
|
|
2464
2602
|
}
|
|
2465
2603
|
};
|
|
2466
2604
|
class Metadata extends Map {
|
|
2605
|
+
// TODO: we expose the global identifiers in this way so that in the future we can instantiate our own
|
|
2606
|
+
// However, it should NOT be done until all the methods used by metadata can be part of this metadata object
|
|
2607
|
+
// and access this one; currently they all access the global identifiers directly.
|
|
2608
|
+
get identifiers() {
|
|
2609
|
+
return identifiers;
|
|
2610
|
+
}
|
|
2467
2611
|
get(key) {
|
|
2468
2612
|
if (!super.has(key)) {
|
|
2469
2613
|
throw new Error(`Metadata for "${key}" not found`);
|
|
@@ -2487,10 +2631,12 @@ class Metadata extends Map {
|
|
|
2487
2631
|
seenTables.set(meta.tableName, true);
|
|
2488
2632
|
}
|
|
2489
2633
|
}
|
|
2490
|
-
loadModels(models
|
|
2491
|
-
for (const model of
|
|
2634
|
+
loadModels(models) {
|
|
2635
|
+
for (const model of cloneDeep(models ?? [])) {
|
|
2636
|
+
const tableName = identifiers.getTableName(model.tableName);
|
|
2492
2637
|
this.add({
|
|
2493
2638
|
...model,
|
|
2639
|
+
tableName,
|
|
2494
2640
|
attributes: {
|
|
2495
2641
|
...model.attributes
|
|
2496
2642
|
},
|
|
@@ -2531,10 +2677,13 @@ class Metadata extends Map {
|
|
|
2531
2677
|
}
|
|
2532
2678
|
}
|
|
2533
2679
|
const createAttribute = (attributeName, attribute) => {
|
|
2534
|
-
|
|
2680
|
+
if ("columnName" in attribute && attribute.columnName) {
|
|
2681
|
+
return;
|
|
2682
|
+
}
|
|
2683
|
+
const columnName = identifiers.getColumnName(snakeCase(attributeName));
|
|
2535
2684
|
Object.assign(attribute, { columnName });
|
|
2536
2685
|
};
|
|
2537
|
-
const createMetadata = (models
|
|
2686
|
+
const createMetadata = (models) => {
|
|
2538
2687
|
const metadata = new Metadata();
|
|
2539
2688
|
if (models.length) {
|
|
2540
2689
|
metadata.loadModels(models);
|
|
@@ -3029,10 +3178,13 @@ const XtoOne = async (input, ctx) => {
|
|
|
3029
3178
|
rootTable: qb2.alias,
|
|
3030
3179
|
on: joinTable.on
|
|
3031
3180
|
}).select([joinColAlias, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3032
|
-
const map22 = rows2.reduce(
|
|
3033
|
-
map3
|
|
3034
|
-
|
|
3035
|
-
|
|
3181
|
+
const map22 = rows2.reduce(
|
|
3182
|
+
(map3, row) => {
|
|
3183
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3184
|
+
return map3;
|
|
3185
|
+
},
|
|
3186
|
+
{}
|
|
3187
|
+
);
|
|
3036
3188
|
results.forEach((result) => {
|
|
3037
3189
|
result[attributeName] = map22[result[referencedColumnName]] || { count: 0 };
|
|
3038
3190
|
});
|
|
@@ -3105,10 +3257,13 @@ const oneToMany = async (input, ctx) => {
|
|
|
3105
3257
|
rootTable: qb2.alias,
|
|
3106
3258
|
on: joinTable.on
|
|
3107
3259
|
}).select([joinColAlias, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3108
|
-
const map22 = rows2.reduce(
|
|
3109
|
-
map3
|
|
3110
|
-
|
|
3111
|
-
|
|
3260
|
+
const map22 = rows2.reduce(
|
|
3261
|
+
(map3, row) => {
|
|
3262
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3263
|
+
return map3;
|
|
3264
|
+
},
|
|
3265
|
+
{}
|
|
3266
|
+
);
|
|
3112
3267
|
results.forEach((result) => {
|
|
3113
3268
|
result[attributeName] = map22[result[referencedColumnName]] || { count: 0 };
|
|
3114
3269
|
});
|
|
@@ -3162,10 +3317,13 @@ const manyToMany = async (input, ctx) => {
|
|
|
3162
3317
|
rootTable: populateQb.alias,
|
|
3163
3318
|
on: joinTable.on
|
|
3164
3319
|
}).select([joinColAlias, populateQb.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3165
|
-
const map22 = rows2.reduce(
|
|
3166
|
-
map3
|
|
3167
|
-
|
|
3168
|
-
|
|
3320
|
+
const map22 = rows2.reduce(
|
|
3321
|
+
(map3, row) => {
|
|
3322
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3323
|
+
return map3;
|
|
3324
|
+
},
|
|
3325
|
+
{}
|
|
3326
|
+
);
|
|
3169
3327
|
results.forEach((result) => {
|
|
3170
3328
|
result[attributeName] = map22[result[referencedColumnName]] || { count: 0 };
|
|
3171
3329
|
});
|
|
@@ -4676,21 +4834,24 @@ const sortConnectArray = (connectArr, initialArr = [], strictSort = true) => {
|
|
|
4676
4834
|
(acc, rel) => ({ ...acc, [rel.id]: true }),
|
|
4677
4835
|
{}
|
|
4678
4836
|
);
|
|
4679
|
-
const mappedRelations = connectArr.reduce(
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4837
|
+
const mappedRelations = connectArr.reduce(
|
|
4838
|
+
(mapper, relation) => {
|
|
4839
|
+
const adjacentRelId = relation.position?.before || relation.position?.after;
|
|
4840
|
+
if (!adjacentRelId || !relationInInitialArray[adjacentRelId] && !mapper[adjacentRelId]) {
|
|
4841
|
+
needsSorting = true;
|
|
4842
|
+
}
|
|
4843
|
+
if (mapper[relation.id]) {
|
|
4844
|
+
throw new InvalidRelationError(
|
|
4845
|
+
`The relation with id ${relation.id} is already connected. You cannot connect the same relation twice.`
|
|
4846
|
+
);
|
|
4847
|
+
}
|
|
4848
|
+
return {
|
|
4849
|
+
[relation.id]: { ...relation, computed: false },
|
|
4850
|
+
...mapper
|
|
4851
|
+
};
|
|
4852
|
+
},
|
|
4853
|
+
{}
|
|
4854
|
+
);
|
|
4694
4855
|
if (!needsSorting)
|
|
4695
4856
|
return connectArr;
|
|
4696
4857
|
const computeRelation = (relation, relationsSeenInBranch) => {
|
|
@@ -4798,14 +4959,17 @@ const relationsOrderer = (initArr, idColumn, orderColumn, strict2) => {
|
|
|
4798
4959
|
* Get a map between the relation id and its order
|
|
4799
4960
|
*/
|
|
4800
4961
|
getOrderMap() {
|
|
4801
|
-
return _$1(computedRelations).groupBy("order").reduce(
|
|
4802
|
-
|
|
4962
|
+
return _$1(computedRelations).groupBy("order").reduce(
|
|
4963
|
+
(acc, relations) => {
|
|
4964
|
+
if (relations[0]?.init)
|
|
4965
|
+
return acc;
|
|
4966
|
+
relations.forEach((relation, idx) => {
|
|
4967
|
+
acc[relation.id] = Math.floor(relation.order) + (idx + 1) / (relations.length + 1);
|
|
4968
|
+
});
|
|
4803
4969
|
return acc;
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
return acc;
|
|
4808
|
-
}, {});
|
|
4970
|
+
},
|
|
4971
|
+
{}
|
|
4972
|
+
);
|
|
4809
4973
|
}
|
|
4810
4974
|
};
|
|
4811
4975
|
};
|
|
@@ -5559,10 +5723,13 @@ const createEntityManager = (db) => {
|
|
|
5559
5723
|
const entry = await this.findOne(uid, {
|
|
5560
5724
|
select: ["id"],
|
|
5561
5725
|
where: { id: entity.id },
|
|
5562
|
-
populate: fieldsArr.reduce(
|
|
5563
|
-
acc
|
|
5564
|
-
|
|
5565
|
-
|
|
5726
|
+
populate: fieldsArr.reduce(
|
|
5727
|
+
(acc, field) => {
|
|
5728
|
+
acc[field] = populate || true;
|
|
5729
|
+
return acc;
|
|
5730
|
+
},
|
|
5731
|
+
{}
|
|
5732
|
+
)
|
|
5566
5733
|
});
|
|
5567
5734
|
if (!entry) {
|
|
5568
5735
|
return null;
|
|
@@ -5678,7 +5845,280 @@ const createUserMigrationProvider = (db) => {
|
|
|
5678
5845
|
}
|
|
5679
5846
|
};
|
|
5680
5847
|
};
|
|
5681
|
-
const
|
|
5848
|
+
const QUERIES = {
|
|
5849
|
+
async postgres(knex2, params) {
|
|
5850
|
+
const res = await knex2.raw(
|
|
5851
|
+
`
|
|
5852
|
+
SELECT :tableName:.id as id, string_agg(DISTINCT :inverseJoinColumn:::character varying, ',') as other_ids
|
|
5853
|
+
FROM :tableName:
|
|
5854
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5855
|
+
WHERE document_id IS NULL
|
|
5856
|
+
GROUP BY :tableName:.id, :joinColumn:
|
|
5857
|
+
LIMIT 1;
|
|
5858
|
+
`,
|
|
5859
|
+
params
|
|
5860
|
+
);
|
|
5861
|
+
return res.rows;
|
|
5862
|
+
},
|
|
5863
|
+
async mysql(knex2, params) {
|
|
5864
|
+
const [res] = await knex2.raw(
|
|
5865
|
+
`
|
|
5866
|
+
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids
|
|
5867
|
+
FROM :tableName:
|
|
5868
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5869
|
+
WHERE document_id IS NULL
|
|
5870
|
+
GROUP BY :tableName:.id, :joinColumn:
|
|
5871
|
+
LIMIT 1;
|
|
5872
|
+
`,
|
|
5873
|
+
params
|
|
5874
|
+
);
|
|
5875
|
+
return res;
|
|
5876
|
+
},
|
|
5877
|
+
async sqlite(knex2, params) {
|
|
5878
|
+
return knex2.raw(
|
|
5879
|
+
`
|
|
5880
|
+
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids
|
|
5881
|
+
FROM :tableName:
|
|
5882
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5883
|
+
WHERE document_id IS NULL
|
|
5884
|
+
GROUP BY :joinColumn:
|
|
5885
|
+
LIMIT 1;
|
|
5886
|
+
`,
|
|
5887
|
+
params
|
|
5888
|
+
);
|
|
5889
|
+
}
|
|
5890
|
+
};
|
|
5891
|
+
const getNextIdsToCreateDocumentId = async (db, knex2, {
|
|
5892
|
+
joinColumn,
|
|
5893
|
+
inverseJoinColumn,
|
|
5894
|
+
tableName,
|
|
5895
|
+
joinTableName
|
|
5896
|
+
}) => {
|
|
5897
|
+
const res = await QUERIES[db.dialect.client](knex2, {
|
|
5898
|
+
joinColumn,
|
|
5899
|
+
inverseJoinColumn,
|
|
5900
|
+
tableName,
|
|
5901
|
+
joinTableName
|
|
5902
|
+
});
|
|
5903
|
+
if (res.length > 0) {
|
|
5904
|
+
const row = res[0];
|
|
5905
|
+
const otherIds = row.other_ids ? row.other_ids.split(",").map((v) => parseInt(v, 10)) : [];
|
|
5906
|
+
return [row.id, ...otherIds];
|
|
5907
|
+
}
|
|
5908
|
+
return [];
|
|
5909
|
+
};
|
|
5910
|
+
const migrateDocumentIdsWithLocalizations = async (db, knex2, meta) => {
|
|
5911
|
+
const singularName = meta.singularName.toLowerCase();
|
|
5912
|
+
const joinColumn = identifiers.getJoinColumnAttributeIdName(singularName);
|
|
5913
|
+
const inverseJoinColumn = identifiers.getInverseJoinColumnAttributeIdName(singularName);
|
|
5914
|
+
let ids;
|
|
5915
|
+
do {
|
|
5916
|
+
ids = await getNextIdsToCreateDocumentId(db, knex2, {
|
|
5917
|
+
joinColumn,
|
|
5918
|
+
inverseJoinColumn,
|
|
5919
|
+
tableName: meta.tableName,
|
|
5920
|
+
joinTableName: identifiers.getJoinTableName(meta.tableName, `localizations`)
|
|
5921
|
+
});
|
|
5922
|
+
if (ids.length > 0) {
|
|
5923
|
+
await knex2(meta.tableName).update({ document_id: createId() }).whereIn("id", ids);
|
|
5924
|
+
}
|
|
5925
|
+
} while (ids.length > 0);
|
|
5926
|
+
};
|
|
5927
|
+
const migrationDocumentIds = async (db, knex2, meta) => {
|
|
5928
|
+
let run = true;
|
|
5929
|
+
do {
|
|
5930
|
+
const updatedRows = await knex2(meta.tableName).update({ document_id: createId() }).whereIn("id", (builder) => {
|
|
5931
|
+
return builder.whereNull("document_id").select("id").limit(1);
|
|
5932
|
+
});
|
|
5933
|
+
if (updatedRows <= 0) {
|
|
5934
|
+
run = false;
|
|
5935
|
+
}
|
|
5936
|
+
} while (run);
|
|
5937
|
+
};
|
|
5938
|
+
const createDocumentIdColumn = async (knex2, tableName) => {
|
|
5939
|
+
await knex2.schema.alterTable(tableName, (table) => {
|
|
5940
|
+
table.string("document_id");
|
|
5941
|
+
});
|
|
5942
|
+
};
|
|
5943
|
+
const hasLocalizationsJoinTable = async (knex2, tableName) => {
|
|
5944
|
+
const joinTableName = identifiers.getJoinTableName(tableName, "localizations");
|
|
5945
|
+
return knex2.schema.hasTable(joinTableName);
|
|
5946
|
+
};
|
|
5947
|
+
const createdDocumentId = {
|
|
5948
|
+
name: "5.0.0-02-created-document-id",
|
|
5949
|
+
async up(knex2, db) {
|
|
5950
|
+
for (const meta of db.metadata.values()) {
|
|
5951
|
+
const hasTable = await knex2.schema.hasTable(meta.tableName);
|
|
5952
|
+
if (!hasTable) {
|
|
5953
|
+
continue;
|
|
5954
|
+
}
|
|
5955
|
+
if ("documentId" in meta.attributes) {
|
|
5956
|
+
const hasDocumentIdColumn = await knex2.schema.hasColumn(meta.tableName, "document_id");
|
|
5957
|
+
if (hasDocumentIdColumn) {
|
|
5958
|
+
continue;
|
|
5959
|
+
}
|
|
5960
|
+
await createDocumentIdColumn(knex2, meta.tableName);
|
|
5961
|
+
if (await hasLocalizationsJoinTable(knex2, meta.tableName)) {
|
|
5962
|
+
await migrateDocumentIdsWithLocalizations(db, knex2, meta);
|
|
5963
|
+
} else {
|
|
5964
|
+
await migrationDocumentIds(db, knex2, meta);
|
|
5965
|
+
}
|
|
5966
|
+
}
|
|
5967
|
+
}
|
|
5968
|
+
},
|
|
5969
|
+
async down() {
|
|
5970
|
+
throw new Error("not implemented");
|
|
5971
|
+
}
|
|
5972
|
+
};
|
|
5973
|
+
const debug = createDebug("strapi::database::migration");
|
|
5974
|
+
const renameIdentifiersLongerThanMaxLength = {
|
|
5975
|
+
name: "5.0.0-rename-identifiers-longer-than-max-length",
|
|
5976
|
+
async up(knex2, db) {
|
|
5977
|
+
const md = db.metadata;
|
|
5978
|
+
const diffs = findDiffs(md);
|
|
5979
|
+
for (const indexDiff of diffs.indexes) {
|
|
5980
|
+
await renameIndex(knex2, db, indexDiff);
|
|
5981
|
+
}
|
|
5982
|
+
for (const columnDiff of diffs.columns) {
|
|
5983
|
+
const { full, short } = columnDiff;
|
|
5984
|
+
const tableName = full.tableName;
|
|
5985
|
+
const hasTable = await knex2.schema.hasTable(tableName);
|
|
5986
|
+
if (hasTable) {
|
|
5987
|
+
const hasColumn = await knex2.schema.hasColumn(tableName, full.columnName);
|
|
5988
|
+
if (hasColumn) {
|
|
5989
|
+
await knex2.schema.alterTable(tableName, async (table) => {
|
|
5990
|
+
debug(`renaming column ${full.columnName} to ${short.columnName}`);
|
|
5991
|
+
table.renameColumn(full.columnName, short.columnName);
|
|
5992
|
+
});
|
|
5993
|
+
}
|
|
5994
|
+
}
|
|
5995
|
+
}
|
|
5996
|
+
for (const tableDiff of diffs.tables) {
|
|
5997
|
+
const hasTable = await knex2.schema.hasTable(tableDiff.full.tableName);
|
|
5998
|
+
if (hasTable) {
|
|
5999
|
+
debug(`renaming table ${tableDiff.full.tableName} to ${tableDiff.short.tableName}`);
|
|
6000
|
+
await knex2.schema.renameTable(tableDiff.full.tableName, tableDiff.short.tableName);
|
|
6001
|
+
}
|
|
6002
|
+
}
|
|
6003
|
+
},
|
|
6004
|
+
async down() {
|
|
6005
|
+
throw new Error("not implemented");
|
|
6006
|
+
}
|
|
6007
|
+
};
|
|
6008
|
+
const renameIndex = async (knex2, db, diff) => {
|
|
6009
|
+
const client = db.config.connection.client;
|
|
6010
|
+
const short = diff.short;
|
|
6011
|
+
const full = diff.full;
|
|
6012
|
+
if (full.indexName === short.indexName) {
|
|
6013
|
+
debug(`not renaming index ${full.indexName} because name hasn't changed`);
|
|
6014
|
+
return;
|
|
6015
|
+
}
|
|
6016
|
+
if (short.indexName.includes("_lnk_") || full.indexName.includes("_lnk_") || short.indexName.endsWith("fk") || full.indexName.endsWith("fk")) {
|
|
6017
|
+
return;
|
|
6018
|
+
}
|
|
6019
|
+
debug(`renaming index from ${full.indexName} to ${short.indexName}`);
|
|
6020
|
+
try {
|
|
6021
|
+
await knex2.transaction(async (trx) => {
|
|
6022
|
+
if (client === "mysql" || client === "mariadb") {
|
|
6023
|
+
await knex2.raw(
|
|
6024
|
+
`ALTER TABLE \`${full.tableName}\` RENAME INDEX \`${full.indexName}\` TO \`${short.indexName}\``
|
|
6025
|
+
).transacting(trx);
|
|
6026
|
+
} else if (client === "pg" || client === "postgres") {
|
|
6027
|
+
await knex2.raw(`ALTER INDEX "${full.indexName}" RENAME TO "${short.indexName}"`).transacting(trx);
|
|
6028
|
+
} else if (client === "sqlite" || client === "better") {
|
|
6029
|
+
} else {
|
|
6030
|
+
debug("No db client name matches, not creating index");
|
|
6031
|
+
}
|
|
6032
|
+
});
|
|
6033
|
+
} catch (err) {
|
|
6034
|
+
debug(`error creating index: ${JSON.stringify(err)}`);
|
|
6035
|
+
}
|
|
6036
|
+
};
|
|
6037
|
+
const findDiffs = (shortMap) => {
|
|
6038
|
+
const diffs = {
|
|
6039
|
+
tables: [],
|
|
6040
|
+
columns: [],
|
|
6041
|
+
indexes: []
|
|
6042
|
+
};
|
|
6043
|
+
const shortArr = Array.from(shortMap.entries());
|
|
6044
|
+
shortArr.forEach(([, shortObj], index2) => {
|
|
6045
|
+
const fullTableName = identifiers.getUnshortenedName(shortObj.tableName);
|
|
6046
|
+
if (!fullTableName) {
|
|
6047
|
+
throw new Error(`Missing full table name for ${shortObj.tableName}`);
|
|
6048
|
+
}
|
|
6049
|
+
if (shortObj.tableName !== fullTableName) {
|
|
6050
|
+
diffs.tables.push({
|
|
6051
|
+
full: {
|
|
6052
|
+
index: index2,
|
|
6053
|
+
key: "tableName",
|
|
6054
|
+
tableName: fullTableName
|
|
6055
|
+
},
|
|
6056
|
+
short: {
|
|
6057
|
+
index: index2,
|
|
6058
|
+
key: "tableName",
|
|
6059
|
+
tableName: shortObj.tableName
|
|
6060
|
+
}
|
|
6061
|
+
});
|
|
6062
|
+
}
|
|
6063
|
+
for (const attrKey in shortObj.attributes) {
|
|
6064
|
+
if (shortObj.attributes[attrKey].type === "relation") {
|
|
6065
|
+
continue;
|
|
6066
|
+
}
|
|
6067
|
+
const attr = shortObj.attributes[attrKey];
|
|
6068
|
+
const shortColumnName = attr.columnName;
|
|
6069
|
+
const longColumnName = identifiers.getUnshortenedName(shortColumnName);
|
|
6070
|
+
if (!shortColumnName || !longColumnName) {
|
|
6071
|
+
throw new Error(`missing column name(s) for attribute ${JSON.stringify(attr, null, 2)}`);
|
|
6072
|
+
}
|
|
6073
|
+
if (shortColumnName && longColumnName && shortColumnName !== longColumnName) {
|
|
6074
|
+
diffs.columns.push({
|
|
6075
|
+
short: {
|
|
6076
|
+
index: index2,
|
|
6077
|
+
tableName: fullTableName,
|
|
6078
|
+
// NOTE: this means that we must rename columns before tables
|
|
6079
|
+
key: `attributes.${attrKey}`,
|
|
6080
|
+
columnName: shortColumnName
|
|
6081
|
+
},
|
|
6082
|
+
full: {
|
|
6083
|
+
index: index2,
|
|
6084
|
+
tableName: fullTableName,
|
|
6085
|
+
key: `attributes.${attrKey}`,
|
|
6086
|
+
columnName: longColumnName
|
|
6087
|
+
}
|
|
6088
|
+
});
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
for (const attrKey in shortObj.indexes) {
|
|
6092
|
+
const shortIndexName = shortObj.indexes[attrKey].name;
|
|
6093
|
+
const longIndexName = identifiers.getUnshortenedName(shortIndexName);
|
|
6094
|
+
if (!longIndexName) {
|
|
6095
|
+
throw new Error(`Missing full index name for ${shortIndexName}`);
|
|
6096
|
+
}
|
|
6097
|
+
if (shortIndexName && longIndexName && shortIndexName !== longIndexName) {
|
|
6098
|
+
diffs.indexes.push({
|
|
6099
|
+
short: {
|
|
6100
|
+
index: index2,
|
|
6101
|
+
tableName: fullTableName,
|
|
6102
|
+
// NOTE: this means that we must rename columns before tables
|
|
6103
|
+
key: `indexes.${attrKey}`,
|
|
6104
|
+
indexName: shortIndexName
|
|
6105
|
+
},
|
|
6106
|
+
full: {
|
|
6107
|
+
index: index2,
|
|
6108
|
+
tableName: fullTableName,
|
|
6109
|
+
key: `indexes.${attrKey}`,
|
|
6110
|
+
indexName: longIndexName
|
|
6111
|
+
}
|
|
6112
|
+
});
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
6115
|
+
});
|
|
6116
|
+
return diffs;
|
|
6117
|
+
};
|
|
6118
|
+
const internalMigrations = [
|
|
6119
|
+
renameIdentifiersLongerThanMaxLength,
|
|
6120
|
+
createdDocumentId
|
|
6121
|
+
];
|
|
5682
6122
|
const createInternalMigrationProvider = (db) => {
|
|
5683
6123
|
const context = { db };
|
|
5684
6124
|
const umzugProvider = new Umzug({
|
|
@@ -5904,8 +6344,14 @@ const validateBidirectionalRelations = async (db) => {
|
|
|
5904
6344
|
for (const { relation, invRelation } of invalidLinks) {
|
|
5905
6345
|
const modelMetadata = db.metadata.get(invRelation.target);
|
|
5906
6346
|
const invModelMetadata = db.metadata.get(relation.target);
|
|
5907
|
-
const joinTableName = getJoinTableName(
|
|
5908
|
-
|
|
6347
|
+
const joinTableName = identifiers.getJoinTableName(
|
|
6348
|
+
snakeCase(modelMetadata.tableName),
|
|
6349
|
+
snakeCase(invRelation.inversedBy)
|
|
6350
|
+
);
|
|
6351
|
+
const inverseJoinTableName = identifiers.getJoinTableName(
|
|
6352
|
+
snakeCase(invModelMetadata.tableName),
|
|
6353
|
+
snakeCase(relation.inversedBy)
|
|
6354
|
+
);
|
|
5909
6355
|
const joinTableEmpty = await isLinkTableEmpty(db, joinTableName);
|
|
5910
6356
|
const inverseJoinTableEmpty = await isLinkTableEmpty(db, inverseJoinTableName);
|
|
5911
6357
|
if (joinTableEmpty) {
|
|
@@ -5951,7 +6397,7 @@ class Database {
|
|
|
5951
6397
|
};
|
|
5952
6398
|
this.dialect = getDialect(this);
|
|
5953
6399
|
this.dialect.configure();
|
|
5954
|
-
this.metadata = createMetadata();
|
|
6400
|
+
this.metadata = createMetadata([]);
|
|
5955
6401
|
this.connection = createConnection(this.config.connection, {
|
|
5956
6402
|
pool: { afterCreate: afterCreate(this) }
|
|
5957
6403
|
});
|
|
@@ -6028,11 +6474,9 @@ class Database {
|
|
|
6028
6474
|
await this.connection.destroy();
|
|
6029
6475
|
}
|
|
6030
6476
|
}
|
|
6031
|
-
const utils = { identifiers };
|
|
6032
6477
|
export {
|
|
6033
6478
|
Database,
|
|
6034
6479
|
index as errors,
|
|
6035
|
-
isKnexQuery
|
|
6036
|
-
utils
|
|
6480
|
+
isKnexQuery
|
|
6037
6481
|
};
|
|
6038
6482
|
//# sourceMappingURL=index.mjs.map
|