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 25, 2024
1 parent 2433c08 commit 1b7781d
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 32 deletions.
5 changes: 5 additions & 0 deletions packages/core/src/dialects/abstract/index.ts
Expand Up @@ -694,3 +694,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 { AbstractQueryGeneratorTypeScript } from './query-generator-typescript.js';
import type { AttributeToSqlOptions } from './query-generator.internal-types.js';
import type { TableOrModel } 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
3 changes: 2 additions & 1 deletion packages/core/src/index.mjs
Expand Up @@ -84,7 +84,8 @@ export const UniqueConstraintError = Pkg.UniqueConstraintError;
// export { useInflection } from './lib/utils';
export const useInflection = Pkg.useInflection;

// export { QueryTypes, Op, TableHints, IndexHints, DataTypes, Deferrable, ConstraintChecking };
// export { ParameterStyle, QueryTypes, Op, TableHints, IndexHints, DataTypes, GeoJsonType, Deferrable, ConstraintChecking };
export const ParameterStyle = Pkg.ParameterStyle;
export const QueryTypes = Pkg.QueryTypes;
export const Op = Pkg.Op;
export const TableHints = Pkg.TableHints;
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/sequelize.js
Expand Up @@ -15,7 +15,7 @@ import { Association } from './associations/index';
import * as DataTypes from './data-types';
import { ConstraintChecking, Deferrable } from './deferrable';
import { AbstractConnectionManager } from './dialects/abstract/connection-manager.js';
import { AbstractDialect } from './dialects/abstract/index.js';
import { AbstractDialect, ParameterStyle } from './dialects/abstract/index.js';
import { AbstractQueryGenerator } from './dialects/abstract/query-generator.js';
import { AbstractQueryInterface } from './dialects/abstract/query-interface';
import { AbstractQuery } from './dialects/abstract/query.js';
Expand Down Expand Up @@ -1022,6 +1022,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 1b7781d

Please sign in to comment.