Compare commits

..

64 Commits

Author SHA1 Message Date
Stela Augustinova c51dad39e0 Improve saveRows method to handle backpressure and errors in file writing 2026-04-14 09:49:14 +02:00
Stela Augustinova 1d350a3a29 Add validation to assertSafeArchiveName to prevent resolving to archive root 2026-04-13 14:55:29 +02:00
Stela Augustinova 81e3cce070 Add validation for linkedFolder in createLink method 2026-04-13 13:54:48 +02:00
Stela Augustinova f9de2d77b5 Moved functionName validation 2026-04-13 13:44:45 +02:00
Stela Augustinova 3956eaf389 Improve error handling in unzipDirectory by adding readStream error listener and immediate abort on file extraction failure 2026-04-13 13:20:28 +02:00
Stela Augustinova d13e2c2d87 Make fs.writeFile call awaitable in createLink method for proper async handling 2026-04-13 12:53:54 +02:00
Stela Augustinova ebf2371da9 Enhance unzipDirectory error handling and manage active streams for better resource cleanup 2026-04-13 12:43:45 +02:00
Stela Augustinova fa4b12448d Refactor unzipDirectory to improve error handling and prevent multiple rejections 2026-04-13 10:48:01 +02:00
Stela Augustinova 5fe6dfa551 Refactor loaderScriptTemplate to streamline plugin extraction and improve readability 2026-04-13 10:41:12 +02:00
Stela Augustinova 6061c8b0a5 Update JS reserved words 2026-04-13 10:31:12 +02:00
Stela Augustinova 1ac0aa8a3e Add path traversal and null byte checks for archive names and ZIP entries 2026-04-13 09:19:03 +02:00
Stela Augustinova 5d04d7f01f Enhance JavaScript identifier validation and update variable storage method in ScriptWriterEval 2026-04-10 16:15:45 +02:00
Stela Augustinova 9c97e347c5 Add validation for JavaScript identifiers and shell API function names 2026-04-10 13:22:54 +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
40 changed files with 2975 additions and 926 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
+1 -1
View File
@@ -29,7 +29,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
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.7-premium-beta.1",
"version": "7.1.8",
"name": "dbgate-all",
"workspaces": [
"packages/*",
+64 -5
View File
@@ -19,6 +19,26 @@ const unzipDirectory = require('../shell/unzipDirectory');
const logger = getLogger('archive');
/**
* Rejects any archive name (folder or file) that contains path-traversal
* sequences, directory separators, or null bytes. These values are used
* directly in path.join() calls; allowing traversal would let callers read
* or write arbitrary files outside the archive directory.
*/
function assertSafeArchiveName(name, label) {
if (typeof name !== 'string' || name.length === 0) {
throw new Error(`DBGM-00000 Invalid ${label}: must be a non-empty string`);
}
if (name.includes('\0') || name.includes('..') || name.includes('/') || name.includes('\\')) {
throw new Error(`DBGM-00000 Invalid ${label}: path traversal not allowed`);
}
// Reject names that resolve to the archive root itself (e.g. '.')
const resolved = path.resolve(archivedir(), name);
if (resolved === path.resolve(archivedir())) {
throw new Error(`DBGM-00000 Invalid ${label}: must not resolve to the archive root`);
}
}
module.exports = {
folders_meta: true,
async folders() {
@@ -39,6 +59,7 @@ module.exports = {
createFolder_meta: true,
async createFolder({ folder }) {
assertSafeArchiveName(folder, 'folder');
await fs.mkdir(path.join(archivedir(), folder));
socket.emitChanged('archive-folders-changed');
return true;
@@ -46,8 +67,12 @@ module.exports = {
createLink_meta: true,
async createLink({ linkedFolder }) {
if ( typeof linkedFolder !== 'string' || linkedFolder.length === 0) {
throw new Error(`DBGM-00000 Invalid linkedFolder: must be a non-empty string`);
}
assertSafeArchiveName(path.parse(linkedFolder).name, 'linkedFolder');
const folder = await this.getNewArchiveFolder({ database: path.parse(linkedFolder).name + '.link' });
fs.writeFile(path.join(archivedir(), folder), linkedFolder);
await fs.writeFile(path.join(archivedir(), folder), linkedFolder);
clearArchiveLinksCache();
socket.emitChanged('archive-folders-changed');
return folder;
@@ -71,6 +96,7 @@ module.exports = {
files_meta: true,
async files({ folder }) {
assertSafeArchiveName(folder, 'folder');
try {
if (folder.endsWith('.zip')) {
if (await fs.exists(path.join(archivedir(), folder))) {
@@ -121,6 +147,9 @@ module.exports = {
createFile_meta: true,
async createFile({ folder, file, fileType, tableInfo }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
assertSafeArchiveName(fileType, 'fileType');
await fs.writeFile(
path.join(resolveArchiveFolder(folder), `${file}.${fileType}`),
tableInfo ? JSON.stringify({ __isStreamHeader: true, tableInfo }) : ''
@@ -131,6 +160,9 @@ module.exports = {
deleteFile_meta: true,
async deleteFile({ folder, file, fileType }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
assertSafeArchiveName(fileType, 'fileType');
await fs.unlink(path.join(resolveArchiveFolder(folder), `${file}.${fileType}`));
socket.emitChanged(`archive-files-changed`, { folder });
return true;
@@ -138,6 +170,10 @@ module.exports = {
renameFile_meta: true,
async renameFile({ folder, file, newFile, fileType }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
assertSafeArchiveName(newFile, 'newFile');
assertSafeArchiveName(fileType, 'fileType');
await fs.rename(
path.join(resolveArchiveFolder(folder), `${file}.${fileType}`),
path.join(resolveArchiveFolder(folder), `${newFile}.${fileType}`)
@@ -148,6 +184,8 @@ module.exports = {
modifyFile_meta: true,
async modifyFile({ folder, file, changeSet, mergedRows, mergeKey, mergeMode }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
await jsldata.closeDataStore(`archive://${folder}/${file}`);
const changedFilePath = path.join(resolveArchiveFolder(folder), `${file}.jsonl`);
@@ -187,6 +225,8 @@ module.exports = {
renameFolder_meta: true,
async renameFolder({ folder, newFolder }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(newFolder, 'newFolder');
const uniqueName = await this.getNewArchiveFolder({ database: newFolder });
await fs.rename(path.join(archivedir(), folder), path.join(archivedir(), uniqueName));
socket.emitChanged(`archive-folders-changed`);
@@ -196,6 +236,7 @@ module.exports = {
deleteFolder_meta: true,
async deleteFolder({ folder }) {
if (!folder) throw new Error('Missing folder parameter');
assertSafeArchiveName(folder, 'folder');
if (folder.endsWith('.link') || folder.endsWith('.zip')) {
await fs.unlink(path.join(archivedir(), folder));
} else {
@@ -207,6 +248,8 @@ module.exports = {
saveText_meta: true,
async saveText({ folder, file, text }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
await fs.writeFile(path.join(resolveArchiveFolder(folder), `${file}.jsonl`), text);
socket.emitChanged(`archive-files-changed`, { folder });
return true;
@@ -214,6 +257,8 @@ module.exports = {
saveJslData_meta: true,
async saveJslData({ folder, file, jslid, changeSet }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
const source = getJslFileName(jslid);
const target = path.join(resolveArchiveFolder(folder), `${file}.jsonl`);
if (changeSet) {
@@ -232,11 +277,20 @@ module.exports = {
saveRows_meta: true,
async saveRows({ folder, file, rows }) {
const fileStream = fs.createWriteStream(path.join(resolveArchiveFolder(folder), `${file}.jsonl`));
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
const filePath = path.join(resolveArchiveFolder(folder), `${file}.jsonl`);
const fileStream = fs.createWriteStream(filePath);
for (const row of rows) {
await fileStream.write(JSON.stringify(row) + '\n');
const ok = fileStream.write(JSON.stringify(row) + '\n');
if (!ok) {
await new Promise(resolve => fileStream.once('drain', resolve));
}
}
await fileStream.close();
await new Promise((resolve, reject) => {
fileStream.end(() => resolve());
fileStream.on('error', reject);
});
socket.emitChanged(`archive-files-changed`, { folder });
return true;
},
@@ -256,6 +310,8 @@ module.exports = {
getArchiveData_meta: true,
async getArchiveData({ folder, file }) {
assertSafeArchiveName(folder, 'folder');
assertSafeArchiveName(file, 'file');
let rows;
if (folder.endsWith('.zip')) {
rows = await unzipJsonLinesFile(path.join(archivedir(), folder), `${file}.jsonl`);
@@ -270,7 +326,7 @@ module.exports = {
if (!fileName?.endsWith('.zip')) {
throw new Error(`${fileName} is not a ZIP file`);
}
assertSafeArchiveName(fileName.slice(0, -4), 'fileName');
const folder = await this.getNewArchiveFolder({ database: fileName });
await fs.copyFile(filePath, path.join(archivedir(), folder));
socket.emitChanged(`archive-folders-changed`);
@@ -280,6 +336,7 @@ module.exports = {
zip_meta: true,
async zip({ folder }) {
assertSafeArchiveName(folder, 'folder');
const newFolder = await this.getNewArchiveFolder({ database: folder + '.zip' });
await zipDirectory(path.join(archivedir(), folder), path.join(archivedir(), newFolder));
socket.emitChanged(`archive-folders-changed`);
@@ -289,6 +346,7 @@ module.exports = {
unzip_meta: true,
async unzip({ folder }) {
assertSafeArchiveName(folder, 'folder');
const newFolder = await this.getNewArchiveFolder({ database: folder.slice(0, -4) });
await unzipDirectory(path.join(archivedir(), folder), path.join(archivedir(), newFolder));
socket.emitChanged(`archive-folders-changed`);
@@ -298,6 +356,7 @@ module.exports = {
getZippedPath_meta: true,
async getZippedPath({ folder }) {
assertSafeArchiveName(folder, 'folder');
if (folder.endsWith('.zip')) {
return { filePath: path.join(archivedir(), folder) };
}
+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');
+10 -7
View File
@@ -10,6 +10,7 @@ const {
extractShellApiPlugins,
compileShellApiFunctionName,
jsonScriptToJavascript,
assertValidShellApiFunctionName,
getLogger,
safeJsonParse,
pinoLogRecordToMessageRecord,
@@ -54,19 +55,23 @@ logger.info('DBGM-00014 Finished job script');
dbgateApi.runScript(run);
`;
const loaderScriptTemplate = (prefix, functionName, props, runid) => `
const loaderScriptTemplate = (functionName, props, runid) => {
const plugins = extractShellApiPlugins(functionName, props);
const prefix = plugins.map(packageName => `// @require ${packageName}\n`).join('');
return `
${prefix}
const dbgateApi = require(process.env.DBGATE_API);
dbgateApi.initializeApiEnvironment();
${requirePluginsTemplate(extractShellApiPlugins(functionName, props))}
${requirePluginsTemplate(plugins)}
require=null;
async function run() {
const reader=await ${compileShellApiFunctionName(functionName)}(${JSON.stringify(props)});
const writer=await dbgateApi.collectorWriter({runid: '${runid}'});
const writer=await dbgateApi.collectorWriter({runid: ${JSON.stringify(runid)}});
await dbgateApi.copyStream(reader, writer);
}
dbgateApi.runScript(run);
`;
};
module.exports = {
/** @type {import('dbgate-types').OpenedRunner[]} */
@@ -377,14 +382,12 @@ module.exports = {
return { errorMessage: 'DBGM-00289 Unallowed file' };
}
}
const prefix = extractShellApiPlugins(functionName)
.map(packageName => `// @require ${packageName}\n`)
.join('');
const promise = new Promise((resolve, reject) => {
assertValidShellApiFunctionName(functionName);
const runid = crypto.randomUUID();
this.requests[runid] = { resolve, reject, exitOnStreamError: true };
this.startCore(runid, loaderScriptTemplate(prefix, functionName, props, runid));
this.startCore(runid, loaderScriptTemplate(functionName, props, runid));
});
return promise;
},
+56 -7
View File
@@ -16,23 +16,53 @@ function unzipDirectory(zipPath, outputDirectory) {
return new Promise((resolve, reject) => {
yauzl.open(zipPath, { lazyEntries: true }, (err, zipFile) => {
if (err) return reject(err);
let settled = false;
/** Track active streams so we can destroy them on early abort */
const activeStreams = new Set();
const safeReject = rejectErr => {
if (settled) return;
settled = true;
for (const s of activeStreams) {
s.destroy();
}
activeStreams.clear();
zipFile.close();
reject(rejectErr);
};
/** Pending per-file extractions we resolve the main promise after theyre all done */
const pending = [];
// Resolved output boundary used for zip-slip checks on every entry
const resolvedOutputDir = path.resolve(outputDirectory);
// kick things off
zipFile.readEntry();
zipFile.on('entry', entry => {
// Null-byte poison check
if (entry.fileName.includes('\0')) {
return safeReject(new Error(`DBGM-00000 ZIP entry with null byte in filename rejected`));
}
const destPath = path.join(outputDirectory, entry.fileName);
const resolvedDest = path.resolve(destPath);
// Zip-slip protection: every extracted path must stay inside outputDirectory
if (resolvedDest !== resolvedOutputDir && !resolvedDest.startsWith(resolvedOutputDir + path.sep)) {
return safeReject(
new Error(`DBGM-00000 ZIP slip detected: entry "${entry.fileName}" would escape output directory`)
);
}
// Handle directories (their names always end with “/” in ZIPs)
if (/\/$/.test(entry.fileName)) {
// Ensure directory exists, then continue to next entry
fs.promises
.mkdir(destPath, { recursive: true })
.then(() => zipFile.readEntry())
.catch(reject);
.then(() => {
if (!settled) zipFile.readEntry();
})
.catch(safeReject);
return;
}
@@ -46,17 +76,29 @@ function unzipDirectory(zipPath, outputDirectory) {
if (err) return rej(err);
const writeStream = fs.createWriteStream(destPath);
activeStreams.add(readStream);
activeStreams.add(writeStream);
readStream.pipe(writeStream);
// proceed to next entry once weve consumed *this* one
readStream.on('end', () => zipFile.readEntry());
// proceed to next entry once we've consumed *this* one
readStream.on('end', () => {
activeStreams.delete(readStream);
if (!settled) zipFile.readEntry();
});
readStream.on('error', readErr => {
activeStreams.delete(readStream);
rej(readErr);
});
writeStream.on('finish', () => {
activeStreams.delete(writeStream);
logger.info(`DBGM-00068 Extracted "${entry.fileName}" → "${destPath}".`);
res();
});
writeStream.on('error', writeErr => {
activeStreams.delete(writeStream);
logger.error(
extractErrorLogData(writeErr),
`DBGM-00069 Error extracting "${entry.fileName}" from "${zipPath}".`
@@ -67,22 +109,29 @@ function unzipDirectory(zipPath, outputDirectory) {
})
);
// Immediately abort the whole unzip if this file fails; otherwise the
// zip would never emit 'end' (lazyEntries won't advance without readEntry).
filePromise.catch(safeReject);
pending.push(filePromise);
});
// Entire archive enumerated; wait for all streams to finish
zipFile.on('end', () => {
if (settled) return;
Promise.all(pending)
.then(() => {
if (settled) return;
settled = true;
zipFile.close();
logger.info(`DBGM-00070 Archive "${zipPath}" fully extracted to "${outputDirectory}".`);
resolve(true);
})
.catch(reject);
.catch(safeReject);
});
zipFile.on('error', err => {
logger.error(extractErrorLogData(err), `DBGM-00071 ZIP file error in ${zipPath}.`);
reject(err);
safeReject(err);
});
});
});
+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": [
+16 -3
View File
@@ -1,6 +1,6 @@
import _uniq from 'lodash/uniq';
import _cloneDeepWith from 'lodash/cloneDeepWith';
import { evalShellApiFunctionName, compileShellApiFunctionName, extractShellApiPlugins } from './packageTools';
import { evalShellApiFunctionName, compileShellApiFunctionName, extractShellApiPlugins, assertValidJsIdentifier, assertValidShellApiFunctionName } from './packageTools';
export interface ScriptWriterGeneric {
allocVariable(prefix?: string);
@@ -40,6 +40,7 @@ export class ScriptWriterJavaScript implements ScriptWriterGeneric {
}
assignCore(variableName, functionName, props) {
assertValidJsIdentifier(variableName, 'variableName');
this._put(`const ${variableName} = await ${functionName}(${JSON.stringify(props)});`);
}
@@ -49,6 +50,7 @@ export class ScriptWriterJavaScript implements ScriptWriterGeneric {
}
assignValue(variableName, jsonValue) {
assertValidJsIdentifier(variableName, 'variableName');
this._put(`const ${variableName} = ${JSON.stringify(jsonValue)};`);
}
@@ -57,8 +59,13 @@ export class ScriptWriterJavaScript implements ScriptWriterGeneric {
}
copyStream(sourceVar, targetVar, colmapVar = null, progressName?: string | { name: string; runid: string }) {
assertValidJsIdentifier(sourceVar, 'sourceVar');
assertValidJsIdentifier(targetVar, 'targetVar');
let opts = '{';
if (colmapVar) opts += `columns: ${colmapVar}, `;
if (colmapVar) {
assertValidJsIdentifier(colmapVar, 'colmapVar');
opts += `columns: ${colmapVar}, `;
}
if (progressName) opts += `progressName: ${JSON.stringify(progressName)}, `;
opts += '}';
@@ -89,7 +96,7 @@ export class ScriptWriterJavaScript implements ScriptWriterGeneric {
}
zipDirectory(inputDirectory, outputFile) {
this._put(`await dbgateApi.zipDirectory('${inputDirectory}', '${outputFile}');`);
this._put(`await dbgateApi.zipDirectory(${JSON.stringify(inputDirectory)}, ${JSON.stringify(outputFile)});`);
}
}
@@ -214,6 +221,8 @@ export class ScriptWriterEval implements ScriptWriterGeneric {
requirePackage(packageName) {}
async assign(variableName, functionName, props) {
assertValidJsIdentifier(variableName, 'variableName');
assertValidShellApiFunctionName(functionName);
const func = evalShellApiFunctionName(functionName, this.dbgateApi, this.requirePlugin);
this.variables[variableName] = await func(
@@ -226,10 +235,14 @@ export class ScriptWriterEval implements ScriptWriterGeneric {
}
assignValue(variableName, jsonValue) {
assertValidJsIdentifier(variableName, 'variableName');
this.variables[variableName] = jsonValue;
}
async copyStream(sourceVar, targetVar, colmapVar = null, progressName?: string | { name: string; runid: string }) {
assertValidJsIdentifier(sourceVar, 'sourceVar');
assertValidJsIdentifier(targetVar, 'targetVar');
if (colmapVar != null) assertValidJsIdentifier(colmapVar, 'colmapVar');
await this.dbgateApi.copyStream(this.variables[sourceVar], this.variables[targetVar], {
progressName: _cloneDeepWith(progressName, node => {
if (node?.$runid) {
+67 -2
View File
@@ -3,6 +3,64 @@ import _camelCase from 'lodash/camelCase';
import _isString from 'lodash/isString';
import _isPlainObject from 'lodash/isPlainObject';
const JS_IDENTIFIER_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
// ECMAScript reserved words, strict-mode keywords, and async-context keywords
// that cannot be used as variable or function names in the generated scripts.
// Sources: ECMA-262 §12.7.2 (reserved words), §12.7.3 (strict mode), §14 (contextual).
const JS_RESERVED_WORDS = new Set([
// Keywords
'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default',
'delete', 'do', 'else', 'export', 'extends', 'false', 'finally', 'for',
'function', 'if', 'import', 'in', 'instanceof', 'let', 'new', 'null', 'return',
'static', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var',
'void', 'while', 'with', 'yield',
// Strict-mode reserved words
'implements', 'interface', 'package', 'private', 'protected', 'public',
// Async context keywords
'async', 'await',
// Future reserved
'enum',
'eval', 'arguments',
]);
export function isValidJsIdentifier(name: string): boolean {
return typeof name === 'string' && JS_IDENTIFIER_RE.test(name) && !JS_RESERVED_WORDS.has(name);
}
export function assertValidJsIdentifier(name: string, label: string): void {
if (!isValidJsIdentifier(name)) {
throw new Error(`DBGM-00000 Invalid ${label}: ${String(name).substring(0, 100)}`);
}
}
/**
* Validates a shell API function name.
* Allowed forms:
* - "someFunctionName" (plain identifier, resolved as dbgateApi.someFunctionName)
* - "funcName@dbgate-plugin-xxx" (namespaced, resolved as plugin.shellApi.funcName)
*/
export function assertValidShellApiFunctionName(functionName: string): void {
if (typeof functionName !== 'string') {
throw new Error('DBGM-00000 functionName must be a string');
}
const nsMatch = functionName.match(/^([^@]+)@([^@]+)$/);
if (nsMatch) {
if (!isValidJsIdentifier(nsMatch[1])) {
throw new Error(`DBGM-00000 Invalid function part in functionName: ${nsMatch[1].substring(0, 100)}`);
}
if (!/^dbgate-plugin-[a-zA-Z0-9_-]+$/.test(nsMatch[2])) {
throw new Error(`DBGM-00000 Invalid plugin package in functionName: ${nsMatch[2].substring(0, 100)}`);
}
} else {
if (!isValidJsIdentifier(functionName)) {
throw new Error(`DBGM-00000 Invalid functionName: ${functionName.substring(0, 100)}`);
}
}
}
const VALID_PLUGIN_NAME_RE = /^dbgate-plugin-[a-zA-Z0-9_-]+$/;
export function extractShellApiPlugins(functionName, props): string[] {
const res = [];
const nsMatch = functionName.match(/^([^@]+)@([^@]+)/);
@@ -15,6 +73,11 @@ export function extractShellApiPlugins(functionName, props): string[] {
res.push(nsMatchEngine[2]);
}
}
for (const plugin of res) {
if (!VALID_PLUGIN_NAME_RE.test(plugin)) {
throw new Error(`DBGM-00000 Invalid plugin name: ${String(plugin).substring(0, 100)}`);
}
}
return res;
}
@@ -28,7 +91,8 @@ export function extractPackageName(name): string {
}
export function compileShellApiFunctionName(functionName) {
const nsMatch = functionName.match(/^([^@]+)@([^@]+)/);
assertValidShellApiFunctionName(functionName);
const nsMatch = functionName.match(/^([^@]+)@([^@]+)$/);
if (nsMatch) {
return `${_camelCase(nsMatch[2])}.shellApi.${nsMatch[1]}`;
}
@@ -36,7 +100,8 @@ export function compileShellApiFunctionName(functionName) {
}
export function evalShellApiFunctionName(functionName, dbgateApi, requirePlugin) {
const nsMatch = functionName.match(/^([^@]+)@([^@]+)/);
assertValidShellApiFunctionName(functionName);
const nsMatch = functionName.match(/^([^@]+)@([^@]+)$/);
if (nsMatch) {
return requirePlugin(nsMatch[2]).shellApi[nsMatch[1]];
}
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',
@@ -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');
}
+11 -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' }),
},
];
@@ -253,6 +253,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) {
@@ -619,7 +623,7 @@
}
async function handleExplainError(errorObject) {
if (!isProApp()) return;
if (!isProApp() || isAiDisabled()) return;
isAiAssistantVisible = true;
await tick();
domAiAssistant?.explainError({
+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,
@@ -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}
+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
+1675 -65
View File
File diff suppressed because it is too large Load Diff