mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-18 07:56:01 +00:00
feat: basic firebird analyser
This commit is contained in:
51
plugins/dbgate-plugin-firebird/src/backend/Analyser.js
Normal file
51
plugins/dbgate-plugin-firebird/src/backend/Analyser.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const _ = require('lodash');
|
||||
const sql = require('./sql');
|
||||
const { getDataTypeString } = require('./helpers');
|
||||
|
||||
const { DatabaseAnalyser } = require('dbgate-tools');
|
||||
|
||||
class Analyser extends DatabaseAnalyser {
|
||||
constructor(dbhan, driver, version) {
|
||||
super(dbhan, driver, version);
|
||||
}
|
||||
|
||||
async _runAnalysis() {
|
||||
const tablesResult = await this.driver.query(this.dbhan, sql.tables);
|
||||
const columnsResult = await this.driver.query(this.dbhan, sql.columns);
|
||||
|
||||
const columns = columnsResult.rows.map(i => ({
|
||||
tableName: i.TABLENAME,
|
||||
columnName: i.COLUMNNAME,
|
||||
notNull: i.NOTNULL,
|
||||
isPrimaryKey: i.ISPRIMARYKEY,
|
||||
dataType: getDataTypeString(i),
|
||||
precision: i.NUMBERPRECISION,
|
||||
scale: i.SCALE,
|
||||
length: i.LENGTH,
|
||||
defaultValue: i.DEFAULTVALUE,
|
||||
columnComment: i.COLUMNCOMMENT,
|
||||
isUnsigned: i.ISUNSIGNED,
|
||||
pureName: i.PURENAME,
|
||||
schemaName: i.SCHEMANAME,
|
||||
}));
|
||||
const tables = tablesResult.rows.map(i => ({
|
||||
pureName: i.PURENAME,
|
||||
objectId: i.OBJECTID,
|
||||
schemaName: i.SCHEMANAME,
|
||||
objectComment: i.OBJECTCOMMENT,
|
||||
}));
|
||||
|
||||
return {
|
||||
tables: tables.map(table => ({
|
||||
...table,
|
||||
columns: columns.filter(column => column.tableName === table.pureName),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
async _getFastSnapshot() {
|
||||
return this._runAnalysis();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Analyser;
|
||||
146
plugins/dbgate-plugin-firebird/src/backend/driver.js
Normal file
146
plugins/dbgate-plugin-firebird/src/backend/driver.js
Normal file
@@ -0,0 +1,146 @@
|
||||
const _ = require('lodash');
|
||||
const driverBase = require('../frontend/driver');
|
||||
const Analyser = require('./Analyser');
|
||||
const Firebird = require('node-firebird');
|
||||
const { getLogger, extractErrorLogData, createBulkInsertStreamBase } = require('dbgate-tools');
|
||||
const sql = require('./sql');
|
||||
|
||||
const logger = getLogger('firebird');
|
||||
|
||||
/** @type {import('dbgate-types').EngineDriver<Firebird.Database>} */
|
||||
const driver = {
|
||||
...driverBase,
|
||||
analyserClass: Analyser,
|
||||
async connect({ port, user, password, server, databaseFile }) {
|
||||
const options = {
|
||||
host: server,
|
||||
port,
|
||||
database: databaseFile,
|
||||
user,
|
||||
password,
|
||||
};
|
||||
|
||||
/**@type {Firebird.Database} */
|
||||
const db = await new Promise((resolve, reject) => {
|
||||
Firebird.attach(options, (err, db) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(db);
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
client: db,
|
||||
};
|
||||
},
|
||||
|
||||
async query(dbhan, sql) {
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
dbhan.client.query(sql, (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
const columns = res[0] ? Object.keys(res[0]).map(i => ({ columnName: i })) : [];
|
||||
|
||||
return {
|
||||
rows: res,
|
||||
columns,
|
||||
};
|
||||
},
|
||||
|
||||
async script(dbhan, sql) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
async stream(dbhan, sql, options) {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
let hasSentColumns = false;
|
||||
dbhan.client.sequentially(
|
||||
sql,
|
||||
[],
|
||||
(row, index) => {
|
||||
if (!hasSentColumns) {
|
||||
hasSentColumns = true;
|
||||
const columns = Object.keys(row).map(i => ({ columnName: i }));
|
||||
options.recordset(columns);
|
||||
}
|
||||
|
||||
options.row(row);
|
||||
},
|
||||
err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
options.done();
|
||||
} catch (err) {
|
||||
logger.error(extractErrorLogData(err), 'Stream error');
|
||||
options.info({
|
||||
message: err.message,
|
||||
line: err.line,
|
||||
// procedure: procName,
|
||||
time: new Date(),
|
||||
severity: 'error',
|
||||
});
|
||||
options.done();
|
||||
}
|
||||
},
|
||||
|
||||
async readQuery(dbhan, sql, structure) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
async writeTable(dbhan, name, options) {
|
||||
return createBulkInsertStream(this, stream, dbhan, name, options);
|
||||
},
|
||||
|
||||
async getVersion(dbhan) {
|
||||
const res = await this.query(dbhan, sql.version);
|
||||
const version = res.rows?.[0]?.VERSION;
|
||||
|
||||
return {
|
||||
version,
|
||||
versionText: `Firebird ${version}`,
|
||||
};
|
||||
},
|
||||
|
||||
async listDatabases(dbhan) {
|
||||
return [
|
||||
{
|
||||
name: 'default',
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
async createDatabase(dbhan, name) {},
|
||||
|
||||
async dropDatabase(dbhan, name) {},
|
||||
|
||||
async close(dbhan) {
|
||||
return new Promise((resolve, reject) => {
|
||||
dbhan.client.detach(err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = driver;
|
||||
54
plugins/dbgate-plugin-firebird/src/backend/helpers.js
Normal file
54
plugins/dbgate-plugin-firebird/src/backend/helpers.js
Normal file
@@ -0,0 +1,54 @@
|
||||
function getDataTypeString(column) {
|
||||
if (!column) {
|
||||
return null;
|
||||
}
|
||||
const { DATATYPECODE, SCALE, LENGTH, NUMBERPRECISION } = column;
|
||||
|
||||
switch (DATATYPECODE) {
|
||||
case 7:
|
||||
return 'SMALLINT';
|
||||
|
||||
case 8:
|
||||
return 'INTEGER';
|
||||
|
||||
case 9:
|
||||
return 'BIGINT';
|
||||
|
||||
case 10:
|
||||
return 'FLOAT';
|
||||
|
||||
case 11:
|
||||
return 'DOUBLE PRECISION';
|
||||
|
||||
case 12:
|
||||
return 'DATE';
|
||||
|
||||
case 13:
|
||||
return 'TIME';
|
||||
|
||||
case 14:
|
||||
return `CHAR(${LENGTH})`;
|
||||
|
||||
case 16:
|
||||
return `DECIMAL(${NUMBERPRECISION}, ${SCALE})`;
|
||||
|
||||
case 27:
|
||||
return 'DOUBLE PRECISION';
|
||||
|
||||
case 35:
|
||||
return 'BLOB';
|
||||
|
||||
case 37:
|
||||
return `VARCHAR(${LENGTH})`;
|
||||
|
||||
case 261:
|
||||
return 'CSTRING';
|
||||
|
||||
default:
|
||||
return `UNKNOWN (${DATATYPECODE})`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getDataTypeString,
|
||||
};
|
||||
7
plugins/dbgate-plugin-firebird/src/backend/index.js
Normal file
7
plugins/dbgate-plugin-firebird/src/backend/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const driver = require('./driver');
|
||||
|
||||
module.exports = {
|
||||
packageName: 'dbgate-plugin-firebird',
|
||||
drivers: [driver],
|
||||
initialize(dbgateEnv) {},
|
||||
};
|
||||
43
plugins/dbgate-plugin-firebird/src/backend/sql/columns.js
Normal file
43
plugins/dbgate-plugin-firebird/src/backend/sql/columns.js
Normal file
@@ -0,0 +1,43 @@
|
||||
module.exports = `
|
||||
SELECT DISTINCT
|
||||
CAST(TRIM(rf.rdb$relation_name) AS VARCHAR(255)) AS tableName,
|
||||
CAST(TRIM(rf.rdb$field_name) AS VARCHAR(255)) AS columnName,
|
||||
CASE rf.rdb$null_flag WHEN 1 THEN FALSE ELSE TRUE END AS notNull,
|
||||
CASE
|
||||
WHEN EXISTS (
|
||||
SELECT 1
|
||||
FROM rdb$relation_constraints rc
|
||||
JOIN rdb$index_segments idx ON rc.rdb$index_name = idx.rdb$index_name
|
||||
WHERE rc.rdb$relation_name = rf.rdb$relation_name
|
||||
AND idx.rdb$field_name = rf.rdb$field_name
|
||||
AND rc.rdb$constraint_type = 'PRIMARY KEY'
|
||||
) THEN TRUE
|
||||
ELSE FALSE
|
||||
END AS isPrimaryKey,
|
||||
f.rdb$field_type AS dataTypeCode,
|
||||
f.rdb$field_precision AS numberprecision,
|
||||
f.rdb$field_scale AS scale,
|
||||
f.rdb$field_length AS length,
|
||||
CAST(TRIM(rf.rdb$default_value) AS VARCHAR(255)) AS defaultValue,
|
||||
CAST(TRIM(rf.rdb$description) AS VARCHAR(255)) AS columnComment,
|
||||
CASE
|
||||
WHEN f.rdb$field_type IN (8, 9, 16) AND f.rdb$field_scale < 0 THEN TRUE
|
||||
ELSE FALSE
|
||||
END AS isUnsigned,
|
||||
CAST(TRIM(rf.rdb$field_name) AS VARCHAR(255)) AS pureName,
|
||||
CAST(TRIM(r.rdb$owner_name) AS VARCHAR(255)) AS schemaName
|
||||
FROM
|
||||
rdb$relation_fields rf
|
||||
JOIN
|
||||
rdb$relations r ON rf.rdb$relation_name = r.rdb$relation_name
|
||||
LEFT JOIN
|
||||
rdb$fields f ON rf.rdb$field_source = f.rdb$field_name
|
||||
LEFT JOIN
|
||||
rdb$character_sets cs ON f.rdb$character_set_id = cs.rdb$character_set_id
|
||||
LEFT JOIN
|
||||
rdb$collations co ON f.rdb$collation_id = co.rdb$collation_id
|
||||
WHERE
|
||||
r.rdb$system_flag = 0
|
||||
ORDER BY
|
||||
tableName, rf.rdb$field_position;
|
||||
`;
|
||||
9
plugins/dbgate-plugin-firebird/src/backend/sql/index.js
Normal file
9
plugins/dbgate-plugin-firebird/src/backend/sql/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const version = require('./version');
|
||||
const tables = require('./tables');
|
||||
const columns = require('./columns');
|
||||
|
||||
module.exports = {
|
||||
version,
|
||||
columns,
|
||||
tables,
|
||||
};
|
||||
9
plugins/dbgate-plugin-firebird/src/backend/sql/tables.js
Normal file
9
plugins/dbgate-plugin-firebird/src/backend/sql/tables.js
Normal file
@@ -0,0 +1,9 @@
|
||||
module.exports = `
|
||||
SELECT
|
||||
TRIM(RDB$RELATION_NAME) AS pureName,
|
||||
RDB$DESCRIPTION AS objectComment,
|
||||
RDB$FORMAT AS objectTypeField
|
||||
FROM RDB$RELATIONS
|
||||
WHERE RDB$SYSTEM_FLAG = 0 -- only user-defined tables
|
||||
ORDER BY pureName;
|
||||
`;
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = `SELECT rdb$get_context('SYSTEM', 'ENGINE_VERSION') as version from rdb$database;`;
|
||||
Reference in New Issue
Block a user