diff --git a/integration-tests/__tests__/alter-database.spec.js b/integration-tests/__tests__/alter-database.spec.js index b786c471..b06dcf58 100644 --- a/integration-tests/__tests__/alter-database.spec.js +++ b/integration-tests/__tests__/alter-database.spec.js @@ -7,7 +7,9 @@ const { getAlterDatabaseScript, extendDatabaseInfo, generateDbPairingId } = requ function flatSource() { return _.flatten( - engines.map(engine => (engine.objects || []).map(object => [engine.label, object.type, object, engine])) + engines + .filter(x => !x.skipReferences) + .map(engine => (engine.objects || []).map(object => [engine.label, object.type, object, engine])) ); } @@ -41,7 +43,7 @@ async function testDatabaseDiff(conn, driver, mangle, createObject = null) { } describe('Alter database', () => { - test.each(engines.map(engine => [engine.label, engine]))( + test.each(engines.filter(x => !x.skipReferences).map(engine => [engine.label, engine]))( 'Drop referenced table - %s', testWrapper(async (conn, driver, engine) => { await testDatabaseDiff(conn, driver, db => { diff --git a/integration-tests/__tests__/alter-table.spec.js b/integration-tests/__tests__/alter-table.spec.js index 7b6f1e0b..1b8a020f 100644 --- a/integration-tests/__tests__/alter-table.spec.js +++ b/integration-tests/__tests__/alter-table.spec.js @@ -6,39 +6,44 @@ const engines = require('../engines'); const crypto = require('crypto'); const { getAlterTableScript, extendDatabaseInfo, generateDbPairingId } = require('dbgate-tools'); -function pickImportantTableInfo(table) { +function pickImportantTableInfo(engine, table) { + const props = ['columnName']; + if (!engine.skipNullability) props.push('notNull'); + if (!engine.skipAutoIncrement) props.push('autoIncrement'); return { pureName: table.pureName, - columns: table.columns - .filter(x => x.columnName != 'rowid') - .map(fp.pick(['columnName', 'notNull', 'autoIncrement'])), + columns: table.columns.filter(x => x.columnName != 'rowid').map(fp.pick(props)), }; } -function checkTableStructure(t1, t2) { +function checkTableStructure(engine, t1, t2) { // expect(t1.pureName).toEqual(t2.pureName) - expect(pickImportantTableInfo(t1)).toEqual(pickImportantTableInfo(t2)); + expect(pickImportantTableInfo(engine, t1)).toEqual(pickImportantTableInfo(engine, t2)); } -async function testTableDiff(conn, driver, mangle) { +async function testTableDiff(engine, conn, driver, mangle) { await driver.query(conn, `create table t0 (id int not null primary key)`); await driver.query( conn, `create table t1 ( col_pk int not null primary key, - col_std int null, - col_def int null default 12, - col_fk int null references t0(id), - col_idx int null, - col_uq int null unique, - col_ref int null unique + col_std int, + col_def int default 12, + ${engine.skipReferences ? '' : 'col_fk int references t0(id),'} + col_idx int, + col_uq int ${engine.skipUnique ? '' : 'unique'} , + col_ref int ${engine.skipUnique ? '' : 'unique'} )` ); - await driver.query(conn, `create index idx1 on t1(col_idx)`); + if (!engine.skipIndexes) { + await driver.query(conn, `create index idx1 on t1(col_idx)`); + } - await driver.query(conn, `create table t2 (id int not null primary key, fkval int null references t1(col_ref))`); + if (!engine.skipReferences) { + await driver.query(conn, `create table t2 (id int not null primary key, fkval int null references t1(col_ref))`); + } const tget = x => x.tables.find(y => y.pureName == 't1'); const structure1 = generateDbPairingId(extendDatabaseInfo(await driver.analyseFull(conn))); @@ -53,7 +58,7 @@ async function testTableDiff(conn, driver, mangle) { const structure2Real = extendDatabaseInfo(await driver.analyseFull(conn)); - checkTableStructure(tget(structure2Real), tget(structure2)); + checkTableStructure(engine, tget(structure2Real), tget(structure2)); // expect(stableStringify(structure2)).toEqual(stableStringify(structure2Real)); } @@ -72,7 +77,7 @@ describe('Alter table', () => { test.each(engines.map(engine => [engine.label, engine]))( 'Add column - %s', testWrapper(async (conn, driver, engine) => { - await testTableDiff(conn, driver, tbl => { + await testTableDiff(engine, conn, driver, tbl => { tbl.columns.push({ columnName: 'added', dataType: 'int', @@ -87,7 +92,7 @@ describe('Alter table', () => { test.each(engines_columns_source())( 'Drop column - %s - %s', testWrapper(async (conn, driver, column, engine) => { - await testTableDiff(conn, driver, tbl => (tbl.columns = tbl.columns.filter(x => x.columnName != column))); + await testTableDiff(engine, conn, driver, tbl => (tbl.columns = tbl.columns.filter(x => x.columnName != column))); }) ); @@ -95,6 +100,7 @@ describe('Alter table', () => { 'Change nullability - %s - %s', testWrapper(async (conn, driver, column, engine) => { await testTableDiff( + engine, conn, driver, tbl => (tbl.columns = tbl.columns.map(x => (x.columnName == column ? { ...x, notNull: true } : x))) @@ -106,6 +112,7 @@ describe('Alter table', () => { 'Rename column - %s - %s', testWrapper(async (conn, driver, column, engine) => { await testTableDiff( + engine, conn, driver, tbl => (tbl.columns = tbl.columns.map(x => (x.columnName == column ? { ...x, columnName: 'col_renamed' } : x))) @@ -116,7 +123,7 @@ describe('Alter table', () => { test.each(engines.map(engine => [engine.label, engine]))( 'Drop index - %s', testWrapper(async (conn, driver, engine) => { - await testTableDiff(conn, driver, tbl => { + await testTableDiff(engine, conn, driver, tbl => { tbl.indexes = []; }); }) diff --git a/integration-tests/__tests__/query.spec.js b/integration-tests/__tests__/query.spec.js index 1336a7df..d0164828 100644 --- a/integration-tests/__tests__/query.spec.js +++ b/integration-tests/__tests__/query.spec.js @@ -2,7 +2,11 @@ const engines = require('../engines'); const { splitQuery } = require('dbgate-query-splitter'); const { testWrapper } = require('../tools'); -const initSql = ['CREATE TABLE t1 (id int)', 'INSERT INTO t1 (id) VALUES (1)', 'INSERT INTO t1 (id) VALUES (2)']; +const initSql = [ + 'CREATE TABLE t1 (id int primary key)', + 'INSERT INTO t1 (id) VALUES (1)', + 'INSERT INTO t1 (id) VALUES (2)', +]; expect.extend({ dataRow(row, expected) { @@ -64,7 +68,7 @@ describe('Query', () => { test.each(engines.map(engine => [engine.label, engine]))( 'Simple query - %s', testWrapper(async (conn, driver, engine) => { - for (const sql of initSql) await driver.query(conn, sql); + for (const sql of initSql) await driver.query(conn, sql, { discardResult: true }); const res = await driver.query(conn, 'SELECT id FROM t1 ORDER BY id'); expect(res.columns).toEqual([ @@ -87,7 +91,7 @@ describe('Query', () => { test.each(engines.map(engine => [engine.label, engine]))( 'Simple stream query - %s', testWrapper(async (conn, driver, engine) => { - for (const sql of initSql) await driver.query(conn, sql); + for (const sql of initSql) await driver.query(conn, sql, { discardResult: true }); const results = await executeStream(driver, conn, 'SELECT id FROM t1 ORDER BY id'); expect(results.length).toEqual(1); const res = results[0]; @@ -100,7 +104,7 @@ describe('Query', () => { test.each(engines.map(engine => [engine.label, engine]))( 'More queries - %s', testWrapper(async (conn, driver, engine) => { - for (const sql of initSql) await driver.query(conn, sql); + for (const sql of initSql) await driver.query(conn, sql, { discardResult: true }); const results = await executeStream( driver, conn, @@ -124,7 +128,7 @@ describe('Query', () => { const results = await executeStream( driver, conn, - 'CREATE TABLE t1 (id int); INSERT INTO t1 (id) VALUES (1); INSERT INTO t1 (id) VALUES (2); SELECT id FROM t1 ORDER BY id; ' + 'CREATE TABLE t1 (id int primary key); INSERT INTO t1 (id) VALUES (1); INSERT INTO t1 (id) VALUES (2); SELECT id FROM t1 ORDER BY id; ' ); expect(results.length).toEqual(1); @@ -146,14 +150,15 @@ describe('Query', () => { }) ); - test.each(engines.map(engine => [engine.label, engine]))( + test.each(engines.filter(x => !x.skipDataModifications).map(engine => [engine.label, engine]))( 'Save data query - %s', testWrapper(async (conn, driver, engine) => { - for (const sql of initSql) await driver.query(conn, sql); + for (const sql of initSql) await driver.query(conn, sql, { discardResult: true }); await driver.script( conn, - 'INSERT INTO t1 (id) VALUES (3);INSERT INTO t1 (id) VALUES (4);UPDATE t1 SET id=10 WHERE id=1;DELETE FROM t1 WHERE id=2;' + 'INSERT INTO t1 (id) VALUES (3);INSERT INTO t1 (id) VALUES (4);UPDATE t1 SET id=10 WHERE id=1;DELETE FROM t1 WHERE id=2;', + { discardResult: true } ); const res = await driver.query(conn, 'SELECT COUNT(*) AS cnt FROM t1'); // console.log(res); diff --git a/integration-tests/docker-compose.yaml b/integration-tests/docker-compose.yaml index 5c0d846d..7f0bc431 100644 --- a/integration-tests/docker-compose.yaml +++ b/integration-tests/docker-compose.yaml @@ -26,15 +26,23 @@ services: # environment: # - MYSQL_ROOT_PASSWORD=Pwd2020Db - mssql: - image: mcr.microsoft.com/mssql/server + clickhouse: + image: bitnami/clickhouse:24.8.4 restart: always ports: - - 15002:1433 + - 15005:8123 environment: - - ACCEPT_EULA=Y - - SA_PASSWORD=Pwd2020Db - - MSSQL_PID=Express + - CLICKHOUSE_ADMIN_PASSWORD=Pwd2020Db + + # mssql: + # image: mcr.microsoft.com/mssql/server + # restart: always + # ports: + # - 15002:1433 + # environment: + # - ACCEPT_EULA=Y + # - SA_PASSWORD=Pwd2020Db + # - MSSQL_PID=Express # cockroachdb: # image: cockroachdb/cockroach diff --git a/integration-tests/engines.js b/integration-tests/engines.js index 19691922..070e1bce 100644 --- a/integration-tests/engines.js +++ b/integration-tests/engines.js @@ -129,6 +129,25 @@ const engines = [ skipOnCI: true, objects: [views, matviews], }, + { + label: 'ClickHouse', + connection: { + engine: 'clickhouse@dbgate-plugin-clickhouse', + databaseUrl: 'http://clickhouse:8123', + password: 'Pwd2020Db', + }, + local: { + databaseUrl: 'http://localhost:15005', + }, + // skipOnCI: true, + objects: [views], + skipDataModifications: true, + skipReferences: true, + skipIndexes: true, + skipNullability: true, + skipUnique: true, + skipAutoIncrement:true + }, ]; const filterLocal = [ @@ -137,8 +156,9 @@ const filterLocal = [ '-MariaDB', '-PostgreSQL', '-SQL Server', - 'SQLite', + '-SQLite', '-CockroachDB', + 'ClickHouse', ]; const enginesPostgre = engines.filter(x => x.label == 'PostgreSQL'); diff --git a/plugins/dbgate-plugin-clickhouse/src/backend/driver.js b/plugins/dbgate-plugin-clickhouse/src/backend/driver.js index dcc83f97..82e4a736 100644 --- a/plugins/dbgate-plugin-clickhouse/src/backend/driver.js +++ b/plugins/dbgate-plugin-clickhouse/src/backend/driver.js @@ -38,6 +38,12 @@ const driver = { }); const dataSet = await resultSet.json(); + if (!dataSet?.[0]) { + return { + rows: [], + columns: [], + }; + } const columns = dataSet[0].map((columnName, i) => ({ columnName, @@ -207,6 +213,10 @@ const driver = { const dataset = await resultSet.json(); return dataset; }, + + async close(client) { + return client.close(); + }, }; module.exports = driver;