mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
Compare commits
84 Commits
Author | SHA1 | Date | |
---|---|---|---|
5c21b67b3d | |||
08d5b1b329 | |||
61a42d51f5 | |||
d96907ca2d | |||
75bbd5f66e | |||
faa07a077c | |||
7ca69e51ae | |||
d868c772b9 | |||
a69bdeb20d | |||
51db2795bb | |||
2d278aa14e | |||
f74bca7bb4 | |||
978a7c5f5b | |||
0110756204 | |||
7df0cf8389 | |||
3aef7e953e | |||
a975df38dd | |||
a0a025e450 | |||
7cc5e7b67f | |||
e2ebb04a90 | |||
e579f37438 | |||
3829b94bf7 | |||
5c8ee66f43 | |||
b77e77f3bb | |||
1d5614278c | |||
8a20befd09 | |||
4133fc452f | |||
690a4541f9 | |||
71910ca5c8 | |||
cce5adbac7 | |||
e6d67fcb0c | |||
e83b80afc9 | |||
8742fa10f0 | |||
ab54eb086f | |||
308f69f12e | |||
2f30bafa33 | |||
|
1716077182 | ||
5562e73e75 | |||
8a7cc2a14f | |||
9ca059d979 | |||
c9ccf786cd | |||
5e9c88a7fd | |||
e0e6483d1f | |||
66227569f4 | |||
faa799c8ea | |||
acc1eeb094 | |||
865dad80df | |||
f502620779 | |||
f61d7eeaf4 | |||
e71c7568c0 | |||
79f033e524 | |||
310cfaa3c2 | |||
6f93e1f9ab | |||
04bdd085a5 | |||
c568acae14 | |||
7a46a119d8 | |||
f1636f1528 | |||
f839d5cd3c | |||
8f4f1d393c | |||
ab17c6d4bf | |||
b4b2f3d0cf | |||
7a766f04e6 | |||
f6b77d1243 | |||
840241c3cc | |||
0f10c9e824 | |||
1e37f2a96f | |||
0d6137195d | |||
d26f168250 | |||
5efcad4d3f | |||
0a872e7023 | |||
0e425a9c6d | |||
434711a360 | |||
854472c7a3 | |||
9a7cd90d5c | |||
026400d242 | |||
4bd8cbbf6c | |||
1382bc9300 | |||
ae103e5477 | |||
6b0b8b19d7 | |||
475397ca34 | |||
7a62131cc7 | |||
c7663be338 | |||
496490b14a | |||
|
672573d8c2 |
@@ -1,4 +1,4 @@
|
||||
name: Build/release
|
||||
name: Build/release [linux]
|
||||
|
||||
on: push
|
||||
|
||||
@@ -8,8 +8,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
# os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
os: [macos-latest, ubuntu-latest]
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
@@ -18,7 +17,7 @@ jobs:
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12
|
||||
node-version: 14
|
||||
|
||||
- name: Build/release Electron app
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
26
.github/workflows/build-mac.yml
vendored
Normal file
26
.github/workflows/build-mac.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Build/release [mac]
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
|
||||
- name: Build/release Electron app
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
26
.github/workflows/build-win.yml
vendored
Normal file
26
.github/workflows/build-win.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Build/release [windows]
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
|
||||
- name: Build/release Electron app
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
127
CHANGELOG.md
127
CHANGELOG.md
@@ -2,6 +2,133 @@
|
||||
|
||||
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.2.0](https://github.com/Fabio286/antares/compare/v0.1.13...v0.2.0) (2021-07-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* contextual option to duplicate tables ([08d5b1b](https://github.com/Fabio286/antares/commit/08d5b1b3299adc5c53419f50892aee3b58e6fe72))
|
||||
* **PostgreSQL:** trigger functions support ([75bbd5f](https://github.com/Fabio286/antares/commit/75bbd5f66e38a971482acff9dde636cf89858c8e))
|
||||
* **PostgreSQL:** triggers creation ([7df0cf8](https://github.com/Fabio286/antares/commit/7df0cf83895ba784e30efa7814433cf329175364))
|
||||
* **UI:** active tab animation ([978a7c5](https://github.com/Fabio286/antares/commit/978a7c5f5b314f2ba4067c86471755fdcc341b3f))
|
||||
* **UI:** context option to copy cell or row value ([d868c77](https://github.com/Fabio286/antares/commit/d868c772b9e7c70e173d1f80ad8de51aa40991ef))
|
||||
* **UI:** resizer border mouse hover animation ([a69bdeb](https://github.com/Fabio286/antares/commit/a69bdeb20db7d67613dace554ae37a0c5e112ec2))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **PostgreSQL:** error opening setting tab for some stored routines ([d96907c](https://github.com/Fabio286/antares/commit/d96907ca2de344114e3edae52843f6258468934b))
|
||||
* **UI:** contextual sub-menu alignment when close to the lower edge of the window ([61a42d5](https://github.com/Fabio286/antares/commit/61a42d51f55ec3010c4ee67bad2f699ff14c9f7c))
|
||||
* fields default not correctly set in table filler ([faa07a0](https://github.com/Fabio286/antares/commit/faa07a077c899af5c71124e0c20869876e57fd9a))
|
||||
* rows loses internal id after export ([2d278aa](https://github.com/Fabio286/antares/commit/2d278aa14e0e5080725196538b7265ec00a7aebb))
|
||||
* **UI:** editor blinking on first connection with a dark theme ([0110756](https://github.com/Fabio286/antares/commit/0110756204b79388535a74017b303e1ba41e8fd8))
|
||||
* unhandled exception in connection test ([a975df3](https://github.com/Fabio286/antares/commit/a975df38dd471ab0ac8645a1021470a4600e6598))
|
||||
* vulnerability in server error toast messages ([3aef7e9](https://github.com/Fabio286/antares/commit/3aef7e953ea82a9105d470cc62c68aacfc97f9d9))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* improved contextual menu appearance ([f74bca7](https://github.com/Fabio286/antares/commit/f74bca7bb4fc6802143446a44663fbf12319bf29))
|
||||
* **UI:** increased application border-radius ([a0a025e](https://github.com/Fabio286/antares/commit/a0a025e4502141e95394ca9717b1ec65df2728c1))
|
||||
|
||||
### [0.1.13](https://github.com/Fabio286/antares/compare/v0.1.12...v0.1.13) (2021-06-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **PostgreSQL:** alter trigger support ([5c8ee66](https://github.com/Fabio286/antares/commit/5c8ee66f432585db7bd72103fa814558bf406bcc))
|
||||
* **UI:** ability to manually insert page number in DATA tabs, closes [#65](https://github.com/Fabio286/antares/issues/65) ([e2ebb04](https://github.com/Fabio286/antares/commit/e2ebb04a9047d25187889644aa625fe675de808b))
|
||||
* **UI:** option to change query editors font size, closes [#77](https://github.com/Fabio286/antares/issues/77) ([e579f37](https://github.com/Fabio286/antares/commit/e579f374381b329422b646b8f7ab5acf298db981))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **MySQL:** pool connections not released after MySQL errors, causing endless load animation ([4133fc4](https://github.com/Fabio286/antares/commit/4133fc452fc2e961eb587590e010f4968675db7e))
|
||||
* **UI:** various fixes in displaying content with small window size ([8a20bef](https://github.com/Fabio286/antares/commit/8a20befd0941cb2e6d5e29552606454bd871b092))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* remove comments from queries before execution ([690a454](https://github.com/Fabio286/antares/commit/690a4541f96eaf831dea07b777a421729173b654))
|
||||
|
||||
### [0.1.12](https://github.com/Fabio286/antares/compare/v0.1.11...v0.1.12) (2021-06-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **PostgreSQL:** trigger rename and delete ([cce5adb](https://github.com/Fabio286/antares/commit/cce5adbac75170186956211b54f4db5be25aba07))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* internal exceptions ([8742fa1](https://github.com/Fabio286/antares/commit/8742fa10f08c92a98325df50be9a7df8e36b0f2c))
|
||||
* page offset not reset when selected table changes ([5562e73](https://github.com/Fabio286/antares/commit/5562e73e75c659051a49d61041d1eee9d66ff6e2))
|
||||
* **MySQL:** view's data tab doesn't work with some views, closes [#71](https://github.com/Fabio286/antares/issues/71) ([9ca059d](https://github.com/Fabio286/antares/commit/9ca059d979161c0132f64db372b4ed7ecc844b2d))
|
||||
* **UI:** unable to browse view's result pages ([8a7cc2a](https://github.com/Fabio286/antares/commit/8a7cc2a14fa289c2f3d9e9eb1a5435ffbbaab006))
|
||||
|
||||
### [0.1.11](https://github.com/Fabio286/antares/compare/v0.1.10...v0.1.11) (2021-06-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **UI:** prevent canc key to trigger delete modal when editing a row ([5e9c88a](https://github.com/Fabio286/antares/commit/5e9c88a7fd656dd69b92ba4384ca81f4e8cdd422))
|
||||
* table row loses internal id after cell update ([6622756](https://github.com/Fabio286/antares/commit/66227569f4c032298e20d44c253d30109ffde0b2))
|
||||
* **MySQL:** missing schema altering tables in some conditions ([faa799c](https://github.com/Fabio286/antares/commit/faa799c8ea2329e7ca1b54c9414a060bc8a2a840))
|
||||
* empty offset in cell update queries ([acc1eeb](https://github.com/Fabio286/antares/commit/acc1eeb0948db35f2c9a4d85d70fb654b41b15e5))
|
||||
|
||||
### [0.1.10](https://github.com/Fabio286/antares/compare/v0.1.9...v0.1.10) (2021-05-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* key shortcuts to change DATA tab page ([f61d7ee](https://github.com/Fabio286/antares/commit/f61d7eeaf42406eb88b3285ac4606794f56e88d3))
|
||||
* option to set DATA tab page size ([e71c756](https://github.com/Fabio286/antares/commit/e71c7568c09bf8ad184753d3d0f14046f1e386d0))
|
||||
* prev/next buttons to browse the results pages of data tab ([79f033e](https://github.com/Fabio286/antares/commit/79f033e5245e0e744c35540d25a30b1eadc4d603))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **MySQL:** wrong schema in view data tab select, closes [#71](https://github.com/Fabio286/antares/issues/71) ([310cfaa](https://github.com/Fabio286/antares/commit/310cfaa3c2324b1159a46be566b6af2555dc9732))
|
||||
* wrong detection of field default expressions in some cases ([04bdd08](https://github.com/Fabio286/antares/commit/04bdd085a56240273133586d23196e52341d5f27))
|
||||
|
||||
### [0.1.9](https://github.com/Fabio286/antares/compare/v0.1.8...v0.1.9) (2021-05-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **MySQL:** can't access tables having UNIQUE KEY, closes [#69](https://github.com/Fabio286/antares/issues/69) ([f1636f1](https://github.com/Fabio286/antares/commit/f1636f1528b5765423535f84027fdc853c58ce80))
|
||||
|
||||
### [0.1.8](https://github.com/Fabio286/antares/compare/v0.1.7...v0.1.8) (2021-05-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* internal id not removed before row update ([0f10c9e](https://github.com/Fabio286/antares/commit/0f10c9e824b679cfebd8a31085236da3d38a7953))
|
||||
* unable to add new ENUM fields ([1e37f2a](https://github.com/Fabio286/antares/commit/1e37f2a96f660a36f2739fea57ca298059e9c31a))
|
||||
* unable to delete table rows ([0d61371](https://github.com/Fabio286/antares/commit/0d6137195da9e0e9afe68f6d3ebcd50e913a888e))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* improved the way how field default value are handled ([7a766f0](https://github.com/Fabio286/antares/commit/7a766f04e668868e8844aac1d7d445bc2da21558))
|
||||
|
||||
### [0.1.7](https://github.com/Fabio286/antares/compare/v0.1.6...v0.1.7) (2021-05-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* row loses internal id after cell update ([854472c](https://github.com/Fabio286/antares/commit/854472c7a3d8442b1eede12f978cad5aa684094e))
|
||||
* **MySQL:** connection loses schema in some conditions ([6b0b8b1](https://github.com/Fabio286/antares/commit/6b0b8b19d7176ef8647d6f401a33315f8732fdf3))
|
||||
* issue with ENUM and SET fields on table filler modal ([475397c](https://github.com/Fabio286/antares/commit/475397ca34c5b7d8925e2d97d32216d5b80d8211))
|
||||
* issue with ENUM and SET length when creating a new field ([7a62131](https://github.com/Fabio286/antares/commit/7a62131cc707aa1c98bb12513de76229472b7c38))
|
||||
* multiple row select on sorted tables not work properly ([c7663be](https://github.com/Fabio286/antares/commit/c7663be338ccb5ff31e241e824593531ad95bd32))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* **core:** increased connection pool size to improve performance ([ae103e5](https://github.com/Fabio286/antares/commit/ae103e5477ad190c541d0f29e277c41de6947063))
|
||||
* **MySQL:** improved connections pool handling ([434711a](https://github.com/Fabio286/antares/commit/434711a360bd7539d2ec3adec92e7ccc96fa828e))
|
||||
|
||||
### [0.1.6](https://github.com/Fabio286/antares/compare/v0.1.5...v0.1.6) (2021-05-08)
|
||||
|
||||
|
||||
|
15
README.md
15
README.md
@@ -4,7 +4,7 @@
|
||||
|
||||
# Antares SQL Client
|
||||
|
||||
   [](https://snapcraft.io/antares) [](https://snapcraft.io/antares) [](https://www.treedom.net/en/user/fabio-di-stasio/event/antares-for-the-planet)
|
||||
 [](https://actions-badge.atrox.dev/fabio286/antares/goto)   [](https://snapcraft.io/antares) [](https://snapcraft.io/antares) [](https://twitter.com/AntaresSQL) [](https://www.treedom.net/en/user/fabio-di-stasio/event/antares-for-the-planet)
|
||||
|
||||
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.
|
||||
@@ -14,7 +14,7 @@ Many of its current features are enough to have a pleasant user experience with
|
||||
I'm actively working on it, hoping to provide cool features and fixes as soon as possible.
|
||||
|
||||
🔗 If you are curious to try Antares you can download and install the [latest release](https://github.com/Fabio286/antares/releases/latest).
|
||||
👁 To stay tuned for new releases watch this repo on **Release only** channel.
|
||||
👁 To stay tuned for new releases [follow Antares SQL](https://twitter.com/AntaresSQL) on Twitter.
|
||||
🌟 Don't forget to **leave a star** if you appreciate this project.
|
||||
|
||||
## Philosophy
|
||||
@@ -25,12 +25,14 @@ A modern application created with minimalism and semplicity in mind, with featur
|
||||
|
||||
## Download
|
||||
|
||||
[](https://snapcraft.io/antares) [](https://www.microsoft.com/p/antares-sql-client/9nhtb9sq51r1?cid=storebadge&ocid=badge&rtc=1&activetab=pivot:overviewtab)
|
||||
[](https://snapcraft.io/antares) [](https://aur.archlinux.org/packages/antares-sql/) [](https://www.microsoft.com/p/antares-sql-client/9nhtb9sq51r1?cid=storebadge&ocid=badge&rtc=1&activetab=pivot:overviewtab)
|
||||
🚀 **[Other Downloads](https://github.com/Fabio286/antares/releases/latest)**
|
||||
|
||||
## How to contribute
|
||||
|
||||
- [Translate Antares](https://github.com/Fabio286/antares/wiki/Translate-Antares)
|
||||
- 🌍 [Translate Antares](https://github.com/Fabio286/antares/wiki/Translate-Antares)
|
||||
- 📖 [Contributors Guide](https://github.com/Fabio286/antares/wiki/Contributors-Guide)
|
||||
- 🚧 [Project Board](https://github.com/users/Fabio286/projects/1)
|
||||
|
||||
## Current main features
|
||||
|
||||
@@ -83,7 +85,7 @@ This is a roadmap with major features will come in near future.
|
||||
#### • ARM
|
||||
|
||||
- [ ] Windows
|
||||
- [ ] Linux
|
||||
- [x] Linux
|
||||
- [ ] MacOS
|
||||
|
||||
## Translations
|
||||
@@ -92,7 +94,8 @@ This is a roadmap with major features will come in near future.
|
||||
**Arabic Translation** / [Mohd-PH](https://github.com/Mohd-PH) [[#29](https://github.com/Fabio286/antares/pull/29)]
|
||||
**Spanish Translation** / [hongkfui](https://github.com/hongkfui) [[#32](https://github.com/Fabio286/antares/pull/32)]
|
||||
**French Translation** / [MrAnyx](https://github.com/MrAnyx) [[#44](https://github.com/Fabio286/antares/pull/44)]
|
||||
**Portugues (Brasil)** / [Daniel Eduardo](https://github.com/daeleduardo) [[#54](https://github.com/Fabio286/antares/pull/54)]
|
||||
**Portugues (Brasil)** / [Daniel Eduardo](https://github.com/daeleduardo) [[#54](https://github.com/Fabio286/antares/pull/54)]
|
||||
**Deutsch (Deutschland)** / [Christian Ratz](https://github.com/digitalgopnik) [[#74](https://github.com/Fabio286/antares/pull/74)]
|
||||
|
||||
## Reviews
|
||||
|
||||
|
77
docs/aur-badge.svg
Normal file
77
docs/aur-badge.svg
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Gravit.io --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
style="isolation:isolate" viewBox="0 0 182 56" width="182px" height="56px">
|
||||
<defs>
|
||||
<clipPath id="_clipPath_tR1uglJ1Zei76xP861DY1TsjAiQWS9qF">
|
||||
<rect width="182" height="56" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path="url(#_clipPath_tR1uglJ1Zei76xP861DY1TsjAiQWS9qF)">
|
||||
<path
|
||||
d="M 2.5 0.5 L 180.5 0.5 C 181.604 0.5 182.5 1.396 182.5 2.5 L 182.5 54.5 C 182.5 55.604 181.604 56.5 180.5 56.5 L 2.5 56.5 C 1.396 56.5 0.5 55.604 0.5 54.5 L 0.5 2.5 C 0.5 1.396 1.396 0.5 2.5 0.5 Z"
|
||||
style="stroke:none;fill:#252525;stroke-miterlimit:10;" />
|
||||
<path
|
||||
d="M 2.5 0.5 L 180.5 0.5 C 181.604 0.5 182.5 1.396 182.5 2.5 L 182.5 54.5 C 182.5 55.604 181.604 56.5 180.5 56.5 L 2.5 56.5 C 1.396 56.5 0.5 55.604 0.5 54.5 L 0.5 2.5 C 0.5 1.396 1.396 0.5 2.5 0.5 Z"
|
||||
style="fill:none;stroke:#CDCDCD;stroke-width:1;stroke-miterlimit:2;" />
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
d=" M 60.898 13.777 C 58.555 13.774 56.61 14.254 55.858 14.516 L 55.083 18.697 C 55.081 18.712 58.937 17.669 60.635 17.73 C 63.447 17.831 63.706 18.805 63.656 20.119 C 63.704 20.196 62.931 18.931 60.498 18.889 C 57.43 18.836 53.098 19.976 53.104 24.608 C 53.022 29.818 56.997 31.351 59.704 31.379 C 62.138 31.335 63.279 30.458 63.904 29.988 C 64.726 29.129 65.665 28.265 66.561 27.229 C 65.714 28.77 64.978 29.835 64.213 30.651 L 64.213 31.339 L 67.913 30.716 L 67.938 20.66 C 67.901 19.237 68.754 13.791 60.898 13.777 Z M 60.367 22.533 C 61.9 22.554 63.659 23.31 63.662 25.129 C 63.669 26.784 61.589 27.674 60.235 27.66 C 58.881 27.646 57.085 26.596 57.077 24.982 C 57.103 23.54 58.771 22.496 60.367 22.533 Z "
|
||||
fill-rule="evenodd" fill="rgb(255,255,255)" />
|
||||
<path
|
||||
d=" M 70.378 14.707 L 70.352 31.36 L 74.662 30.529 L 74.67 21.087 C 74.671 19.681 76.679 18.039 79.198 18.065 C 79.733 17.097 80.738 14.625 80.983 14.062 C 75.354 14.049 75.282 15.68 74.303 16.483 C 74.293 14.952 74.3 14.033 74.3 14.033 L 70.378 14.707 L 70.378 14.707 Z "
|
||||
fill-rule="evenodd" fill="rgb(255,255,255)" />
|
||||
<path
|
||||
d=" M 94.632 16.893 C 94.591 16.873 92.385 14.312 87.949 14.292 C 83.795 14.223 79.135 15.834 79.061 22.8 C 79.097 28.925 83.537 31.318 87.973 31.365 C 92.72 31.414 94.609 28.396 94.722 28.322 C 94.156 27.83 92.034 25.728 92.034 25.728 C 92.034 25.728 90.709 27.615 88.138 27.639 C 85.566 27.664 83.331 25.651 83.299 22.844 C 83.266 20.036 85.354 18.515 88.157 18.392 C 90.584 18.392 91.984 19.959 91.984 19.959 L 94.632 16.893 L 94.632 16.893 Z "
|
||||
fill-rule="evenodd" fill="rgb(255,255,255)" />
|
||||
<path
|
||||
d=" M 100.065 8.879 L 95.996 9.835 L 96.026 31.526 L 100.034 30.802 L 100.08 20.595 C 100.089 19.524 101.628 17.88 104.2 17.933 C 106.658 17.958 107.207 19.571 107.201 19.775 L 107.272 31.592 L 111.224 30.894 L 111.239 18.363 C 111.265 17.157 108.598 14.61 104.311 14.592 C 102.273 14.596 101.145 15.057 100.571 15.397 C 99.588 16.156 98.466 16.883 97.362 17.811 C 98.382 16.501 99.239 15.595 100.075 14.921 L 100.065 8.879 L 100.065 8.879 Z "
|
||||
fill-rule="evenodd" fill="rgb(255,255,255)" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d=" M 114.673 9.441 L 116.508 8.982 L 116.595 30.85 L 114.73 31.168 L 114.673 9.441 L 114.673 9.441 Z "
|
||||
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||
<path
|
||||
d=" M 119.663 15.968 L 121.271 15.252 L 121.285 30.932 L 119.731 31.253 L 119.663 15.968 L 119.663 15.968 Z M 119.28 10.314 L 120.577 9.255 L 121.655 10.454 L 120.357 11.54 L 119.28 10.314 L 119.28 10.314 Z "
|
||||
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||
<path
|
||||
d=" M 124.296 15.682 L 126.131 15.308 L 126.14 18.586 C 126.14 18.727 127.148 14.924 132.008 15.009 C 136.727 15.035 137.499 18.688 137.473 19.507 L 137.531 31.034 L 135.922 31.384 L 135.913 19.998 C 135.932 19.665 135.178 16.853 131.843 16.843 C 128.509 16.833 126.199 19.265 126.203 20.818 L 126.229 30.848 L 124.365 31.335 L 124.296 15.682 L 124.296 15.682 Z "
|
||||
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||
<path
|
||||
d=" M 153.547 31.117 L 151.711 31.492 L 151.703 28.214 C 151.703 28.073 150.694 31.876 145.835 31.791 C 141.116 31.765 140.344 28.112 140.37 27.293 L 140.311 15.765 L 142.261 15.372 L 142.292 26.758 C 142.292 27.069 142.665 29.947 145.999 29.957 C 149.334 29.967 151.669 27.949 151.686 24.911 L 151.662 15.928 L 153.477 15.464 L 153.547 31.117 L 153.547 31.117 Z "
|
||||
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||
<path
|
||||
d=" M 157.144 15.553 L 155.857 16.56 L 160.792 23.018 L 155.529 30.478 L 156.894 31.492 L 161.841 24.563 L 166.948 31.656 L 168.211 30.649 L 162.738 23.065 L 167.104 16.933 L 165.762 15.797 L 161.785 21.472 L 157.144 15.553 L 157.144 15.553 Z "
|
||||
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||
</g>
|
||||
<path
|
||||
d=" M 33.112 3.879 C 30.965 9.143 29.67 12.587 27.279 17.695 C 28.745 19.248 30.544 21.058 33.466 23.101 C 30.325 21.809 28.182 20.511 26.581 19.164 C 23.521 25.549 18.728 34.643 9 52.121 C 16.645 47.707 22.572 44.986 28.095 43.948 C 27.858 42.928 27.723 41.824 27.733 40.673 L 27.742 40.428 C 27.863 35.53 30.411 31.763 33.43 32.019 C 36.448 32.274 38.794 36.455 38.673 41.353 C 38.65 42.275 38.546 43.162 38.364 43.984 C 43.828 45.053 49.691 47.767 57.233 52.121 C 55.746 49.383 54.419 46.915 53.151 44.565 C 51.154 43.017 49.072 41.003 44.823 38.822 C 47.743 39.581 49.834 40.456 51.464 41.435 C 38.575 17.439 37.532 14.251 33.112 3.879 Z "
|
||||
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||
<g>
|
||||
<path
|
||||
d=" M 170.614 30.156 L 170.614 28.802 L 170.109 28.802 L 170.109 28.621 L 171.325 28.621 L 171.325 28.802 L 170.817 28.802 L 170.817 30.156 L 170.614 30.156 Z "
|
||||
fill="rgb(23,147,209)" />
|
||||
<path
|
||||
d=" M 171.536 30.156 L 171.536 28.621 L 171.842 28.621 L 172.205 29.708 C 172.238 29.809 172.263 29.884 172.278 29.935 C 172.295 29.879 172.323 29.797 172.36 29.689 L 172.727 28.621 L 173 28.621 L 173 30.156 L 172.804 30.156 L 172.804 28.871 L 172.358 30.156 L 172.175 30.156 L 171.732 28.849 L 171.732 30.156 L 171.536 30.156 Z "
|
||||
fill="rgb(23,147,209)" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d=" M 57.471 47.815 L 57.471 46.493 L 56.977 46.493 L 56.977 46.316 L 58.166 46.316 L 58.166 46.493 L 57.67 46.493 L 57.67 47.815 L 57.471 47.815 Z "
|
||||
fill="rgb(23,147,209)" />
|
||||
<path
|
||||
d=" M 58.372 47.815 L 58.372 46.316 L 58.671 46.316 L 59.026 47.377 C 59.059 47.476 59.083 47.55 59.098 47.599 C 59.115 47.545 59.141 47.465 59.177 47.359 L 59.536 46.316 L 59.803 46.316 L 59.803 47.815 L 59.612 47.815 L 59.612 46.56 L 59.176 47.815 L 58.997 47.815 L 58.564 46.539 L 58.564 47.815 L 58.372 47.815"
|
||||
fill="rgb(23,147,209)" />
|
||||
</g>
|
||||
</g>
|
||||
<g clip-path="url(#_clipPath_NvFIpNfWUS6M4fZAtfyVzggsKR3URDoi)"><text transform="matrix(1,0,0,1,87.023,43.899)"
|
||||
style="font-family:'Open Sans';font-weight:700;font-size:11px;font-style:normal;fill:#ffffff;stroke:none;">user
|
||||
repository</text></g>
|
||||
<defs>
|
||||
<clipPath id="_clipPath_NvFIpNfWUS6M4fZAtfyVzggsKR3URDoi">
|
||||
<rect x="0" y="0" width="86" height="14.98" transform="matrix(1,0,0,1,87,32.142)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.5 KiB |
90
package.json
90
package.json
@@ -1,20 +1,22 @@
|
||||
{
|
||||
"name": "antares",
|
||||
"productName": "Antares",
|
||||
"version": "0.1.6",
|
||||
"version": "0.2.0",
|
||||
"description": "A cross-platform easy to use SQL client.",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/Fabio286/antares.git",
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development electron-webpack dev",
|
||||
"compile": "electron-webpack",
|
||||
"build": "cross-env NODE_ENV=production npm run compile && electron-builder",
|
||||
"build:appx": "npm run build -- --win appx",
|
||||
"build": "cross-env NODE_ENV=production npm run compile",
|
||||
"build:local": "npm run build && electron-builder",
|
||||
"build:appx": "npm run build:local -- --win appx",
|
||||
"release": "standard-version",
|
||||
"release:pre": "npm run release -- --prerelease alpha",
|
||||
"test": "npm run lint",
|
||||
"lint": "eslint . --ext .js,.vue && stylelint \"./src/**/*.{css,scss,sass,vue}\"",
|
||||
"lint:fix": "eslint . --ext .js,.vue --fix && stylelint \"./src/**/*.{css,scss,sass,vue}\" --fix"
|
||||
"lint:fix": "eslint . --ext .js,.vue --fix && stylelint \"./src/**/*.{css,scss,sass,vue}\" --fix",
|
||||
"postinstall": "electron-builder install-app-deps"
|
||||
},
|
||||
"author": "Fabio Di Stasio <fabio286@gmail.com>",
|
||||
"build": {
|
||||
@@ -26,24 +28,28 @@
|
||||
"portable"
|
||||
]
|
||||
},
|
||||
"dmg": {
|
||||
"contents": [
|
||||
{
|
||||
"x": 130,
|
||||
"y": 220
|
||||
},
|
||||
{
|
||||
"x": 410,
|
||||
"y": 220,
|
||||
"type": "link",
|
||||
"path": "/Applications"
|
||||
}
|
||||
]
|
||||
"mac": {
|
||||
"target": {
|
||||
"target": "default",
|
||||
"arch": [
|
||||
"x64"
|
||||
]
|
||||
}
|
||||
},
|
||||
"linux": {
|
||||
"target": [
|
||||
"deb",
|
||||
"AppImage"
|
||||
{
|
||||
"target": "deb",
|
||||
"arch": "x64"
|
||||
},
|
||||
{
|
||||
"target": "AppImage",
|
||||
"arch": [
|
||||
"x64",
|
||||
"armv7l",
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"category": "Development"
|
||||
},
|
||||
@@ -59,6 +65,20 @@
|
||||
"identityName": "62514FabioDiStasio.AntaresSQLClient",
|
||||
"publisher": "CN=1A2729ED-865C-41D2-9038-39AE2A63AA52",
|
||||
"applicationId": "FabioDiStasio.AntaresSQLClient"
|
||||
},
|
||||
"dmg": {
|
||||
"contents": [
|
||||
{
|
||||
"x": 130,
|
||||
"y": 220
|
||||
},
|
||||
{
|
||||
"x": 410,
|
||||
"y": 220,
|
||||
"type": "link",
|
||||
"path": "/Applications"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"electronWebpack": {
|
||||
@@ -67,14 +87,14 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/remote": "^1.1.0",
|
||||
"@electron/remote": "^1.2.0",
|
||||
"@mdi/font": "^5.9.55",
|
||||
"ace-builds": "^1.4.12",
|
||||
"electron-log": "^4.3.5",
|
||||
"electron-store": "^8.0.0",
|
||||
"electron-updater": "^4.3.5",
|
||||
"faker": "^5.3.1",
|
||||
"marked": "^2.0.2",
|
||||
"electron-updater": "^4.3.9",
|
||||
"faker": "^5.5.3",
|
||||
"marked": "^2.1.1",
|
||||
"moment": "^2.29.1",
|
||||
"mysql2": "^2.2.5",
|
||||
"pg": "^8.5.1",
|
||||
@@ -85,30 +105,30 @@
|
||||
"v-mask": "^2.2.4",
|
||||
"vue-i18n": "^8.24.4",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"vuex": "^3.6.0"
|
||||
"vuex": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.13.14",
|
||||
"@babel/eslint-parser": "^7.14.5",
|
||||
"cross-env": "^7.0.2",
|
||||
"electron": "^12.0.7",
|
||||
"electron-builder": "^22.9.1",
|
||||
"electron": "^13.1.2",
|
||||
"electron-builder": "^22.11.7",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"electron-webpack": "^2.8.2",
|
||||
"electron-webpack-vue": "^2.4.0",
|
||||
"eslint": "^7.26.0",
|
||||
"eslint-config-standard": "^16.0.2",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint": "^7.29.0",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
"eslint-plugin-import": "^2.23.4",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-vue": "^7.9.0",
|
||||
"node-sass": "^5.0.0",
|
||||
"sass-loader": "^10.1.1",
|
||||
"eslint-plugin-vue": "^7.11.1",
|
||||
"sass": "^1.35.1",
|
||||
"sass-loader": "^10.2.0",
|
||||
"standard-version": "^9.3.0",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
"stylelint-scss": "^3.19.0",
|
||||
"vue": "^2.6.12",
|
||||
"vue-template-compiler": "^2.6.12",
|
||||
"vue": "^2.6.14",
|
||||
"vue-template-compiler": "^2.6.14",
|
||||
"webpack": "^4.46.0"
|
||||
}
|
||||
}
|
||||
|
@@ -62,8 +62,14 @@ module.exports = {
|
||||
functionSql: false,
|
||||
functionContext: false,
|
||||
functionLanguage: false,
|
||||
triggerMiltipleEvents: false,
|
||||
triggerSql: false,
|
||||
triggerStatementInCreation: false,
|
||||
triggerMultipleEvents: false,
|
||||
triggerTableInName: false,
|
||||
triggerUpdateColumns: false,
|
||||
triggerOnlyRename: false,
|
||||
triggerFunctionSql: false,
|
||||
triggerFunctionlanguages: false,
|
||||
parametersLength: false,
|
||||
languages: false
|
||||
};
|
||||
|
@@ -51,6 +51,7 @@ module.exports = {
|
||||
procedureDataAccess: true,
|
||||
procedureSql: 'BEGIN\r\n\r\nEND',
|
||||
procedureContext: true,
|
||||
triggerSql: 'BEGIN\r\n\r\nEND',
|
||||
functionDeterministic: true,
|
||||
functionDataAccess: true,
|
||||
functionSql: 'BEGIN\r\n\r\nEND',
|
||||
|
@@ -13,30 +13,40 @@ module.exports = {
|
||||
// Structure
|
||||
tables: true,
|
||||
views: true,
|
||||
triggers: false,
|
||||
triggers: true,
|
||||
triggerFunctions: true,
|
||||
routines: true,
|
||||
functions: true,
|
||||
// Settings
|
||||
tableAdd: true,
|
||||
viewAdd: true,
|
||||
triggerAdd: false,
|
||||
triggerAdd: true,
|
||||
triggerFunctionAdd: true,
|
||||
routineAdd: true,
|
||||
functionAdd: true,
|
||||
databaseEdit: false,
|
||||
tableSettings: true,
|
||||
viewSettings: true,
|
||||
triggerSettings: false,
|
||||
triggerSettings: true,
|
||||
triggerFunctionSettings: true,
|
||||
routineSettings: true,
|
||||
functionSettings: true,
|
||||
indexes: true,
|
||||
foreigns: true,
|
||||
nullable: true,
|
||||
tableArray: true,
|
||||
procedureSql: '$BODY$\r\n\r\n$BODY$',
|
||||
procedureSql: '$procedure$\r\n\r\n$procedure$',
|
||||
procedureContext: true,
|
||||
procedureLanguage: true,
|
||||
functionSql: '$BODY$\r\n\r\n$BODY$',
|
||||
functionSql: '$function$\r\n\r\n$function$',
|
||||
triggerFunctionSql: '$function$\r\nBEGIN\r\n\r\nEND\r\n$function$',
|
||||
triggerFunctionlanguages: ['plpgsql'],
|
||||
functionContext: true,
|
||||
functionLanguage: true,
|
||||
triggerSql: 'EXECUTE PROCEDURE ',
|
||||
triggerStatementInCreation: true,
|
||||
triggerMultipleEvents: true,
|
||||
triggerTableInName: true,
|
||||
triggerOnlyRename: false,
|
||||
languages: ['sql', 'plpgsql', 'c', 'internal']
|
||||
};
|
||||
|
@@ -1,8 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
import { app, BrowserWindow, nativeImage } from 'electron';
|
||||
import { app, BrowserWindow, /* session, */ nativeImage } from 'electron';
|
||||
import * as path from 'path';
|
||||
import { format as formatUrl } from 'url';
|
||||
import Store from 'electron-store';
|
||||
|
||||
import ipcHandlers from './ipc-handlers';
|
||||
@@ -39,21 +38,18 @@ async function createMainWindow () {
|
||||
});
|
||||
|
||||
try {
|
||||
if (isDevelopment) {
|
||||
if (isDevelopment) { //
|
||||
await window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`);
|
||||
|
||||
const { default: installExtension, VUEJS_DEVTOOLS } = require('electron-devtools-installer');
|
||||
// const { default: installExtension, VUEJS3_DEVTOOLS } = require('electron-devtools-installer');
|
||||
|
||||
const toolName = await installExtension(VUEJS_DEVTOOLS);
|
||||
console.log(toolName, 'installed');
|
||||
}
|
||||
else {
|
||||
await window.loadURL(formatUrl({
|
||||
pathname: path.join(__dirname, 'index.html'),
|
||||
protocol: 'file',
|
||||
slashes: true
|
||||
}));
|
||||
// const oldDevToolsID = session.defaultSession.getAllExtensions().find(ext => ext.name === 'Vue.js devtools').id;
|
||||
// session.defaultSession.removeExtension(oldDevToolsID);
|
||||
// const toolName = await installExtension(VUEJS3_DEVTOOLS);
|
||||
// console.log(toolName, 'installed');
|
||||
}
|
||||
else
|
||||
await window.loadURL(new URL(`file:///${path.join(__dirname, 'index.html')}`).href);
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
|
@@ -29,9 +29,8 @@ export default connections => {
|
||||
params
|
||||
});
|
||||
|
||||
await connection.connect();
|
||||
|
||||
try {
|
||||
await connection.connect();
|
||||
await connection.select('1+1').run();
|
||||
connection.destroy();
|
||||
|
||||
@@ -71,7 +70,7 @@ export default connections => {
|
||||
const connection = ClientsFactory.getConnection({
|
||||
client: conn.client,
|
||||
params,
|
||||
poolSize: 1
|
||||
poolSize: 5
|
||||
});
|
||||
|
||||
await connection.connect();
|
||||
|
@@ -31,6 +31,16 @@ export default (connections) => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('alter-trigger-function', async (event, params) => {
|
||||
try {
|
||||
await connections[params.uid].alterTriggerFunction(params);
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('create-function', async (event, params) => {
|
||||
try {
|
||||
await connections[params.uid].createFunction(params);
|
||||
@@ -40,4 +50,14 @@ export default (connections) => {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('create-trigger-function', async (event, params) => {
|
||||
try {
|
||||
await connections[params.uid].createTriggerFunction(params);
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@@ -128,7 +128,7 @@ export default connections => {
|
||||
if (!query) return;
|
||||
|
||||
try {
|
||||
const result = await connections[uid].raw(query, { nest: true, details: true });
|
||||
const result = await connections[uid].raw(query, { nest: true, details: true, schema });
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
|
@@ -16,13 +16,15 @@ export default (connections) => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-table-data', async (event, { uid, schema, table, sortParams }) => {
|
||||
ipcMain.handle('get-table-data', async (event, { uid, schema, table, limit, page, sortParams }) => {
|
||||
try {
|
||||
const offset = (page - 1) * limit;
|
||||
const query = connections[uid]
|
||||
.select('*')
|
||||
.schema(schema)
|
||||
.from(table)
|
||||
.limit(1000);
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
|
||||
if (sortParams && sortParams.field && sortParams.dir)
|
||||
query.orderBy({ [sortParams.field]: sortParams.dir.toUpperCase() });
|
||||
@@ -59,6 +61,8 @@ export default (connections) => {
|
||||
});
|
||||
|
||||
ipcMain.handle('update-table-cell', async (event, params) => {
|
||||
delete params.row._id;
|
||||
|
||||
try { // TODO: move to client classes
|
||||
let escapedParam;
|
||||
let reload = false;
|
||||
@@ -420,6 +424,16 @@ export default (connections) => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('duplicate-table', async (event, params) => {
|
||||
try {
|
||||
await connections[params.uid].duplicateTable(params);
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('truncate-table', async (event, params) => {
|
||||
try {
|
||||
await connections[params.uid].truncateTable(params);
|
||||
|
@@ -26,6 +26,7 @@ export class AntaresCore {
|
||||
groupBy: [],
|
||||
orderBy: [],
|
||||
limit: [],
|
||||
offset: [],
|
||||
join: [],
|
||||
update: [],
|
||||
insert: [],
|
||||
@@ -109,6 +110,11 @@ export class AntaresCore {
|
||||
return this;
|
||||
}
|
||||
|
||||
offset (...args) {
|
||||
this._query.offset = args;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String | Array} args field = value
|
||||
* @returns
|
||||
@@ -134,7 +140,7 @@ export class AntaresCore {
|
||||
* @returns {Promise}
|
||||
* @memberof AntaresCore
|
||||
*/
|
||||
async run (args) {
|
||||
run (args) {
|
||||
const rawQuery = this.getSQL();
|
||||
this._resetQuery();
|
||||
return this.raw(rawQuery, args);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
'use strict';
|
||||
import mysql from 'mysql2';
|
||||
import mysql from 'mysql2/promise';
|
||||
import { AntaresCore } from '../AntaresCore';
|
||||
import dataTypes from 'common/data-types/mysql';
|
||||
|
||||
@@ -102,8 +102,10 @@ export class MySQLClient extends AntaresCore {
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async connect () {
|
||||
delete this._params.application_name;
|
||||
|
||||
if (!this._poolSize)
|
||||
this._connection = mysql.createConnection(this._params);
|
||||
this._connection = await mysql.createConnection(this._params);
|
||||
else {
|
||||
this._connection = mysql.createPool({
|
||||
...this._params,
|
||||
@@ -303,10 +305,64 @@ export class MySQLClient extends AntaresCore {
|
||||
.select('*')
|
||||
.schema('information_schema')
|
||||
.from('COLUMNS')
|
||||
.where({ TABLE_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'` })
|
||||
.where({ TABLE_SCHEMA: `= '${this._schema || schema}'`, TABLE_NAME: `= '${table}'` })
|
||||
.orderBy({ ORDINAL_POSITION: 'ASC' })
|
||||
.run();
|
||||
|
||||
const { rows: fields } = await this.raw(`SHOW CREATE TABLE \`${this._schema || schema}\`.\`${table}\``);
|
||||
|
||||
const remappedFields = fields.map(row => {
|
||||
if (!row['Create Table']) return false;
|
||||
|
||||
let n = 0;
|
||||
return row['Create Table']
|
||||
.split('')
|
||||
.reduce((acc, curr) => {
|
||||
if (curr === ')') n--;
|
||||
if (n !== 0) acc += curr;
|
||||
if (curr === '(') n++;
|
||||
return acc;
|
||||
}, '')
|
||||
.replaceAll('\n', '')
|
||||
.split(',')
|
||||
.map(f => {
|
||||
try {
|
||||
const fieldArr = f.trim().split(' ');
|
||||
const nameAndType = fieldArr.slice(0, 2);
|
||||
if (nameAndType[0].charAt(0) !== '`') return false;
|
||||
|
||||
const details = fieldArr.slice(2).join(' ');
|
||||
let defaultValue = null;
|
||||
if (details.includes('DEFAULT')) {
|
||||
defaultValue = details.match(/(?<=DEFAULT ).*?$/gs)[0].split(' COMMENT')[0];
|
||||
const defaultValueArr = defaultValue.split('');
|
||||
if (defaultValueArr[0] === '\'') {
|
||||
defaultValueArr.shift();
|
||||
defaultValueArr.pop();
|
||||
defaultValue = defaultValueArr.join('');
|
||||
}
|
||||
}
|
||||
|
||||
const typeAndLength = nameAndType[1].replace(')', '').split('(');
|
||||
|
||||
return {
|
||||
name: nameAndType[0].replaceAll('`', ''),
|
||||
type: typeAndLength[0].toUpperCase(),
|
||||
length: typeAndLength[1] ? typeAndLength[1] : null,
|
||||
default: defaultValue
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
.reduce((acc, curr) => {
|
||||
acc[curr.name] = curr;
|
||||
return acc;
|
||||
}, {});
|
||||
})[0];
|
||||
|
||||
return rows.map(field => {
|
||||
let numLength = field.COLUMN_TYPE.match(/int\(([^)]+)\)/);
|
||||
numLength = numLength ? +numLength.pop() : null;
|
||||
@@ -317,7 +373,7 @@ export class MySQLClient extends AntaresCore {
|
||||
return {
|
||||
name: field.COLUMN_NAME,
|
||||
key: field.COLUMN_KEY.toLowerCase(),
|
||||
type: field.DATA_TYPE.toUpperCase(),
|
||||
type: remappedFields ? remappedFields[field.COLUMN_NAME].type : field.DATA_TYPE,
|
||||
schema: field.TABLE_SCHEMA,
|
||||
table: field.TABLE_NAME,
|
||||
numPrecision: field.NUMERIC_PRECISION,
|
||||
@@ -329,7 +385,7 @@ export class MySQLClient extends AntaresCore {
|
||||
unsigned: field.COLUMN_TYPE.includes('unsigned'),
|
||||
zerofill: field.COLUMN_TYPE.includes('zerofill'),
|
||||
order: field.ORDINAL_POSITION,
|
||||
default: field.COLUMN_DEFAULT,
|
||||
default: remappedFields ? remappedFields[field.COLUMN_NAME].default : field.COLUMN_DEFAULT,
|
||||
charset: field.CHARACTER_SET_NAME,
|
||||
collation: field.COLLATION_NAME,
|
||||
autoIncrement: field.EXTRA.includes('auto_increment'),
|
||||
@@ -488,7 +544,7 @@ export class MySQLClient extends AntaresCore {
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropView (params) {
|
||||
const sql = `DROP VIEW \`${params.view}\``;
|
||||
const sql = `DROP VIEW \`${this._schema}\`.\`${params.view}\``;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
@@ -500,10 +556,10 @@ export class MySQLClient extends AntaresCore {
|
||||
*/
|
||||
async alterView (params) {
|
||||
const { view } = params;
|
||||
let sql = `ALTER ALGORITHM = ${view.algorithm}${view.definer ? ` DEFINER=${view.definer}` : ''} SQL SECURITY ${view.security} VIEW \`${view.oldName}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`;
|
||||
let sql = `ALTER ALGORITHM = ${view.algorithm}${view.definer ? ` DEFINER=${view.definer}` : ''} SQL SECURITY ${view.security} VIEW \`${this._schema}\`.\`${view.oldName}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`;
|
||||
|
||||
if (view.name !== view.oldName)
|
||||
sql += `; RENAME TABLE \`${view.oldName}\` TO \`${view.name}\``;
|
||||
sql += `; RENAME TABLE \`${this._schema}\`.\`${view.oldName}\` TO \`${this._schema}\`.\`${view.name}\``;
|
||||
|
||||
return await this.raw(sql);
|
||||
}
|
||||
@@ -515,7 +571,7 @@ export class MySQLClient extends AntaresCore {
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async createView (view) {
|
||||
const sql = `CREATE ALGORITHM = ${view.algorithm} ${view.definer ? `DEFINER=${view.definer} ` : ''}SQL SECURITY ${view.security} VIEW \`${view.name}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`;
|
||||
const sql = `CREATE ALGORITHM = ${view.algorithm} ${view.definer ? `DEFINER=${view.definer} ` : ''}SQL SECURITY ${view.security} VIEW \`${this._schema}\`.\`${view.name}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
@@ -535,8 +591,8 @@ export class MySQLClient extends AntaresCore {
|
||||
sql: row['SQL Original Statement'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
||||
name: row.Trigger,
|
||||
table: row['SQL Original Statement'].match(/(?<=ON `).*?(?=`)/gs)[0],
|
||||
event1: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0],
|
||||
event2: row['SQL Original Statement'].match(/(INSERT|UPDATE|DELETE)/gs)[0]
|
||||
activation: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0],
|
||||
event: row['SQL Original Statement'].match(/(INSERT|UPDATE|DELETE)/gs)[0]
|
||||
};
|
||||
})[0];
|
||||
}
|
||||
@@ -548,7 +604,7 @@ export class MySQLClient extends AntaresCore {
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropTrigger (params) {
|
||||
const sql = `DROP TRIGGER \`${params.trigger}\``;
|
||||
const sql = `DROP TRIGGER \`${this._schema}\`.\`${params.trigger}\``;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
@@ -581,7 +637,7 @@ export class MySQLClient extends AntaresCore {
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async createTrigger (trigger) {
|
||||
const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${trigger.name}\` ${trigger.event1} ${trigger.event2} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`;
|
||||
const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${this._schema}\`.\`${trigger.name}\` ${trigger.activation} ${trigger.event} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`;
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
||||
@@ -655,7 +711,7 @@ export class MySQLClient extends AntaresCore {
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropRoutine (params) {
|
||||
const sql = `DROP PROCEDURE \`${params.routine}\``;
|
||||
const sql = `DROP PROCEDURE \`${this._schema}\`.\`${params.routine}\``;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
@@ -782,7 +838,7 @@ export class MySQLClient extends AntaresCore {
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropFunction (params) {
|
||||
const sql = `DROP FUNCTION \`${params.func}\``;
|
||||
const sql = `DROP FUNCTION \`${this._schema}\`.\`${params.func}\``;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
@@ -822,7 +878,7 @@ export class MySQLClient extends AntaresCore {
|
||||
|
||||
const body = func.returns ? func.sql : 'BEGIN\n RETURN 0;\nEND';
|
||||
|
||||
const sql = `CREATE ${func.definer ? `DEFINER=${func.definer} ` : ''}FUNCTION \`${func.name}\`(${parameters}) RETURNS ${func.returns || 'SMALLINT'}${func.returnsLength ? `(${func.returnsLength})` : ''}
|
||||
const sql = `CREATE ${func.definer ? `DEFINER=${func.definer} ` : ''}FUNCTION \`${this._schema}\`.\`${func.name}\`(${parameters}) RETURNS ${func.returns || 'SMALLINT'}${func.returnsLength ? `(${func.returnsLength})` : ''}
|
||||
LANGUAGE SQL
|
||||
${func.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
|
||||
${func.dataAccess}
|
||||
@@ -874,7 +930,7 @@ export class MySQLClient extends AntaresCore {
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropEvent (params) {
|
||||
const sql = `DROP EVENT \`${params.scheduler}\``;
|
||||
const sql = `DROP EVENT \`${this._schema}\`.\`${params.scheduler}\``;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
@@ -890,13 +946,13 @@ export class MySQLClient extends AntaresCore {
|
||||
if (scheduler.execution === 'EVERY' && scheduler.every[0].includes('-'))
|
||||
scheduler.every[0] = `'${scheduler.every[0]}'`;
|
||||
|
||||
const sql = `ALTER ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.oldName}\`
|
||||
const sql = `ALTER ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${this._schema}\`.\`${scheduler.oldName}\`
|
||||
ON SCHEDULE
|
||||
${scheduler.execution === 'EVERY'
|
||||
? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
|
||||
: `AT '${scheduler.at}'`}
|
||||
ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
|
||||
${scheduler.name !== scheduler.oldName ? `RENAME TO \`${scheduler.name}\`` : ''}
|
||||
${scheduler.name !== scheduler.oldName ? `RENAME TO \`${this._schema}\`.\`${scheduler.name}\`` : ''}
|
||||
${scheduler.state}
|
||||
COMMENT '${scheduler.comment}'
|
||||
DO ${scheduler.sql}`;
|
||||
@@ -911,7 +967,7 @@ export class MySQLClient extends AntaresCore {
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async createEvent (scheduler) {
|
||||
const sql = `CREATE ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.name}\`
|
||||
const sql = `CREATE ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${this._schema}\`.\`${scheduler.name}\`
|
||||
ON SCHEDULE
|
||||
${scheduler.execution === 'EVERY'
|
||||
? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
|
||||
@@ -1048,7 +1104,7 @@ export class MySQLClient extends AntaresCore {
|
||||
engine
|
||||
} = params;
|
||||
|
||||
const sql = `CREATE TABLE \`${name}\` (\`${name}_ID\` INT NULL) COMMENT='${comment}', COLLATE='${collation}', ENGINE=${engine}`;
|
||||
const sql = `CREATE TABLE \`${this._schema}\`.\`${name}\` (\`${name}_ID\` INT NULL) COMMENT='${comment}', COLLATE='${collation}', ENGINE=${engine}`;
|
||||
|
||||
return await this.raw(sql);
|
||||
}
|
||||
@@ -1070,7 +1126,7 @@ export class MySQLClient extends AntaresCore {
|
||||
options
|
||||
} = params;
|
||||
|
||||
let sql = `ALTER TABLE \`${this._schema}\`.\`${table}\` `;
|
||||
let sql = `ALTER TABLE \`${this._schema || params.options.schema}\`.\`${table}\` `;
|
||||
const alterColumns = [];
|
||||
|
||||
// OPTIONS
|
||||
@@ -1082,7 +1138,7 @@ export class MySQLClient extends AntaresCore {
|
||||
// ADD FIELDS
|
||||
additions.forEach(addition => {
|
||||
const typeInfo = this._getTypeInfo(addition.type);
|
||||
const length = typeInfo.length ? addition.numLength || addition.charLength || addition.datePrecision : false;
|
||||
const length = typeInfo.length ? addition.enumValues || addition.numLength || addition.charLength || addition.datePrecision : false;
|
||||
|
||||
alterColumns.push(`ADD COLUMN \`${addition.name}\`
|
||||
${addition.type.toUpperCase()}${length ? `(${length})` : ''}
|
||||
@@ -1182,11 +1238,22 @@ export class MySQLClient extends AntaresCore {
|
||||
sql += alterColumns.join(', ');
|
||||
|
||||
// RENAME
|
||||
if (options.name) sql += `; RENAME TABLE \`${table}\` TO \`${options.name}\``;
|
||||
if (options.name) sql += `; RENAME TABLE \`${this._schema}\`.\`${table}\` TO \`${this._schema}\`.\`${options.name}\``;
|
||||
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* DUPLICATE TABLE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async duplicateTable (params) {
|
||||
const sql = `CREATE TABLE \`${this._schema}\`.\`${params.table}_copy\` LIKE \`${this._schema}\`.\`${params.table}\``;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* TRUNCATE TABLE
|
||||
*
|
||||
@@ -1260,7 +1327,10 @@ export class MySQLClient extends AntaresCore {
|
||||
// LIMIT
|
||||
const limitRaw = this._query.limit.length ? `LIMIT ${this._query.limit.join(', ')} ` : '';
|
||||
|
||||
return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${insertRaw}`;
|
||||
// OFFSET
|
||||
const offsetRaw = this._query.offset.length ? `OFFSET ${this._query.offset.join(', ')} ` : '';
|
||||
|
||||
return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${offsetRaw}${insertRaw}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1273,18 +1343,29 @@ export class MySQLClient extends AntaresCore {
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async raw (sql, args) {
|
||||
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');
|
||||
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
||||
|
||||
args = {
|
||||
nest: false,
|
||||
details: false,
|
||||
split: true,
|
||||
...args
|
||||
};
|
||||
|
||||
const nestTables = args.nest ? '.' : false;
|
||||
const resultsArr = [];
|
||||
let paramsArr = [];
|
||||
const queries = args.split ? sql.split(/((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm) : [sql];
|
||||
const queries = args.split
|
||||
? sql.split(/((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm)
|
||||
.filter(Boolean)
|
||||
.map(q => q.trim())
|
||||
: [sql];
|
||||
const isPool = typeof this._connection.getConnection === 'function';
|
||||
const connection = isPool ? await this._connection.getConnection() : this._connection;
|
||||
|
||||
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
||||
if (args.schema)
|
||||
await connection.query(`USE \`${args.schema}\``);
|
||||
|
||||
for (const query of queries) {
|
||||
if (!query) continue;
|
||||
@@ -1293,87 +1374,90 @@ export class MySQLClient extends AntaresCore {
|
||||
let keysArr = [];
|
||||
|
||||
const { rows, report, fields, keys, duration } = await new Promise((resolve, reject) => {
|
||||
this._connection.query({ sql: query, nestTables }, async (err, response, fields) => {
|
||||
connection.query({ sql: query, nestTables }).then(async ([response, fields]) => {
|
||||
timeStop = new Date();
|
||||
const queryResult = response;
|
||||
|
||||
if (err)
|
||||
reject(err);
|
||||
else {
|
||||
let remappedFields = fields
|
||||
? fields.map(field => {
|
||||
if (!field || Array.isArray(field))
|
||||
return false;
|
||||
let remappedFields = fields
|
||||
? fields.map(field => {
|
||||
if (!field || Array.isArray(field))
|
||||
return false;
|
||||
|
||||
const type = this._getType(field);
|
||||
const type = this._getType(field);
|
||||
|
||||
return {
|
||||
name: field.orgName,
|
||||
alias: field.name,
|
||||
orgName: field.orgName,
|
||||
schema: field.schema,
|
||||
table: field.table,
|
||||
tableAlias: field.table,
|
||||
orgTable: field.orgTable,
|
||||
type: type.name,
|
||||
length: type.length
|
||||
};
|
||||
}).filter(Boolean)
|
||||
: [];
|
||||
|
||||
if (args.details) {
|
||||
let cachedTable;
|
||||
|
||||
if (remappedFields.length) {
|
||||
paramsArr = remappedFields.map(field => {
|
||||
if (field.orgTable) cachedTable = field.orgTable;// Needed for some queries on information_schema
|
||||
return {
|
||||
name: field.orgName,
|
||||
alias: field.name,
|
||||
orgName: field.orgName,
|
||||
schema: field.schema,
|
||||
table: field.table,
|
||||
tableAlias: field.table,
|
||||
orgTable: field.orgTable,
|
||||
type: type.name,
|
||||
length: type.length
|
||||
table: field.orgTable || cachedTable,
|
||||
schema: field.schema || 'INFORMATION_SCHEMA'
|
||||
};
|
||||
}).filter(Boolean)
|
||||
: [];
|
||||
}).filter((val, i, arr) => arr.findIndex(el => el.schema === val.schema && el.table === val.table) === i);
|
||||
|
||||
if (args.details) {
|
||||
let cachedTable;
|
||||
for (const paramObj of paramsArr) {
|
||||
if (!paramObj.table || !paramObj.schema) continue;
|
||||
|
||||
if (remappedFields.length) {
|
||||
paramsArr = remappedFields.map(field => {
|
||||
if (field.orgTable) cachedTable = field.orgTable;// Needed for some queries on information_schema
|
||||
return {
|
||||
table: field.orgTable || cachedTable,
|
||||
schema: field.schema || 'INFORMATION_SCHEMA'
|
||||
};
|
||||
}).filter((val, i, arr) => arr.findIndex(el => el.schema === val.schema && el.table === val.table) === i);
|
||||
try { // Column details
|
||||
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)
|
||||
field = { ...field, ...detailedField };
|
||||
return field;
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
if (isPool) connection.release();
|
||||
reject(err);
|
||||
}
|
||||
|
||||
for (const paramObj of paramsArr) {
|
||||
if (!paramObj.table || !paramObj.schema) continue;
|
||||
|
||||
try { // Column details
|
||||
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)
|
||||
field = { ...detailedField, ...field };
|
||||
return field;
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
try { // Key usage (foreign keys)
|
||||
const response = await this.getKeyUsage(paramObj);
|
||||
keysArr = keysArr ? [...keysArr, ...response] : response;
|
||||
}
|
||||
catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
try { // Key usage (foreign keys)
|
||||
const response = await this.getKeyUsage(paramObj);
|
||||
keysArr = keysArr ? [...keysArr, ...response] : response;
|
||||
}
|
||||
catch (err) {
|
||||
if (isPool) connection.release();
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolve({
|
||||
duration: timeStop - timeStart,
|
||||
rows: Array.isArray(queryResult) ? queryResult.some(el => Array.isArray(el)) ? [] : queryResult : false,
|
||||
report: !Array.isArray(queryResult) ? queryResult : false,
|
||||
fields: remappedFields,
|
||||
keys: keysArr
|
||||
});
|
||||
}
|
||||
|
||||
resolve({
|
||||
duration: timeStop - timeStart,
|
||||
rows: Array.isArray(queryResult) ? queryResult.some(el => Array.isArray(el)) ? [] : queryResult : false,
|
||||
report: !Array.isArray(queryResult) ? queryResult : false,
|
||||
fields: remappedFields,
|
||||
keys: keysArr
|
||||
});
|
||||
}).catch((err) => {
|
||||
if (isPool) connection.release();
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
|
||||
resultsArr.push({ rows, report, fields, keys, duration });
|
||||
}
|
||||
|
||||
if (isPool) connection.release();
|
||||
|
||||
return resultsArr.length === 1 ? resultsArr[0] : resultsArr;
|
||||
}
|
||||
}
|
||||
|
@@ -215,6 +215,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
functions: [],
|
||||
procedures: [],
|
||||
triggers: [],
|
||||
triggerFunctions: [],
|
||||
schedulers: []
|
||||
};
|
||||
}
|
||||
@@ -496,17 +497,33 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async getTriggerInformations ({ schema, trigger }) {
|
||||
const sql = `SHOW CREATE TRIGGER \`${schema}\`.\`${trigger}\``;
|
||||
const results = await this.raw(sql);
|
||||
const [table, triggerName] = trigger.split('.');
|
||||
|
||||
const results = await this.raw(`
|
||||
SELECT event_object_schema AS table_schema,
|
||||
event_object_table AS table_name,
|
||||
trigger_schema,
|
||||
trigger_name,
|
||||
string_agg(event_manipulation, ',') AS event,
|
||||
action_timing AS activation,
|
||||
action_condition AS condition,
|
||||
action_statement AS definition
|
||||
FROM information_schema.triggers
|
||||
WHERE trigger_schema = '${schema}'
|
||||
AND trigger_name = '${triggerName}'
|
||||
AND event_object_table = '${table}'
|
||||
GROUP BY 1,2,3,4,6,7,8
|
||||
ORDER BY table_schema,
|
||||
table_name
|
||||
`);
|
||||
|
||||
return results.rows.map(row => {
|
||||
return {
|
||||
definer: row['SQL Original Statement'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
||||
sql: row['SQL Original Statement'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
||||
name: row.Trigger,
|
||||
table: row['SQL Original Statement'].match(/(?<=ON `).*?(?=`)/gs)[0],
|
||||
event1: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0],
|
||||
event2: row['SQL Original Statement'].match(/(INSERT|UPDATE|DELETE)/gs)[0]
|
||||
sql: row.definition,
|
||||
name: row.trigger_name,
|
||||
table: row.table_name,
|
||||
event: row.event.split(','),
|
||||
activation: row.activation
|
||||
};
|
||||
})[0];
|
||||
}
|
||||
@@ -519,7 +536,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
*/
|
||||
async dropTrigger (params) {
|
||||
const triggerParts = params.trigger.split('.');
|
||||
const sql = `DROP TRIGGER ${triggerParts[1]} ON ${triggerParts[0]}`;
|
||||
const sql = `DROP TRIGGER "${triggerParts[1]}" ON "${triggerParts[0]}"`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
@@ -536,8 +553,8 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
|
||||
try {
|
||||
await this.createTrigger(tempTrigger);
|
||||
await this.dropTrigger({ trigger: tempTrigger.name });
|
||||
await this.dropTrigger({ trigger: trigger.oldName });
|
||||
await this.dropTrigger({ trigger: `${tempTrigger.table}.${tempTrigger.name}` });
|
||||
await this.dropTrigger({ trigger: `${trigger.table}.${trigger.oldName}` });
|
||||
await this.createTrigger(trigger);
|
||||
}
|
||||
catch (err) {
|
||||
@@ -552,7 +569,8 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async createTrigger (trigger) {
|
||||
const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${trigger.name}\` ${trigger.event1} ${trigger.event2} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`;
|
||||
const eventsString = Array.isArray(trigger.event) ? trigger.event.join(' OR ') : trigger.event;
|
||||
const sql = `CREATE TRIGGER "${trigger.name}" ${trigger.activation} ${eventsString} ON "${trigger.table}" FOR EACH ROW ${trigger.sql}`;
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
||||
@@ -606,7 +624,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
const parameters = results.rows.map(row => {
|
||||
return {
|
||||
name: row.parameter_name,
|
||||
type: row.data_type.toUpperCase(),
|
||||
type: row.data_type ? row.data_type.toUpperCase() : '',
|
||||
length: '',
|
||||
context: row.parameter_mode
|
||||
};
|
||||
@@ -805,7 +823,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
if (this._schema !== 'public')
|
||||
await this.use(this._schema);
|
||||
|
||||
const body = func.returns ? func.sql : '$BODY$\n$BODY$';
|
||||
const body = func.returns ? func.sql : '$function$\n$function$';
|
||||
|
||||
const sql = `CREATE FUNCTION ${this._schema}.${func.name}(${parameters})
|
||||
RETURNS ${func.returns || 'void'}
|
||||
@@ -817,95 +835,46 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
}
|
||||
|
||||
/**
|
||||
* SHOW CREATE EVENT
|
||||
*
|
||||
* @returns {Array.<Object>} view informations
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
// async getEventInformations ({ schema, scheduler }) {
|
||||
// const sql = `SHOW CREATE EVENT \`${schema}\`.\`${scheduler}\``;
|
||||
// const results = await this.raw(sql);
|
||||
|
||||
// return results.rows.map(row => {
|
||||
// const schedule = row['Create Event'];
|
||||
// const execution = schedule.includes('EVERY') ? 'EVERY' : 'ONCE';
|
||||
// const every = execution === 'EVERY' ? row['Create Event'].match(/(?<=EVERY )(\s*([^\s]+)){0,2}/gs)[0].replaceAll('\'', '').split(' ') : [];
|
||||
// const starts = execution === 'EVERY' && schedule.includes('STARTS') ? schedule.match(/(?<=STARTS ').*?(?='\s)/gs)[0] : '';
|
||||
// const ends = execution === 'EVERY' && schedule.includes('ENDS') ? schedule.match(/(?<=ENDS ').*?(?='\s)/gs)[0] : '';
|
||||
// const at = execution === 'ONCE' && schedule.includes('AT') ? schedule.match(/(?<=AT ').*?(?='\s)/gs)[0] : '';
|
||||
|
||||
// return {
|
||||
// definer: row['Create Event'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
||||
// sql: row['Create Event'].match(/(?<=DO )(.*)/gs)[0],
|
||||
// name: row.Event,
|
||||
// comment: row['Create Event'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Event'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
|
||||
// state: row['Create Event'].includes('ENABLE') ? 'ENABLE' : row['Create Event'].includes('DISABLE ON SLAVE') ? 'DISABLE ON SLAVE' : 'DISABLE',
|
||||
// preserve: row['Create Event'].includes('ON COMPLETION PRESERVE'),
|
||||
// execution,
|
||||
// every,
|
||||
// starts,
|
||||
// ends,
|
||||
// at
|
||||
// };
|
||||
// })[0];
|
||||
// }
|
||||
|
||||
/**
|
||||
* DROP EVENT
|
||||
* ALTER TRIGGER FUNCTION
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
// async dropEvent (params) {
|
||||
// const sql = `DROP EVENT \`${params.scheduler}\``;
|
||||
// return await this.raw(sql);
|
||||
// }
|
||||
async alterTriggerFunction (params) {
|
||||
const { func } = params;
|
||||
|
||||
if (this._schema !== 'public')
|
||||
await this.use(this._schema);
|
||||
|
||||
const body = func.returns ? func.sql : '$function$\n$function$';
|
||||
|
||||
const sql = `CREATE OR REPLACE FUNCTION ${this._schema}.${func.name}()
|
||||
RETURNS TRIGGER
|
||||
LANGUAGE ${func.language}
|
||||
AS ${body}`;
|
||||
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* ALTER EVENT
|
||||
* CREATE TRIGGER FUNCTION
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
// async alterEvent (params) {
|
||||
// const { scheduler } = params;
|
||||
async createTriggerFunction (func) {
|
||||
if (this._schema !== 'public')
|
||||
await this.use(this._schema);
|
||||
|
||||
// if (scheduler.execution === 'EVERY' && scheduler.every[0].includes('-'))
|
||||
// scheduler.every[0] = `'${scheduler.every[0]}'`;
|
||||
const body = func.returns ? func.sql : '$function$\r\nBEGIN\r\n\r\nEND\r\n$function$';
|
||||
|
||||
// const sql = `ALTER ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.oldName}\`
|
||||
// ON SCHEDULE
|
||||
// ${scheduler.execution === 'EVERY'
|
||||
// ? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
|
||||
// : `AT '${scheduler.at}'`}
|
||||
// ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
|
||||
// ${scheduler.name !== scheduler.oldName ? `RENAME TO \`${scheduler.name}\`` : ''}
|
||||
// ${scheduler.state}
|
||||
// COMMENT '${scheduler.comment}'
|
||||
// DO ${scheduler.sql}`;
|
||||
const sql = `CREATE FUNCTION ${this._schema}.${func.name}()
|
||||
RETURNS TRIGGER
|
||||
LANGUAGE ${func.language}
|
||||
AS ${body}`;
|
||||
|
||||
// return await this.raw(sql, { split: false });
|
||||
// }
|
||||
|
||||
/**
|
||||
* CREATE EVENT
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
// async createEvent (scheduler) {
|
||||
// const sql = `CREATE ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.name}\`
|
||||
// ON SCHEDULE
|
||||
// ${scheduler.execution === 'EVERY'
|
||||
// ? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
|
||||
// : `AT '${scheduler.at}'`}
|
||||
// ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
|
||||
// ${scheduler.state}
|
||||
// COMMENT '${scheduler.comment}'
|
||||
// DO ${scheduler.sql}`;
|
||||
|
||||
// return await this.raw(sql, { split: false });
|
||||
// }
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* SELECT * FROM pg_collation
|
||||
@@ -1157,6 +1126,17 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* DUPLICATE TABLE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async duplicateTable (params) {
|
||||
const sql = `CREATE TABLE ${this._schema}.${params.table}_copy (LIKE ${this._schema}.${params.table} INCLUDING ALL)`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* TRUNCATE TABLE
|
||||
*
|
||||
@@ -1230,7 +1210,10 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
// LIMIT
|
||||
const limitRaw = selectArray.length && this._query.limit.length ? `LIMIT ${this._query.limit.join(', ')} ` : '';
|
||||
|
||||
return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${insertRaw}`;
|
||||
// OFFSET
|
||||
const offsetRaw = selectArray.length && this._query.offset.length ? `OFFSET ${this._query.offset.join(', ')} ` : '';
|
||||
|
||||
return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${offsetRaw}${insertRaw}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1243,6 +1226,8 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async raw (sql, args) {
|
||||
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');
|
||||
|
||||
args = {
|
||||
nest: false,
|
||||
details: false,
|
||||
@@ -1250,12 +1235,16 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
...args
|
||||
};
|
||||
|
||||
if (args.nest && this._schema !== 'public')
|
||||
await this.use(this._schema);
|
||||
if (args.schema && args.schema !== 'public')
|
||||
await this.use(args.schema);
|
||||
|
||||
const resultsArr = [];
|
||||
let paramsArr = [];
|
||||
const queries = args.split ? sql.split(/(?!\B'[^']*);(?![^']*'\B)/gm) : [sql];
|
||||
const queries = args.split
|
||||
? sql.split(/(?!\B'[^']*);(?![^']*'\B)/gm)
|
||||
.filter(Boolean)
|
||||
.map(q => q.trim())
|
||||
: [sql];
|
||||
|
||||
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
||||
|
||||
|
@@ -19,6 +19,7 @@
|
||||
<TheScratchpad v-if="isScratchpad" />
|
||||
<ModalSettings v-if="isSettingModal" />
|
||||
<ModalDiscardChanges v-if="isUnsavedDiscardModal" />
|
||||
<BaseTextEditor class="d-none" value="" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -40,7 +41,8 @@ export default {
|
||||
ModalNewConnection: () => import(/* webpackChunkName: "ModalNewConnection" */'@/components/ModalNewConnection'),
|
||||
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings'),
|
||||
TheScratchpad: () => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad'),
|
||||
ModalDiscardChanges: () => import(/* webpackChunkName: "ModalDiscardChanges" */'@/components/ModalDiscardChanges')
|
||||
ModalDiscardChanges: () => import(/* webpackChunkName: "ModalDiscardChanges" */'@/components/ModalDiscardChanges'),
|
||||
BaseTextEditor: () => import(/* webpackChunkName: "BaseTextEditor" */'@/components/BaseTextEditor')
|
||||
},
|
||||
data () {
|
||||
return {};
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="context">
|
||||
<div class="context" :class="{'bottom': isBottom}">
|
||||
<a
|
||||
class="context-overlay"
|
||||
@click="close"
|
||||
@@ -23,18 +23,21 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
contextSize: null
|
||||
contextSize: null,
|
||||
isBottom: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
position () {
|
||||
const { clientY, clientX } = this.contextEvent;
|
||||
let topCord = `${clientY + 5}px`;
|
||||
let topCord = `${clientY + 2}px`;
|
||||
let leftCord = `${clientX + 5}px`;
|
||||
|
||||
if (this.contextSize) {
|
||||
if (clientY + this.contextSize.height + 5 >= window.innerHeight)
|
||||
topCord = `${clientY - this.contextSize.height}px`;
|
||||
if (clientY + (this.contextSize.height < 200 ? 200 : this.contextSize.height) + 5 >= window.innerHeight) {
|
||||
topCord = `${clientY + 3 - this.contextSize.height}px`;
|
||||
this.isBottom = true;
|
||||
}
|
||||
|
||||
if (clientX + this.contextSize.width + 5 >= window.innerWidth)
|
||||
leftCord = `${clientX - this.contextSize.width}px`;
|
||||
@@ -83,12 +86,21 @@ export default {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
|
||||
&:not(.bottom) .context-submenu {
|
||||
top: -0.2rem;
|
||||
}
|
||||
|
||||
&.bottom .context-submenu {
|
||||
bottom: -0.2rem;
|
||||
}
|
||||
|
||||
.context-container {
|
||||
min-width: 100px;
|
||||
z-index: 10;
|
||||
padding: 0;
|
||||
background: #1d1d1d;
|
||||
border-radius: 0.1rem;
|
||||
border-radius: $border-radius;
|
||||
border: 1px solid $bg-color-light-dark;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
@@ -97,19 +109,24 @@ export default {
|
||||
.context-element {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0.2rem;
|
||||
padding: 0.1rem 0.3rem;
|
||||
border-radius: $border-radius;
|
||||
cursor: pointer;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
|
||||
.context-submenu {
|
||||
border-radius: $border-radius;
|
||||
border: 1px solid $bg-color-light-dark;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.2s;
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 0;
|
||||
min-width: 100px;
|
||||
background: #1d1d1d;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
@@ -34,6 +34,7 @@ export default {
|
||||
computed: {
|
||||
...mapGetters({
|
||||
editorTheme: 'settings/getEditorTheme',
|
||||
editorFontSize: 'settings/getEditorFontSize',
|
||||
autoComplete: 'settings/getAutoComplete',
|
||||
lineWrap: 'settings/getLineWrap'
|
||||
})
|
||||
@@ -47,6 +48,19 @@ export default {
|
||||
if (this.editor)
|
||||
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
||||
},
|
||||
editorFontSize () {
|
||||
const sizes = {
|
||||
small: '12px',
|
||||
medium: '14px',
|
||||
large: '16px'
|
||||
};
|
||||
|
||||
if (this.editor) {
|
||||
this.editor.setOptions({
|
||||
fontSize: sizes[this.editorFontSize]
|
||||
});
|
||||
}
|
||||
},
|
||||
autoComplete () {
|
||||
if (this.editor) {
|
||||
this.editor.setOptions({
|
||||
|
@@ -4,7 +4,7 @@
|
||||
class="toast mt-2"
|
||||
:class="toastStatus.className"
|
||||
>
|
||||
<span class="p-vcentered text-left" v-html="`${toastStatus.iconTag} ${message}`" />
|
||||
<span class="p-vcentered text-left"><i class="mdi mdi-24px mr-1" :class="toastStatus.iconName" /> {{ message }}</span>
|
||||
<button class="btn btn-clear" @click="hideToast" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -30,27 +30,27 @@ export default {
|
||||
computed: {
|
||||
toastStatus () {
|
||||
let className = '';
|
||||
let iconTag = '';
|
||||
let iconName = '';
|
||||
switch (this.status) {
|
||||
case 'success':
|
||||
className = 'toast-success';
|
||||
iconTag = '<i class="mdi mdi-24px mdi-check mr-1"></i>';
|
||||
iconName = 'mdi-check';
|
||||
break;
|
||||
case 'error':
|
||||
className = 'toast-error';
|
||||
iconTag = '<i class="mdi mdi-24px mdi-alert-rhombus mr-1"></i>';
|
||||
iconName = 'mdi-alert-rhombus';
|
||||
break;
|
||||
case 'warning':
|
||||
className = 'toast-warning';
|
||||
iconTag = '<i class="mdi mdi-24px mdi-alert mr-1"></i>';
|
||||
iconName = 'mdi-alert';
|
||||
break;
|
||||
case 'primary':
|
||||
className = 'toast-primary';
|
||||
iconTag = '<i class="mdi mdi-24px mdi-information-outline mr-1"></i>';
|
||||
iconName = 'mdi-information-outline';
|
||||
break;
|
||||
}
|
||||
|
||||
return { className, iconTag };
|
||||
return { className, iconName };
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@@ -63,7 +63,7 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.file-uploader {
|
||||
border-radius: 0.1rem;
|
||||
border-radius: $border-radius;
|
||||
height: 1.8rem;
|
||||
line-height: 1.2rem;
|
||||
display: flex;
|
||||
|
@@ -66,6 +66,21 @@
|
||||
:type="inputProps().type"
|
||||
:disabled="!isChecked"
|
||||
>
|
||||
<select
|
||||
v-else-if="enumArray"
|
||||
v-model="selectedValue"
|
||||
class="form-select"
|
||||
:disabled="!isChecked"
|
||||
@change="onChange"
|
||||
>
|
||||
<option
|
||||
v-for="val in enumArray"
|
||||
:key="val"
|
||||
:value="val"
|
||||
>
|
||||
{{ val }}
|
||||
</option>
|
||||
</select>
|
||||
<input
|
||||
v-else
|
||||
ref="formInput"
|
||||
@@ -121,7 +136,8 @@ export default {
|
||||
selectedMethod: '',
|
||||
selectedValue: '',
|
||||
debounceTimeout: null,
|
||||
methodParams: {}
|
||||
methodParams: {},
|
||||
enumArray: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -150,8 +166,14 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
fieldObj () {
|
||||
if (this.fieldObj)
|
||||
this.selectedValue = this.fieldObj.value;
|
||||
if (this.fieldObj) {
|
||||
if (Array.isArray(this.fieldObj.value)) {
|
||||
this.enumArray = this.fieldObj.value;
|
||||
this.selectedValue = this.fieldObj.value[0];
|
||||
}
|
||||
else
|
||||
this.selectedValue = this.fieldObj.value;
|
||||
}
|
||||
},
|
||||
selectedGroup () {
|
||||
if (this.fakerMethods.length)
|
||||
|
@@ -51,7 +51,8 @@ export default {
|
||||
}),
|
||||
isValidDefault () {
|
||||
if (!this.foreignList.length) return true;
|
||||
return this.value === null || this.foreignList.some(foreign => foreign.foreign_column.toString() === this.value.toString());
|
||||
if (this.value === null) return false;
|
||||
return this.foreignList.some(foreign => foreign.foreign_column.toString() === this.value.toString());
|
||||
}
|
||||
},
|
||||
async created () {
|
||||
@@ -65,7 +66,7 @@ export default {
|
||||
try { // Field data
|
||||
const { status, response } = await Tables.getTableColumns(params);
|
||||
if (status === 'success') {
|
||||
const textField = response.find(field => [...TEXT, ...LONG_TEXT].includes(field.type));
|
||||
const textField = response.find(field => [...TEXT, ...LONG_TEXT].includes(field.type) && field.name !== this.keyUsage.refField);
|
||||
foreignDesc = textField ? textField.name : false;
|
||||
}
|
||||
else
|
||||
|
@@ -8,7 +8,8 @@
|
||||
>
|
||||
<template slot="header">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-play mr-1" /> {{ $t('word.parameters') }}: {{ localRoutine.name }}
|
||||
<i class="mdi mdi-24px mdi-play mr-1" />
|
||||
<span class="cut-text">{{ $t('word.parameters') }}: {{ localRoutine.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div slot="body">
|
||||
@@ -19,10 +20,10 @@
|
||||
:key="parameter._id"
|
||||
class="form-group"
|
||||
>
|
||||
<div class="col-3">
|
||||
<div class="col-4">
|
||||
<label class="form-label">{{ parameter.name }}</label>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<div class="col-8">
|
||||
<div class="input-group">
|
||||
<input
|
||||
:ref="i === 0 ? 'firstInput' : ''"
|
||||
|
@@ -15,18 +15,18 @@
|
||||
<div class="panel-nav">
|
||||
<ul class="tab tab-block">
|
||||
<li
|
||||
class="tab-item"
|
||||
class="tab-item c-hand"
|
||||
:class="{'active': selectedTab === 'general'}"
|
||||
@click="selectTab('general')"
|
||||
>
|
||||
<a class="c-hand">{{ $t('word.general') }}</a>
|
||||
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||
</li>
|
||||
<li
|
||||
class="tab-item"
|
||||
class="tab-item c-hand"
|
||||
:class="{'active': selectedTab === 'ssl'}"
|
||||
@click="selectTab('ssl')"
|
||||
>
|
||||
<a class="c-hand">{{ $t('word.ssl') }}</a>
|
||||
<a class="tab-link">{{ $t('word.ssl') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@@ -5,7 +5,8 @@
|
||||
<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.editSchema') }}
|
||||
<i class="mdi mdi-24px mdi-database-edit mr-1" />
|
||||
<span class="cut-text">{{ $t('message.editSchema') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||
|
@@ -5,7 +5,8 @@
|
||||
<div class="modal-header pl-2">
|
||||
<div class="modal-title h6">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-playlist-plus mr-1" /> {{ $t('message.tableFiller') }}
|
||||
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||
<span class="cut-text">{{ $t('message.tableFiller') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||
@@ -250,25 +251,32 @@ export default {
|
||||
if (field.default === 'NULL') fieldDefault = null;
|
||||
else {
|
||||
if ([...NUMBER, ...FLOAT].includes(field.type))
|
||||
fieldDefault = +field.default;
|
||||
|
||||
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
||||
fieldDefault = field.default ? field.default.substring(1, field.default.length - 1) : '';
|
||||
|
||||
if ([...TIME, ...DATE].includes(field.type))
|
||||
fieldDefault = Number.isNaN(+field.default) ? null : +field.default;
|
||||
else if ([...TEXT, ...LONG_TEXT].includes(field.type)) {
|
||||
fieldDefault = field.default
|
||||
? field.default.includes('\'')
|
||||
? field.default.split('\'')[1]
|
||||
: field.default
|
||||
: '';
|
||||
}
|
||||
else if ([...TIME, ...DATE].includes(field.type))
|
||||
fieldDefault = field.default;
|
||||
|
||||
if (BIT.includes(field.type))
|
||||
else if (BIT.includes(field.type))
|
||||
fieldDefault = field.default.replaceAll('\'', '').replaceAll('b', '');
|
||||
|
||||
if (DATETIME.includes(field.type)) {
|
||||
if (field.default && field.default.toLowerCase().includes('current_timestamp')) {
|
||||
else if (DATETIME.includes(field.type)) {
|
||||
if (field.default && ['current_timestamp', 'now()'].includes(field.default.toLowerCase())) {
|
||||
let datePrecision = '';
|
||||
for (let i = 0; i < field.datePrecision; i++)
|
||||
datePrecision += i === 0 ? '.S' : 'S';
|
||||
fieldDefault = moment().format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
|
||||
}
|
||||
else
|
||||
fieldDefault = field.default;
|
||||
}
|
||||
else if (field.enumValues)
|
||||
fieldDefault = field.enumValues.replaceAll('\'', '').split(',');
|
||||
else
|
||||
fieldDefault = field.default;
|
||||
}
|
||||
|
||||
rowObj[field.name] = { value: fieldDefault };
|
||||
|
@@ -5,7 +5,8 @@
|
||||
<div class="modal-header pl-2">
|
||||
<div class="modal-title h6">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-server-plus mr-1" /> {{ $t('message.createNewConnection') }}
|
||||
<i class="mdi mdi-24px mdi-server-plus mr-1" />
|
||||
<span class="cut-text">{{ $t('message.createNewConnection') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-clear c-hand" @click="closeModal" />
|
||||
@@ -15,18 +16,18 @@
|
||||
<div class="panel-nav">
|
||||
<ul class="tab tab-block">
|
||||
<li
|
||||
class="tab-item"
|
||||
class="tab-item c-hand"
|
||||
:class="{'active': selectedTab === 'general'}"
|
||||
@click="selectTab('general')"
|
||||
>
|
||||
<a class="c-hand">{{ $t('word.general') }}</a>
|
||||
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||
</li>
|
||||
<li
|
||||
class="tab-item"
|
||||
class="tab-item c-hand"
|
||||
:class="{'active': selectedTab === 'ssl'}"
|
||||
@click="selectTab('ssl')"
|
||||
>
|
||||
<a class="c-hand">{{ $t('word.ssl') }}</a>
|
||||
<a class="tab-link">{{ $t('word.ssl') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -343,6 +344,7 @@ export default {
|
||||
else {
|
||||
try {
|
||||
const res = await Connection.makeTest(this.connection);
|
||||
console.log(res.response);
|
||||
if (res.status === 'error')
|
||||
this.toast = { status: 'error', message: res.response.message };
|
||||
else
|
||||
|
@@ -155,8 +155,8 @@ export default {
|
||||
if (this.customizations.languages)
|
||||
this.localFunction.language = this.customizations.languages[0];
|
||||
|
||||
if (this.customizations.procedureSql)
|
||||
this.localFunction.sql = this.customizations.procedureSql;
|
||||
if (this.customizations.functionSql)
|
||||
this.localFunction.sql = this.customizations.functionSql;
|
||||
setTimeout(() => {
|
||||
this.$refs.firstInput.focus();
|
||||
}, 20);
|
||||
|
@@ -5,7 +5,8 @@
|
||||
<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.createNewSchema') }}
|
||||
<i class="mdi mdi-24px mdi-database-plus mr-1" />
|
||||
<span class="cut-text">{{ $t('message.createNewSchema') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||
|
@@ -5,7 +5,8 @@
|
||||
<div class="modal-header pl-2">
|
||||
<div class="modal-title h6">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-playlist-plus mr-1" /> {{ $t('message.addNewRow') }}
|
||||
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||
<span class="cut-text">{{ $t('message.addNewRow') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||
|
@@ -25,7 +25,7 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div v-if="customizations.definer" class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.definer') }}
|
||||
</label>
|
||||
@@ -71,30 +71,56 @@
|
||||
</label>
|
||||
<div class="column">
|
||||
<div class="input-group">
|
||||
<select v-model="localTrigger.event1" class="form-select">
|
||||
<select v-model="localTrigger.activation" class="form-select">
|
||||
<option>BEFORE</option>
|
||||
<option>AFTER</option>
|
||||
</select>
|
||||
<select v-model="localTrigger.event2" class="form-select">
|
||||
<option>INSERT</option>
|
||||
<option>UPDATE</option>
|
||||
<option>DELETE</option>
|
||||
<select
|
||||
v-if="!customizations.triggerMultipleEvents"
|
||||
v-model="localTrigger.event"
|
||||
class="form-select"
|
||||
>
|
||||
<option v-for="event in Object.keys(localEvents)" :key="event">
|
||||
{{ event }}
|
||||
</option>
|
||||
</select>
|
||||
<div v-if="customizations.triggerMultipleEvents" class="px-4">
|
||||
<label
|
||||
v-for="event in Object.keys(localEvents)"
|
||||
:key="event"
|
||||
class="form-checkbox form-inline"
|
||||
@change.prevent="changeEvents(event)"
|
||||
>
|
||||
<input :checked="localEvents[event]" type="checkbox"><i class="form-icon" /> {{ event }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div v-if="customizations.triggerStatementInCreation" class="workspace-query-results column col-12 mt-2">
|
||||
<label class="form-label ml-2">{{ $t('message.triggerStatement') }}</label>
|
||||
<QueryEditor
|
||||
ref="queryEditor"
|
||||
:value.sync="localTrigger.sql"
|
||||
:workspace="workspace"
|
||||
:schema="schema"
|
||||
:height="editorHeight"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ConfirmModal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||
import QueryEditor from '@/components/QueryEditor';
|
||||
|
||||
export default {
|
||||
name: 'ModalNewTrigger',
|
||||
components: {
|
||||
ConfirmModal
|
||||
ConfirmModal,
|
||||
QueryEditor
|
||||
},
|
||||
props: {
|
||||
workspace: Object
|
||||
@@ -103,13 +129,15 @@ export default {
|
||||
return {
|
||||
localTrigger: {
|
||||
definer: '',
|
||||
sql: 'BEGIN\r\n\r\nEND',
|
||||
sql: '',
|
||||
name: '',
|
||||
table: '',
|
||||
event1: 'BEFORE',
|
||||
event2: 'INSERT'
|
||||
activation: 'BEFORE',
|
||||
event: 'INSERT'
|
||||
},
|
||||
isOptionsChanging: false
|
||||
isOptionsChanging: false,
|
||||
localEvents: { INSERT: false, UPDATE: false, DELETE: false },
|
||||
editorHeight: 150
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -122,11 +150,16 @@ export default {
|
||||
.map(schema => schema.tables);
|
||||
|
||||
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||
},
|
||||
customizations () {
|
||||
return this.workspace.customizations;
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
created () {
|
||||
this.localTrigger.table = this.schemaTables.length ? this.schemaTables[0].name : '';
|
||||
|
||||
this.localTrigger.sql = this.customizations.triggerSql;
|
||||
},
|
||||
mounted () {
|
||||
setTimeout(() => {
|
||||
this.$refs.firstInput.focus();
|
||||
}, 20);
|
||||
@@ -134,6 +167,16 @@ export default {
|
||||
methods: {
|
||||
confirmNewTrigger () {
|
||||
this.$emit('open-create-trigger-editor', this.localTrigger);
|
||||
},
|
||||
changeEvents (event) {
|
||||
if (this.customizations.triggerMultipleEvents) {
|
||||
this.localEvents[event] = !this.localEvents[event];
|
||||
this.localTrigger.event = [];
|
||||
for (const key in this.localEvents) {
|
||||
if (this.localEvents[key])
|
||||
this.localTrigger.event.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
132
src/renderer/components/ModalNewTriggerFunction.vue
Normal file
132
src/renderer/components/ModalNewTriggerFunction.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<ConfirmModal
|
||||
:confirm-text="$t('word.confirm')"
|
||||
size="400"
|
||||
@confirm="confirmNewFunction"
|
||||
@hide="$emit('close')"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-plus mr-1" /> {{ $t('message.createNewFunction') }}
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.name') }}
|
||||
</label>
|
||||
<div class="column">
|
||||
<input
|
||||
ref="firstInput"
|
||||
v-model="localFunction.name"
|
||||
class="form-input"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="customizations.languages" class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.language') }}
|
||||
</label>
|
||||
<div class="column">
|
||||
<select v-model="localFunction.language" class="form-select">
|
||||
<option v-for="language in customizations.triggerFunctionlanguages" :key="language">
|
||||
{{ language }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="customizations.definer" class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.definer') }}
|
||||
</label>
|
||||
<div class="column">
|
||||
<select
|
||||
v-if="workspace.users.length"
|
||||
v-model="localFunction.definer"
|
||||
class="form-select"
|
||||
>
|
||||
<option value="">
|
||||
{{ $t('message.currentUser') }}
|
||||
</option>
|
||||
<option
|
||||
v-for="user in workspace.users"
|
||||
:key="`${user.name}@${user.host}`"
|
||||
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||
>
|
||||
{{ user.name }}@{{ user.host }}
|
||||
</option>
|
||||
</select>
|
||||
<select v-if="!workspace.users.length" class="form-select">
|
||||
<option value="">
|
||||
{{ $t('message.currentUser') }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="customizations.comment" class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.comment') }}
|
||||
</label>
|
||||
<div class="column">
|
||||
<input
|
||||
v-model="localFunction.comment"
|
||||
class="form-input"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</ConfirmModal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||
|
||||
export default {
|
||||
name: 'ModalNewTriggerFunction',
|
||||
components: {
|
||||
ConfirmModal
|
||||
},
|
||||
props: {
|
||||
workspace: Object
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
localFunction: {
|
||||
definer: '',
|
||||
sql: '',
|
||||
name: '',
|
||||
comment: '',
|
||||
language: null
|
||||
},
|
||||
isOptionsChanging: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
schema () {
|
||||
return this.workspace.breadcrumbs.schema;
|
||||
},
|
||||
customizations () {
|
||||
return this.workspace.customizations;
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.customizations.triggerFunctionlanguages)
|
||||
this.localFunction.language = this.customizations.triggerFunctionlanguages[0];
|
||||
|
||||
if (this.customizations.triggerFunctionSql)
|
||||
this.localFunction.sql = this.customizations.triggerFunctionSql;
|
||||
setTimeout(() => {
|
||||
this.$refs.firstInput.focus();
|
||||
}, 20);
|
||||
},
|
||||
methods: {
|
||||
confirmNewFunction () {
|
||||
this.$emit('open-create-function-editor', this.localFunction);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@@ -5,7 +5,8 @@
|
||||
<div class="modal-header pl-2">
|
||||
<div class="modal-title h6">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-memory mr-1" /> {{ $t('message.processesList') }}: {{ connectionName }}
|
||||
<i class="mdi mdi-24px mdi-memory mr-1" />
|
||||
<span class="cut-text">{{ $t('message.processesList') }}: {{ connectionName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<div class="modal-title h6">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-cog mr-1" />
|
||||
{{ $t('word.settings') }}
|
||||
<span class="cut-text">{{ $t('word.settings') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-clear c-hand" @click="closeModal" />
|
||||
@@ -16,40 +16,40 @@
|
||||
<div class="panel-nav">
|
||||
<ul class="tab tab-block">
|
||||
<li
|
||||
class="tab-item"
|
||||
class="tab-item c-hand"
|
||||
:class="{'active': selectedTab === 'general'}"
|
||||
@click="selectTab('general')"
|
||||
>
|
||||
<a class="c-hand">{{ $t('word.general') }}</a>
|
||||
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||
</li>
|
||||
<li
|
||||
class="tab-item"
|
||||
class="tab-item c-hand"
|
||||
:class="{'active': selectedTab === 'themes'}"
|
||||
@click="selectTab('themes')"
|
||||
>
|
||||
<a class="c-hand">{{ $t('word.themes') }}</a>
|
||||
<a class="tab-link">{{ $t('word.themes') }}</a>
|
||||
</li>
|
||||
<li
|
||||
v-if="updateStatus !== 'disabled'"
|
||||
class="tab-item"
|
||||
class="tab-item c-hand"
|
||||
:class="{'active': selectedTab === 'update'}"
|
||||
@click="selectTab('update')"
|
||||
>
|
||||
<a class="c-hand" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a>
|
||||
<a class="tab-link" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a>
|
||||
</li>
|
||||
<li
|
||||
class="tab-item"
|
||||
class="tab-item c-hand"
|
||||
:class="{'active': selectedTab === 'changelog'}"
|
||||
@click="selectTab('changelog')"
|
||||
>
|
||||
<a class="c-hand">{{ $t('word.changelog') }}</a>
|
||||
<a class="tab-link">{{ $t('word.changelog') }}</a>
|
||||
</li>
|
||||
<li
|
||||
class="tab-item"
|
||||
class="tab-item c-hand"
|
||||
:class="{'active': selectedTab === 'about'}"
|
||||
@click="selectTab('about')"
|
||||
>
|
||||
<a class="c-hand">{{ $t('word.about') }}</a>
|
||||
<a class="tab-link">{{ $t('word.about') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -83,6 +83,27 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-6 col-sm-12">
|
||||
<label class="form-label">
|
||||
{{ $t('message.dataTabPageSize') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-6 col-sm-12">
|
||||
<select
|
||||
v-model="localPageSize"
|
||||
class="form-select"
|
||||
@change="changePageSize(+localPageSize)"
|
||||
>
|
||||
<option
|
||||
v-for="size in pageSizes"
|
||||
:key="size"
|
||||
>
|
||||
{{ size }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-6 col-sm-12">
|
||||
<label class="form-label">
|
||||
@@ -201,6 +222,31 @@
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
<div class="column col-6 mb-4">
|
||||
<div class="btn-group btn-group-block">
|
||||
<button
|
||||
class="btn btn-dark cut-text"
|
||||
:class="{'active': editorFontSize === 'small'}"
|
||||
@click="changeEditorFontSize('small')"
|
||||
>
|
||||
{{ $t('word.small') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-dark cut-text"
|
||||
:class="{'active': editorFontSize === 'medium'}"
|
||||
@click="changeEditorFontSize('medium')"
|
||||
>
|
||||
{{ $t('word.medium') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-dark cut-text"
|
||||
:class="{'active': editorFontSize === 'large'}"
|
||||
@click="changeEditorFontSize('large')"
|
||||
>
|
||||
{{ $t('word.large') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column col-12">
|
||||
<BaseTextEditor
|
||||
:value="exampleQuery"
|
||||
@@ -227,7 +273,7 @@
|
||||
<h4>{{ appName }}</h4>
|
||||
<p>
|
||||
{{ $t('word.version') }} {{ appVersion }}<br>
|
||||
<a class="c-hand" @click="openOutside('https://github.com/Fabio286/antares')">GitHub</a> | <a class="c-hand" @click="openOutside('https://antares-sql.app/')">Website</a><br>
|
||||
<a class="c-hand" @click="openOutside('https://github.com/Fabio286/antares')"><i class="mdi mdi-github d-inline" /> GitHub</a> • <a class="c-hand" @click="openOutside('https://twitter.com/AntaresSQL')"><i class="mdi mdi-twitter d-inline" /> Twitter</a> • <a class="c-hand" @click="openOutside('https://antares-sql.app/')"><i class="mdi mdi-web d-inline" /> Website</a><br>
|
||||
<small>{{ $t('word.author') }} <a class="c-hand" @click="openOutside('https://github.com/Fabio286')">Fabio Di Stasio</a></small><br>
|
||||
<small>{{ $t('message.madeWithJS') }}</small>
|
||||
</p>
|
||||
@@ -257,9 +303,11 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
localLocale: null,
|
||||
localPageSize: null,
|
||||
localTimeout: null,
|
||||
localEditorTheme: null,
|
||||
selectedTab: 'general',
|
||||
pageSizes: [40, 100, 250, 500, 1000],
|
||||
editorThemes: [
|
||||
{
|
||||
group: this.$t('word.light'),
|
||||
@@ -317,11 +365,13 @@ export default {
|
||||
appVersion: 'application/appVersion',
|
||||
selectedSettingTab: 'application/selectedSettingTab',
|
||||
selectedLocale: 'settings/getLocale',
|
||||
pageSize: 'settings/getDataTabLimit',
|
||||
selectedAutoComplete: 'settings/getAutoComplete',
|
||||
selectedLineWrap: 'settings/getLineWrap',
|
||||
notificationsTimeout: 'settings/getNotificationsTimeout',
|
||||
applicationTheme: 'settings/getApplicationTheme',
|
||||
editorTheme: 'settings/getEditorTheme',
|
||||
editorFontSize: 'settings/getEditorFontSize',
|
||||
updateStatus: 'application/getUpdateStatus',
|
||||
selectedWorkspace: 'workspaces/getSelected',
|
||||
getWorkspace: 'workspaces/getWorkspace'
|
||||
@@ -359,6 +409,7 @@ ORDER BY
|
||||
},
|
||||
created () {
|
||||
this.localLocale = this.selectedLocale;
|
||||
this.localPageSize = this.pageSize;
|
||||
this.localTimeout = this.notificationsTimeout;
|
||||
this.localEditorTheme = this.editorTheme;
|
||||
this.selectedTab = this.selectedSettingTab;
|
||||
@@ -371,10 +422,12 @@ ORDER BY
|
||||
...mapActions({
|
||||
closeModal: 'application/hideSettingModal',
|
||||
changeLocale: 'settings/changeLocale',
|
||||
changePageSize: 'settings/changePageSize',
|
||||
changeAutoComplete: 'settings/changeAutoComplete',
|
||||
changeLineWrap: 'settings/changeLineWrap',
|
||||
changeApplicationTheme: 'settings/changeApplicationTheme',
|
||||
changeEditorTheme: 'settings/changeEditorTheme',
|
||||
changeEditorFontSize: 'settings/changeEditorFontSize',
|
||||
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
||||
}),
|
||||
selectTab (tab) {
|
||||
|
@@ -38,6 +38,7 @@ export default {
|
||||
computed: {
|
||||
...mapGetters({
|
||||
editorTheme: 'settings/getEditorTheme',
|
||||
editorFontSize: 'settings/getEditorFontSize',
|
||||
autoComplete: 'settings/getAutoComplete',
|
||||
lineWrap: 'settings/getLineWrap',
|
||||
baseCompleter: 'application/getBaseCompleter'
|
||||
@@ -158,6 +159,19 @@ export default {
|
||||
if (this.editor)
|
||||
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
||||
},
|
||||
editorFontSize () {
|
||||
const sizes = {
|
||||
small: '12px',
|
||||
medium: '14px',
|
||||
large: '16px'
|
||||
};
|
||||
|
||||
if (this.editor) {
|
||||
this.editor.setOptions({
|
||||
fontSize: sizes[this.editorFontSize]
|
||||
});
|
||||
}
|
||||
},
|
||||
autoComplete () {
|
||||
if (this.editor) {
|
||||
this.editor.setOptions({
|
||||
@@ -173,8 +187,15 @@ export default {
|
||||
}
|
||||
},
|
||||
isSelected () {
|
||||
if (this.isSelected)
|
||||
if (this.isSelected) {
|
||||
this.lastSchema = this.schema;
|
||||
this.editor.resize();
|
||||
}
|
||||
},
|
||||
height () {
|
||||
setTimeout(() => {
|
||||
this.editor.resize();
|
||||
}, 20);
|
||||
},
|
||||
lastSchema () {
|
||||
if (this.editor) {
|
||||
|
@@ -174,6 +174,7 @@ export default {
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
border-radius: 0;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
@@ -181,6 +182,21 @@ export default {
|
||||
|
||||
&.selected {
|
||||
opacity: 1;
|
||||
|
||||
&::before {
|
||||
height: $settingbar-width;
|
||||
}
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
height: 0;
|
||||
width: 3px;
|
||||
transition: height 0.2s;
|
||||
background-color: $primary-color;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
|
||||
.settingbar-element-icon {
|
||||
@@ -211,7 +227,7 @@ export default {
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.7rem;
|
||||
background: rgba(48, 55, 66, 0.95);
|
||||
border-radius: 0.1rem;
|
||||
border-radius: $border-radius;
|
||||
color: #fff;
|
||||
max-width: 320px;
|
||||
pointer-events: none;
|
||||
|
@@ -127,6 +127,12 @@
|
||||
:connection="connection"
|
||||
:function="workspace.breadcrumbs.function"
|
||||
/>
|
||||
<WorkspacePropsTabTriggerFunction
|
||||
v-show="selectedTab === 'prop' && workspace.breadcrumbs.triggerFunction"
|
||||
:is-selected="selectedTab === 'prop'"
|
||||
:connection="connection"
|
||||
:function="workspace.breadcrumbs.triggerFunction"
|
||||
/>
|
||||
<WorkspacePropsTabScheduler
|
||||
v-show="selectedTab === 'prop' && workspace.breadcrumbs.scheduler"
|
||||
:is-selected="selectedTab === 'prop'"
|
||||
@@ -165,6 +171,7 @@ import WorkspacePropsTabView from '@/components/WorkspacePropsTabView';
|
||||
import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
|
||||
import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine';
|
||||
import WorkspacePropsTabFunction from '@/components/WorkspacePropsTabFunction';
|
||||
import WorkspacePropsTabTriggerFunction from '@/components/WorkspacePropsTabTriggerFunction';
|
||||
import WorkspacePropsTabScheduler from '@/components/WorkspacePropsTabScheduler';
|
||||
import ModalProcessesList from '@/components/ModalProcessesList';
|
||||
|
||||
@@ -179,6 +186,7 @@ export default {
|
||||
WorkspacePropsTabTrigger,
|
||||
WorkspacePropsTabRoutine,
|
||||
WorkspacePropsTabFunction,
|
||||
WorkspacePropsTabTriggerFunction,
|
||||
WorkspacePropsTabScheduler,
|
||||
ModalProcessesList
|
||||
},
|
||||
@@ -208,6 +216,7 @@ export default {
|
||||
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.triggerFunction && this.workspace.customizations.functionSettings) return true;
|
||||
if (this.workspace.breadcrumbs.scheduler && this.workspace.customizations.schedulerSettings) return true;
|
||||
return false;
|
||||
},
|
||||
@@ -219,6 +228,7 @@ export default {
|
||||
this.workspace.breadcrumbs.trigger === null &&
|
||||
this.workspace.breadcrumbs.procedure === null &&
|
||||
this.workspace.breadcrumbs.function === null &&
|
||||
this.workspace.breadcrumbs.triggerFunction === null &&
|
||||
this.workspace.breadcrumbs.scheduler === null &&
|
||||
['data', 'prop'].includes(this.workspace.selected_tab)
|
||||
) ||
|
||||
@@ -362,7 +372,7 @@ export default {
|
||||
min-width: 100%;
|
||||
|
||||
.menu-item a {
|
||||
border-radius: 0.1rem;
|
||||
border-radius: $border-radius;
|
||||
color: inherit;
|
||||
display: block;
|
||||
margin: 0 -0.4rem;
|
||||
|
@@ -90,6 +90,12 @@
|
||||
@close="hideCreateFunctionModal"
|
||||
@open-create-function-editor="openCreateFunctionEditor"
|
||||
/>
|
||||
<ModalNewTriggerFunction
|
||||
v-if="isNewTriggerFunctionModal"
|
||||
:workspace="workspace"
|
||||
@close="hideCreateTriggerFunctionModal"
|
||||
@open-create-function-editor="openCreateTriggerFunctionEditor"
|
||||
/>
|
||||
<ModalNewScheduler
|
||||
v-if="isNewSchedulerModal"
|
||||
:workspace="workspace"
|
||||
@@ -106,6 +112,7 @@
|
||||
@show-create-trigger-modal="showCreateTriggerModal"
|
||||
@show-create-routine-modal="showCreateRoutineModal"
|
||||
@show-create-function-modal="showCreateFunctionModal"
|
||||
@show-create-trigger-function-modal="showCreateTriggerFunctionModal"
|
||||
@show-create-scheduler-modal="showCreateSchedulerModal"
|
||||
@reload="refresh"
|
||||
/>
|
||||
@@ -147,6 +154,7 @@ import ModalNewView from '@/components/ModalNewView';
|
||||
import ModalNewTrigger from '@/components/ModalNewTrigger';
|
||||
import ModalNewRoutine from '@/components/ModalNewRoutine';
|
||||
import ModalNewFunction from '@/components/ModalNewFunction';
|
||||
import ModalNewTriggerFunction from '@/components/ModalNewTriggerFunction';
|
||||
import ModalNewScheduler from '@/components/ModalNewScheduler';
|
||||
|
||||
export default {
|
||||
@@ -163,6 +171,7 @@ export default {
|
||||
ModalNewTrigger,
|
||||
ModalNewRoutine,
|
||||
ModalNewFunction,
|
||||
ModalNewTriggerFunction,
|
||||
ModalNewScheduler
|
||||
},
|
||||
props: {
|
||||
@@ -179,6 +188,7 @@ export default {
|
||||
isNewTriggerModal: false,
|
||||
isNewRoutineModal: false,
|
||||
isNewFunctionModal: false,
|
||||
isNewTriggerFunctionModal: false,
|
||||
isNewSchedulerModal: false,
|
||||
|
||||
localWidth: null,
|
||||
@@ -209,6 +219,9 @@ export default {
|
||||
},
|
||||
connectionName () {
|
||||
return this.getConnectionName(this.connection.uid);
|
||||
},
|
||||
customizations () {
|
||||
return this.workspace.customizations;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -363,7 +376,8 @@ export default {
|
||||
|
||||
if (status === 'success') {
|
||||
await this.refresh();
|
||||
this.changeBreadcrumbs({ schema: this.selectedDatabase, trigger: payload.name });
|
||||
const triggerName = this.customizations.triggerTableInName ? `${payload.table}.${payload.name}` : payload.name;
|
||||
this.changeBreadcrumbs({ schema: this.selectedDatabase, trigger: triggerName });
|
||||
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
||||
}
|
||||
else
|
||||
@@ -399,6 +413,13 @@ export default {
|
||||
hideCreateFunctionModal () {
|
||||
this.isNewFunctionModal = false;
|
||||
},
|
||||
showCreateTriggerFunctionModal () {
|
||||
this.closeDatabaseContext();
|
||||
this.isNewTriggerFunctionModal = true;
|
||||
},
|
||||
hideCreateTriggerFunctionModal () {
|
||||
this.isNewTriggerFunctionModal = false;
|
||||
},
|
||||
showCreateSchedulerModal () {
|
||||
this.closeDatabaseContext();
|
||||
this.isNewSchedulerModal = true;
|
||||
@@ -422,6 +443,22 @@ export default {
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
},
|
||||
async openCreateTriggerFunctionEditor (payload) {
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
...payload
|
||||
};
|
||||
|
||||
const { status, response } = await Functions.createTriggerFunction(params);
|
||||
|
||||
if (status === 'success') {
|
||||
await this.refresh();
|
||||
this.changeBreadcrumbs({ schema: this.selectedDatabase, triggerFunction: payload.name });
|
||||
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
||||
}
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
},
|
||||
async openCreateSchedulerEditor (payload) {
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
@@ -451,6 +488,11 @@ export default {
|
||||
height: calc(100vh - #{$excluding-size});
|
||||
cursor: ew-resize;
|
||||
z-index: 99;
|
||||
transition: background 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: rgba($primary-color, 50%);
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-explorebar {
|
||||
|
@@ -20,7 +20,8 @@
|
||||
>
|
||||
<template slot="header">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-delete mr-1" /> {{ deleteMessage }}
|
||||
<i class="mdi mdi-24px mdi-delete mr-1" />
|
||||
<span class="cut-text">{{ deleteMessage }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div slot="body">
|
||||
@@ -83,6 +84,7 @@ export default {
|
||||
case 'procedure':
|
||||
return this.$t('message.deleteRoutine');
|
||||
case 'function':
|
||||
case 'triggerFunction':
|
||||
return this.$t('message.deleteFunction');
|
||||
case 'scheduler':
|
||||
return this.$t('message.deleteScheduler');
|
||||
@@ -134,6 +136,7 @@ export default {
|
||||
});
|
||||
break;
|
||||
case 'function':
|
||||
case 'triggerFunction':
|
||||
res = await Functions.dropFunction({
|
||||
uid: this.selectedWorkspace,
|
||||
func: this.selectedMisc.name
|
||||
|
@@ -93,6 +93,34 @@
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div v-if="filteredTriggerFunctions.length && customizations.triggerFunctions" class="database-misc">
|
||||
<details class="accordion">
|
||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction}">
|
||||
<i class="misc-icon mdi mdi-18px mdi-folder-refresh mr-1" />
|
||||
{{ $tc('word.triggerFunction', 2) }}
|
||||
</summary>
|
||||
<div class="accordion-body">
|
||||
<div>
|
||||
<ul class="menu menu-nav pt-0">
|
||||
<li
|
||||
v-for="(func, i) of filteredTriggerFunctions"
|
||||
:key="`${func.name}-${i}`"
|
||||
class="menu-item"
|
||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name}"
|
||||
@click="setBreadcrumbs({schema: database.name, triggerFunction: func.name})"
|
||||
@contextmenu.prevent="showMiscContext($event, {...func, type: 'triggerFunction'})"
|
||||
>
|
||||
<a class="table-name">
|
||||
<i class="table-icon mdi mdi-cog-clockwise mdi-18px mr-1" />
|
||||
<span v-html="highlightWord(func.name)" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<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}">
|
||||
@@ -189,6 +217,11 @@ export default {
|
||||
filteredFunctions () {
|
||||
return this.database.functions.filter(func => func.name.search(this.searchTerm) >= 0);
|
||||
},
|
||||
filteredTriggerFunctions () {
|
||||
return this.database.triggerFunctions
|
||||
? this.database.triggerFunctions.filter(func => func.name.search(this.searchTerm) >= 0)
|
||||
: [];
|
||||
},
|
||||
filteredSchedulers () {
|
||||
return this.database.schedulers.filter(scheduler => scheduler.name.search(this.searchTerm) >= 0);
|
||||
},
|
||||
@@ -311,7 +344,7 @@ export default {
|
||||
.database-name,
|
||||
.misc-name {
|
||||
&:hover {
|
||||
border-radius: 2px;
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,7 +353,7 @@ export default {
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
border-radius: 2px;
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -42,6 +42,13 @@
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.function', 1) }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="workspace.customizations.triggerFunctionAdd"
|
||||
class="context-element"
|
||||
@click="showCreateTriggerFunctionModal"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-cog-clockwise pr-1" /> {{ $tc('word.triggerFunction', 1) }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="workspace.customizations.schedulerAdd"
|
||||
class="context-element"
|
||||
@@ -69,7 +76,8 @@
|
||||
>
|
||||
<template slot="header">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-database-remove mr-1" /> {{ $t('message.deleteSchema') }}
|
||||
<i class="mdi mdi-24px mdi-database-remove mr-1" />
|
||||
<span class="cut-text">{{ $t('message.deleteSchema') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div slot="body">
|
||||
@@ -139,6 +147,9 @@ export default {
|
||||
showCreateFunctionModal () {
|
||||
this.$emit('show-create-function-modal');
|
||||
},
|
||||
showCreateTriggerFunctionModal () {
|
||||
this.$emit('show-create-trigger-function-modal');
|
||||
},
|
||||
showCreateSchedulerModal () {
|
||||
this.$emit('show-create-scheduler-modal');
|
||||
},
|
||||
|
@@ -3,6 +3,13 @@
|
||||
:context-event="contextEvent"
|
||||
@close-context="closeContext"
|
||||
>
|
||||
<div
|
||||
v-if="selectedTable.type === 'table'"
|
||||
class="context-element"
|
||||
@click="duplicateTable"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-multiple text-light pr-1" /> {{ $t('message.duplicateTable') }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedTable.type === 'table'"
|
||||
class="context-element"
|
||||
@@ -21,7 +28,7 @@
|
||||
>
|
||||
<template slot="header">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-table-off mr-1" /> {{ $t('message.emptyTable') }}
|
||||
<i class="mdi mdi-24px mdi-table-off mr-1" /> <span class="cut-text">{{ $t('message.emptyTable') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div slot="body">
|
||||
@@ -37,7 +44,8 @@
|
||||
>
|
||||
<template slot="header">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-table-remove mr-1" /> {{ selectedTable.type === 'table' ? $t('message.deleteTable') : $t('message.deleteView') }}
|
||||
<i class="mdi mdi-24px mdi-table-remove mr-1" />
|
||||
<span class="cut-text">{{ selectedTable.type === 'table' ? $t('message.deleteTable') : $t('message.deleteView') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div slot="body">
|
||||
@@ -104,6 +112,24 @@ export default {
|
||||
closeContext () {
|
||||
this.$emit('close-context');
|
||||
},
|
||||
async duplicateTable () {
|
||||
try {
|
||||
const { status, response } = await Tables.duplicateTable({
|
||||
uid: this.selectedWorkspace,
|
||||
table: this.selectedTable.name
|
||||
});
|
||||
|
||||
if (status === 'success') {
|
||||
this.closeContext();
|
||||
this.$emit('reload');
|
||||
}
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
},
|
||||
async emptyTable () {
|
||||
try {
|
||||
const { status, response } = await Tables.truncateTable({
|
||||
|
@@ -8,7 +8,8 @@
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-key-link mr-1" /> {{ $t('word.foreignKeys') }} "{{ table }}"
|
||||
<i class="mdi mdi-24px mdi-key-link mr-1" />
|
||||
<span class="cut-text">{{ $t('word.foreignKeys') }} "{{ table }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
@@ -361,7 +362,7 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tile {
|
||||
border-radius: 2px;
|
||||
border-radius: $border-radius;
|
||||
opacity: 0.5;
|
||||
transition: background 0.2s;
|
||||
transition: opacity 0.2s;
|
||||
|
@@ -7,7 +7,8 @@
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('word.options') }} "{{ localOptions.name }}"
|
||||
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||
<span class="cut-text">{{ $t('word.options') }} "{{ localOptions.name }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
|
@@ -8,7 +8,8 @@
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" /> {{ $t('word.parameters') }} "{{ func }}"
|
||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||
<span class="cut-text">{{ $t('word.parameters') }} "{{ func }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
@@ -273,7 +274,7 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tile {
|
||||
border-radius: 2px;
|
||||
border-radius: $border-radius;
|
||||
opacity: 0.5;
|
||||
transition: background 0.2s;
|
||||
transition: opacity 0.2s;
|
||||
|
@@ -8,7 +8,8 @@
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" /> {{ $t('word.indexes') }} "{{ table }}"
|
||||
<i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" />
|
||||
<span class="cut-text">{{ $t('word.indexes') }} "{{ table }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
@@ -246,7 +247,7 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tile {
|
||||
border-radius: 2px;
|
||||
border-radius: $border-radius;
|
||||
opacity: 0.5;
|
||||
transition: background 0.2s;
|
||||
transition: opacity 0.2s;
|
||||
|
@@ -7,7 +7,8 @@
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('word.options') }} "{{ table }}"
|
||||
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||
<span class="cut-text">{{ $t('word.options') }} "{{ table }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
@@ -47,6 +48,7 @@
|
||||
v-model="optionsProxy.autoIncrement"
|
||||
class="form-input"
|
||||
type="number"
|
||||
:disabled="optionsProxy.autoIncrement === null"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -7,7 +7,8 @@
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('word.options') }} "{{ localOptions.name }}"
|
||||
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||
<span class="cut-text">{{ $t('word.options') }} "{{ localOptions.name }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
|
@@ -8,7 +8,8 @@
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" /> {{ $t('word.parameters') }} "{{ routine }}"
|
||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||
<span class="cut-text">{{ $t('word.parameters') }} "{{ routine }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
@@ -273,7 +274,7 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tile {
|
||||
border-radius: 2px;
|
||||
border-radius: $border-radius;
|
||||
opacity: 0.5;
|
||||
transition: background 0.2s;
|
||||
transition: opacity 0.2s;
|
||||
|
@@ -7,7 +7,8 @@
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-timer mr-1" /> {{ $t('word.timing') }} "{{ localOptions.name }}"
|
||||
<i class="mdi mdi-24px mdi-timer mr-1" />
|
||||
<span class="cut-text">{{ $t('word.timing') }} "{{ localOptions.name }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
|
@@ -239,11 +239,11 @@ export default {
|
||||
field.defaultType = 'noval';
|
||||
else if (field.default === 'NULL')
|
||||
field.defaultType = 'null';
|
||||
else if (field.default.match(/^\s*(\w+)\s*\((.*)\)$/))
|
||||
else if (isNaN(+field.default) && field.default.charAt(0) !== '\'')
|
||||
field.defaultType = 'expression';
|
||||
else {
|
||||
field.defaultType = 'custom';
|
||||
if (isNaN(field.default) && !field.default.includes('\''))
|
||||
if (isNaN(+field.default) && !field.default.includes('\''))
|
||||
field.default = `'${field.default}'`;
|
||||
}
|
||||
|
||||
|
@@ -137,7 +137,9 @@ export default {
|
||||
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
|
||||
},
|
||||
isDefinerInUsers () {
|
||||
return this.originalFunction ? this.workspace.users.some(user => this.originalFunction.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
||||
return this.originalFunction
|
||||
? this.workspace.users.some(user => this.originalFunction.definer === `\`${user.name}\`@\`${user.host}\``)
|
||||
: true;
|
||||
},
|
||||
schemaTables () {
|
||||
const schemaTables = this.workspace.structure
|
||||
|
@@ -37,7 +37,7 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column col-auto">
|
||||
<div v-if="customizations.definer" class="column col-auto">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{{ $t('word.definer') }}</label>
|
||||
<select
|
||||
@@ -67,7 +67,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<fieldset class="columns" :disabled="customizations.triggerOnlyRename">
|
||||
<div class="column col-auto">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{{ $t('word.table') }}</label>
|
||||
@@ -82,19 +82,33 @@
|
||||
<div class="form-group">
|
||||
<label class="form-label">{{ $t('word.event') }}</label>
|
||||
<div class="input-group">
|
||||
<select v-model="localTrigger.event1" class="form-select">
|
||||
<select v-model="localTrigger.activation" class="form-select">
|
||||
<option>BEFORE</option>
|
||||
<option>AFTER</option>
|
||||
</select>
|
||||
<select v-model="localTrigger.event2" class="form-select">
|
||||
<option>INSERT</option>
|
||||
<option>UPDATE</option>
|
||||
<option>DELETE</option>
|
||||
<select
|
||||
v-if="!customizations.triggerMultipleEvents"
|
||||
v-model="localTrigger.event"
|
||||
class="form-select"
|
||||
>
|
||||
<option v-for="event in Object.keys(localEvents)" :key="event">
|
||||
{{ event }}
|
||||
</option>
|
||||
</select>
|
||||
<div v-if="customizations.triggerMultipleEvents" class="px-4">
|
||||
<label
|
||||
v-for="event in Object.keys(localEvents)"
|
||||
:key="event"
|
||||
class="form-checkbox form-inline"
|
||||
@change.prevent="changeEvents(event)"
|
||||
>
|
||||
<input :checked="localEvents[event]" type="checkbox"><i class="form-icon" /> {{ event }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||
<BaseLoader v-if="isLoading" />
|
||||
@@ -136,7 +150,8 @@ export default {
|
||||
localTrigger: { sql: '' },
|
||||
lastTrigger: null,
|
||||
sqlProxy: '',
|
||||
editorHeight: 300
|
||||
editorHeight: 300,
|
||||
localEvents: { INSERT: false, UPDATE: false, DELETE: false }
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -147,6 +162,9 @@ export default {
|
||||
workspace () {
|
||||
return this.getWorkspace(this.connection.uid);
|
||||
},
|
||||
customizations () {
|
||||
return this.workspace.customizations;
|
||||
},
|
||||
isSelected () {
|
||||
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.trigger;
|
||||
},
|
||||
@@ -209,6 +227,10 @@ export default {
|
||||
async getTriggerData () {
|
||||
if (!this.trigger) return;
|
||||
|
||||
Object.keys(this.localEvents).forEach(event => {
|
||||
this.localEvents[event] = false;
|
||||
});
|
||||
|
||||
this.localTrigger = { sql: '' };
|
||||
this.isLoading = true;
|
||||
|
||||
@@ -224,6 +246,12 @@ export default {
|
||||
this.originalTrigger = response;
|
||||
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
|
||||
this.sqlProxy = this.localTrigger.sql;
|
||||
|
||||
if (this.customizations.triggerMultipleEvents) {
|
||||
this.originalTrigger.event.forEach(e => {
|
||||
this.localEvents[e] = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
@@ -235,6 +263,16 @@ export default {
|
||||
this.resizeQueryEditor();
|
||||
this.isLoading = false;
|
||||
},
|
||||
changeEvents (event) {
|
||||
if (this.customizations.triggerMultipleEvents) {
|
||||
this.localEvents[event] = !this.localEvents[event];
|
||||
this.localTrigger.event = [];
|
||||
for (const key in this.localEvents) {
|
||||
if (this.localEvents[key])
|
||||
this.localTrigger.event.push(key);
|
||||
}
|
||||
}
|
||||
},
|
||||
async saveChanges () {
|
||||
if (this.isSaving) return;
|
||||
this.isSaving = true;
|
||||
@@ -257,7 +295,8 @@ export default {
|
||||
|
||||
if (oldName !== this.localTrigger.name) {
|
||||
this.setUnsavedChanges(false);
|
||||
this.changeBreadcrumbs({ schema: this.schema, trigger: this.localTrigger.name });
|
||||
const triggerName = this.customizations.triggerTableInName ? `${this.localTrigger.table}.${this.localTrigger.name}` : this.localTrigger.name;
|
||||
this.changeBreadcrumbs({ schema: this.schema, trigger: triggerName });
|
||||
}
|
||||
|
||||
this.getTriggerData();
|
||||
@@ -274,6 +313,16 @@ export default {
|
||||
clearChanges () {
|
||||
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
|
||||
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
||||
|
||||
Object.keys(this.localEvents).forEach(event => {
|
||||
this.localEvents[event] = false;
|
||||
});
|
||||
|
||||
if (this.customizations.triggerMultipleEvents) {
|
||||
this.originalTrigger.event.forEach(e => {
|
||||
this.localEvents[e] = true;
|
||||
});
|
||||
}
|
||||
},
|
||||
resizeQueryEditor () {
|
||||
if (this.$refs.queryEditor) {
|
||||
|
315
src/renderer/components/WorkspacePropsTabTriggerFunction.vue
Normal file
315
src/renderer/components/WorkspacePropsTabTriggerFunction.vue
Normal file
@@ -0,0 +1,315 @@
|
||||
<template>
|
||||
<div class="workspace-query-tab column col-12 columns col-gapless">
|
||||
<div class="workspace-query-runner column col-12">
|
||||
<div class="workspace-query-runner-footer">
|
||||
<div class="workspace-query-buttons">
|
||||
<button
|
||||
class="btn btn-primary btn-sm"
|
||||
:disabled="!isChanged"
|
||||
:class="{'loading':isSaving}"
|
||||
title="CTRL+S"
|
||||
@click="saveChanges"
|
||||
>
|
||||
<span>{{ $t('word.save') }}</span>
|
||||
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
||||
</button>
|
||||
<button
|
||||
:disabled="!isChanged"
|
||||
class="btn btn-link btn-sm mr-0"
|
||||
:title="$t('message.clearChanges')"
|
||||
@click="clearChanges"
|
||||
>
|
||||
<span>{{ $t('word.clear') }}</span>
|
||||
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
||||
</button>
|
||||
|
||||
<div class="divider-vert py-3" />
|
||||
|
||||
<button class="btn btn-dark btn-sm" @click="showOptionsModal">
|
||||
<span>{{ $t('word.options') }}</span>
|
||||
<i class="mdi mdi-24px mdi-cogs ml-1" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||
<BaseLoader v-if="isLoading" />
|
||||
<label class="form-label ml-2">{{ $t('message.functionBody') }}</label>
|
||||
<QueryEditor
|
||||
v-show="isSelected"
|
||||
ref="queryEditor"
|
||||
:value.sync="localFunction.sql"
|
||||
:workspace="workspace"
|
||||
:schema="schema"
|
||||
:height="editorHeight"
|
||||
/>
|
||||
</div>
|
||||
<WorkspacePropsTriggerFunctionOptionsModal
|
||||
v-if="isOptionsModal"
|
||||
:local-options="localFunction"
|
||||
:workspace="workspace"
|
||||
@hide="hideOptionsModal"
|
||||
@options-update="optionsUpdate"
|
||||
/>
|
||||
<ModalAskParameters
|
||||
v-if="isAskingParameters"
|
||||
:local-routine="localFunction"
|
||||
:client="workspace.client"
|
||||
@confirm="runFunction"
|
||||
@close="hideAskParamsModal"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
import BaseLoader from '@/components/BaseLoader';
|
||||
import QueryEditor from '@/components/QueryEditor';
|
||||
import WorkspacePropsTriggerFunctionOptionsModal from '@/components/WorkspacePropsTriggerFunctionOptionsModal';
|
||||
import ModalAskParameters from '@/components/ModalAskParameters';
|
||||
import Functions from '@/ipc-api/Functions';
|
||||
|
||||
export default {
|
||||
name: 'WorkspacePropsTabTriggerFunction',
|
||||
components: {
|
||||
BaseLoader,
|
||||
QueryEditor,
|
||||
WorkspacePropsTriggerFunctionOptionsModal,
|
||||
ModalAskParameters
|
||||
},
|
||||
props: {
|
||||
connection: Object,
|
||||
function: String
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
tabUid: 'prop',
|
||||
isLoading: false,
|
||||
isSaving: false,
|
||||
isOptionsModal: false,
|
||||
isParamsModal: false,
|
||||
isAskingParameters: false,
|
||||
originalFunction: null,
|
||||
localFunction: { sql: '' },
|
||||
lastFunction: null,
|
||||
sqlProxy: '',
|
||||
editorHeight: 300
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
selectedWorkspace: 'workspaces/getSelected',
|
||||
getWorkspace: 'workspaces/getWorkspace'
|
||||
}),
|
||||
workspace () {
|
||||
return this.getWorkspace(this.connection.uid);
|
||||
},
|
||||
isSelected () {
|
||||
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.function;
|
||||
},
|
||||
schema () {
|
||||
return this.workspace.breadcrumbs.schema;
|
||||
},
|
||||
isChanged () {
|
||||
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
|
||||
},
|
||||
isDefinerInUsers () {
|
||||
return this.originalFunction
|
||||
? this.workspace.users.some(user => this.originalFunction.definer === `\`${user.name}\`@\`${user.host}\``)
|
||||
: true;
|
||||
},
|
||||
schemaTables () {
|
||||
const schemaTables = this.workspace.structure
|
||||
.filter(schema => schema.name === this.schema)
|
||||
.map(schema => schema.tables);
|
||||
|
||||
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
async function () {
|
||||
if (this.isSelected) {
|
||||
await this.getFunctionData();
|
||||
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
||||
this.lastFunction = this.function;
|
||||
}
|
||||
},
|
||||
async isSelected (val) {
|
||||
if (val && this.lastFunction !== this.function) {
|
||||
await this.getFunctionData();
|
||||
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
||||
this.lastFunction = this.function;
|
||||
}
|
||||
},
|
||||
isChanged (val) {
|
||||
if (this.isSelected && this.lastFunction === this.function && this.function !== null)
|
||||
this.setUnsavedChanges(val);
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('resize', this.resizeQueryEditor);
|
||||
},
|
||||
destroyed () {
|
||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||
},
|
||||
created () {
|
||||
window.addEventListener('keydown', this.onKey);
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('keydown', this.onKey);
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
addNotification: 'notifications/addNotification',
|
||||
refreshStructure: 'workspaces/refreshStructure',
|
||||
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||
newTab: 'workspaces/newTab'
|
||||
}),
|
||||
async getFunctionData () {
|
||||
if (!this.function) return;
|
||||
|
||||
this.isLoading = true;
|
||||
this.localFunction = { sql: '' };
|
||||
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
schema: this.schema,
|
||||
func: this.workspace.breadcrumbs.triggerFunction
|
||||
};
|
||||
|
||||
try {
|
||||
const { status, response } = await Functions.getFunctionInformations(params);
|
||||
if (status === 'success') {
|
||||
this.originalFunction = response;
|
||||
|
||||
this.originalFunction.parameters = [...this.originalFunction.parameters.map(param => {
|
||||
param._id = uidGen();
|
||||
return param;
|
||||
})];
|
||||
|
||||
this.localFunction = JSON.parse(JSON.stringify(this.originalFunction));
|
||||
this.sqlProxy = this.localFunction.sql;
|
||||
}
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
this.resizeQueryEditor();
|
||||
this.isLoading = false;
|
||||
},
|
||||
async saveChanges () {
|
||||
if (this.isSaving) return;
|
||||
this.isSaving = true;
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
schema: this.schema,
|
||||
func: {
|
||||
...this.localFunction,
|
||||
oldName: this.originalFunction.name
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const { status, response } = await Functions.alterTriggerFunction(params);
|
||||
|
||||
if (status === 'success') {
|
||||
const oldName = this.originalFunction.name;
|
||||
|
||||
await this.refreshStructure(this.connection.uid);
|
||||
|
||||
if (oldName !== this.localFunction.name) {
|
||||
this.setUnsavedChanges(false);
|
||||
this.changeBreadcrumbs({ schema: this.schema, function: this.localFunction.name });
|
||||
}
|
||||
|
||||
this.getFunctionData();
|
||||
}
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
this.isSaving = false;
|
||||
},
|
||||
clearChanges () {
|
||||
this.localFunction = JSON.parse(JSON.stringify(this.originalFunction));
|
||||
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
||||
},
|
||||
resizeQueryEditor () {
|
||||
if (this.$refs.queryEditor) {
|
||||
const footer = document.getElementById('footer');
|
||||
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||
this.editorHeight = size;
|
||||
this.$refs.queryEditor.editor.resize();
|
||||
}
|
||||
},
|
||||
optionsUpdate (options) {
|
||||
this.localFunction = options;
|
||||
},
|
||||
parametersUpdate (parameters) {
|
||||
this.localFunction = { ...this.localFunction, parameters };
|
||||
},
|
||||
runFunctionCheck () {
|
||||
if (this.localFunction.parameters.length)
|
||||
this.showAskParamsModal();
|
||||
else
|
||||
this.runFunction();
|
||||
},
|
||||
runFunction (params) {
|
||||
if (!params) params = [];
|
||||
|
||||
let sql;
|
||||
switch (this.connection.client) { // TODO: move in a better place
|
||||
case 'maria':
|
||||
case 'mysql':
|
||||
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
||||
break;
|
||||
case 'pg':
|
||||
sql = `SELECT ${this.originalFunction.name}(${params.join(',')})`;
|
||||
break;
|
||||
case 'mssql':
|
||||
sql = `SELECT ${this.originalFunction.name} ${params.join(',')}`;
|
||||
break;
|
||||
default:
|
||||
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
||||
}
|
||||
|
||||
this.newTab({ uid: this.connection.uid, content: sql, autorun: true });
|
||||
},
|
||||
showOptionsModal () {
|
||||
this.isOptionsModal = true;
|
||||
},
|
||||
hideOptionsModal () {
|
||||
this.isOptionsModal = false;
|
||||
},
|
||||
showParamsModal () {
|
||||
this.isParamsModal = true;
|
||||
},
|
||||
hideParamsModal () {
|
||||
this.isParamsModal = false;
|
||||
},
|
||||
showAskParamsModal () {
|
||||
this.isAskingParameters = true;
|
||||
},
|
||||
hideAskParamsModal () {
|
||||
this.isAskingParameters = false;
|
||||
},
|
||||
onKey (e) {
|
||||
if (this.isSelected) {
|
||||
e.stopPropagation();
|
||||
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
||||
if (this.isChanged)
|
||||
this.saveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@@ -96,7 +96,12 @@
|
||||
class="cell-content"
|
||||
@dblclick="editON($event, localLength, 'length')"
|
||||
>
|
||||
{{ localLength }}
|
||||
<span v-if="localRow.enumValues">
|
||||
{{ localRow.enumValues }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ localLength }}
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
v-else-if="localRow.enumValues"
|
||||
@@ -227,7 +232,8 @@
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> {{ $t('word.default') }} "{{ row.name }}"
|
||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" />
|
||||
<span class="cut-text">{{ $t('word.default') }} "{{ row.name }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
@@ -361,7 +367,7 @@ export default {
|
||||
getWorkspace: 'workspaces/getWorkspace'
|
||||
}),
|
||||
localLength () {
|
||||
return this.localRow.enumValues || this.localRow.numLength || this.localRow.charLength || this.localRow.datePrecision || this.localRow.numPrecision || 0;
|
||||
return this.localRow.numLength || this.localRow.charLength || this.localRow.datePrecision || this.localRow.numPrecision || 0;
|
||||
},
|
||||
fieldType () {
|
||||
const fieldType = this.dataTypes.reduce((acc, group) => [...acc, ...group.types], []).filter(type =>
|
||||
@@ -445,8 +451,13 @@ export default {
|
||||
|
||||
this.defaultValue.onUpdate = this.localRow.onUpdate;
|
||||
this.defaultValue.type = this.localRow.defaultType;
|
||||
if (this.defaultValue.type === 'custom')
|
||||
this.defaultValue.custom = this.localRow.default;
|
||||
if (this.defaultValue.type === 'custom') {
|
||||
this.defaultValue.custom = this.localRow.default
|
||||
? this.localRow.default.includes('\'')
|
||||
? this.localRow.default.split('\'')[1]
|
||||
: this.localRow.default
|
||||
: '';
|
||||
}
|
||||
if (this.defaultValue.type === 'expression')
|
||||
this.defaultValue.expression = this.localRow.default;
|
||||
},
|
||||
@@ -492,11 +503,13 @@ export default {
|
||||
this.localRow.numLength = null;
|
||||
this.localRow.charLength = null;
|
||||
this.localRow.datePrecision = null;
|
||||
this.localRow.enumValues = '';
|
||||
|
||||
if (this.fieldType.length) {
|
||||
if (['integer', 'float', 'binary', 'spatial'].includes(this.fieldType.group)) this.localRow.numLength = 11;
|
||||
if (['string', 'other'].includes(this.fieldType.group)) this.localRow.charLength = 15;
|
||||
if (['string'].includes(this.fieldType.group)) this.localRow.charLength = 15;
|
||||
if (['time'].includes(this.fieldType.group)) this.localRow.datePrecision = 0;
|
||||
if (['other'].includes(this.fieldType.group)) this.localRow.enumValues = '\'valA\',\'valB\'';
|
||||
}
|
||||
|
||||
if (!this.fieldType.collation)
|
||||
@@ -523,7 +536,7 @@ export default {
|
||||
break;
|
||||
case 'custom':
|
||||
this.localRow.autoIncrement = false;
|
||||
this.localRow.default = this.defaultValue.custom;
|
||||
this.localRow.default = Number.isNaN(+this.defaultValue.custom) ? `'${this.defaultValue.custom}'` : this.defaultValue.custom;
|
||||
break;
|
||||
case 'expression':
|
||||
this.localRow.autoIncrement = false;
|
||||
|
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<ConfirmModal
|
||||
:confirm-text="$t('word.confirm')"
|
||||
size="400"
|
||||
@confirm="confirmOptionsChange"
|
||||
@hide="$emit('hide')"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||
<span class="cut-text">{{ $t('word.options') }} "{{ localOptions.name }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<form class="form-horizontal">
|
||||
<div v-if="customizations.triggerFunctionlanguages" class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.language') }}
|
||||
</label>
|
||||
<div class="column">
|
||||
<select v-model="optionsProxy.language" class="form-select">
|
||||
<option v-for="language in customizations.triggerFunctionlanguages" :key="language">
|
||||
{{ language }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="customizations.definer" class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.definer') }}
|
||||
</label>
|
||||
<div class="column">
|
||||
<select
|
||||
v-if="workspace.users.length"
|
||||
v-model="optionsProxy.definer"
|
||||
class="form-select"
|
||||
>
|
||||
<option value="">
|
||||
{{ $t('message.currentUser') }}
|
||||
</option>
|
||||
<option
|
||||
v-for="user in workspace.users"
|
||||
:key="`${user.name}@${user.host}`"
|
||||
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||
>
|
||||
{{ user.name }}@{{ user.host }}
|
||||
</option>
|
||||
</select>
|
||||
<select v-if="!workspace.users.length" class="form-select">
|
||||
<option value="">
|
||||
{{ $t('message.currentUser') }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="customizations.comment" class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.comment') }}
|
||||
</label>
|
||||
<div class="column">
|
||||
<input
|
||||
v-model="optionsProxy.comment"
|
||||
class="form-input"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</ConfirmModal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||
|
||||
export default {
|
||||
name: 'WorkspacePropsTriggerFunctionOptionsModal',
|
||||
components: {
|
||||
ConfirmModal
|
||||
},
|
||||
props: {
|
||||
localOptions: Object,
|
||||
workspace: Object
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
optionsProxy: {},
|
||||
isOptionsChanging: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isTableNameValid () {
|
||||
return this.optionsProxy.name !== '';
|
||||
},
|
||||
customizations () {
|
||||
return this.workspace.customizations;
|
||||
},
|
||||
isInDataTypes () {
|
||||
let typeNames = [];
|
||||
for (const group of this.workspace.dataTypes) {
|
||||
typeNames = group.types.reduce((acc, curr) => {
|
||||
acc.push(curr.name);
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
return typeNames.includes(this.localOptions.returns);
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.optionsProxy = JSON.parse(JSON.stringify(this.localOptions));
|
||||
|
||||
setTimeout(() => {
|
||||
this.$refs.firstInput.focus();
|
||||
}, 20);
|
||||
},
|
||||
methods: {
|
||||
confirmOptionsChange () {
|
||||
if (!this.isTableNameValid)
|
||||
this.optionsProxy.name = this.localOptions.name;
|
||||
|
||||
this.$emit('options-update', this.optionsProxy);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@@ -31,25 +31,6 @@
|
||||
<span>{{ $t('word.run') }}</span>
|
||||
<i class="mdi mdi-24px mdi-play" />
|
||||
</button>
|
||||
<div class="dropdown export-dropdown pr-2">
|
||||
<button
|
||||
:disabled="!results.length || isQuering"
|
||||
class="btn btn-dark btn-sm dropdown-toggle mr-0 pr-0"
|
||||
tabindex="0"
|
||||
>
|
||||
<span>{{ $t('word.export') }}</span>
|
||||
<i class="mdi mdi-24px mdi-file-export ml-1" />
|
||||
<i class="mdi mdi-24px mdi-menu-down" />
|
||||
</button>
|
||||
<ul class="menu text-left">
|
||||
<li class="menu-item">
|
||||
<a class="c-hand" @click="downloadTable('json')">JSON</a>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<a class="c-hand" @click="downloadTable('csv')">CSV</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-dark btn-sm"
|
||||
:disabled="!query || isQuering"
|
||||
@@ -68,6 +49,28 @@
|
||||
<span>{{ $t('word.clear') }}</span>
|
||||
<i class="mdi mdi-24px mdi-delete-sweep pl-1" />
|
||||
</button>
|
||||
|
||||
<div class="divider-vert py-3" />
|
||||
|
||||
<div class="dropdown table-dropdown pr-2">
|
||||
<button
|
||||
:disabled="!results.length || isQuering"
|
||||
class="btn btn-dark btn-sm dropdown-toggle mr-0 pr-0"
|
||||
tabindex="0"
|
||||
>
|
||||
<span>{{ $t('word.export') }}</span>
|
||||
<i class="mdi mdi-24px mdi-file-export ml-1" />
|
||||
<i class="mdi mdi-24px mdi-menu-down" />
|
||||
</button>
|
||||
<ul class="menu text-left">
|
||||
<li class="menu-item">
|
||||
<a class="c-hand" @click="downloadTable('json')">JSON</a>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<a class="c-hand" @click="downloadTable('csv')">CSV</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workspace-query-info">
|
||||
<div
|
||||
@@ -281,11 +284,16 @@ export default {
|
||||
|
||||
.query-area-resizer {
|
||||
position: absolute;
|
||||
height: 5px;
|
||||
height: 4px;
|
||||
bottom: 40px;
|
||||
width: 100%;
|
||||
cursor: ns-resize;
|
||||
z-index: 99;
|
||||
transition: background 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: rgba($primary-color, 50%);
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-query-runner-footer {
|
||||
@@ -319,10 +327,4 @@ export default {
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.export-dropdown {
|
||||
.menu {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -12,6 +12,8 @@
|
||||
:selected-rows="selectedRows"
|
||||
@show-delete-modal="showDeleteConfirmModal"
|
||||
@set-null="setNull"
|
||||
@copy-cell="copyCell"
|
||||
@copy-row="copyRow"
|
||||
@close-context="closeContext"
|
||||
/>
|
||||
<ul v-if="resultsWithRows.length > 1" class="tab tab-block result-tabs">
|
||||
@@ -85,7 +87,8 @@
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-delete mr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}
|
||||
<i class="mdi mdi-24px mdi-delete mr-1" />
|
||||
<span class="cut-text">{{ $tc('message.deleteRows', selectedRows.length) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
@@ -139,7 +142,8 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
getWorkspace: 'workspaces/getWorkspace'
|
||||
getWorkspace: 'workspaces/getWorkspace',
|
||||
pageSize: 'settings/getDataTabLimit'
|
||||
}),
|
||||
workspaceSchema () {
|
||||
return this.getWorkspace(this.connUid).breadcrumbs.schema;
|
||||
@@ -157,7 +161,7 @@ export default {
|
||||
return this.fields.every(field => field.name);
|
||||
},
|
||||
isHardSort () {
|
||||
return this.mode === 'table' && this.localResults.length === 1000;
|
||||
return this.mode === 'table' && this.localResults.length === this.pageSize;
|
||||
},
|
||||
sortedResults () {
|
||||
if (this.currentSort && !this.isHardSort) {
|
||||
@@ -319,8 +323,6 @@ export default {
|
||||
},
|
||||
updateField (payload, row) {
|
||||
const orgRow = this.localResults.find(lr => lr._id === row._id);
|
||||
delete row._id;
|
||||
delete orgRow._id;
|
||||
|
||||
Object.keys(orgRow).forEach(key => { // remap the row
|
||||
if (orgRow[key] instanceof Date && moment(orgRow[key]).isValid()) { // if datetime
|
||||
@@ -347,7 +349,9 @@ export default {
|
||||
closeContext () {
|
||||
this.isContext = false;
|
||||
},
|
||||
showDeleteConfirmModal () {
|
||||
showDeleteConfirmModal (e) {
|
||||
if (e && e.path && ['INPUT', 'TEXTAREA', 'SELECT'].includes(e.path[0].tagName))
|
||||
return;
|
||||
this.isDeleteConfirmModal = true;
|
||||
},
|
||||
hideDeleteConfirmModal () {
|
||||
@@ -370,7 +374,6 @@ export default {
|
||||
},
|
||||
setNull () {
|
||||
const row = this.localResults.find(row => this.selectedRows.includes(row._id));
|
||||
delete row._id;
|
||||
|
||||
const params = {
|
||||
primary: this.primaryField.name,
|
||||
@@ -384,6 +387,22 @@ export default {
|
||||
};
|
||||
this.$emit('update-field', params);
|
||||
},
|
||||
copyCell () {
|
||||
const row = this.localResults.find(row => this.selectedRows.includes(row._id));
|
||||
const cellName = Object.keys(row).find(prop => [
|
||||
this.selectedCell.field,
|
||||
`${this.fields[0].table}.${this.selectedCell.field}`,
|
||||
`${this.fields[0].tableAlias}.${this.selectedCell.field}`
|
||||
].includes(prop));
|
||||
const valueToCopy = row[cellName];
|
||||
navigator.clipboard.writeText(valueToCopy);
|
||||
},
|
||||
copyRow () {
|
||||
const row = this.localResults.find(row => this.selectedRows.includes(row._id));
|
||||
const rowToCopy = JSON.parse(JSON.stringify(row));
|
||||
delete rowToCopy._id;
|
||||
navigator.clipboard.writeText(JSON.stringify(rowToCopy));
|
||||
},
|
||||
applyUpdate (params) {
|
||||
const { primary, id, field, table, content } = params;
|
||||
|
||||
@@ -408,15 +427,15 @@ export default {
|
||||
this.selectedRows.push(row);
|
||||
else {
|
||||
const lastID = this.selectedRows.slice(-1)[0];
|
||||
const lastIndex = this.localResults.findIndex(el => el._id === lastID);
|
||||
const clickedIndex = this.localResults.findIndex(el => el._id === row);
|
||||
const lastIndex = this.sortedResults.findIndex(el => el._id === lastID);
|
||||
const clickedIndex = this.sortedResults.findIndex(el => el._id === row);
|
||||
if (lastIndex > clickedIndex) {
|
||||
for (let i = clickedIndex; i < lastIndex; i++)
|
||||
this.selectedRows.push(this.localResults[i]._id);
|
||||
this.selectedRows.push(this.sortedResults[i]._id);
|
||||
}
|
||||
else if (lastIndex < clickedIndex) {
|
||||
for (let i = clickedIndex; i > lastIndex; i--)
|
||||
this.selectedRows.push(this.localResults[i]._id);
|
||||
this.selectedRows.push(this.sortedResults[i]._id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,6 +454,8 @@ export default {
|
||||
sort (field) {
|
||||
if (!this.isSortable) return;
|
||||
|
||||
this.selectedRows = [];
|
||||
|
||||
if (this.mode === 'query')
|
||||
field = `${this.getTable(this.resultsetIndex)}.${field}`;
|
||||
|
||||
@@ -462,7 +483,7 @@ export default {
|
||||
downloadTable (format, filename) {
|
||||
if (!this.sortedResults) return;
|
||||
|
||||
const rows = [...this.sortedResults].map(row => {
|
||||
const rows = JSON.parse(JSON.stringify(this.sortedResults)).map(row => {
|
||||
delete row._id;
|
||||
return row;
|
||||
});
|
||||
|
@@ -3,6 +3,30 @@
|
||||
:context-event="contextEvent"
|
||||
@close-context="closeContext"
|
||||
>
|
||||
<div v-if="selectedRows.length === 1" class="context-element">
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-content-copy text-light pr-1" /> {{ $t('word.copy') }}</span>
|
||||
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
||||
<div class="context-submenu">
|
||||
<div
|
||||
v-if="selectedRows.length === 1"
|
||||
class="context-element"
|
||||
@click="copyCell"
|
||||
>
|
||||
<span class="d-flex">
|
||||
<i class="mdi mdi-18px mdi-numeric-0 mdi-rotate-90 text-light pr-1" /> {{ $tc('word.cell', 1) }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedRows.length === 1"
|
||||
class="context-element"
|
||||
@click="copyRow"
|
||||
>
|
||||
<span class="d-flex">
|
||||
<i class="mdi mdi-18px mdi-table-row text-light pr-1" /> {{ $tc('word.row', 1) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedRows.length === 1"
|
||||
class="context-element"
|
||||
@@ -44,6 +68,14 @@ export default {
|
||||
setNull () {
|
||||
this.$emit('set-null');
|
||||
this.closeContext();
|
||||
},
|
||||
copyCell () {
|
||||
this.$emit('copy-cell');
|
||||
this.closeContext();
|
||||
},
|
||||
copyRow () {
|
||||
this.$emit('copy-row');
|
||||
this.closeContext();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -10,7 +10,7 @@
|
||||
>
|
||||
<template v-if="cKey !== '_id'">
|
||||
<span
|
||||
v-if="!isInlineEditor[cKey]"
|
||||
v-if="!isInlineEditor[cKey] && fields[cKey]"
|
||||
class="cell-content px-2"
|
||||
:class="`${isNull(col)} ${typeClass(fields[cKey].type)}`"
|
||||
@dblclick="editON($event, col, cKey)"
|
||||
@@ -74,7 +74,7 @@
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> {{ $t('word.edit') }} "{{ editingField }}"
|
||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> <span class="cut-text">{{ $t('word.edit') }} "{{ editingField }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
@@ -138,7 +138,8 @@
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> {{ $t('word.edit') }} "{{ editingField }}"
|
||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" />
|
||||
<span class="cut-text">{{ $t('word.edit') }} "{{ editingField }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
@@ -348,7 +349,7 @@ export default {
|
||||
return false;
|
||||
},
|
||||
enumArray () {
|
||||
if (this.fields[this.editingField].enumValues)
|
||||
if (this.fields[this.editingField] && this.fields[this.editingField].enumValues)
|
||||
return this.fields[this.editingField].enumValues.replaceAll('\'', '').split(',');
|
||||
return false;
|
||||
}
|
||||
|
@@ -32,6 +32,44 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button
|
||||
class="btn btn-dark btn-sm mr-0"
|
||||
:disabled="isQuering || page === 1"
|
||||
title="CTRL+ᐊ"
|
||||
@click="pageChange('prev')"
|
||||
>
|
||||
<i class="mdi mdi-24px mdi-skip-previous" />
|
||||
</button>
|
||||
<div class="dropdown" :class="{'active': isPageMenu}">
|
||||
<div @click="openPageMenu">
|
||||
<div class="btn btn-dark btn-sm mr-0 no-radius dropdown-toggle text-bold px-3">
|
||||
{{ page }}
|
||||
</div>
|
||||
<div class="menu px-3">
|
||||
<span>{{ $t('message.pageNumber') }}</span>
|
||||
<input
|
||||
ref="pageSelect"
|
||||
v-model="pageProxy"
|
||||
type="number"
|
||||
min="1"
|
||||
class="form-input"
|
||||
@blur="setPageNumber"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-dark btn-sm mr-0"
|
||||
:disabled="isQuering || (results.length && results[0].rows.length < limit)"
|
||||
title="CTRL+ᐅ"
|
||||
@click="pageChange('next')"
|
||||
>
|
||||
<i class="mdi mdi-24px mdi-skip-next" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="divider-vert py-3" />
|
||||
|
||||
<button
|
||||
v-if="isTable"
|
||||
@@ -43,7 +81,7 @@
|
||||
<i class="mdi mdi-24px mdi-playlist-plus ml-1" />
|
||||
</button>
|
||||
|
||||
<div class="dropdown export-dropdown pr-2">
|
||||
<div class="dropdown table-dropdown pr-2">
|
||||
<button
|
||||
:disabled="isQuering"
|
||||
class="btn btn-dark btn-sm dropdown-toggle mr-0 pr-0"
|
||||
@@ -72,10 +110,10 @@
|
||||
<i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ results[0].duration / 1000 }}s</b>
|
||||
</div>
|
||||
<div v-if="results.length && results[0].rows">
|
||||
{{ $t('word.results') }}: <b>{{ results[0].rows.length.toLocaleString() }}</b>
|
||||
{{ $t('word.results') }}: <b>{{ results[0].rows.length | localeString }}</b>
|
||||
</div>
|
||||
<div v-if="hasApproximately">
|
||||
{{ $t('word.total') }}: <b>{{ tableInfo.rows.toLocaleString() }}</b> <small>({{ $t('word.approximately') }})</small>
|
||||
<div v-if="hasApproximately || (page > 1 && tableInfo.rows)">
|
||||
{{ $t('word.total') }}: <b>{{ tableInfo.rows | localeString }}</b> <small>({{ $t('word.approximately') }})</small>
|
||||
</div>
|
||||
<div v-if="workspace.breadcrumbs.database">
|
||||
{{ $t('word.schema') }}: <b>{{ workspace.breadcrumbs.database }}</b>
|
||||
@@ -134,6 +172,12 @@ export default {
|
||||
ModalNewTableRow,
|
||||
ModalFakerRows
|
||||
},
|
||||
filters: {
|
||||
localeString (val) {
|
||||
if (val)
|
||||
return val.toLocaleString();
|
||||
}
|
||||
},
|
||||
mixins: [tableTabs],
|
||||
props: {
|
||||
connection: Object,
|
||||
@@ -143,19 +187,23 @@ export default {
|
||||
return {
|
||||
tabUid: 'data',
|
||||
isQuering: false,
|
||||
isPageMenu: false,
|
||||
results: [],
|
||||
lastTable: null,
|
||||
isAddModal: false,
|
||||
isFakerModal: false,
|
||||
autorefreshTimer: 0,
|
||||
refreshInterval: null,
|
||||
sortParams: {}
|
||||
sortParams: {},
|
||||
page: 1,
|
||||
pageProxy: 1
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
getWorkspace: 'workspaces/getWorkspace',
|
||||
selectedWorkspace: 'workspaces/getSelected'
|
||||
selectedWorkspace: 'workspaces/getSelected',
|
||||
limit: 'settings/getDataTabLimit'
|
||||
}),
|
||||
workspace () {
|
||||
return this.getWorkspace(this.connection.uid);
|
||||
@@ -184,19 +232,26 @@ export default {
|
||||
return this.results.length &&
|
||||
this.results[0].rows &&
|
||||
this.tableInfo &&
|
||||
this.results[0].rows.length === 1000 &&
|
||||
this.results[0].rows.length === this.limit &&
|
||||
this.results[0].rows.length < this.tableInfo.rows;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
table () {
|
||||
if (this.isSelected) {
|
||||
this.page = 1;
|
||||
this.sortParams = {};
|
||||
this.getTableData();
|
||||
this.lastTable = this.table;
|
||||
this.$refs.queryTable.resetSort();
|
||||
}
|
||||
},
|
||||
page (val, oldVal) {
|
||||
if (val && val > 0 && val !== oldVal) {
|
||||
this.pageProxy = this.page;
|
||||
this.getTableData();
|
||||
}
|
||||
},
|
||||
isSelected (val) {
|
||||
if (val && this.lastTable !== this.table) {
|
||||
this.getTableData();
|
||||
@@ -228,6 +283,8 @@ export default {
|
||||
uid: this.connection.uid,
|
||||
schema: this.schema,
|
||||
table: this.workspace.breadcrumbs.table || this.workspace.breadcrumbs.view,
|
||||
limit: this.limit,
|
||||
page: this.page,
|
||||
sortParams
|
||||
};
|
||||
|
||||
@@ -255,6 +312,33 @@ export default {
|
||||
this.sortParams = sortParams;
|
||||
this.getTableData(sortParams);
|
||||
},
|
||||
openPageMenu () {
|
||||
if (this.isQuering) return;
|
||||
|
||||
this.isPageMenu = true;
|
||||
if (this.isPageMenu)
|
||||
setTimeout(() => this.$refs.pageSelect.focus(), 20);
|
||||
},
|
||||
setPageNumber () {
|
||||
this.isPageMenu = false;
|
||||
|
||||
if (this.pageProxy > 0)
|
||||
this.page = this.pageProxy;
|
||||
else
|
||||
this.pageProxy = this.page;
|
||||
},
|
||||
pageChange (direction) {
|
||||
if (this.isQuering) return;
|
||||
|
||||
if (direction === 'next' && (this.results.length && this.results[0].rows.length === this.limit)) {
|
||||
if (!this.page)
|
||||
this.page = 2;
|
||||
else
|
||||
this.page++;
|
||||
}
|
||||
else if (direction === 'prev' && this.page > 1)
|
||||
this.page--;
|
||||
},
|
||||
showAddModal () {
|
||||
this.isAddModal = true;
|
||||
},
|
||||
@@ -273,6 +357,13 @@ export default {
|
||||
e.stopPropagation();
|
||||
if (e.key === 'F5')
|
||||
this.reloadTable();
|
||||
|
||||
if (e.ctrlKey) {
|
||||
if (e.key === 'ArrowRight')
|
||||
this.pageChange('next');
|
||||
if (e.key === 'ArrowLeft')
|
||||
this.pageChange('prev');
|
||||
}
|
||||
}
|
||||
},
|
||||
setRefreshInterval () {
|
||||
@@ -292,10 +383,3 @@ export default {
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.export-dropdown {
|
||||
.menu {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
379
src/renderer/i18n/de-DE.js
Normal file
379
src/renderer/i18n/de-DE.js
Normal file
@@ -0,0 +1,379 @@
|
||||
module.exports = {
|
||||
word: {
|
||||
edit: 'Bearbeiten',
|
||||
save: 'Speichern',
|
||||
close: 'Schließen',
|
||||
delete: 'Löschen',
|
||||
confirm: 'Bestätigen',
|
||||
cancel: 'Abbrechen',
|
||||
send: 'Senden',
|
||||
connectionName: 'Verbindungsname',
|
||||
client: 'Client',
|
||||
hostName: 'Hostname',
|
||||
port: 'Port',
|
||||
user: 'Nutzer',
|
||||
password: 'Kennwort',
|
||||
credentials: 'Zugangsdaten',
|
||||
connect: 'Verbinden',
|
||||
connected: 'Verbunden',
|
||||
disconnect: 'Trennen',
|
||||
disconnected: 'Getrennt',
|
||||
refresh: 'Aktualisieren',
|
||||
settings: 'Einstellungen',
|
||||
general: 'Allgemein',
|
||||
themes: 'Designs',
|
||||
update: 'Aktualisierung',
|
||||
about: 'Über',
|
||||
language: 'Sprache',
|
||||
version: 'Version',
|
||||
donate: 'Spenden',
|
||||
run: 'Ausführen',
|
||||
schema: 'Schema',
|
||||
results: 'Ergebnisse',
|
||||
size: 'Größe',
|
||||
seconds: 'Sekunden',
|
||||
type: 'Typ',
|
||||
mimeType: 'Mime-Type',
|
||||
download: 'Herunterladen',
|
||||
add: 'Hinzufügen',
|
||||
data: 'Daten',
|
||||
properties: 'Eigenschaften',
|
||||
insert: 'Einfügen',
|
||||
connecting: 'Verbinden',
|
||||
name: 'Name',
|
||||
collation: 'Kollation',
|
||||
clear: 'Leeren',
|
||||
options: 'Optionen',
|
||||
autoRefresh: 'Auto-Aktualisierung',
|
||||
indexes: 'Indizes',
|
||||
foreignKeys: 'Fremdschlüssel',
|
||||
length: 'Länge',
|
||||
unsigned: 'Unsigniert',
|
||||
default: 'Standard',
|
||||
comment: 'Kommentar',
|
||||
key: 'Schlüssel',
|
||||
order: 'Sortierung',
|
||||
expression: 'Ausdruck',
|
||||
autoIncrement: 'Auto Inkrement',
|
||||
engine: 'Engine',
|
||||
field: 'Feld | Felder',
|
||||
approximately: 'Ungefähr',
|
||||
total: 'Gesamt',
|
||||
table: 'Tabelle',
|
||||
discard: 'Verwerfen',
|
||||
stay: 'Warten',
|
||||
author: 'Autor',
|
||||
light: 'Hell',
|
||||
dark: 'Dunkel',
|
||||
autoCompletion: 'Auto-Vervollständigung',
|
||||
application: 'Anwendung',
|
||||
editor: 'Editor',
|
||||
view: 'Ansicht',
|
||||
definer: 'Bestimmer',
|
||||
algorithm: 'Algorithmus',
|
||||
trigger: 'Auslöser',
|
||||
storedRoutine: 'Gespeicherte Routine | Gespeicherte Routinen',
|
||||
scheduler: 'Zeitplaner',
|
||||
event: 'Ereignis',
|
||||
parameters: 'Parameter',
|
||||
function: 'Funktion | Funktionen',
|
||||
deterministic: 'Deterministisch',
|
||||
context: 'Kontext',
|
||||
export: 'Export',
|
||||
returns: 'Rückgabe',
|
||||
timing: 'Dauer',
|
||||
state: 'Stand',
|
||||
execution: 'Ausführung',
|
||||
starts: 'Startet',
|
||||
ends: 'Endet',
|
||||
ssl: 'SSL',
|
||||
privateKey: 'Privater Schlüssel',
|
||||
certificate: 'Zertifikat',
|
||||
caCertificate: 'CA Zertifikat',
|
||||
ciphers: 'Chiffren',
|
||||
upload: 'Hochladen',
|
||||
browse: 'Suchen',
|
||||
faker: 'Faker',
|
||||
content: 'Inhalt',
|
||||
cut: 'Ausschneiden',
|
||||
copy: 'Kopieren',
|
||||
paste: 'Einfügen',
|
||||
tools: 'Tools',
|
||||
variables: 'Variablen',
|
||||
processes: 'Prozesse',
|
||||
database: 'Datenbank',
|
||||
scratchpad: 'Scratchpad',
|
||||
array: 'Array',
|
||||
format: 'Formatierung'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Willkommen im Antares SQL Client!',
|
||||
appFirstStep: 'Dein erster Schritt: Erstelle eine neue Datenbankverbindung.',
|
||||
addConnection: 'Verbindung hinzufügen',
|
||||
createConnection: 'Verbindung erstellen',
|
||||
createNewConnection: 'Neue Verbindung erstellen',
|
||||
askCredentials: 'Frage nach Zugangsdaten',
|
||||
testConnection: 'Verbindung testen',
|
||||
editConnection: 'Verbindung bearbeiten',
|
||||
deleteConnection: 'Verbindung löschen',
|
||||
deleteCorfirm: 'Bestätige den Abbruch von',
|
||||
connectionSuccessfullyMade: 'Verbindung erfolgreich erstellt!',
|
||||
madeWithJS: 'Mit 💛 und JavaScript gemacht!',
|
||||
checkForUpdates: 'Suche nach Aktualisierungen',
|
||||
noUpdatesAvailable: 'Keine Aktualisierungen verfügbar',
|
||||
checkingForUpdate: 'Suche nach Aktualisierungen',
|
||||
checkFailure: 'Suche fehlgeschlagen, bitte versuche es später noch einmal',
|
||||
updateAvailable: 'Aktualisierung verfügbar',
|
||||
downloadingUpdate: 'Aktualisierung wird heruntergeladen',
|
||||
updateDownloaded: 'Aktualisierung heruntergeladen',
|
||||
restartToInstall: 'Starte Antares neu für die Installation',
|
||||
unableEditFieldWithoutPrimary: 'Feld kann ohne Primärschlüssel in Ergebnisliste nicht bearbeitet werden',
|
||||
editCell: 'Zelle bearbeiten',
|
||||
deleteRows: 'Zeile löschen | Lösche {count} Zeilen',
|
||||
confirmToDeleteRows: 'Eine Zeile wirklich löschen? | {count} Zeilen wirklich löschen?',
|
||||
notificationsTimeout: 'Timeout für Benachrichtigungen',
|
||||
uploadFile: 'Datei hochladen',
|
||||
addNewRow: 'Neue Zeile hinzufügen',
|
||||
numberOfInserts: 'Anzahl der eingefügten Zeilen',
|
||||
openNewTab: 'Öffne einen neuen Tab',
|
||||
affectedRows: 'Betroffene Zeilen',
|
||||
createNewDatabase: 'Erstelle ein neue Datenbank',
|
||||
databaseName: 'Datenbankname',
|
||||
serverDefault: 'Server default',
|
||||
deleteDatabase: 'Datenbank löschen',
|
||||
editDatabase: 'Datenbank bearbeiten',
|
||||
clearChanges: 'Änderungen leeren',
|
||||
addNewField: 'Neues Feld hinzufügen',
|
||||
manageIndexes: 'Indizes verwalten',
|
||||
manageForeignKeys: 'Fremdschlüssel verwalten',
|
||||
allowNull: 'Erlaube NULL',
|
||||
zeroFill: 'Nullauffüllung',
|
||||
customValue: 'Spezifischer Wert',
|
||||
onUpdate: 'Bei Aktualisierung',
|
||||
deleteField: 'Feld löschen',
|
||||
createNewIndex: 'Neuen Index erstellen',
|
||||
addToIndex: 'Zum Index hinzufügen',
|
||||
createNewTable: 'Neue Tabelle erstellen',
|
||||
emptyTable: 'Tabelle leeren',
|
||||
deleteTable: 'Tabelle löschen',
|
||||
emptyCorfirm: 'Wirklich leeren?',
|
||||
unsavedChanges: 'Ungespeicherte Änderungen',
|
||||
discardUnsavedChanges: 'Du hast ungespeicherte Änderungen. Wenn du den Tab verlässt, werden diese Änderungen verworfen.',
|
||||
thereAreNoIndexes: 'Es gibt keine Indizes',
|
||||
thereAreNoForeign: 'Es gibt keine Fremdschlüssel',
|
||||
createNewForeign: 'Neuen Fremdschlüssel erstellen',
|
||||
referenceTable: 'Referenztabelle',
|
||||
referenceField: 'Referenzfeld',
|
||||
foreignFields: 'Fremdfelder',
|
||||
invalidDefault: 'Ungültiger Standard',
|
||||
onDelete: 'Bei Löschung',
|
||||
applicationTheme: 'Anwendungsdesign',
|
||||
editorTheme: 'Editordesign',
|
||||
wrapLongLines: 'Lange Zeilen umbrechen',
|
||||
selectStatement: 'Select-Anweisung',
|
||||
triggerStatement: 'Trigger-Anweisung',
|
||||
sqlSecurity: 'SQL-Sicherheit',
|
||||
updateOption: 'Aktualisierungsoption',
|
||||
deleteView: 'View löschen',
|
||||
createNewView: 'Neue View erstellen',
|
||||
deleteTrigger: 'Trigger löschen',
|
||||
createNewTrigger: 'Neuen Trigger erstellen',
|
||||
currentUser: 'Aktueller Nutzer',
|
||||
routineBody: 'Routineninhalt',
|
||||
dataAccess: 'Datenzugriff',
|
||||
thereAreNoParameters: 'Es gibt keine Parameter',
|
||||
createNewParameter: 'Neue Parameter erstellen',
|
||||
createNewRoutine: 'Neue Routine erstellen',
|
||||
deleteRoutine: 'Routine löschen',
|
||||
functionBody: 'Funktionsinhalt',
|
||||
createNewFunction: 'Neue Funktion erstellen',
|
||||
deleteFunction: 'Funktion löschen',
|
||||
schedulerBody: 'Zeitplaner-Inhalt',
|
||||
createNewScheduler: 'Neuen Zeitplaner erstellen',
|
||||
deleteScheduler: 'Zeitplaner löschen',
|
||||
preserveOnCompletion: 'Bei Vervollständigung erhalten',
|
||||
enableSsl: 'Aktiviere SSL',
|
||||
manualValue: 'Manueller Wert',
|
||||
tableFiller: 'Tabellenfüller',
|
||||
fakeDataLanguage: 'Fingierte Datensprache',
|
||||
searchForElements: 'Suche nach Elemente',
|
||||
selectAll: 'Alle auswählen',
|
||||
queryDuration: 'Dauer der Abfrage',
|
||||
includeBetaUpdates: 'Beta-Aktualisierungen berücksichtigen',
|
||||
setNull: 'Setze NULL',
|
||||
processesList: 'Prozessliste',
|
||||
processInfo: 'Prozessinformationen',
|
||||
manageUsers: 'Benutzer verwalten',
|
||||
createNewSchema: 'Neues Schema erstellen',
|
||||
schemaName: 'Schemaname',
|
||||
editSchema: 'Schema bearbeiten',
|
||||
deleteSchema: 'Schema löschen',
|
||||
markdownSupported: 'Unterstützt Markdown',
|
||||
plantATree: 'Pflanze einen Baum',
|
||||
dataTabPageSize: 'Einträge pro Tab / Seite'
|
||||
},
|
||||
faker: {
|
||||
address: 'Adresse',
|
||||
commerce: 'Handel',
|
||||
company: 'Firma',
|
||||
database: 'Datenbank',
|
||||
date: 'Datum',
|
||||
finance: 'Finanzen',
|
||||
git: 'Git',
|
||||
hacker: 'Hacker',
|
||||
internet: 'Internet',
|
||||
lorem: 'Lorem',
|
||||
name: 'Name',
|
||||
music: 'Musik',
|
||||
phone: 'Telefon',
|
||||
random: 'Zufällig',
|
||||
system: 'System',
|
||||
time: 'Zeit',
|
||||
vehicle: 'Fahrzeug',
|
||||
zipCode: 'Postleitzahl',
|
||||
zipCodeByState: 'Postleitzahl nach Stadt',
|
||||
city: 'Stadt',
|
||||
cityPrefix: 'Stadtpräfix',
|
||||
citySuffix: 'Stadtzusatz',
|
||||
streetName: 'Straßenname',
|
||||
streetAddress: 'Anschrift',
|
||||
streetSuffix: 'Straßenzusatz',
|
||||
streetPrefix: 'Straßenpräfix',
|
||||
secondaryAddress: 'Zweite Anschrift',
|
||||
county: 'Landkreis',
|
||||
country: 'Land',
|
||||
countryCode: 'Ländercode',
|
||||
state: 'Bundesland',
|
||||
stateAbbr: 'Bundeslandkürzel',
|
||||
latitude: 'Breitengrad',
|
||||
longitude: 'Längengrad',
|
||||
direction: 'Richtung',
|
||||
cardinalDirection: 'Himmelsrichtung',
|
||||
ordinalDirection: 'Nebenhimmelsrichtung',
|
||||
nearbyGPSCoordinate: 'Nächste GPS-Koordinate',
|
||||
timeZone: 'Zeitzone',
|
||||
color: 'Farbe',
|
||||
department: 'Abteilung',
|
||||
productName: 'Produktname',
|
||||
price: 'Preis',
|
||||
productAdjective: 'Produkteigenschaft',
|
||||
productMaterial: 'Produktmaterial',
|
||||
product: 'Produkt',
|
||||
productDescription: 'Produktbeschreibung',
|
||||
suffixes: 'Zusätze',
|
||||
companyName: 'Firmenname',
|
||||
companySuffix: 'Firmenzusatz',
|
||||
catchPhrase: 'Slogan',
|
||||
bs: 'BS',
|
||||
catchPhraseAdjective: 'Sloganeigenschaft',
|
||||
catchPhraseDescriptor: 'Sloganbeschreibung',
|
||||
catchPhraseNoun: 'Slogannomen',
|
||||
bsAdjective: 'BS-Eigenschaft',
|
||||
bsBuzz: 'BS-Klatsch',
|
||||
bsNoun: 'BS-Nomen',
|
||||
column: 'Spalte',
|
||||
type: 'Typ',
|
||||
collation: 'Kollation',
|
||||
engine: 'Engine',
|
||||
past: 'Vergangenheit',
|
||||
future: 'Zukunft',
|
||||
between: 'Zwischen',
|
||||
recent: 'Kürzlich',
|
||||
soon: 'Bald',
|
||||
month: 'Monat',
|
||||
weekday: 'Wochentag',
|
||||
account: 'Konto',
|
||||
accountName: 'Kontoname',
|
||||
routingNumber: 'Bankleitzahl',
|
||||
mask: 'Maske',
|
||||
amount: 'Wert',
|
||||
transactionType: 'Vorgangstyp',
|
||||
currencyCode: 'Währungscode',
|
||||
currencyName: 'Währungsname',
|
||||
currencySymbol: 'Währungssymbol',
|
||||
bitcoinAddress: 'Bitcoin-Adresse',
|
||||
litecoinAddress: 'Litecoin-Adresse',
|
||||
creditCardNumber: 'Kreditkartennummer',
|
||||
creditCardCVV: 'Kartenprüfnummer',
|
||||
ethereumAddress: 'Ethereum-Adresse',
|
||||
iban: 'IBAN',
|
||||
bic: 'BIC',
|
||||
transactionDescription: 'Vorgangsbeschreibung',
|
||||
branch: 'Zweig',
|
||||
commitEntry: 'Commit-Eintrag',
|
||||
commitMessage: 'Commit-Nachricht',
|
||||
commitSha: 'Commit-SHA',
|
||||
shortSha: 'Kurzer SHA',
|
||||
abbreviation: 'Kürzel',
|
||||
adjective: 'Adjektiv',
|
||||
noun: 'Nomen',
|
||||
verb: 'Verb',
|
||||
ingverb: 'Ingverb',
|
||||
phrase: 'Phrase',
|
||||
avatar: 'Avatar',
|
||||
email: 'E-Mail',
|
||||
exampleEmail: 'Beispiel-E-Mail',
|
||||
userName: 'Benutzername',
|
||||
protocol: 'Protokoll',
|
||||
url: 'Url',
|
||||
domainName: 'Domainname',
|
||||
domainSuffix: 'Domainzusatz',
|
||||
domainWord: 'Domainwort',
|
||||
ip: 'IP',
|
||||
ipv6: 'IPv6',
|
||||
userAgent: 'Browserkennung',
|
||||
mac: 'Mac',
|
||||
password: 'Kennwort',
|
||||
word: 'Wort',
|
||||
words: 'Wörter',
|
||||
sentence: 'Satz',
|
||||
slug: 'Slug',
|
||||
sentences: 'Sätze',
|
||||
paragraph: 'Paragraph',
|
||||
paragraphs: 'Paragraphen',
|
||||
text: 'Text',
|
||||
lines: 'Zeilen',
|
||||
genre: 'Genre',
|
||||
firstName: 'Vorname',
|
||||
lastName: 'Nachname',
|
||||
middleName: 'Zweitname',
|
||||
findName: 'Vollständiger Name',
|
||||
jobTitle: 'Berufsbezeichnung',
|
||||
gender: 'Geschlecht',
|
||||
prefix: 'Präfix',
|
||||
suffix: 'Zusatz',
|
||||
title: 'Titel',
|
||||
jobDescriptor: 'Berufsbeschreibung',
|
||||
jobArea: 'Berufsfeld',
|
||||
jobType: 'Anstellungsart',
|
||||
phoneNumber: 'Telefonnummer',
|
||||
phoneNumberFormat: 'Telefonnummerformat',
|
||||
phoneFormats: 'Telefonnummerformate',
|
||||
number: 'Nummer',
|
||||
float: 'Gleitkommazahl',
|
||||
arrayElement: 'Array-Element',
|
||||
arrayElements: 'Array-Elemente',
|
||||
objectElement: 'Object-Element',
|
||||
uuid: 'Uuid',
|
||||
boolean: 'Boolean',
|
||||
image: 'Grafik',
|
||||
locale: 'Sprachumgebung',
|
||||
alpha: 'Alpha',
|
||||
alphaNumeric: 'Alphanumerisch',
|
||||
hexaDecimal: 'Hexadezimal',
|
||||
fileName: 'Dateiname',
|
||||
commonFileName: 'Allgemeiner Dateiname',
|
||||
mimeType: 'Mimetype',
|
||||
commonFileType: 'Allgemeiner Dateityp',
|
||||
commonFileExt: 'Allgemeine Dateiendung',
|
||||
fileType: 'Dateityp',
|
||||
fileExt: 'Dateiendung',
|
||||
directoryPath: 'Verzeichnispfad',
|
||||
filePath: 'Dateipfad',
|
||||
semver: 'Semver',
|
||||
manufacturer: 'Hersteller',
|
||||
model: 'Modell',
|
||||
fuel: 'Treibstoff',
|
||||
vin: 'Wein'
|
||||
}
|
||||
};
|
@@ -105,7 +105,14 @@ module.exports = {
|
||||
scratchpad: 'Scratchpad',
|
||||
array: 'Array',
|
||||
changelog: 'Changelog',
|
||||
format: 'Format'
|
||||
format: 'Format',
|
||||
structure: 'Structure',
|
||||
small: 'Small',
|
||||
medium: 'Medium',
|
||||
large: 'Large',
|
||||
row: 'Row | Rows',
|
||||
cell: 'Cell | Cells',
|
||||
triggerFunction: 'Trigger function | Trigger functions'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Welcome to Antares SQL Client!',
|
||||
@@ -210,7 +217,10 @@ module.exports = {
|
||||
editSchema: 'Edit schema',
|
||||
deleteSchema: 'Delete schema',
|
||||
markdownSupported: 'Markdown supported',
|
||||
plantATree: 'Plant a Tree'
|
||||
plantATree: 'Plant a Tree',
|
||||
dataTabPageSize: 'DATA tab page size',
|
||||
pageNumber: 'Page number',
|
||||
duplicateTable: 'Duplicate table'
|
||||
},
|
||||
faker: {
|
||||
address: 'Address',
|
||||
|
@@ -10,7 +10,8 @@ const i18n = new VueI18n({
|
||||
'ar-SA': require('./ar-SA'),
|
||||
'es-ES': require('./es-ES'),
|
||||
'fr-FR': require('./fr-FR'),
|
||||
'pt-BR': require('./pt-BR')
|
||||
'pt-BR': require('./pt-BR'),
|
||||
'de-DE': require('./de-DE')
|
||||
}
|
||||
});
|
||||
export default i18n;
|
||||
|
@@ -4,5 +4,6 @@ export default {
|
||||
'ar-SA': 'العربية',
|
||||
'es-ES': 'Español',
|
||||
'fr-FR': 'Français',
|
||||
'pt-BR': 'Português (Brasil)'
|
||||
'pt-BR': 'Português (Brasil)',
|
||||
'de-DE': 'Deutsch (Deutschland)'
|
||||
};
|
||||
|
@@ -14,7 +14,15 @@ export default class {
|
||||
return ipcRenderer.invoke('alter-function', params);
|
||||
}
|
||||
|
||||
static alterTriggerFunction (params) {
|
||||
return ipcRenderer.invoke('alter-trigger-function', params);
|
||||
}
|
||||
|
||||
static createFunction (params) {
|
||||
return ipcRenderer.invoke('create-function', params);
|
||||
}
|
||||
|
||||
static createTriggerFunction (params) {
|
||||
return ipcRenderer.invoke('create-trigger-function', params);
|
||||
}
|
||||
}
|
||||
|
@@ -46,6 +46,10 @@ export default class {
|
||||
return ipcRenderer.invoke('alter-table', params);
|
||||
}
|
||||
|
||||
static duplicateTable (params) {
|
||||
return ipcRenderer.invoke('duplicate-table', params);
|
||||
}
|
||||
|
||||
static truncateTable (params) {
|
||||
return ipcRenderer.invoke('truncate-table', params);
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ $enum-color: goldenrod;
|
||||
$unknown-color: gray;
|
||||
|
||||
/* Sizes */
|
||||
$border-radius: 0.3rem;
|
||||
$titlebar-height: 1.5rem;
|
||||
$settingbar-width: 3rem;
|
||||
$explorebar-width: 14rem;
|
||||
|
@@ -34,12 +34,22 @@ body {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.no-radius {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
.no-border {
|
||||
outline: none !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.cut-text {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.workspace-tabs {
|
||||
align-content: baseline;
|
||||
|
||||
@@ -103,7 +113,7 @@ body {
|
||||
.modal-container,
|
||||
.modal-sm .modal-container {
|
||||
padding: 0;
|
||||
border-radius: 3px;
|
||||
border-radius: $border-radius;
|
||||
|
||||
.modal-header {
|
||||
padding: 0.4rem 0.8rem;
|
||||
@@ -111,13 +121,46 @@ body {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-radius: 3px 3px 0 0;
|
||||
border-radius: $border-radius $border-radius 0 0;
|
||||
|
||||
.modal-title {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab {
|
||||
.tab-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.tab-link {
|
||||
min-width: 0;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.tab-link {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
&::after {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
height: 2px;
|
||||
width: 0;
|
||||
transition: width 0.2s;
|
||||
background-color: $primary-color;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.btn-clear {
|
||||
margin-top: -0.1rem;
|
||||
font-size: 0.6rem;
|
||||
@@ -185,9 +228,47 @@ body {
|
||||
max-height: 5000rem !important;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.btn.loading {
|
||||
> .mdi,
|
||||
> span {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.table-dropdown {
|
||||
.menu {
|
||||
min-width: 100%;
|
||||
padding: 0;
|
||||
|
||||
.menu-item {
|
||||
padding: 0;
|
||||
|
||||
> a {
|
||||
margin: 0.2rem;
|
||||
padding: 0.1rem 0.3rem;
|
||||
|
||||
&:hover {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ace Editor
|
||||
.ace_editor {
|
||||
&.ace_autocomplete {
|
||||
border-radius: $border-radius;
|
||||
|
||||
.ace_marker-layer {
|
||||
.ace_active-line,
|
||||
.ace_line-hover {
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -58,6 +58,10 @@
|
||||
&:hover {
|
||||
background: $bg-color-gray;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,10 +215,8 @@
|
||||
}
|
||||
|
||||
.bg-checkered {
|
||||
background-image:
|
||||
linear-gradient(to right, rgba(192, 192, 192, 0.75), rgba(192, 192, 192, 0.75)),
|
||||
linear-gradient(to right, black 50%, white 50%),
|
||||
linear-gradient(to bottom, black 50%, white 50%);
|
||||
background-image: linear-gradient(to right, rgba(192, 192, 192, 0.75), rgba(192, 192, 192, 0.75)),
|
||||
linear-gradient(to right, black 50%, white 50%), linear-gradient(to bottom, black 50%, white 50%);
|
||||
background-blend-mode: normal, difference, normal;
|
||||
background-size: 2em 2em;
|
||||
}
|
||||
@@ -233,7 +235,7 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $primary-color;
|
||||
background: rgba($light-color, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -264,7 +266,7 @@
|
||||
}
|
||||
|
||||
.editor-col {
|
||||
border-left: 2px solid $bg-color-light-dark;
|
||||
border-left: 0.05rem solid rgba($bg-color-light-dark, 60%);
|
||||
}
|
||||
|
||||
.table {
|
||||
@@ -361,26 +363,6 @@
|
||||
margin: 0;
|
||||
|
||||
.settingbar-element {
|
||||
height: $settingbar-width;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border-left: 3px solid transparent;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-left-color: $body-font-color-dark;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.settingbar-element-icon {
|
||||
&.badge::after {
|
||||
bottom: -10px;
|
||||
|
@@ -62,6 +62,10 @@
|
||||
&:hover {
|
||||
background: $bg-color-gray;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +98,7 @@
|
||||
}
|
||||
|
||||
.editor-col {
|
||||
border-left: 2px solid darken($bg-color-light-gray, 15%);
|
||||
border-left: 0.05rem solid darken($bg-color-light-gray, 15%);
|
||||
}
|
||||
|
||||
.file-uploader {
|
||||
@@ -143,26 +147,6 @@
|
||||
margin: 0;
|
||||
|
||||
.settingbar-element {
|
||||
height: $settingbar-width;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border-left: 3px solid transparent;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-left-color: $body-font-color-dark;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.settingbar-element-icon {
|
||||
&.badge::after {
|
||||
bottom: -10px;
|
||||
@@ -235,7 +219,7 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $primary-color;
|
||||
background: rgba($light-color, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,20 +11,24 @@ export default {
|
||||
allow_prerelease: persistentStore.get('allow_prerelease', true),
|
||||
explorebar_size: persistentStore.get('explorebar_size', null),
|
||||
notifications_timeout: persistentStore.get('notifications_timeout', 5),
|
||||
data_tab_limit: persistentStore.get('data_tab_limit', 1000),
|
||||
auto_complete: persistentStore.get('auto_complete', true),
|
||||
line_wrap: persistentStore.get('line_wrap', true),
|
||||
application_theme: persistentStore.get('application_theme', 'dark'),
|
||||
editor_theme: persistentStore.get('editor_theme', 'twilight')
|
||||
editor_theme: persistentStore.get('editor_theme', 'twilight'),
|
||||
editor_font_size: persistentStore.get('editor_font_size', 'medium')
|
||||
},
|
||||
getters: {
|
||||
getLocale: state => state.locale,
|
||||
getDataTabLimit: state => state.data_tab_limit,
|
||||
getAllowPrerelease: state => state.allow_prerelease,
|
||||
getExplorebarSize: state => state.explorebar_size,
|
||||
getNotificationsTimeout: state => state.notifications_timeout,
|
||||
getAutoComplete: state => state.auto_complete,
|
||||
getLineWrap: state => state.line_wrap,
|
||||
getApplicationTheme: state => state.application_theme,
|
||||
getEditorTheme: state => state.editor_theme
|
||||
getEditorTheme: state => state.editor_theme,
|
||||
getEditorFontSize: state => state.editor_font_size
|
||||
},
|
||||
mutations: {
|
||||
SET_LOCALE (state, locale) {
|
||||
@@ -32,6 +36,10 @@ export default {
|
||||
i18n.locale = locale;
|
||||
persistentStore.set('locale', state.locale);
|
||||
},
|
||||
SET_DATA_TAB_LIMIT (state, limit) {
|
||||
state.data_tab_limit = limit;
|
||||
persistentStore.set('data_tab_limit', state.data_tab_limit);
|
||||
},
|
||||
SET_ALLOW_PRERELEASE (state, allow) {
|
||||
state.allow_prerelease = allow;
|
||||
persistentStore.set('allow_prerelease', state.allow_prerelease);
|
||||
@@ -59,12 +67,19 @@ export default {
|
||||
SET_EDITOR_THEME (state, theme) {
|
||||
state.editor_theme = theme;
|
||||
persistentStore.set('editor_theme', state.editor_theme);
|
||||
},
|
||||
SET_EDITOR_FONT_SIZE (state, size) {
|
||||
state.editor_font_size = size;
|
||||
persistentStore.set('editor_font_size', state.editor_font_size);
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
changeLocale ({ commit }, locale) {
|
||||
commit('SET_LOCALE', locale);
|
||||
},
|
||||
changePageSize ({ commit }, limit) {
|
||||
commit('SET_DATA_TAB_LIMIT', limit);
|
||||
},
|
||||
changeAllowPrerelease ({ commit }, allow) {
|
||||
commit('SET_ALLOW_PRERELEASE', allow);
|
||||
},
|
||||
@@ -85,6 +100,9 @@ export default {
|
||||
},
|
||||
changeEditorTheme ({ commit }, theme) {
|
||||
commit('SET_EDITOR_THEME', theme);
|
||||
},
|
||||
changeEditorFontSize ({ commit }, size) {
|
||||
commit('SET_EDITOR_FONT_SIZE', size);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user