Compare commits
77 Commits
v4.3.0-beta.1
...
v4.3.1
| Author | SHA1 | Date | |
|---|---|---|---|
| d81826214f | |||
| 95b86f437e | |||
| 99876e3158 | |||
| 97215e31e9 | |||
| a1bf7ddb50 | |||
| 5ae33a320c | |||
| 2ef81942db | |||
| 571be4b3f9 | |||
| 12c9e638f5 | |||
| 2df98c610a | |||
| f59c6c9fe5 | |||
| 5b4e69fa0a | |||
| aad39227b7 | |||
| 2c2b4fc08f | |||
| 8910a6bd07 | |||
| 3b15d315ec | |||
| 1f8021833b | |||
| e2ce2f7057 | |||
| 17a3fec158 | |||
| 8ef8685e4e | |||
| 1c8e69225d | |||
| e7634a2521 | |||
| a03682fc8a | |||
| d185fe8a8c | |||
| 7e82e83faa | |||
| f85460cce8 | |||
| 34fce0ef58 | |||
| 7699cca6b9 | |||
| da5b96c0a8 | |||
| b503ea9a02 | |||
| c6c04b87e1 | |||
| 065af0b4a8 | |||
| f92153d957 | |||
| d60f052897 | |||
| f664771643 | |||
| 402ac726f3 | |||
| 8363a887bc | |||
| 760f5c9616 | |||
| ba74f98015 | |||
| b055fff6e0 | |||
| ed17b425a3 | |||
| 87fe62005d | |||
| fce2f9a46a | |||
| bb86a3b8cc | |||
| 77c5192798 | |||
| e5b47baee3 | |||
| 81fbed7a7f | |||
| 90262a0318 | |||
| 72633a0c0d | |||
| 246c54f863 | |||
| 68fd49c224 | |||
| d179f4c753 | |||
| 80624d3abe | |||
| 02aa94afc9 | |||
| 1b853dd3b3 | |||
| e715a95cc0 | |||
| 3ca0810756 | |||
| af4d354ff4 | |||
| ca1c7cc556 | |||
| cb2a0b2492 | |||
| 185699cb51 | |||
| ce85f8f94d | |||
| 39748bdd6c | |||
| dcaf8351b5 | |||
| 2fa48b1138 | |||
| 02ce6b0204 | |||
| af75858ce8 | |||
| 3760217ff0 | |||
| 624ada2873 | |||
| e7c64265ae | |||
| f47c83fece | |||
| 82601dea24 | |||
| da4f465ed3 | |||
| a5f39da8ae | |||
| 016e96d0e6 | |||
| 37c305461f | |||
| 9f204bf187 |
@@ -19,7 +19,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js 10.x
|
||||
@@ -78,8 +78,9 @@ jobs:
|
||||
cp app/dist/*x86*.AppImage artifacts/dbgate-beta.AppImage || true
|
||||
cp app/dist/*arm64*.AppImage artifacts/dbgate-beta-arm64.AppImage || true
|
||||
cp app/dist/*armv7l*.AppImage artifacts/dbgate-beta-armv7l.AppImage || true
|
||||
cp app/dist/*.exe artifacts/dbgate-beta.exe || true
|
||||
cp app/dist/*win*.zip artifacts/dbgate-windows-beta.zip || true
|
||||
cp app/dist/*win*.exe artifacts/dbgate-beta.exe || true
|
||||
cp app/dist/*win_x64.zip artifacts/dbgate-windows-beta.zip || true
|
||||
cp app/dist/*win_arm64.zip artifacts/dbgate-windows-beta-arm64.zip || true
|
||||
cp app/dist/*-mac.dmg artifacts/dbgate-beta.dmg || true
|
||||
cp app/dist/*-mac_arm64.dmg artifacts/dbgate-beta-arm64.dmg || true
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js 10.x
|
||||
@@ -79,7 +79,8 @@ jobs:
|
||||
cp app/dist/*arm64*.AppImage artifacts/dbgate-latest-arm64.AppImage || true
|
||||
cp app/dist/*armv7l*.AppImage artifacts/dbgate-latest-armv7l.AppImage || true
|
||||
cp app/dist/*.exe artifacts/dbgate-latest.exe || true
|
||||
cp app/dist/*win*.zip artifacts/dbgate-windows-latest.zip || true
|
||||
cp app/dist/*win_x64.zip artifacts/dbgate-windows-latest.zip || true
|
||||
cp app/dist/*win_arm64.zip artifacts/dbgate-windows-latest-arm64.zip || true
|
||||
cp app/dist/*-mac.dmg artifacts/dbgate-latest.dmg || true
|
||||
cp app/dist/*-mac_arm64.dmg artifacts/dbgate-latest-arm64.dmg || true
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js 10.x
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js 10.x
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js 10.x
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: yarn install
|
||||
|
||||
+11
-3
@@ -1,9 +1,17 @@
|
||||
# ChangeLog
|
||||
|
||||
### 4.3.0
|
||||
- ADDED: Table structure editor
|
||||
- ADDED: Index support
|
||||
- ADDED: Unique constraint support
|
||||
- ADDED: Context menu for drop/rename table/columns and for drop view/procedure/function
|
||||
- ADDED: Added support for Windows arm64 platform
|
||||
- FIXED: Search by _id in MongoDB
|
||||
|
||||
### 4.2.6
|
||||
- Fixed MongoDB import
|
||||
- Configurable thousands separator #136
|
||||
- Using case insensitive text search in postgres
|
||||
- FIXED: Fixed MongoDB import
|
||||
- ADDED: Configurable thousands separator #136
|
||||
- ADDED: Using case insensitive text search in postgres
|
||||
|
||||
### 4.2.5
|
||||
- FIXED: Fixed crash when using large model on some installations
|
||||
|
||||
+7
-1
@@ -68,7 +68,13 @@
|
||||
"win": {
|
||||
"target": [
|
||||
"nsis",
|
||||
"zip"
|
||||
{
|
||||
"target": "zip",
|
||||
"arch": [
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"icon": "icon.ico",
|
||||
"publish": [
|
||||
|
||||
+3
-1
@@ -50,8 +50,10 @@ function buildMenu() {
|
||||
commandItem('file.open'),
|
||||
commandItem('group.save'),
|
||||
commandItem('group.saveAs'),
|
||||
commandItem('database.search'),
|
||||
{ type: 'separator' },
|
||||
{ role: 'close' },
|
||||
commandItem('tabs.closeTab'),
|
||||
commandItem('file.exit'),
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
const stableStringify = require('json-stable-stringify');
|
||||
const _ = require('lodash');
|
||||
const fp = require('lodash/fp');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const { testWrapper } = require('../tools');
|
||||
const engines = require('../engines');
|
||||
const { getAlterDatabaseScript, extendDatabaseInfo, generateDbPairingId } = require('dbgate-tools');
|
||||
|
||||
function flatSource() {
|
||||
return _.flatten(
|
||||
engines.map(engine => (engine.objects || []).map(object => [engine.label, object.type, object, engine]))
|
||||
);
|
||||
}
|
||||
|
||||
async function testDatabaseDiff(conn, driver, mangle, createObject = null) {
|
||||
await driver.query(conn, `create table t1 (id int not null primary key)`);
|
||||
|
||||
await driver.query(
|
||||
conn,
|
||||
`create table t2 (
|
||||
id int not null primary key,
|
||||
t1_id int null references t1(id)
|
||||
)`
|
||||
);
|
||||
|
||||
if (createObject) await driver.query(conn, createObject);
|
||||
|
||||
const structure1 = generateDbPairingId(extendDatabaseInfo(await driver.analyseFull(conn)));
|
||||
let structure2 = _.cloneDeep(structure1);
|
||||
mangle(structure2);
|
||||
structure2 = extendDatabaseInfo(structure2);
|
||||
|
||||
const { sql } = getAlterDatabaseScript(structure1, structure2, {}, structure2, driver);
|
||||
console.log('RUNNING ALTER SQL', driver.engine, ':', sql);
|
||||
|
||||
await driver.script(conn, sql);
|
||||
|
||||
const structure2Real = extendDatabaseInfo(await driver.analyseFull(conn));
|
||||
|
||||
expect(structure2Real.tables.length).toEqual(structure2.tables.length);
|
||||
return structure2Real;
|
||||
}
|
||||
|
||||
describe('Alter database', () => {
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Drop referenced table - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDiff(conn, driver, db => {
|
||||
_.remove(db.tables, x => x.pureName == 't1');
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test.each(flatSource())(
|
||||
'Drop object - %s - %s',
|
||||
testWrapper(async (conn, driver, type, object, engine) => {
|
||||
const db = await testDatabaseDiff(
|
||||
conn,
|
||||
driver,
|
||||
db => {
|
||||
_.remove(db[type], x => x.pureName == 'obj1');
|
||||
},
|
||||
object.create1
|
||||
);
|
||||
expect(db[type].length).toEqual(0);
|
||||
})
|
||||
);
|
||||
});
|
||||
+2
-2
@@ -46,7 +46,7 @@ async function testTableDiff(conn, driver, mangle) {
|
||||
mangle(tget(structure2));
|
||||
structure2 = extendDatabaseInfo(structure2);
|
||||
|
||||
const sql = getAlterTableScript(tget(structure1), tget(structure2), {}, structure2, driver);
|
||||
const { sql } = getAlterTableScript(tget(structure1), tget(structure2), {}, structure2, driver);
|
||||
console.log('RUNNING ALTER SQL', driver.engine, ':', sql);
|
||||
|
||||
await driver.script(conn, sql);
|
||||
@@ -68,7 +68,7 @@ function engines_columns_source() {
|
||||
return _.flatten(engines.map(engine => TESTED_COLUMNS.map(column => [engine.label, column, engine])));
|
||||
}
|
||||
|
||||
describe('Alter processor', () => {
|
||||
describe('Alter table', () => {
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Add column - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
@@ -116,11 +116,11 @@ const engines = [
|
||||
|
||||
const filterLocal = [
|
||||
// filter local testing
|
||||
'-MySQL',
|
||||
'-PostgreSQL',
|
||||
'MySQL',
|
||||
'PostgreSQL',
|
||||
'SQL Server',
|
||||
'-SQLite',
|
||||
'-CockroachDB',
|
||||
'SQLite',
|
||||
'CockroachDB',
|
||||
];
|
||||
|
||||
module.exports = process.env.CITEST
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "4.3.0-beta.1",
|
||||
"version": "4.3.1",
|
||||
"name": "dbgate-all",
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"env-cmd": "^10.1.0",
|
||||
"node-loader": "^1.0.2",
|
||||
"nodemon": "^2.0.2",
|
||||
"typescript": "^3.7.4",
|
||||
"typescript": "^4.4.3",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11"
|
||||
},
|
||||
|
||||
@@ -14,6 +14,7 @@ let analysedStructure = null;
|
||||
let lastPing = null;
|
||||
let lastStatus = null;
|
||||
let analysedTime = 0;
|
||||
let serverVersion;
|
||||
|
||||
async function checkedAsyncCall(promise) {
|
||||
try {
|
||||
@@ -36,7 +37,7 @@ async function handleFullRefresh() {
|
||||
loadingModel = true;
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
setStatusName('loadStructure');
|
||||
analysedStructure = await checkedAsyncCall(driver.analyseFull(systemConnection));
|
||||
analysedStructure = await checkedAsyncCall(driver.analyseFull(systemConnection, serverVersion));
|
||||
analysedTime = new Date().getTime();
|
||||
process.send({ msgtype: 'structure', structure: analysedStructure });
|
||||
process.send({ msgtype: 'structureTime', analysedTime });
|
||||
@@ -48,7 +49,7 @@ async function handleIncrementalRefresh(forceSend) {
|
||||
loadingModel = true;
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
setStatusName('checkStructure');
|
||||
const newStructure = await checkedAsyncCall(driver.analyseIncremental(systemConnection, analysedStructure));
|
||||
const newStructure = await checkedAsyncCall(driver.analyseIncremental(systemConnection, analysedStructure, serverVersion));
|
||||
analysedTime = new Date().getTime();
|
||||
if (newStructure != null) {
|
||||
analysedStructure = newStructure;
|
||||
@@ -84,6 +85,7 @@ async function readVersion() {
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
const version = await driver.getVersion(systemConnection);
|
||||
process.send({ msgtype: 'version', version });
|
||||
serverVersion = version;
|
||||
}
|
||||
|
||||
async function handleConnect({ connection, structure, globalSettings }) {
|
||||
@@ -93,7 +95,7 @@ async function handleConnect({ connection, structure, globalSettings }) {
|
||||
if (!structure) setStatusName('pending');
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
systemConnection = await checkedAsyncCall(connectUtility(driver, storedConnection));
|
||||
readVersion();
|
||||
await checkedAsyncCall(readVersion());
|
||||
if (structure) {
|
||||
analysedStructure = structure;
|
||||
handleIncrementalRefresh(true);
|
||||
|
||||
@@ -70,14 +70,14 @@ function decryptPasswordField(connection, field) {
|
||||
function encryptConnection(connection) {
|
||||
connection = encryptPasswordField(connection, 'password');
|
||||
connection = encryptPasswordField(connection, 'sshPassword');
|
||||
connection = encryptPasswordField(connection, 'sshKeyFilePassword');
|
||||
connection = encryptPasswordField(connection, 'sshKeyfilePassword');
|
||||
return connection;
|
||||
}
|
||||
|
||||
function decryptConnection(connection) {
|
||||
connection = decryptPasswordField(connection, 'password');
|
||||
connection = decryptPasswordField(connection, 'sshPassword');
|
||||
connection = decryptPasswordField(connection, 'sshKeyFilePassword');
|
||||
connection = decryptPasswordField(connection, 'sshKeyfilePassword');
|
||||
return connection;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ const platformInfo = {
|
||||
environment: process.env.NODE_ENV,
|
||||
platform,
|
||||
runningInWebpack: !!process.env.WEBPACK_DEV_SERVER_URL,
|
||||
defaultKeyFile: path.join(os.homedir(), '.ssh/id_rsa'),
|
||||
defaultKeyfile: path.join(os.homedir(), '.ssh/id_rsa'),
|
||||
};
|
||||
|
||||
module.exports = platformInfo;
|
||||
|
||||
@@ -16,9 +16,9 @@ const CONNECTION_FIELDS = [
|
||||
'sshLogin',
|
||||
'sshPassword',
|
||||
'sshMode',
|
||||
'sshKeyFile',
|
||||
'sshKeyfile',
|
||||
'sshBastionHost',
|
||||
'sshKeyFilePassword',
|
||||
'sshKeyfilePassword',
|
||||
];
|
||||
const TUNNEL_FIELDS = [...CONNECTION_FIELDS, 'server', 'port'];
|
||||
|
||||
@@ -31,7 +31,7 @@ async function getSshConnection(connection) {
|
||||
endPort: connection.sshPort || 22,
|
||||
bastionHost: connection.sshBastionHost || '',
|
||||
agentForward: connection.sshMode == 'agent',
|
||||
passphrase: connection.sshMode == 'keyFile' ? connection.sshKeyFilePassword : undefined,
|
||||
passphrase: connection.sshMode == 'keyFile' ? connection.sshKeyfilePassword : undefined,
|
||||
username: connection.sshLogin,
|
||||
password: connection.sshMode == 'userPassword' ? connection.sshPassword : undefined,
|
||||
agentSocket: connection.sshMode == 'agent' ? platformInfo.sshAuthSock : undefined,
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
"devDependencies": {
|
||||
"dbgate-types": "^4.1.1",
|
||||
"@types/node": "^13.7.0",
|
||||
"typescript": "^3.7.5"
|
||||
"typescript": "^4.4.3"
|
||||
}
|
||||
}
|
||||
@@ -118,7 +118,11 @@ export function setChangeSetValue(
|
||||
};
|
||||
}
|
||||
|
||||
export function setChangeSetRowData(changeSet: ChangeSet, definition: ChangeSetRowDefinition, document: any): ChangeSet {
|
||||
export function setChangeSetRowData(
|
||||
changeSet: ChangeSet,
|
||||
definition: ChangeSetRowDefinition,
|
||||
document: any
|
||||
): ChangeSet {
|
||||
if (!changeSet || !definition) return changeSet;
|
||||
let [fieldName, existingItem] = findExistingChangeSetItem(changeSet, definition);
|
||||
if (fieldName == 'deletes') {
|
||||
@@ -213,7 +217,7 @@ function extractFields(item: ChangeSetItem, allowNulls = true): UpdateField[] {
|
||||
}));
|
||||
}
|
||||
|
||||
function insertToSql(
|
||||
function changeSetInsertToSql(
|
||||
item: ChangeSetItem,
|
||||
dbinfo: DatabaseInfo = null
|
||||
): [AllowIdentityInsert, Insert, AllowIdentityInsert] {
|
||||
@@ -257,7 +261,7 @@ function insertToSql(
|
||||
];
|
||||
}
|
||||
|
||||
function extractCondition(item: ChangeSetItem): Condition {
|
||||
export function extractChangeSetCondition(item: ChangeSetItem, alias?: string): Condition {
|
||||
return {
|
||||
conditionType: 'and',
|
||||
conditions: _.keys(item.condition).map(columnName => ({
|
||||
@@ -271,6 +275,7 @@ function extractCondition(item: ChangeSetItem): Condition {
|
||||
pureName: item.pureName,
|
||||
schemaName: item.schemaName,
|
||||
},
|
||||
alias,
|
||||
},
|
||||
},
|
||||
right: {
|
||||
@@ -281,7 +286,7 @@ function extractCondition(item: ChangeSetItem): Condition {
|
||||
};
|
||||
}
|
||||
|
||||
function updateToSql(item: ChangeSetItem): Update {
|
||||
function changeSetUpdateToSql(item: ChangeSetItem): Update {
|
||||
return {
|
||||
from: {
|
||||
name: {
|
||||
@@ -291,11 +296,11 @@ function updateToSql(item: ChangeSetItem): Update {
|
||||
},
|
||||
commandType: 'update',
|
||||
fields: extractFields(item),
|
||||
where: extractCondition(item),
|
||||
where: extractChangeSetCondition(item),
|
||||
};
|
||||
}
|
||||
|
||||
function deleteToSql(item: ChangeSetItem): Delete {
|
||||
function changeSetDeleteToSql(item: ChangeSetItem): Delete {
|
||||
return {
|
||||
from: {
|
||||
name: {
|
||||
@@ -304,16 +309,16 @@ function deleteToSql(item: ChangeSetItem): Delete {
|
||||
},
|
||||
},
|
||||
commandType: 'delete',
|
||||
where: extractCondition(item),
|
||||
where: extractChangeSetCondition(item),
|
||||
};
|
||||
}
|
||||
|
||||
export function changeSetToSql(changeSet: ChangeSet, dbinfo: DatabaseInfo): Command[] {
|
||||
return _.compact(
|
||||
_.flatten([
|
||||
...(changeSet.inserts.map(item => insertToSql(item, dbinfo)) as any),
|
||||
...changeSet.updates.map(updateToSql),
|
||||
...changeSet.deletes.map(deleteToSql),
|
||||
...(changeSet.inserts.map(item => changeSetInsertToSql(item, dbinfo)) as any),
|
||||
...changeSet.updates.map(changeSetUpdateToSql),
|
||||
...changeSet.deletes.map(changeSetDeleteToSql),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import _ from 'lodash';
|
||||
import { Command, Insert, Update, Delete, UpdateField, Condition, AllowIdentityInsert } from 'dbgate-sqltree';
|
||||
import { NamedObjectInfo, DatabaseInfo, ForeignKeyInfo, TableInfo } from 'dbgate-types';
|
||||
import { ChangeSet, ChangeSetItem, extractChangeSetCondition } from './ChangeSet';
|
||||
|
||||
export interface ChangeSetDeleteCascade {
|
||||
title: string;
|
||||
commands: Command[];
|
||||
}
|
||||
|
||||
// function getDeleteScript()
|
||||
|
||||
function processDependencies(
|
||||
changeSet: ChangeSet,
|
||||
result: ChangeSetDeleteCascade[],
|
||||
allForeignKeys: ForeignKeyInfo[],
|
||||
fkPath: ForeignKeyInfo[],
|
||||
table: TableInfo,
|
||||
baseCmd: ChangeSetItem,
|
||||
dbinfo: DatabaseInfo
|
||||
) {
|
||||
if (result.find(x => x.title == table.pureName)) return;
|
||||
|
||||
const dependencies = allForeignKeys.filter(
|
||||
x => x.refSchemaName == table.schemaName && x.refTableName == table.pureName
|
||||
);
|
||||
|
||||
for (const fk of dependencies) {
|
||||
const depTable = dbinfo.tables.find(x => x.pureName == fk.pureName && x.schemaName == fk.schemaName);
|
||||
const subFkPath = [...fkPath, fk];
|
||||
if (depTable && depTable.pureName != baseCmd.pureName) {
|
||||
processDependencies(changeSet, result, allForeignKeys, subFkPath, depTable, baseCmd, dbinfo);
|
||||
}
|
||||
|
||||
const refCmd: Delete = {
|
||||
commandType: 'delete',
|
||||
from: {
|
||||
name: {
|
||||
pureName: fk.pureName,
|
||||
schemaName: fk.schemaName,
|
||||
},
|
||||
},
|
||||
where: {
|
||||
conditionType: 'exists',
|
||||
subQuery: {
|
||||
commandType: 'select',
|
||||
selectAll: true,
|
||||
from: {
|
||||
name: {
|
||||
pureName: fk.pureName,
|
||||
schemaName: fk.schemaName,
|
||||
},
|
||||
alias: 't0',
|
||||
relations: subFkPath.map((fkItem, fkIndex) => ({
|
||||
joinType: 'INNER JOIN',
|
||||
alias: `t${fkIndex + 1}`,
|
||||
name: {
|
||||
pureName: fkItem.refTableName,
|
||||
schemaName: fkItem.refSchemaName,
|
||||
},
|
||||
conditions: fkItem.columns.map(column => ({
|
||||
conditionType: 'binary',
|
||||
operator: '=',
|
||||
left: {
|
||||
exprType: 'column',
|
||||
columnName: column.columnName,
|
||||
source: { alias: `t${fkIndex}` },
|
||||
},
|
||||
right: {
|
||||
exprType: 'column',
|
||||
columnName: column.refColumnName,
|
||||
source: { alias: `t${fkIndex + 1}` },
|
||||
},
|
||||
})),
|
||||
})),
|
||||
},
|
||||
where: {
|
||||
conditionType: 'and',
|
||||
conditions: [
|
||||
extractChangeSetCondition(baseCmd, `t${subFkPath.length}`),
|
||||
// @ts-ignore
|
||||
...table.primaryKey.columns.map(column => ({
|
||||
conditionType: 'binary',
|
||||
operator: '=',
|
||||
left: {
|
||||
exprType: 'column',
|
||||
columnName: column.columnName,
|
||||
source: { alias: 't0' },
|
||||
},
|
||||
right: {
|
||||
exprType: 'column',
|
||||
columnName: column.columnName,
|
||||
source: {
|
||||
name: fk,
|
||||
},
|
||||
},
|
||||
})),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
let resItem = result.find(x => x.title == fk.pureName);
|
||||
if (!resItem) {
|
||||
resItem = {
|
||||
title: fk.pureName,
|
||||
commands: [],
|
||||
};
|
||||
result.push(resItem);
|
||||
}
|
||||
resItem.commands.push(refCmd);
|
||||
}
|
||||
}
|
||||
|
||||
export function getDeleteCascades(changeSet: ChangeSet, dbinfo: DatabaseInfo): ChangeSetDeleteCascade[] {
|
||||
const result: ChangeSetDeleteCascade[] = [];
|
||||
const allForeignKeys = _.flatten(dbinfo.tables.map(x => x.foreignKeys));
|
||||
for (const baseCmd of changeSet.deletes) {
|
||||
const table = dbinfo.tables.find(x => x.pureName == baseCmd.pureName && x.schemaName == baseCmd.schemaName);
|
||||
if (!table.primaryKey) continue;
|
||||
|
||||
processDependencies(changeSet, result, allForeignKeys, [], table, baseCmd, dbinfo);
|
||||
|
||||
// let resItem = result.find(x => x.title == baseCmd.pureName);
|
||||
// if (!resItem) {
|
||||
// resItem = {
|
||||
// title: baseCmd.pureName,
|
||||
// commands: [],
|
||||
// };
|
||||
// result.push(resItem);
|
||||
// }
|
||||
// resItem.commands.push(changeSetDeleteToSql(baseCmd));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -11,3 +11,4 @@ export * from './runMacro';
|
||||
export * from './FormViewDisplay';
|
||||
export * from './TableFormViewDisplay';
|
||||
export * from './CollectionGridDisplay';
|
||||
export * from './deleteCascade';
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"@types/node": "^13.7.0",
|
||||
"jest": "^24.9.0",
|
||||
"ts-jest": "^25.2.1",
|
||||
"typescript": "^3.7.5"
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/parsimmon": "^1.10.1",
|
||||
|
||||
@@ -28,6 +28,20 @@ const numberTestCondition = () => value => ({
|
||||
],
|
||||
});
|
||||
|
||||
const objectIdTestCondition = () => value => ({
|
||||
$or: [
|
||||
{
|
||||
__placeholder__: {
|
||||
$regex: `.*${value}.*`,
|
||||
$options: 'i',
|
||||
},
|
||||
},
|
||||
{
|
||||
__placeholder__: { $oid: value },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const testCondition = (operator, value) => () => ({
|
||||
__placeholder__: {
|
||||
[operator]: value,
|
||||
@@ -64,9 +78,12 @@ const createParser = () => {
|
||||
.map(Number)
|
||||
.desc('number'),
|
||||
|
||||
objectid: () => token(P.regexp(/[0-9a-f]{24}/)).desc('ObjectId'),
|
||||
|
||||
noQuotedString: () => P.regexp(/[^\s^,^'^"]+/).desc('string unquoted'),
|
||||
|
||||
value: r => P.alt(r.string1, r.string2, r.number, r.noQuotedString),
|
||||
value: r => P.alt(r.objectid, r.string1, r.string2, r.number, r.noQuotedString),
|
||||
valueTestObjectId: r => r.objectid.map(objectIdTestCondition()),
|
||||
valueTestNum: r => r.number.map(numberTestCondition()),
|
||||
valueTest: r => r.value.map(regexCondition('.*#VALUE#.*')),
|
||||
|
||||
@@ -108,6 +125,7 @@ const createParser = () => {
|
||||
r.startsWithNot,
|
||||
r.endsWithNot,
|
||||
r.containsNot,
|
||||
r.valueTestObjectId,
|
||||
r.valueTestNum,
|
||||
r.valueTest
|
||||
).trim(whitespace),
|
||||
|
||||
@@ -32,6 +32,6 @@
|
||||
"@types/node": "^13.7.0",
|
||||
"jest": "^24.9.0",
|
||||
"ts-jest": "^25.2.1",
|
||||
"typescript": "^3.7.5"
|
||||
"typescript": "^4.4.3"
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
"devDependencies": {
|
||||
"@types/node": "^13.7.0",
|
||||
"dbgate-types": "^4.1.1",
|
||||
"typescript": "^3.7.5"
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.21"
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"dbgate-types": "^4.1.1",
|
||||
"jest": "^24.9.0",
|
||||
"ts-jest": "^25.2.1",
|
||||
"typescript": "^3.7.5"
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.21",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DatabaseInfo, DatabaseModification, EngineDriver } from 'dbgate-types';
|
||||
import { DatabaseInfo, DatabaseModification, EngineDriver, SqlDialect } from 'dbgate-types';
|
||||
import _sortBy from 'lodash/sortBy';
|
||||
import _groupBy from 'lodash/groupBy';
|
||||
import _pick from 'lodash/pick';
|
||||
@@ -13,8 +13,11 @@ export class DatabaseAnalyser {
|
||||
modifications: DatabaseModification[];
|
||||
singleObjectFilter: any;
|
||||
singleObjectId: string = null;
|
||||
dialect: SqlDialect;
|
||||
|
||||
constructor(public pool, public driver: EngineDriver) {}
|
||||
constructor(public pool, public driver: EngineDriver, version) {
|
||||
this.dialect = (driver?.dialectByVersion && driver?.dialectByVersion(version)) || driver?.dialect;
|
||||
}
|
||||
|
||||
async _runAnalysis() {
|
||||
return DatabaseAnalyser.createEmptyStructure();
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
UniqueInfo,
|
||||
CheckInfo,
|
||||
AlterProcessor,
|
||||
SqlObjectInfo,
|
||||
} from 'dbgate-types';
|
||||
import _isString from 'lodash/isString';
|
||||
import _isNumber from 'lodash/isNumber';
|
||||
@@ -190,7 +191,7 @@ export class SqlDumper implements AlterProcessor {
|
||||
if (includeNullable) {
|
||||
this.put(column.notNull ? '^not ^null' : '^null');
|
||||
}
|
||||
if (includeDefault && column.defaultValue != null) {
|
||||
if (includeDefault && column.defaultValue?.trim()) {
|
||||
this.columnDefault(column);
|
||||
}
|
||||
}
|
||||
@@ -537,11 +538,14 @@ export class SqlDumper implements AlterProcessor {
|
||||
}
|
||||
|
||||
recreateTable(oldTable: TableInfo, newTable: TableInfo) {
|
||||
if (oldTable.pairingId != newTable.pairingId) {
|
||||
if (!oldTable.pairingId || !newTable.pairingId || oldTable.pairingId != newTable.pairingId) {
|
||||
throw new Error('Recreate is not possible: oldTable.paringId != newTable.paringId');
|
||||
}
|
||||
const tmpTable = `temp_${uuidv1()}`;
|
||||
|
||||
// console.log('oldTable', oldTable);
|
||||
// console.log('newTable', newTable);
|
||||
|
||||
const columnPairs = oldTable.columns
|
||||
.map(oldcol => ({
|
||||
oldcol,
|
||||
@@ -577,4 +581,27 @@ export class SqlDumper implements AlterProcessor {
|
||||
|
||||
this.dropTable({ ...oldTable, pureName: tmpTable });
|
||||
}
|
||||
|
||||
createSqlObject(obj: SqlObjectInfo) {
|
||||
this.putCmd(obj.createSql);
|
||||
}
|
||||
|
||||
getSqlObjectSqlName(ojectTypeField: string) {
|
||||
switch (ojectTypeField) {
|
||||
case 'procedures':
|
||||
return 'PROCEDURE';
|
||||
case 'views':
|
||||
return 'VIEW';
|
||||
case 'functions':
|
||||
return 'FUNCTION';
|
||||
case 'triggers':
|
||||
return 'TRIGGER';
|
||||
case 'matviews':
|
||||
return 'MATERIALIZED VIEW';
|
||||
}
|
||||
}
|
||||
|
||||
dropSqlObject(obj: SqlObjectInfo) {
|
||||
this.putCmd('^drop %s %f', this.getSqlObjectSqlName(obj.objectTypeField), obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import _ from 'lodash';
|
||||
import { generateTablePairingId } from '.';
|
||||
import {
|
||||
AlterProcessor,
|
||||
ColumnInfo,
|
||||
ConstraintInfo,
|
||||
DatabaseInfo,
|
||||
NamedObjectInfo,
|
||||
SqlObjectInfo,
|
||||
SqlDialect,
|
||||
TableInfo,
|
||||
} from '../../types';
|
||||
@@ -21,6 +22,16 @@ interface AlterOperation_DropTable {
|
||||
oldObject: TableInfo;
|
||||
}
|
||||
|
||||
interface AlterOperation_CreateSqlObject {
|
||||
operationType: 'createSqlObject';
|
||||
newObject: SqlObjectInfo;
|
||||
}
|
||||
|
||||
interface AlterOperation_DropSqlObject {
|
||||
operationType: 'dropSqlObject';
|
||||
oldObject: SqlObjectInfo;
|
||||
}
|
||||
|
||||
interface AlterOperation_RenameTable {
|
||||
operationType: 'renameTable';
|
||||
object: TableInfo;
|
||||
@@ -88,9 +99,17 @@ type AlterOperation =
|
||||
| AlterOperation_RenameTable
|
||||
| AlterOperation_RenameColumn
|
||||
| AlterOperation_RenameConstraint
|
||||
| AlterOperation_CreateSqlObject
|
||||
| AlterOperation_DropSqlObject
|
||||
| AlterOperation_RecreateTable;
|
||||
|
||||
export class AlterPlan {
|
||||
recreates = {
|
||||
tables: 0,
|
||||
constraints: 0,
|
||||
sqlObjects: 0,
|
||||
};
|
||||
|
||||
public operations: AlterOperation[] = [];
|
||||
constructor(public db: DatabaseInfo, public dialect: SqlDialect) {}
|
||||
|
||||
@@ -108,6 +127,20 @@ export class AlterPlan {
|
||||
});
|
||||
}
|
||||
|
||||
createSqlObject(obj: SqlObjectInfo) {
|
||||
this.operations.push({
|
||||
operationType: 'createSqlObject',
|
||||
newObject: obj,
|
||||
});
|
||||
}
|
||||
|
||||
dropSqlObject(obj: SqlObjectInfo) {
|
||||
this.operations.push({
|
||||
operationType: 'dropSqlObject',
|
||||
oldObject: obj,
|
||||
});
|
||||
}
|
||||
|
||||
createColumn(column: ColumnInfo) {
|
||||
this.operations.push({
|
||||
operationType: 'createColumn',
|
||||
@@ -182,6 +215,7 @@ export class AlterPlan {
|
||||
table,
|
||||
operations,
|
||||
});
|
||||
this.recreates.tables += 1;
|
||||
}
|
||||
|
||||
run(processor: AlterProcessor) {
|
||||
@@ -243,9 +277,41 @@ export class AlterPlan {
|
||||
return opRes;
|
||||
}),
|
||||
];
|
||||
|
||||
if (constraints.length > 0) {
|
||||
this.recreates.constraints += 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
if (op.operationType == 'dropTable') {
|
||||
return [
|
||||
...(this.dialect.dropReferencesWhenDropTable
|
||||
? (op.oldObject.dependencies || []).map(oldObject => {
|
||||
const opRes: AlterOperation = {
|
||||
operationType: 'dropConstraint',
|
||||
oldObject,
|
||||
};
|
||||
return opRes;
|
||||
})
|
||||
: []),
|
||||
op,
|
||||
];
|
||||
}
|
||||
|
||||
if (op.operationType == 'changeConstraint') {
|
||||
this.recreates.constraints += 1;
|
||||
const opDrop: AlterOperation = {
|
||||
operationType: 'dropConstraint',
|
||||
oldObject: op.oldObject,
|
||||
};
|
||||
const opCreate: AlterOperation = {
|
||||
operationType: 'createConstraint',
|
||||
newObject: op.newObject,
|
||||
};
|
||||
return [opDrop, opCreate];
|
||||
}
|
||||
|
||||
return [op];
|
||||
});
|
||||
|
||||
@@ -302,6 +368,7 @@ export class AlterPlan {
|
||||
const table = this.db.tables.find(
|
||||
x => x.pureName == op[objectField].pureName && x.schemaName == op[objectField].schemaName
|
||||
);
|
||||
this.recreates.tables += 1;
|
||||
return [
|
||||
{
|
||||
operationType: 'recreateTable',
|
||||
@@ -318,12 +385,17 @@ export class AlterPlan {
|
||||
const recreates = {};
|
||||
for (const op of this.operations) {
|
||||
if (op.operationType == 'recreateTable') {
|
||||
const recreate = {
|
||||
...op,
|
||||
operations: [...op.operations],
|
||||
};
|
||||
res.push(recreate);
|
||||
recreates[`${op.table.schemaName}||${op.table.pureName}`] = recreate;
|
||||
const existingRecreate = recreates[`${op.table.schemaName}||${op.table.pureName}`];
|
||||
if (existingRecreate) {
|
||||
existingRecreate.operations.push(...op.operations);
|
||||
} else {
|
||||
const recreate = {
|
||||
...op,
|
||||
operations: [...op.operations],
|
||||
};
|
||||
res.push(recreate);
|
||||
recreates[`${op.table.schemaName}||${op.table.pureName}`] = recreate;
|
||||
}
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const oldObject: TableInfo = op.oldObject;
|
||||
@@ -371,6 +443,9 @@ export function runAlterOperation(op: AlterOperation, processor: AlterProcessor)
|
||||
case 'dropColumn':
|
||||
processor.dropColumn(op.oldObject);
|
||||
break;
|
||||
case 'dropTable':
|
||||
processor.dropTable(op.oldObject);
|
||||
break;
|
||||
case 'changeConstraint':
|
||||
processor.changeConstraint(op.oldObject, op.newObject);
|
||||
break;
|
||||
@@ -389,9 +464,16 @@ export function runAlterOperation(op: AlterOperation, processor: AlterProcessor)
|
||||
case 'renameConstraint':
|
||||
processor.renameConstraint(op.object, op.newName);
|
||||
break;
|
||||
case 'createSqlObject':
|
||||
processor.createSqlObject(op.newObject);
|
||||
break;
|
||||
case 'dropSqlObject':
|
||||
processor.dropSqlObject(op.oldObject);
|
||||
break;
|
||||
case 'recreateTable':
|
||||
{
|
||||
const newTable = _.cloneDeep(op.table);
|
||||
const oldTable = generateTablePairingId(op.table);
|
||||
const newTable = _.cloneDeep(oldTable);
|
||||
const newDb = DatabaseAnalyser.createEmptyStructure();
|
||||
newDb.tables.push(newTable);
|
||||
// console.log('////////////////////////////newTable1', newTable);
|
||||
@@ -399,7 +481,7 @@ export function runAlterOperation(op: AlterOperation, processor: AlterProcessor)
|
||||
// console.log('////////////////////////////op.operations', op.operations);
|
||||
// console.log('////////////////////////////op.table', op.table);
|
||||
// console.log('////////////////////////////newTable2', newTable);
|
||||
processor.recreateTable(op.table, newTable);
|
||||
processor.recreateTable(oldTable, newTable);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
ColumnInfo,
|
||||
ConstraintInfo,
|
||||
@@ -8,6 +9,7 @@ import {
|
||||
IndexInfo,
|
||||
CheckInfo,
|
||||
UniqueInfo,
|
||||
SqlObjectInfo,
|
||||
} from '../../types';
|
||||
|
||||
export class DatabaseInfoAlterProcessor {
|
||||
@@ -18,7 +20,18 @@ export class DatabaseInfoAlterProcessor {
|
||||
}
|
||||
|
||||
dropTable(table: TableInfo) {
|
||||
this.db.tables = this.db.tables.filter(x => x.pureName != table.pureName && x.schemaName != table.schemaName);
|
||||
_.remove(this.db.tables, x => x.pureName == table.pureName && x.schemaName == table.schemaName);
|
||||
}
|
||||
|
||||
createSqlObject(obj: SqlObjectInfo) {
|
||||
this.db[obj.objectTypeField].push(obj);
|
||||
}
|
||||
|
||||
dropSqlObject(obj: SqlObjectInfo) {
|
||||
_.remove(
|
||||
this.db[obj.objectTypeField] as SqlObjectInfo[],
|
||||
x => x.pureName == obj.pureName && x.schemaName == obj.schemaName
|
||||
);
|
||||
}
|
||||
|
||||
createColumn(column: ColumnInfo) {
|
||||
@@ -33,7 +46,7 @@ export class DatabaseInfoAlterProcessor {
|
||||
|
||||
dropColumn(column: ColumnInfo) {
|
||||
const table = this.db.tables.find(x => x.pureName == column.pureName && x.schemaName == column.schemaName);
|
||||
table.columns = table.columns.filter(x => x.columnName != column.columnName);
|
||||
_.remove(table.columns, x => x.columnName == column.columnName);
|
||||
}
|
||||
|
||||
createConstraint(constraint: ConstraintInfo) {
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
import { ColumnInfo, DatabaseInfo, EngineDriver, NamedObjectInfo, TableInfo } from 'dbgate-types';
|
||||
import {
|
||||
ColumnInfo,
|
||||
ConstraintInfo,
|
||||
DatabaseInfo,
|
||||
EngineDriver,
|
||||
NamedObjectInfo,
|
||||
SqlDialect,
|
||||
TableInfo,
|
||||
} from 'dbgate-types';
|
||||
import _ from 'lodash';
|
||||
import uuidv1 from 'uuid/v1';
|
||||
import { AlterPlan } from './alterPlan';
|
||||
import stableStringify from 'json-stable-stringify';
|
||||
import { isArray } from 'lodash';
|
||||
|
||||
type DbDiffSchemaMode = 'strict' | 'ignore' | 'ignoreImplicit';
|
||||
|
||||
@@ -21,19 +32,23 @@ export function generateTablePairingId(table: TableInfo): TableInfo {
|
||||
if (!table.pairingId) {
|
||||
return {
|
||||
...table,
|
||||
columns: table.columns.map(col => ({
|
||||
columns: table.columns?.map(col => ({
|
||||
...col,
|
||||
pairingId: col.pairingId || uuidv1(),
|
||||
})),
|
||||
foreignKeys: table.foreignKeys.map(cnt => ({
|
||||
foreignKeys: table.foreignKeys?.map(cnt => ({
|
||||
...cnt,
|
||||
pairingId: cnt.pairingId || uuidv1(),
|
||||
})),
|
||||
checks: table.checks.map(cnt => ({
|
||||
checks: table.checks?.map(cnt => ({
|
||||
...cnt,
|
||||
pairingId: cnt.pairingId || uuidv1(),
|
||||
})),
|
||||
indexes: table.indexes.map(cnt => ({
|
||||
indexes: table.indexes?.map(cnt => ({
|
||||
...cnt,
|
||||
pairingId: cnt.pairingId || uuidv1(),
|
||||
})),
|
||||
uniques: table.uniques?.map(cnt => ({
|
||||
...cnt,
|
||||
pairingId: cnt.pairingId || uuidv1(),
|
||||
})),
|
||||
@@ -43,11 +58,27 @@ export function generateTablePairingId(table: TableInfo): TableInfo {
|
||||
return table;
|
||||
}
|
||||
|
||||
function generateObjectPairingId(obj) {
|
||||
if (obj.objectTypeField)
|
||||
return {
|
||||
...obj,
|
||||
pairingId: obj.pairingId || uuidv1(),
|
||||
};
|
||||
return obj;
|
||||
}
|
||||
|
||||
export function generateDbPairingId(db: DatabaseInfo): DatabaseInfo {
|
||||
if (!db) return db;
|
||||
|
||||
return {
|
||||
...db,
|
||||
tables: (db.tables || []).map(generateTablePairingId),
|
||||
// ..._.mapValues(db, v => (_.isArray(v) ? v.map(generateObjectPairingId) : v)),
|
||||
tables: db.tables?.map(generateTablePairingId),
|
||||
views: db.views?.map(generateObjectPairingId),
|
||||
procedures: db.procedures?.map(generateObjectPairingId),
|
||||
functions: db.functions?.map(generateObjectPairingId),
|
||||
triggers: db.triggers?.map(generateObjectPairingId),
|
||||
matviews: db.matviews?.map(generateObjectPairingId),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -178,6 +209,10 @@ export function testEqualColumns(
|
||||
return true;
|
||||
}
|
||||
|
||||
function testEqualConstraints(a: ConstraintInfo, b: ConstraintInfo, opts: DbDiffOptions = {}) {
|
||||
return stableStringify(a) == stableStringify(b);
|
||||
}
|
||||
|
||||
export function testEqualTypes(a: ColumnInfo, b: ColumnInfo, opts: DbDiffOptions = {}) {
|
||||
if (a.dataType != b.dataType) {
|
||||
// opts.DiffLogger.Trace("Column {0}, {1}: different types: {2}; {3}", a, b, a.DataType, b.DataType);
|
||||
@@ -210,6 +245,7 @@ function getTableConstraints(table: TableInfo) {
|
||||
if (table.primaryKey) res.push(table.primaryKey);
|
||||
if (table.foreignKeys) res.push(...table.foreignKeys);
|
||||
if (table.indexes) res.push(...table.indexes);
|
||||
if (table.uniques) res.push(...table.uniques);
|
||||
if (table.checks) res.push(...table.checks);
|
||||
return res;
|
||||
}
|
||||
@@ -265,6 +301,15 @@ function planAlterTable(plan: AlterPlan, oldTable: TableInfo, newTable: TableInf
|
||||
}
|
||||
});
|
||||
|
||||
constraintPairs
|
||||
.filter(x => x[0] && x[1])
|
||||
.forEach(x => {
|
||||
if (!testEqualConstraints(x[0], x[1], opts)) {
|
||||
// console.log('PLAN CHANGE CONSTRAINT', x[0], x[1]);
|
||||
plan.changeConstraint(x[0], x[1]);
|
||||
}
|
||||
});
|
||||
|
||||
constraintPairs.filter(x => x[0] == null).forEach(x => plan.createConstraint(x[1]));
|
||||
}
|
||||
|
||||
@@ -285,15 +330,75 @@ export function createAlterTablePlan(
|
||||
return plan;
|
||||
}
|
||||
|
||||
export function createAlterDatabasePlan(
|
||||
oldDb: DatabaseInfo,
|
||||
newDb: DatabaseInfo,
|
||||
opts: DbDiffOptions,
|
||||
db: DatabaseInfo,
|
||||
driver: EngineDriver
|
||||
): AlterPlan {
|
||||
const plan = new AlterPlan(db, driver.dialect);
|
||||
|
||||
for (const objectTypeField of ['tables', 'views', 'procedures', 'matviews', 'functions']) {
|
||||
for (const oldobj of oldDb[objectTypeField] || []) {
|
||||
const newobj = (newDb[objectTypeField] || []).find(x => x.pairingId == oldobj.pairingId);
|
||||
if (objectTypeField == 'tables') {
|
||||
if (newobj == null) plan.dropTable(oldobj);
|
||||
else planAlterTable(plan, oldobj, newobj, opts);
|
||||
} else {
|
||||
if (newobj == null) plan.dropSqlObject(oldobj);
|
||||
else if (newobj.createSql != oldobj.createSql) {
|
||||
plan.recreates.sqlObjects += 1;
|
||||
plan.dropSqlObject(oldobj);
|
||||
plan.createSqlObject(newobj);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const newobj of newDb[objectTypeField] || []) {
|
||||
const oldobj = (oldDb[objectTypeField] || []).find(x => x.pairingId == newobj.pairingId);
|
||||
if (objectTypeField == 'tables') {
|
||||
if (newobj == null) plan.createTable(newobj);
|
||||
} else {
|
||||
if (newobj == null) plan.createSqlObject(newobj);
|
||||
}
|
||||
}
|
||||
}
|
||||
plan.transformPlan();
|
||||
return plan;
|
||||
}
|
||||
|
||||
export function getAlterTableScript(
|
||||
oldTable: TableInfo,
|
||||
newTable: TableInfo,
|
||||
opts: DbDiffOptions,
|
||||
db: DatabaseInfo,
|
||||
driver: EngineDriver
|
||||
): string {
|
||||
) {
|
||||
const plan = createAlterTablePlan(oldTable, newTable, opts, db, driver);
|
||||
const dmp = driver.createDumper();
|
||||
plan.run(dmp );
|
||||
return dmp.s;
|
||||
if (!driver.dialect.disableExplicitTransaction) dmp.beginTransaction();
|
||||
plan.run(dmp);
|
||||
if (!driver.dialect.disableExplicitTransaction) dmp.commitTransaction();
|
||||
return {
|
||||
sql: dmp.s,
|
||||
recreates: plan.recreates,
|
||||
};
|
||||
}
|
||||
|
||||
export function getAlterDatabaseScript(
|
||||
oldDb: DatabaseInfo,
|
||||
newDb: DatabaseInfo,
|
||||
opts: DbDiffOptions,
|
||||
db: DatabaseInfo,
|
||||
driver: EngineDriver
|
||||
) {
|
||||
const plan = createAlterDatabasePlan(oldDb, newDb, opts, db, driver);
|
||||
const dmp = driver.createDumper();
|
||||
if (!driver.dialect.disableExplicitTransaction) dmp.beginTransaction();
|
||||
plan.run(dmp);
|
||||
if (!driver.dialect.disableExplicitTransaction) dmp.commitTransaction();
|
||||
return {
|
||||
sql: dmp.s,
|
||||
recreates: plan.recreates,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ export const driverBase = {
|
||||
dumperClass: SqlDumper,
|
||||
dialect,
|
||||
|
||||
async analyseFull(pool) {
|
||||
const analyser = new this.analyserClass(pool, this);
|
||||
async analyseFull(pool, version) {
|
||||
const analyser = new this.analyserClass(pool, this, version);
|
||||
return analyser.fullAnalysis();
|
||||
},
|
||||
async analyseSingleObject(pool, name, typeField = 'tables') {
|
||||
@@ -28,8 +28,8 @@ export const driverBase = {
|
||||
analyseSingleTable(pool, name) {
|
||||
return this.analyseSingleObject(pool, name, 'tables');
|
||||
},
|
||||
async analyseIncremental(pool, structure) {
|
||||
const analyser = new this.analyserClass(pool, this);
|
||||
async analyseIncremental(pool, structure, version) {
|
||||
const analyser = new this.analyserClass(pool, this, version);
|
||||
return analyser.incrementalAnalysis(structure);
|
||||
},
|
||||
createDumper() {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ColumnInfo, DatabaseInfo, DatabaseInfoObjects, TableInfo } from 'dbgate-types';
|
||||
import _ from 'lodash';
|
||||
import { ColumnInfo, ColumnReference, DatabaseInfo, DatabaseInfoObjects, SqlDialect, TableInfo } from 'dbgate-types';
|
||||
|
||||
export function fullNameFromString(name) {
|
||||
const m = name.match(/\[([^\]]+)\]\.\[([^\]]+)\]/);
|
||||
@@ -68,3 +69,28 @@ export function makeUniqueColumnNames(res: ColumnInfo[]) {
|
||||
usedNames.add(res[i].columnName);
|
||||
}
|
||||
}
|
||||
|
||||
function columnsConstraintName(prefix: string, table: TableInfo, columns: ColumnReference[]) {
|
||||
return `${prefix}_${table.pureName}_${columns.map(x => x.columnName.replace(' ', '_')).join('_')}`;
|
||||
}
|
||||
|
||||
export function fillConstraintNames(table: TableInfo, dialect: SqlDialect) {
|
||||
if (!table) return table;
|
||||
const res = _.cloneDeep(table);
|
||||
if (res.primaryKey && !res.primaryKey.constraintName && !dialect.anonymousPrimaryKey) {
|
||||
res.primaryKey.constraintName = `PK_${res.pureName}`;
|
||||
}
|
||||
for (const fk of res.foreignKeys || []) {
|
||||
if (fk.constraintName) continue;
|
||||
fk.constraintName = columnsConstraintName('FK', res, fk.columns);
|
||||
}
|
||||
for (const ix of res.indexes || []) {
|
||||
if (ix.constraintName) continue;
|
||||
ix.constraintName = columnsConstraintName('IX', res, ix.columns);
|
||||
}
|
||||
for (const uq of res.uniques || []) {
|
||||
if (uq.constraintName) continue;
|
||||
uq.constraintName = columnsConstraintName('UQ', res, uq.columns);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import uuidv1 from 'uuid/v1';
|
||||
import _omit from 'lodash/omit';
|
||||
import { ColumnInfo, ConstraintInfo, ForeignKeyInfo, PrimaryKeyInfo, TableInfo } from 'dbgate-types';
|
||||
import {
|
||||
ColumnInfo,
|
||||
ConstraintInfo,
|
||||
ForeignKeyInfo,
|
||||
IndexInfo,
|
||||
PrimaryKeyInfo,
|
||||
TableInfo,
|
||||
UniqueInfo,
|
||||
} from 'dbgate-types';
|
||||
import _ from 'lodash';
|
||||
|
||||
export interface EditorColumnInfo extends ColumnInfo {
|
||||
isPrimaryKey?: boolean;
|
||||
@@ -8,8 +17,8 @@ export interface EditorColumnInfo extends ColumnInfo {
|
||||
|
||||
export function fillEditorColumnInfo(column: ColumnInfo, table: TableInfo): EditorColumnInfo {
|
||||
return {
|
||||
isPrimaryKey: !!(table?.primaryKey && table.primaryKey.columns.find(x => x.columnName == column.columnName)),
|
||||
dataType: column ? undefined : 'int',
|
||||
isPrimaryKey: !!table?.primaryKey?.columns?.find(x => x.columnName == column.columnName),
|
||||
dataType: _.isEmpty(column) ? 'int' : undefined,
|
||||
...column,
|
||||
};
|
||||
}
|
||||
@@ -118,6 +127,26 @@ export function editorAddConstraint(table: TableInfo, constraint: ConstraintInfo
|
||||
];
|
||||
}
|
||||
|
||||
if (constraint.constraintType == 'index') {
|
||||
res.indexes = [
|
||||
...(res.indexes || []),
|
||||
{
|
||||
pairingId: uuidv1(),
|
||||
...constraint,
|
||||
} as IndexInfo,
|
||||
];
|
||||
}
|
||||
|
||||
if (constraint.constraintType == 'unique') {
|
||||
res.uniques = [
|
||||
...(res.uniques || []),
|
||||
{
|
||||
pairingId: uuidv1(),
|
||||
...constraint,
|
||||
} as UniqueInfo,
|
||||
];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -139,6 +168,14 @@ export function editorModifyConstraint(table: TableInfo, constraint: ConstraintI
|
||||
);
|
||||
}
|
||||
|
||||
if (constraint.constraintType == 'index') {
|
||||
res.indexes = table.indexes.map(fk => (fk.pairingId == constraint.pairingId ? { ...fk, ...constraint } : fk));
|
||||
}
|
||||
|
||||
if (constraint.constraintType == 'unique') {
|
||||
res.uniques = table.uniques.map(fk => (fk.pairingId == constraint.pairingId ? { ...fk, ...constraint } : fk));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -155,5 +192,13 @@ export function editorDeleteConstraint(table: TableInfo, constraint: ConstraintI
|
||||
res.foreignKeys = table.foreignKeys.filter(x => x.pairingId != constraint.pairingId);
|
||||
}
|
||||
|
||||
if (constraint.constraintType == 'index') {
|
||||
res.indexes = table.indexes.filter(x => x.pairingId != constraint.pairingId);
|
||||
}
|
||||
|
||||
if (constraint.constraintType == 'unique') {
|
||||
res.uniques = table.uniques.filter(x => x.pairingId != constraint.pairingId);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DatabaseInfo } from 'dbgate-types';
|
||||
import { DatabaseInfo, TableInfo } from 'dbgate-types';
|
||||
import _flatten from 'lodash/flatten';
|
||||
|
||||
export function addTableDependencies(db: DatabaseInfo): DatabaseInfo {
|
||||
@@ -12,50 +12,54 @@ export function addTableDependencies(db: DatabaseInfo): DatabaseInfo {
|
||||
};
|
||||
}
|
||||
|
||||
function fillTableExtendedInfo(db: DatabaseInfo): DatabaseInfo {
|
||||
export function extendTableInfo(table: TableInfo): TableInfo {
|
||||
return {
|
||||
...table,
|
||||
objectTypeField: 'tables',
|
||||
columns: (table.columns || []).map(column => ({
|
||||
pureName: table.pureName,
|
||||
schemaName: table.schemaName,
|
||||
...column,
|
||||
})),
|
||||
primaryKey: table.primaryKey
|
||||
? {
|
||||
...table.primaryKey,
|
||||
pureName: table.pureName,
|
||||
schemaName: table.schemaName,
|
||||
constraintType: 'primaryKey',
|
||||
}
|
||||
: undefined,
|
||||
foreignKeys: (table.foreignKeys || []).map(cnt => ({
|
||||
...cnt,
|
||||
pureName: table.pureName,
|
||||
schemaName: table.schemaName,
|
||||
constraintType: 'foreignKey',
|
||||
})),
|
||||
indexes: (table.indexes || []).map(cnt => ({
|
||||
...cnt,
|
||||
pureName: table.pureName,
|
||||
schemaName: table.schemaName,
|
||||
constraintType: 'index',
|
||||
})),
|
||||
checks: (table.checks || []).map(cnt => ({
|
||||
...cnt,
|
||||
pureName: table.pureName,
|
||||
schemaName: table.schemaName,
|
||||
constraintType: 'check',
|
||||
})),
|
||||
uniques: (table.uniques || []).map(cnt => ({
|
||||
...cnt,
|
||||
pureName: table.pureName,
|
||||
schemaName: table.schemaName,
|
||||
constraintType: 'unique',
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
function fillDatabaseExtendedInfo(db: DatabaseInfo): DatabaseInfo {
|
||||
return {
|
||||
...db,
|
||||
tables: (db.tables || []).map(obj => ({
|
||||
...obj,
|
||||
objectTypeField: 'tables',
|
||||
columns: (obj.columns || []).map(column => ({
|
||||
pureName: obj.pureName,
|
||||
schemaName: obj.schemaName,
|
||||
...column,
|
||||
})),
|
||||
primaryKey: obj.primaryKey
|
||||
? {
|
||||
...obj.primaryKey,
|
||||
pureName: obj.pureName,
|
||||
schemaName: obj.schemaName,
|
||||
constraintType: 'primaryKey',
|
||||
}
|
||||
: undefined,
|
||||
foreignKeys: (obj.foreignKeys || []).map(cnt => ({
|
||||
...cnt,
|
||||
pureName: obj.pureName,
|
||||
schemaName: obj.schemaName,
|
||||
constraintType: 'foreignKey',
|
||||
})),
|
||||
indexes: (obj.indexes || []).map(cnt => ({
|
||||
...cnt,
|
||||
pureName: obj.pureName,
|
||||
schemaName: obj.schemaName,
|
||||
constraintType: 'index',
|
||||
})),
|
||||
checks: (obj.checks || []).map(cnt => ({
|
||||
...cnt,
|
||||
pureName: obj.pureName,
|
||||
schemaName: obj.schemaName,
|
||||
constraintType: 'check',
|
||||
})),
|
||||
uniques: (obj.uniques || []).map(cnt => ({
|
||||
...cnt,
|
||||
pureName: obj.pureName,
|
||||
schemaName: obj.schemaName,
|
||||
constraintType: 'unique',
|
||||
})),
|
||||
})),
|
||||
tables: (db.tables || []).map(extendTableInfo),
|
||||
collections: (db.collections || []).map(obj => ({
|
||||
...obj,
|
||||
objectTypeField: 'collections',
|
||||
@@ -84,5 +88,5 @@ function fillTableExtendedInfo(db: DatabaseInfo): DatabaseInfo {
|
||||
}
|
||||
|
||||
export function extendDatabaseInfo(db: DatabaseInfo): DatabaseInfo {
|
||||
return fillTableExtendedInfo(addTableDependencies(db));
|
||||
return fillDatabaseExtendedInfo(addTableDependencies(db));
|
||||
}
|
||||
|
||||
Vendored
+3
-1
@@ -1,4 +1,4 @@
|
||||
import { ColumnInfo, ConstraintInfo, TableInfo } from './dbinfo';
|
||||
import { ColumnInfo, ConstraintInfo, TableInfo, SqlObjectInfo } from './dbinfo';
|
||||
|
||||
export interface AlterProcessor {
|
||||
createTable(table: TableInfo);
|
||||
@@ -13,4 +13,6 @@ export interface AlterProcessor {
|
||||
renameColumn(column: ColumnInfo, newName: string);
|
||||
renameConstraint(constraint: ConstraintInfo, newName: string);
|
||||
recreateTable(oldTable: TableInfo, newTable: TableInfo);
|
||||
createSqlObject(obj: SqlObjectInfo);
|
||||
dropSqlObject(obj: SqlObjectInfo);
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -62,6 +62,7 @@ export interface DatabaseObjectInfo extends NamedObjectInfo {
|
||||
createDate?: string;
|
||||
modifyDate?: string;
|
||||
hashCode?: string;
|
||||
objectTypeField?: string;
|
||||
}
|
||||
|
||||
export interface SqlObjectInfo extends DatabaseObjectInfo {
|
||||
|
||||
Vendored
+3
@@ -30,4 +30,7 @@ export interface SqlDialect {
|
||||
dropUnique?: boolean;
|
||||
createCheck?: boolean;
|
||||
dropCheck?: boolean;
|
||||
|
||||
dropReferencesWhenDropTable?: boolean;
|
||||
disableExplicitTransaction?: boolean;
|
||||
}
|
||||
|
||||
Vendored
+2
@@ -17,4 +17,6 @@ export interface SqlDumper extends AlterProcessor {
|
||||
|
||||
endCommand();
|
||||
allowIdentityInsert(table: NamedObjectInfo, allow: boolean);
|
||||
beginTransaction();
|
||||
commitTransaction();
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -67,8 +67,8 @@ export interface EngineDriver {
|
||||
name: string;
|
||||
}[]
|
||||
>;
|
||||
analyseFull(pool: any): Promise<DatabaseInfo>;
|
||||
analyseIncremental(pool: any, structure: DatabaseInfo): Promise<DatabaseInfo>;
|
||||
analyseFull(pool: any, serverVersion): Promise<DatabaseInfo>;
|
||||
analyseIncremental(pool: any, structure: DatabaseInfo, serverVersion): Promise<DatabaseInfo>;
|
||||
dialect: SqlDialect;
|
||||
dialectByVersion(version): SqlDialect;
|
||||
createDumper(): SqlDumper;
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
"devDependencies": {
|
||||
"@ant-design/colors": "^5.0.0",
|
||||
"@mdi/font": "^5.9.55",
|
||||
"@rollup/plugin-commonjs": "^17.0.0",
|
||||
"@rollup/plugin-node-resolve": "^11.0.0",
|
||||
"@rollup/plugin-replace": "^2.4.1",
|
||||
"@rollup/plugin-typescript": "^6.0.0",
|
||||
"@rollup/plugin-commonjs": "^20.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.5",
|
||||
"@rollup/plugin-replace": "^3.0.0",
|
||||
"@rollup/plugin-typescript": "^8.2.5",
|
||||
"@tsconfig/svelte": "^1.0.0",
|
||||
"ace-builds": "^1.4.8",
|
||||
"chart.js": "^2.9.4",
|
||||
@@ -32,7 +32,7 @@
|
||||
"lodash": "^4.17.21",
|
||||
"randomcolor": "^0.6.2",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"rollup": "^2.3.4",
|
||||
"rollup": "^2.57.0",
|
||||
"rollup-plugin-copy": "^3.3.0",
|
||||
"rollup-plugin-css-only": "^3.1.0",
|
||||
"rollup-plugin-livereload": "^2.0.0",
|
||||
@@ -41,13 +41,13 @@
|
||||
"sirv-cli": "^1.0.0",
|
||||
"socket.io-client": "^2.3.0",
|
||||
"sql-formatter": "^2.3.3",
|
||||
"svelte": "^3.35.0",
|
||||
"svelte": "^3.43.0",
|
||||
"svelte-check": "^1.0.0",
|
||||
"svelte-markdown": "^0.1.4",
|
||||
"svelte-preprocess": "^4.0.0",
|
||||
"svelte-preprocess": "^4.9.5",
|
||||
"svelte-select": "^3.17.0",
|
||||
"tslib": "^2.0.0",
|
||||
"typescript": "^3.9.3",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.4.3",
|
||||
"uuid": "^3.4.0"
|
||||
}
|
||||
}
|
||||
@@ -24,10 +24,13 @@
|
||||
import dragDropFileTarget from './utility/dragDropFileTarget';
|
||||
|
||||
$: currentThemeType = $currentThemeDefinition?.themeType == 'dark' ? 'theme-type-dark' : 'theme-type-light';
|
||||
|
||||
</script>
|
||||
|
||||
<div class={`${$currentTheme} ${currentThemeType} root`} use:dragDropFileTarget>
|
||||
<div
|
||||
class={`${$currentTheme} ${currentThemeType} root`}
|
||||
use:dragDropFileTarget
|
||||
on:contextmenu={e => e.preventDefault()}
|
||||
>
|
||||
<div class="iconbar">
|
||||
<WidgetIconPanel />
|
||||
</div>
|
||||
@@ -68,7 +71,7 @@
|
||||
<DragAndDropFileTarget />
|
||||
{/if}
|
||||
<div class="snackbar-container">
|
||||
{#each $openedSnackbars as snackbar(snackbar.id)}
|
||||
{#each $openedSnackbars as snackbar (snackbar.id)}
|
||||
<Snackbar {...snackbar} />
|
||||
{/each}
|
||||
</div>
|
||||
@@ -153,5 +156,4 @@
|
||||
right: 0;
|
||||
bottom: var(--dim-statusbar-height);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -52,8 +52,9 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#each filtered as item}
|
||||
{#each items as item}
|
||||
<AppObjectListItem
|
||||
isHidden={!item.isMatched}
|
||||
{...$$restProps}
|
||||
{module}
|
||||
data={item.data}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
import { asyncFilter } from '../utility/common';
|
||||
import AppObjectGroup from './AppObjectGroup.svelte';
|
||||
|
||||
import AppObjectListItem from './AppObjectListItem.svelte';
|
||||
@@ -16,11 +17,36 @@
|
||||
|
||||
export let groupFunc = undefined;
|
||||
|
||||
$: filtered = list.filter(data => {
|
||||
const matcher = module.createMatcher && module.createMatcher(data);
|
||||
if (matcher && !matcher(filter)) return false;
|
||||
return true;
|
||||
});
|
||||
$: filtered = !groupFunc
|
||||
? list.filter(data => {
|
||||
const matcher = module.createMatcher && module.createMatcher(data);
|
||||
if (matcher && !matcher(filter)) return false;
|
||||
return true;
|
||||
})
|
||||
: null;
|
||||
|
||||
$: childrenMatched = !groupFunc
|
||||
? list.filter(data => {
|
||||
const matcher = module.createChildMatcher && module.createChildMatcher(data);
|
||||
if (matcher && !matcher(filter)) return false;
|
||||
return true;
|
||||
})
|
||||
: null;
|
||||
|
||||
|
||||
// let filtered = [];
|
||||
|
||||
// $: {
|
||||
// if (!groupFunc) {
|
||||
// asyncFilter(list, async data => {
|
||||
// const matcher = module.createMatcher && module.createMatcher(data);
|
||||
// if (matcher && !(await matcher(filter))) return false;
|
||||
// return true;
|
||||
// }).then(res => {
|
||||
// filtered = res;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
$: listGrouped = groupFunc
|
||||
? _.compact(
|
||||
@@ -34,7 +60,6 @@
|
||||
: null;
|
||||
|
||||
$: groups = groupFunc ? _.groupBy(listGrouped, 'group') : null;
|
||||
|
||||
</script>
|
||||
|
||||
{#if groupFunc}
|
||||
@@ -49,11 +74,13 @@
|
||||
{checkedObjectsStore}
|
||||
{groupFunc}
|
||||
{disableContextMenu}
|
||||
{filter}
|
||||
/>
|
||||
{/each}
|
||||
{:else}
|
||||
{#each filtered as data}
|
||||
{#each list as data}
|
||||
<AppObjectListItem
|
||||
isHidden={!filtered.includes(data)}
|
||||
{module}
|
||||
{subItemsComponent}
|
||||
{expandOnClick}
|
||||
@@ -63,6 +90,8 @@
|
||||
{expandIconFunc}
|
||||
{checkedObjectsStore}
|
||||
{disableContextMenu}
|
||||
{filter}
|
||||
isExpandedBySearch={childrenMatched.includes(data)}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
import { tick } from 'svelte';
|
||||
import { plusExpandIcon } from '../icons/expandIcons';
|
||||
|
||||
export let isHidden;
|
||||
export let filter;
|
||||
export let module;
|
||||
export let data;
|
||||
export let subItemsComponent;
|
||||
@@ -18,6 +20,7 @@
|
||||
export let expandIconFunc = plusExpandIcon;
|
||||
export let checkedObjectsStore = null;
|
||||
export let disableContextMenu = false;
|
||||
export let isExpandedBySearch = false;
|
||||
|
||||
let isExpanded = false;
|
||||
|
||||
@@ -37,21 +40,23 @@
|
||||
$: if (!expandable && isExpanded) isExpanded = false;
|
||||
</script>
|
||||
|
||||
<svelte:component
|
||||
this={module.default}
|
||||
{data}
|
||||
on:click={handleExpand}
|
||||
on:expand={handleExpandButton}
|
||||
expandIcon={getExpandIcon(expandable, subItemsComponent, isExpanded, expandIconFunc)}
|
||||
{checkedObjectsStore}
|
||||
{module}
|
||||
{disableContextMenu}
|
||||
/>
|
||||
{#if !isHidden}
|
||||
<svelte:component
|
||||
this={module.default}
|
||||
{data}
|
||||
on:click={handleExpand}
|
||||
on:expand={handleExpandButton}
|
||||
expandIcon={getExpandIcon(!isExpandedBySearch && expandable, subItemsComponent, isExpanded, expandIconFunc)}
|
||||
{checkedObjectsStore}
|
||||
{module}
|
||||
{disableContextMenu}
|
||||
/>
|
||||
|
||||
{#if isExpanded && subItemsComponent}
|
||||
<div class="subitems">
|
||||
<svelte:component this={subItemsComponent} {data} />
|
||||
</div>
|
||||
{#if (isExpanded || isExpandedBySearch) && subItemsComponent}
|
||||
<div class="subitems">
|
||||
<svelte:component this={subItemsComponent} {data} {filter} />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
|
||||
@@ -3,12 +3,37 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
|
||||
import { getColumnIcon } from '../elements/ColumnLabel.svelte';
|
||||
import { renameDatabaseObjectDialog, alterDatabaseDialog } from '../utility/alterDatabaseTools';
|
||||
|
||||
import AppObjectCore from './AppObjectCore.svelte';
|
||||
|
||||
export let data;
|
||||
|
||||
function handleRenameColumn() {
|
||||
renameDatabaseObjectDialog(data.conid, data.database, data.columnName, (db, newName) => {
|
||||
const tbl = db.tables.find(x => x.schemaName == data.schemaName && x.pureName == data.pureName);
|
||||
const col = tbl.columns.find(x => x.columnName == data.columnName);
|
||||
col.columnName = newName;
|
||||
});
|
||||
}
|
||||
|
||||
function handleDropColumn() {
|
||||
alterDatabaseDialog(data.conid, data.database, db => {
|
||||
const tbl = db.tables.find(x => x.schemaName == data.schemaName && x.pureName == data.pureName);
|
||||
_.remove(tbl.columns as any[], x => x.columnName == data.columnName);
|
||||
});
|
||||
}
|
||||
|
||||
function createMenu() {
|
||||
return [
|
||||
{ text: 'Rename column', onClick: handleRenameColumn },
|
||||
{ text: 'Drop column', onClick: handleDropColumn },
|
||||
];
|
||||
}
|
||||
|
||||
$: extInfo = data.foreignKey ? `${data.dataType} -> ${data.foreignKey.refTableName}` : data.dataType;
|
||||
</script>
|
||||
|
||||
@@ -18,5 +43,6 @@
|
||||
title={data.columnName}
|
||||
{extInfo}
|
||||
icon={getColumnIcon(data, true)}
|
||||
menu={createMenu}
|
||||
disableHover
|
||||
/>
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
<script context="module">
|
||||
export const extractKey = data => data._id;
|
||||
export const createMatcher = ({ displayName, server }) => filter => filterName(filter, displayName, server);
|
||||
export const createMatcher = props => filter => {
|
||||
const { _id, displayName, server } = props;
|
||||
const databases = getLocalStorage(`database_list_${_id}`) || [];
|
||||
return filterName(filter, displayName, server, ...databases.map(x => x.name));
|
||||
};
|
||||
export const createChildMatcher = props => filter => {
|
||||
if (!filter) return false;
|
||||
const { _id } = props;
|
||||
const databases = getLocalStorage(`database_list_${_id}`) || [];
|
||||
return filterName(filter, ...databases.map(x => x.name));
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
import AppObjectCore from './AppObjectCore.svelte';
|
||||
import { currentDatabase, extensions, getCurrentConfig, openedConnections } from '../stores';
|
||||
import { currentDatabase, extensions, getCurrentConfig, getOpenedConnections, openedConnections } from '../stores';
|
||||
import axiosInstance from '../utility/axiosInstance';
|
||||
import { filterName } from 'dbgate-tools';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
@@ -17,6 +27,8 @@
|
||||
import { getDatabaseMenuItems } from './DatabaseAppObject.svelte';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import getConnectionLabel from '../utility/getConnectionLabel';
|
||||
import { getDatabaseList } from '../utility/metadataLoaders';
|
||||
import { getLocalStorage } from '../utility/storageCache';
|
||||
|
||||
export let data;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
export const createMatcher = ({ pureName }) => filter => filterName(filter, pureName);
|
||||
const electron = getElectron();
|
||||
|
||||
const icons = {
|
||||
export const databaseObjectIcons = {
|
||||
tables: 'img table',
|
||||
collections: 'img collection',
|
||||
views: 'img view',
|
||||
@@ -40,6 +40,14 @@
|
||||
label: 'Open structure',
|
||||
tab: 'TableStructureTab',
|
||||
},
|
||||
{
|
||||
label: 'Drop table',
|
||||
isDrop: true,
|
||||
},
|
||||
{
|
||||
label: 'Rename table',
|
||||
isRename: true,
|
||||
},
|
||||
{
|
||||
label: 'Query designer',
|
||||
isQueryDesigner: true,
|
||||
@@ -106,6 +114,10 @@
|
||||
label: 'Open structure',
|
||||
tab: 'TableStructureTab',
|
||||
},
|
||||
{
|
||||
label: 'Drop view',
|
||||
isDrop: true,
|
||||
},
|
||||
{
|
||||
label: 'Query designer',
|
||||
isQueryDesigner: true,
|
||||
@@ -167,6 +179,10 @@
|
||||
label: 'Open structure',
|
||||
tab: 'TableStructureTab',
|
||||
},
|
||||
{
|
||||
label: 'Drop view',
|
||||
isDrop: true,
|
||||
},
|
||||
{
|
||||
label: 'Query designer',
|
||||
isQueryDesigner: true,
|
||||
@@ -219,6 +235,10 @@
|
||||
},
|
||||
],
|
||||
procedures: [
|
||||
{
|
||||
label: 'Drop procedure',
|
||||
isDrop: true,
|
||||
},
|
||||
{
|
||||
label: 'SQL: CREATE PROCEDURE',
|
||||
scriptTemplate: 'CREATE OBJECT',
|
||||
@@ -241,6 +261,10 @@
|
||||
},
|
||||
],
|
||||
functions: [
|
||||
{
|
||||
label: 'Drop function',
|
||||
isDrop: true,
|
||||
},
|
||||
{
|
||||
label: 'SQL: CREATE FUNCTION',
|
||||
scriptTemplate: 'CREATE OBJECT',
|
||||
@@ -313,7 +337,7 @@
|
||||
{
|
||||
title: scriptTemplate ? 'Query #' : pureName,
|
||||
tooltip,
|
||||
icon: scriptTemplate ? 'img sql-file' : icons[objectTypeField],
|
||||
icon: scriptTemplate ? 'img sql-file' : databaseObjectIcons[objectTypeField],
|
||||
tabComponent: scriptTemplate ? 'QueryTab' : tabComponent,
|
||||
props: {
|
||||
schemaName,
|
||||
@@ -328,29 +352,8 @@
|
||||
{ forceNewTab }
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
import AppObjectCore from './AppObjectCore.svelte';
|
||||
import { currentDatabase, extensions, openedConnections } from '../stores';
|
||||
import openNewTab from '../utility/openNewTab';
|
||||
import { filterName } from 'dbgate-tools';
|
||||
import { getConnectionInfo } from '../utility/metadataLoaders';
|
||||
import fullDisplayName from '../utility/fullDisplayName';
|
||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import { findEngineDriver } from 'dbgate-tools';
|
||||
import uuidv1 from 'uuid/v1';
|
||||
import SqlGeneratorModal from '../modals/SqlGeneratorModal.svelte';
|
||||
import getConnectionLabel from '../utility/getConnectionLabel';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import { exportElectronFile } from '../utility/exportElectronFile';
|
||||
import createQuickExportMenu from '../utility/createQuickExportMenu';
|
||||
|
||||
export let data;
|
||||
|
||||
function handleClick(forceNewTab = false) {
|
||||
export function handleDatabaseObjectClick(data, forceNewTab = false) {
|
||||
const { schemaName, pureName, conid, database, objectTypeField } = data;
|
||||
|
||||
openDatabaseObjectDetail(
|
||||
@@ -366,19 +369,34 @@
|
||||
forceNewTab,
|
||||
null
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
// openNewTab({
|
||||
// title: data.pureName,
|
||||
// icon: 'img table',
|
||||
// tabComponent: 'TableDataTab',
|
||||
// props: {
|
||||
// schemaName,
|
||||
// pureName,
|
||||
// conid,
|
||||
// database,
|
||||
// objectTypeField,
|
||||
// },
|
||||
// });
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
import AppObjectCore from './AppObjectCore.svelte';
|
||||
import { currentDatabase, extensions, openedConnections } from '../stores';
|
||||
import openNewTab from '../utility/openNewTab';
|
||||
import { filterName, generateDbPairingId, getAlterDatabaseScript } from 'dbgate-tools';
|
||||
import { getConnectionInfo, getDatabaseInfo } from '../utility/metadataLoaders';
|
||||
import fullDisplayName from '../utility/fullDisplayName';
|
||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import { findEngineDriver } from 'dbgate-tools';
|
||||
import uuidv1 from 'uuid/v1';
|
||||
import SqlGeneratorModal from '../modals/SqlGeneratorModal.svelte';
|
||||
import getConnectionLabel from '../utility/getConnectionLabel';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import { exportElectronFile } from '../utility/exportElectronFile';
|
||||
import createQuickExportMenu from '../utility/createQuickExportMenu';
|
||||
import ConfirmSqlModal from '../modals/ConfirmSqlModal.svelte';
|
||||
import axiosInstance from '../utility/axiosInstance';
|
||||
import { alterDatabaseDialog, renameDatabaseObjectDialog } from '../utility/alterDatabaseTools';
|
||||
|
||||
export let data;
|
||||
|
||||
function handleClick(forceNewTab = false) {
|
||||
handleDatabaseObjectClick(data, forceNewTab);
|
||||
}
|
||||
|
||||
const getDriver = async () => {
|
||||
@@ -500,6 +518,22 @@
|
||||
conid: data.conid,
|
||||
database: data.database,
|
||||
});
|
||||
} else if (menu.isDrop) {
|
||||
const { conid, database } = data;
|
||||
alterDatabaseDialog(conid, database, db => {
|
||||
_.remove(
|
||||
db[data.objectTypeField] as any[],
|
||||
x => x.schemaName == data.schemaName && x.pureName == data.pureName
|
||||
);
|
||||
});
|
||||
} else if (menu.isRename) {
|
||||
const { conid, database } = data;
|
||||
renameDatabaseObjectDialog(conid, database, data.pureName, (db, newName) => {
|
||||
const obj = db[data.objectTypeField].find(
|
||||
x => x.schemaName == data.schemaName && x.pureName == data.pureName
|
||||
);
|
||||
obj.pureName = newName;
|
||||
});
|
||||
} else {
|
||||
openDatabaseObjectDetail(menu.tab, menu.scriptTemplate, data, menu.forceNewTab, menu.initialData);
|
||||
}
|
||||
@@ -514,7 +548,7 @@
|
||||
module={$$props.module}
|
||||
{data}
|
||||
title={data.schemaName ? `${data.schemaName}.${data.pureName}` : data.pureName}
|
||||
icon={icons[data.objectTypeField]}
|
||||
icon={databaseObjectIcons[data.objectTypeField]}
|
||||
menu={createMenu}
|
||||
on:click={() => handleClick()}
|
||||
on:middleclick={() => handleClick(true)}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
<AppObjectList
|
||||
list={(data.columns || []).map(col => ({
|
||||
...data,
|
||||
...col,
|
||||
foreignKey: findForeignKeyForColumn(data, col),
|
||||
}))}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { filterName } from 'dbgate-tools';
|
||||
import { useDatabaseList } from '../utility/metadataLoaders';
|
||||
import AppObjectList from './AppObjectList.svelte';
|
||||
import * as databaseAppObject from './DatabaseAppObject.svelte';
|
||||
|
||||
export let filter;
|
||||
export let data;
|
||||
|
||||
$: databases = useDatabaseList({ conid: data._id });
|
||||
</script>
|
||||
|
||||
<AppObjectList list={($databases || []).map(db => ({ ...db, connection: data }))} module={databaseAppObject} />
|
||||
<AppObjectList
|
||||
list={($databases || []).filter(x => filterName(filter, x.name)).map(db => ({ ...db, connection: data }))}
|
||||
module={databaseAppObject}
|
||||
/>
|
||||
|
||||
@@ -1,17 +1,62 @@
|
||||
<script context="module">
|
||||
const electron = getElectron();
|
||||
|
||||
registerCommand({
|
||||
id: 'commandPalette.show',
|
||||
category: 'Command palette',
|
||||
name: 'Show',
|
||||
toolbarName: 'Menu',
|
||||
toolbarName: 'Menu+Search',
|
||||
toolbarOrder: 0,
|
||||
keyText: 'F1',
|
||||
toolbar: true,
|
||||
showDisabled: true,
|
||||
icon: 'icon menu',
|
||||
onClick: () => visibleCommandPalette.set(true),
|
||||
testEnabled: () => !getVisibleCommandPalette(),
|
||||
onClick: () => visibleCommandPalette.set('menu'),
|
||||
testEnabled: () => getVisibleCommandPalette() != 'menu',
|
||||
});
|
||||
|
||||
registerCommand({
|
||||
id: 'database.search',
|
||||
category: 'Database',
|
||||
toolbarName: 'Database search',
|
||||
name: 'Search',
|
||||
keyText: electron ? 'Ctrl+P' : 'F3',
|
||||
onClick: () => visibleCommandPalette.set('database'),
|
||||
testEnabled: () => getVisibleCommandPalette() != 'database',
|
||||
});
|
||||
|
||||
function extractDbItems(db, dbConnectionInfo, connectionList) {
|
||||
const objectList = _.flatten(
|
||||
['tables', 'collections', 'views', 'matviews', 'procedures', 'functions'].map(objectTypeField =>
|
||||
_.sortBy(
|
||||
((db || {})[objectTypeField] || []).map(obj => ({
|
||||
text: obj.pureName,
|
||||
onClick: () => handleDatabaseObjectClick({ objectTypeField, ...dbConnectionInfo, ...obj }),
|
||||
icon: databaseObjectIcons[objectTypeField],
|
||||
})),
|
||||
['text']
|
||||
)
|
||||
)
|
||||
);
|
||||
const databaseList = [];
|
||||
for (const connection of connectionList || []) {
|
||||
const conid = connection._id;
|
||||
const databases = getLocalStorage(`database_list_${conid}`) || [];
|
||||
for (const db of databases) {
|
||||
databaseList.push({
|
||||
text: `${db.name} on ${getConnectionLabel(connection)}`,
|
||||
icon: 'img database',
|
||||
onClick: () => currentDatabase.set({ connection, name: db.name }),
|
||||
});
|
||||
}
|
||||
}
|
||||
return [..._.sortBy(databaseList, 'text'), ...objectList];
|
||||
|
||||
// return db?.tables?.map(table => ({
|
||||
// text: table.pureName,
|
||||
// onClick: () => handleDatabaseObjectClick({ ...dbinfo, ...table }),
|
||||
// }));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
@@ -19,9 +64,21 @@
|
||||
|
||||
import _ from 'lodash';
|
||||
import { onMount } from 'svelte';
|
||||
import { commands, commandsCustomized, getVisibleCommandPalette, visibleCommandPalette } from '../stores';
|
||||
import { databaseObjectIcons, handleDatabaseObjectClick } from '../appobj/DatabaseObjectAppObject.svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import {
|
||||
commands,
|
||||
commandsCustomized,
|
||||
currentDatabase,
|
||||
getVisibleCommandPalette,
|
||||
visibleCommandPalette,
|
||||
} from '../stores';
|
||||
import clickOutside from '../utility/clickOutside';
|
||||
import getConnectionLabel from '../utility/getConnectionLabel';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import keycodes from '../utility/keycodes';
|
||||
import { useConnectionList, useDatabaseInfo } from '../utility/metadataLoaders';
|
||||
import { getLocalStorage } from '../utility/storageCache';
|
||||
import registerCommand from './registerCommand';
|
||||
|
||||
let domInput;
|
||||
@@ -44,9 +101,17 @@
|
||||
'text'
|
||||
);
|
||||
|
||||
$: filteredItems = (parentCommand ? parentCommand.getSubCommands() : sortedComands).filter(
|
||||
x => !x.isGroupCommand && filterName(filter, x.text)
|
||||
);
|
||||
$: conid = _.get($currentDatabase, 'connection._id');
|
||||
$: database = _.get($currentDatabase, 'name');
|
||||
$: databaseInfo = useDatabaseInfo({ conid, database });
|
||||
$: connectionList = useConnectionList();
|
||||
|
||||
$: filteredItems = ($visibleCommandPalette == 'database'
|
||||
? extractDbItems($databaseInfo, { conid, database }, $connectionList)
|
||||
: parentCommand
|
||||
? parentCommand.getSubCommands()
|
||||
: sortedComands
|
||||
).filter(x => !x.isGroupCommand && filterName(filter, x.text));
|
||||
|
||||
function handleCommand(command) {
|
||||
if (command.getSubCommands) {
|
||||
@@ -55,7 +120,7 @@
|
||||
filter = '';
|
||||
selectedIndex = 0;
|
||||
} else {
|
||||
$visibleCommandPalette = false;
|
||||
$visibleCommandPalette = null;
|
||||
command.onClick();
|
||||
}
|
||||
}
|
||||
@@ -68,7 +133,7 @@
|
||||
e.stopPropagation();
|
||||
handleCommand(filteredItems[selectedIndex]);
|
||||
}
|
||||
if (e.keyCode == keycodes.escape) $visibleCommandPalette = false;
|
||||
if (e.keyCode == keycodes.escape) $visibleCommandPalette = null;
|
||||
|
||||
if (e.keyCode == keycodes.pageDown) selectedIndex = Math.min(selectedIndex + 15, filteredItems.length - 1);
|
||||
if (e.keyCode == keycodes.pageUp) selectedIndex = Math.max(selectedIndex - 15, 0);
|
||||
@@ -77,30 +142,65 @@
|
||||
$: if (domItems[selectedIndex]) domItems[selectedIndex].scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
||||
</script>
|
||||
|
||||
<div class="main" use:clickOutside on:clickOutside={() => ($visibleCommandPalette = false)}>
|
||||
<div class="search">
|
||||
<input
|
||||
type="text"
|
||||
bind:this={domInput}
|
||||
bind:value={filter}
|
||||
on:keydown={handleKeyDown}
|
||||
placeholder={parentCommand?.text || ''}
|
||||
/>
|
||||
<div
|
||||
class="main"
|
||||
use:clickOutside
|
||||
on:clickOutside={() => {
|
||||
$visibleCommandPalette = null;
|
||||
}}
|
||||
>
|
||||
<div class="pages">
|
||||
<div
|
||||
class="page"
|
||||
class:selected={$visibleCommandPalette == 'menu'}
|
||||
on:click={() => {
|
||||
$visibleCommandPalette = 'menu';
|
||||
domInput.focus();
|
||||
}}
|
||||
>
|
||||
<FontIcon icon="icon menu" /> Commands
|
||||
</div>
|
||||
<div
|
||||
class="page"
|
||||
class:selected={$visibleCommandPalette == 'database'}
|
||||
on:click={() => {
|
||||
$visibleCommandPalette = 'database';
|
||||
domInput.focus();
|
||||
}}
|
||||
>
|
||||
<FontIcon icon="icon database" /> Database
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
{#each filteredItems as command, index}
|
||||
<div
|
||||
class="command"
|
||||
class:selected={index == selectedIndex}
|
||||
on:click={() => handleCommand(command)}
|
||||
bind:this={domItems[index]}
|
||||
>
|
||||
<div>{command.text}</div>
|
||||
{#if command.keyText}
|
||||
<div class="shortcut">{command.keyText}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
<div class="mainInner">
|
||||
<div class="search">
|
||||
<input
|
||||
type="text"
|
||||
bind:this={domInput}
|
||||
bind:value={filter}
|
||||
on:keydown={handleKeyDown}
|
||||
placeholder={parentCommand?.text || ''}
|
||||
/>
|
||||
</div>
|
||||
<div class="content">
|
||||
{#each filteredItems as command, index}
|
||||
<div
|
||||
class="command"
|
||||
class:selected={index == selectedIndex}
|
||||
on:click={() => handleCommand(command)}
|
||||
bind:this={domItems[index]}
|
||||
>
|
||||
<div>
|
||||
{#if command.icon}
|
||||
<span class="mr-1"><FontIcon icon={command.icon} /></span>
|
||||
{/if}
|
||||
{command.text}
|
||||
</div>
|
||||
{#if command.keyText}
|
||||
<div class="shortcut">{command.keyText}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -108,6 +208,9 @@
|
||||
.main {
|
||||
width: 500px;
|
||||
background: var(--theme-bg-2);
|
||||
}
|
||||
|
||||
.mainInner {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
@@ -135,4 +238,19 @@
|
||||
.shortcut {
|
||||
background: var(--theme-bg-3);
|
||||
}
|
||||
|
||||
.pages {
|
||||
display: flex;
|
||||
}
|
||||
.page {
|
||||
padding: 5px;
|
||||
border: 1px solid var(--theme-border);
|
||||
cursor: pointer;
|
||||
}
|
||||
.page:hover {
|
||||
color: var(--theme-font-hover);
|
||||
}
|
||||
.page.selected {
|
||||
background: var(--theme-bg-1);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { currentDatabase, currentTheme, extensions, getVisibleToolbar, visibleToolbar } from '../stores';
|
||||
import { currentDatabase, currentTheme, extensions, getExtensions, getVisibleToolbar, visibleToolbar } from '../stores';
|
||||
import registerCommand from './registerCommand';
|
||||
import { derived, get } from 'svelte/store';
|
||||
import { ThemeDefinition } from 'dbgate-types';
|
||||
@@ -18,6 +18,7 @@ import { getCurrentConfig, getCurrentDatabase } from '../stores';
|
||||
import './recentDatabaseSwitch';
|
||||
import hasPermission from '../utility/hasPermission';
|
||||
import _ from 'lodash';
|
||||
import { findEngineDriver } from 'dbgate-tools';
|
||||
|
||||
const electron = getElectron();
|
||||
|
||||
@@ -112,6 +113,10 @@ registerCommand({
|
||||
name: 'Table',
|
||||
toolbar: true,
|
||||
toolbarName: 'New table',
|
||||
testEnabled: () => {
|
||||
const driver = findEngineDriver(get(currentDatabase)?.connection, getExtensions());
|
||||
return !!get(currentDatabase) && !driver?.dialect?.nosql;
|
||||
},
|
||||
onClick: () => {
|
||||
const $currentDatabase = get(currentDatabase);
|
||||
const connection = _.get($currentDatabase, 'connection') || {};
|
||||
@@ -261,6 +266,15 @@ if (hasPermission('settings/change')) {
|
||||
});
|
||||
}
|
||||
|
||||
if (electron) {
|
||||
registerCommand({
|
||||
id: 'file.exit',
|
||||
category: 'File',
|
||||
name: 'Exit',
|
||||
onClick: () => electron.remote.getCurrentWindow().close(),
|
||||
});
|
||||
}
|
||||
|
||||
export function registerFileCommands({
|
||||
idPrefix,
|
||||
category,
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
if (!filters[uniqueName]) continue;
|
||||
try {
|
||||
const ast = parseFilter(filters[uniqueName], 'mongo');
|
||||
// console.log('AST', ast);
|
||||
const cond = _.cloneDeepWith(ast, expr => {
|
||||
if (expr.__placeholder__) {
|
||||
return {
|
||||
@@ -112,7 +113,6 @@
|
||||
|
||||
return response.data.count;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -229,7 +229,6 @@
|
||||
|
||||
{ command: 'collectionDataGrid.export', tag: 'export' }
|
||||
);
|
||||
|
||||
</script>
|
||||
|
||||
<LoadingDataGridCore
|
||||
|
||||
@@ -4,15 +4,19 @@
|
||||
import ConstraintLabel from '../elements/ConstraintLabel.svelte';
|
||||
|
||||
import ObjectListControl from '../elements/ObjectListControl.svelte';
|
||||
import Link from './Link.svelte';
|
||||
|
||||
export let collection;
|
||||
export let title;
|
||||
export let clickable;
|
||||
export let onRemove = null;
|
||||
export let onAddNew = null;
|
||||
</script>
|
||||
|
||||
<ObjectListControl
|
||||
{collection}
|
||||
{title}
|
||||
{onAddNew}
|
||||
{clickable}
|
||||
on:clickrow
|
||||
columns={[
|
||||
@@ -38,9 +42,17 @@
|
||||
fieldName: 'deleteAction',
|
||||
header: 'ON DELETE',
|
||||
},
|
||||
onRemove
|
||||
? {
|
||||
fieldName: 'actions',
|
||||
sortable: true,
|
||||
slot: 2,
|
||||
}
|
||||
: null,
|
||||
]}
|
||||
>
|
||||
<svelte:fragment slot="name" let:row><ConstraintLabel {...row} /></svelte:fragment>
|
||||
<svelte:fragment slot="0" let:row>{row?.columns.map(x => x.columnName).join(', ')}</svelte:fragment>
|
||||
<svelte:fragment slot="1" let:row>{row?.columns.map(x => x.refColumnName).join(', ')}</svelte:fragment>
|
||||
<svelte:fragment slot="2" let:row><Link onClick={() => onRemove(row)}>Remove</Link></svelte:fragment>
|
||||
</ObjectListControl>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<script lang="ts">
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
|
||||
import Link from './Link.svelte';
|
||||
|
||||
import TableControl from './TableControl.svelte';
|
||||
|
||||
export let title;
|
||||
@@ -6,13 +10,16 @@
|
||||
export let columns;
|
||||
export let showIfEmpty = false;
|
||||
export let clickable;
|
||||
|
||||
export let onAddNew;
|
||||
</script>
|
||||
|
||||
{#if collection?.length > 0 || showIfEmpty}
|
||||
<div class="wrapper">
|
||||
<div class="header">
|
||||
<span class="title">{title}</span>
|
||||
<span class="title mr-1">{title}</span>
|
||||
{#if onAddNew}
|
||||
<Link onClick={onAddNew}><FontIcon icon="icon add" /> Add new</Link>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="body">
|
||||
<TableControl
|
||||
@@ -78,5 +85,4 @@
|
||||
.body {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
{#each columnList as col}
|
||||
<td>{col.header}</td>
|
||||
<td>{col.header || ''}</td>
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
import { getFormContext } from './FormProviderCore.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let disabled;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const { submitActionRef } = getFormContext();
|
||||
@@ -13,8 +15,10 @@
|
||||
}
|
||||
|
||||
submitActionRef.set(() => {
|
||||
handleClick();
|
||||
if (!disabled) {
|
||||
handleClick();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<FormStyledButton type="submit" on:click={handleClick} {...$$props} />
|
||||
<FormStyledButton type="submit" {disabled} on:click={handleClick} {...$$props} />
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
export let isNative = false;
|
||||
export let isMulti = false;
|
||||
export let notSelected = null;
|
||||
export let defaultValue = '';
|
||||
|
||||
let listOpen = false;
|
||||
let isFocused = false;
|
||||
@@ -21,18 +22,19 @@
|
||||
|
||||
{#if isNative}
|
||||
<select
|
||||
value={value || defaultValue}
|
||||
{...$$restProps}
|
||||
on:change={e => {
|
||||
dispatch('change', e.target['value']);
|
||||
}}
|
||||
>
|
||||
{#if notSelected}
|
||||
<option value="" selected={!value}>
|
||||
<option value="">
|
||||
{_.isString(notSelected) ? notSelected : '(not selected)'}
|
||||
</option>
|
||||
{/if}
|
||||
{#each _.compact(options) as x (x.value)}
|
||||
<option value={x.value} selected={value == x.value}>
|
||||
<option value={x.value}>
|
||||
{x.label}
|
||||
</option>
|
||||
{/each}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<script>
|
||||
import _, { startsWith } from 'lodash';
|
||||
import { writable } from 'svelte/store';
|
||||
import FormStyledButton from '../elements/FormStyledButton.svelte';
|
||||
import FormProvider from '../forms/FormProvider.svelte';
|
||||
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||
import FormProviderCore from '../forms/FormProviderCore.svelte';
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import SqlEditor from '../query/SqlEditor.svelte';
|
||||
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
@@ -10,28 +14,114 @@
|
||||
export let sql;
|
||||
export let onConfirm;
|
||||
export let engine;
|
||||
export let recreates;
|
||||
export let deleteCascadesScripts;
|
||||
|
||||
$: isRecreated = _.sum(_.values(recreates || {})) > 0;
|
||||
const values = writable({});
|
||||
|
||||
// $: console.log('recreates', recreates);
|
||||
</script>
|
||||
|
||||
<FormProvider>
|
||||
<FormProviderCore {values}>
|
||||
<ModalBase {...$$restProps}>
|
||||
<div slot="header">Save changes</div>
|
||||
|
||||
<div class="editor">
|
||||
<SqlEditor {engine} value={sql} readOnly />
|
||||
<SqlEditor
|
||||
{engine}
|
||||
value={$values.deleteReferencesCascade
|
||||
? [
|
||||
...deleteCascadesScripts
|
||||
.filter(({ script, title }) => $values[`deleteReferencesFor_${title}`] !== false)
|
||||
.map(({ script, title }) => script),
|
||||
sql,
|
||||
].join('\n')
|
||||
: sql}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if !_.isEmpty(deleteCascadesScripts)}
|
||||
<div class="mt-2">
|
||||
<FormCheckboxField
|
||||
templateProps={{ noMargin: true }}
|
||||
label="Delete references CASCADE"
|
||||
name="deleteReferencesCascade"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if $values.deleteReferencesCascade}
|
||||
<div class="form-margin flex">
|
||||
<FormStyledButton
|
||||
value="Check all"
|
||||
on:click={() => {
|
||||
$values = _.omitBy($values, (v, k) => k.startsWith('deleteReferencesFor_'));
|
||||
}}
|
||||
/>
|
||||
<FormStyledButton
|
||||
value="Uncheck all"
|
||||
on:click={() => {
|
||||
const newValues = { ...$values };
|
||||
for (const item of deleteCascadesScripts) {
|
||||
newValues[`deleteReferencesFor_${item.title}`] = false;
|
||||
}
|
||||
$values = newValues;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-margin flex flex-wrap">
|
||||
{#each _.sortBy(deleteCascadesScripts, 'title') as deleteTable}
|
||||
<div class="mr-1 nowrap">
|
||||
<FormCheckboxField
|
||||
defaultValue={true}
|
||||
templateProps={{ noMargin: true }}
|
||||
label={deleteTable.title}
|
||||
name={`deleteReferencesFor_${deleteTable.title}`}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if isRecreated}
|
||||
<div class="form-margin">
|
||||
<div>
|
||||
<FontIcon icon="img warn" /> This operation is not directly supported by SQL engine. DbGate can emulate it, but
|
||||
please check the generated SQL script.
|
||||
</div>
|
||||
<FormCheckboxField
|
||||
templateProps={{ noMargin: true }}
|
||||
label="Allow recreate (don't use on production databases)"
|
||||
name="allowRecreate"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div slot="footer">
|
||||
<FormSubmit
|
||||
value="OK"
|
||||
on:click={() => {
|
||||
disabled={isRecreated && !$values.allowRecreate}
|
||||
on:click={e => {
|
||||
closeCurrentModal();
|
||||
onConfirm();
|
||||
onConfirm(
|
||||
e.detail.deleteReferencesCascade
|
||||
? [
|
||||
...deleteCascadesScripts
|
||||
.filter(({ script, title }) => e.detail[`deleteReferencesFor_${title}`] !== false)
|
||||
.map(({ script, title }) => script),
|
||||
sql,
|
||||
].join('\n')
|
||||
: null
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||
</div>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
</FormProviderCore>
|
||||
|
||||
<style>
|
||||
.editor {
|
||||
@@ -39,4 +129,12 @@
|
||||
height: 30vh;
|
||||
width: 40vw;
|
||||
}
|
||||
|
||||
.form-margin {
|
||||
margin: var(--dim-large-form-margin);
|
||||
}
|
||||
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
$: disabledFields = (currentAuthType ? currentAuthType.disabledFields : null) || [];
|
||||
$: driver = $extensions.drivers.find(x => x.engine == engine);
|
||||
$: defaultDatabase = $values.defaultDatabase;
|
||||
|
||||
</script>
|
||||
|
||||
<FormSelectField
|
||||
@@ -125,6 +124,7 @@
|
||||
label="Password mode"
|
||||
isNative
|
||||
name="passwordMode"
|
||||
defaultValue="saveEncrypted"
|
||||
options={[
|
||||
{ value: 'saveEncrypted', label: 'Save and encrypt' },
|
||||
{ value: 'saveRaw', label: 'Save raw (UNSAFE!!)' },
|
||||
@@ -154,5 +154,4 @@
|
||||
.radio :global(label) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
$: {
|
||||
if (!$values.sshMode) setFieldValue('sshMode', 'userPassword');
|
||||
if (!$values.sshPort) setFieldValue('sshPort', '22');
|
||||
if (!$values.sshKeyfile && $platformInfo) setFieldValue('sshKeyfile', $platformInfo.defaultKeyFile);
|
||||
if (!$values.sshKeyfile && $platformInfo) setFieldValue('sshKeyfile', $platformInfo.defaultKeyfile);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -43,4 +43,4 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<FormSelectFieldRaw {name} options={getOptions()} />
|
||||
<FormSelectFieldRaw {name} options={getOptions()} isNative />
|
||||
|
||||
@@ -35,7 +35,7 @@ export const openedConnections = writable([]);
|
||||
export const currentDatabase = writable(null);
|
||||
export const openedTabs = writableWithStorage<TabDefinition[]>([], 'openedTabs');
|
||||
export const extensions = writable<ExtensionsDirectory>(null);
|
||||
export const visibleCommandPalette = writable(false);
|
||||
export const visibleCommandPalette = writable(null);
|
||||
export const commands = writable({});
|
||||
export const currentTheme = writableWithStorage('theme-light', 'currentTheme');
|
||||
export const activeTabId = derived([openedTabs], ([$openedTabs]) => $openedTabs.find(x => x.selected)?.tabid);
|
||||
@@ -150,3 +150,15 @@ useSettings().subscribe(value => {
|
||||
invalidateCommands();
|
||||
});
|
||||
export const getCurrentSettings = () => currentSettingsValue || {};
|
||||
|
||||
let extensionsValue = null;
|
||||
extensions.subscribe(value => {
|
||||
extensionsValue = value;
|
||||
});
|
||||
export const getExtensions = () => extensionsValue;
|
||||
|
||||
let openedConnectionsValue = null;
|
||||
openedConnections.subscribe(value => {
|
||||
openedConnectionsValue = value;
|
||||
});
|
||||
export const getOpenedConnections = () => openedConnectionsValue;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
export let onAddNext;
|
||||
</script>
|
||||
|
||||
<FormProvider initialValues={fillEditorColumnInfo(columnInfo, tableInfo)}>
|
||||
<FormProvider initialValues={fillEditorColumnInfo(columnInfo || {}, tableInfo)}>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header"
|
||||
>{columnInfo ? 'Edit column' : `Add column ${(tableInfo?.columns || []).length + 1}`}</svelte:fragment
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
{#each columns as column, index}
|
||||
<div class="row">
|
||||
<div class="label col-3">Column {index + 1}</div>
|
||||
<div class="col-6">
|
||||
<div class={$$slots.column ? 'col-3' : 'col-6'}>
|
||||
{#key column.columnName}
|
||||
<SelectField
|
||||
value={column.columnName}
|
||||
@@ -73,6 +73,11 @@
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
{#if $$slots.column}
|
||||
<div class="col-3">
|
||||
<slot name="column" {column} setColumns={changeFunc => (columns = changeFunc(columns))} {index} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="col-3 button">
|
||||
<FormStyledButton
|
||||
value="Delete"
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<script lang="ts">
|
||||
import SelectField from '../forms/SelectField.svelte';
|
||||
|
||||
import ColumnsConstraintEditorModal from './ColumnsConstraintEditorModal.svelte';
|
||||
|
||||
export let constraintInfo;
|
||||
export let setTableInfo;
|
||||
export let tableInfo;
|
||||
</script>
|
||||
|
||||
<ColumnsConstraintEditorModal
|
||||
{...$$restProps}
|
||||
constraintLabel="index"
|
||||
constraintType="index"
|
||||
{constraintInfo}
|
||||
{setTableInfo}
|
||||
{tableInfo}
|
||||
>
|
||||
<svelte:fragment slot="column" let:column let:setColumns let:index>
|
||||
<SelectField
|
||||
value={column.isDescending ? 'desc' : 'asc'}
|
||||
isNative
|
||||
options={[
|
||||
{ label: 'ASC', value: 'asc' },
|
||||
{ label: 'DESC', value: 'desc' },
|
||||
]}
|
||||
on:change={e => {
|
||||
setColumns(columns =>
|
||||
columns.map((col, i) =>
|
||||
i == index
|
||||
? {
|
||||
...col,
|
||||
isDescending: e.detail == 'desc',
|
||||
}
|
||||
: col
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</ColumnsConstraintEditorModal>
|
||||
@@ -33,9 +33,33 @@
|
||||
testEnabled: () => getCurrentEditor()?.writable(),
|
||||
onClick: () => getCurrentEditor().addForeignKey(),
|
||||
});
|
||||
|
||||
registerCommand({
|
||||
id: 'tableEditor.addIndex',
|
||||
category: 'Table editor',
|
||||
name: 'Add index',
|
||||
icon: 'icon add-key',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
testEnabled: () => getCurrentEditor()?.writable(),
|
||||
onClick: () => getCurrentEditor().addIndex(),
|
||||
});
|
||||
|
||||
registerCommand({
|
||||
id: 'tableEditor.addUnique',
|
||||
category: 'Table editor',
|
||||
name: 'Add unique',
|
||||
icon: 'icon add-key',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
testEnabled: () => getCurrentEditor()?.writable(),
|
||||
onClick: () => getCurrentEditor().addUnique(),
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { editorDeleteColumn, editorDeleteConstraint } from 'dbgate-tools';
|
||||
|
||||
import _ from 'lodash';
|
||||
import { onMount, tick } from 'svelte';
|
||||
import invalidateCommands from '../commands/invalidateCommands';
|
||||
@@ -44,6 +68,7 @@
|
||||
import ColumnLabel from '../elements/ColumnLabel.svelte';
|
||||
import ConstraintLabel from '../elements/ConstraintLabel.svelte';
|
||||
import ForeignKeyObjectListControl from '../elements/ForeignKeyObjectListControl.svelte';
|
||||
import Link from '../elements/Link.svelte';
|
||||
|
||||
import ObjectListControl from '../elements/ObjectListControl.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
@@ -53,7 +78,9 @@
|
||||
import { useDbCore } from '../utility/metadataLoaders';
|
||||
import ColumnEditorModal from './ColumnEditorModal.svelte';
|
||||
import ForeignKeyEditorModal from './ForeignKeyEditorModal.svelte';
|
||||
import IndexEditorModal from './IndexEditorModal.svelte';
|
||||
import PrimaryKeyEditorModal from './PrimaryKeyEditorModal.svelte';
|
||||
import UniqueEditorModal from './UniqueEditorModal.svelte';
|
||||
|
||||
export const activator = createActivator('TableEditor', true);
|
||||
|
||||
@@ -95,10 +122,28 @@
|
||||
});
|
||||
}
|
||||
|
||||
export function addIndex() {
|
||||
showModal(IndexEditorModal, {
|
||||
setTableInfo,
|
||||
tableInfo,
|
||||
dbInfo,
|
||||
});
|
||||
}
|
||||
|
||||
export function addUnique() {
|
||||
showModal(UniqueEditorModal, {
|
||||
setTableInfo,
|
||||
tableInfo,
|
||||
dbInfo,
|
||||
});
|
||||
}
|
||||
|
||||
$: columns = tableInfo?.columns;
|
||||
$: primaryKey = tableInfo?.primaryKey;
|
||||
$: foreignKeys = tableInfo?.foreignKeys;
|
||||
$: dependencies = tableInfo?.dependencies;
|
||||
$: indexes = tableInfo?.indexes;
|
||||
$: uniques = tableInfo?.uniques;
|
||||
|
||||
$: {
|
||||
tableInfo;
|
||||
@@ -113,6 +158,7 @@
|
||||
showIfEmpty
|
||||
clickable={writable()}
|
||||
on:clickrow={e => showModal(ColumnEditorModal, { columnInfo: e.detail, tableInfo, setTableInfo })}
|
||||
onAddNew={addColumn}
|
||||
columns={[
|
||||
{
|
||||
fieldName: 'notNull',
|
||||
@@ -147,17 +193,33 @@
|
||||
sortable: true,
|
||||
slot: 2,
|
||||
},
|
||||
writable()
|
||||
? {
|
||||
fieldName: 'actions',
|
||||
sortable: true,
|
||||
slot: 3,
|
||||
}
|
||||
: null,
|
||||
]}
|
||||
>
|
||||
<svelte:fragment slot="0" let:row>{row?.notNull ? 'YES' : 'NO'}</svelte:fragment>
|
||||
<svelte:fragment slot="1" let:row>{row?.isSparse ? 'YES' : 'NO'}</svelte:fragment>
|
||||
<svelte:fragment slot="2" let:row>{row?.isPersisted ? 'YES' : 'NO'}</svelte:fragment>
|
||||
<svelte:fragment slot="3" let:row
|
||||
><Link
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
setTableInfo(tbl => editorDeleteColumn(tbl, row));
|
||||
}}>Remove</Link
|
||||
></svelte:fragment
|
||||
>
|
||||
<svelte:fragment slot="name" let:row><ColumnLabel {...row} forceIcon /></svelte:fragment>
|
||||
</ObjectListControl>
|
||||
|
||||
<ObjectListControl
|
||||
collection={_.compact([primaryKey])}
|
||||
title="Primary key"
|
||||
onAddNew={primaryKey ? null : addPrimaryKey}
|
||||
clickable={writable()}
|
||||
on:clickrow={e => showModal(PrimaryKeyEditorModal, { constraintInfo: e.detail, tableInfo, setTableInfo })}
|
||||
columns={[
|
||||
@@ -166,16 +228,99 @@
|
||||
header: 'Columns',
|
||||
slot: 0,
|
||||
},
|
||||
writable()
|
||||
? {
|
||||
fieldName: 'actions',
|
||||
sortable: true,
|
||||
slot: 1,
|
||||
}
|
||||
: null,
|
||||
]}
|
||||
>
|
||||
<svelte:fragment slot="name" let:row><ConstraintLabel {...row} /></svelte:fragment>
|
||||
<svelte:fragment slot="0" let:row>{row?.columns.map(x => x.columnName).join(', ')}</svelte:fragment>
|
||||
<svelte:fragment slot="1" let:row
|
||||
><Link
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
setTableInfo(tbl => editorDeleteConstraint(tbl, row));
|
||||
}}>Remove</Link
|
||||
></svelte:fragment
|
||||
>
|
||||
</ObjectListControl>
|
||||
|
||||
<ObjectListControl
|
||||
collection={indexes}
|
||||
onAddNew={addIndex}
|
||||
title={`Indexes (${indexes?.length || 0})`}
|
||||
clickable={writable()}
|
||||
on:clickrow={e => showModal(UniqueEditorModal, { constraintInfo: e.detail, tableInfo, setTableInfo })}
|
||||
columns={[
|
||||
{
|
||||
fieldName: 'columns',
|
||||
header: 'Columns',
|
||||
slot: 0,
|
||||
},
|
||||
writable()
|
||||
? {
|
||||
fieldName: 'actions',
|
||||
sortable: true,
|
||||
slot: 1,
|
||||
}
|
||||
: null,
|
||||
]}
|
||||
>
|
||||
<svelte:fragment slot="name" let:row><ConstraintLabel {...row} /></svelte:fragment>
|
||||
<svelte:fragment slot="0" let:row>{row?.columns.map(x => x.columnName).join(', ')}</svelte:fragment>
|
||||
<svelte:fragment slot="1" let:row
|
||||
><Link
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
setTableInfo(tbl => editorDeleteConstraint(tbl, row));
|
||||
}}>Remove</Link
|
||||
></svelte:fragment
|
||||
>
|
||||
</ObjectListControl>
|
||||
|
||||
<ObjectListControl
|
||||
collection={uniques}
|
||||
onAddNew={addUnique}
|
||||
title={`Unique constraints (${uniques?.length || 0})`}
|
||||
clickable={writable()}
|
||||
on:clickrow={e => showModal(IndexEditorModal, { constraintInfo: e.detail, tableInfo, setTableInfo })}
|
||||
columns={[
|
||||
{
|
||||
fieldName: 'columns',
|
||||
header: 'Columns',
|
||||
slot: 0,
|
||||
},
|
||||
writable()
|
||||
? {
|
||||
fieldName: 'actions',
|
||||
sortable: true,
|
||||
slot: 1,
|
||||
}
|
||||
: null,
|
||||
]}
|
||||
>
|
||||
<svelte:fragment slot="name" let:row><ConstraintLabel {...row} /></svelte:fragment>
|
||||
<svelte:fragment slot="0" let:row>{row?.columns.map(x => x.columnName).join(', ')}</svelte:fragment>
|
||||
<svelte:fragment slot="1" let:row
|
||||
><Link
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
setTableInfo(tbl => editorDeleteConstraint(tbl, row));
|
||||
}}>Remove</Link
|
||||
></svelte:fragment
|
||||
>
|
||||
</ObjectListControl>
|
||||
|
||||
<ForeignKeyObjectListControl
|
||||
collection={foreignKeys}
|
||||
title="Foreign keys"
|
||||
onAddNew={addForeignKey}
|
||||
title={`Foreign keys (${foreignKeys?.length || 0})`}
|
||||
clickable={writable()}
|
||||
onRemove={row => setTableInfo(tbl => editorDeleteConstraint(tbl, row))}
|
||||
on:clickrow={e => showModal(ForeignKeyEditorModal, { constraintInfo: e.detail, tableInfo, setTableInfo, dbInfo })}
|
||||
/>
|
||||
<ForeignKeyObjectListControl collection={dependencies} title="Dependencies" />
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import ColumnsConstraintEditorModal from './ColumnsConstraintEditorModal.svelte';
|
||||
|
||||
export let constraintInfo;
|
||||
export let setTableInfo;
|
||||
export let tableInfo;
|
||||
|
||||
</script>
|
||||
|
||||
<ColumnsConstraintEditorModal
|
||||
{...$$restProps}
|
||||
constraintLabel="unique"
|
||||
constraintType="unique"
|
||||
{constraintInfo}
|
||||
{setTableInfo}
|
||||
{tableInfo}
|
||||
/>
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
export const matchingProps = ['conid', 'database', 'schemaName', 'pureName'];
|
||||
export const allowAddToFavorites = props => true;
|
||||
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
import App from '../App.svelte';
|
||||
import TableDataGrid from '../datagrid/TableDataGrid.svelte';
|
||||
import useGridConfig from '../utility/useGridConfig';
|
||||
@@ -29,6 +29,7 @@
|
||||
createChangeSet,
|
||||
createGridCache,
|
||||
createGridConfig,
|
||||
getDeleteCascades,
|
||||
TableFormViewDisplay,
|
||||
TableGridDisplay,
|
||||
} from 'dbgate-datalib';
|
||||
@@ -87,11 +88,18 @@
|
||||
export function save() {
|
||||
const driver = findEngineDriver($connection, $extensions);
|
||||
const script = changeSetToSql($changeSetStore?.value, $dbinfo);
|
||||
const deleteCascades = getDeleteCascades($changeSetStore?.value, $dbinfo);
|
||||
const sql = scriptToSql(driver, script);
|
||||
const deleteCascadesScripts = _.map(deleteCascades, ({ title, commands }) => ({
|
||||
title,
|
||||
script: scriptToSql(driver, commands),
|
||||
}));
|
||||
console.log('deleteCascadesScripts', deleteCascadesScripts);
|
||||
showModal(ConfirmSqlModal, {
|
||||
sql,
|
||||
onConfirm: () => handleConfirmSql(sql),
|
||||
onConfirm: sqlOverride => handleConfirmSql(sqlOverride || sql),
|
||||
engine: driver.engine,
|
||||
deleteCascadesScripts,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -105,7 +113,6 @@
|
||||
}
|
||||
|
||||
registerMenu({ command: 'tableData.save', tag: 'save' });
|
||||
|
||||
</script>
|
||||
|
||||
<TableDataGrid
|
||||
|
||||
@@ -28,7 +28,13 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { findEngineDriver, generateTablePairingId, getAlterTableScript } from 'dbgate-tools';
|
||||
import {
|
||||
fillConstraintNames,
|
||||
extendTableInfo,
|
||||
findEngineDriver,
|
||||
generateTablePairingId,
|
||||
getAlterTableScript,
|
||||
} from 'dbgate-tools';
|
||||
|
||||
import _ from 'lodash';
|
||||
import registerCommand from '../commands/registerCommand';
|
||||
@@ -38,7 +44,6 @@
|
||||
import ForeignKeyObjectListControl from '../elements/ForeignKeyObjectListControl.svelte';
|
||||
|
||||
import { extensions } from '../stores';
|
||||
import ObjectListControl from '../elements/ObjectListControl.svelte';
|
||||
import useEditorData from '../query/useEditorData';
|
||||
import TableEditor from '../tableeditor/TableEditor.svelte';
|
||||
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||
@@ -60,9 +65,11 @@
|
||||
export let objectTypeField = 'tables';
|
||||
let domEditor;
|
||||
|
||||
let savedName;
|
||||
|
||||
export const activator = createActivator('TableStructureTab', true);
|
||||
|
||||
$: tableInfo = useDbCore({ conid, database, schemaName, pureName, objectTypeField });
|
||||
$: tableInfo = useDbCore({ conid, database, schemaName, pureName: savedName || pureName, objectTypeField });
|
||||
$: dbInfo = useDatabaseInfo({ conid, database });
|
||||
$: tableInfoWithPairingId = $tableInfo ? generateTablePairingId($tableInfo) : null;
|
||||
$: connection = useConnectionInfo({ conid });
|
||||
@@ -81,9 +88,10 @@
|
||||
} else {
|
||||
showModal(InputTextModal, {
|
||||
header: 'Set table name',
|
||||
value: $editorValue.current.pureName || 'newTable',
|
||||
value: savedName || 'newTable',
|
||||
label: 'Table name',
|
||||
onConfirm: name => {
|
||||
savedName = name;
|
||||
setEditorData(tbl => ({
|
||||
base: tbl.base,
|
||||
current: {
|
||||
@@ -99,10 +107,18 @@
|
||||
|
||||
function doSave(createTableName) {
|
||||
const driver = findEngineDriver($connection, $extensions);
|
||||
const sql = getAlterTableScript($editorValue.base, $editorValue.current, {}, $dbInfo, driver);
|
||||
|
||||
const { sql, recreates } = getAlterTableScript(
|
||||
$editorValue.base,
|
||||
extendTableInfo(fillConstraintNames($editorValue.current, driver.dialect)),
|
||||
{},
|
||||
$dbInfo,
|
||||
driver
|
||||
);
|
||||
|
||||
showModal(ConfirmSqlModal, {
|
||||
sql,
|
||||
recreates,
|
||||
onConfirm: () => {
|
||||
handleConfirmSql(sql, createTableName);
|
||||
},
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
'Sorry, DbGate has crashed again.\nDo you want to clear local user data to avoid crashing after next reload?'
|
||||
)
|
||||
) {
|
||||
localStorage.clear();
|
||||
localStorage.removeItem('openedTabs');
|
||||
try {
|
||||
await localforage.clear();
|
||||
} catch (err) {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import _ from 'lodash';
|
||||
import { findEngineDriver, generateDbPairingId, getAlterDatabaseScript } from 'dbgate-tools';
|
||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import { getExtensions } from '../stores';
|
||||
import { getConnectionInfo, getDatabaseInfo } from './metadataLoaders';
|
||||
import ConfirmSqlModal from '../modals/ConfirmSqlModal.svelte';
|
||||
import axiosInstance from './axiosInstance';
|
||||
|
||||
export async function alterDatabaseDialog(conid, database, updateFunc) {
|
||||
const conn = await getConnectionInfo({ conid });
|
||||
const driver = findEngineDriver(conn, getExtensions());
|
||||
|
||||
const db = generateDbPairingId(await getDatabaseInfo({ conid, database }));
|
||||
const dbUpdated = _.cloneDeep(db);
|
||||
updateFunc(dbUpdated);
|
||||
|
||||
const { sql, recreates } = getAlterDatabaseScript(db, dbUpdated, {}, db, driver);
|
||||
|
||||
showModal(ConfirmSqlModal, {
|
||||
sql,
|
||||
recreates,
|
||||
onConfirm: async () => {
|
||||
const resp = await axiosInstance.request({
|
||||
url: 'database-connections/run-script',
|
||||
method: 'post',
|
||||
params: {
|
||||
conid,
|
||||
database,
|
||||
},
|
||||
data: { sql },
|
||||
});
|
||||
await axiosInstance.post('database-connections/sync-model', { conid, database });
|
||||
},
|
||||
engine: driver.engine,
|
||||
});
|
||||
}
|
||||
|
||||
export async function renameDatabaseObjectDialog(conid, database, oldName, updateFunc) {
|
||||
showModal(InputTextModal, {
|
||||
value: oldName,
|
||||
label: 'New name',
|
||||
header: 'Rename object',
|
||||
onConfirm: newName => {
|
||||
alterDatabaseDialog(conid, database, db => updateFunc(db, newName));
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -32,3 +32,9 @@ export function getObjectTypeFieldLabel(objectTypeField) {
|
||||
if (objectTypeField == 'matviews') return 'Materialized Views';
|
||||
return _.startCase(objectTypeField);
|
||||
}
|
||||
|
||||
export async function asyncFilter(arr, predicate) {
|
||||
const results = await Promise.all(arr.map(predicate));
|
||||
|
||||
return arr.filter((_v, index) => results[index]);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function createActivator(name: string, activateOnTabVisible: bool
|
||||
if (tabVisible) {
|
||||
const unsubscribeTabVisible = tabVisible.subscribe(value => {
|
||||
tabVisibleValue = value;
|
||||
if (activateOnTabVisible) {
|
||||
if (activateOnTabVisible && tabVisibleValue) {
|
||||
activate();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import getAsArray from './getAsArray';
|
||||
import { DatabaseInfo } from 'dbgate-types';
|
||||
import { derived } from 'svelte/store';
|
||||
import { extendDatabaseInfo } from 'dbgate-tools';
|
||||
import { setLocalStorage } from '../utility/storageCache';
|
||||
|
||||
const databaseInfoLoader = ({ conid, database }) => ({
|
||||
url: 'database-connections/structure',
|
||||
@@ -74,6 +75,9 @@ const databaseListLoader = ({ conid }) => ({
|
||||
url: 'server-connections/list-databases',
|
||||
params: { conid },
|
||||
reloadTrigger: `database-list-changed-${conid}`,
|
||||
onLoaded: value => {
|
||||
if (value?.length > 0) setLocalStorage(`database_list_${conid}`, value);
|
||||
},
|
||||
});
|
||||
|
||||
const serverVersionLoader = ({ conid }) => ({
|
||||
@@ -135,7 +139,7 @@ const authTypesLoader = ({ engine }) => ({
|
||||
});
|
||||
|
||||
async function getCore(loader, args) {
|
||||
const { url, params, reloadTrigger, transform } = loader(args);
|
||||
const { url, params, reloadTrigger, transform, onLoaded } = loader(args);
|
||||
const key = stableStringify({ url, ...params });
|
||||
|
||||
async function doLoad() {
|
||||
@@ -144,7 +148,9 @@ async function getCore(loader, args) {
|
||||
url,
|
||||
params,
|
||||
});
|
||||
return (transform || (x => x))(resp.data);
|
||||
const res = (transform || (x => x))(resp.data);
|
||||
if (onLoaded) onLoaded(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
const fromCache = cacheGet(key);
|
||||
@@ -156,7 +162,7 @@ async function getCore(loader, args) {
|
||||
}
|
||||
|
||||
function useCore(loader, args) {
|
||||
const { url, params, reloadTrigger, transform } = loader(args);
|
||||
const { url, params, reloadTrigger, transform, onLoaded } = loader(args);
|
||||
const cacheKey = stableStringify({ url, ...params });
|
||||
|
||||
return {
|
||||
@@ -168,7 +174,9 @@ function useCore(loader, args) {
|
||||
params,
|
||||
url,
|
||||
});
|
||||
return (transform || (x => x))(resp.data);
|
||||
const res = (transform || (x => x))(resp.data);
|
||||
if (onLoaded) onLoaded(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (cacheKey) {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
const cache = {};
|
||||
|
||||
export function getLocalStorage(key) {
|
||||
if (key in cache) return cache[key];
|
||||
const item = localStorage.getItem(key);
|
||||
if (item) {
|
||||
const res = JSON.parse(item);
|
||||
cache[key] = res;
|
||||
return res;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function setLocalStorage(key, value) {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
delete cache[key];
|
||||
}
|
||||
|
||||
export function removeLocalStorage(key) {
|
||||
localStorage.removeItem(key);
|
||||
delete cache[key];
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts" context="module">
|
||||
const electron = getElectron();
|
||||
|
||||
const closeTabFunc = closeCondition => tabid => {
|
||||
openedTabs.update(files => {
|
||||
const active = files.find(x => x.tabid == tabid);
|
||||
@@ -33,6 +35,9 @@
|
||||
}))
|
||||
);
|
||||
};
|
||||
const closeCurrentTab = () => {
|
||||
closeTab(getActiveTabId());
|
||||
};
|
||||
const closeWithSameDb = closeTabFunc(
|
||||
(x, active) =>
|
||||
_.get(x, 'props.conid') == _.get(active, 'props.conid') &&
|
||||
@@ -96,6 +101,15 @@
|
||||
onClick: closeAll,
|
||||
});
|
||||
|
||||
registerCommand({
|
||||
id: 'tabs.closeTab',
|
||||
category: 'Tabs',
|
||||
name: 'Close tab',
|
||||
keyText: electron ? 'Ctrl+W' : null,
|
||||
testEnabled: () => getOpenedTabs().filter(x => !x.closedTime).length >= 1,
|
||||
onClick: closeCurrentTab,
|
||||
});
|
||||
|
||||
registerCommand({
|
||||
id: 'tabs.addToFavorites',
|
||||
category: 'Tabs',
|
||||
@@ -120,11 +134,12 @@
|
||||
import FavoriteModal from '../modals/FavoriteModal.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
|
||||
import { currentDatabase, getActiveTab, getOpenedTabs, openedTabs, activeTabId } from '../stores';
|
||||
import { currentDatabase, getActiveTab, getOpenedTabs, openedTabs, activeTabId, getActiveTabId } from '../stores';
|
||||
import tabs from '../tabs';
|
||||
import { setSelectedTab } from '../utility/common';
|
||||
import contextMenu from '../utility/contextMenu';
|
||||
import getConnectionLabel from '../utility/getConnectionLabel';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import { getConnectionInfo, useConnectionList } from '../utility/metadataLoaders';
|
||||
import { duplicateTab } from '../utility/openNewTab';
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
|
||||
<div class="main">
|
||||
{#if !$visibleToolbar}
|
||||
<div class="wrapper mb-3" on:click={() => ($visibleCommandPalette = true)}>
|
||||
<div class="wrapper mb-3" on:click={() => ($visibleCommandPalette = 'menu')}>
|
||||
<FontIcon icon="icon menu" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
diff --git a/node_modules/svelte/internal/index.js b/node_modules/svelte/internal/index.js
|
||||
index ee20a17..7b6fff8 100644
|
||||
index 1cce90d..6220522 100644
|
||||
--- a/node_modules/svelte/internal/index.js
|
||||
+++ b/node_modules/svelte/internal/index.js
|
||||
@@ -200,7 +200,7 @@ function insert(target, node, anchor) {
|
||||
target.insertBefore(node, anchor || null);
|
||||
@@ -374,7 +374,7 @@ function insert_hydration(target, node, anchor) {
|
||||
}
|
||||
}
|
||||
function detach(node) {
|
||||
- node.parentNode.removeChild(node);
|
||||
@@ -12,11 +12,11 @@ index ee20a17..7b6fff8 100644
|
||||
function destroy_each(iterations, detaching) {
|
||||
for (let i = 0; i < iterations.length; i += 1) {
|
||||
diff --git a/node_modules/svelte/internal/index.mjs b/node_modules/svelte/internal/index.mjs
|
||||
index 4146e56..f5f00c5 100644
|
||||
index 6650e85..b746187 100644
|
||||
--- a/node_modules/svelte/internal/index.mjs
|
||||
+++ b/node_modules/svelte/internal/index.mjs
|
||||
@@ -196,7 +196,7 @@ function insert(target, node, anchor) {
|
||||
target.insertBefore(node, anchor || null);
|
||||
@@ -370,7 +370,7 @@ function insert_hydration(target, node, anchor) {
|
||||
}
|
||||
}
|
||||
function detach(node) {
|
||||
- node.parentNode.removeChild(node);
|
||||
@@ -1,8 +1,8 @@
|
||||
const { DatabaseAnalyser } = require('dbgate-tools');
|
||||
|
||||
class Analyser extends DatabaseAnalyser {
|
||||
constructor(pool, driver) {
|
||||
super(pool, driver);
|
||||
constructor(pool, driver, version) {
|
||||
super(pool, driver, version);
|
||||
}
|
||||
|
||||
async _runAnalysis() {
|
||||
|
||||
@@ -18,7 +18,7 @@ function readCursor(cursor, options) {
|
||||
}
|
||||
|
||||
const mongoIdRegex = /^[0-9a-f]{24}$/;
|
||||
function convertCondition(condition) {
|
||||
function convertConditionInternal(condition) {
|
||||
if (condition && _.isString(condition._id) && condition._id.match(mongoIdRegex)) {
|
||||
return {
|
||||
_id: ObjectId(condition._id),
|
||||
@@ -27,6 +27,12 @@ function convertCondition(condition) {
|
||||
return condition;
|
||||
}
|
||||
|
||||
function convertConditionUser(condition) {
|
||||
return _.cloneDeepWith(condition, (x) => {
|
||||
if (x && x.$oid) return ObjectId(x.$oid);
|
||||
});
|
||||
}
|
||||
|
||||
function findArrayResult(resValue) {
|
||||
if (!_.isPlainObject(resValue)) return null;
|
||||
const arrays = _.values(resValue).filter((x) => _.isArray(x));
|
||||
@@ -191,10 +197,11 @@ const driver = {
|
||||
try {
|
||||
const collection = pool.__getDatabase().collection(options.pureName);
|
||||
if (options.countDocuments) {
|
||||
const count = await collection.countDocuments(options.condition || {});
|
||||
const count = await collection.countDocuments(convertConditionUser(options.condition) || {});
|
||||
return { count };
|
||||
} else {
|
||||
let cursor = await collection.find(options.condition || {});
|
||||
// console.log('options.condition', JSON.stringify(options.condition, undefined, 2));
|
||||
let cursor = await collection.find(convertConditionUser(options.condition) || {});
|
||||
if (options.sort) cursor = cursor.sort(options.sort);
|
||||
if (options.skip) cursor = cursor.skip(options.skip);
|
||||
if (options.limit) cursor = cursor.limit(options.limit);
|
||||
@@ -230,22 +237,24 @@ const driver = {
|
||||
...update.document,
|
||||
...update.fields,
|
||||
};
|
||||
const doc = await collection.findOne(convertCondition(update.condition));
|
||||
const doc = await collection.findOne(convertConditionInternal(update.condition));
|
||||
if (doc) {
|
||||
const resdoc = await collection.replaceOne(convertCondition(update.condition), {
|
||||
const resdoc = await collection.replaceOne(convertConditionInternal(update.condition), {
|
||||
...document,
|
||||
_id: doc._id,
|
||||
});
|
||||
res.replaced.push(resdoc._id);
|
||||
}
|
||||
} else {
|
||||
const resdoc = await collection.updateOne(convertCondition(update.condition), { $set: update.fields });
|
||||
const resdoc = await collection.updateOne(convertConditionInternal(update.condition), {
|
||||
$set: update.fields,
|
||||
});
|
||||
res.updated.push(resdoc._id);
|
||||
}
|
||||
}
|
||||
for (const del of changeSet.deletes) {
|
||||
const collection = db.collection(del.pureName);
|
||||
const resdoc = await collection.deleteOne(convertCondition(del.condition));
|
||||
const resdoc = await collection.deleteOne(convertConditionInternal(del.condition));
|
||||
res.deleted.push(resdoc._id);
|
||||
}
|
||||
return res;
|
||||
|
||||
@@ -50,8 +50,8 @@ function getColumnInfo({
|
||||
}
|
||||
|
||||
class MsSqlAnalyser extends DatabaseAnalyser {
|
||||
constructor(pool, driver) {
|
||||
super(pool, driver);
|
||||
constructor(pool, driver, version) {
|
||||
super(pool, driver, version);
|
||||
}
|
||||
|
||||
createQuery(resFileName, typeFields) {
|
||||
|
||||
@@ -33,6 +33,8 @@ const dialect = {
|
||||
dropUnique: true,
|
||||
createCheck: true,
|
||||
dropCheck: true,
|
||||
|
||||
dropReferencesWhenDropTable: true,
|
||||
};
|
||||
|
||||
/** @type {import('dbgate-types').EngineDriver} */
|
||||
|
||||
@@ -29,8 +29,8 @@ function getColumnInfo({
|
||||
}
|
||||
|
||||
class Analyser extends DatabaseAnalyser {
|
||||
constructor(pool, driver) {
|
||||
super(pool, driver);
|
||||
constructor(pool, driver, version) {
|
||||
super(pool, driver, version);
|
||||
}
|
||||
|
||||
createQuery(resFileName, typeFields) {
|
||||
|
||||
@@ -28,6 +28,8 @@ const dialect = {
|
||||
dropUnique: true,
|
||||
createCheck: true,
|
||||
dropCheck: true,
|
||||
|
||||
dropReferencesWhenDropTable: false,
|
||||
};
|
||||
|
||||
const mysqlDriverBase = {
|
||||
|
||||
@@ -36,8 +36,8 @@ function getColumnInfo({
|
||||
}
|
||||
|
||||
class Analyser extends DatabaseAnalyser {
|
||||
constructor(pool, driver) {
|
||||
super(pool, driver);
|
||||
constructor(pool, driver, version) {
|
||||
super(pool, driver, version);
|
||||
}
|
||||
|
||||
createQuery(resFileName, typeFields) {
|
||||
@@ -67,8 +67,12 @@ class Analyser extends DatabaseAnalyser {
|
||||
? await this.driver.query(this.pool, this.createQuery('matviewColumns', ['matviews']))
|
||||
: null;
|
||||
const routines = await this.driver.query(this.pool, this.createQuery('routines', ['procedures', 'functions']));
|
||||
const indexes = await this.driver.query(this.pool, this.createQuery('indexes', ['tables']));
|
||||
const indexcols = await this.driver.query(this.pool, this.createQuery('indexcols', ['tables']));
|
||||
const indexes = this.driver.__analyserInternals.skipIndexes
|
||||
? { rows: [] }
|
||||
: await this.driver.query(this.pool, this.createQuery('indexes', ['tables']));
|
||||
const indexcols = this.driver.__analyserInternals.skipIndexes
|
||||
? { rows: [] }
|
||||
: await this.driver.query(this.pool, this.createQuery('indexcols', ['tables']));
|
||||
const uniqueNames = await this.driver.query(this.pool, this.createQuery('uniqueNames', ['tables']));
|
||||
|
||||
return {
|
||||
|
||||
@@ -133,10 +133,15 @@ const drivers = driverBases.map(driverBase => ({
|
||||
|
||||
const m = version.match(/([\d\.]+)/);
|
||||
let versionText = null;
|
||||
let versionMajor = null;
|
||||
let versionMinor = null;
|
||||
if (m) {
|
||||
if (isCockroach) versionText = `CockroachDB ${m[1]}`;
|
||||
if (isRedshift) versionText = `Redshift ${m[1]}`;
|
||||
if (isPostgres) versionText = `PostgreSQL ${m[1]}`;
|
||||
const numbers = m[1].split('.');
|
||||
if (numbers[0]) versionMajor = parseInt(numbers[0]);
|
||||
if (numbers[1]) versionMinor = parseInt(numbers[1]);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -145,6 +150,8 @@ const drivers = driverBases.map(driverBase => ({
|
||||
isPostgres,
|
||||
isCockroach,
|
||||
isRedshift,
|
||||
versionMajor,
|
||||
versionMinor,
|
||||
};
|
||||
},
|
||||
async readQuery(client, sql, structure) {
|
||||
|
||||
@@ -30,6 +30,8 @@ const dialect = {
|
||||
dropUnique: true,
|
||||
createCheck: true,
|
||||
dropCheck: true,
|
||||
|
||||
dropReferencesWhenDropTable: true,
|
||||
};
|
||||
|
||||
const postgresDriverBase = {
|
||||
@@ -55,6 +57,20 @@ const postgresDriver = {
|
||||
...dialect,
|
||||
materializedViews: true,
|
||||
},
|
||||
|
||||
dialectByVersion(version) {
|
||||
if (version) {
|
||||
return {
|
||||
...dialect,
|
||||
materializedViews:
|
||||
version &&
|
||||
version.versionMajor != null &&
|
||||
version.versionMinor != null &&
|
||||
(version.versionMajor > 9 || version.versionMajor == 9 || version.versionMinor >= 3),
|
||||
};
|
||||
}
|
||||
return dialect;
|
||||
},
|
||||
};
|
||||
|
||||
/** @type {import('dbgate-types').EngineDriver} */
|
||||
@@ -81,6 +97,10 @@ const redshiftDriver = {
|
||||
...dialect,
|
||||
stringAgg: false,
|
||||
},
|
||||
__analyserInternals: {
|
||||
refTableCond: '',
|
||||
skipIndexes: true,
|
||||
},
|
||||
engine: 'redshift@dbgate-plugin-postgres',
|
||||
title: 'Amazon Redshift',
|
||||
defaultPort: 5439,
|
||||
|
||||
@@ -12,16 +12,17 @@ SELECT
|
||||
pragma_index_list(m.name) AS il,
|
||||
pragma_index_info(il.name) AS ii
|
||||
WHERE m.type='table' AND il.origin <> 'pk'
|
||||
ORDER BY ii.seqno
|
||||
ORDER BY ii.seqno, il.name
|
||||
`;
|
||||
|
||||
class Analyser extends DatabaseAnalyser {
|
||||
constructor(pool, driver) {
|
||||
super(pool, driver);
|
||||
constructor(pool, driver, version) {
|
||||
super(pool, driver, version);
|
||||
}
|
||||
|
||||
async _getFastSnapshot() {
|
||||
const objects = await this.driver.query(this.pool, "select * from sqlite_master where type='table' or type='view'");
|
||||
const indexcols = await this.driver.query(this.pool, indexcolsQuery);
|
||||
|
||||
return {
|
||||
tables: objects.rows
|
||||
@@ -29,7 +30,12 @@ class Analyser extends DatabaseAnalyser {
|
||||
.map((x) => ({
|
||||
pureName: x.name,
|
||||
objectId: x.name,
|
||||
contentHash: x.sql,
|
||||
contentHash: [
|
||||
x.sql,
|
||||
...indexcols.rows
|
||||
.filter((y) => y.tableName == x.name)
|
||||
.map((y) => `-- ${y.constraintName}: ${y.columnName}`),
|
||||
].join(';\n'),
|
||||
})),
|
||||
views: objects.rows
|
||||
.filter((x) => x.type == 'view')
|
||||
|
||||
@@ -21,6 +21,8 @@ const dialect = {
|
||||
quoteIdentifier(s) {
|
||||
return `[${s}]`;
|
||||
},
|
||||
anonymousPrimaryKey: true,
|
||||
disableExplicitTransaction: true,
|
||||
|
||||
createColumn: true,
|
||||
dropColumn: true,
|
||||
@@ -30,6 +32,7 @@ const dialect = {
|
||||
dropForeignKey: false,
|
||||
createPrimaryKey: false,
|
||||
dropPrimaryKey: false,
|
||||
dropReferencesWhenDropTable: false,
|
||||
};
|
||||
|
||||
/** @type {import('dbgate-types').EngineDriver} */
|
||||
|
||||
@@ -942,10 +942,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.11.tgz#aeb16f50649a91af79dbe36574b66d0f9e4d9f71"
|
||||
integrity sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA==
|
||||
|
||||
"@rollup/plugin-commonjs@^17.0.0":
|
||||
version "17.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz#757ec88737dffa8aa913eb392fade2e45aef2a2d"
|
||||
integrity sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew==
|
||||
"@rollup/plugin-commonjs@^20.0.0":
|
||||
version "20.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-20.0.0.tgz#3246872dcbcb18a54aaa6277a8c7d7f1b155b745"
|
||||
integrity sha512-5K0g5W2Ol8hAcTHqcTBHiA7M58tfmYi1o9KxeJuuRNpGaTa5iLjcyemBitCBcKXaHamOBBEH2dGom6v6Unmqjg==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.1.0"
|
||||
commondir "^1.0.1"
|
||||
@@ -955,10 +955,10 @@
|
||||
magic-string "^0.25.7"
|
||||
resolve "^1.17.0"
|
||||
|
||||
"@rollup/plugin-node-resolve@^11.0.0":
|
||||
version "11.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.0.tgz#a5ab88c35bb7622d115f44984dee305112b6f714"
|
||||
integrity sha512-qHjNIKYt5pCcn+5RUBQxK8krhRvf1HnyVgUCcFFcweDS7fhkOLZeYh0mhHK6Ery8/bb9tvN/ubPzmfF0qjDCTA==
|
||||
"@rollup/plugin-node-resolve@^13.0.5":
|
||||
version "13.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.5.tgz#016abe58796a4ff544d6beac7818921e3d3777fc"
|
||||
integrity sha512-mVaw6uxtvuGx/XCI4qBQXsDZJUfyx5vp39iE0J/7Hd6wDhEbjHr6aES7Nr9yWbuE0BY+oKp6N7Bq6jX5NCGNmQ==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.1.0"
|
||||
"@types/resolve" "1.17.1"
|
||||
@@ -967,18 +967,18 @@
|
||||
is-module "^1.0.0"
|
||||
resolve "^1.19.0"
|
||||
|
||||
"@rollup/plugin-replace@^2.4.1":
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-2.4.1.tgz#c411b5ab72809fb1bfc8b487d8d02eef661460d3"
|
||||
integrity sha512-XwC1oK5rrtRJ0tn1ioLHS6OV5JTluJF7QE1J/q1hN3bquwjnVxjtMyY9iCnoyH9DQbf92CxajB3o98wZbP3oAQ==
|
||||
"@rollup/plugin-replace@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-3.0.0.tgz#3a4c9665d4e7a4ce2c360cf021232784892f3fac"
|
||||
integrity sha512-3c7JCbMuYXM4PbPWT4+m/4Y6U60SgsnDT/cCyAyUKwFHg7pTSfsSQzIpETha3a3ig6OdOKzZz87D9ZXIK3qsDg==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.1.0"
|
||||
magic-string "^0.25.7"
|
||||
|
||||
"@rollup/plugin-typescript@^6.0.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-6.1.0.tgz#289e7f0ea12fd659bd13ad59dda73b9055538b83"
|
||||
integrity sha512-hJxaiE6WyNOsK+fZpbFh9CUijZYqPQuAOWO5khaGTUkM8DYNNyA2TDlgamecE+qLOG1G1+CwbWMAx3rbqpp6xQ==
|
||||
"@rollup/plugin-typescript@^8.2.5":
|
||||
version "8.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.2.5.tgz#e0319761b2b5105615e5a0c371ae05bc2984b7de"
|
||||
integrity sha512-QL/LvDol/PAGB2O0S7/+q2HpSUNodpw7z6nGn9BfoVCPOZ0r4EALrojFU29Bkoi2Hr2jgTocTejJ5GGWZfOxbQ==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.1.0"
|
||||
resolve "^1.17.0"
|
||||
@@ -2305,6 +2305,11 @@ bson@^1.1.4:
|
||||
resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.6.tgz#fb819be9a60cd677e0853aee4ca712a785d6618a"
|
||||
integrity sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==
|
||||
|
||||
buffer-crc32@^0.2.5:
|
||||
version "0.2.13"
|
||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||
@@ -3700,6 +3705,11 @@ es-to-primitive@^1.2.1:
|
||||
is-date-object "^1.0.1"
|
||||
is-symbol "^1.0.2"
|
||||
|
||||
es6-promise@^3.1.2:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613"
|
||||
integrity sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=
|
||||
|
||||
escalade@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||
@@ -4443,7 +4453,7 @@ fsevents@^1.2.7:
|
||||
bindings "^1.5.0"
|
||||
nan "^2.12.1"
|
||||
|
||||
fsevents@^2.3.2, fsevents@~2.3.1:
|
||||
fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
||||
@@ -4710,6 +4720,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
|
||||
integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
|
||||
|
||||
graceful-fs@^4.1.3:
|
||||
version "4.2.8"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
|
||||
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
|
||||
|
||||
graceful-fs@^4.2.3, graceful-fs@^4.2.4:
|
||||
version "4.2.6"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
|
||||
@@ -9025,7 +9040,7 @@ rimraf@2.6.3:
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
rimraf@^2.5.4, rimraf@^2.6.3:
|
||||
rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.3:
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
|
||||
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
|
||||
@@ -9097,12 +9112,12 @@ rollup-pluginutils@^2.8.2:
|
||||
dependencies:
|
||||
estree-walker "^0.6.1"
|
||||
|
||||
rollup@^2.3.4:
|
||||
version "2.39.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.39.0.tgz#be4f98c9e421793a8fec82c854fb567c35e22ab6"
|
||||
integrity sha512-+WR3bttcq7zE+BntH09UxaW3bQo3vItuYeLsyk4dL2tuwbeSKJuvwiawyhEnvRdRgrII0Uzk00FpctHO/zB1kw==
|
||||
rollup@^2.57.0:
|
||||
version "2.57.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.57.0.tgz#c1694475eb22e1022477c0f4635fd0ac80713173"
|
||||
integrity sha512-bKQIh1rWKofRee6mv8SrF2HdP6pea5QkwBZSMImJysFj39gQuiV8MEPBjXOCpzk3wSYp63M2v2wkWBmFC8O/rg==
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.1"
|
||||
fsevents "~2.3.2"
|
||||
|
||||
rsvp@^4.8.4:
|
||||
version "4.8.5"
|
||||
@@ -9166,6 +9181,16 @@ safe-regex@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
sander@^0.5.0:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/sander/-/sander-0.5.1.tgz#741e245e231f07cafb6fdf0f133adfa216a502ad"
|
||||
integrity sha1-dB4kXiMfB8r7b98PEzrfohalAq0=
|
||||
dependencies:
|
||||
es6-promise "^3.1.2"
|
||||
graceful-fs "^4.1.3"
|
||||
mkdirp "^0.5.1"
|
||||
rimraf "^2.5.2"
|
||||
|
||||
sane@^4.0.3:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded"
|
||||
@@ -9550,6 +9575,16 @@ socks@^2.3.3:
|
||||
ip "^1.1.5"
|
||||
smart-buffer "^4.1.0"
|
||||
|
||||
sorcery@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/sorcery/-/sorcery-0.10.0.tgz#8ae90ad7d7cb05fc59f1ab0c637845d5c15a52b7"
|
||||
integrity sha1-iukK19fLBfxZ8asMY3hF1cFaUrc=
|
||||
dependencies:
|
||||
buffer-crc32 "^0.2.5"
|
||||
minimist "^1.2.0"
|
||||
sander "^0.5.0"
|
||||
sourcemap-codec "^1.3.0"
|
||||
|
||||
source-list-map@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
||||
@@ -9602,7 +9637,7 @@ source-map@^0.7.3, source-map@~0.7.2:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||
|
||||
sourcemap-codec@^1.4.4:
|
||||
sourcemap-codec@^1.3.0, sourcemap-codec@^1.4.4:
|
||||
version "1.4.8"
|
||||
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
||||
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
||||
@@ -10064,15 +10099,27 @@ svelte-preprocess@^4.0.0:
|
||||
detect-indent "^6.0.0"
|
||||
strip-indent "^3.0.0"
|
||||
|
||||
svelte-preprocess@^4.9.5:
|
||||
version "4.9.5"
|
||||
resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-4.9.5.tgz#e11bdf3fcdacbd90188cdf29a7371030991f9eba"
|
||||
integrity sha512-RbJbtuwKbuZq9RyzlljZUmmFNaojrg/zUEyDrS8io7haTcuITQmE4NERx8qiqHreApo6cQst5Qtp4MxUwr58Ew==
|
||||
dependencies:
|
||||
"@types/pug" "^2.0.4"
|
||||
"@types/sass" "^1.16.0"
|
||||
detect-indent "^6.0.0"
|
||||
magic-string "^0.25.7"
|
||||
sorcery "^0.10.0"
|
||||
strip-indent "^3.0.0"
|
||||
|
||||
svelte-select@^3.17.0:
|
||||
version "3.17.0"
|
||||
resolved "https://registry.yarnpkg.com/svelte-select/-/svelte-select-3.17.0.tgz#6bea0cb8d0c9465d28a2bac562f2de8a61f48a9f"
|
||||
integrity sha512-ITmX/XUiSdkaILmsTviKRkZPaXckM5/FA7Y8BhiUPoamaZG/ZDyOo6ydjFu9fDVFTbwoAUGUi6HBjs+ZdK2AwA==
|
||||
|
||||
svelte@^3.35.0:
|
||||
version "3.35.0"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.35.0.tgz#e0d0ba60c4852181c2b4fd851194be6fda493e65"
|
||||
integrity sha512-gknlZkR2sXheu/X+B7dDImwANVvK1R0QGQLd8CNIfxxGPeXBmePnxfzb6fWwTQRsYQG7lYkZXvpXJvxvpsoB7g==
|
||||
svelte@^3.43.0:
|
||||
version "3.43.0"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.43.0.tgz#d28d06ec523bf0cea3de15558a3241c339a85762"
|
||||
integrity sha512-T2pMPHrxXp+SM8pLLUXLQgkdo+JhTls7aqj9cD7z8wT2ccP+OrCAmtQS7h6pvMjitaZhXFNnCK582NxDpy8HSw==
|
||||
|
||||
symbol-tree@^3.2.2, symbol-tree@^3.2.4:
|
||||
version "3.2.4"
|
||||
@@ -10456,6 +10503,11 @@ tslib@^2.0.0, tslib@^2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
|
||||
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
|
||||
|
||||
tslib@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
|
||||
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
|
||||
|
||||
tty-browserify@0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
|
||||
@@ -10530,15 +10582,10 @@ typescript@*:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72"
|
||||
integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==
|
||||
|
||||
typescript@^3.7.4, typescript@^3.7.5:
|
||||
version "3.7.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
|
||||
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
|
||||
|
||||
typescript@^3.9.3:
|
||||
version "3.9.9"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.9.tgz#e69905c54bc0681d0518bd4d587cc6f2d0b1a674"
|
||||
integrity sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==
|
||||
typescript@^4.4.3:
|
||||
version "4.4.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.3.tgz#bdc5407caa2b109efd4f82fe130656f977a29324"
|
||||
integrity sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==
|
||||
|
||||
tz-offset@0.0.1:
|
||||
version "0.0.1"
|
||||
|
||||
Reference in New Issue
Block a user