不要怂,就是干,撸起袖子干!

Commit 4383922a by Ali Taheri Moghaddar Committed by Felix Becker

feat(dependency): Support pg version 7 (#7888)

1 parent 85ee83d9
......@@ -201,7 +201,7 @@ const sequelize = new Sequelize('sqlite:relativePath/dbname.db')
### PostgreSQL
The library for PostgreSQL is`pg@~3.6.0` You'll just need to define the dialect:
The library for PostgreSQL is`pg@^5.0.0 || ^6.0.0 || ^7.0.0` You'll just need to define the dialect:
```js
const sequelize = new Sequelize('database', 'username', 'password', {
......
......@@ -153,25 +153,22 @@ class ConnectionManager extends AbstractConnectionManager {
query += 'SELECT typname, oid, typarray FROM pg_type WHERE typtype = \'b\' AND typname IN (\'hstore\', \'geometry\', \'geography\')';
}
return new Promise((resolve, reject) => {
connection.query(query)
.on('error', err => reject(err))
.on('row', row => {
let type;
if (row.typname === 'geometry') {
type = dataTypes.postgres.GEOMETRY;
} else if (row.typname === 'hstore') {
type = dataTypes.postgres.HSTORE;
} else if (row.typname === 'geography') {
type = dataTypes.postgres.GEOGRAPHY;
}
return new Promise((resolve, reject) => connection.query(query, (error, result) => error ? reject(error) : resolve(result))).then(result => {
for (const row of result.rows) {
let type;
if (row.typname === 'geometry') {
type = dataTypes.postgres.GEOMETRY;
} else if (row.typname === 'hstore') {
type = dataTypes.postgres.HSTORE;
} else if (row.typname === 'geography') {
type = dataTypes.postgres.GEOGRAPHY;
}
type.types.postgres.oids.push(row.oid);
type.types.postgres.array_oids.push(row.typarray);
type.types.postgres.oids.push(row.oid);
type.types.postgres.array_oids.push(row.typarray);
this._refreshTypeParser(type);
})
.on('end', () => resolve());
this._refreshTypeParser(type);
}
});
});
}
......
......@@ -60,9 +60,9 @@ class Query extends AbstractQuery {
this.sql = this.sequelize.queryInterface.QueryGenerator.setSearchPath(this.options.searchPath) + sql;
}
const query = parameters && parameters.length ? this.client.query(this.sql, parameters) : this.client.query(this.sql);
const rows = [];
let receivedError = false;
const query = parameters && parameters.length
? new Promise((resolve, reject) => this.client.query(this.sql, parameters, (error, result) => error ? reject(error) : resolve(result)))
: new Promise((resolve, reject) => this.client.query(this.sql, (error, result) => error ? reject(error) : resolve(result)));
//do we need benchmark for this query execution
const benchmark = this.sequelize.options.benchmark || this.options.benchmark;
......@@ -76,207 +76,195 @@ class Query extends AbstractQuery {
debug(`executing(${this.client.uuid || 'default'}) : ${this.sql}`);
return new Promise((resolve, reject) => {
query.on('row', row => {
rows.push(row);
});
query.on('error', err => {
// set the client so that it will be reaped if the connection resets while executing
if (err.code === 'ECONNRESET') {
this.client._invalid = true;
}
receivedError = true;
err.sql = sql;
reject(this.formatError(err));
});
query.on('end', result => {
return query.catch(err => {
// set the client so that it will be reaped if the connection resets while executing
if (err.code === 'ECONNRESET') {
this.client._invalid = true;
}
err.sql = sql;
throw this.formatError(err);
})
.then(queryResult => {
debug(`executed(${this.client.uuid || 'default'}) : ${this.sql}`);
if (benchmark) {
this.sequelize.log('Executed (' + (this.client.uuid || 'default') + '): ' + this.sql, Date.now() - queryBegin, this.options);
}
if (receivedError) {
return;
return queryResult;
})
.then(queryResult => {
const rows = queryResult.rows;
const rowCount = queryResult.rowCount;
const isTableNameQuery = sql.indexOf('SELECT table_name FROM information_schema.tables') === 0;
const isRelNameQuery = sql.indexOf('SELECT relname FROM pg_class WHERE oid IN') === 0;
if (isRelNameQuery) {
return rows.map(row => ({
name: row.relname,
tableName: row.relname.split('_')[0]
}));
} else if (isTableNameQuery) {
return rows.map(row => _.values(row));
}
resolve([rows, sql, result]);
});
}).spread((rows, sql, result) => {
const results = rows;
const isTableNameQuery = sql.indexOf('SELECT table_name FROM information_schema.tables') === 0;
const isRelNameQuery = sql.indexOf('SELECT relname FROM pg_class WHERE oid IN') === 0;
if (isRelNameQuery) {
return rows.map(row => ({
name: row.relname,
tableName: row.relname.split('_')[0]
}));
} else if (isTableNameQuery) {
return rows.map(row => _.values(row));
}
if (rows[0] && rows[0].sequelize_caught_exception !== undefined) {
if (rows[0].sequelize_caught_exception !== null) {
throw this.formatError({
code: '23505',
detail: rows[0].sequelize_caught_exception
});
} else {
rows = rows.map(row => {
delete row.sequelize_caught_exception;
return row;
});
}
}
if (this.isShowIndexesQuery()) {
for (const result of results) {
const attributes = /ON .*? (?:USING .*?\s)?\(([^]*)\)/gi.exec(result.definition)[1].split(',');
// Map column index in table to column name
const columns = _.zipObject(
result.column_indexes,
this.sequelize.queryInterface.QueryGenerator.fromArray(result.column_names)
);
delete result.column_indexes;
delete result.column_names;
let field;
let attribute;
// Indkey is the order of attributes in the index, specified by a string of attribute indexes
result.fields = result.indkey.split(' ').map((indKey, index) => {
field = columns[indKey];
// for functional indices indKey = 0
if (!field) {
return null;
if (rows[0] && rows[0].sequelize_caught_exception !== undefined) {
if (rows[0].sequelize_caught_exception !== null) {
throw this.formatError({
code: '23505',
detail: rows[0].sequelize_caught_exception
});
} else {
for (const row of rows) {
delete row.sequelize_caught_exception;
}
attribute = attributes[index];
return {
attribute: field,
collate: attribute.match(/COLLATE "(.*?)"/) ? /COLLATE "(.*?)"/.exec(attribute)[1] : undefined,
order: attribute.indexOf('DESC') !== -1 ? 'DESC' : attribute.indexOf('ASC') !== -1 ? 'ASC': undefined,
length: undefined
};
}).filter(n => n !== null);
delete result.columns;
}
}
return results;
} else if (this.isForeignKeysQuery()) {
result = [];
for (const row of rows) {
let defParts;
if (row.condef !== undefined && (defParts = row.condef.match(/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)( ON (UPDATE|DELETE) (CASCADE|RESTRICT))?( ON (UPDATE|DELETE) (CASCADE|RESTRICT))?/))) {
row.id = row.constraint_name;
row.table = defParts[2];
row.from = defParts[1];
row.to = defParts[3];
let i;
for (i=5;i<=8;i+=3) {
if (/(UPDATE|DELETE)/.test(defParts[i])) {
row['on_'+defParts[i].toLowerCase()] = defParts[i+1];
if (this.isShowIndexesQuery()) {
for (const row of rows) {
const attributes = /ON .*? (?:USING .*?\s)?\(([^]*)\)/gi.exec(row.definition)[1].split(',');
// Map column index in table to column name
const columns = _.zipObject(
row.column_indexes,
this.sequelize.queryInterface.QueryGenerator.fromArray(row.column_names)
);
delete row.column_indexes;
delete row.column_names;
let field;
let attribute;
// Indkey is the order of attributes in the index, specified by a string of attribute indexes
row.fields = row.indkey.split(' ').map((indKey, index) => {
field = columns[indKey];
// for functional indices indKey = 0
if (!field) {
return null;
}
}
attribute = attributes[index];
return {
attribute: field,
collate: attribute.match(/COLLATE "(.*?)"/) ? /COLLATE "(.*?)"/.exec(attribute)[1] : undefined,
order: attribute.indexOf('DESC') !== -1 ? 'DESC' : attribute.indexOf('ASC') !== -1 ? 'ASC': undefined,
length: undefined
};
}).filter(n => n !== null);
delete row.columns;
}
result.push(row);
}
return result;
} else if (this.isSelectQuery()) {
// Postgres will treat tables as case-insensitive, so fix the case
// of the returned values to match attributes
if (this.options.raw === false && this.sequelize.options.quoteIdentifiers === false) {
const attrsMap = _.reduce(this.model.attributes, (m, v, k) => {
m[k.toLowerCase()] = k;
return m;
}, {});
rows = _.map(rows, row=> {
return _.mapKeys(row, (value, key)=> {
const targetAttr = attrsMap[key];
if (typeof targetAttr === 'string' && targetAttr !== key) {
return targetAttr;
} else {
return key;
return rows;
} else if (this.isForeignKeysQuery()) {
const result = [];
for (const row of rows) {
let defParts;
if (row.condef !== undefined && (defParts = row.condef.match(/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)( ON (UPDATE|DELETE) (CASCADE|RESTRICT))?( ON (UPDATE|DELETE) (CASCADE|RESTRICT))?/))) {
row.id = row.constraint_name;
row.table = defParts[2];
row.from = defParts[1];
row.to = defParts[3];
let i;
for (i=5;i<=8;i+=3) {
if (/(UPDATE|DELETE)/.test(defParts[i])) {
row['on_'+defParts[i].toLowerCase()] = defParts[i+1];
}
}
});
});
}
return this.handleSelectQuery(rows);
} else if (QueryTypes.DESCRIBE === this.options.type) {
result = {};
for (const _result of rows) {
result[_result.Field] = {
type: _result.Type.toUpperCase(),
allowNull: _result.Null === 'YES',
defaultValue: _result.Default,
special: _result.special ? this.sequelize.queryInterface.QueryGenerator.fromArray(_result.special) : [],
primaryKey: _result.Constraint === 'PRIMARY KEY'
};
if (result[_result.Field].type === 'BOOLEAN') {
result[_result.Field].defaultValue = { 'false': false, 'true': true }[result[_result.Field].defaultValue];
if (result[_result.Field].defaultValue === undefined) {
result[_result.Field].defaultValue = null;
}
result.push(row);
}
return result;
} else if (this.isSelectQuery()) {
let result = rows;
// Postgres will treat tables as case-insensitive, so fix the case
// of the returned values to match attributes
if (this.options.raw === false && this.sequelize.options.quoteIdentifiers === false) {
const attrsMap = _.reduce(this.model.attributes, (m, v, k) => {
m[k.toLowerCase()] = k;
return m;
}, {});
result = _.map(rows, row=> {
return _.mapKeys(row, (value, key)=> {
const targetAttr = attrsMap[key];
if (typeof targetAttr === 'string' && targetAttr !== key) {
return targetAttr;
} else {
return key;
}
});
});
}
return this.handleSelectQuery(result);
} else if (QueryTypes.DESCRIBE === this.options.type) {
const result = {};
for (const row of rows) {
result[row.Field] = {
type: row.Type.toUpperCase(),
allowNull: row.Null === 'YES',
defaultValue: row.Default,
special: row.special ? this.sequelize.queryInterface.QueryGenerator.fromArray(row.special) : [],
primaryKey: row.Constraint === 'PRIMARY KEY'
};
if (typeof result[_result.Field].defaultValue === 'string') {
result[_result.Field].defaultValue = result[_result.Field].defaultValue.replace(/'/g, '');
if (result[row.Field].type === 'BOOLEAN') {
result[row.Field].defaultValue = { 'false': false, 'true': true }[result[row.Field].defaultValue];
if (result[_result.Field].defaultValue.indexOf('::') > -1) {
const split = result[_result.Field].defaultValue.split('::');
if (split[1].toLowerCase() !== 'regclass)') {
result[_result.Field].defaultValue = split[0];
if (result[row.Field].defaultValue === undefined) {
result[row.Field].defaultValue = null;
}
}
if (typeof result[row.Field].defaultValue === 'string') {
result[row.Field].defaultValue = result[row.Field].defaultValue.replace(/'/g, '');
if (result[row.Field].defaultValue.indexOf('::') > -1) {
const split = result[row.Field].defaultValue.split('::');
if (split[1].toLowerCase() !== 'regclass)') {
result[row.Field].defaultValue = split[0];
}
}
}
}
}
return result;
} else if (this.isVersionQuery()) {
return results[0].server_version;
} else if (this.isShowOrDescribeQuery()) {
return results;
} else if (QueryTypes.BULKUPDATE === this.options.type) {
if (!this.options.returning) {
return parseInt(result.rowCount, 10);
}
return result;
} else if (this.isVersionQuery()) {
return rows[0].server_version;
} else if (this.isShowOrDescribeQuery()) {
return rows;
} else if (QueryTypes.BULKUPDATE === this.options.type) {
if (!this.options.returning) {
return parseInt(rowCount, 10);
}
return this.handleSelectQuery(rows);
} else if (QueryTypes.BULKDELETE === this.options.type) {
return parseInt(result.rowCount, 10);
} else if (this.isUpsertQuery()) {
return rows[0].sequelize_upsert;
} else if (this.isInsertQuery() || this.isUpdateQuery()) {
if (this.instance && this.instance.dataValues) {
for (const key in rows[0]) {
if (rows[0].hasOwnProperty(key)) {
const record = rows[0][key];
return this.handleSelectQuery(rows);
} else if (QueryTypes.BULKDELETE === this.options.type) {
return parseInt(rowCount, 10);
} else if (this.isUpsertQuery()) {
return rows[0].sequelize_upsert;
} else if (this.isInsertQuery() || this.isUpdateQuery()) {
if (this.instance && this.instance.dataValues) {
for (const key in rows[0]) {
if (rows[0].hasOwnProperty(key)) {
const record = rows[0][key];
const attr = _.find(this.model.rawAttributes, attribute => attribute.fieldName === key || attribute.field === key);
const attr = _.find(this.model.rawAttributes, attribute => attribute.fieldName === key || attribute.field === key);
this.instance.dataValues[attr && attr.fieldName || key] = record;
this.instance.dataValues[attr && attr.fieldName || key] = record;
}
}
}
}
return [
this.instance || rows && (this.options.plain && rows[0] || rows) || undefined,
result.rowCount
];
} else if (this.isRawQuery()) {
return [rows, result];
} else {
return results;
}
});
return [
this.instance || rows && (this.options.plain && rows[0] || rows) || undefined,
rowCount
];
} else if (this.isRawQuery()) {
return [rows, queryResult];
} else {
return rows;
}
});
}
formatError(err) {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!