get version result and login from oracle

This commit is contained in:
Rinie Kervel
2022-09-26 17:54:24 +02:00
parent 1535dfd407
commit 4939b74179
36 changed files with 1508 additions and 0 deletions

View File

@@ -0,0 +1,353 @@
const fp = require('lodash/fp');
const _ = require('lodash');
const sql = require('./sql');
const { DatabaseAnalyser } = require('dbgate-tools');
const { isTypeString, isTypeNumeric } = require('dbgate-tools');
function normalizeTypeName(dataType) {
if (dataType == 'character varying') return 'varchar';
if (dataType == 'timestamp without time zone') return 'timestamp';
return dataType;
}
function getColumnInfo(
{ is_nullable, column_name, data_type, char_max_length, numeric_precision, numeric_ccale, default_value },
table = undefined,
geometryColumns = undefined,
geographyColumns = undefined
) {
const normDataType = normalizeTypeName(data_type);
let fullDataType = normDataType;
if (char_max_length && isTypeString(normDataType)) fullDataType = `${normDataType}(${char_max_length})`;
if (numeric_precision && numeric_ccale && isTypeNumeric(normDataType))
fullDataType = `${normDataType}(${numeric_precision},${numeric_ccale})`;
const autoIncrement = !!(default_value && default_value.startsWith('nextval('));
if (
table &&
geometryColumns &&
geometryColumns.rows.find(
x => x.schema_name == table.schemaName && x.pure_name == table.pureName && x.column_name == column_name
)
) {
fullDataType = 'geometry';
}
if (
table &&
geographyColumns &&
geographyColumns.rows.find(
x => x.schema_name == table.schemaName && x.pure_name == table.pureName && x.column_name == column_name
)
) {
fullDataType = 'geography';
}
return {
columnName: column_name,
dataType: fullDataType,
notNull: !is_nullable || is_nullable == 'NO' || is_nullable == 'no',
defaultValue: autoIncrement ? undefined : default_value,
autoIncrement,
};
}
class Analyser extends DatabaseAnalyser {
constructor(pool, driver, version) {
super(pool, driver, version);
}
createQuery(resFileName, typeFields) {
const query = super.createQuery(sql[resFileName], typeFields);
if (query) return query.replace('#REFTABLECOND#', this.driver.__analyserInternals.refTableCond);
return null;
}
async _computeSingleObjectId() {
const { typeField, schemaName, pureName } = this.singleObjectFilter;
this.singleObjectId = `${typeField}:${schemaName || 'public'}.${pureName}`;
}
async _runAnalysis() {
this.feedback({ analysingMessage: 'Loading tables' });
const tables = await this.driver.query(
this.pool,
this.createQuery(this.driver.dialect.stringAgg ? 'tableModifications' : 'tableList', ['tables'])
);
this.feedback({ analysingMessage: 'Loading columns' });
const columns = await this.driver.query(this.pool, this.createQuery('columns', ['tables', 'views']));
this.feedback({ analysingMessage: 'Loading primary keys' });
const pkColumns = await this.driver.query(this.pool, this.createQuery('primaryKeys', ['tables']));
let fkColumns = null;
// if (true) {
if (this.containsObjectIdCondition(['tables']) || this.driver.__analyserInternals.refTableCond) {
this.feedback({ analysingMessage: 'Loading foreign keys' });
fkColumns = await this.driver.query(this.pool, this.createQuery('foreignKeys', ['tables']));
} else {
this.feedback({ analysingMessage: 'Loading foreign key constraints' });
const fk_tableConstraints = await this.driver.query(
this.pool,
this.createQuery('fk_tableConstraints', ['tables'])
);
this.feedback({ analysingMessage: 'Loading foreign key refs' });
const fk_referentialConstraints = await this.driver.query(
this.pool,
this.createQuery('fk_referentialConstraints', ['tables'])
);
this.feedback({ analysingMessage: 'Loading foreign key columns' });
const fk_keyColumnUsage = await this.driver.query(this.pool, this.createQuery('fk_keyColumnUsage', ['tables']));
const cntKey = x => `${x.constraint_name}|${x.constraint_schema}`;
const rows = [];
const constraintDct = _.keyBy(fk_tableConstraints.rows, cntKey);
for (const fkRef of fk_referentialConstraints.rows) {
const cntBase = constraintDct[cntKey(fkRef)];
const cntRef = constraintDct[`${fkRef.unique_constraint_name}|${fkRef.unique_constraint_schema}`];
if (!cntBase || !cntRef) continue;
const baseCols = _.sortBy(
fk_keyColumnUsage.rows.filter(
x => x.table_name == cntBase.table_name && x.constraint_name == cntBase.constraint_name
),
'ordinal_position'
);
const refCols = _.sortBy(
fk_keyColumnUsage.rows.filter(
x => x.table_name == cntRef.table_name && x.constraint_name == cntRef.constraint_name
),
'ordinal_position'
);
if (baseCols.length != refCols.length) continue;
for (let i = 0; i < baseCols.length; i++) {
const baseCol = baseCols[i];
const refCol = refCols[i];
rows.push({
...fkRef,
pure_name: cntBase.table_name,
schema_name: cntBase.table_schema,
ref_table_name: cntRef.table_name,
ref_schema_name: cntRef.table_schema,
column_name: baseCol.column_name,
ref_column_name: refCol.column_name,
});
}
}
fkColumns = { rows };
}
this.feedback({ analysingMessage: 'Loading views' });
const views = await this.driver.query(this.pool, this.createQuery('views', ['views']));
this.feedback({ analysingMessage: 'Loading materialized views' });
const matviews = this.driver.dialect.materializedViews
? await this.driver.query(this.pool, this.createQuery('matviews', ['matviews']))
: null;
this.feedback({ analysingMessage: 'Loading materialized view columns' });
const matviewColumns = this.driver.dialect.materializedViews
? await this.driver.query(this.pool, this.createQuery('matviewColumns', ['matviews']))
: null;
this.feedback({ analysingMessage: 'Loading routines' });
const routines = await this.driver.query(this.pool, this.createQuery('routines', ['procedures', 'functions']));
this.feedback({ analysingMessage: 'Loading indexes' });
const indexes = this.driver.__analyserInternals.skipIndexes
? { rows: [] }
: await this.driver.query(this.pool, this.createQuery('indexes', ['tables']));
this.feedback({ analysingMessage: 'Loading index columns' });
const indexcols = this.driver.__analyserInternals.skipIndexes
? { rows: [] }
: await this.driver.query(this.pool, this.createQuery('indexcols', ['tables']));
this.feedback({ analysingMessage: 'Loading unique names' });
const uniqueNames = await this.driver.query(this.pool, this.createQuery('uniqueNames', ['tables']));
let geometryColumns = { rows: [] };
if (views.rows.find(x => x.pure_name == 'geometry_columns' && x.schema_name == 'public')) {
this.feedback({ analysingMessage: 'Loading geometry columns' });
geometryColumns = await this.safeQuery(this.createQuery('geometryColumns', ['tables']));
}
let geographyColumns = { rows: [] };
if (views.rows.find(x => x.pure_name == 'geography_columns' && x.schema_name == 'public')) {
this.feedback({ analysingMessage: 'Loading geography columns' });
geographyColumns = await this.safeQuery(this.createQuery('geographyColumns', ['tables']));
}
this.feedback({ analysingMessage: 'Finalizing DB structure' });
const columnColumnsMapped = fkColumns.rows.map(x => ({
pureName: x.pure_name,
schemaName: x.schema_name,
constraintSchema: x.constraint_schema,
constraintName: x.constraint_name,
columnName: x.column_name,
refColumnName: x.ref_column_name,
updateAction: x.update_action,
deleteAction: x.delete_action,
refTableName: x.ref_table_name,
refSchemaName: x.ref_schema_name,
}));
const pkColumnsMapped = pkColumns.rows.map(x => ({
pureName: x.pure_name,
schemaName: x.schema_name,
constraintSchema: x.constraint_schema,
constraintName: x.constraint_name,
columnName: x.column_name,
}));
const res = {
tables: tables.rows.map(table => {
const newTable = {
pureName: table.pure_name,
schemaName: table.schema_name,
objectId: `tables:${table.schema_name}.${table.pure_name}`,
contentHash: table.hash_code_columns ? `${table.hash_code_columns}-${table.hash_code_constraints}` : null,
};
return {
...newTable,
columns: columns.rows
.filter(col => col.pure_name == table.pure_name && col.schema_name == table.schema_name)
.map(col => getColumnInfo(col, newTable, geometryColumns, geographyColumns)),
primaryKey: DatabaseAnalyser.extractPrimaryKeys(newTable, pkColumnsMapped),
foreignKeys: DatabaseAnalyser.extractForeignKeys(newTable, columnColumnsMapped),
indexes: indexes.rows
.filter(
x =>
x.table_name == table.pure_name &&
x.schema_name == table.schema_name &&
!uniqueNames.rows.find(y => y.constraint_name == x.index_name)
)
.map(idx => ({
constraintName: idx.index_name,
isUnique: idx.is_unique,
columns: _.compact(
idx.indkey
.split(' ')
.map(colid => indexcols.rows.find(col => col.oid == idx.oid && col.attnum == colid))
.filter(col => col != null)
.map(col => ({
columnName: col.column_name,
}))
),
})),
uniques: indexes.rows
.filter(
x =>
x.table_name == table.pure_name &&
x.schema_name == table.schema_name &&
uniqueNames.rows.find(y => y.constraint_name == x.index_name)
)
.map(idx => ({
constraintName: idx.index_name,
columns: _.compact(
idx.indkey
.split(' ')
.map(colid => indexcols.rows.find(col => col.oid == idx.oid && col.attnum == colid))
.filter(col => col != null)
.map(col => ({
columnName: col.column_name,
}))
),
})),
};
}),
views: views.rows.map(view => ({
objectId: `views:${view.schema_name}.${view.pure_name}`,
pureName: view.pure_name,
schemaName: view.schema_name,
contentHash: view.hash_code,
createSql: `CREATE VIEW "${view.schema_name}"."${view.pure_name}"\nAS\n${view.create_sql}`,
columns: columns.rows
.filter(col => col.pure_name == view.pure_name && col.schema_name == view.schema_name)
.map(col => getColumnInfo(col)),
})),
matviews: matviews
? matviews.rows.map(matview => ({
objectId: `matviews:${matview.schema_name}.${matview.pure_name}`,
pureName: matview.pure_name,
schemaName: matview.schema_name,
contentHash: matview.hash_code,
createSql: `CREATE MATERIALIZED VIEW "${matview.schema_name}"."${matview.pure_name}"\nAS\n${matview.definition}`,
columns: matviewColumns.rows
.filter(col => col.pure_name == matview.pure_name && col.schema_name == matview.schema_name)
.map(col => getColumnInfo(col)),
}))
: undefined,
procedures: routines.rows
.filter(x => x.object_type == 'PROCEDURE')
.map(proc => ({
objectId: `procedures:${proc.schema_name}.${proc.pure_name}`,
pureName: proc.pure_name,
schemaName: proc.schema_name,
createSql: `CREATE PROCEDURE "${proc.schema_name}"."${proc.pure_name}"() LANGUAGE ${proc.language}\nAS\n$$\n${proc.definition}\n$$`,
contentHash: proc.hash_code,
})),
functions: routines.rows
.filter(x => x.object_type == 'FUNCTION')
.map(func => ({
objectId: `functions:${func.schema_name}.${func.pure_name}`,
createSql: `CREATE FUNCTION "${func.schema_name}"."${func.pure_name}"() RETURNS ${func.data_type} LANGUAGE ${func.language}\nAS\n$$\n${func.definition}\n$$`,
pureName: func.pure_name,
schemaName: func.schema_name,
contentHash: func.hash_code,
})),
};
this.feedback({ analysingMessage: null });
return res;
}
async _getFastSnapshot() {
const tableModificationsQueryData = this.driver.dialect.stringAgg
? await this.driver.query(this.pool, this.createQuery('tableModifications'))
: null;
const viewModificationsQueryData = await this.driver.query(this.pool, this.createQuery('viewModifications'));
const matviewModificationsQueryData = this.driver.dialect.materializedViews
? await this.driver.query(this.pool, this.createQuery('matviewModifications'))
: null;
const routineModificationsQueryData = await this.driver.query(this.pool, this.createQuery('routineModifications'));
return {
tables: tableModificationsQueryData
? tableModificationsQueryData.rows.map(x => ({
objectId: `tables:${x.schema_name}.${x.pure_name}`,
pureName: x.pure_name,
schemaName: x.schema_name,
contentHash: `${x.hash_code_columns}-${x.hash_code_constraints}`,
}))
: null,
views: viewModificationsQueryData.rows.map(x => ({
objectId: `views:${x.schema_name}.${x.pure_name}`,
pureName: x.pure_name,
schemaName: x.schema_name,
contentHash: x.hash_code,
})),
matviews: matviewModificationsQueryData
? matviewModificationsQueryData.rows.map(x => ({
objectId: `matviews:${x.schema_name}.${x.pure_name}`,
pureName: x.pure_name,
schemaName: x.schema_name,
contentHash: x.hash_code,
}))
: undefined,
procedures: routineModificationsQueryData.rows
.filter(x => x.object_type == 'PROCEDURE')
.map(x => ({
objectId: `procedures:${x.schema_name}.${x.pure_name}`,
pureName: x.pure_name,
schemaName: x.schema_name,
contentHash: x.hash_code,
})),
functions: routineModificationsQueryData.rows
.filter(x => x.object_type == 'FUNCTION')
.map(x => ({
objectId: `functions:${x.schema_name}.${x.pure_name}`,
pureName: x.pure_name,
schemaName: x.schema_name,
contentHash: x.hash_code,
})),
};
}
}
module.exports = Analyser;

View File

@@ -0,0 +1,303 @@
const _ = require('lodash');
const stream = require('stream');
const driverBases = require('../frontend/drivers');
const Analyser = require('./Analyser');
//--const pg = require('pg');
const oracledb = require('oracledb');
const { createBulkInsertStreamBase, makeUniqueColumnNames } = require('dbgate-tools');
/*
pg.types.setTypeParser(1082, 'text', val => val); // date
pg.types.setTypeParser(1114, 'text', val => val); // timestamp without timezone
pg.types.setTypeParser(1184, 'text', val => val); // timestamp
*/
function extractOracleColumns(result) {
console.log('result', result);
console.log('result.name', result[0].name);
console.log('result.map', result.map(fld => ({
columnName: fld.name.toLowerCase(),
})));
if (!result /*|| !result.fields */) return [];
const res = result.map(fld => ({
columnName: fld.name.toLowerCase(),
}));
makeUniqueColumnNames(res);
return res;
}
function zipDataRow(rowArray, columns) {
return _.zipObject(
columns.map(x => x.columnName),
rowArray
);
}
/** @type {import('dbgate-types').EngineDriver} */
const drivers = driverBases.map(driverBase => ({
...driverBase,
analyserClass: Analyser,
async connect({
engine,
server,
port,
user,
password,
database,
databaseUrl,
useDatabaseUrl,
ssl,
isReadOnly,
authType,
socketPath,
}) {
let options = null;
if (engine == 'redshift@dbgate-plugin-oracle') {
let url = databaseUrl;
if (url && url.startsWith('jdbc:redshift://')) {
url = url.substring('jdbc:redshift://'.length);
}
if (user && password) {
url = `oracle://${user}:${password}@${url}`;
} else if (user) {
url = `oracle://${user}@${url}`;
} else {
url = `oracle://${url}`;
}
options = {
connectionString: url,
};
} else {
options = useDatabaseUrl
? {
connectionString: databaseUrl,
}
: {
host: authType == 'socket' ? socketPath || driverBase.defaultSocketPath : server,
port: authType == 'socket' ? null : port,
user,
password,
database: database || 'oracle',
ssl,
};
}
console.log('OPTIONS', options);
/*
const client = new pg.Client(options);
await client.connect();
if (isReadOnly) {
await this.query(client, 'SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY');
}
*/
client = await oracledb.getConnection( {
user : options.user,
password : options.password,
connectString : options.host
});
return client;
},
async close(pool) {
return pool.end();
},
async query(client, sql) {
if (sql == null) {
return {
rows: [],
columns: [],
};
}
console.log('sql', sql);
const res = await client.execute(sql);
console.log('res', res);
const columns = extractOracleColumns(res.metaData);
console.log('columns', columns);
return { rows: (res.rows || []).map(row => zipDataRow(row, columns)), columns };
},
stream(client, sql, options) {
/*
const query = new pg.Query({
text: sql,
rowMode: 'array',
});
*/
console.log('queryStream', sql);
const query = client.queryStream(sql);
let wasHeader = false;
query.on('metaData', row => {
console.log('metaData', row);
if (!wasHeader) {
columns = extractOracleColumns(query.metaData);
if (columns && columns.length > 0) {
options.recordset(columns);
}
wasHeader = true;
}
options.row(zipDataRow(row, columns));
});
query.on('data', row => {
console.log('DATA', row);
if (!wasHeader) {
columns = extractOracleColumns(query._result);
if (columns && columns.length > 0) {
options.recordset(columns);
}
wasHeader = true;
}
options.row(zipDataRow(row, columns));
});
query.on('end', () => {
const { command, rowCount } = query._result || {};
if (command != 'SELECT' && _.isNumber(rowCount)) {
options.info({
message: `${rowCount} rows affected`,
time: new Date(),
severity: 'info',
});
}
if (!wasHeader) {
columns = extractOracleColumns(query._result);
if (columns && columns.length > 0) {
options.recordset(columns);
}
wasHeader = true;
}
options.done();
});
query.on('error', error => {
console.log('ERROR', error);
const { message, lineNumber, procName } = error;
options.info({
message,
line: lineNumber,
procedure: procName,
time: new Date(),
severity: 'error',
});
options.done();
});
client.query(query);
},
async getVersion(client) {
//const { rows } = await this.query(client, "SELECT banner as version FROM v$version WHERE banner LIKE 'Oracle%'");
const { rows } = await this.query(client, "SELECT version FROM v$instance");
const { version } = rows[0];
const isCockroach = false; //version.toLowerCase().includes('cockroachdb');
const isRedshift = false; // version.toLowerCase().includes('redshift');
const isOracle = true;
const m = version.match(/([\d\.]+)/);
//console.log('M', m);
let versionText = null;
let versionMajor = null;
let versionMinor = null;
if (m) {
if (isOracle) versionText = `Oracle ${m[1]}`;
const numbers = m[1].split('.');
if (numbers[0]) versionMajor = parseInt(numbers[0]);
if (numbers[1]) versionMinor = parseInt(numbers[1]);
}
return {
version,
versionText,
isOracle,
isCockroach,
isRedshift,
versionMajor,
versionMinor,
};
},
async readQuery(client, sql, structure) {
/*
const query = new pg.Query({
text: sql,
rowMode: 'array',
});
*/
console.log('readQuery', sql, structure);
const query = await client.queryStream(sql);
let wasHeader = false;
let columns = null;
const pass = new stream.PassThrough({
objectMode: true,
highWaterMark: 100,
});
query.on('data', row => {
if (!wasHeader) {
columns = extractOracleColumns(query._result);
pass.write({
__isStreamHeader: true,
...(structure || { columns }),
});
wasHeader = true;
}
pass.write(zipDataRow(row, columns));
});
query.on('end', () => {
if (!wasHeader) {
columns = extractOracleColumns(query._result);
pass.write({
__isStreamHeader: true,
...(structure || { columns }),
});
wasHeader = true;
}
pass.end();
});
query.on('error', error => {
console.error(error);
pass.end();
});
client.query(query);
return pass;
},
async writeTable(pool, name, options) {
// @ts-ignore
return createBulkInsertStreamBase(this, stream, pool, name, options);
},
async listDatabases(client) {
const { rows } = await this.query(client, 'SELECT instance_name AS name FROM v$instance');
return rows;
},
getAuthTypes() {
return [
{
title: 'Host and port',
name: 'hostPort',
},
{
title: 'Socket',
name: 'socket',
},
];
},
}));
module.exports = drivers;

View File

@@ -0,0 +1,6 @@
const drivers = require('./drivers');
module.exports = {
packageName: 'dbgate-plugin-oracle',
drivers,
};

View File

@@ -0,0 +1,23 @@
module.exports = `
select
table_schema as "schema_name",
table_name as "pure_name",
column_name as "column_name",
is_nullable as "is_nullable",
data_type as "data_type",
character_maximum_length as "char_max_length",
numeric_precision as "numeric_precision",
numeric_scale as "numeric_scale",
column_default as "default_value"
from information_schema.columns
where
table_schema <> 'information_schema'
and table_schema <> 'pg_catalog'
and table_schema !~ '^pg_toast'
and (
('tables:' || table_schema || '.' || table_name) =OBJECT_ID_CONDITION
or
('views:' || table_schema || '.' || table_name) =OBJECT_ID_CONDITION
)
order by ordinal_position
`;

View File

@@ -0,0 +1,10 @@
module.exports = `
select
basecol.constraint_name,
basecol.constraint_schema,
basecol.column_name as "column_name",
basecol.table_schema,
basecol.table_name,
basecol.ordinal_position
from information_schema.key_column_usage basecol
`;

View File

@@ -0,0 +1,10 @@
module.exports = `
select
fk.constraint_name as "constraint_name",
fk.constraint_schema as "constraint_schema",
fk.update_rule as "update_action",
fk.delete_rule as "delete_action",
fk.unique_constraint_name as "unique_constraint_name",
fk.unique_constraint_schema as "unique_constraint_schema"
from information_schema.referential_constraints fk
`;

View File

@@ -0,0 +1,8 @@
module.exports = `
select
base.table_name as "table_name",
base.table_schema as "table_schema",
base.constraint_name as "constraint_name",
base.constraint_schema as "constraint_schema"
from information_schema.table_constraints base
`;

View File

@@ -0,0 +1,24 @@
module.exports = `
select
fk.constraint_name as "constraint_name",
fk.constraint_schema as "constraint_schema",
base.table_name as "pure_name",
base.table_schema as "schema_name",
fk.update_rule as "update_action",
fk.delete_rule as "delete_action",
ref.table_name as "ref_table_name",
ref.table_schema as "ref_schema_name",
basecol.column_name as "column_name",
refcol.column_name as "ref_column_name"
from information_schema.referential_constraints fk
inner join information_schema.table_constraints base on fk.constraint_name = base.constraint_name and fk.constraint_schema = base.constraint_schema
inner join information_schema.table_constraints ref on fk.unique_constraint_name = ref.constraint_name and fk.unique_constraint_schema = ref.constraint_schema #REFTABLECOND#
inner join information_schema.key_column_usage basecol on base.table_name = basecol.table_name and base.constraint_name = basecol.constraint_name
inner join information_schema.key_column_usage refcol on ref.table_name = refcol.table_name and ref.constraint_name = refcol.constraint_name and basecol.ordinal_position = refcol.ordinal_position
where
base.table_schema <> 'information_schema'
and base.table_schema <> 'pg_catalog'
and base.table_schema !~ '^pg_toast'
and ('tables:' || base.table_schema || '.' || base.table_name) =OBJECT_ID_CONDITION
order by basecol.ordinal_position
`;

View File

@@ -0,0 +1,8 @@
module.exports = `
select
f_table_schema as "schema_name",
f_table_name as "pure_name",
f_geography_column as "column_name"
from public.geography_columns
where ('tables:' || f_table_schema || '.' || f_table_name) =OBJECT_ID_CONDITION
`;

View File

@@ -0,0 +1,8 @@
module.exports = `
select
f_table_schema as "schema_name",
f_table_name as "pure_name",
f_geometry_column as "column_name"
from public.geometry_columns
where ('tables:' || f_table_schema || '.' || f_table_name) =OBJECT_ID_CONDITION
`;

View File

@@ -0,0 +1,44 @@
const columns = require('./columns');
const tableModifications = require('./tableList');
const tableList = require('./tableList');
const viewModifications = require('./views');
const matviewModifications = require('./matviewModifications');
const primaryKeys = require('./primaryKeys');
const foreignKeys = require('./foreignKeys');
const views = require('./views');
const matviews = require('./matviews');
const routines = require('./routines');
const routineModifications = require('./routineModifications');
const matviewColumns = require('./matviewColumns');
const indexes = require('./indexes');
const indexcols = require('./indexcols');
const uniqueNames = require('./uniqueNames');
const geometryColumns = require('./geometryColumns');
const geographyColumns = require('./geographyColumns');
const fk_keyColumnUsage = require('./fk_key_column_usage');
const fk_referentialConstraints = require('./fk_referential_constraints');
const fk_tableConstraints = require('./fk_table_constraints');
module.exports = {
columns,
tableModifications,
tableList,
viewModifications,
primaryKeys,
foreignKeys,
fk_keyColumnUsage,
fk_referentialConstraints,
fk_tableConstraints,
views,
routines,
routineModifications,
matviews,
matviewModifications,
matviewColumns,
indexes,
indexcols,
uniqueNames,
geometryColumns,
geographyColumns,
};

View File

@@ -0,0 +1,24 @@
module.exports = `
select
a.attname as "column_name",
a.attnum as "attnum",
a.attrelid as "oid"
from
pg_class t,
pg_class i,
pg_attribute a,
pg_index ix,
pg_namespace c
where
t.oid = ix.indrelid
and a.attnum = ANY(ix.indkey)
and a.attrelid = t.oid
and i.oid = ix.indexrelid
and t.relkind = 'r'
and ix.indisprimary = false
and t.relnamespace = c.oid
and c.nspname != 'pg_catalog'
and ('tables:' || c.nspname || '.' || t.relname) =OBJECT_ID_CONDITION
order by
t.relname
`;

View File

@@ -0,0 +1,25 @@
module.exports = `
select
t.relname as "table_name",
c.nspname as "schema_name",
i.relname as "index_name",
ix.indisprimary as "is_primary",
ix.indisunique as "is_unique",
ix.indkey as "indkey",
t.oid as "oid"
from
pg_class t,
pg_class i,
pg_index ix,
pg_namespace c
where
t.oid = ix.indrelid
and i.oid = ix.indexrelid
and t.relkind = 'r'
and ix.indisprimary = false
and t.relnamespace = c.oid
and c.nspname != 'pg_catalog'
and ('tables:' || c.nspname || '.' || t.relname) =OBJECT_ID_CONDITION
order by
t.relname
`;

View File

@@ -0,0 +1,17 @@
module.exports = `
SELECT pg_namespace.nspname AS "schema_name"
, pg_class.relname AS "pure_name"
, pg_attribute.attname AS "column_name"
, pg_catalog.format_type(pg_attribute.atttypid, pg_attribute.atttypmod) AS "data_type"
FROM pg_catalog.pg_class
INNER JOIN pg_catalog.pg_namespace
ON pg_class.relnamespace = pg_namespace.oid
INNER JOIN pg_catalog.pg_attribute
ON pg_class.oid = pg_attribute.attrelid
-- Keeps only materialized views, and non-db/catalog/index columns
WHERE pg_class.relkind = 'm'
AND pg_attribute.attnum >= 1
AND ('matviews:' || pg_namespace.nspname || '.' || pg_class.relname) =OBJECT_ID_CONDITION
ORDER BY pg_attribute.attnum
`;

View File

@@ -0,0 +1,8 @@
module.exports = `
select
matviewname as "pure_name",
schemaname as "schema_name",
md5(definition) as "hash_code"
from
pg_catalog.pg_matviews WHERE schemaname NOT LIKE 'pg_%'
`;

View File

@@ -0,0 +1,10 @@
module.exports = `
select
matviewname as "pure_name",
schemaname as "schema_name",
definition as "definition",
md5(definition) as "hash_code"
from
pg_catalog.pg_matviews WHERE schemaname NOT LIKE 'pg_%'
and ('matviews:' || schemaname || '.' || matviewname) =OBJECT_ID_CONDITION
`;

View File

@@ -0,0 +1,17 @@
module.exports = `
select
table_constraints.constraint_schema as "constraint_schema",
table_constraints.constraint_name as "constraint_name",
table_constraints.table_schema as "schema_name",
table_constraints.table_name as "pure_name",
key_column_usage.column_name as "column_name"
from information_schema.table_constraints
inner join information_schema.key_column_usage on table_constraints.table_name = key_column_usage.table_name and table_constraints.constraint_name = key_column_usage.constraint_name
where
table_constraints.table_schema <> 'information_schema'
and table_constraints.table_schema <> 'pg_catalog'
and table_constraints.table_schema !~ '^pg_toast'
and table_constraints.constraint_type = 'PRIMARY KEY'
and ('tables:' || table_constraints.table_schema || '.' || table_constraints.table_name) =OBJECT_ID_CONDITION
order by key_column_usage.ordinal_position
`;

View File

@@ -0,0 +1,10 @@
module.exports = `
select
routine_name as "pure_name",
routine_schema as "schema_name",
md5(routine_definition) as "hash_code",
routine_type as "object_type"
from
information_schema.routines where routine_schema != 'information_schema' and routine_schema != 'pg_catalog'
and routine_type in ('PROCEDURE', 'FUNCTION')
`;

View File

@@ -0,0 +1,17 @@
module.exports = `
select
routine_name as "pure_name",
routine_schema as "schema_name",
routine_definition as "definition",
md5(routine_definition) as "hash_code",
routine_type as "object_type",
data_type as "data_type",
external_language as "language"
from
information_schema.routines where routine_schema != 'information_schema' and routine_schema != 'pg_catalog'
and (
(routine_type = 'PROCEDURE' and ('procedures:' || routine_schema || '.' || routine_name) =OBJECT_ID_CONDITION)
or
(routine_type = 'FUNCTION' and ('functions:' || routine_schema || '.' || routine_name) =OBJECT_ID_CONDITION)
)
`;

View File

@@ -0,0 +1,18 @@
module.exports = `
select ao.owner as "schema_name", ao.object_name as "pure_name"
from all_objects ao
where exists(select null from user_objects uo where uo.object_id = ao.object_id)
and object_type = 'TABLE'
`;
/*
module.exports = `
select infoTables.table_schema as "schema_name", infoTables.table_name as "pure_name"
from information_schema.tables infoTables
where infoTables.table_type not like '%VIEW%'
and ('tables:' || infoTables.table_schema || '.' || infoTables.table_name) =OBJECT_ID_CONDITION
and infoTables.table_schema <> 'pg_catalog'
and infoTables.table_schema <> 'information_schema'
and infoTables.table_schema <> 'pg_internal'
and infoTables.table_schema !~ '^pg_toast'
`;
*/

View File

@@ -0,0 +1,28 @@
module.exports = `
select infoTables.table_schema as "schema_name", infoTables.table_name as "pure_name",
(
select md5(string_agg(
infoColumns.column_name || '|' || infoColumns.data_type || '|' || infoColumns.is_nullable::varchar(255) || '|' || coalesce(infoColumns.character_maximum_length, -1)::varchar(255)
|| '|' || coalesce(infoColumns.numeric_precision, -1)::varchar(255) ,
',' order by infoColumns.ordinal_position
)) as "hash_code_columns"
from information_schema.columns infoColumns
where infoColumns.table_schema = infoTables.table_schema and infoColumns.table_name = infoTables.table_name
),
(
select md5(string_agg(
infoConstraints.constraint_name || '|' || infoConstraints.constraint_type ,
',' order by infoConstraints.constraint_name
)) as "hash_code_constraints"
from information_schema.table_constraints infoConstraints
where infoConstraints.table_schema = infoTables.table_schema and infoConstraints.table_name = infoTables.table_name
)
from information_schema.tables infoTables
where infoTables.table_type not like '%VIEW%'
and ('tables:' || infoTables.table_schema || '.' || infoTables.table_name) =OBJECT_ID_CONDITION
and infoTables.table_schema <> 'pg_catalog'
and infoTables.table_schema <> 'information_schema'
and infoTables.table_schema <> 'pg_internal'
and infoTables.table_schema !~ '^pg_toast'
`;

View File

@@ -0,0 +1,3 @@
module.exports = `
select conname as "constraint_name" from pg_constraint where contype = 'u'
`;

View File

@@ -0,0 +1,8 @@
module.exports = `
select
table_name as "pure_name",
table_schema as "schema_name",
md5(view_definition) as "hash_code"
from
information_schema.views where table_schema != 'information_schema' and table_schema != 'pg_catalog'
`;

View File

@@ -0,0 +1,9 @@
module.exports = `
select
ao.owner as "schema_name", ao.object_name as "pure_name",
'later' as "create_sql",
object_id as "hash_code"
from all_objects ao
where exists(select null from user_objects uo where uo.object_id = ao.object_id)
and object_type = 'VIEW'
`;

View File

@@ -0,0 +1,100 @@
const { SqlDumper, arrayToHexString, testEqualTypes } = global.DBGATE_TOOLS;
class Dumper extends SqlDumper {
/** @param type {import('dbgate-types').TransformType} */
transform(type, dumpExpr) {
switch (type) {
case 'GROUP:YEAR':
case 'YEAR':
this.put('^extract(^year ^from %c)', dumpExpr);
break;
case 'MONTH':
this.put('^extract(^month ^from %c)', dumpExpr);
break;
case 'DAY':
this.put('^extract(^day ^from %c)', dumpExpr);
break;
case 'GROUP:MONTH':
this.put("^to_char(%c, '%s')", dumpExpr, 'YYYY-MM');
break;
case 'GROUP:DAY':
this.put("^to_char(%c, '%s')", dumpExpr, 'YYYY-MM-DD');
break;
default:
dumpExpr();
break;
}
}
dropRecreatedTempTable(tmptable) {
this.putCmd('^drop ^table %i ^cascade', tmptable);
}
renameTable(obj, newname) {
this.putCmd('^alter ^table %f ^rename ^to %i', obj, newname);
}
renameColumn(column, newcol) {
this.putCmd('^alter ^table %f ^rename ^column %i ^to %i', column, column.columnName, newcol);
}
dropTable(obj, options = {}) {
this.put('^drop ^table');
if (options.testIfExists) this.put(' ^if ^exists');
this.put(' %f', obj);
this.endCommand();
}
//public override void CreateIndex(IndexInfo ix)
//{
//}
enableConstraints(table, enabled) {
this.putCmd('^alter ^table %f %k ^trigger ^all', table, enabled ? 'enable' : 'disable');
}
columnDefinition(col, options) {
if (col.autoIncrement) {
this.put('^serial');
return;
}
super.columnDefinition(col, options);
}
changeColumn(oldcol, newcol, constraints) {
if (oldcol.columnName != newcol.columnName) {
this.putCmd('^alter ^table %f ^rename ^column %i ^to %i', oldcol, oldcol.columnName, newcol.columnName);
}
if (!testEqualTypes(oldcol, newcol)) {
this.putCmd('^alter ^table %f ^alter ^column %i ^type %s', oldcol, newcol.columnName, newcol.dataType);
}
if (oldcol.notNull != newcol.notNull) {
if (newcol.notNull) this.putCmd('^alter ^table %f ^alter ^column %i ^set ^not ^null', newcol, newcol.columnName);
else this.putCmd('^alter ^table %f ^alter ^column %i ^drop ^not ^null', newcol, newcol.columnName);
}
if (oldcol.defaultValue != newcol.defaultValue) {
if (newcol.defaultValue == null) {
this.putCmd('^alter ^table %f ^alter ^column %i ^drop ^default', newcol, newcol.columnName);
} else {
this.putCmd(
'^alter ^table %f ^alter ^column %i ^set ^default %s',
newcol,
newcol.columnName,
newcol.defaultValue
);
}
}
}
putValue(value) {
if (value === true) this.putRaw('true');
else if (value === false) this.putRaw('false');
else super.putValue(value);
}
putByteArrayValue(value) {
this.putRaw(`e'\\\\x${arrayToHexString(value)}'`);
}
}
module.exports = Dumper;

View File

@@ -0,0 +1,200 @@
const { driverBase } = global.DBGATE_TOOLS;
const Dumper = require('./Dumper');
const { oracleSplitterOptions } = require('dbgate-query-splitter/lib/options');
const spatialTypes = ['GEOGRAPHY'];
/** @type {import('dbgate-types').SqlDialect} */
const dialect = {
rangeSelect: true,
ilike: true,
// stringEscapeChar: '\\',
stringEscapeChar: "'",
fallbackDataType: 'varchar',
anonymousPrimaryKey: true,
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,
dropReferencesWhenDropTable: 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) {
if (dataType && spatialTypes.includes(dataType.toUpperCase())) {
return {
exprType: 'call',
func: 'ST_AsText',
alias: alias || columnName,
args: [
{
exprType: 'column',
columnName,
source,
},
],
};
}
},
};
const oracleDriverBase = {
...driverBase,
dumperClass: Dumper,
dialect,
// showConnectionField: (field, values) =>
// ['server', 'port', 'user', 'password', 'defaultDatabase', 'singleDatabase'].includes(field),
getQuerySplitterOptions: () => oracleSplitterOptions,
readOnlySessions: true,
databaseUrlPlaceholder: 'e.g. oracledb://user:password@localhost:1521',
showConnectionField: (field, values) => {
if (field == 'useDatabaseUrl') return true;
if (values.useDatabaseUrl) {
return ['databaseUrl', 'isReadOnly'].includes(field);
}
return (
['authType', 'user', 'password', 'defaultDatabase', 'singleDatabase', 'isReadOnly'].includes(field) ||
(values.authType == 'socket' && ['socketPath'].includes(field)) ||
(values.authType != 'socket' && ['server', 'port'].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: {
refTableCond: '',
},
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;`,
},
];
},
authTypeLabel: 'Connection mode',
defaultAuthTypeName: 'hostPort',
defaultSocketPath: '/var/run/oracledb',
};
/** @type {import('dbgate-types').EngineDriver} */
const oracleDriver = {
...oracleDriverBase,
engine: 'oracle@dbgate-plugin-oracle',
title: 'OracleDB',
defaultPort: 1521,
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),
};
}
return dialect;
},
};
module.exports = [oracleDriver];

View File

@@ -0,0 +1,6 @@
import drivers from './drivers';
export default {
packageName: 'dbgate-plugin-oracle',
drivers,
};