Compare commits

...

94 Commits

Author SHA1 Message Date
Stela Augustinova de76df88ab Refactor export parameters in ResultTabs and JslDataGridCore to conditionally set values based on result count 2026-04-13 14:50:01 +02:00
Stela Augustinova 13977756bc Add export parameters to AllResultsTab and ResultTabs components 2026-04-13 13:41:51 +02:00
Stela Augustinova 07fae27ad6 Add export parameters to ResultTabs and QueryTab components 2026-04-13 13:16:17 +02:00
Stela Augustinova 22967d123d Add 7.1.8 entry to CHANGELOG with fixed NPM packages build 2026-04-09 16:05:50 +02:00
Stela Augustinova 3fed650254 Add postgresql optimalization to 7.1.7 changelog 2026-04-09 16:03:24 +02:00
Stela Augustinova b57b2083d3 v7.1.8 2026-04-09 15:35:13 +02:00
Stela Augustinova 1f47e8c62e v7.1.8-alpha.7 2026-04-09 15:26:28 +02:00
CI workflows d7ce653d74 chore: auto-update github workflows 2026-04-09 13:25:14 +00:00
Stela Augustinova 07c803efee Update npm publishing steps and remove unnecessary access flag 2026-04-09 15:24:48 +02:00
Stela Augustinova 26b6d9133e v7.1.8-alpha.6 2026-04-09 15:15:37 +02:00
CI workflows 146084bdb3 chore: auto-update github workflows 2026-04-09 13:14:54 +00:00
Stela Augustinova fa82b4630b Specify npm version to 11.5.1 for consistency 2026-04-09 15:14:24 +02:00
Stela Augustinova d00841030f v7.1.8-alpha.5 2026-04-09 15:01:14 +02:00
CI workflows c517bb0be6 chore: auto-update github workflows 2026-04-09 13:00:00 +00:00
Stela Augustinova e585d8be8f Add public access to npm publish commands in build workflow 2026-04-09 14:59:25 +02:00
Stela Augustinova 8be76832a5 v7.1.8-alpha.4 2026-04-09 14:54:23 +02:00
Stela Augustinova 99df266a3e v7.1.8-aplha.4 2026-04-09 14:50:56 +02:00
CI workflows 5660874992 chore: auto-update github workflows 2026-04-09 12:50:31 +00:00
Stela Augustinova b0dade9da3 Configure NPM token in build workflow 2026-04-09 14:50:11 +02:00
Stela Augustinova a533858804 v7.1.8-alpha.3 2026-04-09 14:20:39 +02:00
CI workflows d3bcc984e7 chore: auto-update github workflows 2026-04-09 12:18:26 +00:00
Stela Augustinova 99e8307a80 Enable NPM token configuration in build workflow 2026-04-09 14:17:59 +02:00
Stela Augustinova 73926ea392 v7.1.8-alpha.2 2026-04-09 14:12:17 +02:00
CI workflows 5ff24526b7 chore: auto-update github workflows 2026-04-09 12:11:15 +00:00
Stela Augustinova 32ed1c57bd Update Node.js setup to use yarn caching and remove npm install step 2026-04-09 14:10:50 +02:00
Stela Augustinova f4c3a95348 v7.1.8-alpha.1 2026-04-09 14:02:38 +02:00
CI workflows b1a908343a chore: auto-update github workflows 2026-04-09 11:58:25 +00:00
Stela Augustinova 7f9d7eb36e Update Node.js setup action and enable npm caching 2026-04-09 13:57:51 +02:00
Stela Augustinova 30820e29fc Update CHANGELOG for version 7.1.7 2026-04-09 13:23:07 +02:00
Stela Augustinova a85ea2e0f7 v7.1.7 2026-04-09 12:56:57 +02:00
Stela Augustinova 993e713955 v7.1.7-premium-beta.5 2026-04-09 12:11:02 +02:00
Stela Augustinova 3151e30db1 SYNC: Update translations 2026-04-09 08:59:26 +00:00
Jan Prochazka eb5219dd68 Merge pull request #1422 from dbgate/feature/duplicate-translation-keys
Remove duplicate translation keys
2026-04-09 10:49:30 +02:00
Stela Augustinova bb44783369 Refactor translation keys to eliminate duplicates in QueryTab component 2026-04-09 10:33:33 +02:00
CI workflows 33b46c4db3 chore: auto-update github workflows 2026-04-09 08:24:34 +00:00
Jan Prochazka 3730aae62a Merge pull request #1419 from dbgate/feature/map-referer
Added referer
2026-04-09 10:24:25 +02:00
CI workflows 065062d58a Update pro ref 2026-04-09 08:24:16 +00:00
Jan Prochazka 7b2f58e68e SYNC: Merge pull request #92 from dbgate/feature/ai-toggle 2026-04-09 08:24:02 +00:00
Stela Augustinova e2fc23fcf8 Remove duplicate translation keys 2026-04-09 10:12:39 +02:00
SPRINX0\prochazka 6f56ef284d v7.1.7-premium-beta.4 2026-04-08 16:14:19 +02:00
SPRINX0\prochazka 08a644ba39 v7.1.7-premuim-beta.4 2026-04-08 16:07:40 +02:00
CI workflows 6ae19ac4a6 chore: auto-update github workflows 2026-04-08 14:06:22 +00:00
CI workflows 7761cbe81d Update pro ref 2026-04-08 14:05:57 +00:00
Jan Prochazka f981d88150 SYNC: Merge pull request #91 from dbgate/feature/query-history-per-user 2026-04-08 14:05:40 +00:00
CI workflows e2a23eaa0d chore: auto-update github workflows 2026-04-08 12:57:03 +00:00
CI workflows 9d510b3c08 Update pro ref 2026-04-08 12:56:40 +00:00
SPRINX0\prochazka a98f5ac45e reverted yarn.lock 2026-04-08 14:03:13 +02:00
SPRINX0\prochazka b989e964c0 v7.1.7-premium-beta.3 2026-04-08 13:34:11 +02:00
CI workflows 3ff6eefa06 chore: auto-update github workflows 2026-04-08 11:29:47 +00:00
CI workflows 67fde9be3c Update pro ref 2026-04-08 11:29:28 +00:00
SPRINX0\prochazka df7ac89723 SYNC: v7.1.7-premium-beta.2 2026-04-08 11:29:18 +00:00
SPRINX0\prochazka 358df9f53b SYNC: try to fix ms entra login 2026-04-08 11:29:15 +00:00
Stela Augustinova 02e3bfaa8a Added referer 2026-04-08 12:05:42 +02:00
Jan Prochazka dde74fa73b Merge pull request #1407 from dbgate/feature/postgres-optimalization
Feature/postgres optimalization
2026-04-08 11:46:42 +02:00
SPRINX0\prochazka 100e3fe75f deleted sast workflows 2026-04-08 10:59:29 +02:00
SPRINX0\prochazka af7930cea2 Enhance aggregation functions in SQL queries for improved PostgreSQL compatibility 2026-04-08 10:55:24 +02:00
SPRINX0\prochazka 6b4f6b909c Merge branch 'feature/postgres-optimalization' of https://github.com/dbgate/dbgate into feature/postgres-optimalization 2026-04-08 10:26:35 +02:00
Jan Prochazka 9a6e5cd7cc Update plugins/dbgate-plugin-postgres/src/backend/sql/views.js
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-08 10:21:22 +02:00
SPRINX0\prochazka 9f64b6ec7a Merge branch 'master' into feature/postgres-optimalization 2026-04-08 10:20:28 +02:00
Stela Augustinova 77f720e34c Refactor connection handling in extractShellConnection to improve volatile ID management and ensure secure credential handling 2026-04-08 10:20:09 +02:00
Stela Augustinova 168dcb7824 Enhance error handling for connection requests in subprocesses and validate connection ID format 2026-04-08 10:20:09 +02:00
Stela Augustinova 759186a212 Improve error handling for volatile connection responses in subprocess communication 2026-04-08 10:20:09 +02:00
Stela Augustinova 71ed7a76ea Handle errors in volatile connection resolution and remove unused registration function 2026-04-08 10:20:09 +02:00
Stela Augustinova bd939b22c7 Fix volatile connection resolution to prevent multiple resolves 2026-04-08 10:20:09 +02:00
Stela Augustinova c327f77294 Refactor volatile connections handling in connections and runners modules 2026-04-08 10:20:09 +02:00
Stela Augustinova d907d79beb Streamline volatile connections handling and remove unused registration module 2026-04-08 10:20:09 +02:00
Stela Augustinova 93b879927c Implement volatile connections handling in runners and shell modules 2026-04-08 10:20:09 +02:00
Stela Augustinova 0c545d4cf9 Enhance clipboard formatters to skip empty rows, improving data handling in clipboard operations 2026-04-08 10:20:09 +02:00
Stela Augustinova 95c90c1517 Improve clipboard formatters to omit undefined values, enhancing data integrity in exports 2026-04-08 10:20:09 +02:00
CI workflows cb731fa858 chore: auto-update github workflows 2026-04-08 10:20:09 +02:00
Stela Augustinova 9bb3b09ecf SYNC: Add SAST workflow for security scanning using Semgrep 2026-04-08 10:20:09 +02:00
SPRINX0\prochazka 7c8f541d3e deleted sast workflow 2026-04-08 10:18:37 +02:00
Jan Prochazka ce41687382 Merge pull request #1417 from dbgate/feature/auth-error
Implement volatile connections handling in runners and shell modules
2026-04-08 10:14:02 +02:00
Stela Augustinova 4b083dea5c Refactor connection handling in extractShellConnection to improve volatile ID management and ensure secure credential handling 2026-04-07 14:56:29 +02:00
Stela Augustinova c84473c1eb Enhance error handling for connection requests in subprocesses and validate connection ID format 2026-04-07 14:26:58 +02:00
Stela Augustinova 7fc078f3e6 Improve error handling for volatile connection responses in subprocess communication 2026-04-07 14:15:18 +02:00
Stela Augustinova cbbd538248 Handle errors in volatile connection resolution and remove unused registration function 2026-04-07 14:01:13 +02:00
Stela Augustinova 825f6e562b Fix volatile connection resolution to prevent multiple resolves 2026-04-07 13:46:34 +02:00
Stela Augustinova a278afb260 Refactor volatile connections handling in connections and runners modules 2026-04-07 13:42:11 +02:00
Stela Augustinova 2fbeea717c Streamline volatile connections handling and remove unused registration module 2026-04-07 13:26:16 +02:00
Jan Prochazka c7259e4663 Merge pull request #1412 from dbgate/feature/copy-sql
Improve clipboard formatters to omit undefined values, enhancing data…
2026-04-07 13:11:49 +02:00
Stela Augustinova 69a2669342 Implement volatile connections handling in runners and shell modules 2026-04-07 13:06:04 +02:00
CI workflows 42d1ca8fd4 chore: auto-update github workflows 2026-04-07 10:27:40 +00:00
Stela Augustinova 1cf52d8b39 SYNC: Add SAST workflow for security scanning using Semgrep 2026-04-07 10:27:24 +00:00
Jan Prochazka 6e482afab2 v7.1.7-premium-beta.1 2026-04-02 16:39:06 +02:00
SPRINX0\prochazka ddf3295e6d Merge branch 'master' into feature/postgres-optimalization 2026-04-02 16:33:25 +02:00
SPRINX0\prochazka 79e087abd3 Optimize PostgreSQL analysis queries and add support for Info Schema routines 2026-04-02 16:32:36 +02:00
CI workflows a7cf51bdf7 chore: auto-update github workflows 2026-04-02 13:55:33 +00:00
Jan Prochazka dfdb31e2f8 Merge pull request #1413 from dbgate/feature/integration-test-pro
Update test workflow to include directory changes for integration tests
2026-04-02 15:55:14 +02:00
Stela Augustinova 137fc6b928 Enhance clipboard formatters to skip empty rows, improving data handling in clipboard operations 2026-04-02 10:29:02 +02:00
Stela Augustinova 9d1d7b7e34 Improve clipboard formatters to omit undefined values, enhancing data integrity in exports 2026-04-01 15:49:35 +02:00
SPRINX0\prochazka 899aec2658 v7.1.5-premium-beta.1 2026-03-20 14:24:11 +01:00
SPRINX0\prochazka 74e47587e2 Merge branch 'master' into feature/postgres-optimalization 2026-03-20 14:23:40 +01:00
SPRINX0\prochazka cb70f3c318 postgres loading optimalization 2026-03-19 12:17:29 +01:00
57 changed files with 1900 additions and 1315 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 6b5e2ff831db9baedb2a43862daa4247810b15de
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 6b5e2ff831db9baedb2a43862daa4247810b15de
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
+1 -1
View File
@@ -39,7 +39,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 6b5e2ff831db9baedb2a43862daa4247810b15de
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
+1 -1
View File
@@ -44,7 +44,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 6b5e2ff831db9baedb2a43862daa4247810b15de
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
+2 -2
View File
@@ -35,7 +35,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 6b5e2ff831db9baedb2a43862daa4247810b15de
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -53,7 +53,7 @@ jobs:
cd dbgate-merged
node adjustNpmPackageJsonPremium
- name: Update npm
run: npm install -g npm@latest
run: npm install -g npm@11.5.1
- name: Remove dbmodel - should be not published
run: |
cd ..
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
with:
node-version: 22.x
- name: Update npm
run: npm install -g npm@latest
run: npm install -g npm@11.5.1
- name: yarn install
run: |
yarn install
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 6b5e2ff831db9baedb2a43862daa4247810b15de
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
+23
View File
@@ -23,26 +23,49 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- name: Checkout dbgate/dbgate-pro
uses: actions/checkout@v2
with:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
mv dbgate-pro/* ../dbgate-pro/
cd ..
mkdir dbgate-merged
cd dbgate-pro
cd sync
yarn
node sync.js --nowatch
cd ..
- name: yarn install
run: |
cd ../dbgate-merged
yarn install
- name: Integration tests
run: |
cd ../dbgate-merged
cd integration-tests
yarn test:ci
- name: Filter parser tests
if: always()
run: |
cd ../dbgate-merged
cd packages/filterparser
yarn test:ci
- name: Datalib (perspective) tests
if: always()
run: |
cd ../dbgate-merged
cd packages/datalib
yarn test:ci
- name: Tools tests
if: always()
run: |
cd ../dbgate-merged
cd packages/tools
yarn test:ci
services:
+1
View File
@@ -6,3 +6,4 @@
- GUI uses Svelte4 (packages/web)
- GUI is tested with E2E tests in `e2e-tests` folder, using Cypress. Use data-testid attribute in components to make them easier to test.
- data-testid format: ComponentName_identifier. Use reasonable identifiers
- don't change content of storageModel.js - this is generated from table YAMLs with "yarn storage-json" command
+20
View File
@@ -9,6 +9,26 @@ Builds:
- linux - application for linux
- win - application for Windows
## 7.1.8
- FIXED: NPM packages build
## 7.1.7
- FIXED: Resolved duplicate translation tags #1420
- FIXED: Referer error on map display #1418
- FIXED: Export failure when password mode is enabled #1409
- FIXED: Unreadable text in export #1408
- FIXED: Column names set to "undefined" in export #1406
- FIXED: Fixed freezing issues with large fields #1399
- ADDED: "Fetch All" button #1398
- ADDED: Option to disable AI features
- ADDED: PostgreSQL loading optimalization
## 7.1.6
- FIXED: Issues with cloud and file loading
## 7.1.5
- FIXED: Issues with cloud and file loading
+8
View File
@@ -400,6 +400,14 @@ function createWindow() {
},
});
mainWindow.webContents.session.webRequest.onBeforeSendHeaders(
{ urls: ['https://*.tile.openstreetmap.org/*'] },
(details, callback) => {
details.requestHeaders['Referer'] = 'https://www.dbgate.io';
callback({ requestHeaders: details.requestHeaders });
}
);
if (initialConfig['winIsMaximized']) {
mainWindow.maximize();
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"private": true,
"version": "7.1.6",
"version": "7.1.8",
"name": "dbgate-all",
"workspaces": [
"packages/*",
+55 -1
View File
@@ -492,7 +492,61 @@ module.exports = {
return mask && !platformInfo.allowShellConnection ? maskConnection(res) : encryptConnection(res);
}
const res = await this.datastore.get(conid);
return res || null;
if (res) return res;
// In a forked runner-script child process, ask the parent for connections that may be
// volatile (in-memory only, e.g. ask-for-password). We only do this when
// there really is a parent (process.send exists) to avoid an infinite loop
// when the parent's own getCore falls through here.
// The check is intentionally narrow: only runner scripts pass
// --process-display-name script, so connect/session/ssh-forward subprocesses
// are not affected and continue to return null immediately.
if (process.send && processArgs.processDisplayName === 'script') {
const conn = await new Promise(resolve => {
let resolved = false;
const cleanup = () => {
process.removeListener('message', handler);
process.removeListener('disconnect', onDisconnect);
clearTimeout(timeout);
};
const settle = value => {
if (!resolved) {
resolved = true;
cleanup();
resolve(value);
}
};
const handler = message => {
if (message?.msgtype === 'volatile-connection-response' && message.conid === conid) {
settle(message.conn || null);
}
};
const onDisconnect = () => settle(null);
const timeout = setTimeout(() => settle(null), 5000);
// Don't let the timer alone keep the process alive if all other work is done
timeout.unref();
process.on('message', handler);
process.once('disconnect', onDisconnect);
try {
process.send({ msgtype: 'get-volatile-connection', conid });
} catch {
settle(null);
}
});
if (conn) {
volatileConnections[conn._id] = conn; // cache for subsequent calls
return conn;
}
}
return null;
},
get_meta: true,
+22 -6
View File
@@ -33,19 +33,35 @@ function readCore(reader, skip, limit, filter) {
});
}
module.exports = {
read_meta: true,
async read({ skip, limit, filter }) {
function readJsonl({ skip, limit, filter }) {
return new Promise(async (resolve, reject) => {
const fileName = path.join(datadir(), 'query-history.jsonl');
// @ts-ignore
if (!(await fs.exists(fileName))) return [];
if (!(await fs.exists(fileName))) return resolve([]);
const reader = fsReverse(fileName);
const res = await readCore(reader, skip, limit, filter);
return res;
resolve(res);
});
}
module.exports = {
read_meta: true,
async read({ skip, limit, filter }, req) {
const storage = require('./storage');
const storageResult = await storage.readQueryHistory({ skip, limit, filter }, req);
if (storageResult) return storageResult;
return readJsonl({ skip, limit, filter });
},
write_meta: true,
async write({ data }) {
async write({ data }, req) {
const storage = require('./storage');
const written = await storage.writeQueryHistory({ data }, req);
if (written) {
socket.emit('query-history-changed');
return 'OK';
}
const fileName = path.join(datadir(), 'query-history.jsonl');
await fs.appendFile(fileName, JSON.stringify(data) + '\n');
socket.emit('query-history-changed');
+21
View File
@@ -196,6 +196,27 @@ module.exports = {
// @ts-ignore
const { msgtype } = message;
if (handleProcessCommunication(message, subprocess)) return;
if (msgtype === 'get-volatile-connection') {
const connections = require('./connections');
// @ts-ignore
const conid = message.conid;
if (!conid || typeof conid !== 'string') return;
const trySend = payload => {
if (!subprocess.connected) return;
try {
subprocess.send(payload);
} catch {
// child disconnected between the check and the send — ignore
}
};
connections.getCore({ conid }).then(conn => {
trySend({ msgtype: 'volatile-connection-response', conid, conn: conn?.unsaved ? conn : null });
}).catch(err => {
logger.error({ ...extractErrorLogData(err), conid }, 'DBGM-00000 Error resolving volatile connection for child process');
trySend({ msgtype: 'volatile-connection-response', conid, conn: null });
});
return;
}
this[`handle_${msgtype}`](runid, message);
});
return _.pick(newOpened, ['runid']);
+1
View File
@@ -7,6 +7,7 @@ async function runScript(func) {
if (processArgs.checkParent) {
childProcessChecker();
}
try {
await func();
process.exit(0);
+108
View File
@@ -874,6 +874,114 @@ module.exports = {
}
]
},
{
"pureName": "query_history",
"columns": [
{
"pureName": "query_history",
"columnName": "id",
"dataType": "int",
"autoIncrement": true,
"notNull": true
},
{
"pureName": "query_history",
"columnName": "created",
"dataType": "bigint",
"notNull": true
},
{
"pureName": "query_history",
"columnName": "user_id",
"dataType": "int",
"notNull": false
},
{
"pureName": "query_history",
"columnName": "role_id",
"dataType": "int",
"notNull": false
},
{
"pureName": "query_history",
"columnName": "sql",
"dataType": "text",
"notNull": false
},
{
"pureName": "query_history",
"columnName": "conid",
"dataType": "varchar(100)",
"notNull": false
},
{
"pureName": "query_history",
"columnName": "database",
"dataType": "varchar(200)",
"notNull": false
}
],
"foreignKeys": [
{
"constraintType": "foreignKey",
"constraintName": "FK_query_history_user_id",
"pureName": "query_history",
"refTableName": "users",
"deleteAction": "CASCADE",
"columns": [
{
"columnName": "user_id",
"refColumnName": "id"
}
]
},
{
"constraintType": "foreignKey",
"constraintName": "FK_query_history_role_id",
"pureName": "query_history",
"refTableName": "roles",
"deleteAction": "CASCADE",
"columns": [
{
"columnName": "role_id",
"refColumnName": "id"
}
]
}
],
"indexes": [
{
"constraintName": "idx_query_history_user_id",
"pureName": "query_history",
"constraintType": "index",
"columns": [
{
"columnName": "user_id"
}
]
},
{
"constraintName": "idx_query_history_role_id",
"pureName": "query_history",
"constraintType": "index",
"columns": [
{
"columnName": "role_id"
}
]
}
],
"primaryKey": {
"pureName": "query_history",
"constraintType": "primaryKey",
"constraintName": "PK_query_history",
"columns": [
{
"columnName": "id"
}
]
}
},
{
"pureName": "roles",
"columns": [
File diff suppressed because it is too large Load Diff
+5 -3
View File
@@ -46,7 +46,7 @@ import { isProApp } from '../utility/proTools';
import { openWebLink } from '../utility/simpleTools';
import { _t } from '../translations';
import ExportImportConnectionsModal from '../modals/ExportImportConnectionsModal.svelte';
import { getBoolSettingsValue } from '../settings/settingsTools';
import { getBoolSettingsValue, isAiDisabled } from '../settings/settingsTools';
import { __t } from '../translations';
// function themeCommand(theme: ThemeDefinition) {
@@ -753,7 +753,8 @@ if (isProApp()) {
testEnabled: () =>
getCurrentDatabase() != null &&
findEngineDriver(getCurrentDatabase()?.connection, getExtensions())?.databaseEngineTypes?.includes('sql') &&
hasPermission('dbops/chat'),
hasPermission('dbops/chat') &&
!isAiDisabled(),
onClick: () => {
openNewTab({
title: 'Chat',
@@ -776,7 +777,8 @@ if (isProApp()) {
testEnabled: () =>
getCurrentDatabase() != null &&
findEngineDriver(getCurrentDatabase()?.connection, getExtensions())?.databaseEngineTypes?.includes('graphql') &&
hasPermission('dbops/chat'),
hasPermission('dbops/chat') &&
!isAiDisabled(),
onClick: () => {
openNewTab({
title: 'GraphQL Chat',
+256 -215
View File
@@ -1,217 +1,258 @@
<script context="module" lang="ts">
import { __t } from '../translations';
const getCurrentEditor = () => getActiveComponent('JslDataGridCore');
registerCommand({
id: 'jslTableGrid.export',
category: __t('command.dataGrid', { defaultMessage: 'Data grid' }),
name: __t('command.dataGrid.export', { defaultMessage: 'Export' }),
icon: 'icon export',
keyText: 'CtrlOrCommand+E',
testEnabled: () => getCurrentEditor() != null,
onClick: () => getCurrentEditor().exportGrid(),
});
async function loadDataPage(props, offset, limit) {
const { jslid, display, formatterFunction } = props;
const response = await apiCall('jsldata/get-rows', {
jslid,
offset,
limit,
formatterFunction,
filters: display ? display.compileJslFilters() : null,
sort: display.config.sort,
});
return response;
}
function dataPageAvailable(props) {
return true;
}
async function loadRowCount(props) {
const { jslid } = props;
const response = await apiCall('jsldata/get-stats', { jslid });
return response.rowCount;
}
export let formatterPlugin;
export let formatterFunction;
</script>
<script lang="ts">
import _ from 'lodash';
import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte';
import registerCommand from '../commands/registerCommand';
import { apiCall, apiOff, apiOn } from '../utility/api';
import { registerMenu } from '../utility/contextMenu';
import createActivator, { getActiveComponent } from '../utility/createActivator';
import createQuickExportMenu from '../utility/createQuickExportMenu';
import { exportQuickExportFile } from '../utility/exportFileTools';
import useEffect from '../utility/useEffect';
import ChangeSetGrider from './ChangeSetGrider';
import LoadingDataGridCore from './LoadingDataGridCore.svelte';
import { openImportExportTab } from '../utility/importExportTools'; export let jslid;
export let display;
export let formatterFunction;
export let changeSetState;
export let dispatchChangeSet;
export let macroPreview;
export let macroValues;
export let onPublishedCellsChanged;
export const activator = createActivator('JslDataGridCore', false);
export let setLoadedRows;
let publishedCells = [];
let loadedRows = [];
let domGrid;
let changeIndex = 0;
let rowCountLoaded = null;
const throttleLoadNext = _.throttle(() => domGrid?.resetLoadedAll(), 500);
const handleJslDataStats = stats => {
if (stats.changeIndex < changeIndex) return;
changeIndex = stats.changeIndex;
rowCountLoaded = stats.rowCount;
throttleLoadNext();
};
$: effect = useEffect(() => onJslId(jslid));
function onJslId(jslidVal) {
if (jslidVal) {
apiOn(`jsldata-stats-${jslidVal}`, handleJslDataStats);
return () => {
apiOff(`jsldata-stats-${jslidVal}`, handleJslDataStats);
};
} else {
return () => {};
}
}
$: $effect;
let grider;
$: {
if (macroPreview) {
grider = new ChangeSetGrider(
loadedRows,
changeSetState,
dispatchChangeSet,
display,
macroPreview,
macroValues,
publishedCells,
true
);
}
}
$: {
if (!macroPreview) {
grider = new ChangeSetGrider(
loadedRows,
changeSetState,
dispatchChangeSet,
display,
undefined,
undefined,
undefined,
true
);
}
}
// $: grider = new RowsArrayGrider(loadedRows);
export function exportGrid() {
const initialValues = {} as any;
const archiveMatch = jslid.match(/^archive:\/\/([^/]+)\/(.*)$/);
if (archiveMatch) {
initialValues.sourceStorageType = 'archive';
initialValues.sourceArchiveFolder = archiveMatch[1];
initialValues.sourceList = [archiveMatch[2]];
initialValues[`columns_${archiveMatch[2]}`] = display.getExportColumnMap();
} else {
initialValues.sourceStorageType = 'jsldata';
initialValues.sourceJslId = jslid;
initialValues.sourceList = ['query-data'];
initialValues[`columns_query-data`] = display.getExportColumnMap();
}
openImportExportTab(initialValues);
// showModal(ImportExportModal, { initialValues });
}
const quickExportHandler = fmt => async () => {
const archiveMatch = jslid.match(/^archive:\/\/([^/]+)\/(.*)$/);
if (archiveMatch) {
exportQuickExportFile(
archiveMatch[2],
{
functionName: 'archiveReader',
props: {
folderName: archiveMatch[1],
fileName: archiveMatch[2],
},
},
fmt,
display.getExportColumnMap()
);
} else {
exportQuickExportFile(
'Query',
{
functionName: 'jslDataReader',
props: {
jslid,
},
},
fmt,
display.getExportColumnMap()
);
}
};
registerQuickExportHandler(quickExportHandler);
registerMenu(() =>
createQuickExportMenu(
quickExportHandler,
{
command: 'jslTableGrid.export',
},
{ tag: 'export' }
)
);
function handleSetLoadedRows(rows) {
loadedRows = rows;
setLoadedRows?.(rows);
}
</script>
<LoadingDataGridCore
bind:this={domGrid}
{...$$props}
setLoadedRows={handleSetLoadedRows}
onPublishedCellsChanged={value => {
publishedCells = value;
if (onPublishedCellsChanged) {
onPublishedCellsChanged(value);
}
}}
{loadDataPage}
{dataPageAvailable}
{loadRowCount}
{grider}
{rowCountLoaded}
/>
const getCurrentEditor = () => getActiveComponent('JslDataGridCore');
registerCommand({
id: 'jslTableGrid.export',
category: __t('command.dataGrid', { defaultMessage: 'Data grid' }),
name: __t('command.dataGrid.export', { defaultMessage: 'Export' }),
icon: 'icon export',
keyText: 'CtrlOrCommand+E',
testEnabled: () => getCurrentEditor() != null,
onClick: () => getCurrentEditor().exportGrid(),
});
async function loadDataPage(props, offset, limit) {
const { jslid, display, formatterFunction } = props;
const response = await apiCall('jsldata/get-rows', {
jslid,
offset,
limit,
formatterFunction,
filters: display ? display.compileJslFilters() : null,
sort: display.config.sort,
});
return response;
}
function dataPageAvailable(props) {
return true;
}
async function loadRowCount(props) {
const { jslid } = props;
const response = await apiCall('jsldata/get-stats', { jslid });
return response.rowCount;
}
export let formatterPlugin;
export let formatterFunction;
</script>
<script lang="ts">
import _ from 'lodash';
import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte';
import registerCommand from '../commands/registerCommand';
import { apiCall, apiOff, apiOn } from '../utility/api';
import { registerMenu } from '../utility/contextMenu';
import createActivator, { getActiveComponent } from '../utility/createActivator';
import createQuickExportMenu from '../utility/createQuickExportMenu';
import { exportQuickExportFile } from '../utility/exportFileTools';
import { extractShellConnectionHostable, extractShellHostConnection } from '../impexp/createImpExpScript';
import { getConnectionInfo } from '../utility/metadataLoaders';
import useEffect from '../utility/useEffect';
import ChangeSetGrider from './ChangeSetGrider';
import LoadingDataGridCore from './LoadingDataGridCore.svelte';
import { openImportExportTab } from '../utility/importExportTools';
export let jslid;
export let display;
export let formatterFunction;
export let changeSetState;
export let dispatchChangeSet;
export let macroPreview;
export let macroValues;
export let onPublishedCellsChanged;
export let exportQuery = null;
export let exportConid = null;
export let exportDatabase = null;
export const activator = createActivator('JslDataGridCore', false);
function isReadOnlyQuery(sql) {
if (!sql) return false;
const trimmed = sql
.trim()
.replace(/^\/\*[\s\S]*?\*\//g, '')
.trim();
return /^(SELECT|WITH)\b/i.test(trimmed);
}
$: safeExportQuery = exportQuery && isReadOnlyQuery(exportQuery) ? exportQuery : null;
export let setLoadedRows;
let publishedCells = [];
let loadedRows = [];
let domGrid;
let changeIndex = 0;
let rowCountLoaded = null;
const throttleLoadNext = _.throttle(() => domGrid?.resetLoadedAll(), 500);
const handleJslDataStats = stats => {
if (stats.changeIndex < changeIndex) return;
changeIndex = stats.changeIndex;
rowCountLoaded = stats.rowCount;
throttleLoadNext();
};
$: effect = useEffect(() => onJslId(jslid));
function onJslId(jslidVal) {
if (jslidVal) {
apiOn(`jsldata-stats-${jslidVal}`, handleJslDataStats);
return () => {
apiOff(`jsldata-stats-${jslidVal}`, handleJslDataStats);
};
} else {
return () => {};
}
}
$: $effect;
let grider;
$: {
if (macroPreview) {
grider = new ChangeSetGrider(
loadedRows,
changeSetState,
dispatchChangeSet,
display,
macroPreview,
macroValues,
publishedCells,
true
);
}
}
$: {
if (!macroPreview) {
grider = new ChangeSetGrider(
loadedRows,
changeSetState,
dispatchChangeSet,
display,
undefined,
undefined,
undefined,
true
);
}
}
// $: grider = new RowsArrayGrider(loadedRows);
export async function exportGrid() {
const initialValues = {} as any;
const archiveMatch = jslid.match(/^archive:\/\/([^/]+)\/(.*)$/);
if (archiveMatch) {
initialValues.sourceStorageType = 'archive';
initialValues.sourceArchiveFolder = archiveMatch[1];
initialValues.sourceList = [archiveMatch[2]];
initialValues[`columns_${archiveMatch[2]}`] = display.getExportColumnMap();
} else if (safeExportQuery && exportConid) {
initialValues.sourceStorageType = 'query';
initialValues.sourceConnectionId = exportConid;
initialValues.sourceDatabaseName = exportDatabase;
initialValues.sourceQuery = safeExportQuery;
initialValues.sourceQueryType = 'native';
initialValues.sourceList = ['query-data'];
initialValues[`columns_query-data`] = display.getExportColumnMap();
} else {
initialValues.sourceStorageType = 'jsldata';
initialValues.sourceJslId = jslid;
initialValues.sourceList = ['query-data'];
initialValues[`columns_query-data`] = display.getExportColumnMap();
}
openImportExportTab(initialValues);
// showModal(ImportExportModal, { initialValues });
}
const quickExportHandler = fmt => async () => {
const archiveMatch = jslid.match(/^archive:\/\/([^/]+)\/(.*)$/);
if (archiveMatch) {
exportQuickExportFile(
archiveMatch[2],
{
functionName: 'archiveReader',
props: {
folderName: archiveMatch[1],
fileName: archiveMatch[2],
},
},
fmt,
display.getExportColumnMap()
);
} else if (safeExportQuery && exportConid) {
const coninfo = await getConnectionInfo({ conid: exportConid });
exportQuickExportFile(
'Query',
{
functionName: 'queryReader',
props: {
...extractShellConnectionHostable(coninfo, exportDatabase),
queryType: 'native',
query: safeExportQuery,
},
hostConnection: extractShellHostConnection(coninfo, exportDatabase),
},
fmt,
display.getExportColumnMap()
);
} else {
exportQuickExportFile(
'Query',
{
functionName: 'jslDataReader',
props: {
jslid,
},
},
fmt,
display.getExportColumnMap()
);
}
};
registerQuickExportHandler(quickExportHandler);
registerMenu(() =>
createQuickExportMenu(
quickExportHandler,
{
command: 'jslTableGrid.export',
},
{ tag: 'export' }
)
);
function handleSetLoadedRows(rows) {
loadedRows = rows;
setLoadedRows?.(rows);
}
</script>
<LoadingDataGridCore
bind:this={domGrid}
{...$$props}
setLoadedRows={handleSetLoadedRows}
onPublishedCellsChanged={value => {
publishedCells = value;
if (onPublishedCellsChanged) {
onPublishedCellsChanged(value);
}
}}
{loadDataPage}
{dataPageAvailable}
{loadRowCount}
{grider}
{rowCountLoaded}
/>
@@ -6,6 +6,7 @@ import { getConnectionInfo } from '../utility/metadataLoaders';
import { findEngineDriver, findObjectLike } from 'dbgate-tools';
import { findFileFormat } from '../plugins/fileformats';
import { getCurrentConfig, getExtensions } from '../stores';
import { getVolatileRemapping } from '../utility/api';
export function getTargetName(extensions, source, values) {
const key = `targetName_${source}`;
@@ -38,6 +39,30 @@ function extractDriverApiParameters(values, direction, driver) {
export function extractShellConnection(connection, database) {
const config = getCurrentConfig();
// Case 1: connection._id is the original ID and a volatile remap exists.
// Use the volatile ID so the backend child process can look up the credentials.
const volatileId = getVolatileRemapping(connection._id);
if (volatileId !== connection._id) {
return {
_id: volatileId,
engine: connection.engine,
database,
};
}
// Case 2: apiCall.transformApiArgs already remapped the conid before the
// connection was fetched, so connection._id IS already the volatile ID and
// connection.unsaved === true. Falling through to allowShellConnection here
// would embed plaintext credentials in the generated script — always use the
// _id reference instead.
if (connection.unsaved) {
return {
_id: connection._id,
engine: connection.engine,
database,
};
}
return config.allowShellConnection
? {
..._.omitBy(
+4 -1
View File
@@ -2,6 +2,9 @@
import JslDataGrid from '../datagrid/JslDataGrid.svelte';
export let resultInfos = [];
export let exportConid = null;
export let exportDatabase = null;
export let exportQuery = null;
</script>
<div
@@ -12,7 +15,7 @@
>
{#each resultInfos as info}
<div class="wrapper">
<JslDataGrid jslid={info.jslid} multipleGridsOnTab={resultInfos.length >= 2} />
<JslDataGrid jslid={info.jslid} multipleGridsOnTab={resultInfos.length >= 2} {exportConid} {exportDatabase} {exportQuery} />
</div>
{/each}
</div>
+26 -4
View File
@@ -21,6 +21,9 @@
export let resultCount;
export let onSetFrontMatterField;
export let onGetFrontMatter;
export let exportConid = null;
export let exportDatabase = null;
export let exportQuery = null;
onMount(() => {
allResultsInOneTab = $allResultsInOneTabDefault;
@@ -74,6 +77,9 @@
component: AllResultsTab,
props: {
resultInfos,
exportConid: resultInfos.length === 1 ? exportConid : null,
exportDatabase: resultInfos.length === 1 ? exportDatabase : null,
exportQuery: resultInfos.length === 1 ? exportQuery : null,
},
},
]
@@ -82,10 +88,20 @@
isResult: true,
component: JslDataGrid,
resultIndex: info.resultIndex,
props: { jslid: info.jslid, driver, onOpenChart: () => handleOpenChart(info.resultIndex) },
props: {
jslid: info.jslid,
driver,
onOpenChart: () => handleOpenChart(info.resultIndex),
exportConid: resultInfos.length === 1 ? exportConid : null,
exportDatabase: resultInfos.length === 1 ? exportDatabase : null,
exportQuery: resultInfos.length === 1 ? exportQuery : null,
},
}))),
...charts.map((info, index) => ({
label: _t('resultTabs.chartNumber', { defaultMessage: 'Chart {number}', values: { number: info.resultIndex + 1 } }),
label: _t('resultTabs.chartNumber', {
defaultMessage: 'Chart {number}',
values: { number: info.resultIndex + 1 },
}),
isChart: true,
resultIndex: info.resultIndex,
component: JslChart,
@@ -175,8 +191,14 @@
tabs={allTabs}
menu={resultInfos.length > 0 && [
oneTab
? { text: _t('resultTabs.everyResultInSingleTab', { defaultMessage: 'Every result in single tab' }), onClick: () => setOneTabValue(false) }
: { text: _t('resultTabs.allResultsInOneTab', { defaultMessage: 'All results in one tab' }), onClick: () => setOneTabValue(true) },
? {
text: _t('resultTabs.everyResultInSingleTab', { defaultMessage: 'Every result in single tab' }),
onClick: () => setOneTabValue(false),
}
: {
text: _t('resultTabs.allResultsInOneTab', { defaultMessage: 'All results in one tab' }),
onClick: () => setOneTabValue(true),
},
]}
onUserChange={value => {
if (allTabs[value].isChart) {
@@ -36,6 +36,10 @@ export function getObjectSettingsValue(name, defaultValue) {
return res;
}
export function isAiDisabled(): boolean {
return getBoolSettingsValue('storage.disableAiFeatures', false);
}
export function getConnectionClickActionSetting(): 'connect' | 'openDetails' | 'none' {
return getStringSettingsValue('defaultAction.connectionClick', 'connect');
}
+16 -7
View File
@@ -24,7 +24,7 @@
name: __t('command.query.AiAssistant', { defaultMessage: 'AI Assistant' }),
keyText: 'Shift+Alt+A',
icon: 'icon ai',
testEnabled: () => isProApp(),
testEnabled: () => isProApp() && !isAiDisabled(),
onClick: () => getCurrentEditor().toggleAiAssistant(),
});
registerCommand({
@@ -164,7 +164,7 @@
import HorizontalSplitter from '../elements/HorizontalSplitter.svelte';
import uuidv1 from 'uuid/v1';
import ToolStripButton from '../buttons/ToolStripButton.svelte';
import { getIntSettingsValue } from '../settings/settingsTools';
import { getIntSettingsValue, isAiDisabled } from '../settings/settingsTools';
import RowsLimitModal from '../modals/RowsLimitModal.svelte';
import _ from 'lodash';
import FontIcon from '../icons/FontIcon.svelte';
@@ -197,19 +197,19 @@
},
{
value: '@',
text: _t('query.variable', { defaultMessage: '@variable' }),
text: '@' + _t('query.variable', { defaultMessage: 'variable' }),
},
{
value: ':',
text: _t('query.named', { defaultMessage: ':variable' }),
text: ':' + _t('query.variable', { defaultMessage: 'variable' }),
},
{
value: '$',
text: _t('query.variable', { defaultMessage: '$variable' }),
text: '$' + _t('query.variable', { defaultMessage: 'variable' }),
},
{
value: '#',
text: _t('query.variable', { defaultMessage: '#variable' }),
text: '#' + _t('query.variable', { defaultMessage: 'variable' }),
},
];
@@ -232,6 +232,7 @@
let splitterInitialValue = undefined;
let autoDetectCharts = false;
let domResultTabs;
let lastExecutedSql = null;
const queryRowsLimitLocalStorageKey = `tabdata_limitRows_${tabid}`;
function getInitialRowsLimit() {
@@ -253,6 +254,10 @@
let isAiAssistantVisible = isProApp() && localStorage.getItem(`tabdata_isAiAssistantVisible_${tabid}`) == 'true';
let domAiAssistant;
$: if ($settingsValue?.['storage.disableAiFeatures']) {
isAiAssistantVisible = false;
}
onMount(() => {
intervalId = setInterval(() => {
if (!driver?.singleConnectionOnly && sessionId) {
@@ -382,6 +387,7 @@
return;
}
lastExecutedSql = sql;
executeStartLine = startLine;
executeNumber++;
visibleResultTabs = true;
@@ -619,7 +625,7 @@
}
async function handleExplainError(errorObject) {
if (!isProApp()) return;
if (!isProApp() || isAiDisabled()) return;
isAiAssistantVisible = true;
await tick();
domAiAssistant?.explainError({
@@ -780,6 +786,9 @@
{driver}
onSetFrontMatterField={handleSetFrontMatterField}
onGetFrontMatter={() => getSqlFrontMatter($editorValue, yaml)}
exportConid={conid}
exportDatabase={database}
exportQuery={queryRowsLimit ? lastExecutedSql : null}
>
<svelte:fragment slot="0">
<SocketMessageView
+6 -2
View File
@@ -20,8 +20,11 @@
import SQLEditorSettings from '../settings/SQLEditorSettings.svelte';
import AiSettingsTab from '../settings/AiSettingsTab.svelte';
import hasPermission from '../utility/hasPermission';
import { useSettings } from '../utility/metadataLoaders';
import { openedTabs } from '../stores';
const settings = useSettings();
export let selectedItem = 'general';
export let tabid = null;
@@ -33,7 +36,7 @@
);
}
const menuItems = [
$: menuItems = [
{
label: _t('settings.general', { defaultMessage: 'General' }),
identifier: 'general',
@@ -113,7 +116,8 @@
testid: 'settings-license',
},
hasPermission('settings/change') &&
isProApp() && {
isProApp() &&
!$settings?.['storage.disableAiFeatures'] && {
label: _t('settings.AI', { defaultMessage: 'AI' }),
identifier: 'ai',
component: AiSettingsTab,
+11 -7
View File
@@ -98,29 +98,31 @@ const clipboardTextFormatter = (delimiter, headers) => (columns, rows, options)
const clipboardJsonFormatter = () => (columns, rows) => {
return JSON.stringify(
rows.map(row => _.pick(row, columns)),
rows.map(row => _.omitBy(_.pick(row, columns), _.isUndefined)),
undefined,
2
);
};
const clipboardYamlFormatter = () => (columns, rows) => {
return yaml.dump(rows.map(row => _.pick(row, columns)));
return yaml.dump(rows.map(row => _.omitBy(_.pick(row, columns), _.isUndefined)));
};
const clipboardJsonLinesFormatter = () => (columns, rows) => {
return rows.map(row => JSON.stringify(_.pick(row, columns))).join('\r\n');
return rows.map(row => JSON.stringify(_.omitBy(_.pick(row, columns), _.isUndefined))).join('\r\n');
};
const clipboardInsertsFormatter = () => (columns, rows, options) => {
const { schemaName, pureName, driver } = options;
const dmp = driver.createDumper();
for (const row of rows) {
const definedColumns = columns.filter(col => row[col] !== undefined);
if (definedColumns.length === 0) continue;
dmp.putCmd(
'^insert ^into %f (%,i) ^values (%,v)',
{ schemaName, pureName },
columns,
columns.map(col => row[col])
definedColumns,
definedColumns.map(col => row[col])
);
}
return dmp.s;
@@ -130,8 +132,10 @@ const clipboardUpdatesFormatter = () => (columns, rows, options) => {
const { schemaName, pureName, driver, keyColumns } = options;
const dmp = driver.createDumper();
for (const row of rows) {
const definedColumns = columns.filter(col => row[col] !== undefined);
if (definedColumns.length === 0) continue;
dmp.put('^update %f ^set ', { schemaName, pureName });
dmp.putCollection(', ', columns, col => dmp.put('%i=%v', col, row[col]));
dmp.putCollection(', ', definedColumns, col => dmp.put('%i=%v', col, row[col]));
dmp.put(' ^where ');
dmp.putCollection(' ^and ', keyColumns, col => dmp.put('%i=%v', col, row[col]));
dmp.endCommand();
@@ -141,7 +145,7 @@ const clipboardUpdatesFormatter = () => (columns, rows, options) => {
const clipboardMongoInsertFormatter = () => (columns, rows, options) => {
const { pureName } = options;
return rows.map(row => `db.${pureName}.insert(${JSON.stringify(_.pick(row, columns), undefined, 2)});`).join('\n');
return rows.map(row => `db.${pureName}.insert(${JSON.stringify(_.omitBy(_.pick(row, columns), _.isUndefined), undefined, 2)});`).join('\n');
};
export function formatClipboardRows(format, columns, rows, options) {
@@ -2,9 +2,12 @@
import { rightPanelWidget } from '../stores';
import hasPermission from '../utility/hasPermission';
import { isProApp } from '../utility/proTools';
import { useSettings } from '../utility/metadataLoaders';
import ThemeAiAssistantWidget from '../ai/ThemeAiAssistantWidget.svelte';
const settings = useSettings();
</script>
{#if $rightPanelWidget == 'themeAiAssistant' && hasPermission('widgets/themeAiAssistant') && isProApp()}
{#if $rightPanelWidget == 'themeAiAssistant' && hasPermission('widgets/themeAiAssistant') && isProApp() && !$settings?.['storage.disableAiFeatures']}
<ThemeAiAssistantWidget />
{/if}
@@ -72,6 +72,8 @@ class Analyser extends DatabaseAnalyser {
...replacements,
$typeAggFunc: this.driver.dialect.stringAgg ? 'string_agg' : 'max',
$typeAggParam: this.driver.dialect.stringAgg ? ", '|'" : '',
$hashColumnAggTail: this.driver.dialect.stringAgg ? ", ',' ORDER BY a.attnum" : '',
$hashConstraintAggTail: this.driver.dialect.stringAgg ? ", ',' ORDER BY con.conname" : '',
$md5Function: this.dialect?.isFipsComplianceOn ? 'LENGTH' : 'MD5',
});
return query;
@@ -83,131 +85,92 @@ class Analyser extends DatabaseAnalyser {
}
async _runAnalysis() {
this.feedback({ analysingMessage: 'DBGM-00241 Loading tables' });
const tables = await this.analyserQuery('tableList', ['tables']);
const useInfoSchema = this.driver.__analyserInternals.useInfoSchemaRoutines;
const routinesQueryName = useInfoSchema ? 'routinesInfoSchema' : 'routines';
const proceduresParametersQueryName = useInfoSchema ? 'proceduresParametersInfoSchema' : 'proceduresParameters';
this.feedback({ analysingMessage: 'DBGM-00242 Loading columns' });
const columns = await this.analyserQuery('columns', ['tables', 'views']);
this.feedback({ analysingMessage: 'DBGM-00243 Loading primary keys' });
const pkColumns = await this.analyserQuery('primaryKeys', ['tables']);
let fkColumns = null;
this.feedback({ analysingMessage: 'DBGM-00244 Loading foreign key constraints' });
// const fk_tableConstraints = await this.analyserQuery('fk_tableConstraints', ['tables']);
this.feedback({ analysingMessage: 'DBGM-00245 Loading foreign key refs' });
const foreignKeys = await this.analyserQuery('foreignKeys', ['tables']);
this.feedback({ analysingMessage: 'DBGM-00246 Loading foreign key columns' });
const fk_keyColumnUsage = await this.analyserQuery('fk_keyColumnUsage', ['tables']);
// const cntKey = x => `${x.constraint_name}|${x.constraint_schema}`;
const fkRows = [];
// const fkConstraintDct = _.keyBy(fk_tableConstraints.rows, cntKey);
for (const fkRef of foreignKeys.rows) {
// const cntBase = fkConstraintDct[cntKey(fkRef)];
// const cntRef = fkConstraintDct[`${fkRef.unique_constraint_name}|${fkRef.unique_constraint_schema}`];
// if (!cntBase || !cntRef) continue;
const baseCols = _.sortBy(
fk_keyColumnUsage.rows.filter(
x =>
x.table_name == fkRef.table_name &&
x.constraint_name == fkRef.constraint_name &&
x.table_schema == fkRef.table_schema
),
'ordinal_position'
);
const refCols = _.sortBy(
fk_keyColumnUsage.rows.filter(
x =>
x.table_name == fkRef.ref_table_name &&
x.constraint_name == fkRef.unique_constraint_name &&
x.table_schema == fkRef.ref_table_schema
),
'ordinal_position'
);
if (baseCols.length != refCols.length) continue;
for (let i = 0; i < baseCols.length; i++) {
const baseCol = baseCols[i];
const refCol = refCols[i];
fkRows.push({
...fkRef,
pure_name: fkRef.table_name,
schema_name: fkRef.table_schema,
ref_table_name: fkRef.ref_table_name,
ref_schema_name: fkRef.ref_table_schema,
column_name: baseCol.column_name,
ref_column_name: refCol.column_name,
update_action: fkRef.update_action,
delete_action: fkRef.delete_action,
});
}
}
fkColumns = { rows: fkRows };
this.feedback({ analysingMessage: 'DBGM-00247 Loading views' });
const views = await this.analyserQuery('views', ['views']);
this.feedback({ analysingMessage: 'DBGM-00248 Loading materialized views' });
const matviews = this.driver.dialect.materializedViews ? await this.analyserQuery('matviews', ['matviews']) : null;
this.feedback({ analysingMessage: 'DBGM-00249 Loading materialized view columns' });
const matviewColumns = this.driver.dialect.materializedViews
? await this.analyserQuery('matviewColumns', ['matviews'])
: null;
this.feedback({ analysingMessage: 'DBGM-00250 Loading routines' });
const routines = await this.analyserQuery('routines', ['procedures', 'functions']);
this.feedback({ analysingMessage: 'DBGM-00251 Loading routine parameters' });
const routineParametersRows = await this.analyserQuery('proceduresParameters');
this.feedback({ analysingMessage: 'DBGM-00252 Loading indexes' });
const indexes = this.driver.__analyserInternals.skipIndexes
? { rows: [] }
: await this.analyserQuery('indexes', ['tables']);
this.feedback({ analysingMessage: 'DBGM-00253 Loading index columns' });
const indexcols = this.driver.__analyserInternals.skipIndexes
? { rows: [] }
: await this.analyserQuery('indexcols', ['tables']);
this.feedback({ analysingMessage: 'DBGM-00254 Loading unique names' });
const uniqueNames = await this.analyserQuery('uniqueNames', ['tables']);
// Run all independent queries in parallel
this.feedback({ analysingMessage: 'DBGM-00241 Loading database structure' });
const [
tables,
views,
columns,
pkColumns,
foreignKeys,
uniqueNames,
routines,
routineParametersRows,
indexes,
indexcols,
matviews,
matviewColumns,
triggers,
] = await Promise.all([
this.analyserQuery('tableList', ['tables']),
this.analyserQuery('views', ['views']),
this.analyserQuery('columns', ['tables', 'views']),
this.analyserQuery('primaryKeys', ['tables']),
this.analyserQuery('foreignKeys', ['tables']),
this.analyserQuery('uniqueNames', ['tables']),
this.analyserQuery(routinesQueryName, ['procedures', 'functions']),
this.analyserQuery(proceduresParametersQueryName),
this.driver.__analyserInternals.skipIndexes
? Promise.resolve({ rows: [] })
: this.analyserQuery('indexes', ['tables']),
this.driver.__analyserInternals.skipIndexes
? Promise.resolve({ rows: [] })
: this.analyserQuery('indexcols', ['tables']),
this.driver.dialect.materializedViews
? this.analyserQuery('matviews', ['matviews'])
: Promise.resolve(null),
this.driver.dialect.materializedViews
? this.analyserQuery('matviewColumns', ['matviews'])
: Promise.resolve(null),
this.analyserQuery('triggers'),
]);
// Load geometry/geography columns if the views exist (these are rare, so run after views are loaded)
let geometryColumns = { rows: [] };
if (views.rows.find(x => x.pure_name == 'geometry_columns' && x.schema_name == 'public')) {
this.feedback({ analysingMessage: 'DBGM-00255 Loading geometry columns' });
geometryColumns = await this.analyserQuery('geometryColumns', ['tables']);
}
let geographyColumns = { rows: [] };
if (views.rows.find(x => x.pure_name == 'geography_columns' && x.schema_name == 'public')) {
this.feedback({ analysingMessage: 'DBGM-00256 Loading geography columns' });
geographyColumns = await this.analyserQuery('geographyColumns', ['tables']);
const hasGeometry = views.rows.find(x => x.pure_name == 'geometry_columns' && x.schema_name == 'public');
const hasGeography = views.rows.find(x => x.pure_name == 'geography_columns' && x.schema_name == 'public');
if (hasGeometry || hasGeography) {
const [geomCols, geogCols] = await Promise.all([
hasGeometry
? this.analyserQuery('geometryColumns', ['tables'])
: Promise.resolve({ rows: [] }),
hasGeography
? this.analyserQuery('geographyColumns', ['tables'])
: Promise.resolve({ rows: [] }),
]);
geometryColumns = geomCols;
geographyColumns = geogCols;
}
this.feedback({ analysingMessage: 'DBGM-00257 Loading triggers' });
const triggers = await this.analyserQuery('triggers');
this.feedback({ analysingMessage: 'DBGM-00258 Finalizing DB structure' });
const columnColumnsMapped = fkColumns.rows.map(x => ({
pureName: x.pure_name,
schemaName: x.schema_name,
constraintSchema: x.constraint_schema,
// Pre-build lookup maps for O(1) access instead of O(n) scanning per table/view
const columnsByTable = _.groupBy(columns.rows, x => `${x.schema_name}.${x.pure_name}`);
const indexcolsByOidAttnum = _.keyBy(indexcols.rows, x => `${x.oid}_${x.attnum}`);
const uniqueNameSet = new Set(uniqueNames.rows.map(x => x.constraint_name));
const indexesByTable = _.groupBy(indexes.rows, x => `${x.schema_name}.${x.table_name}`);
const matviewColumnsByTable = matviewColumns
? _.groupBy(matviewColumns.rows, x => `${x.schema_name}.${x.pure_name}`)
: {};
const columnColumnsMapped = foreignKeys.rows.map(x => ({
pureName: x.table_name,
schemaName: x.table_schema,
constraintName: x.constraint_name,
columnName: x.column_name,
refColumnName: x.ref_column_name,
updateAction: x.update_action,
deleteAction: x.delete_action,
refTableName: x.ref_table_name,
refSchemaName: x.ref_schema_name,
refSchemaName: x.ref_table_schema,
}));
const fkByTable = _.groupBy(columnColumnsMapped, x => `${x.schemaName}.${x.pureName}`);
const pkColumnsMapped = pkColumns.rows.map(x => ({
pureName: x.pure_name,
schemaName: x.schema_name,
@@ -215,6 +178,7 @@ class Analyser extends DatabaseAnalyser {
constraintName: x.constraint_name,
columnName: x.column_name,
}));
const pkByTable = _.groupBy(pkColumnsMapped, x => `${x.schemaName}.${x.pureName}`);
const procedureParameters = routineParametersRows.rows
.filter(i => i.routine_type == 'PROCEDURE')
@@ -252,6 +216,7 @@ class Analyser extends DatabaseAnalyser {
const res = {
tables: tables.rows.map(table => {
const tableKey = `${table.schema_name}.${table.pure_name}`;
const newTable = {
pureName: table.pure_name,
schemaName: table.schema_name,
@@ -259,20 +224,16 @@ class Analyser extends DatabaseAnalyser {
objectId: `tables:${table.schema_name}.${table.pure_name}`,
contentHash: table.hash_code_columns ? `${table.hash_code_columns}-${table.hash_code_constraints}` : null,
};
const tableIndexes = indexesByTable[tableKey] || [];
return {
...newTable,
columns: columns.rows
.filter(col => col.pure_name == table.pure_name && col.schema_name == table.schema_name)
.map(col => getColumnInfo(col, newTable, geometryColumns, geographyColumns)),
primaryKey: DatabaseAnalyser.extractPrimaryKeys(newTable, pkColumnsMapped),
foreignKeys: DatabaseAnalyser.extractForeignKeys(newTable, columnColumnsMapped),
indexes: indexes.rows
.filter(
x =>
x.table_name == table.pure_name &&
x.schema_name == table.schema_name &&
!uniqueNames.rows.find(y => y.constraint_name == x.index_name)
)
columns: (columnsByTable[tableKey] || []).map(col =>
getColumnInfo(col, newTable, geometryColumns, geographyColumns)
),
primaryKey: DatabaseAnalyser.extractPrimaryKeys(newTable, pkByTable[tableKey] || []),
foreignKeys: DatabaseAnalyser.extractForeignKeys(newTable, fkByTable[tableKey] || []),
indexes: tableIndexes
.filter(x => !uniqueNameSet.has(x.index_name))
.map(idx => {
const indOptionSplit = idx.indoption.split(' ');
return {
@@ -281,7 +242,7 @@ class Analyser extends DatabaseAnalyser {
columns: _.compact(
idx.indkey
.split(' ')
.map(colid => indexcols.rows.find(col => col.oid == idx.oid && col.attnum == colid))
.map(colid => indexcolsByOidAttnum[`${idx.oid}_${colid}`])
.filter(col => col != null)
.map((col, colIndex) => ({
columnName: col.column_name,
@@ -290,19 +251,14 @@ class Analyser extends DatabaseAnalyser {
),
};
}),
uniques: indexes.rows
.filter(
x =>
x.table_name == table.pure_name &&
x.schema_name == table.schema_name &&
uniqueNames.rows.find(y => y.constraint_name == x.index_name)
)
uniques: tableIndexes
.filter(x => uniqueNameSet.has(x.index_name))
.map(idx => ({
constraintName: idx.index_name,
columns: _.compact(
idx.indkey
.split(' ')
.map(colid => indexcols.rows.find(col => col.oid == idx.oid && col.attnum == colid))
.map(colid => indexcolsByOidAttnum[`${idx.oid}_${colid}`])
.filter(col => col != null)
.map(col => ({
columnName: col.column_name,
@@ -317,9 +273,7 @@ class Analyser extends DatabaseAnalyser {
schemaName: view.schema_name,
contentHash: view.hash_code,
createSql: `CREATE VIEW "${view.schema_name}"."${view.pure_name}"\nAS\n${view.create_sql}`,
columns: columns.rows
.filter(col => col.pure_name == view.pure_name && col.schema_name == view.schema_name)
.map(col => getColumnInfo(col)),
columns: (columnsByTable[`${view.schema_name}.${view.pure_name}`] || []).map(col => getColumnInfo(col)),
})),
matviews: matviews
? matviews.rows.map(matview => ({
@@ -328,8 +282,7 @@ class Analyser extends DatabaseAnalyser {
schemaName: matview.schema_name,
contentHash: matview.hash_code,
createSql: `CREATE MATERIALIZED VIEW "${matview.schema_name}"."${matview.pure_name}"\nAS\n${matview.definition}`,
columns: matviewColumns.rows
.filter(col => col.pure_name == matview.pure_name && col.schema_name == matview.schema_name)
columns: (matviewColumnsByTable[`${matview.schema_name}.${matview.pure_name}`] || [])
.map(col => getColumnInfo(col)),
}))
: undefined,
@@ -396,14 +349,31 @@ class Analyser extends DatabaseAnalyser {
}
async _getFastSnapshot() {
const viewModificationsQueryData = await this.analyserQuery('viewModifications');
const matviewModificationsQueryData = this.driver.dialect.materializedViews
? await this.analyserQuery('matviewModifications')
: null;
const routineModificationsQueryData = await this.analyserQuery('routineModifications');
const useInfoSchema = this.driver.__analyserInternals.useInfoSchemaRoutines;
const routineModificationsQueryName = useInfoSchema ? 'routineModificationsInfoSchema' : 'routineModifications';
// Run all modification queries in parallel
const [
tableModificationsQueryData,
viewModificationsQueryData,
matviewModificationsQueryData,
routineModificationsQueryData,
] = await Promise.all([
this.analyserQuery('tableModifications'),
this.analyserQuery('viewModifications'),
this.driver.dialect.materializedViews
? this.analyserQuery('matviewModifications')
: Promise.resolve(null),
this.analyserQuery(routineModificationsQueryName),
]);
return {
tables: null,
tables: tableModificationsQueryData.rows.map(x => ({
objectId: `tables:${x.schema_name}.${x.pure_name}`,
pureName: x.pure_name,
schemaName: x.schema_name,
contentHash: `${x.hash_code_columns}-${x.hash_code_constraints}`,
})),
views: viewModificationsQueryData.rows.map(x => ({
objectId: `views:${x.schema_name}.${x.pure_name}`,
pureName: x.pure_name,
@@ -1,22 +1,38 @@
module.exports = `
select
table_schema as "schema_name",
table_name as "pure_name",
column_name as "column_name",
is_nullable as "is_nullable",
data_type as "data_type",
character_maximum_length as "char_max_length",
numeric_precision as "numeric_precision",
numeric_scale as "numeric_scale",
column_default as "default_value"
from information_schema.columns
where
table_schema !~ '^_timescaledb_'
and (
('tables:' || table_schema || '.' || table_name) =OBJECT_ID_CONDITION
or
('views:' || table_schema || '.' || table_name) =OBJECT_ID_CONDITION
)
and table_schema =SCHEMA_NAME_CONDITION
order by ordinal_position
SELECT
n.nspname AS "schema_name",
c.relname AS "pure_name",
a.attname AS "column_name",
CASE WHEN a.attnotnull THEN 'NO' ELSE 'YES' END AS "is_nullable",
format_type(a.atttypid, NULL) AS "data_type",
CASE
WHEN a.atttypmod > 0 AND t.typname IN ('varchar', 'bpchar', 'char') THEN a.atttypmod - 4
WHEN a.atttypmod > 0 AND t.typname IN ('bit', 'varbit') THEN a.atttypmod
ELSE NULL
END AS "char_max_length",
CASE
WHEN a.atttypmod > 0 AND t.typname = 'numeric' THEN ((a.atttypmod - 4) >> 16) & 65535
ELSE NULL
END AS "numeric_precision",
CASE
WHEN a.atttypmod > 0 AND t.typname = 'numeric' THEN (a.atttypmod - 4) & 65535
ELSE NULL
END AS "numeric_scale",
pg_get_expr(d.adbin, d.adrelid) AS "default_value"
FROM pg_catalog.pg_attribute a
JOIN pg_catalog.pg_class c ON c.oid = a.attrelid
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
JOIN pg_catalog.pg_type t ON t.oid = a.atttypid
LEFT JOIN pg_catalog.pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
WHERE a.attnum > 0
AND NOT a.attisdropped
AND c.relkind IN ('r', 'v', 'p', 'f')
AND n.nspname !~ '^_timescaledb_'
AND (
('tables:' || n.nspname || '.' || c.relname) =OBJECT_ID_CONDITION
OR
('views:' || n.nspname || '.' || c.relname) =OBJECT_ID_CONDITION
)
AND n.nspname =SCHEMA_NAME_CONDITION
ORDER BY a.attnum
`;
@@ -5,7 +5,8 @@ SELECT
con.conname AS constraint_name,
nsp2.nspname AS ref_table_schema,
rel2.relname AS ref_table_name,
conpk.conname AS unique_constraint_name,
att.attname AS column_name,
att2.attname AS ref_column_name,
CASE con.confupdtype
WHEN 'a' THEN 'NO ACTION'
WHEN 'r' THEN 'RESTRICT'
@@ -13,26 +14,26 @@ SELECT
WHEN 'n' THEN 'SET NULL'
WHEN 'd' THEN 'SET DEFAULT'
ELSE con.confupdtype::text
END AS update_action,
CASE con.confdeltype
END AS update_action,
CASE con.confdeltype
WHEN 'a' THEN 'NO ACTION'
WHEN 'r' THEN 'RESTRICT'
WHEN 'c' THEN 'CASCADE'
WHEN 'n' THEN 'SET NULL'
WHEN 'd' THEN 'SET DEFAULT'
ELSE con.confdeltype::text
END AS delete_action
END AS delete_action
FROM pg_constraint con
JOIN pg_class rel ON rel.oid = con.conrelid
JOIN pg_namespace nsp ON nsp.oid = rel.relnamespace
JOIN pg_class rel2 ON rel2.oid = con.confrelid
JOIN pg_namespace nsp2 ON nsp2.oid = rel2.relnamespace
JOIN pg_constraint conpk
ON conpk.conrelid = con.confrelid
AND conpk.conkey = con.confkey
AND conpk.contype IN ('p','u') -- 'p' = primary key, 'u' = unique constraint
WHERE con.contype = 'f' AND ('tables:' || nsp.nspname || '.' || rel.relname) =OBJECT_ID_CONDITION AND nsp.nspname =SCHEMA_NAME_CONDITION
JOIN LATERAL unnest(con.conkey, con.confkey) WITH ORDINALITY AS cols(attnum, ref_attnum, ordinal_position) ON TRUE
JOIN pg_attribute att ON att.attrelid = con.conrelid AND att.attnum = cols.attnum
JOIN pg_attribute att2 ON att2.attrelid = con.confrelid AND att2.attnum = cols.ref_attnum
WHERE con.contype = 'f'
AND ('tables:' || nsp.nspname || '.' || rel.relname) =OBJECT_ID_CONDITION
AND nsp.nspname =SCHEMA_NAME_CONDITION
ORDER BY con.conname, cols.ordinal_position
;
`;
@@ -19,15 +19,16 @@ const triggers = require('./triggers');
const listDatabases = require('./listDatabases');
const listVariables = require('./listVariables');
const listProcesses = require('./listProcesses');
const fk_keyColumnUsage = require('./fk_key_column_usage');
const routinesInfoSchema = require('./routinesInfoSchema');
const proceduresParametersInfoSchema = require('./proceduresParametersInfoSchema');
const routineModificationsInfoSchema = require('./routineModificationsInfoSchema');
const tableModifications = require('./tableModifications');
module.exports = {
columns,
tableList,
viewModifications,
primaryKeys,
fk_keyColumnUsage,
foreignKeys,
views,
routines,
@@ -45,4 +46,8 @@ module.exports = {
listDatabases,
listVariables,
listProcesses,
routinesInfoSchema,
proceduresParametersInfoSchema,
routineModificationsInfoSchema,
tableModifications,
};
@@ -1,31 +1,34 @@
module.exports = `
SELECT
proc.specific_schema AS schema_name,
proc.routine_name AS pure_name,
proc.routine_type as routine_type,
args.parameter_name AS parameter_name,
args.parameter_mode,
args.data_type AS data_type,
args.ordinal_position AS parameter_index,
args.parameter_mode AS parameter_mode
FROM
information_schema.routines proc
LEFT JOIN
information_schema.parameters args
ON proc.specific_schema = args.specific_schema
AND proc.specific_name = args.specific_name
WHERE
proc.specific_schema NOT IN ('pg_catalog', 'information_schema') -- Exclude system schemas
AND args.parameter_name IS NOT NULL
AND proc.routine_type IN ('PROCEDURE', 'FUNCTION') -- Filter for procedures
AND proc.specific_schema !~ '^_timescaledb_'
AND proc.specific_schema =SCHEMA_NAME_CONDITION
SELECT
n.nspname AS "schema_name",
p.proname AS "pure_name",
CASE p.prokind WHEN 'p' THEN 'PROCEDURE' ELSE 'FUNCTION' END AS "routine_type",
a.parameter_name AS "parameter_name",
CASE (p.proargmodes::text[])[a.ordinal_position]
WHEN 'o' THEN 'OUT'
WHEN 'b' THEN 'INOUT'
WHEN 'v' THEN 'VARIADIC'
WHEN 't' THEN 'TABLE'
ELSE 'IN'
END AS "parameter_mode",
pg_catalog.format_type(a.parameter_type, NULL) AS "data_type",
a.ordinal_position AS "parameter_index"
FROM pg_catalog.pg_proc p
JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
CROSS JOIN LATERAL unnest(
COALESCE(p.proallargtypes, p.proargtypes::oid[]),
p.proargnames
) WITH ORDINALITY AS a(parameter_type, parameter_name, ordinal_position)
WHERE p.prokind IN ('f', 'p')
AND p.proargnames IS NOT NULL
AND a.parameter_name IS NOT NULL
AND n.nspname !~ '^_timescaledb_'
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
AND n.nspname =SCHEMA_NAME_CONDITION
AND (
(routine_type = 'PROCEDURE' AND ('procedures:' || proc.specific_schema || '.' || routine_name) =OBJECT_ID_CONDITION)
OR
(routine_type = 'FUNCTION' AND ('functions:' || proc.specific_schema || '.' || routine_name) =OBJECT_ID_CONDITION)
(p.prokind = 'p' AND ('procedures:' || n.nspname || '.' || p.proname) =OBJECT_ID_CONDITION)
OR
(p.prokind != 'p' AND ('functions:' || n.nspname || '.' || p.proname) =OBJECT_ID_CONDITION)
)
ORDER BY
schema_name,
args.ordinal_position;
ORDER BY n.nspname, a.ordinal_position
`;
@@ -0,0 +1,31 @@
module.exports = `
SELECT
proc.specific_schema AS schema_name,
proc.routine_name AS pure_name,
proc.routine_type as routine_type,
args.parameter_name AS parameter_name,
args.parameter_mode,
args.data_type AS data_type,
args.ordinal_position AS parameter_index,
args.parameter_mode AS parameter_mode
FROM
information_schema.routines proc
LEFT JOIN
information_schema.parameters args
ON proc.specific_schema = args.specific_schema
AND proc.specific_name = args.specific_name
WHERE
proc.specific_schema NOT IN ('pg_catalog', 'information_schema')
AND args.parameter_name IS NOT NULL
AND proc.routine_type IN ('PROCEDURE', 'FUNCTION')
AND proc.specific_schema !~ '^_timescaledb_'
AND proc.specific_schema =SCHEMA_NAME_CONDITION
AND (
(routine_type = 'PROCEDURE' AND ('procedures:' || proc.specific_schema || '.' || routine_name) =OBJECT_ID_CONDITION)
OR
(routine_type = 'FUNCTION' AND ('functions:' || proc.specific_schema || '.' || routine_name) =OBJECT_ID_CONDITION)
)
ORDER BY
schema_name,
args.ordinal_position;
`;
@@ -1,10 +1,13 @@
module.exports = `
select
routine_name as "pure_name",
routine_schema as "schema_name",
$md5Function(routine_definition) as "hash_code",
routine_type as "object_type"
from
information_schema.routines where routine_schema !~ '^_timescaledb_'
and routine_type in ('PROCEDURE', 'FUNCTION') and routine_schema =SCHEMA_NAME_CONDITION
SELECT
p.proname AS "pure_name",
n.nspname AS "schema_name",
$md5Function(p.prosrc) AS "hash_code",
CASE p.prokind WHEN 'p' THEN 'PROCEDURE' ELSE 'FUNCTION' END AS "object_type"
FROM pg_catalog.pg_proc p
JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
WHERE p.prokind IN ('f', 'p')
AND n.nspname !~ '^_timescaledb_'
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
AND n.nspname =SCHEMA_NAME_CONDITION
`;
@@ -0,0 +1,10 @@
module.exports = `
select
routine_name as "pure_name",
routine_schema as "schema_name",
$md5Function(routine_definition) as "hash_code",
routine_type as "object_type"
from
information_schema.routines where routine_schema !~ '^_timescaledb_'
and routine_type in ('PROCEDURE', 'FUNCTION') and routine_schema =SCHEMA_NAME_CONDITION
`;
@@ -1,19 +1,23 @@
module.exports = `
select
routine_name as "pure_name",
routine_schema as "schema_name",
max(routine_definition) as "definition",
max($md5Function(routine_definition)) as "hash_code",
routine_type as "object_type",
$typeAggFunc(data_type $typeAggParam) as "data_type",
max(external_language) as "language"
from
information_schema.routines where routine_schema !~ '^_timescaledb_'
and routine_schema =SCHEMA_NAME_CONDITION
and (
(routine_type = 'PROCEDURE' and ('procedures:' || routine_schema || '.' || routine_name) =OBJECT_ID_CONDITION)
or
(routine_type = 'FUNCTION' and ('functions:' || routine_schema || '.' || routine_name) =OBJECT_ID_CONDITION)
)
group by routine_name, routine_schema, routine_type
SELECT
p.proname AS "pure_name",
n.nspname AS "schema_name",
max(p.prosrc) AS "definition",
max($md5Function(p.prosrc)) AS "hash_code",
CASE max(p.prokind) WHEN 'p' THEN 'PROCEDURE' ELSE 'FUNCTION' END AS "object_type",
$typeAggFunc(pg_catalog.format_type(p.prorettype, NULL) $typeAggParam) AS "data_type",
max(l.lanname) AS "language"
FROM pg_catalog.pg_proc p
JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
JOIN pg_catalog.pg_language l ON l.oid = p.prolang
WHERE p.prokind IN ('f', 'p')
AND n.nspname !~ '^_timescaledb_'
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
AND n.nspname =SCHEMA_NAME_CONDITION
AND (
(p.prokind = 'p' AND ('procedures:' || n.nspname || '.' || p.proname) =OBJECT_ID_CONDITION)
OR
(p.prokind != 'p' AND ('functions:' || n.nspname || '.' || p.proname) =OBJECT_ID_CONDITION)
)
GROUP BY p.proname, n.nspname, p.prokind
`;
@@ -0,0 +1,19 @@
module.exports = `
select
routine_name as "pure_name",
routine_schema as "schema_name",
max(routine_definition) as "definition",
max($md5Function(routine_definition)) as "hash_code",
routine_type as "object_type",
$typeAggFunc(data_type $typeAggParam) as "data_type",
max(external_language) as "language"
from
information_schema.routines where routine_schema !~ '^_timescaledb_'
and routine_schema =SCHEMA_NAME_CONDITION
and (
(routine_type = 'PROCEDURE' and ('procedures:' || routine_schema || '.' || routine_name) =OBJECT_ID_CONDITION)
or
(routine_type = 'FUNCTION' and ('functions:' || routine_schema || '.' || routine_name) =OBJECT_ID_CONDITION)
)
group by routine_name, routine_schema, routine_type
`;
@@ -1,10 +1,35 @@
module.exports = `
select infoTables.table_schema as "schema_name", infoTables.table_name as "pure_name",
pg_relation_size('"'||infoTables.table_schema||'"."'||infoTables.table_name||'"') as "size_bytes"
from information_schema.tables infoTables
where infoTables.table_type not like '%VIEW%'
and ('tables:' || infoTables.table_schema || '.' || infoTables.table_name) =OBJECT_ID_CONDITION
and infoTables.table_schema <> 'pg_internal'
and infoTables.table_schema !~ '^_timescaledb_'
and infoTables.table_schema =SCHEMA_NAME_CONDITION
SELECT
n.nspname AS "schema_name",
c.relname AS "pure_name",
pg_relation_size(c.oid) AS "size_bytes",
$md5Function(
COALESCE(
(SELECT $typeAggFunc(
a.attname || ':' || pg_catalog.format_type(a.atttypid, a.atttypmod) || ':' || a.attnotnull::text
$hashColumnAggTail
)
FROM pg_catalog.pg_attribute a
WHERE a.attrelid = c.oid AND a.attnum > 0 AND NOT a.attisdropped),
''
)
) AS "hash_code_columns",
$md5Function(
COALESCE(
(SELECT $typeAggFunc(
con.conname || ':' || con.contype::text
$hashConstraintAggTail
)
FROM pg_catalog.pg_constraint con
WHERE con.conrelid = c.oid),
''
)
) AS "hash_code_constraints"
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r', 'p', 'f')
AND ('tables:' || n.nspname || '.' || c.relname) =OBJECT_ID_CONDITION
AND n.nspname <> 'pg_internal'
AND n.nspname !~ '^_timescaledb_'
AND n.nspname =SCHEMA_NAME_CONDITION
`;
@@ -0,0 +1,34 @@
module.exports = `
SELECT
n.nspname AS "schema_name",
c.relname AS "pure_name",
$md5Function(
COALESCE(
(SELECT $typeAggFunc(
a.attname || ':' || pg_catalog.format_type(a.atttypid, a.atttypmod) || ':' || a.attnotnull::text
$hashColumnAggTail
)
FROM pg_catalog.pg_attribute a
WHERE a.attrelid = c.oid AND a.attnum > 0 AND NOT a.attisdropped),
''
)
) AS "hash_code_columns",
$md5Function(
COALESCE(
(SELECT $typeAggFunc(
con.conname || ':' || con.contype::text
$hashConstraintAggTail
)
FROM pg_catalog.pg_constraint con
WHERE con.conrelid = c.oid),
''
)
) AS "hash_code_constraints"
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r', 'p', 'f')
AND n.nspname <> 'pg_internal'
AND n.nspname !~ '^_timescaledb_'
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
AND n.nspname =SCHEMA_NAME_CONDITION
`;
@@ -1,8 +1,13 @@
module.exports = `
select
table_name as "pure_name",
table_schema as "schema_name",
$md5Function(view_definition) as "hash_code"
from
information_schema.views where table_schema != 'information_schema' and table_schema != 'pg_catalog' and table_schema !~ '^_timescaledb_' and table_schema =SCHEMA_NAME_CONDITION
SELECT
c.relname AS "pure_name",
n.nspname AS "schema_name",
$md5Function(pg_get_viewdef(c.oid, true)) AS "hash_code"
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'v'
AND n.nspname != 'information_schema'
AND n.nspname != 'pg_catalog'
AND n.nspname !~ '^_timescaledb_'
AND n.nspname =SCHEMA_NAME_CONDITION
`;
@@ -1,11 +1,20 @@
module.exports = `
select
table_name as "pure_name",
table_schema as "schema_name",
view_definition as "create_sql",
$md5Function(view_definition) as "hash_code"
from
information_schema.views
where table_schema !~ '^_timescaledb_' and table_schema =SCHEMA_NAME_CONDITION
and ('views:' || table_schema || '.' || table_name) =OBJECT_ID_CONDITION
WITH view_defs AS (
SELECT
c.relname AS pure_name,
n.nspname AS schema_name,
pg_get_viewdef(c.oid, true) AS viewdef
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'v'
AND n.nspname !~ '^_timescaledb_'
AND n.nspname =SCHEMA_NAME_CONDITION
AND ('views:' || n.nspname || '.' || c.relname) =OBJECT_ID_CONDITION
)
SELECT
pure_name AS "pure_name",
schema_name AS "schema_name",
viewdef AS "create_sql",
$md5Function(viewdef) AS "hash_code"
FROM view_defs
`;
@@ -418,6 +418,7 @@ const redshiftDriver = {
},
__analyserInternals: {
skipIndexes: true,
useInfoSchemaRoutines: true,
},
engine: 'redshift@dbgate-plugin-postgres',
title: 'Amazon Redshift',
+9 -2
View File
@@ -207,6 +207,8 @@
"command.datagrid.editCell": "Upravit hodnotu buňky",
"command.datagrid.editJsonDocument": "Upravit řádek jako JSON dokument",
"command.datagrid.editSelection": "Upravit výběr jako tabulku",
"command.datagrid.fetchAll": "Načíst všechny řádky",
"command.datagrid.fetchAll.toolbar": "Načíst vše",
"command.datagrid.filterSelected": "Filtrovat vybranou hodnotu",
"command.datagrid.findColumn": "Najít sloupec",
"command.datagrid.generateSql": "Generovat SQL",
@@ -723,6 +725,11 @@
"datagrid.columnName": "Název sloupce",
"datagrid.columnNameFilter": "Filtr názvu sloupce",
"datagrid.copyAdvanced": "Pokročilé kopírování",
"datagrid.fetchAll.confirm": "Načíst vše",
"datagrid.fetchAll.progress": "Načítání všech řádků... načteno {count}",
"datagrid.fetchAll.progressDb": "Načítání dat z databáze...",
"datagrid.fetchAll.title": "Načíst všechny řádky",
"datagrid.fetchAll.warning": "Tímto se načtou všechny zbývající řádky do paměti. U velkých tabulek to může spotřebovat významné množství paměti a ovlivnit výkon aplikace.",
"datagrid.macros.calculation": "Výpočet",
"datagrid.macros.calculationDescription": "Vlastní výraz. Použijte řádek.název_sloupce pro přístup k hodnotám sloupců, value pro původní hodnotu",
"datagrid.macros.changeTextCase": "Změnit velikost písmen",
@@ -1238,7 +1245,6 @@
"query.groupFilter": "Filtr skupiny",
"query.isolationLevel": "Úroveň izolace",
"query.limitRows": "Omezit na {queryRowsLimit} řádků",
"query.named": ":proměnná",
"query.noParameters": "(žádné parametry)",
"query.noRowsLimit": "(bez limitu řádků)",
"query.orFilter": "NEBO filtr {number}",
@@ -1256,7 +1262,7 @@
"query.sortOrder": "Řazení",
"query.table": "Tabulka",
"query.unlimitedRows": "Neomezený počet řádků",
"query.variable": "#proměnná",
"query.variable": "proměnná",
"queryParameters.editQueryParameters": "Upravit parametry dotazu",
"queryParameters.runQuery": "Spustit dotaz",
"queryParameters.stringValuesMustBeQuoted": "Řetězcové hodnoty musí být 'v uvozovkách'. Můžete použít platné SQL výrazy.",
@@ -1333,6 +1339,7 @@
"settings.confirmations": "Potvrzení",
"settings.confirmations.skipConfirm.collectionDataSave": "Přeskočit potvrzení při ukládání údajů kolekce (NoSQL)",
"settings.confirmations.skipConfirm.tableDataSave": "Přeskočit potvrzení při ukládání údajů tabulky (SQL)",
"settings.confirmations.skipFetchAllConfirm": "Přeskočit potvrzení při načítání všech řádků",
"settings.connection": "Připojení",
"settings.connection.autoRefresh": "Automatické obnovení modelu databáze na pozadí",
"settings.connection.autoRefreshInterval": "Interval mezi automatickým načítáním struktury DB v sekundách",
+9 -2
View File
@@ -207,6 +207,8 @@
"command.datagrid.editCell": "Zellwert bearbeiten",
"command.datagrid.editJsonDocument": "Zeile als JSON-Dokument bearbeiten",
"command.datagrid.editSelection": "Auswahl als Tabelle bearbeiten",
"command.datagrid.fetchAll": "Alle Zeilen laden",
"command.datagrid.fetchAll.toolbar": "Alle laden",
"command.datagrid.filterSelected": "Ausgewählten Wert filtern",
"command.datagrid.findColumn": "Spalte finden",
"command.datagrid.generateSql": "SQL generieren",
@@ -723,6 +725,11 @@
"datagrid.columnName": "Spaltenname",
"datagrid.columnNameFilter": "Spaltenname-Filter",
"datagrid.copyAdvanced": "Erweitert kopieren",
"datagrid.fetchAll.confirm": "Alle laden",
"datagrid.fetchAll.progress": "Alle Zeilen werden geladen... {count} geladen",
"datagrid.fetchAll.progressDb": "Daten werden aus der Datenbank geladen...",
"datagrid.fetchAll.title": "Alle Zeilen laden",
"datagrid.fetchAll.warning": "Dadurch werden alle verbleibenden Zeilen in den Speicher geladen. Bei großen Tabellen kann dies erheblichen Speicher verbrauchen und die Anwendungsleistung beeinträchtigen.",
"datagrid.macros.calculation": "Berechnung",
"datagrid.macros.calculationDescription": "Benutzerdefinierter Ausdruck. Verwenden Sie row.spaltenname für den Zugriff auf Spaltenwerte, value für den ursprünglichen Wert",
"datagrid.macros.changeTextCase": "Textgroß-/Kleinschreibung ändern",
@@ -1238,7 +1245,6 @@
"query.groupFilter": "Gruppenfilter",
"query.isolationLevel": "Isolationsstufe",
"query.limitRows": "Auf {queryRowsLimit} Zeilen begrenzen",
"query.named": ":Variable",
"query.noParameters": "(keine Parameter)",
"query.noRowsLimit": "(Keine Zeilenbegrenzung)",
"query.orFilter": "ODER-Filter {number}",
@@ -1256,7 +1262,7 @@
"query.sortOrder": "Sortierreihenfolge",
"query.table": "Tabelle",
"query.unlimitedRows": "Unbegrenzte Zeilen",
"query.variable": "#Variable",
"query.variable": "Variable",
"queryParameters.editQueryParameters": "Abfrageparameter bearbeiten",
"queryParameters.runQuery": "Abfrage ausführen",
"queryParameters.stringValuesMustBeQuoted": "Zeichenkettenwerte müssen in 'Anführungszeichen' stehen. Sie können gültige SQL-Ausdrücke verwenden.",
@@ -1333,6 +1339,7 @@
"settings.confirmations": "Bestätigungen",
"settings.confirmations.skipConfirm.collectionDataSave": "Bestätigung beim Speichern von Sammlungsdaten überspringen (NoSQL)",
"settings.confirmations.skipConfirm.tableDataSave": "Bestätigung beim Speichern von Tabellendaten überspringen (SQL)",
"settings.confirmations.skipFetchAllConfirm": "Bestätigung beim Laden aller Zeilen überspringen",
"settings.connection": "Verbindung",
"settings.connection.autoRefresh": "Automatische Aktualisierung des Datenbankmodells im Hintergrund",
"settings.connection.autoRefreshInterval": "Intervall zwischen automatischen DB-Strukturaktualisierungen in Sekunden",
+9 -2
View File
@@ -207,6 +207,8 @@
"command.datagrid.editCell": "Edit cell value",
"command.datagrid.editJsonDocument": "Edit row as JSON document",
"command.datagrid.editSelection": "Edit selection as table",
"command.datagrid.fetchAll": "Fetch all rows",
"command.datagrid.fetchAll.toolbar": "Fetch all",
"command.datagrid.filterSelected": "Filter selected value",
"command.datagrid.findColumn": "Find column",
"command.datagrid.generateSql": "Generate SQL",
@@ -723,6 +725,11 @@
"datagrid.columnName": "Column name",
"datagrid.columnNameFilter": "Column name filter",
"datagrid.copyAdvanced": "Copy advanced",
"datagrid.fetchAll.confirm": "Fetch All",
"datagrid.fetchAll.progress": "Fetching all rows... {count} loaded",
"datagrid.fetchAll.progressDb": "Fetching data from database...",
"datagrid.fetchAll.title": "Fetch All Rows",
"datagrid.fetchAll.warning": "This will load all remaining rows into memory. For large tables, this may consume a significant amount of memory and could affect application performance.",
"datagrid.macros.calculation": "Calculation",
"datagrid.macros.calculationDescription": "Custom expression. Use row.column_name for accessing column values, value for original value",
"datagrid.macros.changeTextCase": "Change text case",
@@ -1238,7 +1245,6 @@
"query.groupFilter": "Group filter",
"query.isolationLevel": "Isolation level",
"query.limitRows": "Limit {queryRowsLimit} rows",
"query.named": ":variable",
"query.noParameters": "(no parameters)",
"query.noRowsLimit": "(No rows limit)",
"query.orFilter": "OR Filter {number}",
@@ -1256,7 +1262,7 @@
"query.sortOrder": "Sort order",
"query.table": "Table",
"query.unlimitedRows": "Unlimited rows",
"query.variable": "#variable",
"query.variable": "variable",
"queryParameters.editQueryParameters": "Edit query parameters",
"queryParameters.runQuery": "Run query",
"queryParameters.stringValuesMustBeQuoted": "String values must be 'quoted'. You can use valid SQL expressions.",
@@ -1333,6 +1339,7 @@
"settings.confirmations": "Confirmations",
"settings.confirmations.skipConfirm.collectionDataSave": "Skip confirmation when saving collection data (NoSQL)",
"settings.confirmations.skipConfirm.tableDataSave": "Skip confirmation when saving table data (SQL)",
"settings.confirmations.skipFetchAllConfirm": "Skip confirmation when fetching all rows",
"settings.connection": "Connection",
"settings.connection.autoRefresh": "Automatic refresh of database model on background",
"settings.connection.autoRefreshInterval": "Interval between automatic DB structure reloads in seconds",
+9 -2
View File
@@ -207,6 +207,8 @@
"command.datagrid.editCell": "Editar valor de celda",
"command.datagrid.editJsonDocument": "Editar fila como documento JSON",
"command.datagrid.editSelection": "Editar selección como tabla",
"command.datagrid.fetchAll": "Cargar todas las filas",
"command.datagrid.fetchAll.toolbar": "Cargar todo",
"command.datagrid.filterSelected": "Filtrar valor seleccionado",
"command.datagrid.findColumn": "Buscar columna",
"command.datagrid.generateSql": "Generar SQL",
@@ -723,6 +725,11 @@
"datagrid.columnName": "Nombre de columna",
"datagrid.columnNameFilter": "Filtro de nombre de columna",
"datagrid.copyAdvanced": "Copiar avanzado",
"datagrid.fetchAll.confirm": "Cargar todo",
"datagrid.fetchAll.progress": "Cargando todas las filas... {count} cargadas",
"datagrid.fetchAll.progressDb": "Cargando datos desde la base de datos...",
"datagrid.fetchAll.title": "Cargar todas las filas",
"datagrid.fetchAll.warning": "Esto cargará todas las filas restantes en memoria. Para tablas grandes, esto puede consumir una cantidad significativa de memoria y podría afectar el rendimiento de la aplicación.",
"datagrid.macros.calculation": "Cálculo",
"datagrid.macros.calculationDescription": "Expresión personalizada. Use row.column_name para acceder a valores de columna, value para valor original",
"datagrid.macros.changeTextCase": "Cambiar mayúsculas/minúsculas",
@@ -1238,7 +1245,6 @@
"query.groupFilter": "Filtro de grupo",
"query.isolationLevel": "Nivel de aislamiento",
"query.limitRows": "Limitar {queryRowsLimit} filas",
"query.named": ":variable",
"query.noParameters": "(sin parámetros)",
"query.noRowsLimit": "(Sin límite de filas)",
"query.orFilter": "Filtro OR {number}",
@@ -1256,7 +1262,7 @@
"query.sortOrder": "Orden de clasificación",
"query.table": "Tabla",
"query.unlimitedRows": "Filas ilimitadas",
"query.variable": "#variable",
"query.variable": "variable",
"queryParameters.editQueryParameters": "Editar parámetros de consulta",
"queryParameters.runQuery": "Ejecutar consulta",
"queryParameters.stringValuesMustBeQuoted": "Los valores de cadena deben estar 'entre comillas'. Puede usar expresiones SQL válidas.",
@@ -1333,6 +1339,7 @@
"settings.confirmations": "Confirmaciones",
"settings.confirmations.skipConfirm.collectionDataSave": "Omitir confirmación al guardar datos de colección (NoSQL)",
"settings.confirmations.skipConfirm.tableDataSave": "Omitir confirmación al guardar datos de tabla (SQL)",
"settings.confirmations.skipFetchAllConfirm": "Omitir confirmación al cargar todas las filas",
"settings.connection": "Conexión",
"settings.connection.autoRefresh": "Recarga automática del modelo de base de datos en segundo plano",
"settings.connection.autoRefreshInterval": "Intervalo entre recargas automáticas de estructura de BD en segundos",
+9 -2
View File
@@ -207,6 +207,8 @@
"command.datagrid.editCell": "Modifier la valeur de cellule",
"command.datagrid.editJsonDocument": "Modifier la ligne en tant que document JSON",
"command.datagrid.editSelection": "Modifier la sélection en tant que table",
"command.datagrid.fetchAll": "Charger toutes les lignes",
"command.datagrid.fetchAll.toolbar": "Tout charger",
"command.datagrid.filterSelected": "Filtrer la valeur sélectionnée",
"command.datagrid.findColumn": "Rechercher une colonne",
"command.datagrid.generateSql": "Générer du SQL",
@@ -723,6 +725,11 @@
"datagrid.columnName": "Nom de la colonne",
"datagrid.columnNameFilter": "Filtre de nom de colonne",
"datagrid.copyAdvanced": "Copie avancée",
"datagrid.fetchAll.confirm": "Tout charger",
"datagrid.fetchAll.progress": "Chargement de toutes les lignes... {count} chargées",
"datagrid.fetchAll.progressDb": "Chargement des données depuis la base de données...",
"datagrid.fetchAll.title": "Charger toutes les lignes",
"datagrid.fetchAll.warning": "Cela chargera toutes les lignes restantes en mémoire. Pour les grandes tables, cela peut consommer une quantité importante de mémoire et affecter les performances de l'application.",
"datagrid.macros.calculation": "Calcul",
"datagrid.macros.calculationDescription": "Expression personnalisée. Utilisez row.column_name pour accéder aux valeurs de colonne et value pour la valeur d'origine",
"datagrid.macros.changeTextCase": "Modifier la casse du texte",
@@ -1238,7 +1245,6 @@
"query.groupFilter": "Filtre de groupe",
"query.isolationLevel": "Niveau d'isolation",
"query.limitRows": "Limiter à {queryRowsLimit} lignes",
"query.named": ":variable",
"query.noParameters": "(aucun paramètre)",
"query.noRowsLimit": "(Aucune limite de lignes)",
"query.orFilter": "Filtre OU {number}",
@@ -1256,7 +1262,7 @@
"query.sortOrder": "Ordre de tri",
"query.table": "Table",
"query.unlimitedRows": "Lignes illimitées",
"query.variable": "#variable",
"query.variable": "variable",
"queryParameters.editQueryParameters": "Modifier les paramètres de requête",
"queryParameters.runQuery": "Exécuter la requête",
"queryParameters.stringValuesMustBeQuoted": "Les valeurs de type chaîne doivent être 'entre guillemets'. Vous pouvez utiliser des expressions SQL valides.",
@@ -1333,6 +1339,7 @@
"settings.confirmations": "Confirmations",
"settings.confirmations.skipConfirm.collectionDataSave": "Ignorer la confirmation lors de l'enregistrement des données de collection (NoSQL)",
"settings.confirmations.skipConfirm.tableDataSave": "Ignorer la confirmation lors de l'enregistrement des données de table (SQL)",
"settings.confirmations.skipFetchAllConfirm": "Ignorer la confirmation lors du chargement de toutes les lignes",
"settings.connection": "Connexion",
"settings.connection.autoRefresh": "Rafraîchissement automatique du modèle de base de données en arrière-plan",
"settings.connection.autoRefreshInterval": "Intervalle entre les rechargements automatiques de la structure de BD en secondes",
+9 -2
View File
@@ -207,6 +207,8 @@
"command.datagrid.editCell": "Modifica valore cella",
"command.datagrid.editJsonDocument": "Modifica riga come documento JSON",
"command.datagrid.editSelection": "Modifica selezione come tabella",
"command.datagrid.fetchAll": "Carica tutte le righe",
"command.datagrid.fetchAll.toolbar": "Carica tutto",
"command.datagrid.filterSelected": "Filtra valore selezionato",
"command.datagrid.findColumn": "Trova colonna",
"command.datagrid.generateSql": "Genera SQL",
@@ -723,6 +725,11 @@
"datagrid.columnName": "Nome colonna",
"datagrid.columnNameFilter": "Filtro nome colonna",
"datagrid.copyAdvanced": "Copia avanzato",
"datagrid.fetchAll.confirm": "Carica tutto",
"datagrid.fetchAll.progress": "Caricamento di tutte le righe... {count} caricate",
"datagrid.fetchAll.progressDb": "Caricamento dati dal database...",
"datagrid.fetchAll.title": "Carica tutte le righe",
"datagrid.fetchAll.warning": "Questo caricherà tutte le righe rimanenti in memoria. Per tabelle grandi, potrebbe consumare una quantità significativa di memoria e influire sulle prestazioni dell'applicazione.",
"datagrid.macros.calculation": "Calcolo",
"datagrid.macros.calculationDescription": "Espressione personalizzata. Usa row.column_name per accedere ai valori colonna, value per il valore originale",
"datagrid.macros.changeTextCase": "Cambia maiuscole/minuscole",
@@ -1238,7 +1245,6 @@
"query.groupFilter": "Filtro gruppo",
"query.isolationLevel": "Livello di isolamento",
"query.limitRows": "Limita a {queryRowsLimit} righe",
"query.named": ":variabile",
"query.noParameters": "(nessun parametro)",
"query.noRowsLimit": "(Nessun limite righe)",
"query.orFilter": "Filtro OR {number}",
@@ -1256,7 +1262,7 @@
"query.sortOrder": "Ordinamento",
"query.table": "Tabella",
"query.unlimitedRows": "Righe illimitate",
"query.variable": "#variabile",
"query.variable": "variabile",
"queryParameters.editQueryParameters": "Modifica parametri query",
"queryParameters.runQuery": "Esegui query",
"queryParameters.stringValuesMustBeQuoted": "I valori stringa devono essere 'quoted'. Puoi usare espressioni SQL valide.",
@@ -1333,6 +1339,7 @@
"settings.confirmations": "Conferme",
"settings.confirmations.skipConfirm.collectionDataSave": "Salta conferma quando salvi dati collezione (NoSQL)",
"settings.confirmations.skipConfirm.tableDataSave": "Salta conferma quando salvi dati tabella (SQL)",
"settings.confirmations.skipFetchAllConfirm": "Salta conferma quando carichi tutte le righe",
"settings.connection": "Connessione",
"settings.connection.autoRefresh": "Aggiornamento automatico del modello database in background",
"settings.connection.autoRefreshInterval": "Intervallo tra ricaricamenti automatici struttura DB in secondi",
+9 -2
View File
@@ -207,6 +207,8 @@
"command.datagrid.editCell": "セルの値を編集",
"command.datagrid.editJsonDocument": "行をJSONドキュメントとして編集",
"command.datagrid.editSelection": "選択範囲をテーブルとして編集",
"command.datagrid.fetchAll": "すべての行を取得",
"command.datagrid.fetchAll.toolbar": "すべて取得",
"command.datagrid.filterSelected": "選択した値でフィルター",
"command.datagrid.findColumn": "カラムを検索",
"command.datagrid.generateSql": "SQLを生成",
@@ -723,6 +725,11 @@
"datagrid.columnName": "カラム名",
"datagrid.columnNameFilter": "カラム名フィルター",
"datagrid.copyAdvanced": "高度なコピー",
"datagrid.fetchAll.confirm": "すべて取得",
"datagrid.fetchAll.progress": "すべての行を取得中... {count} 件読み込み済み",
"datagrid.fetchAll.progressDb": "データベースからデータを取得中...",
"datagrid.fetchAll.title": "すべての行を取得",
"datagrid.fetchAll.warning": "これにより、残りのすべての行がメモリに読み込まれます。大きなテーブルの場合、かなりのメモリを消費し、アプリケーションのパフォーマンスに影響を与える可能性があります。",
"datagrid.macros.calculation": "Calculation",
"datagrid.macros.calculationDescription": "Custom expression. Use row.column_name for accessing column values, value for original value",
"datagrid.macros.changeTextCase": "Change text case",
@@ -1238,7 +1245,6 @@
"query.groupFilter": "グループフィルター",
"query.isolationLevel": "分離レベル",
"query.limitRows": "{queryRowsLimit}行に制限",
"query.named": ":variable",
"query.noParameters": "(パラメーターなし)",
"query.noRowsLimit": "(行数制限なし)",
"query.orFilter": "ORフィルター {number}",
@@ -1256,7 +1262,7 @@
"query.sortOrder": "ソート順",
"query.table": "テーブル",
"query.unlimitedRows": "無制限",
"query.variable": "#variable",
"query.variable": "変数",
"queryParameters.editQueryParameters": "クエリパラメーターを編集",
"queryParameters.runQuery": "クエリを実行",
"queryParameters.stringValuesMustBeQuoted": "文字列値は 'クォート' する必要があります。有効なSQL式を使用できます。",
@@ -1333,6 +1339,7 @@
"settings.confirmations": "確認",
"settings.confirmations.skipConfirm.collectionDataSave": "コレクションデータ保存時の確認をスキップ (NoSQL)",
"settings.confirmations.skipConfirm.tableDataSave": "テーブルデータ保存時の確認をスキップ (SQL)",
"settings.confirmations.skipFetchAllConfirm": "すべての行を取得する際の確認をスキップ",
"settings.connection": "接続",
"settings.connection.autoRefresh": "バックグラウンドでデータベースモデルを自動更新",
"settings.connection.autoRefreshInterval": "DB構造の自動再読み込み間隔(秒)",
+9 -2
View File
@@ -207,6 +207,8 @@
"command.datagrid.editCell": "셀 값 편집",
"command.datagrid.editJsonDocument": "행을 JSON 문서로 편집",
"command.datagrid.editSelection": "선택 영역을 테이블로 편집",
"command.datagrid.fetchAll": "모든 행 가져오기",
"command.datagrid.fetchAll.toolbar": "모두 가져오기",
"command.datagrid.filterSelected": "선택한 값으로 필터",
"command.datagrid.findColumn": "컬럼 찾기",
"command.datagrid.generateSql": "SQL 생성",
@@ -723,6 +725,11 @@
"datagrid.columnName": "컬럼 이름",
"datagrid.columnNameFilter": "컬럼 이름 필터",
"datagrid.copyAdvanced": "고급 복사",
"datagrid.fetchAll.confirm": "모든 행 가져오기",
"datagrid.fetchAll.progress": "모든 행 가져오는 중... {count}개 로드됨",
"datagrid.fetchAll.progressDb": "데이터베이스에서 데이터 가져오는 중...",
"datagrid.fetchAll.title": "모든 행 가져오기",
"datagrid.fetchAll.warning": "남은 모든 행을 메모리로 로드합니다. 테이블이 큰 경우 상당한 메모리를 사용할 수 있으며 애플리케이션 성능에 영향을 줄 수 있습니다.",
"datagrid.macros.calculation": "계산",
"datagrid.macros.calculationDescription": "사용자 정의 표현식. 컬럼 값에 접근하려면 row.column_name, 원래 값은 value 사용",
"datagrid.macros.changeTextCase": "대소문자 변경",
@@ -1238,7 +1245,6 @@
"query.groupFilter": "그룹 필터",
"query.isolationLevel": "격리 수준",
"query.limitRows": "{queryRowsLimit}행 제한",
"query.named": ":variable",
"query.noParameters": "(매개변수 없음)",
"query.noRowsLimit": "(행 제한 없음)",
"query.orFilter": "OR 필터 {number}",
@@ -1256,7 +1262,7 @@
"query.sortOrder": "정렬 순서",
"query.table": "테이블",
"query.unlimitedRows": "무제한 행",
"query.variable": "#variable",
"query.variable": "변수",
"queryParameters.editQueryParameters": "쿼리 매개변수 편집",
"queryParameters.runQuery": "쿼리 실행",
"queryParameters.stringValuesMustBeQuoted": "문자열 값은 '따옴표'로 감싸야 합니다. 유효한 SQL 표현식을 사용할 수 있습니다.",
@@ -1333,6 +1339,7 @@
"settings.confirmations": "확인",
"settings.confirmations.skipConfirm.collectionDataSave": "컬렉션 데이터 저장 시 확인 건너뛰기(NoSQL)",
"settings.confirmations.skipConfirm.tableDataSave": "테이블 데이터 저장 시 확인 건너뛰기(SQL)",
"settings.confirmations.skipFetchAllConfirm": "모든 행 가져오기 시 확인 건너뛰기",
"settings.connection": "연결",
"settings.connection.autoRefresh": "백그라운드에서 데이터베이스 모델 자동 새로 고침",
"settings.connection.autoRefreshInterval": "자동 DB 구조 재로딩 간격(초)",
+9 -2
View File
@@ -207,6 +207,8 @@
"command.datagrid.editCell": "Editar valor da célula",
"command.datagrid.editJsonDocument": "Editar linha como documento JSON",
"command.datagrid.editSelection": "Editar seleção como tabela",
"command.datagrid.fetchAll": "Buscar todas as linhas",
"command.datagrid.fetchAll.toolbar": "Buscar todas",
"command.datagrid.filterSelected": "Filtrar valor selecionado",
"command.datagrid.findColumn": "Localizar coluna",
"command.datagrid.generateSql": "Gerar SQL",
@@ -723,6 +725,11 @@
"datagrid.columnName": "Nome da coluna",
"datagrid.columnNameFilter": "Filtro de nome de coluna",
"datagrid.copyAdvanced": "Cópia avançada",
"datagrid.fetchAll.confirm": "Buscar todas",
"datagrid.fetchAll.progress": "Buscando todas as linhas... {count} carregadas",
"datagrid.fetchAll.progressDb": "Buscando dados do banco de dados...",
"datagrid.fetchAll.title": "Buscar todas as linhas",
"datagrid.fetchAll.warning": "Isso irá carregar todas as linhas restantes na memória. Para tabelas grandes, isso pode consumir uma quantidade significativa de memória e afetar o desempenho da aplicação.",
"datagrid.macros.calculation": "Cálculo",
"datagrid.macros.calculationDescription": "Expressão personalizada. Use row.nome_coluna para acessar valores de colunas, value para valor original",
"datagrid.macros.changeTextCase": "Alterar maiúsculas/minúsculas",
@@ -1238,7 +1245,6 @@
"query.groupFilter": "Filtro de grupo",
"query.isolationLevel": "Nível de isolamento",
"query.limitRows": "Limitar a {queryRowsLimit} linhas",
"query.named": ":variável",
"query.noParameters": "(sem parâmetros)",
"query.noRowsLimit": "(Sem limite de linhas)",
"query.orFilter": "Filtro OU {number}",
@@ -1256,7 +1262,7 @@
"query.sortOrder": "Ordem de classificação",
"query.table": "Tabela",
"query.unlimitedRows": "Linhas ilimitadas",
"query.variable": "#variável",
"query.variable": "variável",
"queryParameters.editQueryParameters": "Editar parâmetros da consulta",
"queryParameters.runQuery": "Executar consulta",
"queryParameters.stringValuesMustBeQuoted": "Valores de texto devem estar 'entre aspas'. Você pode usar expressões SQL válidas.",
@@ -1333,6 +1339,7 @@
"settings.confirmations": "Confirmações",
"settings.confirmations.skipConfirm.collectionDataSave": "Pular confirmação ao salvar dados de coleção (NoSQL)",
"settings.confirmations.skipConfirm.tableDataSave": "Pular confirmação ao salvar dados de tabela (SQL)",
"settings.confirmations.skipFetchAllConfirm": "Pular confirmação ao buscar todas as linhas",
"settings.connection": "Conexão",
"settings.connection.autoRefresh": "Atualização automática do modelo de banco de dados em segundo plano",
"settings.connection.autoRefreshInterval": "Intervalo entre recarregamentos automáticos da estrutura do BD em segundos",
+9 -2
View File
@@ -207,6 +207,8 @@
"command.datagrid.editCell": "Upraviť hodnotu bunky",
"command.datagrid.editJsonDocument": "Upraviť riadok ako JSON dokument",
"command.datagrid.editSelection": "Upraviť výber ako tabuľku",
"command.datagrid.fetchAll": "Načítať všetky riadky",
"command.datagrid.fetchAll.toolbar": "Načítať všetko",
"command.datagrid.filterSelected": "Filtrovať vybranú hodnotu",
"command.datagrid.findColumn": "Nájsť stĺpec",
"command.datagrid.generateSql": "Generovať SQL",
@@ -723,6 +725,11 @@
"datagrid.columnName": "Názov stĺpca",
"datagrid.columnNameFilter": "Filter názvu stĺpca",
"datagrid.copyAdvanced": "Pokročilé kopírovanie",
"datagrid.fetchAll.confirm": "Načítať všetko",
"datagrid.fetchAll.progress": "Načítavam všetky riadky... {count} načítaných",
"datagrid.fetchAll.progressDb": "Načítavanie dát z databázy...",
"datagrid.fetchAll.title": "Načítať všetky riadky",
"datagrid.fetchAll.warning": "Týmto sa načítajú všetky zostávajúce riadky do pamäte. Pri veľkých tabuľkách to môže spotrebovať značné množstvo pamäte a môže ovplyvniť výkon aplikácie.",
"datagrid.macros.calculation": "Výpočet",
"datagrid.macros.calculationDescription": "Vlastný výraz. Použite riadok.názov_stĺpca pre prístup k hodnotám stĺpcov, value pre pôvodnú hodnotu",
"datagrid.macros.changeTextCase": "Zmeniť veľkosť písmen",
@@ -1238,7 +1245,6 @@
"query.groupFilter": "Filter skupiny",
"query.isolationLevel": "Úroveň izolácie",
"query.limitRows": "Obmedziť na {queryRowsLimit} riadkov",
"query.named": ":premenná",
"query.noParameters": "(žiadne parametre)",
"query.noRowsLimit": "(bez limitu riadkov)",
"query.orFilter": "OR filter {number}",
@@ -1256,7 +1262,7 @@
"query.sortOrder": "Poradie zoradenia",
"query.table": "Tabuľka",
"query.unlimitedRows": "Neobmedzené riadky",
"query.variable": "#premenná",
"query.variable": "premenná",
"queryParameters.editQueryParameters": "Upraviť parametre dotazu",
"queryParameters.runQuery": "Spustiť dotaz",
"queryParameters.stringValuesMustBeQuoted": "Reťazcové hodnoty musia byť 'v úvodzovkách'. Môžete použiť platné SQL výrazy.",
@@ -1333,6 +1339,7 @@
"settings.confirmations": "Potvrdenia",
"settings.confirmations.skipConfirm.collectionDataSave": "Preskočiť potvrdenie pri ukladaní údajov kolekcie (NoSQL)",
"settings.confirmations.skipConfirm.tableDataSave": "Preskočiť potvrdenie pri ukladaní údajov tabuľky (SQL)",
"settings.confirmations.skipFetchAllConfirm": "Preskočiť potvrdenie pri načítaní všetkých riadkov",
"settings.connection": "Pripojenie",
"settings.connection.autoRefresh": "Automatické obnovenie modelu databázy na pozadí",
"settings.connection.autoRefreshInterval": "Interval medzi automatickým načítaním štruktúry DB (v sekundách)",
+9 -2
View File
@@ -207,6 +207,8 @@
"command.datagrid.editCell": "编辑单元格值",
"command.datagrid.editJsonDocument": "将行编辑为 JSON 文档",
"command.datagrid.editSelection": "将选区编辑为表",
"command.datagrid.fetchAll": "获取所有行",
"command.datagrid.fetchAll.toolbar": "获取全部",
"command.datagrid.filterSelected": "筛选选中值",
"command.datagrid.findColumn": "查找列",
"command.datagrid.generateSql": "生成 SQL",
@@ -723,6 +725,11 @@
"datagrid.columnName": "列名",
"datagrid.columnNameFilter": "列名筛选",
"datagrid.copyAdvanced": "高级复制",
"datagrid.fetchAll.confirm": "获取全部",
"datagrid.fetchAll.progress": "正在获取所有行... 已加载 {count}",
"datagrid.fetchAll.progressDb": "正在从数据库获取数据...",
"datagrid.fetchAll.title": "获取所有行",
"datagrid.fetchAll.warning": "这将把所有剩余的行加载到内存中。对于大型表,这可能会占用大量内存,并可能影响应用程序性能。",
"datagrid.macros.calculation": "计算",
"datagrid.macros.calculationDescription": "自定义表达式。使用 row.column_name 访问列值,value 访问原始值",
"datagrid.macros.changeTextCase": "更改文本大小写",
@@ -1238,7 +1245,6 @@
"query.groupFilter": "分组筛选",
"query.isolationLevel": "隔离级别",
"query.limitRows": "限制 {queryRowsLimit} 行",
"query.named": ":variable",
"query.noParameters": "(无参数)",
"query.noRowsLimit": "(无行数限制)",
"query.orFilter": "OR 筛选 {number}",
@@ -1256,7 +1262,7 @@
"query.sortOrder": "排序顺序",
"query.table": "表",
"query.unlimitedRows": "不限行数",
"query.variable": "#variable",
"query.variable": "变量",
"queryParameters.editQueryParameters": "编辑查询参数",
"queryParameters.runQuery": "运行查询",
"queryParameters.stringValuesMustBeQuoted": "字符串值必须使用引号括起来。您可以使用有效的 SQL 表达式。",
@@ -1333,6 +1339,7 @@
"settings.confirmations": "确认",
"settings.confirmations.skipConfirm.collectionDataSave": "保存集合数据时跳过确认(NoSQL)",
"settings.confirmations.skipConfirm.tableDataSave": "保存表数据时跳过确认(SQL",
"settings.confirmations.skipFetchAllConfirm": "获取所有行时跳过确认",
"settings.connection": "连接",
"settings.connection.autoRefresh": "后台自动刷新数据库模型",
"settings.connection.autoRefreshInterval": "自动重新加载数据库结构的间隔(秒)",
+1 -1
View File
@@ -57,7 +57,7 @@ jobs:
# Ensure npm 11.5.1 or later is installed
- name: Update npm
run: npm install -g npm@latest
run: npm install -g npm@11.5.1
- name: Remove dbmodel - should be not published
run: |
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
# Ensure npm 11.5.1 or later is installed
- name: Update npm
run: npm install -g npm@latest
run: npm install -g npm@11.5.1
# - name: Configure NPM token
# env:
+1 -1
View File
@@ -7,7 +7,7 @@ checkout-and-merge-pro:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
path: dbgate-pro
ref: 6b5e2ff831db9baedb2a43862daa4247810b15de
ref: 87c3efdaf83786abee4366dee2c58fea355edc4c
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro