mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
d20414b692 | |||
22a8c25717 | |||
db47b4040a | |||
e89911b185 | |||
fccfe92453 | |||
d465e18dba | |||
ffb1712a59 | |||
9f6a183d9b | |||
1f80a64fe1 | |||
fc651149b9 | |||
964570247f | |||
8a6c59f7ce | |||
4d844fe2c9 | |||
d892fa6fb3 | |||
8c9e4f6e96 | |||
966ca60c89 | |||
9bbe218f90 | |||
a1c6be372b |
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -3,7 +3,7 @@ name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
assignees: Fabio286
|
||||
|
||||
---
|
||||
|
||||
@@ -25,13 +25,6 @@ If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
|
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -3,7 +3,7 @@ name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
assignees: Fabio286
|
||||
|
||||
---
|
||||
|
||||
|
23
CHANGELOG.md
23
CHANGELOG.md
@@ -2,6 +2,29 @@
|
||||
|
||||
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.1.0](https://github.com/Fabio286/antares/compare/v0.0.20...v0.1.0) (2021-03-21)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **PostgreSQL:** database in connection parameters ([9645702](https://github.com/Fabio286/antares/commit/964570247ff5b7b8317419730eec5bed4f0f0580))
|
||||
* **PostgreSQL:** edit array and text search fields ([fc65114](https://github.com/Fabio286/antares/commit/fc651149b95399c52d2d63e946731e9c1b0303a9))
|
||||
* **PostgreSQL:** insert and edit blob fields ([1f80a64](https://github.com/Fabio286/antares/commit/1f80a64fe1400baacca26f1a762c5aeb4ef6350d))
|
||||
* **PostgreSQL:** partial postgre implementation ([d892fa6](https://github.com/Fabio286/antares/commit/d892fa6fb3e86fbb96887d8eb67319ae855260a1))
|
||||
* **PostgreSQL:** support to microseconds ([d465e18](https://github.com/Fabio286/antares/commit/d465e18dba8ea3aa00726e33f9b1f70ca4c0683c))
|
||||
* **UI:** support to boolean fields ([ffb1712](https://github.com/Fabio286/antares/commit/ffb1712a593d1421793011e48a17369b884ea3c0))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update or delete rows with more than one primary key ([22a8c25](https://github.com/Fabio286/antares/commit/22a8c25717a4d4b285855426098a3a2846ce7448))
|
||||
* **MySQL:** handle NEWDECIMAL data type ([a1c6be3](https://github.com/Fabio286/antares/commit/a1c6be372b570cf13e89ef7ecf9b7a7c033a9293))
|
||||
* **PostgreSQL:** issue getting foreign keys informations ([db47b40](https://github.com/Fabio286/antares/commit/db47b4040a5282a6ac0711b1926c4c2ac867999e))
|
||||
* remove last char from datetime and time if is a dot ([e89911b](https://github.com/Fabio286/antares/commit/e89911b1851c19813d4acf2c79adfbc2ac7c1112))
|
||||
* **PostgreSQL:** single quote escape ([9f6a183](https://github.com/Fabio286/antares/commit/9f6a183d9b293dfe9ad9f3759f2375f05f37db8e))
|
||||
* **PostgreSQL:** various issues in query results ([fccfe92](https://github.com/Fabio286/antares/commit/fccfe92453325cd54c0331cc5670af0a56822c5b))
|
||||
* schema content not loaded if selected with right click ([8a6c59f](https://github.com/Fabio286/antares/commit/8a6c59f7ce7d051315b04cea38a96e4739b5b9d3))
|
||||
|
||||
### [0.0.20](https://github.com/Fabio286/antares/compare/v0.0.19...v0.0.20) (2021-03-13)
|
||||
|
||||
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<p align="center">
|
||||
<img width="800" src="https://raw.githubusercontent.com/Fabio286/antares/master/docs/screen-alpha.png">
|
||||
<img width="800" src="https://raw.githubusercontent.com/Fabio286/antares/master/docs/gh-logo.png">
|
||||
</p>
|
||||
|
||||
# Antares SQL Client
|
||||
|
||||
 [](https://travis-ci.com/Fabio286/antares)  
|
||||
 [](https://travis-ci.com/Fabio286/antares)   [](https://snapcraft.io/antares) [](https://snapcraft.io/antares)
|
||||
|
||||
Antares is an SQL client based on [Electron.js](https://github.com/electron/electron) and [Vue.js](https://github.com/vuejs/vue) that aims to become a useful tool, especially for developers.
|
||||
My target is to support as many databases as possible, and all major operating systems, including the ARM versions.
|
||||
@@ -85,7 +85,7 @@ Depending on your distribution, you will need to run the following command:
|
||||
|
||||
- [x] Windows
|
||||
- [x] Linux
|
||||
- [x] MacOS (i need feedbacks)
|
||||
- [x] MacOS (not tested due lack of hardware)
|
||||
|
||||
#### • ARM
|
||||
|
||||
|
BIN
docs/gh-logo.png
Normal file
BIN
docs/gh-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "antares",
|
||||
"productName": "Antares",
|
||||
"version": "0.0.20",
|
||||
"version": "0.1.0",
|
||||
"description": "A cross-platform easy to use SQL client.",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/Fabio286/antares.git",
|
||||
@@ -71,7 +71,9 @@
|
||||
"moment": "^2.29.1",
|
||||
"mssql": "^6.2.3",
|
||||
"mysql2": "^2.2.5",
|
||||
"node-sql-parser": "^3.1.0",
|
||||
"pg": "^8.5.1",
|
||||
"pgsql-ast-parser": "^7.0.2",
|
||||
"source-map-support": "^0.5.16",
|
||||
"spectre.css": "^0.5.9",
|
||||
"v-mask": "^2.2.4",
|
||||
|
41
src/common/customizations/defaults.js
Normal file
41
src/common/customizations/defaults.js
Normal file
@@ -0,0 +1,41 @@
|
||||
module.exports = {
|
||||
// Defaults
|
||||
defaultPort: null,
|
||||
defaultUser: null,
|
||||
defaultDatabase: null,
|
||||
// Core
|
||||
database: false,
|
||||
collations: false,
|
||||
engines: false,
|
||||
// Tools
|
||||
processesList: false,
|
||||
usersManagement: false,
|
||||
variables: false,
|
||||
// Structure
|
||||
schemas: false,
|
||||
tables: false,
|
||||
views: false,
|
||||
triggers: false,
|
||||
routines: false,
|
||||
functions: false,
|
||||
schedulers: false,
|
||||
// Settings
|
||||
tableAdd: false,
|
||||
viewAdd: false,
|
||||
triggerAdd: false,
|
||||
routineAdd: false,
|
||||
functionAdd: false,
|
||||
schedulerAdd: false,
|
||||
databaseEdit: false,
|
||||
schemaEdit: false,
|
||||
tableSettings: false,
|
||||
viewSettings: false,
|
||||
triggerSettings: false,
|
||||
routineSettings: false,
|
||||
functionSettings: false,
|
||||
schedulerSettings: false,
|
||||
indexes: false,
|
||||
foreigns: false,
|
||||
sortableFields: false,
|
||||
zerofill: false
|
||||
};
|
5
src/common/customizations/index.js
Normal file
5
src/common/customizations/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
maria: require('./mysql'),
|
||||
mysql: require('./mysql'),
|
||||
pg: require('./postgresql')
|
||||
};
|
40
src/common/customizations/mysql.js
Normal file
40
src/common/customizations/mysql.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const defaults = require('./defaults');
|
||||
|
||||
module.exports = {
|
||||
...defaults,
|
||||
// Defaults
|
||||
defaultPort: 3306,
|
||||
defaultUser: 'root',
|
||||
defaultDatabase: null,
|
||||
// Core
|
||||
collations: true,
|
||||
engines: true,
|
||||
// Tools
|
||||
processesList: true,
|
||||
// Structure
|
||||
schemas: true,
|
||||
tables: true,
|
||||
views: true,
|
||||
triggers: true,
|
||||
routines: true,
|
||||
functions: true,
|
||||
schedulers: true,
|
||||
// Settings
|
||||
tableAdd: true,
|
||||
viewAdd: true,
|
||||
triggerAdd: true,
|
||||
routineAdd: true,
|
||||
functionAdd: true,
|
||||
schedulerAdd: true,
|
||||
schemaEdit: true,
|
||||
tableSettings: true,
|
||||
viewSettings: true,
|
||||
triggerSettings: true,
|
||||
routineSettings: true,
|
||||
functionSettings: true,
|
||||
schedulerSettings: true,
|
||||
indexes: true,
|
||||
foreigns: true,
|
||||
sortableFields: true,
|
||||
zerofill: true
|
||||
};
|
31
src/common/customizations/postgresql.js
Normal file
31
src/common/customizations/postgresql.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const defaults = require('./defaults');
|
||||
|
||||
module.exports = {
|
||||
...defaults,
|
||||
// Defaults
|
||||
defaultPort: 5432,
|
||||
defaultUser: 'postgres',
|
||||
defaultDatabase: 'postgres',
|
||||
// Core
|
||||
database: true,
|
||||
// Tools
|
||||
processesList: true,
|
||||
// Structure
|
||||
tables: true,
|
||||
views: false,
|
||||
triggers: false,
|
||||
routines: false,
|
||||
functions: false,
|
||||
schedulers: false,
|
||||
// Settings
|
||||
databaseEdit: false,
|
||||
tableSettings: false,
|
||||
viewSettings: false,
|
||||
triggerSettings: false,
|
||||
routineSettings: false,
|
||||
functionSettings: false,
|
||||
schedulerSettings: false,
|
||||
indexes: true,
|
||||
foreigns: true,
|
||||
sortableFields: false
|
||||
};
|
297
src/common/data-types/postgresql.js
Normal file
297
src/common/data-types/postgresql.js
Normal file
@@ -0,0 +1,297 @@
|
||||
module.exports = [
|
||||
{
|
||||
group: 'integer',
|
||||
types: [
|
||||
{
|
||||
name: 'SMALLINT',
|
||||
length: true,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'INTEGER',
|
||||
length: true,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'BIGINT',
|
||||
length: true,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'DECIMAL',
|
||||
length: true,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'NUMERIC',
|
||||
length: true,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'SMALLSERIAL',
|
||||
length: true,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'SERIAL',
|
||||
length: true,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'BIGSERIAL',
|
||||
length: true,
|
||||
unsigned: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'float',
|
||||
types: [
|
||||
{
|
||||
name: 'REAL',
|
||||
length: true,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'DOUBLE PRECISION',
|
||||
length: true,
|
||||
unsigned: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'monetary',
|
||||
types: [
|
||||
{
|
||||
name: 'money',
|
||||
length: true,
|
||||
unsigned: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'string',
|
||||
types: [
|
||||
{
|
||||
name: 'CHARACTER VARYING',
|
||||
length: true,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'CHAR',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'CHARACTER',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'TEXT',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: '"CHAR"',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'NAME',
|
||||
length: false,
|
||||
unsigned: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'binary',
|
||||
types: [
|
||||
{
|
||||
name: 'BYTEA',
|
||||
length: true,
|
||||
unsigned: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'time',
|
||||
types: [
|
||||
{
|
||||
name: 'TIMESTAMP WITHOUT TIME ZONE',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'TIMESTAMP WITH TIME ZONE',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'DATE',
|
||||
length: true,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'TIME',
|
||||
length: true,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'TIME WITH TIME ZONE',
|
||||
length: true,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'INTERVAL',
|
||||
length: false,
|
||||
unsigned: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'boolean',
|
||||
types: [
|
||||
{
|
||||
name: 'BOOLEAN',
|
||||
length: false,
|
||||
unsigned: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'geometric',
|
||||
types: [
|
||||
{
|
||||
name: 'POINT',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'LINE',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'LSEG',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'BOX',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'PATH',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'POLYGON',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'CIRCLE',
|
||||
length: false,
|
||||
unsigned: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'network',
|
||||
types: [
|
||||
{
|
||||
name: 'CIDR',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'INET',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'MACADDR',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'MACADDR8',
|
||||
length: false,
|
||||
unsigned: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'bit',
|
||||
types: [
|
||||
{
|
||||
name: 'BIT',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'BIT VARYING',
|
||||
length: false,
|
||||
unsigned: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'text search',
|
||||
types: [
|
||||
{
|
||||
name: 'TSVECTOR',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'TSQUERY',
|
||||
length: false,
|
||||
unsigned: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'uuid',
|
||||
types: [
|
||||
{
|
||||
name: 'UUID',
|
||||
length: false,
|
||||
unsigned: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'xml',
|
||||
types: [
|
||||
{
|
||||
name: 'XML',
|
||||
length: false,
|
||||
unsigned: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'json',
|
||||
types: [
|
||||
{
|
||||
name: 'JSON',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'JSONB',
|
||||
length: false,
|
||||
unsigned: false
|
||||
},
|
||||
{
|
||||
name: 'JSONPATH',
|
||||
length: false,
|
||||
unsigned: false
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
@@ -1 +0,0 @@
|
||||
module.exports = [];
|
@@ -1,13 +1,75 @@
|
||||
export const TEXT = ['CHAR', 'VARCHAR'];
|
||||
export const LONG_TEXT = ['TEXT', 'MEDIUMTEXT', 'LONGTEXT'];
|
||||
export const TEXT = [
|
||||
'CHAR',
|
||||
'VARCHAR',
|
||||
'CHARACTER',
|
||||
'CHARACTER VARYING'
|
||||
];
|
||||
export const LONG_TEXT = [
|
||||
'TEXT',
|
||||
'MEDIUMTEXT',
|
||||
'LONGTEXT'
|
||||
];
|
||||
|
||||
export const NUMBER = ['INT', 'TINYINT', 'SMALLINT', 'MEDIUMINT', 'BIGINT', 'DECIMAL', 'BOOL'];
|
||||
export const FLOAT = ['FLOAT', 'DOUBLE'];
|
||||
export const ARRAY = [
|
||||
'ARRAY',
|
||||
'ANYARRAY'
|
||||
];
|
||||
|
||||
export const TEXT_SEARCH = [
|
||||
'TSVECTOR',
|
||||
'TSQUERY'
|
||||
];
|
||||
|
||||
export const NUMBER = [
|
||||
'INT',
|
||||
'TINYINT',
|
||||
'SMALLINT',
|
||||
'MEDIUMINT',
|
||||
'BIGINT',
|
||||
'DECIMAL',
|
||||
'NUMERIC',
|
||||
'INTEGER',
|
||||
'SMALLSERIAL',
|
||||
'SERIAL',
|
||||
'BIGSERIAL',
|
||||
'OID',
|
||||
'XID'
|
||||
];
|
||||
|
||||
export const FLOAT = [
|
||||
'FLOAT',
|
||||
'DOUBLE',
|
||||
'REAL',
|
||||
'DOUBLE PRECISION',
|
||||
'MONEY'
|
||||
];
|
||||
|
||||
export const BOOLEAN = [
|
||||
'BOOL',
|
||||
'BOOLEAN'
|
||||
];
|
||||
|
||||
export const DATE = ['DATE'];
|
||||
export const TIME = ['TIME'];
|
||||
export const DATETIME = ['DATETIME', 'TIMESTAMP'];
|
||||
export const TIME = [
|
||||
'TIME',
|
||||
'TIME WITH TIME ZONE'
|
||||
];
|
||||
export const DATETIME = [
|
||||
'DATETIME',
|
||||
'TIMESTAMP',
|
||||
'TIMESTAMP WITHOUT TIME ZONE',
|
||||
'TIMESTAMP WITH TIME ZONE'
|
||||
];
|
||||
|
||||
export const BLOB = ['BLOB', 'TINYBLOB', 'MEDIUMBLOB', 'LONGBLOB'];
|
||||
export const BLOB = [
|
||||
'BLOB',
|
||||
'TINYBLOB',
|
||||
'MEDIUMBLOB',
|
||||
'LONGBLOB',
|
||||
'BYTEA'
|
||||
];
|
||||
|
||||
export const BIT = ['BIT'];
|
||||
export const BIT = [
|
||||
'BIT',
|
||||
'BIT VARYING'
|
||||
];
|
||||
|
5
src/common/index-types/postgresql.js
Normal file
5
src/common/index-types/postgresql.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = [
|
||||
'PRIMARY',
|
||||
'KEY',
|
||||
'UNIQUE'
|
||||
];
|
@@ -11,6 +11,9 @@ export default connections => {
|
||||
password: conn.password
|
||||
};
|
||||
|
||||
if (conn.database)
|
||||
params.database = conn.database;
|
||||
|
||||
if (conn.ssl) {
|
||||
params.ssl = {
|
||||
key: conn.key ? fs.readFileSync(conn.key) : null,
|
||||
@@ -50,6 +53,9 @@ export default connections => {
|
||||
password: conn.password
|
||||
};
|
||||
|
||||
if (conn.database)
|
||||
params.database = conn.database;
|
||||
|
||||
if (conn.ssl) {
|
||||
params.ssl = {
|
||||
key: conn.key ? fs.readFileSync(conn.key) : null,
|
||||
@@ -59,13 +65,13 @@ export default connections => {
|
||||
};
|
||||
}
|
||||
|
||||
const connection = ClientsFactory.getConnection({
|
||||
client: conn.client,
|
||||
params,
|
||||
poolSize: 1
|
||||
});
|
||||
|
||||
try {
|
||||
const connection = ClientsFactory.getConnection({
|
||||
client: conn.client,
|
||||
params,
|
||||
poolSize: 1
|
||||
});
|
||||
|
||||
await connection.connect();
|
||||
|
||||
const structure = await connection.getStructure(new Set());
|
||||
|
@@ -7,7 +7,7 @@ import functions from './functions';
|
||||
import schedulers from './schedulers';
|
||||
import updates from './updates';
|
||||
import application from './application';
|
||||
import database from './database';
|
||||
import schema from './schema';
|
||||
import users from './users';
|
||||
|
||||
const connections = {};
|
||||
@@ -20,7 +20,7 @@ export default () => {
|
||||
routines(connections);
|
||||
functions(connections);
|
||||
schedulers(connections);
|
||||
database(connections);
|
||||
schema(connections);
|
||||
users(connections);
|
||||
updates();
|
||||
application();
|
||||
|
@@ -2,10 +2,9 @@
|
||||
import { ipcMain } from 'electron';
|
||||
|
||||
export default connections => {
|
||||
ipcMain.handle('create-database', async (event, params) => {
|
||||
ipcMain.handle('create-schema', async (event, params) => {
|
||||
try {
|
||||
const query = `CREATE DATABASE \`${params.name}\` COLLATE ${params.collation}`;
|
||||
await connections[params.uid].raw(query);
|
||||
await connections[params.uid].createSchema(params);
|
||||
|
||||
return { status: 'success' };
|
||||
}
|
||||
@@ -14,10 +13,9 @@ export default connections => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('update-database', async (event, params) => {
|
||||
ipcMain.handle('update-schema', async (event, params) => {
|
||||
try {
|
||||
const query = `ALTER DATABASE \`${params.name}\` COLLATE ${params.collation}`;
|
||||
await connections[params.uid].raw(query);
|
||||
await connections[params.uid].alterSchema(params);
|
||||
|
||||
return { status: 'success' };
|
||||
}
|
||||
@@ -26,10 +24,9 @@ export default connections => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('delete-database', async (event, params) => {
|
||||
ipcMain.handle('delete-schema', async (event, params) => {
|
||||
try {
|
||||
const query = `DROP DATABASE \`${params.database}\``;
|
||||
await connections[params.uid].raw(query);
|
||||
await connections[params.uid].dropSchema(params);
|
||||
|
||||
return { status: 'success' };
|
||||
}
|
||||
@@ -38,10 +35,9 @@ export default connections => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-database-collation', async (event, params) => { // TODO: move to mysql class
|
||||
ipcMain.handle('get-schema-collation', async (event, params) => {
|
||||
try {
|
||||
const query = `SELECT \`DEFAULT_COLLATION_NAME\` FROM \`information_schema\`.\`SCHEMATA\` WHERE \`SCHEMA_NAME\`='${params.database}'`;
|
||||
const collation = await connections[params.uid].raw(query);
|
||||
const collation = await connections[params.uid].getDatabaseCollation(params);
|
||||
|
||||
return { status: 'success', response: collation.rows.length ? collation.rows[0].DEFAULT_COLLATION_NAME : '' };
|
||||
}
|
@@ -2,7 +2,7 @@ import { ipcMain } from 'electron';
|
||||
import faker from 'faker';
|
||||
import moment from 'moment';
|
||||
import { sqlEscaper } from 'common/libs/sqlEscaper';
|
||||
import { TEXT, LONG_TEXT, NUMBER, FLOAT, BLOB, BIT, DATE, DATETIME } from 'common/fieldTypes';
|
||||
import { TEXT, LONG_TEXT, ARRAY, TEXT_SEARCH, NUMBER, FLOAT, BLOB, BIT, DATE, DATETIME } from 'common/fieldTypes';
|
||||
import fs from 'fs';
|
||||
|
||||
export default (connections) => {
|
||||
@@ -59,23 +59,56 @@ export default (connections) => {
|
||||
});
|
||||
|
||||
ipcMain.handle('update-table-cell', async (event, params) => {
|
||||
try {
|
||||
try { // TODO: move to client classes
|
||||
let escapedParam;
|
||||
let reload = false;
|
||||
const id = typeof params.id === 'number' ? params.id : `"${params.id}"`;
|
||||
|
||||
if ([...NUMBER, ...FLOAT].includes(params.type))
|
||||
escapedParam = params.content;
|
||||
else if ([...TEXT, ...LONG_TEXT].includes(params.type))
|
||||
escapedParam = `"${sqlEscaper(params.content)}"`;
|
||||
else if ([...TEXT, ...LONG_TEXT].includes(params.type)) {
|
||||
switch (connections[params.uid]._client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
escapedParam = `"${sqlEscaper(params.content)}"`;
|
||||
break;
|
||||
case 'pg':
|
||||
escapedParam = `'${params.content.replaceAll('\'', '\'\'')}'`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ARRAY.includes(params.type))
|
||||
escapedParam = `'${params.content}'`;
|
||||
else if (TEXT_SEARCH.includes(params.type))
|
||||
escapedParam = `'${params.content.replaceAll('\'', '\'\'')}'`;
|
||||
else if (BLOB.includes(params.type)) {
|
||||
if (params.content) {
|
||||
const fileBlob = fs.readFileSync(params.content);
|
||||
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||
let fileBlob;
|
||||
|
||||
switch (connections[params.uid]._client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
fileBlob = fs.readFileSync(params.content);
|
||||
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||
break;
|
||||
case 'pg':
|
||||
fileBlob = fs.readFileSync(params.content);
|
||||
escapedParam = `decode('${fileBlob.toString('hex')}', 'hex')`;
|
||||
break;
|
||||
}
|
||||
reload = true;
|
||||
}
|
||||
else
|
||||
escapedParam = '""';
|
||||
else {
|
||||
switch (connections[params.uid]._client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
escapedParam = '\'\'';
|
||||
break;
|
||||
case 'pg':
|
||||
escapedParam = 'decode(\'\', \'hex\')';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ([...BIT].includes(params.type)) {
|
||||
escapedParam = `b'${sqlEscaper(params.content)}'`;
|
||||
@@ -84,32 +117,33 @@ export default (connections) => {
|
||||
else if (params.content === null)
|
||||
escapedParam = 'NULL';
|
||||
else
|
||||
escapedParam = `"${sqlEscaper(params.content)}"`;
|
||||
escapedParam = `'${sqlEscaper(params.content)}'`;
|
||||
|
||||
if (params.primary) {
|
||||
if (params.primary) { // TODO: handle multiple primary
|
||||
await connections[params.uid]
|
||||
.update({ [params.field]: `= ${escapedParam}` })
|
||||
.schema(params.schema)
|
||||
.from(params.table)
|
||||
.where({ [params.primary]: `= ${id}` })
|
||||
.limit(1)
|
||||
.run();
|
||||
}
|
||||
else {
|
||||
const { row } = params;
|
||||
const { orgRow } = params;
|
||||
reload = true;
|
||||
|
||||
for (const key in row) {
|
||||
if (typeof row[key] === 'string')
|
||||
row[key] = `'${row[key]}'`;
|
||||
for (const key in orgRow) {
|
||||
if (typeof orgRow[key] === 'string')
|
||||
orgRow[key] = `'${orgRow[key]}'`;
|
||||
|
||||
row[key] = `= ${row[key]}`;
|
||||
orgRow[key] = `= ${orgRow[key]}`;
|
||||
}
|
||||
|
||||
await connections[params.uid]
|
||||
.schema(params.schema)
|
||||
.update({ [params.field]: `= ${escapedParam}` })
|
||||
.from(params.table)
|
||||
.where(row)
|
||||
.where(orgRow)
|
||||
.limit(1)
|
||||
.run();
|
||||
}
|
||||
@@ -167,7 +201,7 @@ export default (connections) => {
|
||||
});
|
||||
|
||||
ipcMain.handle('insert-table-rows', async (event, params) => {
|
||||
try {
|
||||
try { // TODO: move to client classes
|
||||
const insertObj = {};
|
||||
for (const key in params.row) {
|
||||
const type = params.fields[key];
|
||||
@@ -176,19 +210,46 @@ export default (connections) => {
|
||||
if (params.row[key] === null)
|
||||
escapedParam = 'NULL';
|
||||
else if ([...NUMBER, ...FLOAT].includes(type))
|
||||
escapedParam = params.row[key];
|
||||
else if ([...TEXT, ...LONG_TEXT].includes(type))
|
||||
escapedParam = `"${sqlEscaper(params.row[key])}"`;
|
||||
else if (BLOB.includes(type)) {
|
||||
if (params.row[key]) {
|
||||
const fileBlob = fs.readFileSync(params.row[key]);
|
||||
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||
escapedParam = +params.row[key];
|
||||
else if ([...TEXT, ...LONG_TEXT].includes(type)) {
|
||||
switch (connections[params.uid]._client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
escapedParam = `"${sqlEscaper(params.row[key].value)}"`;
|
||||
break;
|
||||
case 'pg':
|
||||
escapedParam = `'${params.row[key].value.replaceAll('\'', '\'\'')}'`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (BLOB.includes(type)) {
|
||||
if (params.row[key].value) {
|
||||
let fileBlob;
|
||||
|
||||
switch (connections[params.uid]._client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
fileBlob = fs.readFileSync(params.row[key].value);
|
||||
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||
break;
|
||||
case 'pg':
|
||||
fileBlob = fs.readFileSync(params.row[key].value);
|
||||
escapedParam = `decode('${fileBlob.toString('hex')}', 'hex')`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (connections[params.uid]._client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
escapedParam = '""';
|
||||
break;
|
||||
case 'pg':
|
||||
escapedParam = 'decode(\'\', \'hex\')';
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
escapedParam = '""';
|
||||
}
|
||||
else
|
||||
escapedParam = `"${sqlEscaper(params.row[key])}"`;
|
||||
|
||||
insertObj[key] = escapedParam;
|
||||
}
|
||||
@@ -209,7 +270,7 @@ export default (connections) => {
|
||||
});
|
||||
|
||||
ipcMain.handle('insert-table-fake-rows', async (event, params) => {
|
||||
try {
|
||||
try { // TODO: move to client classes
|
||||
const rows = [];
|
||||
|
||||
for (let i = 0; i < +params.repeat; i++) {
|
||||
@@ -224,20 +285,49 @@ export default (connections) => {
|
||||
escapedParam = 'NULL';
|
||||
else if ([...NUMBER, ...FLOAT].includes(type))
|
||||
escapedParam = params.row[key].value;
|
||||
else if ([...TEXT, ...LONG_TEXT].includes(type))
|
||||
escapedParam = `"${sqlEscaper(params.row[key].value)}"`;
|
||||
else if ([...TEXT, ...LONG_TEXT].includes(type)) {
|
||||
switch (connections[params.uid]._client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
escapedParam = `"${sqlEscaper(params.row[key].value)}"`;
|
||||
break;
|
||||
case 'pg':
|
||||
escapedParam = `'${params.row[key].value.replaceAll('\'', '\'\'')}'`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (BLOB.includes(type)) {
|
||||
if (params.row[key].value) {
|
||||
const fileBlob = fs.readFileSync(params.row[key].value);
|
||||
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||
let fileBlob;
|
||||
|
||||
switch (connections[params.uid]._client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
fileBlob = fs.readFileSync(params.row[key].value);
|
||||
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||
break;
|
||||
case 'pg':
|
||||
fileBlob = fs.readFileSync(params.row[key].value);
|
||||
escapedParam = `decode('${fileBlob.toString('hex')}', 'hex')`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (connections[params.uid]._client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
escapedParam = '""';
|
||||
break;
|
||||
case 'pg':
|
||||
escapedParam = 'decode(\'\', \'hex\')';
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
escapedParam = '""';
|
||||
}
|
||||
else if (BIT.includes(type))
|
||||
escapedParam = `b'${sqlEscaper(params.row[key].value)}'`;
|
||||
else
|
||||
escapedParam = `"${sqlEscaper(params.row[key].value)}"`;
|
||||
escapedParam = `'${sqlEscaper(params.row[key].value)}'`;
|
||||
|
||||
insertObj[key] = escapedParam;
|
||||
}
|
||||
@@ -261,10 +351,10 @@ export default (connections) => {
|
||||
if (typeof fakeValue === 'string') {
|
||||
if (params.row[key].length)
|
||||
fakeValue = fakeValue.substr(0, params.row[key].length);
|
||||
fakeValue = `"${sqlEscaper(fakeValue)}"`;
|
||||
fakeValue = `'${sqlEscaper(fakeValue)}'`;
|
||||
}
|
||||
else if ([...DATE, ...DATETIME].includes(type))
|
||||
fakeValue = `"${moment(fakeValue).format('YYYY-MM-DD HH:mm:ss.SSSSSS')}"`;
|
||||
fakeValue = `'${moment(fakeValue).format('YYYY-MM-DD HH:mm:ss.SSSSSS')}'`;
|
||||
|
||||
insertObj[key] = fakeValue;
|
||||
}
|
||||
@@ -289,13 +379,13 @@ export default (connections) => {
|
||||
ipcMain.handle('get-foreign-list', async (event, { uid, schema, table, column, description }) => {
|
||||
try {
|
||||
const query = connections[uid]
|
||||
.select(`${column} AS foreignColumn`)
|
||||
.select(`${column} AS foreign_column`)
|
||||
.schema(schema)
|
||||
.from(table)
|
||||
.orderBy('foreignColumn ASC');
|
||||
.orderBy('foreign_column ASC');
|
||||
|
||||
if (description)
|
||||
query.select(`LEFT(${description}, 20) AS foreignDescription`);
|
||||
query.select(`LEFT(${description}, 20) AS foreign_description`);
|
||||
|
||||
const results = await query.run();
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
import { MySQLClient } from './clients/MySQLClient';
|
||||
import { PostgreSQLClient } from './clients/PostgreSQLClient';
|
||||
|
||||
export class ClientsFactory {
|
||||
/**
|
||||
@@ -20,8 +21,10 @@ export class ClientsFactory {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
return new MySQLClient(args);
|
||||
case 'pg':
|
||||
return new PostgreSQLClient(args);
|
||||
default:
|
||||
return new Error(`Unknown database client: ${args.client}`);
|
||||
throw new Error(`Unknown database client: ${args.client}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -404,6 +404,44 @@ export class MySQLClient extends AntaresCore {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* CREATE DATABASE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async createSchema (params) {
|
||||
return await this.raw(`CREATE DATABASE \`${params.name}\` COLLATE ${params.collation}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* ALTER DATABASE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async alterSchema (params) {
|
||||
return await this.raw(`ALTER DATABASE \`${params.name}\` COLLATE ${params.collation}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* DROP DATABASE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropSchema (params) {
|
||||
return await this.raw(`DROP DATABASE \`${params.database}\``);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async getDatabaseCollation (params) {
|
||||
return await this.raw(`SELECT \`DEFAULT_COLLATION_NAME\` FROM \`information_schema\`.\`SCHEMATA\` WHERE \`SCHEMA_NAME\`='${params.database}'`);
|
||||
}
|
||||
|
||||
/**
|
||||
* SHOW CREATE VIEW
|
||||
*
|
||||
@@ -1281,7 +1319,7 @@ export class MySQLClient extends AntaresCore {
|
||||
const response = await this.getTableColumns(paramObj);
|
||||
remappedFields = remappedFields.map(field => {
|
||||
const detailedField = response.find(f => f.name === field.name);
|
||||
if (detailedField && field.orgTable === paramObj.table && field.schema === paramObj.schema && detailedField.name === field.orgName)
|
||||
if (detailedField && field.orgTable === paramObj.table && field.schema === paramObj.schema)
|
||||
field = { ...detailedField, ...field };
|
||||
return field;
|
||||
});
|
||||
|
1373
src/main/libs/clients/PostgreSQLClient.js
Normal file
1373
src/main/libs/clients/PostgreSQLClient.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -11,11 +11,11 @@
|
||||
</option>
|
||||
<option
|
||||
v-for="row in foreignList"
|
||||
:key="row.foreignColumn"
|
||||
:value="row.foreignColumn"
|
||||
:selected="row.foreignColumn === value"
|
||||
:key="row.foreign_column"
|
||||
:value="row.foreign_column"
|
||||
:selected="row.foreign_column === value"
|
||||
>
|
||||
{{ row.foreignColumn }} {{ 'foreignDescription' in row ? ` - ${row.foreignDescription}` : '' | cutText }}
|
||||
{{ row.foreign_column }} {{ 'foreign_description' in row ? ` - ${row.foreign_description}` : '' | cutText }}
|
||||
</option>
|
||||
</select>
|
||||
</template>
|
||||
@@ -51,11 +51,11 @@ export default {
|
||||
}),
|
||||
isValidDefault () {
|
||||
if (!this.foreignList.length) return true;
|
||||
return this.foreignList.some(foreign => foreign.foreignColumn.toString() === this.value.toString());
|
||||
return this.foreignList.some(foreign => foreign.foreign_column.toString() === this.value.toString());
|
||||
}
|
||||
},
|
||||
async created () {
|
||||
let firstTextField;
|
||||
let foreignDesc;
|
||||
const params = {
|
||||
uid: this.selectedWorkspace,
|
||||
schema: this.keyUsage.refSchema,
|
||||
@@ -64,8 +64,10 @@ export default {
|
||||
|
||||
try { // Field data
|
||||
const { status, response } = await Tables.getTableColumns(params);
|
||||
if (status === 'success')
|
||||
firstTextField = response.find(field => [...TEXT, ...LONG_TEXT].includes(field.type)).name || false;
|
||||
if (status === 'success') {
|
||||
const textField = response.find(field => [...TEXT, ...LONG_TEXT].includes(field.type));
|
||||
foreignDesc = textField ? textField.name : false;
|
||||
}
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
}
|
||||
@@ -77,7 +79,7 @@ export default {
|
||||
const { status, response } = await Tables.getForeignList({
|
||||
...params,
|
||||
column: this.keyUsage.refField,
|
||||
description: firstTextField
|
||||
description: foreignDesc
|
||||
});
|
||||
|
||||
if (status === 'success')
|
||||
|
@@ -30,7 +30,7 @@
|
||||
class="form-input"
|
||||
type="text"
|
||||
>
|
||||
<span class="input-group-addon field-type" :class="`type-${parameter.type.toLowerCase()}`">
|
||||
<span class="input-group-addon field-type" :class="typeClass(parameter.type)">
|
||||
{{ parameter.type }} {{ parameter.length | wrapNumber }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -75,6 +75,11 @@ export default {
|
||||
window.removeEventListener('keydown', this.onKey);
|
||||
},
|
||||
methods: {
|
||||
typeClass (type) {
|
||||
if (type)
|
||||
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||
return '';
|
||||
},
|
||||
runRoutine () {
|
||||
const valArr = Object.keys(this.values).reduce((acc, curr) => {
|
||||
const value = isNaN(this.values[curr]) ? `"${this.values[curr]}"` : this.values[curr];
|
||||
|
@@ -59,15 +59,15 @@
|
||||
<option value="maria">
|
||||
MariaDB
|
||||
</option>
|
||||
<option value="pg">
|
||||
PostgreSQL
|
||||
</option>
|
||||
<!-- <option value="mssql">
|
||||
Microsoft SQL
|
||||
</option>
|
||||
<option value="pg">
|
||||
PostgreSQL
|
||||
</option>
|
||||
<option value="oracledb">
|
||||
Oracle DB
|
||||
</option> -->
|
||||
Microsoft SQL
|
||||
</option>
|
||||
<option value="oracledb">
|
||||
Oracle DB
|
||||
</option> -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -97,6 +97,18 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="customizations.database" class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.database') }}</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<input
|
||||
v-model="localConnection.database"
|
||||
class="form-input"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.user') }}</label>
|
||||
@@ -247,6 +259,7 @@
|
||||
|
||||
<script>
|
||||
import { mapActions } from 'vuex';
|
||||
import customizations from 'common/customizations';
|
||||
import Connection from '@/ipc-api/Connection';
|
||||
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
||||
import BaseToast from '@/components/BaseToast';
|
||||
@@ -274,6 +287,11 @@ export default {
|
||||
selectedTab: 'general'
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
customizations () {
|
||||
return customizations[this.connection.client];
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.localConnection = Object.assign({}, this.connection);
|
||||
window.addEventListener('keydown', this.onKey);
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<div class="modal-header pl-2">
|
||||
<div class="modal-title h6">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-database-edit mr-1" /> {{ $t('message.editDatabase') }}
|
||||
<i class="mdi mdi-24px mdi-database-edit mr-1" /> {{ $t('message.editSchema') }}
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||
@@ -23,7 +23,7 @@
|
||||
class="form-input"
|
||||
type="text"
|
||||
required
|
||||
:placeholder="$t('message.databaseName')"
|
||||
:placeholder="$t('message.schemaName')"
|
||||
readonly
|
||||
>
|
||||
</div>
|
||||
@@ -53,7 +53,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer text-light">
|
||||
<button class="btn btn-primary mr-2" @click.stop="updateDatabase">
|
||||
<button class="btn btn-primary mr-2" @click.stop="updateSchema">
|
||||
{{ $t('word.update') }}
|
||||
</button>
|
||||
<button class="btn btn-link" @click.stop="closeModal">
|
||||
@@ -66,10 +66,10 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import Database from '@/ipc-api/Database';
|
||||
import Schema from '@/ipc-api/Schema';
|
||||
|
||||
export default {
|
||||
name: 'ModalEditDatabase',
|
||||
name: 'ModalEditSchema',
|
||||
props: {
|
||||
selectedDatabase: String
|
||||
},
|
||||
@@ -98,7 +98,7 @@ export default {
|
||||
async created () {
|
||||
let actualCollation;
|
||||
try {
|
||||
const { status, response } = await Database.getDatabaseCollation({ uid: this.selectedWorkspace, database: this.selectedDatabase });
|
||||
const { status, response } = await Schema.getDatabaseCollation({ uid: this.selectedWorkspace, database: this.selectedDatabase });
|
||||
|
||||
if (status === 'success')
|
||||
actualCollation = response;
|
||||
@@ -130,10 +130,10 @@ export default {
|
||||
...mapActions({
|
||||
addNotification: 'notifications/addNotification'
|
||||
}),
|
||||
async updateDatabase () {
|
||||
async updateSchema () {
|
||||
if (this.database.collation !== this.database.prevCollation) {
|
||||
try {
|
||||
const { status, response } = await Database.updateDatabase({
|
||||
const { status, response } = await Schema.updateSchema({
|
||||
uid: this.selectedWorkspace,
|
||||
...this.database
|
||||
});
|
@@ -34,7 +34,7 @@
|
||||
:field-obj="localRow[field.name]"
|
||||
:value.sync="localRow[field.name]"
|
||||
>
|
||||
<span class="input-group-addon field-type" :class="`type-${field.type.toLowerCase()}`">
|
||||
<span class="input-group-addon field-type" :class="typeClass(field.type)">
|
||||
{{ field.type }} {{ fieldLength(field) | wrapNumber }}
|
||||
</span>
|
||||
<label class="form-checkbox ml-3" :title="$t('word.insert')">
|
||||
@@ -286,6 +286,11 @@ export default {
|
||||
...mapActions({
|
||||
addNotification: 'notifications/addNotification'
|
||||
}),
|
||||
typeClass (type) {
|
||||
if (type)
|
||||
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||
return '';
|
||||
},
|
||||
async insertRows () {
|
||||
this.isInserting = true;
|
||||
const rowToInsert = this.localRow;
|
||||
|
@@ -63,12 +63,12 @@
|
||||
<option value="maria">
|
||||
MariaDB
|
||||
</option>
|
||||
<option value="pg">
|
||||
PostgreSQL
|
||||
</option>
|
||||
<!-- <option value="mssql">
|
||||
Microsoft SQL
|
||||
</option>
|
||||
<option value="pg">
|
||||
PostgreSQL
|
||||
</option>
|
||||
<option value="oracledb">
|
||||
Oracle DB
|
||||
</option> -->
|
||||
@@ -101,6 +101,18 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="customizations.database" class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.database') }}</label>
|
||||
</div>
|
||||
<div class="col-8 col-sm-12">
|
||||
<input
|
||||
v-model="connection.database"
|
||||
class="form-input"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.user') }}</label>
|
||||
@@ -251,6 +263,7 @@
|
||||
|
||||
<script>
|
||||
import { mapActions } from 'vuex';
|
||||
import customizations from 'common/customizations';
|
||||
import Connection from '@/ipc-api/Connection';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
||||
@@ -270,8 +283,9 @@ export default {
|
||||
name: '',
|
||||
client: 'mysql',
|
||||
host: '127.0.0.1',
|
||||
port: '3306',
|
||||
user: 'root',
|
||||
database: null,
|
||||
port: null,
|
||||
user: null,
|
||||
password: '',
|
||||
ask: false,
|
||||
uid: uidGen('C'),
|
||||
@@ -291,7 +305,13 @@ export default {
|
||||
selectedTab: 'general'
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
customizations () {
|
||||
return customizations[this.connection.client];
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.setDefaults();
|
||||
window.addEventListener('keydown', this.onKey);
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -307,20 +327,9 @@ export default {
|
||||
addConnection: 'connections/addConnection'
|
||||
}),
|
||||
setDefaults () {
|
||||
switch (this.connection.client) {
|
||||
case 'mysql':
|
||||
this.connection.port = '3306';
|
||||
break;
|
||||
case 'mssql':
|
||||
this.connection.port = '1433';
|
||||
break;
|
||||
case 'pg':
|
||||
this.connection.port = '5432';
|
||||
break;
|
||||
case 'oracledb':
|
||||
this.connection.port = '1521';
|
||||
break;
|
||||
}
|
||||
this.connection.user = this.customizations.defaultUser;
|
||||
this.connection.port = this.customizations.defaultPort;
|
||||
this.connection.database = this.customizations.defaultDatabase;
|
||||
},
|
||||
async startTest () {
|
||||
this.isTesting = true;
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<div class="modal-header pl-2">
|
||||
<div class="modal-title h6">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-database-plus mr-1" /> {{ $t('message.createNewDatabase') }}
|
||||
<i class="mdi mdi-24px mdi-database-plus mr-1" /> {{ $t('message.createNewSchema') }}
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||
@@ -24,11 +24,11 @@
|
||||
class="form-input"
|
||||
type="text"
|
||||
required
|
||||
:placeholder="$t('message.databaseName')"
|
||||
:placeholder="$t('message.schemaName')"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div v-if="customizations.collations" class="form-group">
|
||||
<div class="col-3">
|
||||
<label class="form-label">{{ $t('word.collation') }}</label>
|
||||
</div>
|
||||
@@ -49,7 +49,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer text-light">
|
||||
<button class="btn btn-primary mr-2" @click.stop="createDatabase">
|
||||
<button
|
||||
class="btn btn-primary mr-2"
|
||||
:class="{'loading': isLoading}"
|
||||
@click.stop="createSchema"
|
||||
>
|
||||
{{ $t('word.add') }}
|
||||
</button>
|
||||
<button class="btn btn-link" @click.stop="closeModal">
|
||||
@@ -62,12 +66,13 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import Database from '@/ipc-api/Database';
|
||||
import Schema from '@/ipc-api/Schema';
|
||||
|
||||
export default {
|
||||
name: 'ModalNewDatabase',
|
||||
name: 'ModalNewSchema',
|
||||
data () {
|
||||
return {
|
||||
isLoading: false,
|
||||
database: {
|
||||
name: '',
|
||||
collation: ''
|
||||
@@ -83,8 +88,11 @@ export default {
|
||||
collations () {
|
||||
return this.getWorkspace(this.selectedWorkspace).collations;
|
||||
},
|
||||
customizations () {
|
||||
return this.getWorkspace(this.selectedWorkspace).customizations;
|
||||
},
|
||||
defaultCollation () {
|
||||
return this.getDatabaseVariable(this.selectedWorkspace, 'collation_server').value || '';
|
||||
return this.getDatabaseVariable(this.selectedWorkspace, 'collation_server') ? this.getDatabaseVariable(this.selectedWorkspace, 'collation_server').value : '';
|
||||
}
|
||||
},
|
||||
created () {
|
||||
@@ -101,9 +109,10 @@ export default {
|
||||
...mapActions({
|
||||
addNotification: 'notifications/addNotification'
|
||||
}),
|
||||
async createDatabase () {
|
||||
async createSchema () {
|
||||
this.isLoading = true;
|
||||
try {
|
||||
const { status, response } = await Database.createDatabase({
|
||||
const { status, response } = await Schema.createSchema({
|
||||
uid: this.selectedWorkspace,
|
||||
...this.database
|
||||
});
|
||||
@@ -118,6 +127,7 @@ export default {
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
this.isLoading = false;
|
||||
},
|
||||
closeModal () {
|
||||
this.$emit('close');
|
@@ -69,7 +69,7 @@
|
||||
:disabled="fieldsToExclude.includes(field.name)"
|
||||
:tabindex="key+1"
|
||||
>
|
||||
<span class="input-group-addon" :class="`type-${field.type.toLowerCase()}`">
|
||||
<span class="input-group-addon" :class="typeCLass(field.type)">
|
||||
{{ field.type }} {{ fieldLength(field) | wrapNumber }}
|
||||
</span>
|
||||
<label class="form-checkbox ml-3" :title="$t('word.insert')">
|
||||
@@ -222,6 +222,11 @@ export default {
|
||||
...mapActions({
|
||||
addNotification: 'notifications/addNotification'
|
||||
}),
|
||||
typeClass (type) {
|
||||
if (type)
|
||||
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||
return '';
|
||||
},
|
||||
async insertRows () {
|
||||
this.isInserting = true;
|
||||
const rowToInsert = this.localRow;
|
||||
|
@@ -101,7 +101,7 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import Database from '@/ipc-api/Database';
|
||||
import Schema from '@/ipc-api/Schema';
|
||||
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
|
||||
import ProcessesListRow from '@/components/ProcessesListRow';
|
||||
|
||||
@@ -181,7 +181,7 @@ export default {
|
||||
this.results = [];
|
||||
|
||||
try { // Table data
|
||||
const { status, response } = await Database.getProcesses(this.connection.uid);
|
||||
const { status, response } = await Schema.getProcesses(this.connection.uid);
|
||||
|
||||
if (status === 'success') {
|
||||
this.results = response;
|
||||
|
@@ -16,19 +16,27 @@
|
||||
<i class="mdi mdi-24px mdi-tools" />
|
||||
</a>
|
||||
<ul class="menu text-left text-uppercase">
|
||||
<li class="menu-item">
|
||||
<li v-if="workspace.customizations.processesList" class="menu-item">
|
||||
<a class="c-hand p-vcentered" @click="showProcessesModal">
|
||||
<i class="mdi mdi-memory mr-1 tool-icon" />
|
||||
<span>{{ $t('message.processesList') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="menu-item" title="Coming...">
|
||||
<li
|
||||
v-if="workspace.customizations.variables"
|
||||
class="menu-item"
|
||||
title="Coming..."
|
||||
>
|
||||
<a class="c-hand p-vcentered disabled">
|
||||
<i class="mdi mdi-shape mr-1 tool-icon" />
|
||||
<span>{{ $t('word.variables') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="menu-item" title="Coming...">
|
||||
<li
|
||||
v-if="workspace.customizations.usersManagement"
|
||||
class="menu-item"
|
||||
title="Coming..."
|
||||
>
|
||||
<a class="c-hand p-vcentered disabled">
|
||||
<i class="mdi mdi-account-group mr-1 tool-icon" />
|
||||
<span>{{ $t('message.manageUsers') }}</span>
|
||||
@@ -37,7 +45,7 @@
|
||||
</ul>
|
||||
</li>
|
||||
<li
|
||||
v-if="schemaChild"
|
||||
v-if="schemaChild && isSettingSupported"
|
||||
class="tab-item"
|
||||
:class="{'active': selectedTab === 'prop'}"
|
||||
@click="selectTab({uid: workspace.uid, tab: 'prop'})"
|
||||
@@ -194,6 +202,15 @@ export default {
|
||||
isSelected () {
|
||||
return this.selectedWorkspace === this.connection.uid;
|
||||
},
|
||||
isSettingSupported () {
|
||||
if (this.workspace.breadcrumbs.table && this.workspace.customizations.tableSettings) return true;
|
||||
if (this.workspace.breadcrumbs.view && this.workspace.customizations.viewSettings) return true;
|
||||
if (this.workspace.breadcrumbs.trigger && this.workspace.customizations.triggerSettings) return true;
|
||||
if (this.workspace.breadcrumbs.procedure && this.workspace.customizations.routineSettings) return true;
|
||||
if (this.workspace.breadcrumbs.function && this.workspace.customizations.functionSettings) return true;
|
||||
if (this.workspace.breadcrumbs.scheduler && this.workspace.customizations.schedulerSettings) return true;
|
||||
return false;
|
||||
},
|
||||
selectedTab () {
|
||||
if (
|
||||
(
|
||||
|
@@ -11,7 +11,7 @@
|
||||
<span v-if="workspace.connected" class="workspace-explorebar-tools">
|
||||
<i
|
||||
class="mdi mdi-18px mdi-database-plus c-hand mr-2"
|
||||
:title="$t('message.createNewDatabase')"
|
||||
:title="$t('message.createNewSchema')"
|
||||
@click="showNewDBModal"
|
||||
/>
|
||||
<i
|
||||
@@ -44,18 +44,18 @@
|
||||
:connection="connection"
|
||||
/>
|
||||
<div v-else class="workspace-explorebar-body">
|
||||
<WorkspaceExploreBarDatabase
|
||||
<WorkspaceExploreBarSchema
|
||||
v-for="db of workspace.structure"
|
||||
:key="db.name"
|
||||
:database="db"
|
||||
:connection="connection"
|
||||
@show-database-context="openDatabaseContext"
|
||||
@show-schema-context="openSchemaContext"
|
||||
@show-table-context="openTableContext"
|
||||
@show-misc-context="openMiscContext"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ModalNewDatabase
|
||||
<ModalNewSchema
|
||||
v-if="isNewDBModal"
|
||||
@close="hideNewDBModal"
|
||||
@reload="refresh"
|
||||
@@ -137,11 +137,11 @@ import Functions from '@/ipc-api/Functions';
|
||||
import Schedulers from '@/ipc-api/Schedulers';
|
||||
|
||||
import WorkspaceConnectPanel from '@/components/WorkspaceConnectPanel';
|
||||
import WorkspaceExploreBarDatabase from '@/components/WorkspaceExploreBarDatabase';
|
||||
import DatabaseContext from '@/components/WorkspaceExploreBarDatabaseContext';
|
||||
import WorkspaceExploreBarSchema from '@/components/WorkspaceExploreBarSchema';
|
||||
import DatabaseContext from '@/components/WorkspaceExploreBarSchemaContext';
|
||||
import TableContext from '@/components/WorkspaceExploreBarTableContext';
|
||||
import MiscContext from '@/components/WorkspaceExploreBarMiscContext';
|
||||
import ModalNewDatabase from '@/components/ModalNewDatabase';
|
||||
import ModalNewSchema from '@/components/ModalNewSchema';
|
||||
import ModalNewTable from '@/components/ModalNewTable';
|
||||
import ModalNewView from '@/components/ModalNewView';
|
||||
import ModalNewTrigger from '@/components/ModalNewTrigger';
|
||||
@@ -153,11 +153,11 @@ export default {
|
||||
name: 'WorkspaceExploreBar',
|
||||
components: {
|
||||
WorkspaceConnectPanel,
|
||||
WorkspaceExploreBarDatabase,
|
||||
WorkspaceExploreBarSchema,
|
||||
DatabaseContext,
|
||||
TableContext,
|
||||
MiscContext,
|
||||
ModalNewDatabase,
|
||||
ModalNewSchema,
|
||||
ModalNewTable,
|
||||
ModalNewView,
|
||||
ModalNewTrigger,
|
||||
@@ -299,8 +299,8 @@ export default {
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
},
|
||||
openDatabaseContext (payload) {
|
||||
this.selectedDatabase = payload.database;
|
||||
openSchemaContext (payload) {
|
||||
this.selectedDatabase = payload.schema;
|
||||
this.databaseContextEvent = payload.event;
|
||||
this.isDatabaseContext = true;
|
||||
},
|
||||
|
@@ -4,7 +4,7 @@
|
||||
class="accordion-header database-name"
|
||||
:class="{'text-bold': breadcrumbs.schema === database.name}"
|
||||
@click="selectSchema(database.name)"
|
||||
@contextmenu.prevent="showDatabaseContext($event, database.name)"
|
||||
@contextmenu.prevent="showSchemaContext($event, database.name)"
|
||||
>
|
||||
<div v-if="isLoading" class="icon loading" />
|
||||
<i v-else class="icon mdi mdi-18px mdi-chevron-right" />
|
||||
@@ -37,7 +37,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="filteredTriggers.length" class="database-misc">
|
||||
<div v-if="filteredTriggers.length && customizations.triggers" class="database-misc">
|
||||
<details class="accordion">
|
||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger}">
|
||||
<i class="misc-icon mdi mdi-18px mdi-folder-cog mr-1" />
|
||||
@@ -65,7 +65,7 @@
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div v-if="filteredProcedures.length" class="database-misc">
|
||||
<div v-if="filteredProcedures.length && customizations.routines" class="database-misc">
|
||||
<details class="accordion">
|
||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure}">
|
||||
<i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" />
|
||||
@@ -93,7 +93,7 @@
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div v-if="filteredFunctions.length" class="database-misc">
|
||||
<div v-if="filteredFunctions.length && customizations.functions" class="database-misc">
|
||||
<details class="accordion">
|
||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function}">
|
||||
<i class="misc-icon mdi mdi-18px mdi-folder-move mr-1" />
|
||||
@@ -121,7 +121,7 @@
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div v-if="filteredSchedulers.length" class="database-misc">
|
||||
<div v-if="filteredSchedulers.length && customizations.schedulers" class="database-misc">
|
||||
<details class="accordion">
|
||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler}">
|
||||
<i class="misc-icon mdi mdi-18px mdi-folder-clock mr-1" />
|
||||
@@ -157,7 +157,7 @@ import { mapActions, mapGetters } from 'vuex';
|
||||
import { formatBytes } from 'common/libs/formatBytes';
|
||||
|
||||
export default {
|
||||
name: 'WorkspaceExploreBarDatabase',
|
||||
name: 'WorkspaceExploreBarSchema',
|
||||
props: {
|
||||
database: Object,
|
||||
connection: Object
|
||||
@@ -194,6 +194,9 @@ export default {
|
||||
breadcrumbs () {
|
||||
return this.getWorkspace(this.connection.uid).breadcrumbs;
|
||||
},
|
||||
customizations () {
|
||||
return this.getWorkspace(this.connection.uid).customizations;
|
||||
},
|
||||
loadedSchemas () {
|
||||
return this.getLoadedSchemas(this.connection.uid);
|
||||
},
|
||||
@@ -222,9 +225,9 @@ export default {
|
||||
|
||||
this.changeBreadcrumbs({ schema, table: null });
|
||||
},
|
||||
showDatabaseContext (event, database) {
|
||||
this.changeBreadcrumbs({ schema: database, table: null });
|
||||
this.$emit('show-database-context', { event, database });
|
||||
showSchemaContext (event, schema) {
|
||||
this.selectSchema(schema);
|
||||
this.$emit('show-schema-context', { event, schema });
|
||||
},
|
||||
showTableContext (event, table) {
|
||||
this.setBreadcrumbs({ schema: this.database.name, [table.type]: table.name });
|
@@ -7,27 +7,55 @@
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-plus text-light pr-1" /> {{ $t('word.add') }}</span>
|
||||
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
||||
<div class="context-submenu">
|
||||
<div class="context-element" @click="showCreateTableModal">
|
||||
<div
|
||||
v-if="workspace.customizations.tableAdd"
|
||||
class="context-element"
|
||||
@click="showCreateTableModal"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table text-light pr-1" /> {{ $t('word.table') }}</span>
|
||||
</div>
|
||||
<div class="context-element" @click="showCreateViewModal">
|
||||
<div
|
||||
v-if="workspace.customizations.viewAdd"
|
||||
class="context-element"
|
||||
@click="showCreateViewModal"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-eye text-light pr-1" /> {{ $t('word.view') }}</span>
|
||||
</div>
|
||||
<div class="context-element" @click="showCreateTriggerModal">
|
||||
<div
|
||||
v-if="workspace.customizations.triggerAdd"
|
||||
class="context-element"
|
||||
@click="showCreateTriggerModal"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $tc('word.trigger', 1) }}</span>
|
||||
</div>
|
||||
<div class="context-element" @click="showCreateRoutineModal">
|
||||
<div
|
||||
v-if="workspace.customizations.routineAdd"
|
||||
class="context-element"
|
||||
@click="showCreateRoutineModal"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span>
|
||||
</div>
|
||||
<div class="context-element" @click="showCreateFunctionModal">
|
||||
<div
|
||||
v-if="workspace.customizations.functionAdd"
|
||||
class="context-element"
|
||||
@click="showCreateFunctionModal"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.function', 1) }}</span>
|
||||
</div>
|
||||
<div class="context-element" @click="showCreateSchedulerModal">
|
||||
<div
|
||||
v-if="workspace.customizations.schedulerAdd"
|
||||
class="context-element"
|
||||
@click="showCreateSchedulerModal"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $tc('word.scheduler', 1) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="context-element" @click="showEditModal">
|
||||
<div
|
||||
v-if="workspace.customizations.schemaEdit"
|
||||
class="context-element"
|
||||
@click="showEditModal"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-database-edit text-light pr-1" /> {{ $t('word.edit') }}</span>
|
||||
</div>
|
||||
<div class="context-element" @click="showDeleteModal">
|
||||
@@ -36,12 +64,12 @@
|
||||
|
||||
<ConfirmModal
|
||||
v-if="isDeleteModal"
|
||||
@confirm="deleteDatabase"
|
||||
@confirm="deleteSchema"
|
||||
@hide="hideDeleteModal"
|
||||
>
|
||||
<template slot="header">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-database-remove mr-1" /> {{ $t('message.deleteDatabase') }}
|
||||
<i class="mdi mdi-24px mdi-database-remove mr-1" /> {{ $t('message.deleteSchema') }}
|
||||
</div>
|
||||
</template>
|
||||
<div slot="body">
|
||||
@@ -50,7 +78,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</ConfirmModal>
|
||||
<ModalEditDatabase
|
||||
<ModalEditSchema
|
||||
v-if="isEditModal"
|
||||
:selected-database="selectedDatabase"
|
||||
@close="hideEditModal"
|
||||
@@ -62,15 +90,15 @@
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import BaseContextMenu from '@/components/BaseContextMenu';
|
||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||
import ModalEditDatabase from '@/components/ModalEditDatabase';
|
||||
import Database from '@/ipc-api/Database';
|
||||
import ModalEditSchema from '@/components/ModalEditSchema';
|
||||
import Schema from '@/ipc-api/Schema';
|
||||
|
||||
export default {
|
||||
name: 'WorkspaceExploreBarDatabaseContext',
|
||||
name: 'WorkspaceExploreBarSchemaContext',
|
||||
components: {
|
||||
BaseContextMenu,
|
||||
ConfirmModal,
|
||||
ModalEditDatabase
|
||||
ModalEditSchema
|
||||
},
|
||||
props: {
|
||||
contextEvent: MouseEvent,
|
||||
@@ -130,9 +158,9 @@ export default {
|
||||
closeContext () {
|
||||
this.$emit('close-context');
|
||||
},
|
||||
async deleteDatabase () {
|
||||
async deleteSchema () {
|
||||
try {
|
||||
const { status, response } = await Database.deleteDatabase({
|
||||
const { status, response } = await Schema.deleteSchema({
|
||||
uid: this.selectedWorkspace,
|
||||
database: this.selectedDatabase
|
||||
});
|
@@ -41,7 +41,7 @@
|
||||
>
|
||||
<div class="tile-icon">
|
||||
<div>
|
||||
<i class="mdi mdi-hexagon mdi-24px" :class="`type-${param.type.toLowerCase()}`" />
|
||||
<i class="mdi mdi-hexagon mdi-24px" :class="typeClass(param.type)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-content">
|
||||
@@ -183,6 +183,11 @@ export default {
|
||||
window.removeEventListener('resize', this.getModalInnerHeight);
|
||||
},
|
||||
methods: {
|
||||
typeClass (type) {
|
||||
if (type)
|
||||
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||
return '';
|
||||
},
|
||||
confirmParametersChange () {
|
||||
this.$emit('parameters-update', this.parametersProxy);
|
||||
},
|
||||
|
@@ -41,7 +41,7 @@
|
||||
>
|
||||
<div class="tile-icon">
|
||||
<div>
|
||||
<i class="mdi mdi-hexagon mdi-24px" :class="`type-${param.type.toLowerCase()}`" />
|
||||
<i class="mdi mdi-hexagon mdi-24px" :class="typeClass(param.type)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-content">
|
||||
@@ -214,6 +214,11 @@ export default {
|
||||
window.removeEventListener('resize', this.getModalInnerHeight);
|
||||
},
|
||||
methods: {
|
||||
typeClass (type) {
|
||||
if (type)
|
||||
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||
return '';
|
||||
},
|
||||
confirmParametersChange () {
|
||||
this.$emit('parameters-update', this.parametersProxy);
|
||||
},
|
||||
|
@@ -48,7 +48,7 @@
|
||||
<span
|
||||
v-if="!isInlineEditor.type"
|
||||
class="cell-content text-left"
|
||||
:class="`type-${lowerCase(localRow.type)}`"
|
||||
:class="typeClass(localRow.type)"
|
||||
@click="editON($event, localRow.type.toUpperCase(), 'type')"
|
||||
>
|
||||
{{ localRow.type }}
|
||||
@@ -378,10 +378,10 @@ export default {
|
||||
return 'UNKNOWN ' + key;
|
||||
}
|
||||
},
|
||||
lowerCase (val) {
|
||||
if (val)
|
||||
return val.toLowerCase();
|
||||
return val;
|
||||
typeClass (type) {
|
||||
if (type)
|
||||
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||
return '';
|
||||
},
|
||||
initLocalRow () {
|
||||
Object.keys(this.localRow).forEach(key => {
|
||||
|
@@ -68,7 +68,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Database from '@/ipc-api/Database';
|
||||
import Schema from '@/ipc-api/Schema';
|
||||
import QueryEditor from '@/components/QueryEditor';
|
||||
import BaseLoader from '@/components/BaseLoader';
|
||||
import WorkspaceQueryTable from '@/components/WorkspaceQueryTable';
|
||||
@@ -150,7 +150,7 @@ export default {
|
||||
query
|
||||
};
|
||||
|
||||
const { status, response } = await Database.rawQuery(params);
|
||||
const { status, response } = await Schema.rawQuery(params);
|
||||
|
||||
if (status === 'success') {
|
||||
this.results = Array.isArray(response) ? response : [response];
|
||||
|
@@ -122,7 +122,12 @@ export default {
|
||||
return this.getWorkspace(this.connUid).breadcrumbs.schema;
|
||||
},
|
||||
primaryField () {
|
||||
return this.fields.filter(field => ['pri', 'uni'].includes(field.key))[0] || false;
|
||||
const primaryFields = this.fields.filter(field => ['pri', 'uni'].includes(field.key));
|
||||
|
||||
if (primaryFields.length > 1 || !primaryFields.length)
|
||||
return false;
|
||||
|
||||
return primaryFields[0];
|
||||
},
|
||||
isSortable () {
|
||||
return this.fields.every(field => field.name);
|
||||
@@ -289,15 +294,17 @@ export default {
|
||||
this.resizeResults();
|
||||
},
|
||||
updateField (payload, row) {
|
||||
const localRow = Object.assign({}, row);
|
||||
delete localRow._id;
|
||||
const orgRow = this.localResults.find(lr => lr._id === row._id);
|
||||
delete row._id;
|
||||
delete orgRow._id;
|
||||
|
||||
const params = {
|
||||
primary: this.primaryField.name,
|
||||
schema: this.getSchema(this.resultsetIndex),
|
||||
table: this.getTable(this.resultsetIndex),
|
||||
id: this.getPrimaryValue(localRow),
|
||||
localRow,
|
||||
id: this.getPrimaryValue(orgRow),
|
||||
row,
|
||||
orgRow,
|
||||
...payload
|
||||
};
|
||||
this.$emit('update-field', params);
|
||||
@@ -326,6 +333,7 @@ export default {
|
||||
table: this.getTable(this.resultsetIndex),
|
||||
id: this.getPrimaryValue(row),
|
||||
row,
|
||||
orgRow: row,
|
||||
field: this.selectedCell.field,
|
||||
content: null
|
||||
};
|
||||
|
@@ -12,7 +12,7 @@
|
||||
<span
|
||||
v-if="!isInlineEditor[cKey]"
|
||||
class="cell-content px-2"
|
||||
:class="`${isNull(col)} type-${fields[cKey].type.toLowerCase()}`"
|
||||
:class="`${isNull(col)} ${typeClass(fields[cKey].type)}`"
|
||||
@dblclick="editON($event, col, cKey)"
|
||||
>{{ col | typeFormat(fields[cKey].type.toLowerCase(), fields[cKey].length) | cutText }}</span>
|
||||
<ForeignKeySelect
|
||||
@@ -34,6 +34,15 @@
|
||||
class="editable-field px-2"
|
||||
@blur="editOFF"
|
||||
>
|
||||
<select
|
||||
v-else-if="inputProps.type === 'boolean'"
|
||||
v-model="editingContent"
|
||||
class="form-select small-select editable-field"
|
||||
@blur="editOFF"
|
||||
>
|
||||
<option>true</option>
|
||||
<option>false</option>
|
||||
</select>
|
||||
<input
|
||||
v-else
|
||||
ref="editField"
|
||||
@@ -173,7 +182,7 @@ import { mimeFromHex } from 'common/libs/mimeFromHex';
|
||||
import { formatBytes } from 'common/libs/formatBytes';
|
||||
import { bufferToBase64 } from 'common/libs/bufferToBase64';
|
||||
import hexToBinary from 'common/libs/hexToBinary';
|
||||
import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
||||
import { TEXT, LONG_TEXT, ARRAY, TEXT_SEARCH, NUMBER, FLOAT, BOOLEAN, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
||||
import { VueMaskDirective } from 'v-mask';
|
||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||
import TextEditor from '@/components/BaseTextEditor';
|
||||
@@ -204,6 +213,9 @@ export default {
|
||||
return moment(val).isValid() ? moment(val).format('YYYY-MM-DD') : val;
|
||||
|
||||
if (DATETIME.includes(type)) {
|
||||
if (typeof val === 'string')
|
||||
return val;
|
||||
|
||||
let datePrecision = '';
|
||||
for (let i = 0; i < precision; i++)
|
||||
datePrecision += i === 0 ? '.S' : 'S';
|
||||
@@ -225,6 +237,12 @@ export default {
|
||||
return hexToBinary(hex);
|
||||
}
|
||||
|
||||
if (ARRAY.includes(type)) {
|
||||
if (Array.isArray(val))
|
||||
return JSON.stringify(val).replaceAll('[', '{').replaceAll(']', '}');
|
||||
return val;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
},
|
||||
@@ -287,8 +305,8 @@ export default {
|
||||
if (BLOB.includes(this.editingType))
|
||||
return { type: 'file', mask: false };
|
||||
|
||||
if (BIT.includes(this.editingType))
|
||||
return { type: 'text', mask: false };
|
||||
if (BOOLEAN.includes(this.editingType))
|
||||
return { type: 'boolean', mask: false };
|
||||
|
||||
return { type: 'text', mask: false };
|
||||
},
|
||||
@@ -331,6 +349,11 @@ export default {
|
||||
isNull (value) {
|
||||
return value === null ? ' is-null' : '';
|
||||
},
|
||||
typeClass (type) {
|
||||
if (type)
|
||||
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||
return '';
|
||||
},
|
||||
bufferToBase64 (val) {
|
||||
return bufferToBase64(val);
|
||||
},
|
||||
@@ -345,7 +368,7 @@ export default {
|
||||
this.editingField = field;
|
||||
this.editingLength = this.fields[field].length;
|
||||
|
||||
if (LONG_TEXT.includes(type)) {
|
||||
if ([...LONG_TEXT, ...ARRAY, ...TEXT_SEARCH].includes(type)) {
|
||||
this.isTextareaEditor = true;
|
||||
this.editingContent = this.$options.filters.typeFormat(content, type);
|
||||
return;
|
||||
@@ -389,7 +412,13 @@ export default {
|
||||
this.isInlineEditor[this.editingField] = false;
|
||||
let content;
|
||||
if (!BLOB.includes(this.editingType)) {
|
||||
if ([...DATETIME, ...TIME].includes(this.editingType)) {
|
||||
if (this.editingContent.substring(this.editingContent.length - 1) === '.')
|
||||
this.editingContent = this.editingContent.slice(0, -1);
|
||||
}
|
||||
|
||||
if (this.editingContent === this.$options.filters.typeFormat(this.originalContent, this.editingType, this.editingLength)) return;// If not changed
|
||||
|
||||
content = this.editingContent;
|
||||
}
|
||||
else { // Handle file upload
|
||||
|
@@ -100,7 +100,8 @@ module.exports = {
|
||||
paste: 'Paste',
|
||||
tools: 'Tools',
|
||||
variables: 'Variables',
|
||||
processes: 'Processes'
|
||||
processes: 'Processes',
|
||||
database: 'Database'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Welcome to Antares SQL Client!',
|
||||
@@ -199,7 +200,11 @@ module.exports = {
|
||||
setNull: 'Set NULL',
|
||||
processesList: 'Processes list',
|
||||
processInfo: 'Process info',
|
||||
manageUsers: 'Manage users'
|
||||
manageUsers: 'Manage users',
|
||||
createNewSchema: 'Create new schema',
|
||||
schemaName: 'Schema name',
|
||||
editSchema: 'Edit schema',
|
||||
deleteSchema: 'Delete schema'
|
||||
},
|
||||
faker: {
|
||||
address: 'Address',
|
||||
|
@@ -2,20 +2,20 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
export default class {
|
||||
static createDatabase (params) {
|
||||
return ipcRenderer.invoke('create-database', params);
|
||||
static createSchema (params) {
|
||||
return ipcRenderer.invoke('create-schema', params);
|
||||
}
|
||||
|
||||
static updateDatabase (params) {
|
||||
return ipcRenderer.invoke('update-database', params);
|
||||
static updateSchema (params) {
|
||||
return ipcRenderer.invoke('update-schema', params);
|
||||
}
|
||||
|
||||
static getDatabaseCollation (params) {
|
||||
return ipcRenderer.invoke('get-database-collation', params);
|
||||
return ipcRenderer.invoke('get-schema-collation', params);
|
||||
}
|
||||
|
||||
static deleteDatabase (params) {
|
||||
return ipcRenderer.invoke('delete-database', params);
|
||||
static deleteSchema (params) {
|
||||
return ipcRenderer.invoke('delete-schema', params);
|
||||
}
|
||||
|
||||
static getStructure (params) {
|
@@ -22,6 +22,14 @@
|
||||
"mediumtext": $string-color,
|
||||
"longtext": $string-color,
|
||||
"json": $string-color,
|
||||
"name": $string-color,
|
||||
"character": $string-color,
|
||||
"character_varying": $string-color,
|
||||
"cidr": $string-color,
|
||||
"inet": $string-color,
|
||||
"macaddr": $string-color,
|
||||
"macaddr8": $string-color,
|
||||
"uuid": $string-color,
|
||||
"int": $number-color,
|
||||
"tinyint": $number-color,
|
||||
"smallint": $number-color,
|
||||
@@ -31,12 +39,26 @@
|
||||
"decimal": $number-color,
|
||||
"bigint": $number-color,
|
||||
"newdecimal": $number-color,
|
||||
"integer": $number-color,
|
||||
"numeric": $number-color,
|
||||
"smallserial": $number-color,
|
||||
"serial": $number-color,
|
||||
"bigserial": $number-color,
|
||||
"real": $number-color,
|
||||
"double_precision": $number-color,
|
||||
"oid": $number-color,
|
||||
"xid": $number-color,
|
||||
"money": $number-color,
|
||||
"datetime": $date-color,
|
||||
"date": $date-color,
|
||||
"time": $date-color,
|
||||
"time_with_time_zone": $date-color,
|
||||
"year": $date-color,
|
||||
"timestamp": $date-color,
|
||||
"timestamp_without_time_zone": $date-color,
|
||||
"timestamp_with_time_zone": $date-color,
|
||||
"bit": $bit-color,
|
||||
"bit_varying": $bit-color,
|
||||
"binary": $blob-color,
|
||||
"varbinary": $blob-color,
|
||||
"blob": $blob-color,
|
||||
@@ -44,8 +66,16 @@
|
||||
"mediumblob": $blob-color,
|
||||
"medium_blob": $blob-color,
|
||||
"longblob": $blob-color,
|
||||
"bytea": $blob-color,
|
||||
"enum": $enum-color,
|
||||
"set": $enum-color,
|
||||
"boolean": $enum-color,
|
||||
"interval": $array-color,
|
||||
"array": $array-color,
|
||||
"anyarray": $array-color,
|
||||
"tsvector": $array-color,
|
||||
"tsquery": $array-color,
|
||||
"pg_node_tree": $array-color,
|
||||
"unknown": $unknown-color,
|
||||
)
|
||||
);
|
||||
|
@@ -15,7 +15,8 @@
|
||||
}
|
||||
|
||||
&.key-mul,
|
||||
&.key-INDEX {
|
||||
&.key-INDEX,
|
||||
&.key-KEY {
|
||||
color: palegreen;
|
||||
}
|
||||
|
||||
|
@@ -14,6 +14,7 @@ $number-color: cornflowerblue;
|
||||
$date-color: coral;
|
||||
$bit-color: lightskyblue;
|
||||
$blob-color: darkorchid;
|
||||
$array-color: greenyellow;
|
||||
$enum-color: gold;
|
||||
$unknown-color: gray;
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
import Connection from '@/ipc-api/Connection';
|
||||
import Database from '@/ipc-api/Database';
|
||||
import Schema from '@/ipc-api/Schema';
|
||||
import Users from '@/ipc-api/Users';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
const tabIndex = [];
|
||||
@@ -55,7 +55,7 @@ export default {
|
||||
state.selected_workspace = uid;
|
||||
},
|
||||
ADD_CONNECTED (state, payload) {
|
||||
const { uid, client, dataTypes, indexTypes, structure, version } = payload;
|
||||
const { uid, client, dataTypes, indexTypes, customizations, structure, version } = payload;
|
||||
|
||||
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
|
||||
? {
|
||||
@@ -63,6 +63,7 @@ export default {
|
||||
client,
|
||||
dataTypes,
|
||||
indexTypes,
|
||||
customizations,
|
||||
structure,
|
||||
connected: true,
|
||||
version
|
||||
@@ -253,16 +254,23 @@ export default {
|
||||
else {
|
||||
let dataTypes = [];
|
||||
let indexTypes = [];
|
||||
let customizations = {};
|
||||
|
||||
switch (connection.client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
dataTypes = require('common/data-types/mysql');
|
||||
indexTypes = require('common/index-types/mysql');
|
||||
customizations = require('common/customizations/mysql');
|
||||
break;
|
||||
case 'pg':
|
||||
dataTypes = require('common/data-types/postgresql');
|
||||
indexTypes = require('common/index-types/postgresql');
|
||||
customizations = require('common/customizations/postgresql');
|
||||
break;
|
||||
}
|
||||
|
||||
const { status, response: version } = await Database.getVersion(connection.uid);
|
||||
const { status, response: version } = await Schema.getVersion(connection.uid);
|
||||
|
||||
if (status === 'error')
|
||||
dispatch('notifications/addNotification', { status, message: version }, { root: true });
|
||||
@@ -285,6 +293,7 @@ export default {
|
||||
client: connection.client,
|
||||
dataTypes,
|
||||
indexTypes,
|
||||
customizations,
|
||||
structure: response,
|
||||
version
|
||||
});
|
||||
@@ -300,7 +309,7 @@ export default {
|
||||
},
|
||||
async refreshStructure ({ dispatch, commit, getters }, uid) {
|
||||
try {
|
||||
const { status, response } = await Database.getStructure({ uid, schemas: getters.getLoadedSchemas(uid) });
|
||||
const { status, response } = await Schema.getStructure({ uid, schemas: getters.getLoadedSchemas(uid) });
|
||||
|
||||
if (status === 'error')
|
||||
dispatch('notifications/addNotification', { status, message: response }, { root: true });
|
||||
@@ -313,7 +322,7 @@ export default {
|
||||
},
|
||||
async refreshSchema ({ dispatch, commit }, { uid, schema }) {
|
||||
try {
|
||||
const { status, response } = await Database.getStructure({ uid, schemas: new Set([schema]) });
|
||||
const { status, response } = await Schema.getStructure({ uid, schemas: new Set([schema]) });
|
||||
if (status === 'error')
|
||||
dispatch('notifications/addNotification', { status, message: response }, { root: true });
|
||||
else
|
||||
@@ -325,7 +334,7 @@ export default {
|
||||
},
|
||||
async refreshCollations ({ dispatch, commit }, uid) {
|
||||
try {
|
||||
const { status, response } = await Database.getCollations(uid);
|
||||
const { status, response } = await Schema.getCollations(uid);
|
||||
if (status === 'error')
|
||||
dispatch('notifications/addNotification', { status, message: response }, { root: true });
|
||||
else
|
||||
@@ -337,7 +346,7 @@ export default {
|
||||
},
|
||||
async refreshVariables ({ dispatch, commit }, uid) {
|
||||
try {
|
||||
const { status, response } = await Database.getVariables(uid);
|
||||
const { status, response } = await Schema.getVariables(uid);
|
||||
if (status === 'error')
|
||||
dispatch('notifications/addNotification', { status, message: response }, { root: true });
|
||||
else
|
||||
@@ -349,7 +358,7 @@ export default {
|
||||
},
|
||||
async refreshEngines ({ dispatch, commit }, uid) {
|
||||
try {
|
||||
const { status, response } = await Database.getEngines(uid);
|
||||
const { status, response } = await Schema.getEngines(uid);
|
||||
if (status === 'error')
|
||||
dispatch('notifications/addNotification', { status, message: response }, { root: true });
|
||||
else
|
||||
@@ -421,7 +430,7 @@ export default {
|
||||
if (lastBreadcrumbs.schema === payload.schema && hasLastChildren && !hasChildren) return;
|
||||
|
||||
if (lastBreadcrumbs.schema !== payload.schema)
|
||||
Database.useSchema({ uid: getters.getSelected, schema: payload.schema });
|
||||
Schema.useSchema({ uid: getters.getSelected, schema: payload.schema });
|
||||
|
||||
commit('CHANGE_BREADCRUMBS', { uid: getters.getSelected, breadcrumbs: { ...breadcrumbsObj, ...payload } });
|
||||
lastBreadcrumbs = { ...breadcrumbsObj, ...payload };
|
||||
|
Reference in New Issue
Block a user