Skip to content
19 changes: 19 additions & 0 deletions packages/core/run_for_all_dialects.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash

# Runs tests for all dialects
# Specify the test you want to run on .mocharc.jsonc on packages/core with the following content:
# {
# "file": "test/integration/query-builder/query-builder.test.js"
# }
# See https://github.com/sequelize/sequelize/blob/main/CONTRIBUTING.md#41-running-only-some-tests
# Remember to run the `start.sh` scripts for the dialects you want to test from the dev folder.

DIALECT=sqlite3 yarn mocha && \
DIALECT=mysql yarn mocha && \
DIALECT=mariadb yarn mocha && \
DIALECT=postgres yarn mocha && \
# DIALECT=mssql yarn mocha && \
# DIALECT=snowflake yarn mocha && \ ## Experimental
# DIALECT=ibmi yarn mocha && \ ## Experimental
# DIALECT=db2 yarn mocha && \ ## No matching manifest for arm64
echo "Done"
89 changes: 89 additions & 0 deletions packages/core/src/abstract-dialect/query-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -1590,6 +1590,8 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
topLevelInfo,
{ minifyAliases: options.minifyAliases },
);
} else if (include._isCustomJoin) {
joinQuery = this.generateCustomJoin(include, includeAs, topLevelInfo);
} else {
this._generateSubQueryFilter(include, includeAs, topLevelInfo);
joinQuery = this.generateJoin(include, topLevelInfo, options);
Expand Down Expand Up @@ -1710,6 +1712,93 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
);
}

generateCustomJoin(include, includeAs, topLevelInfo) {
const right = include.model;
const asRight = includeAs.internalAs;
let joinCondition;
let joinWhere;

if (!include.on) {
throw new Error('Custom joins require an "on" condition to be specified');
}

// Handle the custom join condition
joinCondition = this.whereItemsQuery(include.on, {
mainAlias: asRight,
model: include.model,
replacements: topLevelInfo.options?.replacements,
});

if (include.where) {
joinWhere = this.whereItemsQuery(include.where, {
mainAlias: asRight,
model: include.model,
replacements: topLevelInfo.options?.replacements,
});
if (joinWhere) {
if (include.or) {
joinCondition += ` OR ${joinWhere}`;
} else {
joinCondition += ` AND ${joinWhere}`;
}
}
}

// Handle alias minification like in generateJoin
if (topLevelInfo.options?.minifyAliases && asRight.length > 63) {
const alias = `%${topLevelInfo.options.includeAliases.size}`;
topLevelInfo.options.includeAliases.set(alias, asRight);
}

// Generate attributes for the joined table
const attributes = [];
const rightAttributes = right.modelDefinition.attributes;

// Process each attribute based on include.attributes or all attributes
const attributesToInclude =
include.attributes && include.attributes.length > 0
? include.attributes
: Array.from(rightAttributes.keys());

for (const attr of attributesToInclude) {
if (typeof attr === 'string') {
// Simple attribute name
const field = rightAttributes.get(attr)?.columnName || attr;
attributes.push(
`${this.quoteTable(asRight)}.${this.quoteIdentifier(field)} AS ${this.quoteIdentifier(`${asRight}.${attr}`)}`,
);
} else if (Array.isArray(attr)) {
// [field, alias] format
const [field, alias] = attr;
if (typeof field === 'string') {
const columnName = rightAttributes.get(field)?.columnName || field;
attributes.push(
`${this.quoteTable(asRight)}.${this.quoteIdentifier(columnName)} AS ${this.quoteIdentifier(`${asRight}.${alias}`)}`,
);
} else {
// Handle complex expressions
attributes.push(
`${this.formatSqlExpression(field)} AS ${this.quoteIdentifier(`${asRight}.${alias}`)}`,
);
}
}
}

return {
join: include.required
? 'INNER JOIN'
: include.right && this._dialect.supports['RIGHT JOIN']
? 'RIGHT OUTER JOIN'
: 'LEFT OUTER JOIN',
body: this.quoteTable(right, { ...topLevelInfo.options, ...include, alias: asRight }),
condition: joinCondition,
attributes: {
main: attributes,
subQuery: [],
},
};
}

generateJoin(include, topLevelInfo, options) {
const association = include.association;
const parent = include.parent;
Expand Down
Loading