mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-24 15:15:59 +00:00
Merge branch 'master' of https://github.com/dbgate/dbgate
This commit is contained in:
41
.github/workflows/e2e-tests.yaml
vendored
41
.github/workflows/e2e-tests.yaml
vendored
@@ -1,41 +0,0 @@
|
|||||||
name: E2E tests
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- develop
|
|
||||||
- 'feature/**'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
e2e-tests:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: node:18
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y xvfb libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 1
|
|
||||||
- name: yarn install
|
|
||||||
run: |
|
|
||||||
yarn install
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
yarn prepare:packer
|
|
||||||
- name: yarn install cypress
|
|
||||||
run: |
|
|
||||||
cd e2e-tests
|
|
||||||
yarn install
|
|
||||||
- name: Run Cypress tests
|
|
||||||
run: |
|
|
||||||
cd e2e-tests
|
|
||||||
yarn test:ci
|
|
||||||
|
|
||||||
services:
|
|
||||||
mysql:
|
|
||||||
image: mysql:8.0.18
|
|
||||||
env:
|
|
||||||
MYSQL_ROOT_PASSWORD: Pwd2020Db
|
|
||||||
24
.github/workflows/run-tests.yaml
vendored
24
.github/workflows/run-tests.yaml
vendored
@@ -12,16 +12,27 @@ jobs:
|
|||||||
container: node:18
|
container: node:18
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Context
|
- name: Install dependencies for cypress
|
||||||
env:
|
run: |
|
||||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
apt-get update
|
||||||
run: echo "$GITHUB_CONTEXT"
|
apt-get install -y xvfb libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
- name: yarn install
|
- name: yarn install
|
||||||
run: |
|
run: |
|
||||||
yarn install
|
yarn install
|
||||||
|
- name: Build packer dist for cypress
|
||||||
|
run: |
|
||||||
|
yarn prepare:packer
|
||||||
|
- name: yarn install cypress
|
||||||
|
run: |
|
||||||
|
cd e2e-tests
|
||||||
|
yarn install
|
||||||
|
- name: Run Cypress tests
|
||||||
|
run: |
|
||||||
|
cd e2e-tests
|
||||||
|
yarn test:ci
|
||||||
- name: Integration tests
|
- name: Integration tests
|
||||||
run: |
|
run: |
|
||||||
cd integration-tests
|
cd integration-tests
|
||||||
@@ -84,5 +95,10 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
CLICKHOUSE_ADMIN_PASSWORD: Pwd2020Db
|
CLICKHOUSE_ADMIN_PASSWORD: Pwd2020Db
|
||||||
|
|
||||||
|
oracle:
|
||||||
|
image: gvenzl/oracle-xe:21-slim
|
||||||
|
env:
|
||||||
|
ORACLE_PASSWORD: Pwd2020Db
|
||||||
|
|
||||||
# cockroachdb:
|
# cockroachdb:
|
||||||
# image: cockroachdb/cockroach
|
# image: cockroachdb/cockroach
|
||||||
|
|||||||
@@ -3,9 +3,15 @@ const _ = require('lodash');
|
|||||||
const fp = require('lodash/fp');
|
const fp = require('lodash/fp');
|
||||||
const { testWrapper } = require('../tools');
|
const { testWrapper } = require('../tools');
|
||||||
const engines = require('../engines');
|
const engines = require('../engines');
|
||||||
const { getAlterDatabaseScript, extendDatabaseInfo, generateDbPairingId } = require('dbgate-tools');
|
const {
|
||||||
|
getAlterDatabaseScript,
|
||||||
|
extendDatabaseInfo,
|
||||||
|
generateDbPairingId,
|
||||||
|
formatQueryWithoutParams,
|
||||||
|
runCommandOnDriver,
|
||||||
|
} = require('dbgate-tools');
|
||||||
|
|
||||||
const initSql = ['CREATE TABLE t1 (id int primary key)', 'CREATE TABLE t2 (id int primary key)'];
|
const initSql = ['CREATE TABLE ~t1 (~id int primary key)', 'CREATE TABLE ~t2 (~id int primary key)'];
|
||||||
|
|
||||||
function flatSource(engineCond = x => !x.skipReferences) {
|
function flatSource(engineCond = x => !x.skipReferences) {
|
||||||
return _.flatten(
|
return _.flatten(
|
||||||
@@ -16,13 +22,14 @@ function flatSource(engineCond = x => !x.skipReferences) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function testDatabaseDiff(conn, driver, mangle, createObject = null) {
|
async function testDatabaseDiff(conn, driver, mangle, createObject = null) {
|
||||||
await driver.query(conn, `create table t1 (id int not null primary key)`);
|
await runCommandOnDriver(conn, driver, `create table ~t1 (~id int not null primary key)`);
|
||||||
|
|
||||||
await driver.query(
|
await runCommandOnDriver(
|
||||||
conn,
|
conn,
|
||||||
`create table t2 (
|
driver,
|
||||||
id int not null primary key,
|
`create table ~t2 (
|
||||||
t1_id int null references t1(id)
|
~id int not null primary key,
|
||||||
|
~t1_id int null references ~t1(~id)
|
||||||
)`
|
)`
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -63,7 +70,7 @@ describe('Alter database', () => {
|
|||||||
db => {
|
db => {
|
||||||
_.remove(db[type], x => x.pureName == 'obj1');
|
_.remove(db[type], x => x.pureName == 'obj1');
|
||||||
},
|
},
|
||||||
object.create1
|
formatQueryWithoutParams(driver, object.create1)
|
||||||
);
|
);
|
||||||
expect(db[type].length).toEqual(0);
|
expect(db[type].length).toEqual(0);
|
||||||
})
|
})
|
||||||
@@ -72,9 +79,9 @@ describe('Alter database', () => {
|
|||||||
test.each(flatSource(x => x.supportRenameSqlObject))(
|
test.each(flatSource(x => x.supportRenameSqlObject))(
|
||||||
'Rename object - %s - %s',
|
'Rename object - %s - %s',
|
||||||
testWrapper(async (conn, driver, type, object, engine) => {
|
testWrapper(async (conn, driver, type, object, engine) => {
|
||||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||||
|
|
||||||
await driver.query(conn, object.create1, { discardResult: true });
|
await runCommandOnDriver(conn, driver, object.create1);
|
||||||
|
|
||||||
const structure = extendDatabaseInfo(await driver.analyseFull(conn));
|
const structure = extendDatabaseInfo(await driver.analyseFull(conn));
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ const fp = require('lodash/fp');
|
|||||||
const { testWrapper } = require('../tools');
|
const { testWrapper } = require('../tools');
|
||||||
const engines = require('../engines');
|
const engines = require('../engines');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const { getAlterTableScript, extendDatabaseInfo, generateDbPairingId } = require('dbgate-tools');
|
const {
|
||||||
|
getAlterTableScript,
|
||||||
|
extendDatabaseInfo,
|
||||||
|
generateDbPairingId,
|
||||||
|
formatQueryWithoutParams,
|
||||||
|
} = require('dbgate-tools');
|
||||||
|
|
||||||
function pickImportantTableInfo(engine, table) {
|
function pickImportantTableInfo(engine, table) {
|
||||||
const props = ['columnName', 'defaultValue'];
|
const props = ['columnName', 'defaultValue'];
|
||||||
@@ -15,7 +20,10 @@ function pickImportantTableInfo(engine, table) {
|
|||||||
columns: table.columns
|
columns: table.columns
|
||||||
.filter(x => x.columnName != 'rowid')
|
.filter(x => x.columnName != 'rowid')
|
||||||
.map(fp.pick(props))
|
.map(fp.pick(props))
|
||||||
.map(props => _.omitBy(props, x => x == null)),
|
.map(props => _.omitBy(props, x => x == null))
|
||||||
|
.map(props =>
|
||||||
|
_.omitBy(props, (v, k) => k == 'defaultValue' && v == 'NULL' && engine.setNullDefaultInsteadOfDrop)
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,27 +33,36 @@ function checkTableStructure(engine, t1, t2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function testTableDiff(engine, 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, formatQueryWithoutParams(driver, `create table ~t0 (~id int not null primary key)`));
|
||||||
|
|
||||||
await driver.query(
|
await driver.query(
|
||||||
conn,
|
conn,
|
||||||
`create table t1 (
|
formatQueryWithoutParams(
|
||||||
col_pk int not null primary key,
|
driver,
|
||||||
col_std int,
|
`create table ~t1 (
|
||||||
col_def int default 12,
|
~col_pk int not null primary key,
|
||||||
${engine.skipReferences ? '' : 'col_fk int references t0(id),'}
|
~col_std int,
|
||||||
col_idx int,
|
~col_def int default 12,
|
||||||
col_uq int ${engine.skipUnique ? '' : 'unique'} ,
|
${engine.skipReferences ? '' : '~col_fk int references ~t0(~id),'}
|
||||||
col_ref int ${engine.skipUnique ? '' : 'unique'}
|
~col_idx int,
|
||||||
|
~col_uq int ${engine.skipUnique ? '' : 'unique'} ,
|
||||||
|
~col_ref int ${engine.skipUnique ? '' : 'unique'}
|
||||||
)`
|
)`
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!engine.skipIndexes) {
|
if (!engine.skipIndexes) {
|
||||||
await driver.query(conn, `create index idx1 on t1(col_idx)`);
|
await driver.query(conn, formatQueryWithoutParams(driver, `create index ~idx1 on ~t1(~col_idx)`));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!engine.skipReferences) {
|
if (!engine.skipReferences) {
|
||||||
await driver.query(conn, `create table t2 (id int not null primary key, fkval int null references t1(col_ref))`);
|
await driver.query(
|
||||||
|
conn,
|
||||||
|
formatQueryWithoutParams(
|
||||||
|
driver,
|
||||||
|
`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 tget = x => x.tables.find(y => y.pureName == 't1');
|
||||||
@@ -175,5 +192,4 @@ describe('Alter table', () => {
|
|||||||
// });
|
// });
|
||||||
// })
|
// })
|
||||||
// );
|
// );
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ const engines = require('../engines');
|
|||||||
const stream = require('stream');
|
const stream = require('stream');
|
||||||
const { testWrapper } = require('../tools');
|
const { testWrapper } = require('../tools');
|
||||||
const dataDuplicator = require('dbgate-api/src/shell/dataDuplicator');
|
const dataDuplicator = require('dbgate-api/src/shell/dataDuplicator');
|
||||||
const { runCommandOnDriver } = require('dbgate-tools');
|
const { runCommandOnDriver, runQueryOnDriver } = require('dbgate-tools');
|
||||||
|
|
||||||
describe('Data duplicator', () => {
|
describe('Data duplicator', () => {
|
||||||
test.each(engines.filter(x => !x.skipDataDuplicator).map(engine => [engine.label, engine]))(
|
test.each(engines.filter(x => !x.skipDataDuplicator).map(engine => [engine.label, engine]))(
|
||||||
@@ -84,10 +84,10 @@ describe('Data duplicator', () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const res1 = await driver.query(conn, `select count(*) as cnt from t1`);
|
const res1 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t1`));
|
||||||
expect(res1.rows[0].cnt.toString()).toEqual('6');
|
expect(res1.rows[0].cnt.toString()).toEqual('6');
|
||||||
|
|
||||||
const res2 = await driver.query(conn, `select count(*) as cnt from t2`);
|
const res2 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t2`));
|
||||||
expect(res2.rows[0].cnt.toString()).toEqual('6');
|
expect(res2.rows[0].cnt.toString()).toEqual('6');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -145,13 +145,15 @@ describe('Data duplicator', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const res1 = await driver.query(conn, `select count(*) as cnt from t1`);
|
const res1 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t1`));
|
||||||
expect(res1.rows[0].cnt.toString()).toEqual('1');
|
expect(res1.rows[0].cnt.toString()).toEqual('1');
|
||||||
|
|
||||||
const res2 = await driver.query(conn, `select count(*) as cnt from t2`);
|
const res2 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t2`));
|
||||||
expect(res2.rows[0].cnt.toString()).toEqual('2');
|
expect(res2.rows[0].cnt.toString()).toEqual('2');
|
||||||
|
|
||||||
const res3 = await driver.query(conn, `select count(*) as cnt from t2 where valfk is not null`);
|
const res3 = await runQueryOnDriver(conn, driver, dmp =>
|
||||||
|
dmp.put(`select count(*) as ~cnt from ~t2 where ~valfk is not null`)
|
||||||
|
);
|
||||||
expect(res3.rows[0].cnt.toString()).toEqual('1');
|
expect(res3.rows[0].cnt.toString()).toEqual('1');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const tableWriter = require('dbgate-api/src/shell/tableWriter');
|
|||||||
const copyStream = require('dbgate-api/src/shell/copyStream');
|
const copyStream = require('dbgate-api/src/shell/copyStream');
|
||||||
const importDatabase = require('dbgate-api/src/shell/importDatabase');
|
const importDatabase = require('dbgate-api/src/shell/importDatabase');
|
||||||
const fakeObjectReader = require('dbgate-api/src/shell/fakeObjectReader');
|
const fakeObjectReader = require('dbgate-api/src/shell/fakeObjectReader');
|
||||||
|
const { runQueryOnDriver } = require('dbgate-tools');
|
||||||
|
|
||||||
function createImportStream() {
|
function createImportStream() {
|
||||||
const pass = new stream.PassThrough({
|
const pass = new stream.PassThrough({
|
||||||
@@ -37,7 +38,7 @@ describe('DB Import', () => {
|
|||||||
});
|
});
|
||||||
await copyStream(reader, writer);
|
await copyStream(reader, writer);
|
||||||
|
|
||||||
const res = await driver.query(conn, `select count(*) as cnt from t1`);
|
const res = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t1`));
|
||||||
expect(res.rows[0].cnt.toString()).toEqual('6');
|
expect(res.rows[0].cnt.toString()).toEqual('6');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -65,10 +66,10 @@ describe('DB Import', () => {
|
|||||||
});
|
});
|
||||||
await copyStream(reader2, writer2);
|
await copyStream(reader2, writer2);
|
||||||
|
|
||||||
const res1 = await driver.query(conn, `select count(*) as cnt from t1`);
|
const res1 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t1`));
|
||||||
expect(res1.rows[0].cnt.toString()).toEqual('6');
|
expect(res1.rows[0].cnt.toString()).toEqual('6');
|
||||||
|
|
||||||
const res2 = await driver.query(conn, `select count(*) as cnt from t2`);
|
const res2 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t2`));
|
||||||
expect(res2.rows[0].cnt.toString()).toEqual('6');
|
expect(res2.rows[0].cnt.toString()).toEqual('6');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const { testWrapper, testWrapperPrepareOnly } = require('../tools');
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const engines = require('../engines');
|
const engines = require('../engines');
|
||||||
const deployDb = require('dbgate-api/src/shell/deployDb');
|
const deployDb = require('dbgate-api/src/shell/deployDb');
|
||||||
const { databaseInfoFromYamlModel } = require('dbgate-tools');
|
const { databaseInfoFromYamlModel, runQueryOnDriver, formatQueryWithoutParams } = require('dbgate-tools');
|
||||||
const generateDeploySql = require('dbgate-api/src/shell/generateDeploySql');
|
const generateDeploySql = require('dbgate-api/src/shell/generateDeploySql');
|
||||||
const connectUtility = require('dbgate-api/src/utility/connectUtility');
|
const connectUtility = require('dbgate-api/src/utility/connectUtility');
|
||||||
|
|
||||||
@@ -69,6 +69,29 @@ function checkStructure(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// function convertObjectText(text, driver) {
|
||||||
|
// if (!text) return undefined;
|
||||||
|
// text = formatQueryWithoutParams(driver, text);
|
||||||
|
// if (driver.dialect.requireFromDual && text.startsWith('create view ') && !text.includes('from')) {
|
||||||
|
// text = text + ' from dual';
|
||||||
|
// }
|
||||||
|
// return text;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function convertModelToEngine(model, driver) {
|
||||||
|
// return model.map(x => ({
|
||||||
|
// ...x,
|
||||||
|
// text: convertObjectText(x.text, driver),
|
||||||
|
// }));
|
||||||
|
// }
|
||||||
|
|
||||||
|
function convertModelToEngine(model, driver) {
|
||||||
|
return model.map(x => ({
|
||||||
|
...x,
|
||||||
|
text: x.text ? formatQueryWithoutParams(driver, x.text) : undefined,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
async function testDatabaseDeploy(engine, conn, driver, dbModelsYaml, options) {
|
async function testDatabaseDeploy(engine, conn, driver, dbModelsYaml, options) {
|
||||||
const { testEmptyLastScript, finalCheckAgainstModel, markDeleted, allowDropStatements } = options || {};
|
const { testEmptyLastScript, finalCheckAgainstModel, markDeleted, allowDropStatements } = options || {};
|
||||||
let index = 0;
|
let index = 0;
|
||||||
@@ -83,13 +106,13 @@ async function testDatabaseDeploy(engine, conn, driver, dbModelsYaml, options) {
|
|||||||
|
|
||||||
for (const loadedDbModel of dbModelsYaml) {
|
for (const loadedDbModel of dbModelsYaml) {
|
||||||
if (_.isString(loadedDbModel)) {
|
if (_.isString(loadedDbModel)) {
|
||||||
await driver.script(conn, loadedDbModel);
|
await driver.script(conn, formatQueryWithoutParams(driver, loadedDbModel));
|
||||||
} else {
|
} else {
|
||||||
const { sql, isEmpty } = await generateDeploySql({
|
const { sql, isEmpty } = await generateDeploySql({
|
||||||
systemConnection: conn.isPreparedOnly ? undefined : conn,
|
systemConnection: conn.isPreparedOnly ? undefined : conn,
|
||||||
connection: conn.isPreparedOnly ? conn : undefined,
|
connection: conn.isPreparedOnly ? conn : undefined,
|
||||||
driver,
|
driver,
|
||||||
loadedDbModel,
|
loadedDbModel: convertModelToEngine(loadedDbModel, driver),
|
||||||
dbdiffOptionsExtra,
|
dbdiffOptionsExtra,
|
||||||
});
|
});
|
||||||
console.debug('Generated deploy script:', sql);
|
console.debug('Generated deploy script:', sql);
|
||||||
@@ -106,7 +129,7 @@ async function testDatabaseDeploy(engine, conn, driver, dbModelsYaml, options) {
|
|||||||
systemConnection: conn.isPreparedOnly ? undefined : conn,
|
systemConnection: conn.isPreparedOnly ? undefined : conn,
|
||||||
connection: conn.isPreparedOnly ? conn : undefined,
|
connection: conn.isPreparedOnly ? conn : undefined,
|
||||||
driver,
|
driver,
|
||||||
loadedDbModel,
|
loadedDbModel: convertModelToEngine(loadedDbModel, driver),
|
||||||
dbdiffOptionsExtra,
|
dbdiffOptionsExtra,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -117,7 +140,12 @@ async function testDatabaseDeploy(engine, conn, driver, dbModelsYaml, options) {
|
|||||||
const dbhan = conn.isPreparedOnly ? await connectUtility(driver, conn, 'read') : conn;
|
const dbhan = conn.isPreparedOnly ? await connectUtility(driver, conn, 'read') : conn;
|
||||||
const structure = await driver.analyseFull(dbhan);
|
const structure = await driver.analyseFull(dbhan);
|
||||||
if (conn.isPreparedOnly) await driver.close(dbhan);
|
if (conn.isPreparedOnly) await driver.close(dbhan);
|
||||||
checkStructure(engine, structure, finalCheckAgainstModel ?? _.findLast(dbModelsYaml, x => _.isArray(x)), options);
|
checkStructure(
|
||||||
|
engine,
|
||||||
|
structure,
|
||||||
|
convertModelToEngine(finalCheckAgainstModel ?? _.findLast(dbModelsYaml, x => _.isArray(x)), driver),
|
||||||
|
options
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Deploy database', () => {
|
describe('Deploy database', () => {
|
||||||
@@ -339,7 +367,7 @@ describe('Deploy database', () => {
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const res = await driver.query(conn, `select count(*) as cnt from t1`);
|
const res = await runQueryOnDriver(conn, driver, `select count(*) as ~cnt from ~t1`);
|
||||||
expect(res.rows[0].cnt.toString()).toEqual('3');
|
expect(res.rows[0].cnt.toString()).toEqual('3');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -386,7 +414,7 @@ describe('Deploy database', () => {
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const res = await driver.query(conn, `select val from t1 where id = 2`);
|
const res = await runQueryOnDriver(conn, driver, `select ~val from ~t1 where ~id = 2`);
|
||||||
expect(res.rows[0].val.toString()).toEqual('5');
|
expect(res.rows[0].val.toString()).toEqual('5');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -414,8 +442,8 @@ describe('Deploy database', () => {
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await driver.query(conn, `insert into t1 (id) values (1)`);
|
await runQueryOnDriver(conn, driver, `insert into ~t1 (~id) values (1)`);
|
||||||
const res = await driver.query(conn, ` select val from t1 where id = 1`);
|
const res = await runQueryOnDriver(conn, driver, ` select ~val from ~t1 where ~id = 1`);
|
||||||
expect(res.rows[0].val.toString().substring(0, 2)).toEqual('20');
|
expect(res.rows[0].val.toString().substring(0, 2)).toEqual('20');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -438,7 +466,7 @@ describe('Deploy database', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'insert into t1 (id, val) values (1, 1); insert into t1 (id) values (2)',
|
'insert into ~t1 (~id, ~val) values (1, 1); insert into ~t1 (~id) values (2)',
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: 't1.table.yaml',
|
name: 't1.table.yaml',
|
||||||
@@ -452,16 +480,16 @@ describe('Deploy database', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'insert into t1 (id) values (3);',
|
'insert into ~t1 (~id) values (3);',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const res1 = await driver.query(conn, `select val from t1 where id = 1`);
|
const res1 = await runQueryOnDriver(conn, driver, `select ~val from ~t1 where ~id = 1`);
|
||||||
expect(res1.rows[0].val).toEqual(1);
|
expect(res1.rows[0].val).toEqual(1);
|
||||||
|
|
||||||
const res2 = await driver.query(conn, `select val from t1 where id = 2`);
|
const res2 = await runQueryOnDriver(conn, driver, `select ~val from ~t1 where ~id = 2`);
|
||||||
expect(res2.rows[0].val).toEqual(20);
|
expect(res2.rows[0].val).toEqual(20);
|
||||||
|
|
||||||
const res3 = await driver.query(conn, `select val from t1 where id = 3`);
|
const res3 = await runQueryOnDriver(conn, driver, `select ~val from ~t1 where ~id = 3`);
|
||||||
expect(res2.rows[0].val).toEqual(20);
|
expect(res2.rows[0].val).toEqual(20);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -525,17 +553,17 @@ describe('Deploy database', () => {
|
|||||||
|
|
||||||
const V1 = {
|
const V1 = {
|
||||||
name: 'v1.view.sql',
|
name: 'v1.view.sql',
|
||||||
text: 'create view v1 as select * from t1',
|
text: 'create view ~v1 as select * from ~t1',
|
||||||
};
|
};
|
||||||
|
|
||||||
const V1_VARIANT2 = {
|
const V1_VARIANT2 = {
|
||||||
name: 'v1.view.sql',
|
name: 'v1.view.sql',
|
||||||
text: 'create view v1 as select 1 as c1',
|
text: 'create view ~v1 as select ~id + ~id ~idsum from ~t1',
|
||||||
};
|
};
|
||||||
|
|
||||||
const V1_DELETED = {
|
const V1_DELETED = {
|
||||||
name: '_deleted_v1.view.sql',
|
name: '_deleted_v1.view.sql',
|
||||||
text: 'create view _deleted_v1 as select * from t1',
|
text: 'create view ~_deleted_v1 as select * from ~t1',
|
||||||
};
|
};
|
||||||
|
|
||||||
test.each(engines.map(engine => [engine.label, engine]))(
|
test.each(engines.map(engine => [engine.label, engine]))(
|
||||||
@@ -682,15 +710,15 @@ describe('Deploy database', () => {
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: '1.predeploy.sql',
|
name: '1.predeploy.sql',
|
||||||
text: 'create table t1 (id int primary key); insert into t1 (id) values (1);',
|
text: 'create table ~t1 (~id int primary key); insert into ~t1 (~id) values (1);',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const res1 = await driver.query(conn, 'SELECT COUNT(*) AS cnt FROM t1');
|
const res1 = await runQueryOnDriver(conn, driver, 'SELECT COUNT(*) AS ~cnt FROM ~t1');
|
||||||
expect(res1.rows[0].cnt == 1).toBeTruthy();
|
expect(res1.rows[0].cnt == 1).toBeTruthy();
|
||||||
|
|
||||||
const res2 = await driver.query(conn, 'SELECT COUNT(*) AS cnt FROM dbgate_deploy_journal');
|
const res2 = await runQueryOnDriver(conn, driver, 'SELECT COUNT(*) AS ~cnt FROM ~dbgate_deploy_journal');
|
||||||
expect(res2.rows[0].cnt == 1).toBeTruthy();
|
expect(res2.rows[0].cnt == 1).toBeTruthy();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -702,48 +730,53 @@ describe('Deploy database', () => {
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: 't1.uninstall.sql',
|
name: 't1.uninstall.sql',
|
||||||
text: 'drop table t1',
|
text: 'drop table ~t1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 't1.install.sql',
|
name: 't1.install.sql',
|
||||||
text: 'create table t1 (id int primary key); insert into t1 (id) values (1)',
|
text: 'create table ~t1 (~id int primary key); insert into ~t1 (~id) values (1)',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 't2.once.sql',
|
name: 't2.once.sql',
|
||||||
text: 'create table t2 (id int primary key); insert into t2 (id) values (1)',
|
text: 'create table ~t2 (~id int primary key); insert into ~t2 (~id) values (1)',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: 't1.uninstall.sql',
|
name: 't1.uninstall.sql',
|
||||||
text: 'drop table t1',
|
text: 'drop table ~t1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 't1.install.sql',
|
name: 't1.install.sql',
|
||||||
text: 'create table t1 (id int primary key, val int); insert into t1 (id, val) values (1, 11)',
|
text: 'create table ~t1 (~id int primary key, ~val int); insert into ~t1 (~id, ~val) values (1, 11)',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 't2.once.sql',
|
name: 't2.once.sql',
|
||||||
text: 'insert into t2 (id) values (2)',
|
text: 'insert into ~t2 (~id) values (2)',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const res1 = await driver.query(conn, 'SELECT val from t1 where id = 1');
|
const res1 = await runQueryOnDriver(conn, driver, 'SELECT ~val from ~t1 where ~id = 1');
|
||||||
expect(res1.rows[0].val == 11).toBeTruthy();
|
expect(res1.rows[0].val == 11).toBeTruthy();
|
||||||
|
|
||||||
const res2 = await driver.query(conn, 'SELECT COUNT(*) AS cnt FROM t2');
|
const res2 = await runQueryOnDriver(conn, driver, 'SELECT COUNT(*) AS ~cnt FROM ~t2');
|
||||||
expect(res2.rows[0].cnt == 1).toBeTruthy();
|
expect(res2.rows[0].cnt == 1).toBeTruthy();
|
||||||
|
|
||||||
const res3 = await driver.query(conn, 'SELECT COUNT(*) AS cnt FROM dbgate_deploy_journal');
|
const res3 = await runQueryOnDriver(conn, driver, 'SELECT COUNT(*) AS ~cnt FROM ~dbgate_deploy_journal');
|
||||||
expect(res3.rows[0].cnt == 3).toBeTruthy();
|
expect(res3.rows[0].cnt == 3).toBeTruthy();
|
||||||
|
|
||||||
const res4 = await driver.query(conn, "SELECT run_count from dbgate_deploy_journal where name = 't2.once.sql'");
|
const res4 = await runQueryOnDriver(
|
||||||
|
conn,
|
||||||
|
driver,
|
||||||
|
"SELECT ~run_count from ~dbgate_deploy_journal where ~name = 't2.once.sql'"
|
||||||
|
);
|
||||||
expect(res4.rows[0].run_count == 1).toBeTruthy();
|
expect(res4.rows[0].run_count == 1).toBeTruthy();
|
||||||
|
|
||||||
const res5 = await driver.query(
|
const res5 = await runQueryOnDriver(
|
||||||
conn,
|
conn,
|
||||||
"SELECT run_count from dbgate_deploy_journal where name = 't1.install.sql'"
|
driver,
|
||||||
|
"SELECT ~run_count from ~dbgate_deploy_journal where ~name = 't1.install.sql'"
|
||||||
);
|
);
|
||||||
expect(res5.rows[0].run_count == 2).toBeTruthy();
|
expect(res5.rows[0].run_count == 2).toBeTruthy();
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
const { testWrapper } = require('../tools');
|
const { testWrapper } = require('../tools');
|
||||||
const engines = require('../engines');
|
const engines = require('../engines');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
const { formatQueryWithoutParams, runCommandOnDriver } = require('dbgate-tools');
|
||||||
|
|
||||||
const initSql = ['CREATE TABLE t1 (id int primary key)', 'CREATE TABLE t2 (id int primary key)'];
|
const initSql = ['CREATE TABLE ~t1 (~id int primary key)', 'CREATE TABLE ~t2 (~id int primary key)'];
|
||||||
|
|
||||||
function flatSource() {
|
function flatSource() {
|
||||||
return _.flatten(
|
return _.flatten(
|
||||||
@@ -34,9 +35,9 @@ describe('Object analyse', () => {
|
|||||||
test.each(flatSource())(
|
test.each(flatSource())(
|
||||||
'Full analysis - %s - %s',
|
'Full analysis - %s - %s',
|
||||||
testWrapper(async (conn, driver, type, object, engine) => {
|
testWrapper(async (conn, driver, type, object, engine) => {
|
||||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||||
|
|
||||||
await driver.query(conn, object.create1, { discardResult: true });
|
await runCommandOnDriver(conn, driver, object.create1);
|
||||||
const structure = await driver.analyseFull(conn);
|
const structure = await driver.analyseFull(conn);
|
||||||
|
|
||||||
expect(structure[type].length).toEqual(1);
|
expect(structure[type].length).toEqual(1);
|
||||||
@@ -47,11 +48,11 @@ describe('Object analyse', () => {
|
|||||||
test.each(flatSource())(
|
test.each(flatSource())(
|
||||||
'Incremental analysis - add - %s - %s',
|
'Incremental analysis - add - %s - %s',
|
||||||
testWrapper(async (conn, driver, type, object, engine) => {
|
testWrapper(async (conn, driver, type, object, engine) => {
|
||||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||||
|
|
||||||
await driver.query(conn, object.create2, { discardResult: true });
|
await runCommandOnDriver(conn, driver, object.create2);
|
||||||
const structure1 = await driver.analyseFull(conn);
|
const structure1 = await driver.analyseFull(conn);
|
||||||
await driver.query(conn, object.create1, { discardResult: true });
|
await runCommandOnDriver(conn, driver, object.create1);
|
||||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||||
|
|
||||||
expect(structure2[type].length).toEqual(2);
|
expect(structure2[type].length).toEqual(2);
|
||||||
@@ -62,12 +63,12 @@ describe('Object analyse', () => {
|
|||||||
test.each(flatSource())(
|
test.each(flatSource())(
|
||||||
'Incremental analysis - drop - %s - %s',
|
'Incremental analysis - drop - %s - %s',
|
||||||
testWrapper(async (conn, driver, type, object, engine) => {
|
testWrapper(async (conn, driver, type, object, engine) => {
|
||||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||||
|
|
||||||
await driver.query(conn, object.create1, { discardResult: true });
|
await runCommandOnDriver(conn, driver, object.create1);
|
||||||
await driver.query(conn, object.create2, { discardResult: true });
|
await runCommandOnDriver(conn, driver, object.create2);
|
||||||
const structure1 = await driver.analyseFull(conn);
|
const structure1 = await driver.analyseFull(conn);
|
||||||
await driver.query(conn, object.drop2, { discardResult: true });
|
await runCommandOnDriver(conn, driver, object.drop2);
|
||||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||||
|
|
||||||
expect(structure2[type].length).toEqual(1);
|
expect(structure2[type].length).toEqual(1);
|
||||||
@@ -78,11 +79,11 @@ describe('Object analyse', () => {
|
|||||||
test.each(flatSource())(
|
test.each(flatSource())(
|
||||||
'Create SQL - add - %s - %s',
|
'Create SQL - add - %s - %s',
|
||||||
testWrapper(async (conn, driver, type, object, engine) => {
|
testWrapper(async (conn, driver, type, object, engine) => {
|
||||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||||
|
|
||||||
await driver.query(conn, object.create1, { discardResult: true });
|
await runCommandOnDriver(conn, driver, object.create1);
|
||||||
const structure1 = await driver.analyseFull(conn);
|
const structure1 = await driver.analyseFull(conn);
|
||||||
await driver.query(conn, object.drop1, { discardResult: true });
|
await runCommandOnDriver(conn, driver, object.drop1);
|
||||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||||
expect(structure2[type].length).toEqual(0);
|
expect(structure2[type].length).toEqual(0);
|
||||||
|
|
||||||
@@ -98,10 +99,10 @@ describe('Object analyse', () => {
|
|||||||
test.each(flatSourceParameters())(
|
test.each(flatSourceParameters())(
|
||||||
'Test parameters simple analyse - %s - %s',
|
'Test parameters simple analyse - %s - %s',
|
||||||
testWrapper(async (conn, driver, testName, parameter, engine) => {
|
testWrapper(async (conn, driver, testName, parameter, engine) => {
|
||||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||||
for (const sql of engine.parametersOtherSql) await driver.query(conn, sql, { discardResult: true });
|
for (const sql of engine.parametersOtherSql) await runCommandOnDriver(conn, driver, sql);
|
||||||
|
|
||||||
await driver.query(conn, parameter.create, { discardResult: true });
|
await runCommandOnDriver(conn, driver, parameter.create);
|
||||||
const structure = await driver.analyseFull(conn);
|
const structure = await driver.analyseFull(conn);
|
||||||
|
|
||||||
const parameters = structure[parameter.objectTypeField].find(x => x.pureName == 'obj1').parameters;
|
const parameters = structure[parameter.objectTypeField].find(x => x.pureName == 'obj1').parameters;
|
||||||
@@ -116,15 +117,15 @@ describe('Object analyse', () => {
|
|||||||
test.each(flatSourceParameters())(
|
test.each(flatSourceParameters())(
|
||||||
'Test parameters create SQL - %s - %s',
|
'Test parameters create SQL - %s - %s',
|
||||||
testWrapper(async (conn, driver, testName, parameter, engine) => {
|
testWrapper(async (conn, driver, testName, parameter, engine) => {
|
||||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||||
for (const sql of engine.parametersOtherSql) await driver.query(conn, sql, { discardResult: true });
|
for (const sql of engine.parametersOtherSql) await runCommandOnDriver(conn, driver, sql);
|
||||||
|
|
||||||
await driver.query(conn, parameter.create, { discardResult: true });
|
await runCommandOnDriver(conn, driver, parameter.create);
|
||||||
const structure1 = await driver.analyseFull(conn);
|
const structure1 = await driver.analyseFull(conn);
|
||||||
await driver.query(conn, parameter.drop, { discardResult: true });
|
await runCommandOnDriver(conn, driver, parameter.drop);
|
||||||
|
|
||||||
const obj = structure1[parameter.objectTypeField].find(x => x.pureName == 'obj1');
|
const obj = structure1[parameter.objectTypeField].find(x => x.pureName == 'obj1');
|
||||||
await driver.script(conn, obj.createSql);
|
await driver.script(conn, obj.createSql, { discardResult: true });
|
||||||
|
|
||||||
const structure2 = await driver.analyseFull(conn);
|
const structure2 = await driver.analyseFull(conn);
|
||||||
const parameters = structure2[parameter.objectTypeField].find(x => x.pureName == 'obj1').parameters;
|
const parameters = structure2[parameter.objectTypeField].find(x => x.pureName == 'obj1').parameters;
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
const engines = require('../engines');
|
const engines = require('../engines');
|
||||||
const { splitQuery } = require('dbgate-query-splitter');
|
const { splitQuery } = require('dbgate-query-splitter');
|
||||||
const { testWrapper } = require('../tools');
|
const { testWrapper } = require('../tools');
|
||||||
|
const { runQueryOnDriver, runCommandOnDriver, formatQueryWithoutParams } = require('dbgate-tools');
|
||||||
|
|
||||||
const initSql = [
|
const initSql = [
|
||||||
'CREATE TABLE t1 (id int primary key)',
|
'CREATE TABLE ~t1 (~id int primary key)',
|
||||||
'INSERT INTO t1 (id) VALUES (1)',
|
'INSERT INTO ~t1 (~id) VALUES (1)',
|
||||||
'INSERT INTO t1 (id) VALUES (2)',
|
'INSERT INTO ~t1 (~id) VALUES (2)',
|
||||||
];
|
];
|
||||||
|
|
||||||
expect.extend({
|
expect.extend({
|
||||||
@@ -51,7 +52,7 @@ class StreamHandler {
|
|||||||
function executeStreamItem(driver, conn, sql) {
|
function executeStreamItem(driver, conn, sql) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const handler = new StreamHandler(resolve);
|
const handler = new StreamHandler(resolve);
|
||||||
driver.stream(conn, sql, handler);
|
driver.stream(conn, formatQueryWithoutParams(driver, sql), handler);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,9 +69,11 @@ describe('Query', () => {
|
|||||||
test.each(engines.map(engine => [engine.label, engine]))(
|
test.each(engines.map(engine => [engine.label, engine]))(
|
||||||
'Simple query - %s',
|
'Simple query - %s',
|
||||||
testWrapper(async (conn, driver, engine) => {
|
testWrapper(async (conn, driver, engine) => {
|
||||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
for (const sql of initSql) {
|
||||||
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(sql));
|
||||||
|
}
|
||||||
|
|
||||||
const res = await driver.query(conn, 'SELECT id FROM t1 ORDER BY id');
|
const res = await runQueryOnDriver(conn, driver, dmp => dmp.put('SELECT ~id FROM ~t1 ORDER BY ~id'));
|
||||||
expect(res.columns).toEqual([
|
expect(res.columns).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
columnName: 'id',
|
columnName: 'id',
|
||||||
@@ -91,8 +94,11 @@ describe('Query', () => {
|
|||||||
test.each(engines.map(engine => [engine.label, engine]))(
|
test.each(engines.map(engine => [engine.label, engine]))(
|
||||||
'Simple stream query - %s',
|
'Simple stream query - %s',
|
||||||
testWrapper(async (conn, driver, engine) => {
|
testWrapper(async (conn, driver, engine) => {
|
||||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
for (const sql of initSql) {
|
||||||
const results = await executeStream(driver, conn, 'SELECT id FROM t1 ORDER BY id');
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(sql));
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await executeStream(driver, conn, 'SELECT ~id FROM ~t1 ORDER BY ~id');
|
||||||
expect(results.length).toEqual(1);
|
expect(results.length).toEqual(1);
|
||||||
const res = results[0];
|
const res = results[0];
|
||||||
|
|
||||||
@@ -104,11 +110,14 @@ describe('Query', () => {
|
|||||||
test.each(engines.map(engine => [engine.label, engine]))(
|
test.each(engines.map(engine => [engine.label, engine]))(
|
||||||
'More queries - %s',
|
'More queries - %s',
|
||||||
testWrapper(async (conn, driver, engine) => {
|
testWrapper(async (conn, driver, engine) => {
|
||||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
for (const sql of initSql) {
|
||||||
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(sql));
|
||||||
|
}
|
||||||
|
|
||||||
const results = await executeStream(
|
const results = await executeStream(
|
||||||
driver,
|
driver,
|
||||||
conn,
|
conn,
|
||||||
'SELECT id FROM t1 ORDER BY id; SELECT id FROM t1 ORDER BY id DESC'
|
'SELECT ~id FROM ~t1 ORDER BY ~id; SELECT ~id FROM ~t1 ORDER BY ~id DESC'
|
||||||
);
|
);
|
||||||
expect(results.length).toEqual(2);
|
expect(results.length).toEqual(2);
|
||||||
|
|
||||||
@@ -128,7 +137,7 @@ describe('Query', () => {
|
|||||||
const results = await executeStream(
|
const results = await executeStream(
|
||||||
driver,
|
driver,
|
||||||
conn,
|
conn,
|
||||||
'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; '
|
'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);
|
expect(results.length).toEqual(1);
|
||||||
|
|
||||||
@@ -144,7 +153,7 @@ describe('Query', () => {
|
|||||||
const results = await executeStream(
|
const results = await executeStream(
|
||||||
driver,
|
driver,
|
||||||
conn,
|
conn,
|
||||||
'CREATE TABLE t1 (id int); INSERT INTO t1 (id) VALUES (1); INSERT INTO t1 (id) VALUES (2) '
|
'CREATE TABLE ~t1 (~id int); INSERT INTO ~t1 (~id) VALUES (1); INSERT INTO ~t1 (~id) VALUES (2) '
|
||||||
);
|
);
|
||||||
expect(results.length).toEqual(0);
|
expect(results.length).toEqual(0);
|
||||||
})
|
})
|
||||||
@@ -153,16 +162,57 @@ describe('Query', () => {
|
|||||||
test.each(engines.filter(x => !x.skipDataModifications).map(engine => [engine.label, engine]))(
|
test.each(engines.filter(x => !x.skipDataModifications).map(engine => [engine.label, engine]))(
|
||||||
'Save data query - %s',
|
'Save data query - %s',
|
||||||
testWrapper(async (conn, driver, engine) => {
|
testWrapper(async (conn, driver, engine) => {
|
||||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
for (const sql of initSql) {
|
||||||
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(sql));
|
||||||
|
}
|
||||||
|
|
||||||
await driver.script(
|
await driver.script(
|
||||||
conn,
|
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;',
|
formatQueryWithoutParams(
|
||||||
|
driver,
|
||||||
|
'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 }
|
{ discardResult: true }
|
||||||
);
|
);
|
||||||
const res = await driver.query(conn, 'SELECT COUNT(*) AS cnt FROM t1');
|
const res = await runQueryOnDriver(conn, driver, dmp => dmp.put('SELECT COUNT(*) AS ~cnt FROM ~t1'));
|
||||||
// console.log(res);
|
// console.log(res);
|
||||||
expect(res.rows[0].cnt == 3).toBeTruthy();
|
expect(res.rows[0].cnt == 3).toBeTruthy();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test.each(engines.filter(x => !x.skipDataDuplicator).map(engine => [engine.label, engine]))(
|
||||||
|
'Select scope identity - %s',
|
||||||
|
testWrapper(async (conn, driver, engine) => {
|
||||||
|
await runCommandOnDriver(conn, driver, dmp =>
|
||||||
|
dmp.createTable({
|
||||||
|
pureName: 't1',
|
||||||
|
columns: [
|
||||||
|
{ columnName: 'id', dataType: 'int', notNull: true, autoIncrement: true },
|
||||||
|
{ columnName: 'val', dataType: 'varchar(50)' },
|
||||||
|
],
|
||||||
|
primaryKey: {
|
||||||
|
columns: [{ columnName: 'id' }],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const structure = await driver.analyseFull(conn);
|
||||||
|
const table = structure.tables.find(x => x.pureName == 't1');
|
||||||
|
|
||||||
|
let res;
|
||||||
|
if (driver.dialect.requireStandaloneSelectForScopeIdentity) {
|
||||||
|
await runCommandOnDriver(conn, driver, dmp => dmp.put("INSERT INTO ~t1 (~val) VALUES ('aaa')"));
|
||||||
|
res = await runQueryOnDriver(conn, driver, dmp => dmp.selectScopeIdentity(table));
|
||||||
|
} else {
|
||||||
|
res = await runQueryOnDriver(conn, driver, dmp => {
|
||||||
|
dmp.putCmd("INSERT INTO ~t1 (~val) VALUES ('aaa')");
|
||||||
|
dmp.selectScopeIdentity(table);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const row = res.rows[0];
|
||||||
|
const keys = Object.keys(row);
|
||||||
|
expect(keys.length).toEqual(1);
|
||||||
|
expect(row[keys[0]] == 1).toBeTruthy();
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ describe('Schema tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Base analyser test', () => {
|
describe('Base analyser test', () => {
|
||||||
test.each(engines.map(engine => [engine.label, engine]))(
|
test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))(
|
||||||
'Structure without change - %s',
|
'Structure without change - %s',
|
||||||
testWrapper(async (conn, driver, engine) => {
|
testWrapper(async (conn, driver, engine) => {
|
||||||
await baseStructure(conn, driver);
|
await baseStructure(conn, driver);
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ const engines = require('../engines');
|
|||||||
const { testWrapper } = require('../tools');
|
const { testWrapper } = require('../tools');
|
||||||
|
|
||||||
const t1Sql = 'CREATE TABLE ~t1 (~id int not null primary key, ~val1 varchar(50))';
|
const t1Sql = 'CREATE TABLE ~t1 (~id int not null primary key, ~val1 varchar(50))';
|
||||||
const ix1Sql = 'CREATE index ix1 ON t1(val1, id)';
|
const ix1Sql = 'CREATE index ~ix1 ON ~t1(~val1, ~id)';
|
||||||
const t2Sql = engine =>
|
const t2Sql = engine =>
|
||||||
`CREATE TABLE t2 (id int not null primary key, val2 varchar(50) ${engine.skipUnique ? '' : 'unique'})`;
|
`CREATE TABLE ~t2 (~id int not null primary key, ~val2 varchar(50) ${engine.skipUnique ? '' : 'unique'})`;
|
||||||
const t3Sql = 'CREATE TABLE t3 (id int not null primary key, valfk int, foreign key (valfk) references t2(id))';
|
const t3Sql = 'CREATE TABLE ~t3 (~id int not null primary key, ~valfk int, foreign key (~valfk) references ~t2(~id))';
|
||||||
const t4Sql = 'CREATE TABLE t4 (id int not null primary key, valdef int not null default 12)';
|
const t4Sql = 'CREATE TABLE ~t4 (~id int not null primary key, ~valdef int default 12 not null)';
|
||||||
// const fkSql = 'ALTER TABLE t3 ADD FOREIGN KEY (valfk) REFERENCES t2(id)'
|
// const fkSql = 'ALTER TABLE t3 ADD FOREIGN KEY (valfk) REFERENCES t2(id)'
|
||||||
|
|
||||||
const txMatch = (engine, tname, vcolname, nextcol, defaultValue) =>
|
const txMatch = (engine, tname, vcolname, nextcol, defaultValue) =>
|
||||||
@@ -73,7 +73,7 @@ describe('Table analyse', () => {
|
|||||||
test.each(engines.map(engine => [engine.label, engine]))(
|
test.each(engines.map(engine => [engine.label, engine]))(
|
||||||
'Table add - incremental analysis - %s',
|
'Table add - incremental analysis - %s',
|
||||||
testWrapper(async (conn, driver, engine) => {
|
testWrapper(async (conn, driver, engine) => {
|
||||||
await driver.query(conn, t2Sql(engine));
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
|
||||||
|
|
||||||
const structure1 = await driver.analyseFull(conn);
|
const structure1 = await driver.analyseFull(conn);
|
||||||
expect(structure1.tables.length).toEqual(1);
|
expect(structure1.tables.length).toEqual(1);
|
||||||
@@ -92,13 +92,13 @@ describe('Table analyse', () => {
|
|||||||
'Table remove - incremental analysis - %s',
|
'Table remove - incremental analysis - %s',
|
||||||
testWrapper(async (conn, driver, engine) => {
|
testWrapper(async (conn, driver, engine) => {
|
||||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
|
||||||
await driver.query(conn, t2Sql(engine));
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
|
||||||
const structure1 = await driver.analyseFull(conn);
|
const structure1 = await driver.analyseFull(conn);
|
||||||
expect(structure1.tables.length).toEqual(2);
|
expect(structure1.tables.length).toEqual(2);
|
||||||
expect(structure1.tables.find(x => x.pureName == 't1')).toEqual(t1Match(engine));
|
expect(structure1.tables.find(x => x.pureName == 't1')).toEqual(t1Match(engine));
|
||||||
expect(structure1.tables.find(x => x.pureName == 't2')).toEqual(t2Match(engine));
|
expect(structure1.tables.find(x => x.pureName == 't2')).toEqual(t2Match(engine));
|
||||||
|
|
||||||
await driver.query(conn, 'DROP TABLE t2');
|
await runCommandOnDriver(conn, driver, dmp => dmp.put('DROP TABLE ~t2'));
|
||||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||||
|
|
||||||
expect(structure2.tables.length).toEqual(1);
|
expect(structure2.tables.length).toEqual(1);
|
||||||
@@ -110,14 +110,13 @@ describe('Table analyse', () => {
|
|||||||
'Table change - incremental analysis - %s',
|
'Table change - incremental analysis - %s',
|
||||||
testWrapper(async (conn, driver, engine) => {
|
testWrapper(async (conn, driver, engine) => {
|
||||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
|
||||||
await driver.query(conn, t2Sql(engine));
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
|
||||||
const structure1 = await driver.analyseFull(conn);
|
const structure1 = await driver.analyseFull(conn);
|
||||||
|
|
||||||
if (engine.dbSnapshotBySeconds) await new Promise(resolve => setTimeout(resolve, 1100));
|
if (engine.dbSnapshotBySeconds) await new Promise(resolve => setTimeout(resolve, 1100));
|
||||||
|
|
||||||
await driver.query(
|
await runCommandOnDriver(conn, driver, dmp =>
|
||||||
conn,
|
dmp.put(`ALTER TABLE ~t2 ADD ${engine.alterTableAddColumnSyntax ? 'COLUMN' : ''} ~nextcol varchar(50)`)
|
||||||
`ALTER TABLE t2 ADD ${engine.alterTableAddColumnSyntax ? 'COLUMN' : ''} nextcol varchar(50)`
|
|
||||||
);
|
);
|
||||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||||
|
|
||||||
@@ -133,7 +132,7 @@ describe('Table analyse', () => {
|
|||||||
'Index - full analysis - %s',
|
'Index - full analysis - %s',
|
||||||
testWrapper(async (conn, driver, engine) => {
|
testWrapper(async (conn, driver, engine) => {
|
||||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
|
||||||
await driver.query(conn, ix1Sql);
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(ix1Sql));
|
||||||
const structure = await driver.analyseFull(conn);
|
const structure = await driver.analyseFull(conn);
|
||||||
|
|
||||||
const t1 = structure.tables.find(x => x.pureName == 't1');
|
const t1 = structure.tables.find(x => x.pureName == 't1');
|
||||||
@@ -147,7 +146,7 @@ describe('Table analyse', () => {
|
|||||||
test.each(engines.filter(x => !x.skipUnique).map(engine => [engine.label, engine]))(
|
test.each(engines.filter(x => !x.skipUnique).map(engine => [engine.label, engine]))(
|
||||||
'Unique - full analysis - %s',
|
'Unique - full analysis - %s',
|
||||||
testWrapper(async (conn, driver, engine) => {
|
testWrapper(async (conn, driver, engine) => {
|
||||||
await driver.query(conn, t2Sql(engine));
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
|
||||||
const structure = await driver.analyseFull(conn);
|
const structure = await driver.analyseFull(conn);
|
||||||
|
|
||||||
const t2 = structure.tables.find(x => x.pureName == 't2');
|
const t2 = structure.tables.find(x => x.pureName == 't2');
|
||||||
@@ -161,8 +160,8 @@ describe('Table analyse', () => {
|
|||||||
test.each(engines.filter(x => !x.skipReferences).map(engine => [engine.label, engine]))(
|
test.each(engines.filter(x => !x.skipReferences).map(engine => [engine.label, engine]))(
|
||||||
'Foreign key - full analysis - %s',
|
'Foreign key - full analysis - %s',
|
||||||
testWrapper(async (conn, driver, engine) => {
|
testWrapper(async (conn, driver, engine) => {
|
||||||
await driver.query(conn, t2Sql(engine));
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
|
||||||
await driver.query(conn, t3Sql);
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(t3Sql));
|
||||||
// await driver.query(conn, fkSql);
|
// await driver.query(conn, fkSql);
|
||||||
|
|
||||||
const structure = await driver.analyseFull(conn);
|
const structure = await driver.analyseFull(conn);
|
||||||
@@ -181,7 +180,7 @@ describe('Table analyse', () => {
|
|||||||
test.each(engines.map(engine => [engine.label, engine]))(
|
test.each(engines.map(engine => [engine.label, engine]))(
|
||||||
'Table structure - default value - %s',
|
'Table structure - default value - %s',
|
||||||
testWrapper(async (conn, driver, engine) => {
|
testWrapper(async (conn, driver, engine) => {
|
||||||
await driver.query(conn, t4Sql);
|
await runCommandOnDriver(conn, driver, dmp => dmp.put(t4Sql));
|
||||||
|
|
||||||
const structure = await driver.analyseFull(conn);
|
const structure = await driver.analyseFull(conn);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ const _ = require('lodash');
|
|||||||
const fp = require('lodash/fp');
|
const fp = require('lodash/fp');
|
||||||
const engines = require('../engines');
|
const engines = require('../engines');
|
||||||
const { testWrapper } = require('../tools');
|
const { testWrapper } = require('../tools');
|
||||||
const { extendDatabaseInfo } = require('dbgate-tools');
|
const { extendDatabaseInfo, runCommandOnDriver } = require('dbgate-tools');
|
||||||
|
|
||||||
function createExpector(value) {
|
function createExpector(value) {
|
||||||
return _.cloneDeepWith(value, x => {
|
return _.cloneDeepWith(value, x => {
|
||||||
@@ -25,7 +25,7 @@ function checkTableStructure2(t1, t2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function testTableCreate(conn, driver, table) {
|
async function testTableCreate(conn, driver, table) {
|
||||||
await driver.query(conn, `create table t0 (id int not null primary key)`);
|
await runCommandOnDriver(conn, driver, dmp => dmp.put('create table ~t0 (~id int not null primary key)'));
|
||||||
|
|
||||||
const dmp = driver.createDumper();
|
const dmp = driver.createDumper();
|
||||||
const table1 = {
|
const table1 = {
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ services:
|
|||||||
# restart: on-failure
|
# restart: on-failure
|
||||||
|
|
||||||
oracle:
|
oracle:
|
||||||
image: container-registry.oracle.com/database/express:21.3.0-xe
|
image: gvenzl/oracle-xe:21-slim
|
||||||
environment:
|
environment:
|
||||||
ORACLE_PWD: Pwd2020Db
|
ORACLE_PASSWORD: Pwd2020Db
|
||||||
ports:
|
ports:
|
||||||
- 15006:1521
|
- 15006:1521
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
const views = {
|
const views = {
|
||||||
type: 'views',
|
type: 'views',
|
||||||
create1: 'CREATE VIEW obj1 AS SELECT id FROM t1',
|
create1: 'CREATE VIEW ~obj1 AS SELECT ~id FROM ~t1',
|
||||||
create2: 'CREATE VIEW obj2 AS SELECT id FROM t2',
|
create2: 'CREATE VIEW ~obj2 AS SELECT ~id FROM ~t2',
|
||||||
drop1: 'DROP VIEW obj1',
|
drop1: 'DROP VIEW ~obj1',
|
||||||
drop2: 'DROP VIEW obj2',
|
drop2: 'DROP VIEW ~obj2',
|
||||||
};
|
};
|
||||||
const matviews = {
|
const matviews = {
|
||||||
type: 'matviews',
|
type: 'matviews',
|
||||||
@@ -414,8 +414,27 @@ end;$$`,
|
|||||||
server: 'localhost',
|
server: 'localhost',
|
||||||
port: 15006,
|
port: 15006,
|
||||||
},
|
},
|
||||||
skipOnCI: true,
|
skipOnCI: false,
|
||||||
dbSnapshotBySeconds: true,
|
dbSnapshotBySeconds: true,
|
||||||
|
setNullDefaultInsteadOfDrop: true,
|
||||||
|
skipIncrementalAnalysis: true,
|
||||||
|
objects: [
|
||||||
|
views,
|
||||||
|
{
|
||||||
|
type: 'procedures',
|
||||||
|
create1: 'CREATE PROCEDURE ~obj1 AS BEGIN SELECT ~id FROM ~t1 END',
|
||||||
|
create2: 'CREATE PROCEDURE ~obj2 AS BEGIN SELECT ~id FROM ~t2 END',
|
||||||
|
drop1: 'DROP PROCEDURE ~obj1',
|
||||||
|
drop2: 'DROP PROCEDURE ~obj2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'functions',
|
||||||
|
create1: 'CREATE FUNCTION ~obj1 RETURN NUMBER IS v_count NUMBER; \n BEGIN SELECT COUNT(*) INTO v_count FROM ~t1;\n RETURN v_count;\n END ~obj1',
|
||||||
|
create2: 'CREATE FUNCTION ~obj2 RETURN NUMBER IS v_count NUMBER; \n BEGIN SELECT COUNT(*) INTO v_count FROM ~t2;\n RETURN v_count;\n END ~obj2',
|
||||||
|
drop1: 'DROP FUNCTION ~obj1',
|
||||||
|
drop2: 'DROP FUNCTION ~obj2',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class DuplicatorItemHolder {
|
|||||||
this.autoColumn = this.table.columns.find(x => x.autoIncrement)?.columnName;
|
this.autoColumn = this.table.columns.find(x => x.autoIncrement)?.columnName;
|
||||||
if (
|
if (
|
||||||
this.table.primaryKey?.columns?.length != 1 ||
|
this.table.primaryKey?.columns?.length != 1 ||
|
||||||
this.table.primaryKey?.columns?.[0].columnName != this.autoColumn
|
this.table.primaryKey?.columns?.[0]?.columnName != this.autoColumn
|
||||||
) {
|
) {
|
||||||
this.autoColumn = null;
|
this.autoColumn = null;
|
||||||
}
|
}
|
||||||
@@ -140,6 +140,9 @@ class DuplicatorItemHolder {
|
|||||||
weakref.foreignKey.columns[0].columnName
|
weakref.foreignKey.columns[0].columnName
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
if (this.duplicator.driver.dialect.requireFromDual) {
|
||||||
|
dmp.put(' ^from ^dual');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const qrow = qres.rows[0];
|
const qrow = qres.rows[0];
|
||||||
return this.weakReferences.filter(x => qrow[x.columnName] == 0).map(x => x.columnName);
|
return this.weakReferences.filter(x => qrow[x.columnName] == 0).map(x => x.columnName);
|
||||||
@@ -194,6 +197,7 @@ class DuplicatorItemHolder {
|
|||||||
res = await runQueryOnDriver(pool, driver, dmp => dmp.selectScopeIdentity(this.table));
|
res = await runQueryOnDriver(pool, driver, dmp => dmp.selectScopeIdentity(this.table));
|
||||||
}
|
}
|
||||||
// console.log('IDRES', JSON.stringify(res));
|
// console.log('IDRES', JSON.stringify(res));
|
||||||
|
// console.log('*********** ENTRIES OF', res?.rows?.[0]);
|
||||||
const resId = Object.entries(res?.rows?.[0])?.[0]?.[1];
|
const resId = Object.entries(res?.rows?.[0])?.[0]?.[1];
|
||||||
if (resId != null) {
|
if (resId != null) {
|
||||||
this.idMap[chunk[this.autoColumn]] = resId;
|
this.idMap[chunk[this.autoColumn]] = resId;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import _compact from 'lodash/compact';
|
import _compact from 'lodash/compact';
|
||||||
|
import _isString from 'lodash/isString';
|
||||||
import { SqlDumper } from './SqlDumper';
|
import { SqlDumper } from './SqlDumper';
|
||||||
import { splitQuery } from 'dbgate-query-splitter';
|
import { splitQuery } from 'dbgate-query-splitter';
|
||||||
import { dumpSqlSelect } from 'dbgate-sqltree';
|
import { dumpSqlSelect } from 'dbgate-sqltree';
|
||||||
@@ -26,9 +27,17 @@ const dialect = {
|
|||||||
defaultSchemaName: null,
|
defaultSchemaName: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function runCommandOnDriver(pool, driver: EngineDriver, cmd: (dmp: SqlDumper) => void): Promise<void> {
|
export async function runCommandOnDriver(
|
||||||
|
pool,
|
||||||
|
driver: EngineDriver,
|
||||||
|
cmd: (dmp: SqlDumper) => void | string
|
||||||
|
): Promise<void> {
|
||||||
const dmp = driver.createDumper();
|
const dmp = driver.createDumper();
|
||||||
cmd(dmp as any);
|
if (_isString(cmd)) {
|
||||||
|
dmp.put(cmd);
|
||||||
|
} else {
|
||||||
|
cmd(dmp as any);
|
||||||
|
}
|
||||||
// console.log('CMD:', dmp.s);
|
// console.log('CMD:', dmp.s);
|
||||||
await driver.query(pool, dmp.s, { discardResult: true });
|
await driver.query(pool, dmp.s, { discardResult: true });
|
||||||
}
|
}
|
||||||
@@ -39,11 +48,21 @@ export async function runQueryOnDriver(
|
|||||||
cmd: (dmp: SqlDumper) => void
|
cmd: (dmp: SqlDumper) => void
|
||||||
): Promise<QueryResult> {
|
): Promise<QueryResult> {
|
||||||
const dmp = driver.createDumper();
|
const dmp = driver.createDumper();
|
||||||
cmd(dmp as any);
|
if (_isString(cmd)) {
|
||||||
|
dmp.put(cmd);
|
||||||
|
} else {
|
||||||
|
cmd(dmp as any);
|
||||||
|
}
|
||||||
// console.log('QUERY:', dmp.s);
|
// console.log('QUERY:', dmp.s);
|
||||||
return await driver.query(pool, dmp.s);
|
return await driver.query(pool, dmp.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatQueryWithoutParams(driver: EngineDriver, sql: string) {
|
||||||
|
const dmp = driver.createDumper();
|
||||||
|
dmp.put(sql);
|
||||||
|
return dmp.s;
|
||||||
|
}
|
||||||
|
|
||||||
export const driverBase = {
|
export const driverBase = {
|
||||||
analyserClass: null,
|
analyserClass: null,
|
||||||
dumperClass: SqlDumper,
|
dumperClass: SqlDumper,
|
||||||
|
|||||||
@@ -132,7 +132,9 @@ class MsSqlDumper extends SqlDumper {
|
|||||||
} else {
|
} else {
|
||||||
this.dropDefault(oldcol);
|
this.dropDefault(oldcol);
|
||||||
if (oldcol.columnName != newcol.columnName) this.renameColumn(oldcol, newcol.columnName);
|
if (oldcol.columnName != newcol.columnName) this.renameColumn(oldcol, newcol.columnName);
|
||||||
this.fillNewNotNullDefaults(newcol);
|
if (!oldcol.notNull) {
|
||||||
|
this.fillNewNotNullDefaults(newcol);
|
||||||
|
}
|
||||||
this.put('^alter ^table %f ^alter ^column %i ', oldcol, oldcol.columnName, newcol.columnName);
|
this.put('^alter ^table %f ^alter ^column %i ', oldcol, oldcol.columnName, newcol.columnName);
|
||||||
this.columnDefinition(newcol, { includeDefault: false });
|
this.columnDefinition(newcol, { includeDefault: false });
|
||||||
this.endCommand();
|
this.endCommand();
|
||||||
|
|||||||
@@ -32,10 +32,12 @@ class Dumper extends SqlDumper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
changeColumn(oldcol, newcol, constraints) {
|
changeColumn(oldcol, newcol, constraints) {
|
||||||
this.fillNewNotNullDefaults({
|
if (!oldcol.notNull) {
|
||||||
...newcol,
|
this.fillNewNotNullDefaults({
|
||||||
columnName: oldcol.columnName,
|
...newcol,
|
||||||
});
|
columnName: oldcol.columnName,
|
||||||
|
});
|
||||||
|
}
|
||||||
this.put('^alter ^table %f ^change ^column %i %i ', oldcol, oldcol.columnName, newcol.columnName);
|
this.put('^alter ^table %f ^change ^column %i %i ', oldcol, oldcol.columnName, newcol.columnName);
|
||||||
this.columnDefinition(newcol);
|
this.columnDefinition(newcol);
|
||||||
this.inlineConstraints(constraints);
|
this.inlineConstraints(constraints);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ function getColumnInfo(
|
|||||||
columnName: column_name,
|
columnName: column_name,
|
||||||
dataType: fullDataType,
|
dataType: fullDataType,
|
||||||
notNull: is_nullable == 'N',
|
notNull: is_nullable == 'N',
|
||||||
defaultValue: autoIncrement ? undefined : default_value,
|
defaultValue: autoIncrement ? undefined : default_value?.trim(),
|
||||||
autoIncrement,
|
autoIncrement,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ class Analyser extends DatabaseAnalyser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _computeSingleObjectId() {
|
async _computeSingleObjectId() {
|
||||||
const { typeField, pureName } = this.singleObjectFilter;
|
const { typeField, pureName } = this.singleObjectFilter;
|
||||||
this.singleObjectId = `${typeField}:${pureName}`;
|
this.singleObjectId = `${typeField}:${pureName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +114,8 @@ class Analyser extends DatabaseAnalyser {
|
|||||||
indexes.rows.filter(
|
indexes.rows.filter(
|
||||||
idx =>
|
idx =>
|
||||||
idx.tableName == newTable.pureName &&
|
idx.tableName == newTable.pureName &&
|
||||||
!uniqueNames.rows.find(x => x.constraintName == idx.constraintName)
|
!uniqueNames.rows.find(x => x.constraintName == idx.constraintName) &&
|
||||||
|
!idx.constraintName.startsWith('SYS_C')
|
||||||
),
|
),
|
||||||
'constraintName'
|
'constraintName'
|
||||||
).map(idx => ({
|
).map(idx => ({
|
||||||
@@ -141,6 +142,9 @@ class Analyser extends DatabaseAnalyser {
|
|||||||
..._.pick(col, ['columnName']),
|
..._.pick(col, ['columnName']),
|
||||||
})),
|
})),
|
||||||
})),
|
})),
|
||||||
|
identitySequenceName: (columnsGrouped[columnGroup(table)] || [])
|
||||||
|
.find(x => x?.default_value?.endsWith('.nextval'))
|
||||||
|
?.default_value?.match(/\"([^"]+)\"\.nextval/)?.[1],
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
views: views.rows.map(view => ({
|
views: views.rows.map(view => ({
|
||||||
@@ -167,14 +171,14 @@ class Analyser extends DatabaseAnalyser {
|
|||||||
objectId: `procedures:${proc.pure_name}`,
|
objectId: `procedures:${proc.pure_name}`,
|
||||||
pureName: proc.pure_name,
|
pureName: proc.pure_name,
|
||||||
// schemaName: proc.schema_name,
|
// schemaName: proc.schema_name,
|
||||||
createSql: `CREATE PROCEDURE "${proc.pure_name}"() LANGUAGE ${proc.language}\nAS\n$$\n${proc.definition}\n$$`,
|
createSql: `SET SQLTERMINATOR "/"\nCREATE ${proc.source_code}\n/\n`,
|
||||||
contentHash: proc.hash_code,
|
contentHash: proc.hash_code,
|
||||||
})),
|
})),
|
||||||
functions: routines.rows
|
functions: routines.rows
|
||||||
.filter(x => x.object_type == 'FUNCTION')
|
.filter(x => x.object_type == 'FUNCTION')
|
||||||
.map(func => ({
|
.map(func => ({
|
||||||
objectId: `functions:${func.pure_name}`,
|
objectId: `functions:${func.pure_name}`,
|
||||||
createSql: `CREATE FUNCTION "${func.pure_name}"() RETURNS ${func.data_type} LANGUAGE ${func.language}\nAS\n$$\n${func.definition}\n$$`,
|
createSql: `SET SQLTERMINATOR "/"\nCREATE ${func.source_code}\n/\n`,
|
||||||
pureName: func.pure_name,
|
pureName: func.pure_name,
|
||||||
// schemaName: func.schema_name,
|
// schemaName: func.schema_name,
|
||||||
contentHash: func.hash_code,
|
contentHash: func.hash_code,
|
||||||
|
|||||||
@@ -1,41 +1,10 @@
|
|||||||
module.exports = `
|
module.exports = `
|
||||||
select
|
SELECT
|
||||||
routine_name as "pure_name",
|
name as "pure_name",
|
||||||
-- routine_schema as "schema_name",
|
type as "object_type",
|
||||||
routine_definition as "definition",
|
LISTAGG(text, '') WITHIN GROUP (ORDER BY line) AS "source_code",
|
||||||
ora_hash(routine_definition) as "hash_code",
|
ora_hash(LISTAGG(text, '') WITHIN GROUP (ORDER BY line)) AS "hash_code"
|
||||||
routine_type as "object_type",
|
FROM all_source
|
||||||
'fixme_data_type' as "data_type",
|
WHERE type in ('FUNCTION', 'PROCEDURE') AND OWNER = '$owner'
|
||||||
'fixme_external_language' as "language"
|
GROUP BY name, type
|
||||||
from (select
|
|
||||||
sys_context('userenv', 'DB_NAME') routine_catalog,
|
|
||||||
sys_context('userenv', 'DB_NAME') specific_catalog,
|
|
||||||
ap.owner specific_schema,
|
|
||||||
ap.owner routine_schema,
|
|
||||||
decode( ap.procedure_name, null, ap.object_name || ap.procedure_name, ap.procedure_name ) specific_name,
|
|
||||||
decode( ap.procedure_name, null, ap.object_name || ap.procedure_name, ap.procedure_name ) routine_name,
|
|
||||||
ao.object_type routine_type,
|
|
||||||
decode(impltypeowner, null, to_char(null), SYS_CONTEXT('userenv', 'DB_NAME')) type_udt_catalog,
|
|
||||||
--to_clob(get_proc_text(ap.owner, ap.object_name, ao.object_type, 32767)) routine_body,
|
|
||||||
'fixme_routine_body.' || ap.owner || '.' || decode( ap.procedure_name, null, ap.object_name || ap.procedure_name, ap.procedure_name ) routine_body,
|
|
||||||
--to_clob(get_proc_text(ap.owner, ap.object_name, ao.object_type, 4000)) routine_definition,
|
|
||||||
'fixme_routine_definition.' || ap.owner || '.' || decode( ap.procedure_name, null, ap.object_name || ap.procedure_name, ap.procedure_name ) routine_definition,
|
|
||||||
sys_context('userenv', 'DB_NAME') character_set_catalog,
|
|
||||||
'SYS' character_set_schema,
|
|
||||||
sys_context('userenv', 'DB_NAME') collation_catalog,
|
|
||||||
'SYS' collation_schema,
|
|
||||||
deterministic is_deterministic,
|
|
||||||
pipelined is_pipelined ,
|
|
||||||
aggregate is_aggregate,
|
|
||||||
authid is_definer
|
|
||||||
from
|
|
||||||
all_procedures ap,
|
|
||||||
all_objects ao
|
|
||||||
where
|
|
||||||
ap.owner = '$owner' and
|
|
||||||
ap.owner = ao.owner and
|
|
||||||
ap.object_name = ao.object_name and
|
|
||||||
ao.object_type in ('PACKAGE', 'PROCEDURE', 'FUNCTION')
|
|
||||||
and ao.object_name =OBJECT_ID_CONDITION
|
|
||||||
) routines
|
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from (select
|
|||||||
view_name as "pure_name",
|
view_name as "pure_name",
|
||||||
text as "create_sql"
|
text as "create_sql"
|
||||||
from all_views av
|
from all_views av
|
||||||
where owner = 'C##test' and text is not null
|
where owner = '$owner' and text is not null
|
||||||
) avv
|
) avv
|
||||||
where 'views:' || "pure_name" is not null
|
where 'views:' || "pure_name" is not null
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -60,9 +60,9 @@ class Dumper extends SqlDumper {
|
|||||||
// this.putCmd('^alter ^table %f ^rename ^to %i', obj, newname);
|
// this.putCmd('^alter ^table %f ^rename ^to %i', obj, newname);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// renameColumn(column, newcol) {
|
renameColumn(column, newcol) {
|
||||||
// this.putCmd('^alter ^table %f ^rename ^column %i ^to %i', column, column.columnName, newcol);
|
this.putCmd('^alter ^table %f ^rename ^column %i ^to %i', column, column.columnName, newcol);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// dropTable(obj, options = {}) {
|
// dropTable(obj, options = {}) {
|
||||||
// this.put('^drop ^table');
|
// this.put('^drop ^table');
|
||||||
@@ -87,30 +87,48 @@ class Dumper extends SqlDumper {
|
|||||||
// super.columnDefinition(col, options);
|
// super.columnDefinition(col, options);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// changeColumn(oldcol, newcol, constraints) {
|
changeColumn(oldcol, newcol, constraints) {
|
||||||
// if (oldcol.columnName != newcol.columnName) {
|
if (oldcol.columnName != newcol.columnName) {
|
||||||
// this.putCmd('^alter ^table %f ^rename ^column %i ^to %i', oldcol, 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) {
|
||||||
// }
|
this.fillNewNotNullDefaults(newcol);
|
||||||
// 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 (!testEqualTypes(oldcol, newcol) || oldcol.notNull != newcol.notNull) {
|
||||||
// }
|
this.putCmd(
|
||||||
// if (oldcol.defaultValue != newcol.defaultValue) {
|
'^alter ^table %f ^modify (%i %s %k)',
|
||||||
// if (newcol.defaultValue == null) {
|
newcol,
|
||||||
// this.putCmd('^alter ^table %f ^alter ^column %i ^drop ^default', newcol, newcol.columnName);
|
newcol.columnName,
|
||||||
// } else {
|
newcol.dataType,
|
||||||
// this.putCmd(
|
newcol.notNull ? 'not null' : 'null'
|
||||||
// '^alter ^table %f ^alter ^column %i ^set ^default %s',
|
);
|
||||||
// newcol,
|
}
|
||||||
// newcol.columnName,
|
|
||||||
// newcol.defaultValue
|
if (oldcol.defaultValue != newcol.defaultValue) {
|
||||||
// );
|
if (newcol.defaultValue?.trim()) {
|
||||||
// }
|
this.putCmd('^alter ^table %f ^modify (%i ^default %s)', newcol, newcol.columnName, newcol.defaultValue);
|
||||||
// }
|
} else {
|
||||||
// }
|
this.putCmd('^alter ^table %f ^modify (%i ^default ^null)', newcol, newcol.columnName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectScopeIdentity(table) {
|
||||||
|
const sequence = table.identitySequenceName;
|
||||||
|
if (sequence) {
|
||||||
|
this.put('^select %i.CURRVAL FROM DUAL', sequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renameTable(obj, newname) {
|
||||||
|
this.putCmd('^alter ^table %f ^rename ^to %i', obj, newname);
|
||||||
|
}
|
||||||
|
|
||||||
|
renameSqlObject(obj, newname) {
|
||||||
|
this.putCmd('^rename %f ^to %i', obj, newname);
|
||||||
|
}
|
||||||
|
|
||||||
// putValue(value) {
|
// putValue(value) {
|
||||||
// if (value === true) this.putRaw('true');
|
// if (value === true) this.putRaw('true');
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const dialect = {
|
|||||||
ilike: true,
|
ilike: true,
|
||||||
// stringEscapeChar: '\\',
|
// stringEscapeChar: '\\',
|
||||||
stringEscapeChar: "'",
|
stringEscapeChar: "'",
|
||||||
fallbackDataType: 'varchar',
|
fallbackDataType: 'varchar(250)',
|
||||||
anonymousPrimaryKey: false,
|
anonymousPrimaryKey: false,
|
||||||
enableConstraintsPerTable: true,
|
enableConstraintsPerTable: true,
|
||||||
dropColumnDependencies: ['dependencies'],
|
dropColumnDependencies: ['dependencies'],
|
||||||
@@ -22,6 +22,7 @@ const dialect = {
|
|||||||
},
|
},
|
||||||
userDatabaseNamePrefix: 'C##',
|
userDatabaseNamePrefix: 'C##',
|
||||||
upperCaseAllDbObjectNames: true,
|
upperCaseAllDbObjectNames: true,
|
||||||
|
requireStandaloneSelectForScopeIdentity: true,
|
||||||
|
|
||||||
createColumn: true,
|
createColumn: true,
|
||||||
dropColumn: true,
|
dropColumn: true,
|
||||||
@@ -36,6 +37,7 @@ const dialect = {
|
|||||||
dropUnique: true,
|
dropUnique: true,
|
||||||
createCheck: true,
|
createCheck: true,
|
||||||
dropCheck: true,
|
dropCheck: true,
|
||||||
|
renameSqlObject: true,
|
||||||
|
|
||||||
dropReferencesWhenDropTable: true,
|
dropReferencesWhenDropTable: true,
|
||||||
requireFromDual: true,
|
requireFromDual: true,
|
||||||
|
|||||||
@@ -89,7 +89,9 @@ class Dumper extends SqlDumper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (oldcol.notNull != newcol.notNull) {
|
if (oldcol.notNull != newcol.notNull) {
|
||||||
this.fillNewNotNullDefaults(newcol);
|
if (!oldcol.notNull) {
|
||||||
|
this.fillNewNotNullDefaults(newcol);
|
||||||
|
}
|
||||||
if (newcol.notNull) this.putCmd('^alter ^table %f ^alter ^column %i ^set ^not ^null', newcol, newcol.columnName);
|
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);
|
else this.putCmd('^alter ^table %f ^alter ^column %i ^drop ^not ^null', newcol, newcol.columnName);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user