Skip to content

Commit

Permalink
feat: use bind params in bulkInsertQuery
Browse files Browse the repository at this point in the history
  • Loading branch information
Americas committed Mar 12, 2024
1 parent 4e1ca10 commit 5c09897
Show file tree
Hide file tree
Showing 16 changed files with 863 additions and 179 deletions.
7 changes: 4 additions & 3 deletions packages/core/src/dialects/abstract/query-generator.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type InsertOptions = ParameterOptions &

type BulkInsertOptions = ParameterOptions & {
hasTrigger?: boolean;
bindParam?: false | ((value: unknown) => string);

updateOnDuplicate?: string[];
ignoreDuplicates?: boolean;
Expand Down Expand Up @@ -93,13 +94,13 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
valueHash: object,
columnDefinitions?: { [columnName: string]: NormalizedAttributeOptions },
options?: InsertOptions,
): { query: string; bind?: unknown[] };
): { query: string; bind?: Record<string, unknown> };
bulkInsertQuery(
tableName: TableName,
newEntries: object[],
options?: BulkInsertOptions,
columnDefinitions?: { [columnName: string]: NormalizedAttributeOptions },
): string;
): { query: string; bind?: Record<string, unknown> };

addColumnQuery(
table: TableName,
Expand All @@ -114,7 +115,7 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
where: WhereOptions,
options?: UpdateOptions,
columnDefinitions?: { [columnName: string]: NormalizedAttributeOptions },
): { query: string; bind?: unknown[] };
): { query: string; bind?: Record<string, unknown> };

arithmeticQuery(
operator: string,
Expand Down
14 changes: 12 additions & 2 deletions packages/core/src/dialects/abstract/query-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,8 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
options ||= {};
fieldMappedAttributes ||= {};

const bind = Object.create(null);
const bindParam = options.bindParam ?? this.bindParam(bind);
const tuples = [];
const serials = {};
const allAttributes = [];
Expand All @@ -318,7 +320,7 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {

return this.escape(fieldValueHash[key] ?? null, {
// model // TODO: make bulkInsertQuery accept model instead of fieldValueHashes
// bindParam // TODO: support bind params
bindParam,
type: fieldMappedAttributes[key]?.type,
replacements: options.replacements,
});
Expand Down Expand Up @@ -386,7 +388,7 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
returning += returnValues.returningFragment;
}

return joinSQLFragments([
const query = joinSQLFragments([
'INSERT',
ignoreDuplicates,
'INTO',
Expand All @@ -399,6 +401,14 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
returning,
';',
]);

const result = { query };

if (bindParam !== false) {
result.bind = bind;
}

return result;
}

/**
Expand Down
14 changes: 12 additions & 2 deletions packages/core/src/dialects/abstract/query-interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -475,14 +475,24 @@ export class AbstractQueryInterface extends AbstractQueryInterfaceTypeScript {
* @returns {Promise}
*/
async bulkInsert(tableName, records, options, attributes) {
if (options?.bind) {
assertNoReservedBind(options.bind);
}

options = { ...options, type: QueryTypes.INSERT };

const sql = this.queryGenerator.bulkInsertQuery(tableName, records, options, attributes);
const { bind, query } = this.queryGenerator.bulkInsertQuery(
tableName,
records,
options,
attributes,
);

// unlike bind, replacements are handled by QueryGenerator, not QueryRaw
delete options.replacements;
options.bind = combineBinds(options.bind, bind);

const results = await this.sequelize.queryRaw(sql, options);
const results = await this.sequelize.queryRaw(query, options);

return results[0];
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/dialects/db2/query-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ export class Db2QueryGenerator extends Db2QueryGeneratorTypeScript {

const generatedQuery = template(allQueries.join(';'), this._templateSettings)(replacements);

return generatedQuery;
return { query: generatedQuery };
}

updateQuery(tableName, attrValueHash, where, options, attributes) {
Expand Down
9 changes: 7 additions & 2 deletions packages/core/src/dialects/ibmi/query-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,13 +350,18 @@ export class IBMiQueryGenerator extends IBMiQueryGeneratorTypeScript {

bulkInsertQuery(tableName, fieldValueHashes, options, fieldMappedAttributes) {
// remove the final semi-colon
let query = super.bulkInsertQuery(tableName, fieldValueHashes, options, fieldMappedAttributes);
let { bind, query } = super.bulkInsertQuery(
tableName,
fieldValueHashes,
options,
fieldMappedAttributes,
);
if (query.at(-1) === ';') {
query = query.slice(0, -1);
query = `SELECT * FROM FINAL TABLE (${query})`;
}

return query;
return { bind, query };
}

// bindParam(bind) {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/dialects/mssql/query-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ export class MsSqlQueryGenerator extends MsSqlQueryGeneratorTypeScript {
offset += 1000;
}

return `${commands.join(';')};`;
return { query: `${commands.join(';')};` };
}

updateQuery(tableName, attrValueHash, where, options = {}, attributes) {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/test/integration/model/bulk-create.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ describe('Model', () => {
default: {
// mysql, sqlite
expect(sql).to.include(
'INSERT INTO `Beers` (`id`,`style`,`createdAt`,`updatedAt`) VALUES (NULL',
'INSERT INTO `Beers` (`id`,`style`,`createdAt`,`updatedAt`) VALUES (',
);
}
}
Expand Down
47 changes: 31 additions & 16 deletions packages/core/test/unit/dialects/db2/query-generator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,12 +346,14 @@ if (dialect === 'db2') {
bulkInsertQuery: [
{
arguments: ['myTable', [{ name: 'foo' }, { name: 'bar' }]],
expectation: 'INSERT INTO "myTable" ("name") VALUES (\'foo\'),(\'bar\');',
expectation: { query: 'INSERT INTO "myTable" ("name") VALUES (\'foo\'),(\'bar\');' },
},
{
arguments: ['myTable', [{ name: "foo';DROP TABLE myTable;" }, { name: 'bar' }]],
expectation:
"INSERT INTO \"myTable\" (\"name\") VALUES ('foo'';DROP TABLE myTable;'),('bar');",
expectation: {
query:
"INSERT INTO \"myTable\" (\"name\") VALUES ('foo'';DROP TABLE myTable;'),('bar');",
},
},
{
arguments: [
Expand All @@ -361,7 +363,9 @@ if (dialect === 'db2') {
{ name: 'bar', birthday: new Date(Date.UTC(2012, 2, 27, 10, 1, 55)) },
],
],
expectation: `INSERT INTO "myTable" ("name","birthday") VALUES ('foo','2011-03-27 10:01:55.000'),('bar','2012-03-27 10:01:55.000');`,
expectation: {
query: `INSERT INTO "myTable" ("name","birthday") VALUES ('foo','2011-03-27 10:01:55.000'),('bar','2012-03-27 10:01:55.000');`,
},
},
{
arguments: [
Expand All @@ -371,7 +375,9 @@ if (dialect === 'db2') {
{ name: 'bar', foo: 2 },
],
],
expectation: 'INSERT INTO "myTable" ("name","foo") VALUES (\'foo\',1),(\'bar\',2);',
expectation: {
query: 'INSERT INTO "myTable" ("name","foo") VALUES (\'foo\',1),(\'bar\',2);',
},
},
{
arguments: [
Expand All @@ -381,8 +387,10 @@ if (dialect === 'db2') {
{ name: 'bar', nullValue: null },
],
],
expectation:
'INSERT INTO "myTable" ("name","foo","nullValue") VALUES (\'foo\',1,NULL),(\'bar\',NULL,NULL);',
expectation: {
query:
'INSERT INTO "myTable" ("name","foo","nullValue") VALUES (\'foo\',1,NULL),(\'bar\',NULL,NULL);',
},
},
{
arguments: [
Expand All @@ -392,8 +400,10 @@ if (dialect === 'db2') {
{ name: 'bar', foo: 2, nullValue: null },
],
],
expectation:
'INSERT INTO "myTable" ("name","foo","nullValue") VALUES (\'foo\',1,NULL),(\'bar\',2,NULL);',
expectation: {
query:
'INSERT INTO "myTable" ("name","foo","nullValue") VALUES (\'foo\',1,NULL),(\'bar\',2,NULL);',
},
context: { options: { omitNull: false } },
},
{
Expand All @@ -404,8 +414,10 @@ if (dialect === 'db2') {
{ name: 'bar', foo: 2, nullValue: null },
],
],
expectation:
'INSERT INTO "myTable" ("name","foo","nullValue") VALUES (\'foo\',1,NULL),(\'bar\',2,NULL);',
expectation: {
query:
'INSERT INTO "myTable" ("name","foo","nullValue") VALUES (\'foo\',1,NULL),(\'bar\',2,NULL);',
},
context: { options: { omitNull: true } }, // Note: We don't honour this because it makes little sense when some rows may have nulls and others not
},
{
Expand All @@ -416,8 +428,10 @@ if (dialect === 'db2') {
{ name: 'bar', foo: 2, undefinedValue: undefined },
],
],
expectation:
'INSERT INTO "myTable" ("name","foo","nullValue","undefinedValue") VALUES (\'foo\',1,NULL,NULL),(\'bar\',2,NULL,NULL);',
expectation: {
query:
'INSERT INTO "myTable" ("name","foo","nullValue","undefinedValue") VALUES (\'foo\',1,NULL,NULL),(\'bar\',2,NULL,NULL);',
},
context: { options: { omitNull: true } }, // Note: As above
},
{
Expand All @@ -428,12 +442,13 @@ if (dialect === 'db2') {
{ name: 'bar', value: false },
],
],
expectation:
'INSERT INTO "myTable" ("name","value") VALUES (\'foo\',true),(\'bar\',false);',
expectation: {
query: 'INSERT INTO "myTable" ("name","value") VALUES (\'foo\',true),(\'bar\',false);',
},
},
{
arguments: ['myTable', [{ name: 'foo' }, { name: 'bar' }], { ignoreDuplicates: true }],
expectation: 'INSERT INTO "myTable" ("name") VALUES (\'foo\'),(\'bar\');',
expectation: { query: 'INSERT INTO "myTable" ("name") VALUES (\'foo\'),(\'bar\');' },
},
],

Expand Down

0 comments on commit 5c09897

Please sign in to comment.