BE-SPAN/node_modules/sequelize-oracle/lib/dialects/postgres/query-generator.js
githubna-ilham 722cd440e8 first commit
2024-10-20 22:04:16 +07:00

944 lines
31 KiB
JavaScript

'use strict';
/* jshint -W110 */
var Utils = require('../../utils')
, hstore = require('./hstore')
, range = require('./range')
, util = require('util')
, DataTypes = require('../../data-types')
, SqlString = require('../../sql-string')
, AbstractQueryGenerator = require('../abstract/query-generator')
, primaryKeys = {};
var QueryGenerator = {
options: {},
dialect: 'postgres',
createSchema: function(schema) {
var query = 'CREATE SCHEMA <%= schema%>;';
return Utils._.template(query)({schema: schema});
},
dropSchema: function(schema) {
var query = 'DROP SCHEMA <%= schema%> CASCADE;';
return Utils._.template(query)({schema: schema});
},
showSchemasQuery: function() {
return "SELECT schema_name FROM information_schema.schemata WHERE schema_name <> 'information_schema' AND schema_name != 'public' AND schema_name !~ E'^pg_';";
},
versionQuery: function() {
return 'SELECT VERSION() as "version"';
},
createTableQuery: function(tableName, attributes, options) {
var self = this;
options = Utils._.extend({
}, options || {});
primaryKeys[tableName] = [];
var query = 'CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>)<%= comments %>'
, comments = ''
, attrStr = []
, i;
if (options.comment && Utils._.isString(options.comment)) {
comments += '; COMMENT ON TABLE <%= table %> IS ' + this.escape(options.comment);
}
for (var attr in attributes) {
if ((i = attributes[attr].indexOf('COMMENT')) !== -1) {
// Move comment to a seperate query
comments += '; ' + attributes[attr].substring(i);
attributes[attr] = attributes[attr].substring(0, i);
}
var dataType = this.pgDataTypeMapping(tableName, attr, attributes[attr]);
attrStr.push(this.quoteIdentifier(attr) + ' ' + dataType);
}
var values = {
table: this.quoteTable(tableName),
attributes: attrStr.join(', '),
comments: Utils._.template(comments)({ table: this.quoteTable(tableName)})
};
if (!!options.uniqueKeys) {
Utils._.each(options.uniqueKeys, function(columns) {
if (!columns.singleField) { // If it's a single field its handled in column def, not as an index
values.attributes += ', UNIQUE (' + columns.fields.map(function(f) { return self.quoteIdentifiers(f); }).join(', ') + ')';
}
});
}
var pks = primaryKeys[tableName].map(function(pk) {
return this.quoteIdentifier(pk);
}.bind(this)).join(',');
if (pks.length > 0) {
values.attributes += ', PRIMARY KEY (' + pks + ')';
}
return Utils._.template(query)(values).trim() + ';';
},
dropTableQuery: function(tableName, options) {
options = options || {};
var query = 'DROP TABLE IF EXISTS <%= table %><%= cascade %>;';
return Utils._.template(query)({
table: this.quoteTable(tableName),
cascade: options.cascade ? ' CASCADE' : ''
});
},
showTablesQuery: function() {
return "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';";
},
describeTableQuery: function(tableName, schema) {
if (!schema) {
schema = 'public';
}
var query = 'SELECT c.column_name as "Field", c.column_default as "Default", c.is_nullable as "Null", ' +
"CASE WHEN c.udt_name = 'hstore' " +
'THEN c.udt_name ELSE c.data_type END as "Type", (SELECT array_agg(e.enumlabel) ' +
'FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS "special" ' +
'FROM information_schema.columns c WHERE table_name = <%= table %> AND table_schema = <%= schema %>';
return Utils._.template(query)({
table: this.escape(tableName),
schema: this.escape(schema)
});
},
// A recursive parser for nested where conditions
parseConditionObject: function(_conditions, path) {
var self = this;
path = path || [];
return Utils._.reduce(_conditions, function (r, v, k) { // result, key, value
if (Utils._.isObject(v)) {
r = r.concat(self.parseConditionObject(v, path.concat(k))); // Recursively parse objects
} else {
r.push({ path: path.concat(k), value: v });
}
return r;
}, []);
},
handleSequelizeMethod: function (smth, tableName, factory, options, prepend) {
var _ = Utils._;
if (smth instanceof Utils.json) {
// Parse nested object
if (smth.conditions) {
var conditions = _.map(this.parseConditionObject(smth.conditions), function generateSql(condition) {
return util.format("%s#>>'{%s}' = '%s'",
_.first(condition.path),
_.rest(condition.path).join(','),
condition.value);
});
return conditions.join(' and ');
} else if (smth.path) {
var str;
// Allow specifying conditions using the postgres json syntax
if (_.any(['->', '->>', '#>'], _.partial(_.contains, smth.path))) {
str = smth.path;
} else {
// Also support json dot notation
var path = smth.path.split('.');
str = util.format("%s#>>'{%s}'",
_.first(path),
_.rest(path).join(','));
}
if (smth.value) {
str += util.format(' = %s', this.escape(smth.value));
}
return str;
}
} else {
return AbstractQueryGenerator.handleSequelizeMethod.call(this, smth, tableName, factory, options, prepend);
}
},
addColumnQuery: function(table, key, dataType) {
var query = 'ALTER TABLE <%= table %> ADD COLUMN <%= attribute %>;'
, dbDataType = this.attributeToSQL(dataType, {context: 'addColumn'})
, attribute;
if (dataType.type && dataType.type instanceof DataTypes.ENUM || dataType instanceof DataTypes.ENUM) {
query = this.pgEnum(table, key, dataType) + query;
}
attribute = Utils._.template('<%= key %> <%= definition %>')({
key: this.quoteIdentifier(key),
definition: this.pgDataTypeMapping(table, key, dbDataType)
});
return Utils._.template(query)({
table: this.quoteTable(table),
attribute: attribute
});
},
removeColumnQuery: function(tableName, attributeName) {
var query = 'ALTER TABLE <%= tableName %> DROP COLUMN <%= attributeName %>;';
return Utils._.template(query)({
tableName: this.quoteTable(tableName),
attributeName: this.quoteIdentifier(attributeName)
});
},
changeColumnQuery: function(tableName, attributes) {
var query = 'ALTER TABLE <%= tableName %> ALTER COLUMN <%= query %>;'
, sql = [];
for (var attributeName in attributes) {
var definition = this.pgDataTypeMapping(tableName, attributeName, attributes[attributeName]);
var attrSql = '';
if (definition.indexOf('NOT NULL') > 0) {
attrSql += Utils._.template(query)({
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' SET NOT NULL'
});
definition = definition.replace('NOT NULL', '').trim();
} else {
attrSql += Utils._.template(query)({
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' DROP NOT NULL'
});
}
if (definition.indexOf('DEFAULT') > 0) {
attrSql += Utils._.template(query)({
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' SET DEFAULT ' + definition.match(/DEFAULT ([^;]+)/)[1]
});
definition = definition.replace(/(DEFAULT[^;]+)/, '').trim();
} else {
attrSql += Utils._.template(query)({
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' DROP DEFAULT'
});
}
if (attributes[attributeName].match(/^ENUM\(/)) {
query = this.pgEnum(tableName, attributeName, attributes[attributeName]) + query;
definition = definition.replace(/^ENUM\(.+\)/, this.quoteIdentifier('enum_' + tableName + '_' + attributeName));
definition += ' USING (' + this.quoteIdentifier(attributeName) + '::' + this.quoteIdentifier(definition) + ')';
}
if (definition.match(/UNIQUE;*$/)) {
definition = definition.replace(/UNIQUE;*$/, '');
attrSql += Utils._.template(query.replace('ALTER COLUMN', ''))({
tableName: this.quoteTable(tableName),
query: 'ADD CONSTRAINT ' + this.quoteIdentifier(attributeName + '_unique_idx') + ' UNIQUE (' + this.quoteIdentifier(attributeName) + ')'
});
}
attrSql += Utils._.template(query)({
tableName: this.quoteTable(tableName),
query: this.quoteIdentifier(attributeName) + ' TYPE ' + definition
});
sql.push(attrSql);
}
return sql.join('');
},
renameColumnQuery: function(tableName, attrBefore, attributes) {
var query = 'ALTER TABLE <%= tableName %> RENAME COLUMN <%= attributes %>;';
var attrString = [];
for (var attributeName in attributes) {
attrString.push(Utils._.template('<%= before %> TO <%= after %>')({
before: this.quoteIdentifier(attrBefore),
after: this.quoteIdentifier(attributeName)
}));
}
return Utils._.template(query)({
tableName: this.quoteTable(tableName),
attributes: attrString.join(', ')
});
},
fn: function(fnName, tableName, body, returns, language) {
fnName = fnName || 'testfunc';
language = language || 'plpgsql';
returns = returns || 'SETOF ' + this.quoteTable(tableName);
var query = 'CREATE OR REPLACE FUNCTION pg_temp.<%= fnName %>() RETURNS <%= returns %> AS $func$ BEGIN <%= body %> END; $func$ LANGUAGE <%= language %>; SELECT * FROM pg_temp.<%= fnName %>();';
return Utils._.template(query)({
fnName: fnName,
returns: returns,
language: language,
body: body
});
},
exceptionFn: function(fnName, tableName, main, then, when, returns, language) {
when = when || 'unique_violation';
var body = '<%= main %> EXCEPTION WHEN <%= when %> THEN <%= then %>;';
body = Utils._.template(body)({
main: main,
when: when,
then: then
});
return this.fn(fnName, tableName, body, returns, language);
},
// http://www.maori.geek.nz/post/postgres_upsert_update_or_insert_in_ger_using_knex_js
upsertQuery: function (tableName, insertValues, updateValues, where, rawAttributes, options) {
var insert = this.insertQuery(tableName, insertValues, rawAttributes, options);
var update = this.updateQuery(tableName, updateValues, where, options, rawAttributes);
// The numbers here are selected to match the number of affected rows returned by MySQL
return this.exceptionFn(
'sequelize_upsert',
tableName,
insert + ' RETURN 1;',
update + '; RETURN 2',
'unique_violation',
'integer'
);
},
bulkInsertQuery: function(tableName, attrValueHashes, options, modelAttributes) {
options = options || {};
var query = 'INSERT INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %>'
, tuples = []
, serials = []
, allAttributes = [];
if (options.returning) {
query += ' RETURNING *';
}
Utils._.forEach(attrValueHashes, function(attrValueHash) {
Utils._.forOwn(attrValueHash, function(value, key) {
if (allAttributes.indexOf(key) === -1) {
allAttributes.push(key);
}
if (modelAttributes && modelAttributes[key] && modelAttributes[key].autoIncrement === true) {
serials.push(key);
}
});
});
Utils._.forEach(attrValueHashes, function(attrValueHash) {
tuples.push('(' +
allAttributes.map(function(key) {
if (serials.indexOf(key) !== -1) {
return attrValueHash[key] || 'DEFAULT';
}
return this.escape(attrValueHash[key], modelAttributes && modelAttributes[key]);
}.bind(this)).join(',') +
')');
}.bind(this));
var replacements = {
table: this.quoteTable(tableName)
, attributes: allAttributes.map(function(attr) {
return this.quoteIdentifier(attr);
}.bind(this)).join(',')
, tuples: tuples.join(',')
};
query = query + ';';
return Utils._.template(query)(replacements);
},
deleteQuery: function(tableName, where, options, model) {
var query;
options = options || {};
tableName = Utils.removeTicks(this.quoteTable(tableName), '"');
if (options.truncate === true) {
query = 'TRUNCATE ' + QueryGenerator.quoteIdentifier(tableName);
if (options.cascade) {
query += ' CASCADE';
}
return query;
}
if (Utils._.isUndefined(options.limit)) {
options.limit = 1;
}
primaryKeys[tableName] = primaryKeys[tableName] || [];
if (!!model && primaryKeys[tableName].length < 1) {
primaryKeys[tableName] = Object.keys(model.primaryKeys);
}
if (options.limit) {
query = 'DELETE FROM <%= table %> WHERE <%= primaryKeys %> IN (SELECT <%= primaryKeysSelection %> FROM <%= table %><%= where %><%= limit %>)';
} else {
query = 'DELETE FROM <%= table %><%= where %>';
}
var pks;
if (primaryKeys[tableName] && primaryKeys[tableName].length > 0) {
pks = primaryKeys[tableName].map(function(pk) {
return this.quoteIdentifier(pk);
}.bind(this)).join(',');
} else {
pks = this.quoteIdentifier('id');
}
var replacements = {
table: this.quoteIdentifiers(tableName),
where: this.getWhereConditions(where),
limit: !!options.limit ? ' LIMIT ' + this.escape(options.limit) : '',
primaryKeys: primaryKeys[tableName].length > 1 ? '(' + pks + ')' : pks,
primaryKeysSelection: pks
};
if (replacements.where) {
replacements.where = ' WHERE ' + replacements.where;
}
return Utils._.template(query)(replacements);
},
showIndexesQuery: function(tableName) {
if (!Utils._.isString(tableName)) {
tableName = tableName.tableName;
}
// This is ARCANE!
var query = 'SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, ' +
'array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) ' +
'AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a ' +
'WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND a.attnum = ANY(ix.indkey) AND '+
"t.relkind = 'r' and t.relname = '<%= tableName %>' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;";
return Utils._.template(query)({ tableName: tableName });
},
removeIndexQuery: function(tableName, indexNameOrAttributes) {
var sql = 'DROP INDEX IF EXISTS <%= indexName %>'
, indexName = indexNameOrAttributes;
if (typeof indexName !== 'string') {
indexName = Utils.inflection.underscore(tableName + '_' + indexNameOrAttributes.join('_'));
}
return Utils._.template(sql)({
tableName: this.quoteIdentifiers(tableName),
indexName: this.quoteIdentifiers(indexName)
});
},
addLimitAndOffset: function(options) {
var fragment = '';
if (options.limit) fragment += ' LIMIT ' + options.limit;
if (options.offset) fragment += ' OFFSET ' + options.offset;
return fragment;
},
attributeToSQL: function(attribute) {
if (!Utils._.isPlainObject(attribute)) {
attribute = {
type: attribute
};
}
var template = '<%= type %>'
, replacements = {};
if (attribute.type instanceof DataTypes.ENUM) {
if (attribute.type.values && !attribute.values) attribute.values = attribute.type.values;
if (Array.isArray(attribute.values) && (attribute.values.length > 0)) {
replacements.type = 'ENUM(' + Utils._.map(attribute.values, function(value) {
return this.escape(value);
}.bind(this)).join(', ') + ')';
} else {
throw new Error("Values for ENUM haven't been defined.");
}
}
if (!replacements.type) {
replacements.type = attribute.type;
}
if (attribute.hasOwnProperty('allowNull') && (!attribute.allowNull)) {
template += ' NOT NULL';
}
if (attribute.autoIncrement) {
template += ' SERIAL';
}
if (Utils.defaultValueSchemable(attribute.defaultValue)) {
template += ' DEFAULT <%= defaultValue %>';
replacements.defaultValue = this.escape(attribute.defaultValue, attribute);
}
if (attribute.unique === true) {
template += ' UNIQUE';
}
if (attribute.primaryKey) {
template += ' PRIMARY KEY';
}
if (attribute.references) {
attribute = Utils.formatReferences(attribute);
template += ' REFERENCES <%= referencesTable %> (<%= referencesKey %>)';
replacements.referencesTable = this.quoteTable(attribute.references.model);
if (attribute.references.key) {
replacements.referencesKey = this.quoteIdentifiers(attribute.references.key);
} else {
replacements.referencesKey = this.quoteIdentifier('id');
}
if (attribute.onDelete) {
template += ' ON DELETE <%= onDeleteAction %>';
replacements.onDeleteAction = attribute.onDelete.toUpperCase();
}
if (attribute.onUpdate) {
template += ' ON UPDATE <%= onUpdateAction %>';
replacements.onUpdateAction = attribute.onUpdate.toUpperCase();
}
if (attribute.references.deferrable) {
template += ' <%= deferrable %>';
replacements.deferrable = attribute.references.deferrable.toString(this);
}
}
return Utils._.template(template)(replacements);
},
deferConstraintsQuery: function (options) {
return options.deferrable.toString(this);
},
setConstraintQuery: function (columns, type) {
var columnFragment = 'ALL';
if (columns) {
columnFragment = columns.map(function (column) {
return this.quoteIdentifier(column);
}.bind(this)).join(', ');
}
return 'SET CONSTRAINTS ' + columnFragment + ' ' + type;
},
setDeferredQuery: function (columns) {
return this.setConstraintQuery(columns, 'DEFERRED');
},
setImmediateQuery: function (columns) {
return this.setConstraintQuery(columns, 'IMMEDIATE');
},
attributesToSQL: function(attributes, options) {
var result = {}
, key
, attribute;
for (key in attributes) {
attribute = attributes[key];
result[attribute.field || key] = this.attributeToSQL(attribute, options);
}
return result;
},
findAutoIncrementField: function(factory) {
var fields = [];
for (var name in factory.attributes) {
var definition = factory.attributes[name];
if (definition && definition.autoIncrement) {
fields.push(name);
}
}
return fields;
},
createTrigger: function(tableName, triggerName, eventType, fireOnSpec, functionName, functionParams, optionsArray) {
var sql = [
'CREATE <%= constraintVal %>TRIGGER <%= triggerName %>'
, '<%= eventType %> <%= eventSpec %>'
, 'ON <%= tableName %>'
, '<%= optionsSpec %>'
, 'EXECUTE PROCEDURE <%= functionName %>(<%= paramList %>);'
].join('\n\t');
return Utils._.template(sql)({
constraintVal: this.triggerEventTypeIsConstraint(eventType),
triggerName: triggerName,
eventType: this.decodeTriggerEventType(eventType),
eventSpec: this.expandTriggerEventSpec(fireOnSpec),
tableName: tableName,
optionsSpec: this.expandOptions(optionsArray),
functionName: functionName,
paramList: this.expandFunctionParamList(functionParams)
});
},
dropTrigger: function(tableName, triggerName) {
var sql = 'DROP TRIGGER <%= triggerName %> ON <%= tableName %> RESTRICT;';
return Utils._.template(sql)({
triggerName: triggerName,
tableName: tableName
});
},
renameTrigger: function(tableName, oldTriggerName, newTriggerName) {
var sql = 'ALTER TRIGGER <%= oldTriggerName %> ON <%= tableName %> RENAME TO <%= newTriggerName%>;';
return Utils._.template(sql)({
tableName: tableName,
oldTriggerName: oldTriggerName,
newTriggerName: newTriggerName
});
},
createFunction: function(functionName, params, returnType, language, body, options) {
var sql = ['CREATE FUNCTION <%= functionName %>(<%= paramList %>)'
, 'RETURNS <%= returnType %> AS $func$'
, 'BEGIN'
, '\t<%= body %>'
, 'END;'
, "$func$ language '<%= language %>'<%= options %>;"
].join('\n');
return Utils._.template(sql)({
functionName: functionName,
paramList: this.expandFunctionParamList(params),
returnType: returnType,
body: body.replace('\n', '\n\t'),
language: language,
options: this.expandOptions(options)
});
},
dropFunction: function(functionName, params) {
// RESTRICT is (currently, as of 9.2) default but we'll be explicit
var sql = 'DROP FUNCTION <%= functionName %>(<%= paramList %>) RESTRICT;';
return Utils._.template(sql)({
functionName: functionName,
paramList: this.expandFunctionParamList(params)
});
},
renameFunction: function(oldFunctionName, params, newFunctionName) {
var sql = 'ALTER FUNCTION <%= oldFunctionName %>(<%= paramList %>) RENAME TO <%= newFunctionName %>;';
return Utils._.template(sql)({
oldFunctionName: oldFunctionName,
paramList: this.expandFunctionParamList(params),
newFunctionName: newFunctionName
});
},
databaseConnectionUri: function(config) {
var template = '<%= protocol %>://<%= user %>:<%= password %>@<%= host %><% if(port) { %>:<%= port %><% } %>/<%= database %><% if(ssl) { %>?ssl=<%= ssl %><% } %>';
return Utils._.template(template)({
user: config.username,
password: config.password,
database: config.database,
host: config.host,
port: config.port,
protocol: config.protocol,
ssl: config.ssl
});
},
pgEscapeAndQuote: function(val) {
return this.quoteIdentifier(Utils.removeTicks(this.escape(val), "'"));
},
expandFunctionParamList: function expandFunctionParamList(params) {
if (Utils._.isUndefined(params) || !Utils._.isArray(params)) {
throw new Error('expandFunctionParamList: function parameters array required, including an empty one for no arguments');
}
var paramList = Utils._.each(params, function expandParam(curParam) {
var paramDef = [];
if (Utils._.has(curParam, 'type')) {
if (Utils._.has(curParam, 'direction')) { paramDef.push(curParam.direction); }
if (Utils._.has(curParam, 'name')) { paramDef.push(curParam.name); }
paramDef.push(curParam.type);
} else {
throw new Error('createFunction called with a parameter with no type');
}
return paramDef.join(' ');
});
return paramList.join(', ');
},
expandOptions: function expandOptions(options) {
return Utils._.isUndefined(options) || Utils._.isEmpty(options) ?
'' : '\n\t' + options.join('\n\t');
},
decodeTriggerEventType: function decodeTriggerEventType(eventSpecifier) {
var EVENT_DECODER = {
'after': 'AFTER',
'before': 'BEFORE',
'instead_of': 'INSTEAD OF',
'after_constraint': 'AFTER'
};
if (!Utils._.has(EVENT_DECODER, eventSpecifier)) {
throw new Error('Invalid trigger event specified: ' + eventSpecifier);
}
return EVENT_DECODER[eventSpecifier];
},
triggerEventTypeIsConstraint: function triggerEventTypeIsConstraint(eventSpecifier) {
return eventSpecifier === 'after_constrain' ? 'CONSTRAINT ' : '';
},
expandTriggerEventSpec: function expandTriggerEventSpec(fireOnSpec) {
if (Utils._.isEmpty(fireOnSpec)) {
throw new Error('no table change events specified to trigger on');
}
return Utils._.map(fireOnSpec, function parseTriggerEventSpec(fireValue, fireKey) {
var EVENT_MAP = {
'insert': 'INSERT',
'update': 'UPDATE',
'delete': 'DELETE',
'truncate': 'TRUNCATE'
};
if (!Utils._.has(EVENT_MAP, fireKey)) {
throw new Error('parseTriggerEventSpec: undefined trigger event ' + fireKey);
}
var eventSpec = EVENT_MAP[fireKey];
if (eventSpec === 'UPDATE') {
if (Utils._.isArray(fireValue) && fireValue.length > 0) {
eventSpec += ' OF ' + fireValue.join(', ');
}
}
return eventSpec;
}).join(' OR ');
},
pgListEnums: function(tableName, attrName, options) {
if (arguments.length === 1) {
options = tableName;
tableName = null;
}
var enumName = '';
if (!!tableName && !!attrName) {
enumName = ' AND t.typname=' + this.escape('enum_' + tableName + '_' + attrName) + ' ';
}
var query = 'SELECT t.typname enum_name, array_agg(e.enumlabel) enum_value FROM pg_type t ' +
'JOIN pg_enum e ON t.oid = e.enumtypid ' +
'JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace ' +
"WHERE n.nspname = 'public' " + enumName + ' GROUP BY 1';
return query;
},
pgEnum: function(tableName, attr, dataType, options) {
var enumName = this.pgEscapeAndQuote('enum_' + tableName + '_' + attr)
, values;
if (dataType.values) {
values = "ENUM('" + dataType.values.join("', '") + "')";
} else {
values = dataType.toString().match(/^ENUM\(.+\)/)[0];
}
var sql = 'CREATE TYPE ' + enumName + ' AS ' + values + '; ';
if (!!options && options.force === true) {
sql = this.pgEnumDrop(tableName, attr) + sql;
}
return sql;
},
pgEnumAdd: function(tableName, attr, value, options) {
var enumName = this.pgEscapeAndQuote('enum_' + tableName + '_' + attr);
var sql = 'ALTER TYPE ' + enumName + ' ADD VALUE ' + this.escape(value);
if (!!options.before) {
sql += ' BEFORE ' + this.escape(options.before);
}
else if (!!options.after) {
sql += ' AFTER ' + this.escape(options.after);
}
return sql;
},
pgEnumDrop: function(tableName, attr, enumName) {
enumName = enumName || this.pgEscapeAndQuote('enum_' + tableName + '_' + attr);
return 'DROP TYPE IF EXISTS ' + enumName + '; ';
},
fromArray: function(text) {
text = text.replace(/^{/, '').replace(/}$/, '');
var matches = text.match(/("(?:\\.|[^"\\\\])*"|[^,]*)(?:\s*,\s*|\s*$)/ig);
if (matches.length < 1) {
return [];
}
matches = matches.map(function(m) {
return m.replace(/",$/, '').replace(/,$/, '').replace(/(^"|"$)/, '');
});
return matches.slice(0, -1);
},
padInt: function(i) {
return (i < 10) ? '0' + i.toString() : i.toString();
},
pgDataTypeMapping: function(tableName, attr, dataType) {
return this.dataTypeMapping(tableName, attr, dataType);
},
dataTypeMapping: function(tableName, attr, dataType) {
if (Utils._.includes(dataType, 'PRIMARY KEY')) {
primaryKeys[tableName].push(attr);
dataType = dataType.replace(/PRIMARY KEY/, '');
}
if (Utils._.includes(dataType, 'SERIAL')) {
if (Utils._.includes(dataType, 'BIGINT')) {
dataType = dataType.replace(/SERIAL/, 'BIGSERIAL');
dataType = dataType.replace(/BIGINT/, '');
} else {
dataType = dataType.replace(/INTEGER/, '');
}
dataType = dataType.replace(/NOT NULL/, '');
}
if (dataType.match(/^ENUM\(/)) {
dataType = dataType.replace(/^ENUM\(.+\)/, this.pgEscapeAndQuote('enum_' + tableName + '_' + attr));
}
return dataType;
},
quoteIdentifier: function(identifier, force) {
if (identifier === '*') return identifier;
if (!force && this.options && this.options.quoteIdentifiers === false) { // default is `true`
// In Postgres, if tables or attributes are created double-quoted,
// they are also case sensitive. If they contain any uppercase
// characters, they must always be double-quoted. This makes it
// impossible to write queries in portable SQL if tables are created in
// this way. Hence, we strip quotes if we don't want case sensitivity.
return Utils.removeTicks(identifier, '"');
} else {
return Utils.addTicks(identifier, '"');
}
},
/*
Escape a value (e.g. a string, number or date)
*/
escape: function(value, field) {
if (value && value._isSequelizeMethod) {
return this.handleSequelizeMethod(value);
}
if (Utils._.isObject(value) && field && (field.type instanceof DataTypes.HSTORE || DataTypes.ARRAY.is(field.type, DataTypes.HSTORE))) {
if (field.type instanceof DataTypes.HSTORE){
return "'" + hstore.stringify(value) + "'";
} else if (DataTypes.ARRAY.is(field.type, DataTypes.HSTORE)) {
return 'ARRAY[' + Utils._.map(value, function(v){return "'" + hstore.stringify(v) + "'::hstore";}).join(',') + ']::HSTORE[]';
}
} else if(Utils._.isArray(value) && field && (field.type instanceof DataTypes.RANGE || DataTypes.ARRAY.is(field.type, DataTypes.RANGE))) {
if(field.type instanceof DataTypes.RANGE) { // escape single value
return "'" + range.stringify(value) + "'";
}
else if (DataTypes.ARRAY.is(field.type, DataTypes.RANGE)) { // escape array of ranges
return 'ARRAY[' + Utils._.map(value, function(v){return "'" + range.stringify(v) + "'";}).join(',') + ']::' + field.type.toString();
}
} else if (value!==null && field && field.type instanceof DataTypes.JSON) {
value = JSON.stringify(value);
} else if (Array.isArray(value) && field && DataTypes.ARRAY.is(field.type, DataTypes.JSON)) {
var jsonType = field.type.type; // type may be JSON or JSONB
return 'ARRAY[' + value.map(function (v) {
return SqlString.escape(JSON.stringify(v), false, this.options.timezone, this.dialect, field);
}, this).join(',') + ']::' + jsonType.key + '[]';
}
return SqlString.escape(value, false, this.options.timezone, this.dialect, field);
},
/**
* Generates an SQL query that returns all foreign keys of a table.
*
* @param {String} tableName The name of the table.
* @param {String} schemaName The name of the schema.
* @return {String} The generated sql query.
*/
getForeignKeysQuery: function(tableName, schemaName) {
return 'SELECT conname as constraint_name, pg_catalog.pg_get_constraintdef(r.oid, true) as condef FROM pg_catalog.pg_constraint r ' +
"WHERE r.conrelid = (SELECT oid FROM pg_class WHERE relname = '" + tableName + "' LIMIT 1) AND r.contype = 'f' ORDER BY 1;";
},
/**
* Generates an SQL query that removes a foreign key from a table.
*
* @param {String} tableName The name of the table.
* @param {String} foreignKey The name of the foreign key constraint.
* @return {String} The generated sql query.
*/
dropForeignKeyQuery: function(tableName, foreignKey) {
return 'ALTER TABLE ' + this.quoteTable(tableName) + ' DROP CONSTRAINT ' + this.quoteIdentifier(foreignKey) + ';';
},
setAutocommitQuery: function(value, options) {
if (options.parent) {
return;
}
// POSTGRES does not support setting AUTOCOMMIT = OFF
if (!value) {
return;
}
return AbstractQueryGenerator.setAutocommitQuery.call(this, value, options);
}
};
module.exports = Utils._.extend(Utils._.clone(AbstractQueryGenerator), QueryGenerator);