Skip to content

Commit

Permalink
feat: add support for parameterStyle
Browse files Browse the repository at this point in the history
  • Loading branch information
Americas committed Mar 19, 2024
1 parent 7494ed3 commit c951e6e
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 31 deletions.
5 changes: 5 additions & 0 deletions packages/core/src/dialects/abstract/index.ts
Expand Up @@ -672,3 +672,8 @@ export type BindCollector = {
*/
getBindParameterOrder(): string[] | null;
};

export enum ParameterStyle {
bind = 'bind',
replacement = 'replacement',
}
22 changes: 13 additions & 9 deletions packages/core/src/dialects/abstract/query-generator.d.ts
Expand Up @@ -11,6 +11,7 @@ import type {
SearchPathable,
} from '../../model.js';
import type { DataType } from './data-types.js';
import type { ParameterStyle } from './index.js';
import type { TableOrModel } from './query-generator-typescript.js';
import { AbstractQueryGeneratorTypeScript } from './query-generator-typescript.js';
import type { AttributeToSqlOptions } from './query-generator.types.js';
Expand All @@ -19,6 +20,8 @@ import type { ColumnsDescription } from './query-interface.types.js';
import type { WhereOptions } from './where-sql-builder-types.js';

type ParameterOptions = {
parameterStyle?: ParameterStyle;
bindParam?: false | ((value: unknown) => string);
// only named replacements are allowed
replacements?: { [key: string]: unknown };
};
Expand All @@ -30,7 +33,6 @@ type SelectOptions<M extends Model> = FindOptions<M> & {
type InsertOptions = ParameterOptions &
SearchPathable & {
exception?: boolean;
bindParam?: false | ((value: unknown) => string);

updateOnDuplicate?: string[];
ignoreDuplicates?: boolean;
Expand All @@ -40,19 +42,19 @@ type InsertOptions = ParameterOptions &

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

updateOnDuplicate?: string[];
ignoreDuplicates?: boolean;
upsertKeys?: string[];
returning?: boolean | Array<string | Literal | Col>;
};

type UpdateOptions = ParameterOptions & {
bindParam?: false | ((value: unknown) => string);
};
type UpdateOptions = ParameterOptions;

type ArithmeticQueryOptions = {
// only named replacements are allowed
replacements?: { [key: string]: unknown };

type ArithmeticQueryOptions = ParameterOptions & {
returning?: boolean | Array<string | Literal | Col>;
};

Expand All @@ -75,6 +77,8 @@ export interface AddColumnQueryOptions {
ifNotExists?: boolean;
}

type BoundQuery = { query: string; bind?: Record<string, unknown> };

/**
* The base class for all query generators, used to generate all SQL queries.
*
Expand All @@ -94,13 +98,13 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
valueHash: object,
columnDefinitions?: { [columnName: string]: NormalizedAttributeOptions },
options?: InsertOptions,
): { query: string; bind?: Record<string, unknown> };
): BoundQuery;
bulkInsertQuery(
tableName: TableName,
newEntries: object[],
options?: BulkInsertOptions,
columnDefinitions?: { [columnName: string]: NormalizedAttributeOptions },
): { query: string; bind?: Record<string, unknown> };
): BoundQuery;

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

arithmeticQuery(
operator: string,
Expand Down
43 changes: 31 additions & 12 deletions packages/core/src/dialects/abstract/query-generator.js
Expand Up @@ -26,6 +26,7 @@ import { joinSQLFragments } from '../../utils/join-sql-fragments';
import { isModelStatic } from '../../utils/model-utils';
import { nameIndex, spliceStr } from '../../utils/string';
import { attributeTypeToSql } from './data-types-utils';
import { ParameterStyle } from './index.js';
import { AbstractQueryGeneratorInternal } from './query-generator-internal.js';
import { AbstractQueryGeneratorTypeScript } from './query-generator-typescript';
import { joinWithLogicalOperator } from './where-sql-builder';
Expand Down Expand Up @@ -77,12 +78,13 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
defaults(options, this.options);

const modelAttributeMap = {};
const bind = Object.create(null);
const fields = [];
const returningModelAttributes = [];
const values = Object.create(null);
const quotedTable = this.quoteTable(table);
let bindParam = options.bindParam === undefined ? this.bindParam(bind) : options.bindParam;
let bind;
let bindParam;
let parameterStyle = options.parameterStyle ?? ParameterStyle.bind;
let query;
let valueQuery = '';
let emptyQuery = '';
Expand Down Expand Up @@ -120,12 +122,17 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
options.searchPath
) {
// Not currently supported with search path (requires output of multiple queries)
bindParam = undefined;
parameterStyle = ParameterStyle.replacement;
}

if (this.dialect.supports.EXCEPTION && options.exception) {
// Not currently supported with bind parameters (requires output of multiple queries)
bindParam = undefined;
parameterStyle = ParameterStyle.replacement;
}

if (parameterStyle === ParameterStyle.bind) {
bind = Object.create(null);
bindParam = options.bindParam || this.bindParam(bind);
}

valueHash = removeNullishValuesFromHash(valueHash, this.options.omitNull);
Expand Down Expand Up @@ -271,7 +278,7 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {

// Used by Postgres upsertQuery and calls to here with options.exception set to true
const result = { query };
if (options.bindParam !== false) {
if (parameterStyle === ParameterStyle.bind) {
result.bind = bind;
}

Expand All @@ -292,13 +299,19 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
options ||= {};
fieldMappedAttributes ||= {};

const bind = Object.create(null);
const bindParam = options.bindParam ?? this.bindParam(bind);
const parameterStyle = options.parameterStyle ?? ParameterStyle.bind;
const tuples = [];
const serials = {};
const allAttributes = [];
let bind;
let bindParam;
let onDuplicateKeyUpdate = '';

if (parameterStyle === ParameterStyle.bind) {
bind = Object.create(null);
bindParam = options.bindParam || this.bindParam(bind);
}

for (const fieldValueHash of fieldValueHashes) {
forOwn(fieldValueHash, (value, key) => {
if (!allAttributes.includes(key)) {
Expand Down Expand Up @@ -404,7 +417,7 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {

const result = { query };

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

Expand All @@ -429,8 +442,10 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
attrValueHash = removeNullishValuesFromHash(attrValueHash, options.omitNull, options);

const values = [];
const bind = Object.create(null);
const modelAttributeMap = {};
let parameterStyle = options.parameterStyle ?? ParameterStyle.bind;
let bind;
let bindParam;
let outputFragment = '';
let tmpTable = ''; // tmpTable declaration for trigger
let suffix = '';
Expand All @@ -440,10 +455,13 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
options.searchPath
) {
// Not currently supported with search path (requires output of multiple queries)
options.bindParam = false;
parameterStyle = ParameterStyle.replacement;
}

const bindParam = options.bindParam === undefined ? this.bindParam(bind) : options.bindParam;
if (parameterStyle === ParameterStyle.bind) {
bind = Object.create(null);
bindParam = options.bindParam || this.bindParam(bind);
}

if (
this.dialect.supports['LIMIT ON UPDATE'] &&
Expand Down Expand Up @@ -511,7 +529,8 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {

// Used by Postgres upsertQuery and calls to here with options.exception set to true
const result = { query };
if (options.bindParam !== false) {

if (parameterStyle === ParameterStyle.bind) {
result.bind = bind;
}

Expand Down
14 changes: 11 additions & 3 deletions packages/core/src/dialects/sqlite/query-generator.js
Expand Up @@ -12,6 +12,7 @@ import {
import defaults from 'lodash/defaults';
import each from 'lodash/each';
import isObject from 'lodash/isObject';
import { ParameterStyle } from '../abstract/index.js';

const { SqliteQueryGeneratorTypeScript } = require('./query-generator-typescript');

Expand Down Expand Up @@ -129,9 +130,15 @@ export class SqliteQueryGenerator extends SqliteQueryGeneratorTypeScript {
attrValueHash = removeNullishValuesFromHash(attrValueHash, options.omitNull, options);

const modelAttributeMap = Object.create(null);
const parameterStyle = options.parameterStyle ?? ParameterStyle.bind;
const values = [];
const bind = Object.create(null);
const bindParam = options.bindParam === undefined ? this.bindParam(bind) : options.bindParam;
let bind;
let bindParam;

if (parameterStyle === ParameterStyle.bind) {
bind = Object.create(null);
bindParam = options.bindParam || this.bindParam(bind);
}

if (attributes) {
each(attributes, (attribute, key) => {
Expand Down Expand Up @@ -167,7 +174,8 @@ export class SqliteQueryGenerator extends SqliteQueryGeneratorTypeScript {
}

const result = { query };
if (options.bindParam !== false) {

if (parameterStyle === ParameterStyle.bind) {
result.bind = bind;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/index.d.ts
Expand Up @@ -68,7 +68,7 @@ export * from './model';
export * from './sequelize';

export { ConstraintChecking, Deferrable } from './deferrable';
export { AbstractDialect } from './dialects/abstract/index.js';
export { AbstractDialect, ParameterStyle } from './dialects/abstract/index.js';
export { AbstractQueryGenerator } from './dialects/abstract/query-generator.js';
export type { WhereOptions } from './dialects/abstract/where-sql-builder-types.js';
export { importModels } from './import-models.js';
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.mjs
Expand Up @@ -27,6 +27,7 @@ export const JSON_NULL = Pkg.JSON_NULL;

// export * from './lib/query-interface';
export const AbstractQueryInterface = Pkg.AbstractQueryInterface;
export const ParameterStyle = Pkg.ParameterStyle;

// export * from './lib/model';
export const Model = Pkg.Model;
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/sequelize.js
Expand Up @@ -13,6 +13,7 @@ import { HasOneAssociation } from './associations/has-one';
import { Association } from './associations/index';
import * as DataTypes from './data-types';
import { ConstraintChecking, Deferrable } from './deferrable';
import { ParameterStyle } from './dialects/abstract/index.js';
import { AbstractQueryInterface } from './dialects/abstract/query-interface';
import { withSqliteForeignKeysOff } from './dialects/sqlite/sqlite-utils';
import * as SequelizeErrors from './errors';
Expand Down Expand Up @@ -1038,6 +1039,7 @@ Use Sequelize#query if you wish to use replacements.`);
static TransactionType = TransactionType;
static Lock = Lock;
static IsolationLevel = IsolationLevel;
static ParameterStyle = ParameterStyle;

log(...args) {
let options;
Expand Down
6 changes: 3 additions & 3 deletions packages/core/test/unit/query-generator/insert-query.test.ts
@@ -1,4 +1,4 @@
import { DataTypes, literal } from '@sequelize/core';
import { DataTypes, ParameterStyle, literal } from '@sequelize/core';
import { expect } from 'chai';
import { beforeAll2, expectsql, sequelize } from '../../support';

Expand Down Expand Up @@ -85,7 +85,7 @@ describe('QueryGenerator#insertQuery', () => {
});
});

it('parses bind parameters in literals even with bindParams: false', () => {
it('parses bind parameters in literals even with parameterStyle: "replacement"', () => {
const { User } = vars;

const { query, bind } = queryGenerator.insertQuery(
Expand All @@ -97,7 +97,7 @@ describe('QueryGenerator#insertQuery', () => {
},
{},
{
bindParam: false,
parameterStyle: ParameterStyle.replacement,
},
);

Expand Down
6 changes: 3 additions & 3 deletions packages/core/test/unit/query-generator/update-query.test.ts
@@ -1,4 +1,4 @@
import { DataTypes, literal } from '@sequelize/core';
import { DataTypes, ParameterStyle, literal } from '@sequelize/core';
import { expect } from 'chai';
import { beforeAll2, expectsql, sequelize } from '../../support';

Expand Down Expand Up @@ -66,7 +66,7 @@ describe('QueryGenerator#updateQuery', () => {
});
});

it('does not generate extra bind params with bindParams: false', async () => {
it('does not generate extra bind params with parameterStyle: "replacement"', async () => {
const { User } = vars;

const { query, bind } = queryGenerator.updateQuery(
Expand All @@ -78,7 +78,7 @@ describe('QueryGenerator#updateQuery', () => {
},
literal('first_name = $2'),
{
bindParam: false,
parameterStyle: ParameterStyle.replacement,
},
);

Expand Down

0 comments on commit c951e6e

Please sign in to comment.