src/queryinterface.js
/**
* Decorator for Sequelize's query interface
* @param {Sequelize.QueryInterface} queryInterface
* @return {Sequelize.QueryInterface}
*/
export default function decorateQueryInterface(queryInterface) {
queryInterface.dropView = function(viewName, options = {}) {
let sql = `DROP VIEW IF EXISTS ` +
`${this.QueryGenerator.quoteTable(viewName)}`
;
if (this.sequelize.options.dialect === 'postgres' && options.cascade) {
sql += ' CASCADE';
}
return this.sequelize.query(sql);
};
queryInterface.generateQueryFromOptions = function(subQueryOptions) {
const {
qtableName,
qmodel,
fieldsMap,
qoptions,
} = subQueryOptions;
const afieldMap = Object.keys(fieldsMap || {})
.map((k) => [k, fieldsMap[k]])
;
const query = this.QueryGenerator.selectQuery(
qtableName,
qoptions,
qmodel,
)
.replace(/;$/, '')
;
return [query, afieldMap];
};
/**
* Create view method
* @param {string} viewName
* @param {object} attributes
* @param {object} subQueryOptions
* @param {object} options
* @param {Model} model
* @return {Promise}
*/
queryInterface.createView = async function(
viewName, attributes, subQueryOptions, options, model
) {
const attrStr = [];
let sql = '';
attributes = Object.keys(attributes).map((attribute) => this
.sequelize
.normalizeAttribute(attributes[attribute])
);
// Pick options for exclude defaults, index, etc
attributes = attributes.map(({
type, Model, fieldName, _modelAttrbute, field,
}) => ({type, Model, fieldName, _modelAttrbute, field}));
// Postgres requires special SQL commands for ENUM/ENUM[]
/*
if (this.sequelize.options.dialect === 'postgres') {
await PostgresQueryInterface
.ensureEnums(this, viewName, attributes, options, model);
}
*/
attributes = this.QueryGenerator.attributesToSQL(
attributes, {
table: viewName,
context: 'createTable',
},
);
for (const attr in attributes) {
if (!attr.match(/^[a-z0-9]/i)) {
continue;
}
const quotedAttr = this.QueryGenerator.quoteIdentifier(attr);
const dataType = attributes[attr];
const i = attributes[attr].indexOf('COMMENT ');
if (i !== -1) {
attributes[attr] = attributes[attr].substring(0, i);
}
if (this.sequelize.options.dialect === 'postgres') {
const dataType = this.QueryGenerator.dataTypeMapping(
viewName,
attr,
attributes[attr]
);
attrStr.push(`${quotedAttr} ${dataType}`);
} else if (this.sequelize.options.dialect === 'sqlite') {
attrStr.push(`${quotedAttr} ${dataType}`);
}
}
let query = subQueryOptions;
let afieldMap = [];
if (typeof queryOptions !== 'string') {
[query, afieldMap] = this.generateQueryFromOptions(subQueryOptions);
}
const selattrs = Object.keys(attributes)
.map((field) => afieldMap.find(([f]) => f === field) || [field])
.map(([f, t]) => `${this.QueryGenerator.quoteIdentifier(t || f)}` +
(t ? ` AS ${this.QueryGenerator.quoteIdentifier(f)}` : ``)
)
.join(',')
;
switch (this.sequelize.options.dialect) {
case 'sqlite':
sql = `CREATE VIEW IF NOT EXISTS ` +
`${this.QueryGenerator.quoteTable(viewName)} AS SELECT ` +
`${selattrs} FROM (${query});`
;
break;
case 'postgres':
sql = `CREATE OR REPLACE VIEW ` +
`${this.QueryGenerator.quoteTable(viewName)} AS SELECT ` +
`${selattrs} FROM (${query}) as sq;`
;
break;
default:
throw new Error(
`View support is not compatilbe with this dialect`
);
}
return this.sequelize.query(sql, options);
};
return queryInterface;
}