1
1
mirror of https://github.com/Fabio286/antares.git synced 2025-06-05 21:59:22 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
67e849bc66 chore(deps): bump webpack from 5.91.0 to 5.98.0
Bumps [webpack](https://github.com/webpack/webpack) from 5.91.0 to 5.98.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.91.0...v5.98.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-01 19:25:34 +00:00
20 changed files with 514 additions and 1460 deletions

4
.github/FUNDING.yml vendored
View File

@@ -1,6 +1,6 @@
# These are supported funding model platforms # These are supported funding model platforms
github: # [antares-sql,fabio286] github: [antares-sql,fabio286]
patreon: #fabio286 patreon: #fabio286
open_collective: # Replace with a single Open Collective username open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username ko_fi: # Replace with a single Ko-fi username
@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
liberapay: # Replace with a single Liberapay username liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username otechie: # Replace with a single Otechie username
custom: # ['https://paypal.me/fabiodistasio'] custom: ['https://paypal.me/fabiodistasio']

View File

@@ -5,7 +5,7 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
steps: steps:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@v4 uses: actions/checkout@v4

View File

@@ -2,38 +2,6 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.7.35-beta.1](https://github.com/antares-sql/antares/compare/v0.7.35-beta.0...v0.7.35-beta.1) (2025-04-28)
### Bug Fixes
* handle id type correctly in update-table-cell where clause, fixes [#974](https://github.com/antares-sql/antares/issues/974) ([8f84892](https://github.com/antares-sql/antares/commit/8f84892f079e5eb56c69170eb4f7bbbebd1fda72))
* if webcontents with id 1 not found use first webcontents ([b3cf201](https://github.com/antares-sql/antares/commit/b3cf20101b938a4b47c454c7a94b32b3820bce8e))
* improve SQL parameter escaping in update-table-cell, ensuring correct handling of id types ([994aa69](https://github.com/antares-sql/antares/commit/994aa69fd00afc7e24e593b1a6c6667535e090c2))
* **MySQL:** handle absence of CHECK_CONSTRAINTS table, fixes [#981](https://github.com/antares-sql/antares/issues/981) ([86f5052](https://github.com/antares-sql/antares/commit/86f50521d05da0afdc9506d74e6ab007e2ae0a84))
### [0.7.35-beta.0](https://github.com/antares-sql/antares/compare/v0.7.34...v0.7.35-beta.0) (2025-04-04)
### Bug Fixes
* custom connection icon disappears during connection, fixes [#939](https://github.com/antares-sql/antares/issues/939) ([1d1be55](https://github.com/antares-sql/antares/commit/1d1be55d3d4ea621364c37e75de616046371feeb))
* escape SQL parameters in update and delete for where clauses, fixes [#964](https://github.com/antares-sql/antares/issues/964) ([ba63b04](https://github.com/antares-sql/antares/commit/ba63b049a3a059e77256141dc7b761efbbbf8c1e))
* improved handling of query comments, fixes [#963](https://github.com/antares-sql/antares/issues/963) and [#580](https://github.com/antares-sql/antares/issues/580) ([d912faa](https://github.com/antares-sql/antares/commit/d912faa85042219315c9c5658d7f20fda560af44))
* use custom elements wrapper for foreign column and description in query ([973b0fc](https://github.com/antares-sql/antares/commit/973b0fc4be1dac25757e430e4520d6fc2212f93b))
### Improvements
* **translation:** update Spanish translations, closes [#962](https://github.com/antares-sql/antares/issues/962) ([acea18e](https://github.com/antares-sql/antares/commit/acea18e6f061adab7e79d1249e0e68555a620db5))
### [0.7.34](https://github.com/antares-sql/antares/compare/v0.7.33...v0.7.34) (2025-02-14)
### Bug Fixes
* issue with some SSH connections, definitely ([eb706c3](https://github.com/antares-sql/antares/commit/eb706c3e51e9cb7577febd291a33594c0650a34a))
### [0.7.33](https://github.com/antares-sql/antares/compare/v0.7.32...v0.7.33) (2025-02-14) ### [0.7.33](https://github.com/antares-sql/antares/compare/v0.7.32...v0.7.33) (2025-02-14)

1401
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{ {
"name": "antares", "name": "antares",
"productName": "Antares", "productName": "Antares",
"version": "0.7.35-beta.1", "version": "0.7.33",
"description": "A modern, fast and productivity driven SQL client with a focus in UX.", "description": "A modern, fast and productivity driven SQL client with a focus in UX.",
"license": "MIT", "license": "MIT",
"repository": "https://github.com/antares-sql/antares.git", "repository": "https://github.com/antares-sql/antares.git",
@@ -174,7 +174,7 @@
"vue-i18n": "~9.13.1", "vue-i18n": "~9.13.1",
"vue-loader": "~16.8.3", "vue-loader": "~16.8.3",
"vuedraggable": "~4.1.0", "vuedraggable": "~4.1.0",
"webpack": "^5.91.0", "webpack": "^5.98.0",
"webpack-cli": "~4.9.1" "webpack-cli": "~4.9.1"
}, },
"devDependencies": { "devDependencies": {
@@ -214,8 +214,5 @@
"vue-eslint-parser": "~8.3.0", "vue-eslint-parser": "~8.3.0",
"webpack-dev-server": "~4.11.1", "webpack-dev-server": "~4.11.1",
"xvfb-maybe": "~0.2.1" "xvfb-maybe": "~0.2.1"
},
"optionalDependencies": {
"dmg-license": "~1.0.11"
} }
} }

View File

@@ -0,0 +1,86 @@
import { ClientCode } from 'common/interfaces/antares';
export const querySplitter =(sql: string, dbType: ClientCode): string[] => {
const queries: string[] = [];
let currentQuery = '';
let insideBlock = false;
let insideString = false;
let stringDelimiter: string | null = null;
let insideDollarTag = false;
let dollarTagDelimiter: string | null = null;
// Regex patterns for BEGIN-END blocks, dollar tags in PostgreSQL, and semicolons
const beginRegex = /\bBEGIN\b/i;
const endRegex = /\bEND\b;/i;
const dollarTagRegex = /\$(\w+)?\$/; // Matches $tag$ or $$
// Split on semicolons, keeping semicolons attached to the lines
const lines = sql.split(/(?<=;)/);
for (let line of lines) {
line = line.trim();
if (!line) continue;
for (let i = 0; i < line.length; i++) {
const char = line[i];
// Handle string boundaries
if ((char === '\'' || char === '"') && (!insideString || char === stringDelimiter)) {
if (!insideString) {
insideString = true;
stringDelimiter = char;
}
else {
insideString = false;
stringDelimiter = null;
}
}
currentQuery += char;
if (dbType === 'pg') {
// Handle dollar-quoted blocks in PostgreSQL
if (!insideString && line.slice(i).match(dollarTagRegex)) {
const match = line.slice(i).match(dollarTagRegex);
if (match) {
const tag = match[0];
if (!insideDollarTag) {
insideDollarTag = true;
dollarTagDelimiter = tag;
currentQuery += tag;
i += tag.length - 1;
}
else if (dollarTagDelimiter === tag) {
insideDollarTag = false;
dollarTagDelimiter = null;
currentQuery += tag;
i += tag.length - 1;
}
}
}
}
// Check BEGIN-END blocks
if (!insideString && !insideDollarTag) {
if (beginRegex.test(line))
insideBlock = true;
if (insideBlock && endRegex.test(line))
insideBlock = false;
}
}
// Append the query if we encounter a semicolon outside a BEGIN-END block, outside a string, and outside dollar tags
if (!insideBlock && !insideString && !insideDollarTag && /;\s*$/.test(line)) {
queries.push(currentQuery.trim());
currentQuery = '';
}
}
// Add any remaining query
if (currentQuery.trim())
queries.push(currentQuery.trim());
return queries;
};

View File

@@ -3,161 +3,18 @@
import { lineString, point, polygon } from '@turf/helpers'; import { lineString, point, polygon } from '@turf/helpers';
import { BIT, BLOB, DATE, DATETIME, FLOAT, IS_MULTI_SPATIAL, NUMBER, SPATIAL, TEXT_SEARCH } from 'common/fieldTypes'; import { BIT, BLOB, DATE, DATETIME, FLOAT, IS_MULTI_SPATIAL, NUMBER, SPATIAL, TEXT_SEARCH } from 'common/fieldTypes';
import * as antares from 'common/interfaces/antares'; import * as antares from 'common/interfaces/antares';
import { ClientCode } from 'common/interfaces/antares';
import * as moment from 'moment'; import * as moment from 'moment';
import customizations from '../customizations'; import customizations from '../customizations';
import { ClientCode } from '../interfaces/antares';
import { getArrayDepth } from './getArrayDepth'; import { getArrayDepth } from './getArrayDepth';
import hexToBinary, { HexChar } from './hexToBinary'; import hexToBinary, { HexChar } from './hexToBinary';
/** /**
* Splits a SQL string into multiple queries based on semicolons (;). * Escapes a string fo SQL use
* Handles BEGIN-END blocks, strings, comments, and PostgreSQL dollar-quoted tags.
* *
* @param {string} sql - The SQL string to split. * @param { String } string
* @param {ClientCode} dbType - The database type (e.g., 'pg', 'mysql'). * @returns { String } Escaped string
* @returns {string[]} - An array of separated SQL queries.
*/
export const querySplitter =(sql: string, dbType: ClientCode): string[] => {
const queries: string[] = [];
let currentQuery = '';
let insideBlock = false;
let insideString = false;
let stringDelimiter: string | null = null;
let insideDollarTag = false;
let dollarTagDelimiter: string | null = null;
// Regex patterns for BEGIN-END blocks, dollar tags in PostgreSQL, and semicolons
const beginRegex = /\bBEGIN\b/i;
const endRegex = /\bEND\b;/i;
const dollarTagRegex = /\$(\w+)?\$/; // Matches $tag$ or $$
// Split on semicolons, keeping semicolons attached to the lines
const lines = sql.split(/(?<=;)/);
for (let line of lines) {
line = line.trim();
if (!line) continue;
for (let i = 0; i < line.length; i++) {
const char = line[i];
// Handle string boundaries
if ((char === '\'' || char === '"') && (!insideString || char === stringDelimiter)) {
if (!insideString) {
insideString = true;
stringDelimiter = char;
}
else {
insideString = false;
stringDelimiter = null;
}
}
currentQuery += char;
if (dbType === 'pg') {
// Handle dollar-quoted blocks in PostgreSQL
if (!insideString && line.slice(i).match(dollarTagRegex)) {
const match = line.slice(i).match(dollarTagRegex);
if (match) {
const tag = match[0];
if (!insideDollarTag) {
insideDollarTag = true;
dollarTagDelimiter = tag;
currentQuery += tag;
i += tag.length - 1;
}
else if (dollarTagDelimiter === tag) {
insideDollarTag = false;
dollarTagDelimiter = null;
currentQuery += tag;
i += tag.length - 1;
}
}
}
}
// Check BEGIN-END blocks
if (!insideString && !insideDollarTag) {
if (beginRegex.test(line))
insideBlock = true;
if (insideBlock && endRegex.test(line))
insideBlock = false;
}
}
// Append the query if we encounter a semicolon outside a BEGIN-END block, outside a string, and outside dollar tags
if (!insideBlock && !insideString && !insideDollarTag && /;\s*$/.test(line)) {
queries.push(currentQuery.trim());
currentQuery = '';
}
}
// Add any remaining query
if (currentQuery.trim())
queries.push(currentQuery.trim());
return queries;
};
/**
* Removes all comments (both single-line and multi-line) from a SQL string.
*
* @param {string} sql - The SQL string to process.
* @returns {string} - The SQL string without comments.
*/
export const removeComments = (sql: string): string => {
let result = '';
let insideSingleLineComment = false;
let insideMultiLineComment = false;
for (let i = 0; i < sql.length; i++) {
const char = sql[i];
const nextChar = sql[i + 1] || '';
// Handle single-line comments (--)
if (!insideMultiLineComment && char === '-' && nextChar === '-')
insideSingleLineComment = true;
// Handle multi-line comments (/* */)
if (!insideSingleLineComment && char === '/' && nextChar === '*') {
insideMultiLineComment = true;
i++; // Skip the '*' character
continue;
}
if (insideMultiLineComment && char === '*' && nextChar === '/') {
insideMultiLineComment = false;
i++; // Skip the '/' character
continue;
}
// Skip characters inside comments
if (insideSingleLineComment) {
if (char === '\n')
insideSingleLineComment = false;
continue;
}
if (insideMultiLineComment)
continue;
// Append non-comment characters to the result
result += char;
}
return result;
};
/**
* Escapes a string for safe use in SQL queries.
*
* @param {string} string - The string to escape.
* @returns {string} - The escaped string.
*/ */
export const sqlEscaper = (string: string): string => { export const sqlEscaper = (string: string): string => {
// eslint-disable-next-line no-control-regex // eslint-disable-next-line no-control-regex
@@ -170,12 +27,6 @@ export const sqlEscaper = (string: string): string => {
}); });
}; };
/**
* Converts a value into a GeoJSON object based on its type.
*
* @param {any} val - The value to convert.
* @returns {object} - The generated GeoJSON object.
*/
export const objectToGeoJSON = (val: any) => { export const objectToGeoJSON = (val: any) => {
if (Array.isArray(val)) { if (Array.isArray(val)) {
if (getArrayDepth(val) === 1) if (getArrayDepth(val) === 1)
@@ -187,13 +38,6 @@ export const objectToGeoJSON = (val: any) => {
return point([val.x, val.y]); return point([val.x, val.y]);
}; };
/**
* Escapes and wraps a string in quotes for safe use in SQL queries.
*
* @param {string} val - The string to process.
* @param {ClientCode} client - The database type (e.g., 'pg', 'mysql').
* @returns {string} - The escaped and quoted string.
*/
export const escapeAndQuote = (val: string, client: ClientCode) => { export const escapeAndQuote = (val: string, client: ClientCode) => {
const { stringsWrapper: sw } = customizations[client]; const { stringsWrapper: sw } = customizations[client];
// eslint-disable-next-line no-control-regex // eslint-disable-next-line no-control-regex
@@ -230,17 +74,11 @@ export const escapeAndQuote = (val: string, client: ClientCode) => {
return `${sw}${escapedVal}${sw}`; return `${sw}${escapedVal}${sw}`;
}; };
/**
* Converts a value into a SQL string based on the field type and database type.
*
* @param {object} args - Arguments containing the value, database type, and field type.
* @returns {string} - The generated SQL string.
*/
export const valueToSqlString = (args: { export const valueToSqlString = (args: {
val: any; val: any;
client: ClientCode; client: ClientCode;
field: { type: string; datePrecision?: number; precision?: number | false; isArray?: boolean }; field: {type: string; datePrecision?: number; precision?: number | false; isArray?: boolean};
}): string => { }): string => {
let parsedValue; let parsedValue;
const { val, client, field } = args; const { val, client, field } = args;
const { stringsWrapper: sw } = customizations[client]; const { stringsWrapper: sw } = customizations[client];
@@ -327,19 +165,13 @@ export const valueToSqlString = (args: {
return parsedValue; return parsedValue;
}; };
/**
* Converts a JSON array into an SQL INSERT query.
*
* @param {object} args - Arguments containing the JSON data, database type, fields, and options.
* @returns {string} - The generated SQL INSERT query.
*/
export const jsonToSqlInsert = (args: { export const jsonToSqlInsert = (args: {
json: Record<string, any>[]; json: Record<string, any>[];
client: ClientCode; client: ClientCode;
fields: Record<string, { type: string; datePrecision: number }>; fields: Record<string, {type: string; datePrecision: number}>;
table: string; table: string;
options?: { sqlInsertAfter: number; sqlInsertDivider: 'bytes' | 'rows' }; options?: {sqlInsertAfter: number; sqlInsertDivider: 'bytes' | 'rows'};
}) => { }) => {
const { client, json, fields, table, options } = args; const { client, json, fields, table, options } = args;
const sqlInsertAfter = options && options.sqlInsertAfter ? options.sqlInsertAfter : 1; const sqlInsertAfter = options && options.sqlInsertAfter ? options.sqlInsertAfter : 1;
const sqlInsertDivider = options && options.sqlInsertDivider ? options.sqlInsertDivider : 'rows'; const sqlInsertDivider = options && options.sqlInsertDivider ? options.sqlInsertDivider : 'rows';
@@ -361,7 +193,7 @@ export const jsonToSqlInsert = (args: {
(sqlInsertDivider === 'bytes' && queryLength >= sqlInsertAfter * 1024) || (sqlInsertDivider === 'bytes' && queryLength >= sqlInsertAfter * 1024) ||
(sqlInsertDivider === 'rows' && rowsWritten === sqlInsertAfter) (sqlInsertDivider === 'rows' && rowsWritten === sqlInsertAfter)
) { ) {
insertsString += insertStmt + ';'; insertsString += insertStmt+';';
insertStmt = `\nINSERT INTO ${ew}${table}${ew} (${fieldNames.join(', ')}) VALUES `; insertStmt = `\nINSERT INTO ${ew}${table}${ew} (${fieldNames.join(', ')}) VALUES `;
rowsWritten = 0; rowsWritten = 0;
} }
@@ -374,18 +206,11 @@ export const jsonToSqlInsert = (args: {
} }
if (rowsWritten > 0) if (rowsWritten > 0)
insertsString += insertStmt + ';'; insertsString += insertStmt+';';
return insertsString; return insertsString;
}; };
/**
* Formats a JSON value for use in an SQL WHERE clause.
*
* @param {object} jsonValue - The JSON value to format.
* @param {ClientCode} clientType - The database type (e.g., 'pg', 'mysql').
* @returns {string} - The formatted SQL WHERE clause.
*/
export const formatJsonForSqlWhere = (jsonValue: object, clientType: antares.ClientCode) => { export const formatJsonForSqlWhere = (jsonValue: object, clientType: antares.ClientCode) => {
const formattedValue = JSON.stringify(jsonValue); const formattedValue = JSON.stringify(jsonValue);

View File

@@ -183,7 +183,6 @@ export default (connections: Record<string, antares.Client>) => {
const result = await connections[uid].raw(query, { const result = await connections[uid].raw(query, {
nest: true, nest: true,
details: true, details: true,
comments: false,
schema, schema,
tabUid, tabUid,
autocommit autocommit

View File

@@ -135,7 +135,7 @@ export default (connections: Record<string, antares.Client>) => {
try { // TODO: move to client classes try { // TODO: move to client classes
let escapedParam; let escapedParam;
let reload = false; let reload = false;
const id = typeof params.id === 'number' ? params.id : `${sw}${sqlEscaper(params.id)}${sw}`; const id = typeof params.id === 'number' ? params.id : `${sw}${params.id}${sw}`;
if ([...NUMBER, ...FLOAT].includes(params.type)) if ([...NUMBER, ...FLOAT].includes(params.type))
escapedParam = params.content; escapedParam = params.content;
@@ -233,7 +233,7 @@ export default (connections: Record<string, antares.Client>) => {
for (const key in orgRow) { for (const key in orgRow) {
if (typeof orgRow[key] === 'string') if (typeof orgRow[key] === 'string')
orgRow[key] = ` = '${sqlEscaper(orgRow[key])}'`; orgRow[key] = ` = '${orgRow[key]}'`;
else if (typeof orgRow[key] === 'object' && orgRow[key] !== null) else if (typeof orgRow[key] === 'object' && orgRow[key] !== null)
orgRow[key] = formatJsonForSqlWhere(orgRow[key], connections[params.uid]._client); orgRow[key] = formatJsonForSqlWhere(orgRow[key], connections[params.uid]._client);
else if (orgRow[key] === null) else if (orgRow[key] === null)
@@ -290,7 +290,7 @@ export default (connections: Record<string, antares.Client>) => {
for (const row of params.rows) { for (const row of params.rows) {
for (const key in row) { for (const key in row) {
if (typeof row[key] === 'string') if (typeof row[key] === 'string')
row[key] = `'${sqlEscaper(row[key])}'`; row[key] = `'${row[key]}'`;
if (row[key] === null) if (row[key] === null)
row[key] = 'IS NULL'; row[key] = 'IS NULL';
@@ -440,17 +440,16 @@ export default (connections: Record<string, antares.Client>) => {
ipcMain.handle('get-foreign-list', async (event, { uid, schema, table, column, description }) => { ipcMain.handle('get-foreign-list', async (event, { uid, schema, table, column, description }) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' }; if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
const { elementsWrapper: ew } = customizations[connections[uid]._client];
try { try {
const query = connections[uid] const query = connections[uid]
.select(`${ew}${column}${ew} AS foreign_column`) .select(`${column} AS foreign_column`)
.schema(schema) .schema(schema)
.from(table) .from(table)
.orderBy('foreign_column ASC'); .orderBy('foreign_column ASC');
if (description) if (description)
query.select(`LEFT(${ew}${description}${ew}, 20) AS foreign_description`); query.select(`LEFT(${description}, 20) AS foreign_description`);
const results = await query.run<Record<string, string>>(); const results = await query.run<Record<string, string>>();

View File

@@ -1,8 +1,8 @@
import * as antares from 'common/interfaces/antares'; import * as antares from 'common/interfaces/antares';
import { querySplitter } from 'common/libs/sqlUtils';
import mysql from 'mysql2/promise'; import mysql from 'mysql2/promise';
import * as pg from 'pg'; import * as pg from 'pg';
import SSH2Promise = require('@fabio286/ssh2-promise'); import SSH2Promise = require('@fabio286/ssh2-promise');
import { querySplitter } from 'common/libs/querySplitter';
import { ipcLogger, LoggerLevel } from '../misc/ipcLogger'; import { ipcLogger, LoggerLevel } from '../misc/ipcLogger';

View File

@@ -1,7 +1,6 @@
import dataTypes from 'common/data-types/firebird'; import dataTypes from 'common/data-types/firebird';
import { FLOAT, NUMBER } from 'common/fieldTypes'; import { FLOAT, NUMBER } from 'common/fieldTypes';
import * as antares from 'common/interfaces/antares'; import * as antares from 'common/interfaces/antares';
import { removeComments } from 'common/libs/sqlUtils';
import * as firebird from 'node-firebird'; import * as firebird from 'node-firebird';
import * as path from 'path'; import * as path from 'path';
@@ -1037,7 +1036,7 @@ export class FirebirdSQLClient extends BaseClient {
}; };
if (!args.comments) if (!args.comments)
sql = removeComments(sql); sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments
const resultsArr = []; const resultsArr = [];
let paramsArr = []; let paramsArr = [];

View File

@@ -2,7 +2,6 @@ import SSH2Promise = require('@fabio286/ssh2-promise');
import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig'; import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig';
import dataTypes from 'common/data-types/mysql'; import dataTypes from 'common/data-types/mysql';
import * as antares from 'common/interfaces/antares'; import * as antares from 'common/interfaces/antares';
import { removeComments } from 'common/libs/sqlUtils';
import * as mysql from 'mysql2/promise'; import * as mysql from 'mysql2/promise';
import * as EncodingToCharset from '../../../../node_modules/mysql2/lib/constants/encoding_charset.js'; import * as EncodingToCharset from '../../../../node_modules/mysql2/lib/constants/encoding_charset.js';
@@ -696,12 +695,7 @@ export class MySQLClient extends BaseClient {
return rows.length ? rows[0].count : 0; return rows.length ? rows[0].count : 0;
} }
async getTableChecks ({ schema, table }: { schema: string; table: string }): Promise<antares.TableCheck[] | false> { async getTableChecks ({ schema, table }: { schema: string; table: string }): Promise<antares.TableCheck[]> {
const { rows: checkTableExists } = await this.raw('SELECT table_name FROM information_schema.tables WHERE table_schema = "information_schema" AND table_name = "CHECK_CONSTRAINTS"');
if (!checkTableExists.length)// check if CHECK_CONSTRAINTS table exists
return false;
const { rows } = await this.raw(` const { rows } = await this.raw(`
SELECT SELECT
CONSTRAINT_NAME as name, CONSTRAINT_NAME as name,
@@ -1757,7 +1751,7 @@ export class MySQLClient extends BaseClient {
}; };
if (!args.comments) if (!args.comments)
sql = removeComments(sql); sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments
const nestTables = args.nest ? '.' : false; const nestTables = args.nest ? '.' : false;
const resultsArr: antares.QueryResult[] = []; const resultsArr: antares.QueryResult[] = [];

View File

@@ -2,7 +2,6 @@ import SSH2Promise = require('@fabio286/ssh2-promise');
import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig'; import SSHConfig from '@fabio286/ssh2-promise/lib/sshConfig';
import dataTypes from 'common/data-types/postgresql'; import dataTypes from 'common/data-types/postgresql';
import * as antares from 'common/interfaces/antares'; import * as antares from 'common/interfaces/antares';
import { removeComments } from 'common/libs/sqlUtils';
import * as pg from 'pg'; import * as pg from 'pg';
import * as pgAst from 'pgsql-ast-parser'; import * as pgAst from 'pgsql-ast-parser';
import { ConnectionOptions } from 'tls'; import { ConnectionOptions } from 'tls';
@@ -1661,7 +1660,7 @@ export class PostgreSQLClient extends BaseClient {
}; };
if (!args.comments) if (!args.comments)
sql = removeComments(sql); sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments
const resultsArr: antares.QueryResult[] = []; const resultsArr: antares.QueryResult[] = [];
let paramsArr = []; let paramsArr = [];

View File

@@ -2,7 +2,6 @@ import * as sqlite from 'better-sqlite3';
import dataTypes from 'common/data-types/sqlite'; import dataTypes from 'common/data-types/sqlite';
import { DATETIME, FLOAT, NUMBER, TIME } from 'common/fieldTypes'; import { DATETIME, FLOAT, NUMBER, TIME } from 'common/fieldTypes';
import * as antares from 'common/interfaces/antares'; import * as antares from 'common/interfaces/antares';
import { removeComments } from 'common/libs/sqlUtils';
import { BaseClient } from './BaseClient'; import { BaseClient } from './BaseClient';
@@ -625,7 +624,7 @@ export class SQLiteClient extends BaseClient {
}; };
if (!args.comments) if (!args.comments)
sql = removeComments(sql); sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments
const resultsArr = []; const resultsArr = [];
let paramsArr = []; let paramsArr = [];

View File

@@ -3,13 +3,7 @@ export type LoggerLevel = 'query' | 'error'
export const ipcLogger = ({ content, cUid, level }: {content: string; cUid: string; level: LoggerLevel}) => { export const ipcLogger = ({ content, cUid, level }: {content: string; cUid: string; level: LoggerLevel}) => {
if (level === 'error') { if (level === 'error') {
if (process.type !== undefined) { if (process.type !== undefined) {
const contents = require('electron').webContents.getAllWebContents(); const mainWindow = require('electron').webContents.fromId(1);
let mainWindow = require('electron').webContents.fromId(1);
contents.forEach(content => {
if (content.send && mainWindow === undefined) {
mainWindow = content;
}
});
mainWindow.send('non-blocking-exception', { cUid, message: content, date: new Date() }); mainWindow.send('non-blocking-exception', { cUid, message: content, date: new Date() });
} }
if (process.env.NODE_ENV === 'development' && process.type === 'browser') console.log(content); if (process.env.NODE_ENV === 'development' && process.type === 'browser') console.log(content);
@@ -18,13 +12,7 @@ export const ipcLogger = ({ content, cUid, level }: {content: string; cUid: stri
// Remove comments, newlines and multiple spaces // Remove comments, newlines and multiple spaces
const escapedSql = content.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '').replace(/\s\s+/g, ' '); const escapedSql = content.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '').replace(/\s\s+/g, ' ');
if (process.type !== undefined) { if (process.type !== undefined) {
const contents = require('electron').webContents.getAllWebContents(); const mainWindow = require('electron').webContents.fromId(1);
let mainWindow = require('electron').webContents.fromId(1);
contents.forEach(content => {
if (content.send && mainWindow === undefined) {
mainWindow = content;
}
});
mainWindow.send('query-log', { cUid, sql: escapedSql, date: new Date() }); mainWindow.send('query-log', { cUid, sql: escapedSql, date: new Date() });
} }
if (process.env.NODE_ENV === 'development' && process.type === 'browser') console.log(escapedSql); if (process.env.NODE_ENV === 'development' && process.type === 'browser') console.log(escapedSql);

View File

@@ -527,14 +527,14 @@ watch(() => props.connection, () => {
localConnection.value = JSON.parse(JSON.stringify(props.connection)); localConnection.value = JSON.parse(JSON.stringify(props.connection));
}); });
const startConnection = async (): Promise<void> => { const startConnection = async () => {
await saveConnection(); await saveConnection();
isConnecting.value = true; isConnecting.value = true;
if (localConnection.value.ask) if (localConnection.value.ask)
isAsking.value = true; isAsking.value = true;
else { else {
await connectWorkspace(localConnection.value, { signal: abortController.value.signal }).catch((): void => undefined); await connectWorkspace(localConnection.value, { signal: abortController.value.signal }).catch(() => undefined);
isConnecting.value = false; isConnecting.value = false;
} }
}; };
@@ -582,7 +582,7 @@ const continueTest = async (credentials: {user: string; password: string }) => {
try { try {
if (isConnecting.value) { if (isConnecting.value) {
const params = Object.assign({}, props.connection, credentials); const params = Object.assign({}, props.connection, credentials);
await connectWorkspace(params, { signal: abortController.value.signal }).catch((): void => undefined); await connectWorkspace(params, { signal: abortController.value.signal }).catch(() => undefined);
isConnecting.value = false; isConnecting.value = false;
} }
else { else {

View File

@@ -73,7 +73,7 @@
<span>{{ t('database.foreignKeys') }}</span> <span>{{ t('database.foreignKeys') }}</span>
</button> </button>
<button <button
v-if="workspace.customizations.tableCheck && originalTableChecks !== false" v-if="workspace.customizations.tableCheck"
class="btn btn-dark btn-sm ml-2 mr-0" class="btn btn-dark btn-sm ml-2 mr-0"
:disabled="isSaving" :disabled="isSaving"
:title="t('database.manageTableChecks')" :title="t('database.manageTableChecks')"
@@ -234,7 +234,7 @@
/> />
<WorkspaceTabPropsTableChecksModal <WorkspaceTabPropsTableChecksModal
v-if="isTableChecksModal" v-if="isTableChecksModal"
:local-checks="localTableChecks || []" :local-checks="localTableChecks"
:table="table" :table="table"
:workspace="workspace" :workspace="workspace"
@hide="hideTableChecksModal" @hide="hideTableChecksModal"
@@ -305,8 +305,8 @@ const originalKeyUsage: Ref<TableForeign[]> = ref([]);
const localKeyUsage: Ref<TableForeign[]> = ref([]); const localKeyUsage: Ref<TableForeign[]> = ref([]);
const originalIndexes: Ref<TableIndex[]> = ref([]); const originalIndexes: Ref<TableIndex[]> = ref([]);
const localIndexes: Ref<TableIndex[]> = ref([]); const localIndexes: Ref<TableIndex[]> = ref([]);
const originalTableChecks: Ref<TableCheck[] | false> = ref([]); const originalTableChecks: Ref<TableCheck[]> = ref([]);
const localTableChecks: Ref<TableCheck[] | false> = ref(false); const localTableChecks: Ref<TableCheck[]> = ref([]);
const tableOptions: Ref<TableOptions> = ref(null); const tableOptions: Ref<TableOptions> = ref(null);
const localOptions: Ref<TableOptions> = ref({} as TableOptions); const localOptions: Ref<TableOptions> = ref({} as TableOptions);
const lastTable = ref(null); const lastTable = ref(null);
@@ -465,19 +465,13 @@ const getFieldsData = async () => {
const { status, response } = await Tables.getTableChecks(params); const { status, response } = await Tables.getTableChecks(params);
if (status === 'success') { if (status === 'success') {
if (response === false) { originalTableChecks.value = response.map((check: TableCheck) => {
originalTableChecks.value = false; return {
localTableChecks.value = false; _antares_id: uidGen(),
} ...check
else { };
originalTableChecks.value = response.map((check: TableCheck) => { });
return { localTableChecks.value = JSON.parse(JSON.stringify(originalTableChecks.value));
_antares_id: uidGen(),
...check
};
});
localTableChecks.value = JSON.parse(JSON.stringify(originalTableChecks.value));
}
} }
else else
addNotification({ status: 'error', message: response }); addNotification({ status: 'error', message: response });
@@ -582,68 +576,32 @@ const saveChanges = async () => {
// Foreigns Deletions // Foreigns Deletions
foreignChanges.deletions = originalKeyUsage.value.filter(foreign => !localForeignIDs.includes(foreign._antares_id)); foreignChanges.deletions = originalKeyUsage.value.filter(foreign => !localForeignIDs.includes(foreign._antares_id));
// CHECKS
if (originalTableChecks.value !== false && localTableChecks.value !== false) {
const checkChanges = {
additions: [] as TableCheck[],
changes: [] as TableCheck[],
deletions: [] as TableCheck[]
};
const originalCheckIDs = originalTableChecks.value.reduce((acc, curr) => [...acc, curr._antares_id], []);
const localCheckIDs = localTableChecks.value.reduce((acc, curr) => [...acc, curr._antares_id], []);
// Check Additions
checkChanges.additions = localTableChecks.value.filter(check => !originalCheckIDs.includes(check._antares_id));
// Check Changes
originalTableChecks.value.forEach(originalCheck => {
const lI = Array.isArray(localTableChecks.value)
? localTableChecks.value.findIndex(localCheck => localCheck._antares_id === originalCheck._antares_id)
: -1;
if (Array.isArray(localTableChecks.value) && JSON.stringify(originalCheck) !== JSON.stringify(localTableChecks.value[lI])) {
if (localTableChecks.value[lI]) {
checkChanges.changes.push({
...localTableChecks.value[lI]
});
}
}
});
// Check Deletions
checkChanges.deletions = originalTableChecks.value.filter(check => !localCheckIDs.includes(check._antares_id));
}
// CHECKS // CHECKS
const checkChanges = { const checkChanges = {
additions: [] as TableCheck[], additions: [] as TableCheck[],
changes: [] as TableCheck[], changes: [] as TableCheck[],
deletions: [] as TableCheck[] deletions: [] as TableCheck[]
}; };
const originalCheckIDs = originalTableChecks.value.reduce((acc, curr) => [...acc, curr._antares_id], []);
const localCheckIDs = localTableChecks.value.reduce((acc, curr) => [...acc, curr._antares_id], []);
if (originalTableChecks.value !== false && localTableChecks.value !== false) { // Check Additions
const originalCheckIDs = originalTableChecks.value.reduce((acc, curr) => [...acc, curr._antares_id], []); checkChanges.additions = localTableChecks.value.filter(check => !originalCheckIDs.includes(check._antares_id));
const localCheckIDs = localTableChecks.value.reduce((acc, curr) => [...acc, curr._antares_id], []);
// Check Additions // Check Changes
checkChanges.additions = localTableChecks.value.filter(check => !originalCheckIDs.includes(check._antares_id)); originalTableChecks.value.forEach(originalCheck => {
const lI = localTableChecks.value.findIndex(localCheck => localCheck._antares_id === originalCheck._antares_id);
// Check Changes if (JSON.stringify(originalCheck) !== JSON.stringify(localTableChecks.value[lI])) {
originalTableChecks.value.forEach(originalCheck => { if (localTableChecks.value[lI]) {
const lI = Array.isArray(localTableChecks.value) checkChanges.changes.push({
? localTableChecks.value.findIndex(localCheck => localCheck._antares_id === originalCheck._antares_id) ...localTableChecks.value[lI]
: -1; });
if (Array.isArray(localTableChecks.value) && JSON.stringify(originalCheck) !== JSON.stringify(localTableChecks.value[lI])) {
if (localTableChecks.value[lI]) {
checkChanges.changes.push({
...localTableChecks.value[lI]
});
}
} }
}); }
});
// Check Deletions // Check Deletions
checkChanges.deletions = originalTableChecks.value.filter(check => !localCheckIDs.includes(check._antares_id)); checkChanges.deletions = originalTableChecks.value.filter(check => !localCheckIDs.includes(check._antares_id));
}
// ALTER // ALTER
const params = { const params = {

View File

@@ -36,7 +36,7 @@ export const esES = {
stay: 'Mantener', stay: 'Mantener',
author: 'Autor', author: 'Autor',
upload: 'Subir', upload: 'Subir',
browse: 'Explorar', browse: 'Navegar',
content: 'Contenido', content: 'Contenido',
cut: 'Cortar', cut: 'Cortar',
copy: 'Copiar', copy: 'Copiar',
@@ -91,7 +91,7 @@ export const esES = {
user: 'Usuario', user: 'Usuario',
password: 'Contraseña', password: 'Contraseña',
credentials: 'Credenciales', credentials: 'Credenciales',
connect: 'Conectar', connect: 'Connectar',
connected: 'Conectado', connected: 'Conectado',
disconnect: 'Desconectar', disconnect: 'Desconectar',
disconnected: 'Desconectado', disconnected: 'Desconectado',
@@ -117,7 +117,7 @@ export const esES = {
readOnlyMode: 'Solo lectura', readOnlyMode: 'Solo lectura',
allConnections: 'Todas las conexiones', allConnections: 'Todas las conexiones',
searchForConnections: 'Buscar por conexiones', searchForConnections: 'Buscar por conexiones',
keepAliveInterval: 'Mantenimiento de conexión', keepAliveInterval: 'Tiempo de mantenimiento de conexión',
singleConnection: 'Conexión única' singleConnection: 'Conexión única'
}, },
database: { // Database related terms database: { // Database related terms
@@ -198,11 +198,11 @@ export const esES = {
onUpdate: 'On UPDATE', onUpdate: 'On UPDATE',
deleteField: 'Eliminar campo', deleteField: 'Eliminar campo',
createNewIndex: 'Crear nuevo índice', createNewIndex: 'Crear nuevo índice',
createNewCheck: 'Crear nueva validación', createNewCheck: 'Crear nueva verificación',
checkClause: 'Comprobar cláusula', checkClause: 'Comprobar cláusula',
addToIndex: 'Añadir al índice', addToIndex: 'Añadir al índice',
createNewTable: 'Crear nueva tabla', createNewTable: 'Crear nueva tabla',
emptyTable: 'Vaciar tabla', emptyTable: 'Tabla vacía',
duplicateTable: 'Duplicar tabla', duplicateTable: 'Duplicar tabla',
deleteTable: 'Eliminar tabla', deleteTable: 'Eliminar tabla',
exportTable: 'Exportar tabla', exportTable: 'Exportar tabla',
@@ -376,10 +376,10 @@ export const esES = {
newFolder: 'Crear nueva carpeta', newFolder: 'Crear nueva carpeta',
outOfFolder: 'Fuera de la carpeta', outOfFolder: 'Fuera de la carpeta',
editConnectionAppearance: 'Modificar apariencia de conexión', editConnectionAppearance: 'Modificar apariencia de conexión',
defaultCopyType: 'Copiar fichero por defecto', defaultCopyType: 'Default copy type',
showTableSize: 'Mostrar tamaño de tabla en la barra lateral', showTableSize: 'Mostrar tamaño de tabla en la barra lateral',
showTableSizeDescription: 'Solo para MySQL/MariaDB. Habilitar esta opción puede afectar al rendimiento en esquemas con muchas tablas.', showTableSizeDescription: 'Solo para MySQL/MariaDB. Habilitar esta opción puede afectar al rendimiento en esquemas con muchas tablas.',
switchSearchMethod: 'Cambiar método de búsqueda', switchSearchMethod: 'Switch search method',
phpArray: 'Array de PHP', phpArray: 'Array de PHP',
closeAllTabs: 'Cerrar todas las pestañas', closeAllTabs: 'Cerrar todas las pestañas',
closeOtherTabs: 'Cerrar las otras pestañas', closeOtherTabs: 'Cerrar las otras pestañas',
@@ -396,32 +396,27 @@ export const esES = {
importDataExplanation: 'Importará un fichero con extensión .antares que contiene conexiones. Necesitará la contraseña con la que se encriptó el mismo.', importDataExplanation: 'Importará un fichero con extensión .antares que contiene conexiones. Necesitará la contraseña con la que se encriptó el mismo.',
includeConnectionPasswords: 'Incluir contraseñas de conexión', includeConnectionPasswords: 'Incluir contraseñas de conexión',
includeFolders: 'Incluir carpetas', includeFolders: 'Incluir carpetas',
encryptionPassword: 'Contraseña de encriptado', encryptionPassword: 'Encryption password',
encryptionPasswordError: 'La contraseña de encriptado debe tener al menos 8 caracteres', encryptionPasswordError: 'The encryption password must be at least 8 characters long.',
ignoreDuplicates: 'Ignorar duplicados', ignoreDuplicates: 'Ignore duplicates',
wrongImportPassword: 'Contraseña de importación errónea', wrongImportPassword: 'Wrong import password',
wrongFileFormat: 'Formato de fichero erróneo', wrongFileFormat: 'Wrong file format',
invalidFile: 'Fichero no válido', dataImportSuccess: 'Data successfully imported',
dataImportSuccess: 'Datos importados correctamente', note: 'Note | Notes',
note: 'Nota | Notas', thereAreNoNotesYet: 'There are no notes yet',
thereAreNoNotesYet: 'No hay notas', addNote: 'Add note',
addNote: 'Nueva nota', editNote: 'Edit note',
editNote: 'Modificar nota', saveAsNote: 'Save as note',
saveAsNote: 'Guardar como nota', showArchivedNotes: 'Show archived notes',
showArchivedNotes: 'Mostrar notas archivadas', hideArchivedNotes: 'Hide archived notes',
hideArchivedNotes: 'Ocultar notas archivadas', tag: 'Tag', // Note tag,
tag: 'Etiqueta', // Note tag, saveFile: 'Save file',
saveFile: 'Guardar fichero', saveFileAs: 'Save file as',
saveFileAs: 'Guardar fichero como...', openFile: 'Open file',
openFile: 'Abrir fichero', openNotes: 'Open notes',
openNotes: 'Abrir notas', debugConsole: 'Debug console', // <- console tab name
debugConsole: 'Consola de Depuración', // <- console tab name executedQueries: 'Executed queries', // <- console tab name
executedQueries: 'Consultas realizadas', // <- console tab name sizeLimitError: 'Maximum size of {size} exceeded'
sizeLimitError: 'Tamaño maximo de {size} excedido',
fullScreen: 'Pantalla completa',
zoomIn: 'Mas zoom',
zoomOut: 'Menos zoom',
zoomReset: 'Restablecer zoom'
}, },
faker: { // Faker.js methods, used in random generated content faker: { // Faker.js methods, used in random generated content
address: 'Dirección', address: 'Dirección',
@@ -574,12 +569,12 @@ export const esES = {
alphaNumeric: 'Alfanumérico', alphaNumeric: 'Alfanumérico',
hexaDecimal: 'Hexadecimal', hexaDecimal: 'Hexadecimal',
fileName: 'Nombre de fichero', fileName: 'Nombre de fichero',
commonFileName: 'Nombre de fichero común', commonFileName: 'Common file name',
mimeType: 'Mime-Type', mimeType: 'Mime-Type',
commonFileType: 'Tipo de fichero común', commonFileType: 'Common file type',
commonFileExt: 'Extensión de fichero común', commonFileExt: 'Common file extension',
fileType: 'Tipo de fichero', fileType: 'Tipo de fichero',
fileExt: 'Extensión de fichero', fileExt: 'Extension de fichero',
directoryPath: 'Ruta de directorio', directoryPath: 'Ruta de directorio',
filePath: 'Ruta de fichero', filePath: 'Ruta de fichero',
semver: 'SemVer', semver: 'SemVer',

View File

@@ -163,8 +163,7 @@ export const useConnectionsStore = defineStore('connections', {
uid: connection.uid, uid: connection.uid,
client: connection.client, client: connection.client,
icon: conn.icon, icon: conn.icon,
name: conn.name, name: conn.name
hasCustomIcon: conn.hasCustomIcon
}; };
} }
return conn; return conn;

View File

@@ -151,7 +151,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
? { ? {
...workspace, ...workspace,
structure: [] as WorkspaceStructure[], structure: [],
breadcrumbs: {}, breadcrumbs: {},
loadedSchemas: new Set(), loadedSchemas: new Set(),
database: connection.database, database: connection.database,
@@ -167,7 +167,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
? { ? {
...workspace, ...workspace,
structure: [] as WorkspaceStructure[], structure: [],
breadcrumbs: {}, breadcrumbs: {},
loadedSchemas: new Set(), loadedSchemas: new Set(),
connectionStatus: 'disconnected' connectionStatus: 'disconnected'
@@ -187,7 +187,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
? { ? {
...workspace, ...workspace,
structure: [] as WorkspaceStructure[], structure: [],
breadcrumbs: {}, breadcrumbs: {},
loadedSchemas: new Set(), loadedSchemas: new Set(),
connectionStatus: 'failed' connectionStatus: 'failed'
@@ -200,7 +200,9 @@ export const useWorkspacesStore = defineStore('workspaces', {
return reject(new Error('Connection aborted by user')); return reject(new Error('Connection aborted by user'));
else { else {
let clientCustomizations: Customizations; let clientCustomizations: Customizations;
connectionsStore.updateLastConnection(connection.uid); const { updateLastConnection } = connectionsStore;
updateLastConnection(connection.uid);
switch (connection.client) { switch (connection.client) {
case 'mysql': case 'mysql':
@@ -416,7 +418,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === uid this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === uid
? { ? {
...workspace, ...workspace,
structure: [] as WorkspaceStructure[], structure: [],
breadcrumbs: {}, breadcrumbs: {},
loadedSchemas: new Set(), loadedSchemas: new Set(),
connectionStatus: 'disconnected' connectionStatus: 'disconnected'