import { DataTypes, literal } from '@sequelize/core'; import { expect } from 'chai'; import { beforeAll2, expectsql, sequelize } from '../../support'; describe('QueryGenerator#insertQuery', () => { const queryGenerator = sequelize.queryGenerator; const vars = beforeAll2(() => { const User = sequelize.define( 'User', { firstName: DataTypes.STRING, }, { timestamps: false }, ); return { User }; }); // you'll find more replacement tests in query-generator tests it('parses named replacements in literals', () => { const { User } = vars; const { query, bind } = queryGenerator.insertQuery( User.table, { firstName: literal(':name'), }, {}, { replacements: { name: 'Zoe', }, }, ); expectsql(query, { default: `INSERT INTO [Users] ([firstName]) VALUES ('Zoe');`, mssql: `INSERT INTO [Users] ([firstName]) VALUES (N'Zoe');`, db2: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ('Zoe'));`, ibmi: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ('Zoe'))`, }); expect(bind).to.deep.eq({}); }); it('supports named bind parameters in literals', () => { const { User } = vars; const { query, bind } = queryGenerator.insertQuery(User.table, { firstName: 'John', lastName: literal('$lastName'), username: 'jd', }); expectsql(query, { default: `INSERT INTO [Users] ([firstName],[lastName],[username]) VALUES ($sequelize_1,$lastName,$sequelize_2);`, db2: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName","lastName","username") VALUES ($sequelize_1,$lastName,$sequelize_2));`, ibmi: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName","lastName","username") VALUES ($sequelize_1,$lastName,$sequelize_2))`, }); expect(bind).to.deep.eq({ sequelize_1: 'John', sequelize_2: 'jd', }); }); it('parses positional bind parameters in literals', () => { const { User } = vars; const { query, bind } = queryGenerator.insertQuery(User.table, { firstName: 'John', lastName: literal('$1'), username: 'jd', }); // lastName's bind position being changed from $1 to $2 is intentional: bind array order must match their order in the query in some dialects. expectsql(query, { default: `INSERT INTO [Users] ([firstName],[lastName],[username]) VALUES ($sequelize_1,$1,$sequelize_2);`, db2: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName","lastName","username") VALUES ($sequelize_1,$1,$sequelize_2));`, ibmi: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName","lastName","username") VALUES ($sequelize_1,$1,$sequelize_2))`, }); expect(bind).to.deep.eq({ sequelize_1: 'John', sequelize_2: 'jd', }); }); it('parses bind parameters in literals even with bindParams: false', () => { const { User } = vars; const { query, bind } = queryGenerator.insertQuery( User.table, { firstName: 'John', lastName: literal('$1'), username: 'jd', }, {}, { bindParam: false, }, ); expectsql(query, { default: `INSERT INTO [Users] ([firstName],[lastName],[username]) VALUES ('John',$1,'jd');`, mssql: `INSERT INTO [Users] ([firstName],[lastName],[username]) VALUES (N'John',$1,N'jd');`, db2: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName","lastName","username") VALUES ('John',$1,'jd'));`, ibmi: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName","lastName","username") VALUES ('John',$1,'jd'))`, }); expect(bind).to.be.undefined; }); // This test was added due to a regression where these values were being converted to strings it('binds number values', () => { if (!sequelize.dialect.supports.dataTypes.ARRAY) { return; } const { User } = vars; const { query, bind } = queryGenerator.insertQuery(User.tableName, { numbers: [1, 2, 3], }); expectsql(query, { default: `INSERT INTO "Users" ([numbers]) VALUES ($sequelize_1);`, db2: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("numbers") VALUES ($sequelize_1));`, ibmi: `SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("numbers") VALUES ($sequelize_1))`, }); expect(bind).to.deep.eq({ sequelize_1: [1, 2, 3], }); }); describe('returning', () => { it('supports returning: true', () => { const { User } = vars; const { query } = queryGenerator.insertQuery( User.table, { firstName: 'John', }, User.getAttributes(), { returning: true, }, ); expectsql(query, { default: `INSERT INTO [Users] ([firstName]) VALUES ($sequelize_1) RETURNING [id], [firstName];`, // TODO: insertQuery should throw if returning is not supported 'mysql mariadb': `INSERT INTO \`Users\` (\`firstName\`) VALUES ($sequelize_1);`, // TODO: insertQuery should throw if returning is not supported snowflake: `INSERT INTO "Users" ("firstName") VALUES ($sequelize_1);`, mssql: 'INSERT INTO [Users] ([firstName]) OUTPUT INSERTED.[id], INSERTED.[firstName] VALUES ($sequelize_1);', db2: 'SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ($sequelize_1));', ibmi: 'SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ($sequelize_1))', }); }); it('supports array of strings (column names)', () => { const { User } = vars; const { query } = queryGenerator.insertQuery( User.table, { firstName: 'John', }, User.getAttributes(), { returning: ['*', 'myColumn'], }, ); expectsql(query, { default: `INSERT INTO [Users] ([firstName]) VALUES ($sequelize_1) RETURNING [*], [myColumn];`, // TODO: insertQuery should throw if returning is not supported 'mysql mariadb': `INSERT INTO \`Users\` (\`firstName\`) VALUES ($sequelize_1);`, // TODO: insertQuery should throw if returning is not supported snowflake: `INSERT INTO "Users" ("firstName") VALUES ($sequelize_1);`, mssql: 'INSERT INTO [Users] ([firstName]) OUTPUT INSERTED.[*], INSERTED.[myColumn] VALUES ($sequelize_1);', // TODO: should only select specified columns db2: 'SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ($sequelize_1));', ibmi: 'SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ($sequelize_1))', }); }); it('supports array of literals', () => { const { User } = vars; expectsql( () => { return queryGenerator.insertQuery( User.table, { firstName: 'John', }, User.getAttributes(), { returning: [literal('*')], }, ).query; }, { default: `INSERT INTO [Users] ([firstName]) VALUES ($sequelize_1) RETURNING *;`, // TODO: insertQuery should throw if returning is not supported 'mysql mariadb': `INSERT INTO \`Users\` (\`firstName\`) VALUES ($sequelize_1);`, // TODO: insertQuery should throw if returning is not supported snowflake: `INSERT INTO "Users" ("firstName") VALUES ($sequelize_1);`, mssql: new Error( 'literal() cannot be used in the "returning" option array in mssql. Use col(), or a string instead.', ), // TODO: should only select specified columns db2: 'SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ($sequelize_1));', ibmi: 'SELECT * FROM FINAL TABLE (INSERT INTO "Users" ("firstName") VALUES ($sequelize_1))', }, ); }); it('binds date values', () => { const result = queryGenerator.insertQuery('myTable', { birthday: new Date('2011-03-27T10:01:55Z'), }); expectsql(result, { query: { default: 'INSERT INTO [myTable] ([birthday]) VALUES ($sequelize_1);', 'db2 ibmi': 'SELECT * FROM FINAL TABLE (INSERT INTO "myTable" ("birthday") VALUES ($sequelize_1));', }, bind: { mysql: { sequelize_1: '2011-03-27 10:01:55.000', }, mariadb: { sequelize_1: '2011-03-27 10:01:55.000', }, db2: { sequelize_1: '2011-03-27 10:01:55.000', }, ibmi: { sequelize_1: '2011-03-27 10:01:55.000', }, snowflake: { sequelize_1: '2011-03-27 10:01:55.000', }, sqlite3: { sequelize_1: '2011-03-27 10:01:55.000 +00:00', }, postgres: { sequelize_1: '2011-03-27 10:01:55.000 +00:00', }, mssql: { sequelize_1: '2011-03-27 10:01:55.000 +00:00', }, }, }); }); it('binds boolean values', () => { const result = queryGenerator.insertQuery('myTable', { positive: true, negative: false }); expectsql(result, { query: { default: 'INSERT INTO [myTable] ([positive],[negative]) VALUES ($sequelize_1,$sequelize_2);', 'db2 ibmi': 'SELECT * FROM FINAL TABLE (INSERT INTO "myTable" ("positive","negative") VALUES ($sequelize_1,$sequelize_2));', }, bind: { sqlite3: { sequelize_1: 1, sequelize_2: 0, }, mysql: { sequelize_1: 1, sequelize_2: 0, }, mariadb: { sequelize_1: 1, sequelize_2: 0, }, mssql: { sequelize_1: 1, sequelize_2: 0, }, postgres: { sequelize_1: true, sequelize_2: false, }, db2: { sequelize_1: true, sequelize_2: false, }, ibmi: { sequelize_1: 1, sequelize_2: 0, }, snowflake: { sequelize_1: true, sequelize_2: false, }, }, }); }); // TODO: Should we ignore undefined values instead? undefined is closer to "missing property" than null it('treats undefined as null', () => { const { query, bind } = queryGenerator.insertQuery('myTable', { value: undefined, name: 'bar', }); expectsql(query, { default: 'INSERT INTO [myTable] ([value],[name]) VALUES ($sequelize_1,$sequelize_2);', 'db2 ibmi': 'SELECT * FROM FINAL TABLE (INSERT INTO "myTable" ("value","name") VALUES ($sequelize_1,$sequelize_2));', }); expect(bind).to.deep.eq({ sequelize_1: null, sequelize_2: 'bar', }); }); }); });