@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.
Files changed (35) hide show
  1. package/dist/entity-manager/index.d.ts.map +1 -1
  2. package/dist/entity-manager/relations-orderer.d.ts.map +1 -1
  3. package/dist/index.d.ts +3 -6
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +757 -313
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +752 -308
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/lifecycles/types.d.ts +1 -0
  10. package/dist/lifecycles/types.d.ts.map +1 -1
  11. package/dist/metadata/index.d.ts +1 -1
  12. package/dist/metadata/index.d.ts.map +1 -1
  13. package/dist/metadata/metadata.d.ts +2 -1
  14. package/dist/metadata/metadata.d.ts.map +1 -1
  15. package/dist/metadata/relations.d.ts.map +1 -1
  16. package/dist/migrations/internal-migrations/5.0.0-01-convert-identifiers-long-than-max-length.d.ts +3 -0
  17. package/dist/migrations/internal-migrations/5.0.0-01-convert-identifiers-long-than-max-length.d.ts.map +1 -0
  18. package/dist/migrations/internal-migrations/5.0.0-02-document-id.d.ts +3 -0
  19. package/dist/migrations/internal-migrations/5.0.0-02-document-id.d.ts.map +1 -0
  20. package/dist/migrations/internal-migrations/index.d.ts.map +1 -1
  21. package/dist/query/helpers/populate/apply.d.ts.map +1 -1
  22. package/dist/schema/index.d.ts +0 -3
  23. package/dist/schema/index.d.ts.map +1 -1
  24. package/dist/types/index.d.ts +3 -0
  25. package/dist/types/index.d.ts.map +1 -1
  26. package/dist/utils/identifiers/hash.d.ts +31 -0
  27. package/dist/utils/identifiers/hash.d.ts.map +1 -0
  28. package/dist/utils/identifiers/index.d.ts +107 -47
  29. package/dist/utils/identifiers/index.d.ts.map +1 -1
  30. package/dist/utils/identifiers/types.d.ts +27 -0
  31. package/dist/utils/identifiers/types.d.ts.map +1 -0
  32. package/dist/validations/relations/bidirectional.d.ts.map +1 -1
  33. package/package.json +7 -6
  34. package/dist/utils/identifiers/shortener.d.ts +0 -73
  35. 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$1 = createDebug("strapi::database");
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$1(`Creating table: ${table.name}`);
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$1(`Creating table foreign keys: ${table.name}`);
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$1(`Removing table foreign keys: ${table.name}`);
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$1(`Removing table: ${table.name}`);
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$1(`Updating table: ${table.name}`);
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$1(`Dropping index ${removedIndex.name}`);
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$1(`Dropping updated index ${updateddIndex.name}`);
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$1(`Dropping foreign key ${removedForeignKey.name}`);
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$1(`Dropping updated foreign key ${updatedForeignKey.name}`);
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$1(`Dropping column ${removedColumn.name}`);
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$1(`Updating column ${updatedColumn.name}`);
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$1(`Recreating updated foreign key ${updatedForeignKey.name}`);
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$1(`Recreating updated index ${updatedIndex.name}`);
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$1(`Creating column ${addedColumn.name}`);
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$1(`Creating foreign keys ${addedForeignKey.name}`);
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$1(`Creating index ${addedIndex.name}`);
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
- function getShortenedName(name, len) {
1629
- if (!isInteger(len) || len <= 0) {
1630
- throw new Error(`tokenWithHash length must be a positive integer, received ${len}`);
1631
- }
1632
- if (name.length <= len) {
1633
- return name;
1634
- }
1635
- if (len < MIN_TOKEN_LENGTH + HASH_LENGTH) {
1636
- throw new Error(
1637
- `length for part of identifier too short, minimum is hash length (${HASH_LENGTH}) plus min token length (${MIN_TOKEN_LENGTH}), received ${len} for token ${name}`
1638
- );
1639
- }
1640
- const availableLength = len - HASH_LENGTH - HASH_SEPARATOR.length;
1641
- if (availableLength < MIN_TOKEN_LENGTH) {
1642
- throw new Error(
1643
- `length for part of identifier minimum is less than min token length (${MIN_TOKEN_LENGTH}), received ${len} for token ${name}`
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
- return `${name.substring(0, availableLength)}${HASH_SEPARATOR}${createHash(name, HASH_LENGTH)}`;
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
- const fullLengthName = nameTokens.map((token) => snakeCase(token.name)).join(IDENTIFIER_SEPARATOR);
1653
- if (fullLengthName.length <= maxLength || maxLength === 0) {
1654
- return fullLengthName;
1656
+ get options() {
1657
+ return this.#options;
1655
1658
  }
1656
- const [compressible, incompressible] = partition(
1657
- (token) => token.compressible,
1658
- nameTokens
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
- function filterAndIncreaseLength(token) {
1693
- if (token.allocatedLength < token.name.length && surplus > 0) {
1694
- token.allocatedLength += 1;
1695
- surplus -= 1;
1696
- return token.allocatedLength < token.name.length;
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
- return false;
1699
- }
1700
- let previousSurplus = surplus + 1;
1701
- while (surplus > 0 && deficits.length > 0) {
1702
- deficits = deficits.filter((token) => filterAndIncreaseLength(token));
1703
- if (surplus === previousSurplus) {
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
- previousSurplus = surplus;
1707
- }
1708
- const shortenedName = nameTokens.map((token) => {
1709
- if (token.compressible && "allocatedLength" in token && token.allocatedLength !== void 0) {
1710
- return getShortenedName(token.name, token.allocatedLength);
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
- return token.name;
1713
- }).join(IDENTIFIER_SEPARATOR);
1714
- if (shortenedName.length > maxLength) {
1715
- throw new Error(
1716
- `name shortening failed to generate a name of the correct maxLength; name ${shortenedName}`
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
- return shortenedName;
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 ID_COLUMN = "id";
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
- createJoinColum(metadata, {
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
- createJoinColum(metadata, {
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: `${joinTableName}_order_index`,
2336
+ name: identifiers.getOrderIndexName(joinTableName),
2205
2337
  columns: [ORDER]
2206
2338
  },
2207
2339
  {
2208
- name: `${joinTableName}_id_column_index`,
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: getTableName(meta.tableName),
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 createJoinColum = (metadata, { attribute, attributeName }) => {
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 = _.snakeCase(`${attributeName}_id`);
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: joinColumn.name
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(meta.tableName, attributeName);
2298
- const joinColumnName = getJoinColumnAttributeIdName(meta.singularName);
2299
- let inverseJoinColumnName = getJoinColumnAttributeIdName(targetMeta.singularName);
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 _.cloneDeep(models)) {
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
- const columnName = getColumnName(attributeName);
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((map3, row) => {
3033
- map3[row[joinColumnName]] = { count: Number(row.count) };
3034
- return map3;
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((map3, row) => {
3109
- map3[row[joinColumnName]] = { count: Number(row.count) };
3110
- return map3;
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((map3, row) => {
3166
- map3[row[joinColumnName]] = { count: Number(row.count) };
3167
- return map3;
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((mapper, relation) => {
4680
- const adjacentRelId = relation.position?.before || relation.position?.after;
4681
- if (!adjacentRelId || !relationInInitialArray[adjacentRelId] && !mapper[adjacentRelId]) {
4682
- needsSorting = true;
4683
- }
4684
- if (mapper[relation.id]) {
4685
- throw new InvalidRelationError(
4686
- `The relation with id ${relation.id} is already connected. You cannot connect the same relation twice.`
4687
- );
4688
- }
4689
- return {
4690
- [relation.id]: { ...relation, computed: false },
4691
- ...mapper
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((acc, relations) => {
4802
- if (relations[0]?.init)
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
- relations.forEach((relation, idx) => {
4805
- acc[relation.id] = Math.floor(relation.order) + (idx + 1) / (relations.length + 1);
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((acc, field) => {
5563
- acc[field] = populate || true;
5564
- return acc;
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 internalMigrations = [];
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(modelMetadata.tableName, invRelation.inversedBy);
5908
- const inverseJoinTableName = getJoinTableName(invModelMetadata.tableName, relation.inversedBy);
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