@strapi/database 5.0.0-beta.1 → 5.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entity-manager/index.d.ts.map +1 -1
- package/dist/entity-manager/relations-orderer.d.ts.map +1 -1
- package/dist/index.d.ts +3 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +829 -324
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +827 -322
- package/dist/index.mjs.map +1 -1
- package/dist/lifecycles/types.d.ts +1 -0
- package/dist/lifecycles/types.d.ts.map +1 -1
- package/dist/metadata/index.d.ts +1 -1
- package/dist/metadata/index.d.ts.map +1 -1
- package/dist/metadata/metadata.d.ts +2 -1
- package/dist/metadata/metadata.d.ts.map +1 -1
- package/dist/metadata/relations.d.ts.map +1 -1
- package/dist/migrations/internal-migrations/5.0.0-01-convert-identifiers-long-than-max-length.d.ts +3 -0
- package/dist/migrations/internal-migrations/5.0.0-01-convert-identifiers-long-than-max-length.d.ts.map +1 -0
- package/dist/migrations/internal-migrations/5.0.0-02-document-id.d.ts +3 -0
- package/dist/migrations/internal-migrations/5.0.0-02-document-id.d.ts.map +1 -0
- package/dist/migrations/internal-migrations/index.d.ts.map +1 -1
- package/dist/query/helpers/join.d.ts.map +1 -1
- package/dist/query/helpers/populate/apply.d.ts.map +1 -1
- package/dist/query/helpers/where.d.ts +1 -0
- package/dist/query/helpers/where.d.ts.map +1 -1
- package/dist/schema/index.d.ts +0 -3
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/identifiers/hash.d.ts +31 -0
- package/dist/utils/identifiers/hash.d.ts.map +1 -0
- package/dist/utils/identifiers/index.d.ts +107 -47
- package/dist/utils/identifiers/index.d.ts.map +1 -1
- package/dist/utils/identifiers/types.d.ts +27 -0
- package/dist/utils/identifiers/types.d.ts.map +1 -0
- package/dist/validations/relations/bidirectional.d.ts.map +1 -1
- package/package.json +7 -6
- package/dist/utils/identifiers/shortener.d.ts +0 -73
- package/dist/utils/identifiers/shortener.d.ts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fse from "fs-extra";
|
|
3
3
|
import createDebug from "debug";
|
|
4
|
-
import _, { isNil, castArray, prop, omit, isInteger, snakeCase, partition, sumBy, toString, toNumber, isString as isString$1, padCharsEnd, isArray,
|
|
4
|
+
import _, { isNil, castArray, prop, omit, isInteger, snakeCase, partition, sumBy, cloneDeep, toString, toNumber, isString as isString$1, padCharsEnd, isArray, isPlainObject, isFinite, groupBy, pipe, mapValues, map, isEmpty, maxBy, pick, has, uniqBy, isNull, differenceWith, isEqual, compact, difference, isObject, isNumber as isNumber$1, isUndefined, uniqWith } from "lodash/fp";
|
|
5
5
|
import crypto from "crypto";
|
|
6
6
|
import crypto$1 from "node:crypto";
|
|
7
7
|
import * as dateFns from "date-fns";
|
|
8
|
-
import { isOperatorOfType } from "@strapi/utils";
|
|
8
|
+
import { isOperatorOfType, isOperator } from "@strapi/utils";
|
|
9
9
|
import KnexBuilder from "knex/lib/query/querybuilder";
|
|
10
10
|
import KnexRaw from "knex/lib/raw";
|
|
11
11
|
import { Readable } from "stream";
|
|
12
12
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
13
13
|
import _$1 from "lodash";
|
|
14
14
|
import { Umzug } from "umzug";
|
|
15
|
+
import { createId } from "@paralleldrive/cuid2";
|
|
15
16
|
import { strict } from "assert";
|
|
16
17
|
import knex from "knex";
|
|
17
18
|
class Dialect {
|
|
@@ -975,7 +976,7 @@ const getDialect = (db) => {
|
|
|
975
976
|
const dialect = new constructor(db, dialectName);
|
|
976
977
|
return dialect;
|
|
977
978
|
};
|
|
978
|
-
const debug$
|
|
979
|
+
const debug$2 = createDebug("strapi::database");
|
|
979
980
|
const createSchemaBuilder = (db) => {
|
|
980
981
|
const helpers2 = createHelpers(db);
|
|
981
982
|
return {
|
|
@@ -1001,12 +1002,12 @@ const createSchemaBuilder = (db) => {
|
|
|
1001
1002
|
*/
|
|
1002
1003
|
async createTables(tables, trx) {
|
|
1003
1004
|
for (const table of tables) {
|
|
1004
|
-
debug$
|
|
1005
|
+
debug$2(`Creating table: ${table.name}`);
|
|
1005
1006
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1006
1007
|
await helpers2.createTable(schemaBuilder, table);
|
|
1007
1008
|
}
|
|
1008
1009
|
for (const table of tables) {
|
|
1009
|
-
debug$
|
|
1010
|
+
debug$2(`Creating table foreign keys: ${table.name}`);
|
|
1010
1011
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1011
1012
|
await helpers2.createTableForeignKeys(schemaBuilder, table);
|
|
1012
1013
|
}
|
|
@@ -1037,18 +1038,18 @@ const createSchemaBuilder = (db) => {
|
|
|
1037
1038
|
await this.createTables(schemaDiff.tables.added, trx);
|
|
1038
1039
|
if (forceMigration) {
|
|
1039
1040
|
for (const table of schemaDiff.tables.removed) {
|
|
1040
|
-
debug$
|
|
1041
|
+
debug$2(`Removing table foreign keys: ${table.name}`);
|
|
1041
1042
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1042
1043
|
await helpers2.dropTableForeignKeys(schemaBuilder, table);
|
|
1043
1044
|
}
|
|
1044
1045
|
for (const table of schemaDiff.tables.removed) {
|
|
1045
|
-
debug$
|
|
1046
|
+
debug$2(`Removing table: ${table.name}`);
|
|
1046
1047
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1047
1048
|
await helpers2.dropTable(schemaBuilder, table);
|
|
1048
1049
|
}
|
|
1049
1050
|
}
|
|
1050
1051
|
for (const table of schemaDiff.tables.updated) {
|
|
1051
|
-
debug$
|
|
1052
|
+
debug$2(`Updating table: ${table.name}`);
|
|
1052
1053
|
const schemaBuilder = this.getSchemaBuilder(trx);
|
|
1053
1054
|
await helpers2.alterTable(schemaBuilder, table);
|
|
1054
1055
|
}
|
|
@@ -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,15 +2264,11 @@ 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");
|
|
2139
|
-
if ("morphColumn" in attribute && attribute.morphColumn) {
|
|
2140
|
-
return;
|
|
2141
|
-
}
|
|
2267
|
+
const idColumnName = identifiers.getJoinColumnAttributeIdName("target");
|
|
2268
|
+
const typeColumnName = identifiers.getMorphColumnTypeName("target");
|
|
2142
2269
|
Object.assign(attribute, {
|
|
2143
2270
|
owner: true,
|
|
2144
|
-
morphColumn: {
|
|
2145
|
-
// TODO: add referenced column
|
|
2271
|
+
morphColumn: attribute.morphColumn ?? {
|
|
2146
2272
|
typeColumn: {
|
|
2147
2273
|
name: typeColumnName
|
|
2148
2274
|
},
|
|
@@ -2157,11 +2283,11 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2157
2283
|
if ("joinTable" in attribute && attribute.joinTable) {
|
|
2158
2284
|
return;
|
|
2159
2285
|
}
|
|
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);
|
|
2286
|
+
const joinTableName = identifiers.getMorphTableName(meta.tableName, attributeName);
|
|
2287
|
+
const joinColumnName = identifiers.getMorphColumnJoinTableIdName(snakeCase(meta.singularName));
|
|
2288
|
+
const idColumnName = identifiers.getMorphColumnAttributeIdName(attributeName);
|
|
2289
|
+
const typeColumnName = identifiers.getMorphColumnTypeName(attributeName);
|
|
2290
|
+
const fkIndexName = identifiers.getFkIndexName(joinTableName);
|
|
2165
2291
|
metadata.add({
|
|
2166
2292
|
singularName: joinTableName,
|
|
2167
2293
|
uid: joinTableName,
|
|
@@ -2174,7 +2300,9 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2174
2300
|
type: "integer",
|
|
2175
2301
|
column: {
|
|
2176
2302
|
unsigned: true
|
|
2177
|
-
}
|
|
2303
|
+
},
|
|
2304
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2305
|
+
columnName: joinColumnName
|
|
2178
2306
|
},
|
|
2179
2307
|
[idColumnName]: {
|
|
2180
2308
|
type: "integer",
|
|
@@ -2201,11 +2329,11 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2201
2329
|
columns: [joinColumnName]
|
|
2202
2330
|
},
|
|
2203
2331
|
{
|
|
2204
|
-
name:
|
|
2332
|
+
name: identifiers.getOrderIndexName(joinTableName),
|
|
2205
2333
|
columns: [ORDER]
|
|
2206
2334
|
},
|
|
2207
2335
|
{
|
|
2208
|
-
name:
|
|
2336
|
+
name: identifiers.getIdColumnIndexName(joinTableName),
|
|
2209
2337
|
columns: [idColumnName]
|
|
2210
2338
|
}
|
|
2211
2339
|
],
|
|
@@ -2214,7 +2342,7 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2214
2342
|
name: fkIndexName,
|
|
2215
2343
|
columns: [joinColumnName],
|
|
2216
2344
|
referencedColumns: [ID],
|
|
2217
|
-
referencedTable:
|
|
2345
|
+
referencedTable: meta.tableName,
|
|
2218
2346
|
onDelete: "CASCADE"
|
|
2219
2347
|
}
|
|
2220
2348
|
],
|
|
@@ -2261,12 +2389,12 @@ const createMorphMany = (attributeName, attribute, meta, metadata) => {
|
|
|
2261
2389
|
throw new Error(`Morph target attribute not found. Looking for ${attribute.morphBy}`);
|
|
2262
2390
|
}
|
|
2263
2391
|
};
|
|
2264
|
-
const
|
|
2392
|
+
const createJoinColumn = (metadata, { attribute, attributeName }) => {
|
|
2265
2393
|
const targetMeta = metadata.get(attribute.target);
|
|
2266
2394
|
if (!targetMeta) {
|
|
2267
2395
|
throw new Error(`Unknown target ${attribute.target}`);
|
|
2268
2396
|
}
|
|
2269
|
-
const joinColumnName =
|
|
2397
|
+
const joinColumnName = identifiers.getJoinColumnAttributeIdName(snakeCase(attributeName));
|
|
2270
2398
|
const joinColumn = {
|
|
2271
2399
|
name: joinColumnName,
|
|
2272
2400
|
referencedColumn: ID,
|
|
@@ -2281,7 +2409,7 @@ const createJoinColum = (metadata, { attribute, attributeName }) => {
|
|
|
2281
2409
|
Object.assign(inverseAttribute, {
|
|
2282
2410
|
joinColumn: {
|
|
2283
2411
|
name: joinColumn.referencedColumn,
|
|
2284
|
-
referencedColumn:
|
|
2412
|
+
referencedColumn: joinColumnName
|
|
2285
2413
|
}
|
|
2286
2414
|
});
|
|
2287
2415
|
}
|
|
@@ -2294,21 +2422,26 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2294
2422
|
if ("joinTable" in attribute && attribute.joinTable) {
|
|
2295
2423
|
return;
|
|
2296
2424
|
}
|
|
2297
|
-
const joinTableName = getJoinTableName(
|
|
2298
|
-
|
|
2299
|
-
|
|
2425
|
+
const joinTableName = identifiers.getJoinTableName(
|
|
2426
|
+
snakeCase(meta.tableName),
|
|
2427
|
+
snakeCase(attributeName)
|
|
2428
|
+
);
|
|
2429
|
+
const joinColumnName = identifiers.getJoinColumnAttributeIdName(snakeCase(meta.singularName));
|
|
2430
|
+
let inverseJoinColumnName = identifiers.getJoinColumnAttributeIdName(
|
|
2431
|
+
snakeCase(targetMeta.singularName)
|
|
2432
|
+
);
|
|
2300
2433
|
if (joinColumnName === inverseJoinColumnName) {
|
|
2301
|
-
inverseJoinColumnName = getInverseJoinColumnAttributeIdName(
|
|
2302
|
-
targetMeta.singularName
|
|
2434
|
+
inverseJoinColumnName = identifiers.getInverseJoinColumnAttributeIdName(
|
|
2435
|
+
snakeCase(targetMeta.singularName)
|
|
2303
2436
|
);
|
|
2304
2437
|
}
|
|
2305
|
-
const orderColumnName = getOrderColumnName(targetMeta.singularName);
|
|
2306
|
-
let inverseOrderColumnName = getOrderColumnName(meta.singularName);
|
|
2438
|
+
const orderColumnName = identifiers.getOrderColumnName(snakeCase(targetMeta.singularName));
|
|
2439
|
+
let inverseOrderColumnName = identifiers.getOrderColumnName(snakeCase(meta.singularName));
|
|
2307
2440
|
if (attribute.relation === "manyToMany" && orderColumnName === inverseOrderColumnName) {
|
|
2308
|
-
inverseOrderColumnName = getInverseOrderColumnName(meta.singularName);
|
|
2441
|
+
inverseOrderColumnName = identifiers.getInverseOrderColumnName(snakeCase(meta.singularName));
|
|
2309
2442
|
}
|
|
2310
|
-
const fkIndexName = getFkIndexName(joinTableName);
|
|
2311
|
-
const invFkIndexName = getInverseFkIndexName(joinTableName);
|
|
2443
|
+
const fkIndexName = identifiers.getFkIndexName(joinTableName);
|
|
2444
|
+
const invFkIndexName = identifiers.getInverseFkIndexName(joinTableName);
|
|
2312
2445
|
const metadataSchema = {
|
|
2313
2446
|
singularName: joinTableName,
|
|
2314
2447
|
uid: joinTableName,
|
|
@@ -2321,13 +2454,17 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2321
2454
|
type: "integer",
|
|
2322
2455
|
column: {
|
|
2323
2456
|
unsigned: true
|
|
2324
|
-
}
|
|
2457
|
+
},
|
|
2458
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2459
|
+
columnName: joinColumnName
|
|
2325
2460
|
},
|
|
2326
2461
|
[inverseJoinColumnName]: {
|
|
2327
2462
|
type: "integer",
|
|
2328
2463
|
column: {
|
|
2329
2464
|
unsigned: true
|
|
2330
|
-
}
|
|
2465
|
+
},
|
|
2466
|
+
// This must be set explicitly so that it is used instead of shortening the attribute name, which is already shortened
|
|
2467
|
+
columnName: inverseJoinColumnName
|
|
2331
2468
|
}
|
|
2332
2469
|
// TODO: add extra pivot attributes -> user should use an intermediate entity
|
|
2333
2470
|
},
|
|
@@ -2341,7 +2478,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2341
2478
|
columns: [inverseJoinColumnName]
|
|
2342
2479
|
},
|
|
2343
2480
|
{
|
|
2344
|
-
name: getUniqueIndexName(joinTableName),
|
|
2481
|
+
name: identifiers.getUniqueIndexName(joinTableName),
|
|
2345
2482
|
columns: [joinColumnName, inverseJoinColumnName],
|
|
2346
2483
|
type: "unique"
|
|
2347
2484
|
}
|
|
@@ -2352,7 +2489,6 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2352
2489
|
columns: [joinColumnName],
|
|
2353
2490
|
referencedColumns: [ID],
|
|
2354
2491
|
referencedTable: meta.tableName,
|
|
2355
|
-
// TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
|
|
2356
2492
|
onDelete: "CASCADE"
|
|
2357
2493
|
},
|
|
2358
2494
|
{
|
|
@@ -2360,7 +2496,6 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2360
2496
|
columns: [inverseJoinColumnName],
|
|
2361
2497
|
referencedColumns: [ID],
|
|
2362
2498
|
referencedTable: targetMeta.tableName,
|
|
2363
|
-
// TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
|
|
2364
2499
|
onDelete: "CASCADE"
|
|
2365
2500
|
}
|
|
2366
2501
|
],
|
|
@@ -2390,8 +2525,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2390
2525
|
}
|
|
2391
2526
|
};
|
|
2392
2527
|
metadataSchema.indexes.push({
|
|
2393
|
-
name: getOrderFkIndexName(joinTableName),
|
|
2394
|
-
// TODO: should we send joinTableName as parts?
|
|
2528
|
+
name: identifiers.getOrderFkIndexName(joinTableName),
|
|
2395
2529
|
columns: [orderColumnName]
|
|
2396
2530
|
});
|
|
2397
2531
|
joinTable.orderColumnName = orderColumnName;
|
|
@@ -2406,7 +2540,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
2406
2540
|
}
|
|
2407
2541
|
};
|
|
2408
2542
|
metadataSchema.indexes.push({
|
|
2409
|
-
name: getOrderInverseFkIndexName(joinTableName),
|
|
2543
|
+
name: identifiers.getOrderInverseFkIndexName(joinTableName),
|
|
2410
2544
|
columns: [inverseOrderColumnName]
|
|
2411
2545
|
});
|
|
2412
2546
|
joinTable.inverseOrderColumnName = inverseOrderColumnName;
|
|
@@ -2464,6 +2598,12 @@ const createRelation = (attributeName, attribute, meta, metadata) => {
|
|
|
2464
2598
|
}
|
|
2465
2599
|
};
|
|
2466
2600
|
class Metadata extends Map {
|
|
2601
|
+
// TODO: we expose the global identifiers in this way so that in the future we can instantiate our own
|
|
2602
|
+
// However, it should NOT be done until all the methods used by metadata can be part of this metadata object
|
|
2603
|
+
// and access this one; currently they all access the global identifiers directly.
|
|
2604
|
+
get identifiers() {
|
|
2605
|
+
return identifiers;
|
|
2606
|
+
}
|
|
2467
2607
|
get(key) {
|
|
2468
2608
|
if (!super.has(key)) {
|
|
2469
2609
|
throw new Error(`Metadata for "${key}" not found`);
|
|
@@ -2487,10 +2627,12 @@ class Metadata extends Map {
|
|
|
2487
2627
|
seenTables.set(meta.tableName, true);
|
|
2488
2628
|
}
|
|
2489
2629
|
}
|
|
2490
|
-
loadModels(models
|
|
2491
|
-
for (const model of
|
|
2630
|
+
loadModels(models) {
|
|
2631
|
+
for (const model of cloneDeep(models ?? [])) {
|
|
2632
|
+
const tableName = identifiers.getTableName(model.tableName);
|
|
2492
2633
|
this.add({
|
|
2493
2634
|
...model,
|
|
2635
|
+
tableName,
|
|
2494
2636
|
attributes: {
|
|
2495
2637
|
...model.attributes
|
|
2496
2638
|
},
|
|
@@ -2531,10 +2673,13 @@ class Metadata extends Map {
|
|
|
2531
2673
|
}
|
|
2532
2674
|
}
|
|
2533
2675
|
const createAttribute = (attributeName, attribute) => {
|
|
2534
|
-
|
|
2676
|
+
if ("columnName" in attribute && attribute.columnName) {
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2679
|
+
const columnName = identifiers.getColumnName(snakeCase(attributeName));
|
|
2535
2680
|
Object.assign(attribute, { columnName });
|
|
2536
2681
|
};
|
|
2537
|
-
const createMetadata = (models
|
|
2682
|
+
const createMetadata = (models) => {
|
|
2538
2683
|
const metadata = new Metadata();
|
|
2539
2684
|
if (models.length) {
|
|
2540
2685
|
metadata.loadModels(models);
|
|
@@ -2885,11 +3030,54 @@ const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
|
|
|
2885
3030
|
return subAlias;
|
|
2886
3031
|
};
|
|
2887
3032
|
const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
|
|
2888
|
-
const { db, qb } = ctx;
|
|
3033
|
+
const { db, qb, uid } = ctx;
|
|
2889
3034
|
if (attribute.type !== "relation") {
|
|
2890
3035
|
throw new Error(`Cannot join on non relational field ${attributeName}`);
|
|
2891
3036
|
}
|
|
2892
3037
|
const targetMeta = db.metadata.get(attribute.target);
|
|
3038
|
+
if (["morphOne", "morphMany"].includes(attribute.relation)) {
|
|
3039
|
+
const targetAttribute = targetMeta.attributes[attribute.morphBy];
|
|
3040
|
+
const { joinTable: joinTable2, morphColumn } = targetAttribute;
|
|
3041
|
+
if (morphColumn) {
|
|
3042
|
+
const subAlias = refAlias || qb.getAlias();
|
|
3043
|
+
qb.join({
|
|
3044
|
+
alias: subAlias,
|
|
3045
|
+
referencedTable: targetMeta.tableName,
|
|
3046
|
+
referencedColumn: morphColumn.idColumn.name,
|
|
3047
|
+
rootColumn: morphColumn.idColumn.referencedColumn,
|
|
3048
|
+
rootTable: alias,
|
|
3049
|
+
on: {
|
|
3050
|
+
[morphColumn.typeColumn.name]: uid,
|
|
3051
|
+
...morphColumn.on
|
|
3052
|
+
}
|
|
3053
|
+
});
|
|
3054
|
+
return subAlias;
|
|
3055
|
+
}
|
|
3056
|
+
if (joinTable2) {
|
|
3057
|
+
const joinAlias = qb.getAlias();
|
|
3058
|
+
qb.join({
|
|
3059
|
+
alias: joinAlias,
|
|
3060
|
+
referencedTable: joinTable2.name,
|
|
3061
|
+
referencedColumn: joinTable2.morphColumn.idColumn.name,
|
|
3062
|
+
rootColumn: joinTable2.morphColumn.idColumn.referencedColumn,
|
|
3063
|
+
rootTable: alias,
|
|
3064
|
+
on: {
|
|
3065
|
+
[joinTable2.morphColumn.typeColumn.name]: uid,
|
|
3066
|
+
field: attributeName
|
|
3067
|
+
}
|
|
3068
|
+
});
|
|
3069
|
+
const subAlias = refAlias || qb.getAlias();
|
|
3070
|
+
qb.join({
|
|
3071
|
+
alias: subAlias,
|
|
3072
|
+
referencedTable: targetMeta.tableName,
|
|
3073
|
+
referencedColumn: joinTable2.joinColumn.referencedColumn,
|
|
3074
|
+
rootColumn: joinTable2.joinColumn.name,
|
|
3075
|
+
rootTable: joinAlias
|
|
3076
|
+
});
|
|
3077
|
+
return subAlias;
|
|
3078
|
+
}
|
|
3079
|
+
return alias;
|
|
3080
|
+
}
|
|
2893
3081
|
const { joinColumn } = attribute;
|
|
2894
3082
|
if (joinColumn) {
|
|
2895
3083
|
const subAlias = refAlias || qb.getAlias();
|
|
@@ -3029,10 +3217,13 @@ const XtoOne = async (input, ctx) => {
|
|
|
3029
3217
|
rootTable: qb2.alias,
|
|
3030
3218
|
on: joinTable.on
|
|
3031
3219
|
}).select([joinColAlias, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3032
|
-
const map22 = rows2.reduce(
|
|
3033
|
-
map3
|
|
3034
|
-
|
|
3035
|
-
|
|
3220
|
+
const map22 = rows2.reduce(
|
|
3221
|
+
(map3, row) => {
|
|
3222
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3223
|
+
return map3;
|
|
3224
|
+
},
|
|
3225
|
+
{}
|
|
3226
|
+
);
|
|
3036
3227
|
results.forEach((result) => {
|
|
3037
3228
|
result[attributeName] = map22[result[referencedColumnName]] || { count: 0 };
|
|
3038
3229
|
});
|
|
@@ -3105,10 +3296,13 @@ const oneToMany = async (input, ctx) => {
|
|
|
3105
3296
|
rootTable: qb2.alias,
|
|
3106
3297
|
on: joinTable.on
|
|
3107
3298
|
}).select([joinColAlias, qb2.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3108
|
-
const map22 = rows2.reduce(
|
|
3109
|
-
map3
|
|
3110
|
-
|
|
3111
|
-
|
|
3299
|
+
const map22 = rows2.reduce(
|
|
3300
|
+
(map3, row) => {
|
|
3301
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3302
|
+
return map3;
|
|
3303
|
+
},
|
|
3304
|
+
{}
|
|
3305
|
+
);
|
|
3112
3306
|
results.forEach((result) => {
|
|
3113
3307
|
result[attributeName] = map22[result[referencedColumnName]] || { count: 0 };
|
|
3114
3308
|
});
|
|
@@ -3162,10 +3356,13 @@ const manyToMany = async (input, ctx) => {
|
|
|
3162
3356
|
rootTable: populateQb.alias,
|
|
3163
3357
|
on: joinTable.on
|
|
3164
3358
|
}).select([joinColAlias, populateQb.raw("count(*) AS count")]).where({ [joinColAlias]: referencedValues }).groupBy(joinColAlias).execute({ mapResults: false });
|
|
3165
|
-
const map22 = rows2.reduce(
|
|
3166
|
-
map3
|
|
3167
|
-
|
|
3168
|
-
|
|
3359
|
+
const map22 = rows2.reduce(
|
|
3360
|
+
(map3, row) => {
|
|
3361
|
+
map3[row[joinColumnName]] = { count: Number(row.count) };
|
|
3362
|
+
return map3;
|
|
3363
|
+
},
|
|
3364
|
+
{}
|
|
3365
|
+
);
|
|
3169
3366
|
results.forEach((result) => {
|
|
3170
3367
|
result[attributeName] = map22[result[referencedColumnName]] || { count: 0 };
|
|
3171
3368
|
});
|
|
@@ -3539,6 +3736,28 @@ const processNested = (where, ctx) => {
|
|
|
3539
3736
|
}
|
|
3540
3737
|
return processWhere(where, ctx);
|
|
3541
3738
|
};
|
|
3739
|
+
const processRelationWhere = (where, ctx) => {
|
|
3740
|
+
const { qb, alias } = ctx;
|
|
3741
|
+
const idAlias = qb.aliasColumn("id", alias);
|
|
3742
|
+
if (!isRecord$1(where)) {
|
|
3743
|
+
return { [idAlias]: where };
|
|
3744
|
+
}
|
|
3745
|
+
const keys = Object.keys(where);
|
|
3746
|
+
const operatorKeys = keys.filter((key) => isOperator(key));
|
|
3747
|
+
if (operatorKeys.length > 0 && operatorKeys.length !== keys.length) {
|
|
3748
|
+
throw new Error(`Operator and non-operator keys cannot be mixed in a relation where clause`);
|
|
3749
|
+
}
|
|
3750
|
+
if (operatorKeys.length > 1) {
|
|
3751
|
+
throw new Error(
|
|
3752
|
+
`Only one operator key is allowed in a relation where clause, but found: ${operatorKeys}`
|
|
3753
|
+
);
|
|
3754
|
+
}
|
|
3755
|
+
if (operatorKeys.length === 1) {
|
|
3756
|
+
const operator = operatorKeys[0];
|
|
3757
|
+
return { [idAlias]: { [operator]: processNested(where[operator], ctx) } };
|
|
3758
|
+
}
|
|
3759
|
+
return processWhere(where, ctx);
|
|
3760
|
+
};
|
|
3542
3761
|
function processWhere(where, ctx) {
|
|
3543
3762
|
if (!isArray(where) && !isRecord$1(where)) {
|
|
3544
3763
|
throw new Error("Where must be an array or an object");
|
|
@@ -3551,7 +3770,10 @@ function processWhere(where, ctx) {
|
|
|
3551
3770
|
const filters = {};
|
|
3552
3771
|
for (const key of Object.keys(where)) {
|
|
3553
3772
|
const value = where[key];
|
|
3554
|
-
if (isOperatorOfType("group", key)
|
|
3773
|
+
if (isOperatorOfType("group", key)) {
|
|
3774
|
+
if (!Array.isArray(value)) {
|
|
3775
|
+
throw new Error(`Operator ${key} must be an array`);
|
|
3776
|
+
}
|
|
3555
3777
|
filters[key] = value.map((sub) => processNested(sub, ctx));
|
|
3556
3778
|
continue;
|
|
3557
3779
|
}
|
|
@@ -3575,15 +3797,12 @@ function processWhere(where, ctx) {
|
|
|
3575
3797
|
attributeName: key,
|
|
3576
3798
|
attribute
|
|
3577
3799
|
});
|
|
3578
|
-
|
|
3800
|
+
const nestedWhere = processRelationWhere(value, {
|
|
3579
3801
|
db,
|
|
3580
3802
|
qb,
|
|
3581
3803
|
alias: subAlias,
|
|
3582
3804
|
uid: attribute.target
|
|
3583
3805
|
});
|
|
3584
|
-
if (!isRecord$1(nestedWhere) || isOperatorOfType("where", keys(nestedWhere)[0])) {
|
|
3585
|
-
nestedWhere = { [qb.aliasColumn("id", subAlias)]: nestedWhere };
|
|
3586
|
-
}
|
|
3587
3806
|
Object.assign(filters, nestedWhere);
|
|
3588
3807
|
continue;
|
|
3589
3808
|
}
|
|
@@ -3736,8 +3955,8 @@ const applyWhereToColumn = (qb, column, columnWhere) => {
|
|
|
3736
3955
|
}
|
|
3737
3956
|
return qb.where(column, columnWhere);
|
|
3738
3957
|
}
|
|
3739
|
-
const
|
|
3740
|
-
|
|
3958
|
+
const keys = Object.keys(columnWhere);
|
|
3959
|
+
keys.forEach((operator) => {
|
|
3741
3960
|
const value = columnWhere[operator];
|
|
3742
3961
|
applyOperator(qb, column, operator, value);
|
|
3743
3962
|
});
|
|
@@ -4676,21 +4895,24 @@ const sortConnectArray = (connectArr, initialArr = [], strictSort = true) => {
|
|
|
4676
4895
|
(acc, rel) => ({ ...acc, [rel.id]: true }),
|
|
4677
4896
|
{}
|
|
4678
4897
|
);
|
|
4679
|
-
const mappedRelations = connectArr.reduce(
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4898
|
+
const mappedRelations = connectArr.reduce(
|
|
4899
|
+
(mapper, relation) => {
|
|
4900
|
+
const adjacentRelId = relation.position?.before || relation.position?.after;
|
|
4901
|
+
if (!adjacentRelId || !relationInInitialArray[adjacentRelId] && !mapper[adjacentRelId]) {
|
|
4902
|
+
needsSorting = true;
|
|
4903
|
+
}
|
|
4904
|
+
if (mapper[relation.id]) {
|
|
4905
|
+
throw new InvalidRelationError(
|
|
4906
|
+
`The relation with id ${relation.id} is already connected. You cannot connect the same relation twice.`
|
|
4907
|
+
);
|
|
4908
|
+
}
|
|
4909
|
+
return {
|
|
4910
|
+
[relation.id]: { ...relation, computed: false },
|
|
4911
|
+
...mapper
|
|
4912
|
+
};
|
|
4913
|
+
},
|
|
4914
|
+
{}
|
|
4915
|
+
);
|
|
4694
4916
|
if (!needsSorting)
|
|
4695
4917
|
return connectArr;
|
|
4696
4918
|
const computeRelation = (relation, relationsSeenInBranch) => {
|
|
@@ -4798,14 +5020,17 @@ const relationsOrderer = (initArr, idColumn, orderColumn, strict2) => {
|
|
|
4798
5020
|
* Get a map between the relation id and its order
|
|
4799
5021
|
*/
|
|
4800
5022
|
getOrderMap() {
|
|
4801
|
-
return _$1(computedRelations).groupBy("order").reduce(
|
|
4802
|
-
|
|
5023
|
+
return _$1(computedRelations).groupBy("order").reduce(
|
|
5024
|
+
(acc, relations) => {
|
|
5025
|
+
if (relations[0]?.init)
|
|
5026
|
+
return acc;
|
|
5027
|
+
relations.forEach((relation, idx) => {
|
|
5028
|
+
acc[relation.id] = Math.floor(relation.order) + (idx + 1) / (relations.length + 1);
|
|
5029
|
+
});
|
|
4803
5030
|
return acc;
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
return acc;
|
|
4808
|
-
}, {});
|
|
5031
|
+
},
|
|
5032
|
+
{}
|
|
5033
|
+
);
|
|
4809
5034
|
}
|
|
4810
5035
|
};
|
|
4811
5036
|
};
|
|
@@ -5559,10 +5784,13 @@ const createEntityManager = (db) => {
|
|
|
5559
5784
|
const entry = await this.findOne(uid, {
|
|
5560
5785
|
select: ["id"],
|
|
5561
5786
|
where: { id: entity.id },
|
|
5562
|
-
populate: fieldsArr.reduce(
|
|
5563
|
-
acc
|
|
5564
|
-
|
|
5565
|
-
|
|
5787
|
+
populate: fieldsArr.reduce(
|
|
5788
|
+
(acc, field) => {
|
|
5789
|
+
acc[field] = populate || true;
|
|
5790
|
+
return acc;
|
|
5791
|
+
},
|
|
5792
|
+
{}
|
|
5793
|
+
)
|
|
5566
5794
|
});
|
|
5567
5795
|
if (!entry) {
|
|
5568
5796
|
return null;
|
|
@@ -5678,7 +5906,280 @@ const createUserMigrationProvider = (db) => {
|
|
|
5678
5906
|
}
|
|
5679
5907
|
};
|
|
5680
5908
|
};
|
|
5681
|
-
const
|
|
5909
|
+
const QUERIES = {
|
|
5910
|
+
async postgres(knex2, params) {
|
|
5911
|
+
const res = await knex2.raw(
|
|
5912
|
+
`
|
|
5913
|
+
SELECT :tableName:.id as id, string_agg(DISTINCT :inverseJoinColumn:::character varying, ',') as other_ids
|
|
5914
|
+
FROM :tableName:
|
|
5915
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5916
|
+
WHERE document_id IS NULL
|
|
5917
|
+
GROUP BY :tableName:.id, :joinColumn:
|
|
5918
|
+
LIMIT 1;
|
|
5919
|
+
`,
|
|
5920
|
+
params
|
|
5921
|
+
);
|
|
5922
|
+
return res.rows;
|
|
5923
|
+
},
|
|
5924
|
+
async mysql(knex2, params) {
|
|
5925
|
+
const [res] = await knex2.raw(
|
|
5926
|
+
`
|
|
5927
|
+
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids
|
|
5928
|
+
FROM :tableName:
|
|
5929
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5930
|
+
WHERE document_id IS NULL
|
|
5931
|
+
GROUP BY :tableName:.id, :joinColumn:
|
|
5932
|
+
LIMIT 1;
|
|
5933
|
+
`,
|
|
5934
|
+
params
|
|
5935
|
+
);
|
|
5936
|
+
return res;
|
|
5937
|
+
},
|
|
5938
|
+
async sqlite(knex2, params) {
|
|
5939
|
+
return knex2.raw(
|
|
5940
|
+
`
|
|
5941
|
+
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids
|
|
5942
|
+
FROM :tableName:
|
|
5943
|
+
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn:
|
|
5944
|
+
WHERE document_id IS NULL
|
|
5945
|
+
GROUP BY :joinColumn:
|
|
5946
|
+
LIMIT 1;
|
|
5947
|
+
`,
|
|
5948
|
+
params
|
|
5949
|
+
);
|
|
5950
|
+
}
|
|
5951
|
+
};
|
|
5952
|
+
const getNextIdsToCreateDocumentId = async (db, knex2, {
|
|
5953
|
+
joinColumn,
|
|
5954
|
+
inverseJoinColumn,
|
|
5955
|
+
tableName,
|
|
5956
|
+
joinTableName
|
|
5957
|
+
}) => {
|
|
5958
|
+
const res = await QUERIES[db.dialect.client](knex2, {
|
|
5959
|
+
joinColumn,
|
|
5960
|
+
inverseJoinColumn,
|
|
5961
|
+
tableName,
|
|
5962
|
+
joinTableName
|
|
5963
|
+
});
|
|
5964
|
+
if (res.length > 0) {
|
|
5965
|
+
const row = res[0];
|
|
5966
|
+
const otherIds = row.other_ids ? row.other_ids.split(",").map((v) => parseInt(v, 10)) : [];
|
|
5967
|
+
return [row.id, ...otherIds];
|
|
5968
|
+
}
|
|
5969
|
+
return [];
|
|
5970
|
+
};
|
|
5971
|
+
const migrateDocumentIdsWithLocalizations = async (db, knex2, meta) => {
|
|
5972
|
+
const singularName = meta.singularName.toLowerCase();
|
|
5973
|
+
const joinColumn = identifiers.getJoinColumnAttributeIdName(singularName);
|
|
5974
|
+
const inverseJoinColumn = identifiers.getInverseJoinColumnAttributeIdName(singularName);
|
|
5975
|
+
let ids;
|
|
5976
|
+
do {
|
|
5977
|
+
ids = await getNextIdsToCreateDocumentId(db, knex2, {
|
|
5978
|
+
joinColumn,
|
|
5979
|
+
inverseJoinColumn,
|
|
5980
|
+
tableName: meta.tableName,
|
|
5981
|
+
joinTableName: identifiers.getJoinTableName(meta.tableName, `localizations`)
|
|
5982
|
+
});
|
|
5983
|
+
if (ids.length > 0) {
|
|
5984
|
+
await knex2(meta.tableName).update({ document_id: createId() }).whereIn("id", ids);
|
|
5985
|
+
}
|
|
5986
|
+
} while (ids.length > 0);
|
|
5987
|
+
};
|
|
5988
|
+
const migrationDocumentIds = async (db, knex2, meta) => {
|
|
5989
|
+
let run = true;
|
|
5990
|
+
do {
|
|
5991
|
+
const updatedRows = await knex2(meta.tableName).update({ document_id: createId() }).whereIn("id", (builder) => {
|
|
5992
|
+
return builder.whereNull("document_id").select("id").limit(1);
|
|
5993
|
+
});
|
|
5994
|
+
if (updatedRows <= 0) {
|
|
5995
|
+
run = false;
|
|
5996
|
+
}
|
|
5997
|
+
} while (run);
|
|
5998
|
+
};
|
|
5999
|
+
const createDocumentIdColumn = async (knex2, tableName) => {
|
|
6000
|
+
await knex2.schema.alterTable(tableName, (table) => {
|
|
6001
|
+
table.string("document_id");
|
|
6002
|
+
});
|
|
6003
|
+
};
|
|
6004
|
+
const hasLocalizationsJoinTable = async (knex2, tableName) => {
|
|
6005
|
+
const joinTableName = identifiers.getJoinTableName(tableName, "localizations");
|
|
6006
|
+
return knex2.schema.hasTable(joinTableName);
|
|
6007
|
+
};
|
|
6008
|
+
const createdDocumentId = {
|
|
6009
|
+
name: "5.0.0-02-created-document-id",
|
|
6010
|
+
async up(knex2, db) {
|
|
6011
|
+
for (const meta of db.metadata.values()) {
|
|
6012
|
+
const hasTable = await knex2.schema.hasTable(meta.tableName);
|
|
6013
|
+
if (!hasTable) {
|
|
6014
|
+
continue;
|
|
6015
|
+
}
|
|
6016
|
+
if ("documentId" in meta.attributes) {
|
|
6017
|
+
const hasDocumentIdColumn = await knex2.schema.hasColumn(meta.tableName, "document_id");
|
|
6018
|
+
if (hasDocumentIdColumn) {
|
|
6019
|
+
continue;
|
|
6020
|
+
}
|
|
6021
|
+
await createDocumentIdColumn(knex2, meta.tableName);
|
|
6022
|
+
if (await hasLocalizationsJoinTable(knex2, meta.tableName)) {
|
|
6023
|
+
await migrateDocumentIdsWithLocalizations(db, knex2, meta);
|
|
6024
|
+
} else {
|
|
6025
|
+
await migrationDocumentIds(db, knex2, meta);
|
|
6026
|
+
}
|
|
6027
|
+
}
|
|
6028
|
+
}
|
|
6029
|
+
},
|
|
6030
|
+
async down() {
|
|
6031
|
+
throw new Error("not implemented");
|
|
6032
|
+
}
|
|
6033
|
+
};
|
|
6034
|
+
const debug = createDebug("strapi::database::migration");
|
|
6035
|
+
const renameIdentifiersLongerThanMaxLength = {
|
|
6036
|
+
name: "5.0.0-rename-identifiers-longer-than-max-length",
|
|
6037
|
+
async up(knex2, db) {
|
|
6038
|
+
const md = db.metadata;
|
|
6039
|
+
const diffs = findDiffs(md);
|
|
6040
|
+
for (const indexDiff of diffs.indexes) {
|
|
6041
|
+
await renameIndex(knex2, db, indexDiff);
|
|
6042
|
+
}
|
|
6043
|
+
for (const columnDiff of diffs.columns) {
|
|
6044
|
+
const { full, short } = columnDiff;
|
|
6045
|
+
const tableName = full.tableName;
|
|
6046
|
+
const hasTable = await knex2.schema.hasTable(tableName);
|
|
6047
|
+
if (hasTable) {
|
|
6048
|
+
const hasColumn = await knex2.schema.hasColumn(tableName, full.columnName);
|
|
6049
|
+
if (hasColumn) {
|
|
6050
|
+
await knex2.schema.alterTable(tableName, async (table) => {
|
|
6051
|
+
debug(`renaming column ${full.columnName} to ${short.columnName}`);
|
|
6052
|
+
table.renameColumn(full.columnName, short.columnName);
|
|
6053
|
+
});
|
|
6054
|
+
}
|
|
6055
|
+
}
|
|
6056
|
+
}
|
|
6057
|
+
for (const tableDiff of diffs.tables) {
|
|
6058
|
+
const hasTable = await knex2.schema.hasTable(tableDiff.full.tableName);
|
|
6059
|
+
if (hasTable) {
|
|
6060
|
+
debug(`renaming table ${tableDiff.full.tableName} to ${tableDiff.short.tableName}`);
|
|
6061
|
+
await knex2.schema.renameTable(tableDiff.full.tableName, tableDiff.short.tableName);
|
|
6062
|
+
}
|
|
6063
|
+
}
|
|
6064
|
+
},
|
|
6065
|
+
async down() {
|
|
6066
|
+
throw new Error("not implemented");
|
|
6067
|
+
}
|
|
6068
|
+
};
|
|
6069
|
+
const renameIndex = async (knex2, db, diff) => {
|
|
6070
|
+
const client = db.config.connection.client;
|
|
6071
|
+
const short = diff.short;
|
|
6072
|
+
const full = diff.full;
|
|
6073
|
+
if (full.indexName === short.indexName) {
|
|
6074
|
+
debug(`not renaming index ${full.indexName} because name hasn't changed`);
|
|
6075
|
+
return;
|
|
6076
|
+
}
|
|
6077
|
+
if (short.indexName.includes("_lnk_") || full.indexName.includes("_lnk_") || short.indexName.endsWith("fk") || full.indexName.endsWith("fk")) {
|
|
6078
|
+
return;
|
|
6079
|
+
}
|
|
6080
|
+
debug(`renaming index from ${full.indexName} to ${short.indexName}`);
|
|
6081
|
+
try {
|
|
6082
|
+
await knex2.transaction(async (trx) => {
|
|
6083
|
+
if (client === "mysql" || client === "mariadb") {
|
|
6084
|
+
await knex2.raw(
|
|
6085
|
+
`ALTER TABLE \`${full.tableName}\` RENAME INDEX \`${full.indexName}\` TO \`${short.indexName}\``
|
|
6086
|
+
).transacting(trx);
|
|
6087
|
+
} else if (client === "pg" || client === "postgres") {
|
|
6088
|
+
await knex2.raw(`ALTER INDEX "${full.indexName}" RENAME TO "${short.indexName}"`).transacting(trx);
|
|
6089
|
+
} else if (client === "sqlite" || client === "better") {
|
|
6090
|
+
} else {
|
|
6091
|
+
debug("No db client name matches, not creating index");
|
|
6092
|
+
}
|
|
6093
|
+
});
|
|
6094
|
+
} catch (err) {
|
|
6095
|
+
debug(`error creating index: ${JSON.stringify(err)}`);
|
|
6096
|
+
}
|
|
6097
|
+
};
|
|
6098
|
+
const findDiffs = (shortMap) => {
|
|
6099
|
+
const diffs = {
|
|
6100
|
+
tables: [],
|
|
6101
|
+
columns: [],
|
|
6102
|
+
indexes: []
|
|
6103
|
+
};
|
|
6104
|
+
const shortArr = Array.from(shortMap.entries());
|
|
6105
|
+
shortArr.forEach(([, shortObj], index2) => {
|
|
6106
|
+
const fullTableName = identifiers.getUnshortenedName(shortObj.tableName);
|
|
6107
|
+
if (!fullTableName) {
|
|
6108
|
+
throw new Error(`Missing full table name for ${shortObj.tableName}`);
|
|
6109
|
+
}
|
|
6110
|
+
if (shortObj.tableName !== fullTableName) {
|
|
6111
|
+
diffs.tables.push({
|
|
6112
|
+
full: {
|
|
6113
|
+
index: index2,
|
|
6114
|
+
key: "tableName",
|
|
6115
|
+
tableName: fullTableName
|
|
6116
|
+
},
|
|
6117
|
+
short: {
|
|
6118
|
+
index: index2,
|
|
6119
|
+
key: "tableName",
|
|
6120
|
+
tableName: shortObj.tableName
|
|
6121
|
+
}
|
|
6122
|
+
});
|
|
6123
|
+
}
|
|
6124
|
+
for (const attrKey in shortObj.attributes) {
|
|
6125
|
+
if (shortObj.attributes[attrKey].type === "relation") {
|
|
6126
|
+
continue;
|
|
6127
|
+
}
|
|
6128
|
+
const attr = shortObj.attributes[attrKey];
|
|
6129
|
+
const shortColumnName = attr.columnName;
|
|
6130
|
+
const longColumnName = identifiers.getUnshortenedName(shortColumnName);
|
|
6131
|
+
if (!shortColumnName || !longColumnName) {
|
|
6132
|
+
throw new Error(`missing column name(s) for attribute ${JSON.stringify(attr, null, 2)}`);
|
|
6133
|
+
}
|
|
6134
|
+
if (shortColumnName && longColumnName && shortColumnName !== longColumnName) {
|
|
6135
|
+
diffs.columns.push({
|
|
6136
|
+
short: {
|
|
6137
|
+
index: index2,
|
|
6138
|
+
tableName: fullTableName,
|
|
6139
|
+
// NOTE: this means that we must rename columns before tables
|
|
6140
|
+
key: `attributes.${attrKey}`,
|
|
6141
|
+
columnName: shortColumnName
|
|
6142
|
+
},
|
|
6143
|
+
full: {
|
|
6144
|
+
index: index2,
|
|
6145
|
+
tableName: fullTableName,
|
|
6146
|
+
key: `attributes.${attrKey}`,
|
|
6147
|
+
columnName: longColumnName
|
|
6148
|
+
}
|
|
6149
|
+
});
|
|
6150
|
+
}
|
|
6151
|
+
}
|
|
6152
|
+
for (const attrKey in shortObj.indexes) {
|
|
6153
|
+
const shortIndexName = shortObj.indexes[attrKey].name;
|
|
6154
|
+
const longIndexName = identifiers.getUnshortenedName(shortIndexName);
|
|
6155
|
+
if (!longIndexName) {
|
|
6156
|
+
throw new Error(`Missing full index name for ${shortIndexName}`);
|
|
6157
|
+
}
|
|
6158
|
+
if (shortIndexName && longIndexName && shortIndexName !== longIndexName) {
|
|
6159
|
+
diffs.indexes.push({
|
|
6160
|
+
short: {
|
|
6161
|
+
index: index2,
|
|
6162
|
+
tableName: fullTableName,
|
|
6163
|
+
// NOTE: this means that we must rename columns before tables
|
|
6164
|
+
key: `indexes.${attrKey}`,
|
|
6165
|
+
indexName: shortIndexName
|
|
6166
|
+
},
|
|
6167
|
+
full: {
|
|
6168
|
+
index: index2,
|
|
6169
|
+
tableName: fullTableName,
|
|
6170
|
+
key: `indexes.${attrKey}`,
|
|
6171
|
+
indexName: longIndexName
|
|
6172
|
+
}
|
|
6173
|
+
});
|
|
6174
|
+
}
|
|
6175
|
+
}
|
|
6176
|
+
});
|
|
6177
|
+
return diffs;
|
|
6178
|
+
};
|
|
6179
|
+
const internalMigrations = [
|
|
6180
|
+
renameIdentifiersLongerThanMaxLength,
|
|
6181
|
+
createdDocumentId
|
|
6182
|
+
];
|
|
5682
6183
|
const createInternalMigrationProvider = (db) => {
|
|
5683
6184
|
const context = { db };
|
|
5684
6185
|
const umzugProvider = new Umzug({
|
|
@@ -5904,8 +6405,14 @@ const validateBidirectionalRelations = async (db) => {
|
|
|
5904
6405
|
for (const { relation, invRelation } of invalidLinks) {
|
|
5905
6406
|
const modelMetadata = db.metadata.get(invRelation.target);
|
|
5906
6407
|
const invModelMetadata = db.metadata.get(relation.target);
|
|
5907
|
-
const joinTableName = getJoinTableName(
|
|
5908
|
-
|
|
6408
|
+
const joinTableName = identifiers.getJoinTableName(
|
|
6409
|
+
snakeCase(modelMetadata.tableName),
|
|
6410
|
+
snakeCase(invRelation.inversedBy)
|
|
6411
|
+
);
|
|
6412
|
+
const inverseJoinTableName = identifiers.getJoinTableName(
|
|
6413
|
+
snakeCase(invModelMetadata.tableName),
|
|
6414
|
+
snakeCase(relation.inversedBy)
|
|
6415
|
+
);
|
|
5909
6416
|
const joinTableEmpty = await isLinkTableEmpty(db, joinTableName);
|
|
5910
6417
|
const inverseJoinTableEmpty = await isLinkTableEmpty(db, inverseJoinTableName);
|
|
5911
6418
|
if (joinTableEmpty) {
|
|
@@ -5951,7 +6458,7 @@ class Database {
|
|
|
5951
6458
|
};
|
|
5952
6459
|
this.dialect = getDialect(this);
|
|
5953
6460
|
this.dialect.configure();
|
|
5954
|
-
this.metadata = createMetadata();
|
|
6461
|
+
this.metadata = createMetadata([]);
|
|
5955
6462
|
this.connection = createConnection(this.config.connection, {
|
|
5956
6463
|
pool: { afterCreate: afterCreate(this) }
|
|
5957
6464
|
});
|
|
@@ -6028,11 +6535,9 @@ class Database {
|
|
|
6028
6535
|
await this.connection.destroy();
|
|
6029
6536
|
}
|
|
6030
6537
|
}
|
|
6031
|
-
const utils = { identifiers };
|
|
6032
6538
|
export {
|
|
6033
6539
|
Database,
|
|
6034
6540
|
index as errors,
|
|
6035
|
-
isKnexQuery
|
|
6036
|
-
utils
|
|
6541
|
+
isKnexQuery
|
|
6037
6542
|
};
|
|
6038
6543
|
//# sourceMappingURL=index.mjs.map
|