fix(PostgreSQL): various issues in query results

This commit is contained in:
Fabio Di Stasio 2021-03-19 18:49:26 +01:00
parent d465e18dba
commit fccfe92453
6 changed files with 75 additions and 44 deletions

View File

@ -73,6 +73,7 @@
"mysql2": "^2.2.5", "mysql2": "^2.2.5",
"node-sql-parser": "^3.1.0", "node-sql-parser": "^3.1.0",
"pg": "^8.5.1", "pg": "^8.5.1",
"pgsql-ast-parser": "^7.0.2",
"source-map-support": "^0.5.16", "source-map-support": "^0.5.16",
"spectre.css": "^0.5.9", "spectre.css": "^0.5.9",
"v-mask": "^2.2.4", "v-mask": "^2.2.4",

View File

@ -19,7 +19,7 @@ module.exports = {
schedulers: false, schedulers: false,
// Settings // Settings
databaseEdit: false, databaseEdit: false,
tableSettings: false, tableSettings: true,
viewSettings: false, viewSettings: false,
triggerSettings: false, triggerSettings: false,
routineSettings: false, routineSettings: false,

View File

@ -118,7 +118,7 @@ module.exports = [
group: 'time', group: 'time',
types: [ types: [
{ {
name: 'TIMESTAMP', name: 'TIMESTAMP WITHOUT TIME ZONE',
length: false, length: false,
unsigned: false unsigned: false
}, },

View File

@ -1,5 +1,5 @@
module.exports = [ module.exports = [
'PRIMARY', 'PRIMARY',
'INDEX', 'KEY',
'UNIQUE' 'UNIQUE'
]; ];

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import pg, { Pool, Client, types } from 'pg'; import pg, { Pool, Client, types } from 'pg';
import { Parser } from 'node-sql-parser'; import { parse } from 'pgsql-ast-parser';
import { AntaresCore } from '../AntaresCore'; import { AntaresCore } from '../AntaresCore';
import dataTypes from 'common/data-types/postgresql'; import dataTypes from 'common/data-types/postgresql';
@ -322,6 +322,27 @@ export class PostgreSQLClient extends AntaresCore {
}); });
} }
/**
*
* @param {Number} id
* @returns {Array}
*/
async getTableByIDs (ids) {
const { rows } = await this.raw(`
SELECT relid AS tableid, relname, schemaname FROM pg_statio_all_tables WHERE relid IN (${ids})
UNION
SELECT pg_class.oid AS tableid,relname, nspname AS schemaname FROM pg_class JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace WHERE pg_class.oid IN (${ids})
`);
return rows.reduce((acc, curr) => {
acc[curr.tableid] = {
table: curr.relname,
schema: curr.schemaname
};
return acc;
}, {});
}
/** /**
* @param {Object} params * @param {Object} params
* @param {String} params.schema * @param {String} params.schema
@ -330,34 +351,28 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async getKeyUsage ({ schema, table }) { async getKeyUsage ({ schema, table }) {
const { rows } = await this const { rows } = await this.raw(`
.select('*') SELECT *
.schema('information_schema') FROM information_schema.key_column_usage
.from('key_column_usage') JOIN information_schema.referential_constraints ON
.where({ TABLE_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'` }) referential_constraints.constraint_name = key_column_usage.constraint_name
.run(); WHERE table_schema = '${schema}'
AND table_name = '${table}'
const { rows: extras } = await this `);
.select('*')
.schema('information_schema')
.from('referential_constraints')
.where({ constraint_schema: `= '${schema}'`, constraint_name: `= '${table}'` })
.run();
return rows.map(field => { return rows.map(field => {
const extra = extras.find(x => x.CONSTRAINT_NAME === field.CONSTRAINT_NAME);
return { return {
schema: field.TABLE_SCHEMA, schema: field.table_schema,
table: field.TABLE_NAME, table: field.table_name,
field: field.COLUMN_NAME, field: field.column_name,
position: field.ORDINAL_POSITION, position: field.ordinal_position,
constraintPosition: field.POSITION_IN_UNIQUE_CONSTRAINT, constraintPosition: field.position_inUnique_constraint,
constraintName: field.CONSTRAINT_NAME, constraintName: field.constraint_name,
refSchema: field.REFERENCED_TABLE_SCHEMA, refSchema: field.REFERENCED_TABLE_SCHEMA,
refTable: field.REFERENCED_TABLE_NAME, refTable: field.REFERENCED_TABLE_NAME,
refField: field.REFERENCED_COLUMN_NAME, refField: field.REFERENCED_COLUMN_NAME,
onUpdate: extra ? extra.UPDATE_RULE : '', onUpdate: field.update_rule,
onDelete: extra ? extra.DELETE_RULE : '' onDelete: field.delete_rule
}; };
}); });
} }
@ -1228,22 +1243,38 @@ export class PostgreSQLClient extends AntaresCore {
let keysArr = []; let keysArr = [];
const { rows, report, fields, keys, duration } = await new Promise((resolve, reject) => { const { rows, report, fields, keys, duration } = await new Promise((resolve, reject) => {
this._connection.query({ text: query }, async (err, res) => { this._connection.query({
rowMode: args.nest ? 'array' : null,
text: query
}, async (err, res) => {
timeStop = new Date(); timeStop = new Date();
if (err) if (err)
reject(err); reject(err);
else { else {
const { rows, fields } = res;
const queryResult = rows;
const parser = new Parser();
let ast; let ast;
try {
ast = parser.astify(query);
}
catch (err) {
try {
[ast] = parse(query);
} }
catch (err) {}
const { rows, fields } = res;
let queryResult;
if (args.nest) {
const tablesID = [...new Set(fields.map(field => field.tableID))].toString();
const tablesInfo = await this.getTableByIDs(tablesID);
queryResult = rows.map(row => {
return row.reduce((acc, curr, i) => {
const table = tablesInfo[fields[i].tableID].table;
acc[`${table}.${fields[i].name}`] = curr;
return acc;
}, {});
});
}
else
queryResult = rows;
let remappedFields = fields let remappedFields = fields
? fields.map(field => { ? fields.map(field => {
@ -1251,26 +1282,24 @@ export class PostgreSQLClient extends AntaresCore {
return false; return false;
return { return {
...field,
name: field.name, name: field.name,
alias: field.name, alias: field.name,
schema: ast && ast.from ? ast.from[0].db : this._schema, schema: ast && ast.from && 'schema' in ast.from[0] ? ast.from[0].schema : this._schema,
table: ast && ast.from ? ast.from[0].table : null, table: ast && ast.from ? ast.from[0].name : null,
tableAlias: ast && ast.from ? ast.from[0].as : null, tableAlias: ast && ast.from ? ast.from[0].as : null,
orgTable: ast && ast.from ? ast.from[0].table : null, orgTable: ast && ast.from ? ast.from[0].name : null,
type: this.types[field.dataTypeID] type: this.types[field.dataTypeID] || field.format
}; };
}).filter(Boolean) }).filter(Boolean)
: []; : [];
if (args.details) { if (args.details) {
let cachedTable;
if (remappedFields.length) { if (remappedFields.length) {
paramsArr = remappedFields.map(field => { paramsArr = remappedFields.map(field => {
if (field.table) cachedTable = field.table;// Needed for some queries on information_schema
return { return {
table: field.table || cachedTable, table: field.table,
schema: field.schema || 'INFORMATION_SCHEMA' schema: field.schema
}; };
}).filter((val, i, arr) => arr.findIndex(el => el.schema === val.schema && el.table === val.table) === i); }).filter((val, i, arr) => arr.findIndex(el => el.schema === val.schema && el.table === val.table) === i);

View File

@ -15,7 +15,8 @@
} }
&.key-mul, &.key-mul,
&.key-INDEX { &.key-INDEX,
&.key-KEY {
color: palegreen; color: palegreen;
} }