diff --git a/integration-tests/tools.js b/integration-tests/tools.js index 7b13a2657..0a8b3e2b2 100644 --- a/integration-tests/tools.js +++ b/integration-tests/tools.js @@ -5,7 +5,12 @@ const crypto = require('crypto'); function randomDbName(dialect) { const generatedKey = crypto.randomBytes(6); const newKey = generatedKey.toString('hex'); - const res = `db${newKey}`; + let res = `db${newKey}`; + + if (dialect.dbFileExtension) { + res += dialect.dbFileExtension; + } + if (dialect.upperCaseAllDbObjectNames) return res.toUpperCase(); return res; } @@ -17,7 +22,7 @@ async function connect(engine, database) { if (engine.generateDbFile) { const conn = await driver.connect({ ...connection, - databaseFile: `dbtemp/${database}`, + databaseFile: (engine.databaseFileLocationOnServer ?? 'dbtemp/') + database, }); return conn; } else { diff --git a/packages/tools/src/SqlDumper.ts b/packages/tools/src/SqlDumper.ts index 371fb75fe..ce8c36a9f 100644 --- a/packages/tools/src/SqlDumper.ts +++ b/packages/tools/src/SqlDumper.ts @@ -265,11 +265,11 @@ export class SqlDumper implements AlterProcessor { this.columnDefault(column); } if (includeNullable && !this.dialect?.specificNullabilityImplementation) { - this.put(column.notNull ? '^not ^null' : '^null'); + this.put(column.notNull ? '^not ^null' : this.dialect.implicitNullDeclaration ? '^null' : ''); } } else { if (includeNullable && !this.dialect?.specificNullabilityImplementation) { - this.put(column.notNull ? '^not ^null' : '^null'); + this.put(column.notNull ? '^not ^null' : this.dialect.implicitNullDeclaration ? '^null' : ''); } if (includeDefault && column.defaultValue?.toString()?.trim()) { this.columnDefault(column); diff --git a/packages/types/dialect.d.ts b/packages/types/dialect.d.ts index 53baf4a0b..19a25a02f 100644 --- a/packages/types/dialect.d.ts +++ b/packages/types/dialect.d.ts @@ -48,6 +48,7 @@ export interface SqlDialect { namedDefaultConstraint?: boolean; specificNullabilityImplementation?: boolean; + implicitNullDeclaration?: boolean; omitForeignKeys?: boolean; omitUniqueConstraints?: boolean; omitIndexes?: boolean; @@ -67,6 +68,7 @@ export interface SqlDialect { requireFromDual?: boolean; userDatabaseNamePrefix?: string; // c## in Oracle upperCaseAllDbObjectNames?: boolean; + dbFileExtension?: string; defaultValueBeforeNullability?: boolean; predefinedDataTypes: string[]; diff --git a/packages/types/test-engines.d.ts b/packages/types/test-engines.d.ts index 9cea205c4..271c38bb6 100644 --- a/packages/types/test-engines.d.ts +++ b/packages/types/test-engines.d.ts @@ -60,6 +60,8 @@ export type TestEngineInfo = { defaultSchemaName?: string; generateDbFile?: boolean; + generateDbFileOnServer?: boolean; + databaseFileLocationOnServer?: string; dbSnapshotBySeconds?: boolean; dumpFile?: string; dumpChecks?: Array<{ sql: string; res: string }>; diff --git a/plugins/dbgate-plugin-firebird/src/backend/driver.js b/plugins/dbgate-plugin-firebird/src/backend/driver.js index 5c04830b0..be411482c 100644 --- a/plugins/dbgate-plugin-firebird/src/backend/driver.js +++ b/plugins/dbgate-plugin-firebird/src/backend/driver.js @@ -1,4 +1,5 @@ const _ = require('lodash'); +const stream = require('stream'); const driverBase = require('../frontend/driver'); const Analyser = require('./Analyser'); const Firebird = require('node-firebird'); @@ -22,7 +23,7 @@ const driver = { /**@type {Firebird.Database} */ const db = await new Promise((resolve, reject) => { - Firebird.attach(options, (err, db) => { + Firebird.attachOrCreate(options, (err, db) => { if (err) { reject(err); return; @@ -47,18 +48,14 @@ const driver = { resolve(result); }); }); - const columns = res[0] ? Object.keys(res[0]).map(i => ({ columnName: i })) : []; + const columns = res?.[0] ? Object.keys(res[0]).map(i => ({ columnName: i })) : []; return { - rows: res, + rows: res ?? [], columns, }; }, - async script(dbhan, sql) { - throw new Error('Not implemented'); - }, - async stream(dbhan, sql, options) { try { await new Promise((resolve, reject) => { @@ -101,7 +98,36 @@ const driver = { }, async readQuery(dbhan, sql, structure) { - throw new Error('Not implemented'); + console.log('readQuery from SQL:', sql); + const pass = new stream.PassThrough({ + objectMode: true, + highWaterMark: 100, + }); + let hasSentColumns = false; + + dbhan.client.sequentially( + sql, + [], + (row, index) => { + if (!hasSentColumns) { + hasSentColumns = true; + + const columns = Object.keys(row).map(i => ({ columnName: i })); + + pass.write({ + __isStreamHeader: true, + ...(structure || { columns }), + }); + } + + pass.write(row); + }, + err => { + pass.end(); + } + ); + + return pass; }, async writeTable(dbhan, name, options) { @@ -126,10 +152,6 @@ const driver = { ]; }, - async createDatabase(dbhan, name) {}, - - async dropDatabase(dbhan, name) {}, - async close(dbhan) { return new Promise((resolve, reject) => { dbhan.client.detach(err => { diff --git a/plugins/dbgate-plugin-firebird/src/frontend/Dumper.js b/plugins/dbgate-plugin-firebird/src/frontend/Dumper.js index f0df62e8f..154361d0a 100644 --- a/plugins/dbgate-plugin-firebird/src/frontend/Dumper.js +++ b/plugins/dbgate-plugin-firebird/src/frontend/Dumper.js @@ -1,5 +1,9 @@ const { SqlDumper } = global.DBGATE_PACKAGES['dbgate-tools']; -class Dumper extends SqlDumper {} +class Dumper extends SqlDumper { + autoIncrement() { + this.put(' ^generated ^by ^default ^as ^identity'); + } +} module.exports = Dumper; diff --git a/plugins/dbgate-plugin-firebird/src/frontend/driver.js b/plugins/dbgate-plugin-firebird/src/frontend/driver.js index 69d729dae..0212e4f04 100644 --- a/plugins/dbgate-plugin-firebird/src/frontend/driver.js +++ b/plugins/dbgate-plugin-firebird/src/frontend/driver.js @@ -17,6 +17,8 @@ const dialect = { return `"${s}"`; }, + dbFileExtension: '.fdb', + createColumn: true, dropColumn: true, changeColumn: true,