mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-18 12:35:59 +00:00
441 lines
11 KiB
JavaScript
441 lines
11 KiB
JavaScript
const { driverBase } = global.DBGATE_PACKAGES['dbgate-tools'];
|
|
const Dumper = require('./Dumper');
|
|
const { postgreSplitterOptions } = require('dbgate-query-splitter/lib/options');
|
|
|
|
const spatialTypes = ['GEOGRAPHY', 'GEOMETRY'];
|
|
|
|
/** @type {import('dbgate-types').SqlDialect} */
|
|
const dialect = {
|
|
rangeSelect: true,
|
|
ilike: true,
|
|
defaultSchemaName: 'public',
|
|
multipleSchema: true,
|
|
// stringEscapeChar: '\\',
|
|
stringEscapeChar: "'",
|
|
fallbackDataType: 'varchar',
|
|
anonymousPrimaryKey: false,
|
|
enableConstraintsPerTable: true,
|
|
dropColumnDependencies: ['dependencies'],
|
|
quoteIdentifier(s) {
|
|
return '"' + s + '"';
|
|
},
|
|
stringAgg: true,
|
|
|
|
createColumn: true,
|
|
dropColumn: true,
|
|
changeColumn: true,
|
|
createIndex: true,
|
|
dropIndex: true,
|
|
createForeignKey: true,
|
|
dropForeignKey: true,
|
|
createPrimaryKey: true,
|
|
dropPrimaryKey: true,
|
|
createUnique: true,
|
|
dropUnique: true,
|
|
createCheck: true,
|
|
dropCheck: true,
|
|
allowMultipleValuesInsert: true,
|
|
renameSqlObject: true,
|
|
filteredIndexes: true,
|
|
|
|
dropReferencesWhenDropTable: true,
|
|
requireStandaloneSelectForScopeIdentity: true,
|
|
|
|
predefinedDataTypes: [
|
|
'bigint',
|
|
'bigserial',
|
|
'bit',
|
|
'varbit',
|
|
'boolean',
|
|
'box',
|
|
'bytea',
|
|
'char(20)',
|
|
'varchar(250)',
|
|
'cidr',
|
|
'circle',
|
|
'date',
|
|
'double precision',
|
|
'inet',
|
|
'int',
|
|
'interval',
|
|
'json',
|
|
'jsonb',
|
|
'line',
|
|
'lseg',
|
|
'macaddr',
|
|
'macaddr8',
|
|
'money',
|
|
'numeric(10,2)',
|
|
'path',
|
|
'pg_lsn',
|
|
'pg_snapshot',
|
|
'point',
|
|
'polygon',
|
|
'real',
|
|
'smallint',
|
|
'smallserial',
|
|
'serial',
|
|
'text',
|
|
'time',
|
|
'timetz',
|
|
'timestamp',
|
|
'timestamptz',
|
|
'tsquery',
|
|
'tsvector',
|
|
'txid_snapshot',
|
|
'uuid',
|
|
'xml',
|
|
],
|
|
|
|
createColumnViewExpression(columnName, dataType, source, alias, purpose) {
|
|
if (dataType && spatialTypes.includes(dataType.toUpperCase())) {
|
|
return {
|
|
exprType: 'call',
|
|
func: 'ST_AsText',
|
|
alias: alias || columnName,
|
|
args: [
|
|
{
|
|
exprType: 'column',
|
|
columnName,
|
|
source,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
if (dataType?.toLowerCase() == 'uuid' || (purpose == 'filter' && dataType?.toLowerCase()?.startsWith('json'))) {
|
|
return {
|
|
exprType: 'unaryRaw',
|
|
expr: {
|
|
exprType: 'column',
|
|
source,
|
|
columnName,
|
|
},
|
|
afterSql: '::text',
|
|
alias: alias || columnName,
|
|
};
|
|
}
|
|
},
|
|
};
|
|
|
|
const postgresDriverBase = {
|
|
...driverBase,
|
|
supportsTransactions: true,
|
|
dumperClass: Dumper,
|
|
dialect,
|
|
// showConnectionField: (field, values) =>
|
|
// ['server', 'port', 'user', 'password', 'defaultDatabase', 'singleDatabase'].includes(field),
|
|
getQuerySplitterOptions: usage =>
|
|
usage == 'editor'
|
|
? { ...postgreSplitterOptions, ignoreComments: true, preventSingleLineSplit: true }
|
|
: usage == 'import'
|
|
? {
|
|
...postgreSplitterOptions,
|
|
copyFromStdin: true,
|
|
}
|
|
: postgreSplitterOptions,
|
|
readOnlySessions: true,
|
|
|
|
databaseUrlPlaceholder: 'e.g. postgresql://user:password@localhost:5432/default_database',
|
|
|
|
showConnectionField: (field, values) => {
|
|
const allowedFields = ['useDatabaseUrl', 'authType', 'user', 'isReadOnly', 'useSeparateSchemas'];
|
|
|
|
if (values.authType == 'awsIam') {
|
|
allowedFields.push('awsRegion', 'secretAccessKey', 'accessKeyId');
|
|
}
|
|
|
|
if (values.authType == 'socket') {
|
|
allowedFields.push('socketPath');
|
|
} else {
|
|
if (values.useDatabaseUrl) {
|
|
allowedFields.push('databaseUrl');
|
|
} else {
|
|
allowedFields.push('server', 'port');
|
|
}
|
|
}
|
|
|
|
if (values.authType != 'awsIam' && values.authType != 'socket') {
|
|
allowedFields.push('password');
|
|
}
|
|
|
|
if (!values.useDatabaseUrl) {
|
|
allowedFields.push('defaultDatabase', 'singleDatabase');
|
|
}
|
|
|
|
return allowedFields.includes(field);
|
|
},
|
|
|
|
beforeConnectionSave: connection => {
|
|
const { databaseUrl } = connection;
|
|
if (databaseUrl) {
|
|
const m = databaseUrl.match(/\/([^/]+)($|\?)/);
|
|
return {
|
|
...connection,
|
|
singleDatabase: !!m,
|
|
|
|
defaultDatabase: m ? m[1] : null,
|
|
};
|
|
}
|
|
return connection;
|
|
},
|
|
|
|
__analyserInternals: {},
|
|
|
|
getNewObjectTemplates() {
|
|
return [
|
|
{ label: 'New view', sql: 'CREATE VIEW myview\nAS\nSELECT * FROM table1' },
|
|
{ label: 'New materialized view', sql: 'CREATE MATERIALIZED VIEW myview\nAS\nSELECT * FROM table1' },
|
|
{
|
|
label: 'New procedure',
|
|
sql: `CREATE PROCEDURE myproc (arg1 INT)
|
|
LANGUAGE SQL
|
|
AS $$
|
|
SELECT * FROM table1;
|
|
$$`,
|
|
},
|
|
{
|
|
label: 'New function (plpgsql)',
|
|
sql: `CREATE FUNCTION myfunc (arg1 INT)
|
|
RETURNS INT
|
|
AS $$
|
|
BEGIN
|
|
RETURN 1;
|
|
END
|
|
$$ LANGUAGE plpgsql;`,
|
|
},
|
|
{
|
|
label: 'New trigger',
|
|
sql: `CREATE TRIGGER trigger_name
|
|
BEFORE INSERT ON table_name
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION function_name();`,
|
|
},
|
|
];
|
|
},
|
|
|
|
authTypeLabel: 'Connection mode',
|
|
defaultAuthTypeName: 'hostPort',
|
|
defaultSocketPath: '/var/run/postgresql',
|
|
|
|
supportsDatabaseBackup: true,
|
|
supportsDatabaseRestore: true,
|
|
|
|
adaptDataType(dataType) {
|
|
if (dataType?.toLowerCase() == 'datetime') return 'timestamp';
|
|
return dataType;
|
|
},
|
|
|
|
getCliConnectionArgs(connection) {
|
|
const args = [`--username=${connection.user}`, `--host=${connection.server}`];
|
|
if (connection.port) {
|
|
args.push(`--port=${connection.port}`);
|
|
}
|
|
return args;
|
|
},
|
|
|
|
getNativeOperationFormArgs(operation) {
|
|
if (operation == 'backup') {
|
|
return [
|
|
{
|
|
type: 'checkbox',
|
|
label: 'Dump only data (without structure)',
|
|
name: 'dataOnly',
|
|
default: false,
|
|
},
|
|
{
|
|
type: 'checkbox',
|
|
label: 'Dump schema only (no data)',
|
|
name: 'schemaOnly',
|
|
default: false,
|
|
},
|
|
{
|
|
type: 'checkbox',
|
|
label: 'Use SQL insert instead of COPY for rows',
|
|
name: 'insert',
|
|
default: false,
|
|
},
|
|
{
|
|
type: 'checkbox',
|
|
label: 'Prevent dumping of access privileges (grant/revoke)',
|
|
name: 'noPrivileges',
|
|
default: false,
|
|
},
|
|
{
|
|
type: 'checkbox',
|
|
label: 'Do not output commands to set ownership of objects ',
|
|
name: 'noOwner',
|
|
default: false,
|
|
},
|
|
{
|
|
type: 'text',
|
|
label: 'Custom arguments',
|
|
name: 'customArgs',
|
|
},
|
|
];
|
|
}
|
|
return null;
|
|
},
|
|
|
|
backupDatabaseCommand(connection, settings, externalTools) {
|
|
const { outputFile, database, selectedTables, skippedTables, options, argsFormat } = settings;
|
|
const command = externalTools.pg_dump || 'pg_dump';
|
|
const args = this.getCliConnectionArgs(connection, externalTools);
|
|
args.push(`--file=${outputFile}`);
|
|
args.push('--verbose');
|
|
args.push(database);
|
|
|
|
if (options.dataOnly) {
|
|
args.push(`--data-only`);
|
|
}
|
|
if (options.schemaOnly) {
|
|
args.push(`--schema-only`);
|
|
}
|
|
if (options.insert) {
|
|
args.push(`--insert`);
|
|
}
|
|
if (options.noPrivileges) {
|
|
args.push(`--no-privileges`);
|
|
}
|
|
if (options.noOwner) {
|
|
args.push(`--no-owner`);
|
|
}
|
|
if (skippedTables.length > 0) {
|
|
for (const table of selectedTables) {
|
|
args.push(
|
|
argsFormat == 'spawn'
|
|
? `--table="${table.schemaName}"."${table.pureName}"`
|
|
: `--table='"${table.schemaName}"."${table.pureName}"'`
|
|
);
|
|
}
|
|
}
|
|
if (options.customArgs?.trim()) {
|
|
const customArgs = options.customArgs.split(/\s+/).filter(arg => arg.trim() != '');
|
|
args.push(...customArgs);
|
|
}
|
|
|
|
return {
|
|
command,
|
|
args,
|
|
env: { PGPASSWORD: connection.password },
|
|
};
|
|
},
|
|
restoreDatabaseCommand(connection, settings, externalTools) {
|
|
const { inputFile, database } = settings;
|
|
const command = externalTools.psql || 'psql';
|
|
const args = this.getCliConnectionArgs(connection, externalTools);
|
|
args.push(`--dbname=${database}`);
|
|
// args.push('--verbose');
|
|
args.push(`--file=${inputFile}`);
|
|
return {
|
|
command,
|
|
args,
|
|
env: { PGPASSWORD: connection.password },
|
|
};
|
|
},
|
|
transformNativeCommandMessage(message) {
|
|
if (message.message.startsWith('INSERT ') || message.message == 'SET') {
|
|
return null;
|
|
}
|
|
if (message.message.startsWith('pg_dump: processing data for table')) {
|
|
return {
|
|
...message,
|
|
severity: 'info',
|
|
message: message.message.replace('pg_dump: processing data for table', 'Processing table'),
|
|
};
|
|
} else if (message.message.toLowerCase().includes('error:')) {
|
|
return {
|
|
...message,
|
|
severity: 'error',
|
|
};
|
|
} else {
|
|
return {
|
|
...message,
|
|
severity: 'debug',
|
|
};
|
|
}
|
|
return message;
|
|
},
|
|
};
|
|
|
|
/** @type {import('dbgate-types').EngineDriver} */
|
|
const postgresDriver = {
|
|
...postgresDriverBase,
|
|
supportsServerSummary: true,
|
|
engine: 'postgres@dbgate-plugin-postgres',
|
|
title: 'PostgreSQL',
|
|
defaultPort: 5432,
|
|
dialect: {
|
|
...dialect,
|
|
materializedViews: true,
|
|
},
|
|
|
|
dialectByVersion(version) {
|
|
if (version) {
|
|
return {
|
|
...dialect,
|
|
materializedViews:
|
|
version &&
|
|
version.versionMajor != null &&
|
|
version.versionMinor != null &&
|
|
(version.versionMajor > 9 || version.versionMajor == 9 || version.versionMinor >= 3),
|
|
isFipsComplianceOn: version.isFipsComplianceOn,
|
|
};
|
|
}
|
|
return dialect;
|
|
},
|
|
};
|
|
|
|
/** @type {import('dbgate-types').EngineDriver} */
|
|
const cockroachDriver = {
|
|
...postgresDriverBase,
|
|
supportsServerSummary: true,
|
|
engine: 'cockroach@dbgate-plugin-postgres',
|
|
title: 'CockroachDB',
|
|
defaultPort: 26257,
|
|
dialect: {
|
|
...dialect,
|
|
materializedViews: true,
|
|
dropColumnDependencies: ['primaryKey', 'dependencies'],
|
|
dropPrimaryKey: false,
|
|
},
|
|
__analyserInternals: {},
|
|
};
|
|
|
|
/** @type {import('dbgate-types').EngineDriver} */
|
|
const redshiftDriver = {
|
|
...postgresDriverBase,
|
|
supportsServerSummary: true,
|
|
dialect: {
|
|
...dialect,
|
|
stringAgg: false,
|
|
},
|
|
__analyserInternals: {
|
|
skipIndexes: true,
|
|
},
|
|
engine: 'redshift@dbgate-plugin-postgres',
|
|
title: 'Amazon Redshift',
|
|
defaultPort: 5439,
|
|
premiumOnly: true,
|
|
databaseUrlPlaceholder: 'e.g. redshift-cluster-1.xxxx.redshift.amazonaws.com:5439/dev',
|
|
showConnectionField: (field, values) =>
|
|
['databaseUrl', 'user', 'password', 'isReadOnly', 'useSeparateSchemas'].includes(field),
|
|
beforeConnectionSave: connection => {
|
|
const { databaseUrl } = connection;
|
|
if (databaseUrl) {
|
|
const m = databaseUrl.match(/\/([^/]+)$/);
|
|
if (m) {
|
|
return {
|
|
...connection,
|
|
singleDatabase: true,
|
|
defaultDatabase: m[1],
|
|
// displayName: connection.displayName || `${m[1]} on Amazon Redshift`,
|
|
};
|
|
}
|
|
}
|
|
return connection;
|
|
},
|
|
};
|
|
|
|
module.exports = [postgresDriver, cockroachDriver, redshiftDriver];
|