Merge branch 'master' of https://github.com/Fabio286/antares into pr/toriphes/129
@ -1,5 +1,5 @@
|
||||
{
|
||||
"projectName": "Antares",
|
||||
"projectName": "antares",
|
||||
"projectOwner": "Fabio286",
|
||||
"repoType": "github",
|
||||
"repoHost": "https://github.com",
|
||||
@ -111,7 +111,35 @@
|
||||
"contributions": [
|
||||
"platform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kilianstallz",
|
||||
"name": "Kilian Stallinger",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5290318?v=4",
|
||||
"profile": "https://kilianstallinger.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "wenj91",
|
||||
"name": "文杰",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12549338?v=4",
|
||||
"profile": "https://github.com/wenj91",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "goYou",
|
||||
"name": "goYou",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/62732795?v=4",
|
||||
"profile": "https://github.com/goYou",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7
|
||||
"contributorsPerLine": 7,
|
||||
"skipCi": true
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/node_modules
|
||||
/assets/vendor
|
||||
/out
|
||||
/dist
|
||||
node_modules
|
||||
assets
|
||||
out
|
||||
dist
|
@ -45,6 +45,7 @@
|
||||
"no-console": "off",
|
||||
"no-undef": "off",
|
||||
"vue/no-side-effects-in-computed-properties": "off",
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/require-default-prop": "off",
|
||||
"vue/comment-directive": "off",
|
||||
"vue/no-v-html": "off",
|
||||
@ -61,10 +62,11 @@
|
||||
"vue/max-attributes-per-line": [
|
||||
"error",
|
||||
{
|
||||
"singleline": 2,
|
||||
"singleline": {
|
||||
"max": 2
|
||||
},
|
||||
"multiline": {
|
||||
"max": 1,
|
||||
"allowFirstLine": false
|
||||
"max": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
|
8
.github/workflows/build-linux.yml
vendored
@ -12,12 +12,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test
|
||||
|
||||
- name: Build/release Electron app
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
|
8
.github/workflows/build-mac.yml
vendored
@ -12,12 +12,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test
|
||||
|
||||
- name: Build/release Electron app
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
|
8
.github/workflows/build-win.yml
vendored
@ -12,13 +12,19 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test
|
||||
|
||||
- name: Build/release Electron app
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
with:
|
||||
|
4
.vscode/settings.json
vendored
@ -3,7 +3,9 @@
|
||||
"UI",
|
||||
"core",
|
||||
"MySQL",
|
||||
"PostgreSQL"
|
||||
"PostgreSQL",
|
||||
"SQLite",
|
||||
"Windows"
|
||||
],
|
||||
"svg.preview.background": "transparent"
|
||||
}
|
122
CHANGELOG.md
@ -2,6 +2,128 @@
|
||||
|
||||
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.4.3](https://github.com/Fabio286/antares/compare/v0.4.2...v0.4.3) (2022-01-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add Simplified Chinese translation ([6ef565c](https://github.com/Fabio286/antares/commit/6ef565cf078cb3f5b7bcdc226894cddeb6239db9))
|
||||
* **MySQL:** spatial fields support ([#165](https://github.com/Fabio286/antares/issues/165)) ([48ebf23](https://github.com/Fabio286/antares/commit/48ebf23bd1574408f429f2e1200ce878352007f6))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* cell copy returns "undefined" in some conditions, closes [#170](https://github.com/Fabio286/antares/issues/170) ([8fb1f08](https://github.com/Fabio286/antares/commit/8fb1f0803efd9df0b66521e73bb6e1a229cf9691))
|
||||
* indexes and foreign keys not cleared after deletion of related field, closes [#182](https://github.com/Fabio286/antares/issues/182) ([9f033fb](https://github.com/Fabio286/antares/commit/9f033fb994916b4fb165e81e55e86127ca817791))
|
||||
* **PostgreSQL:** schema different than public not automatically selected, closes [#172](https://github.com/Fabio286/antares/issues/172) ([46b45c8](https://github.com/Fabio286/antares/commit/46b45c8ab64fb6837a532c4f8342167e4fd794bb))
|
||||
* scale on numeric fields that doesn't support it ([0cfd793](https://github.com/Fabio286/antares/commit/0cfd7938ee7d607dbad66ae452d0200223a6bab2))
|
||||
* **Windows:** temporary fix to Windows 7 style frame on app startup, closes [#169](https://github.com/Fabio286/antares/issues/169) ([1356011](https://github.com/Fabio286/antares/commit/1356011ba3b7dd72e12cb252a8787ce48a364fd4))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* support of scale in field's length setting ([eef7c1d](https://github.com/Fabio286/antares/commit/eef7c1dcecc6593ab0e69ed678187a57fe0a4fb6))
|
||||
|
||||
### [0.4.2](https://github.com/Fabio286/antares/compare/v0.4.1...v0.4.2) (2022-01-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **MySQL:** ability to cancel queries ([a59f77f](https://github.com/Fabio286/antares/commit/a59f77f618aea6156fc80fb832d3efcb9848411f))
|
||||
* **PostgreSQL:** ability to cancel queries ([0c00291](https://github.com/Fabio286/antares/commit/0c002918eb0226f6b3f21ed62117495f86396fb1))
|
||||
* save window state ([8f9385d](https://github.com/Fabio286/antares/commit/8f9385d50815635d091758ecd5d00884e3297ca0))
|
||||
* **UI:** textarea autofocus selecting a query tab, closes [#166](https://github.com/Fabio286/antares/issues/166) ([b4545b1](https://github.com/Fabio286/antares/commit/b4545b178f795712c781a3f4fc35eec31b5ad902))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SQLite:** exception with some fields ([e7a1858](https://github.com/Fabio286/antares/commit/e7a18580915e7739bfa97948c6a0c4fc90a7e78a))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* hash for foreign key default names ([48c3e6a](https://github.com/Fabio286/antares/commit/48c3e6afc43c51f70a16703f1a71194f43da7a3e))
|
||||
* **MySQL:** support to ANSI_QUOTES sql_mode, closes [#158](https://github.com/Fabio286/antares/issues/158) ([d9a3eab](https://github.com/Fabio286/antares/commit/d9a3eab015302e9f23112f659658073ab3242191))
|
||||
|
||||
### [0.4.1](https://github.com/Fabio286/antares/compare/v0.4.0...v0.4.1) (2021-12-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* language format detection for text fields ([a5fdcc1](https://github.com/Fabio286/antares/commit/a5fdcc1a85aa188ff1b9a15b1a768aced026f360))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* cell disappear on edit in one column tables ([aaa5549](https://github.com/Fabio286/antares/commit/aaa5549609664665bd4513632d621cb249b379c1))
|
||||
* false positive with Windows Defender ([992a033](https://github.com/Fabio286/antares/commit/992a033cb2bede3d1eb52e19482d810f6692de1e))
|
||||
* **MySQL:** wrong datetime fields default in table filler in some cases ([8da0224](https://github.com/Fabio286/antares/commit/8da022487650039b7f34a9c86a7bd9045eba65e2))
|
||||
* **MySQL:** wrong value for fields "on update" in some conditions ([359e14a](https://github.com/Fabio286/antares/commit/359e14a9ebd48f86069ba7762fe00a7056f52d47))
|
||||
* select all rows with ctrl+a when editing a cell ([35cb7e1](https://github.com/Fabio286/antares/commit/35cb7e1dc48d3a74e9d106cb1a37f454c1b4a4d1))
|
||||
* **SQLite:** update rows with a text primary key ([d7f1aa9](https://github.com/Fabio286/antares/commit/d7f1aa97af32a4c51fc7022498bd47e15fa08430))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* **UI:** avoid columns size change when editing cells or scrolling results ([813aa32](https://github.com/Fabio286/antares/commit/813aa320d9ab799efea38a7110b7c0bdf7549123))
|
||||
* **UI:** disable save button in table creation when no fields are added ([e8af2d2](https://github.com/Fabio286/antares/commit/e8af2d24a869f7667c069936648808952d2062ab))
|
||||
|
||||
## [0.4.0](https://github.com/Fabio286/antares/compare/v0.3.9...v0.4.0) (2021-11-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **MySQL:** read-only mode ([4437d44](https://github.com/Fabio286/antares/commit/4437d44486c4f20b0bec4bf89d56016b08e36e79))
|
||||
* **PostgreSQL:** read-only mode ([5d48fe0](https://github.com/Fabio286/antares/commit/5d48fe08c77755ed18b3f7a9ea834268e317e7ef))
|
||||
* **SQLite:** cell update in data tabs ([604b371](https://github.com/Fabio286/antares/commit/604b3719204f7473ce4846624f08f8be9eec8b8f))
|
||||
* **SQLite:** connection add/edit masks ([c54438d](https://github.com/Fabio286/antares/commit/c54438d6d3bad38bc76dfcd61f58929fe30279cb))
|
||||
* **SQLite:** keys support ([fd321be](https://github.com/Fabio286/antares/commit/fd321beece075d3ad23fdd8541f9beb5727045a5))
|
||||
* **SQLite:** readonly mode ([3fc227d](https://github.com/Fabio286/antares/commit/3fc227d2de53aae115226ad3c965bfb6e9f3eca6))
|
||||
* **SQLite:** table data visualization ([f2fcc98](https://github.com/Fabio286/antares/commit/f2fcc9883972402eab4d51ef2a9796638dde2d3d))
|
||||
* **SQLite:** tables management ([3efeb45](https://github.com/Fabio286/antares/commit/3efeb45c460f178b794de72367f8d542fd8ddd56))
|
||||
* **SQLite:** triggers management ([f40e9c5](https://github.com/Fabio286/antares/commit/f40e9c592eeffd204aba21a0a0767a0c523fca49))
|
||||
* **SQLite:** views management ([7671c58](https://github.com/Fabio286/antares/commit/7671c585f5f8049bd863db190d4fc60d8f0c6c66))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SQLite:** hide schema creation ([98165ca](https://github.com/Fabio286/antares/commit/98165cacaa158c85ead0490d3caf579e2a17319f))
|
||||
* **UI:** hide tools menu if no tools available ([da1947e](https://github.com/Fabio286/antares/commit/da1947e4efa7f0a26d6a231fadf750be055fbdd5))
|
||||
* **UI:** notifications timeout anomalies ([cc99491](https://github.com/Fabio286/antares/commit/cc99491fe4a15812368f6c928b8c7801d7b255aa))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* **SQLite:** improvements in data visualization ([94c899e](https://github.com/Fabio286/antares/commit/94c899eb8288b41a5962ac3d24365227e1f9f485))
|
||||
* **SQLite:** improvements in field length detection ([93b4a70](https://github.com/Fabio286/antares/commit/93b4a7063beeb5a7001cb06a74f05b23105212f5))
|
||||
* update italian traslation ([9fe3680](https://github.com/Fabio286/antares/commit/9fe3680bbb17c192cffa85348e68794ab49beb81))
|
||||
|
||||
### [0.3.9](https://github.com/Fabio286/antares/compare/v0.3.8...v0.3.9) (2021-11-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* added macos basic shortcusts and menu ([430490a](https://github.com/Fabio286/antares/commit/430490ad93f3148962ced1f13a5330c79cd86b3b))
|
||||
* **MySQL:** enable/disable schedulers from contextual menu ([5ca3a22](https://github.com/Fabio286/antares/commit/5ca3a22dc538b27a4bf6402f1288c4b9f5bc5a90))
|
||||
* **MySQL:** scheduler status indicator in explore bar ([5c66824](https://github.com/Fabio286/antares/commit/5c668249cf102cd9d601f9f7b4943c7155775217))
|
||||
* **PostgreSQL:** enable/disable triggers from contextual menu ([534659f](https://github.com/Fabio286/antares/commit/534659f9aee12eb5ac477f91bfe5d764387dc17e))
|
||||
* schema size in explore bar ([fd25f88](https://github.com/Fabio286/antares/commit/fd25f881f95779709156cbad93a41d6b391f1a45))
|
||||
* **UI:** double click on the title bar will toggle window fullscreen size ([a35566f](https://github.com/Fabio286/antares/commit/a35566f273322602abe434b8bd30817ba8885900))
|
||||
* **UI:** improved topbar look&feel on MacOS ([7657d05](https://github.com/Fabio286/antares/commit/7657d05edfbeaed6a14eb337fc562da5126e6ba0))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* copy&paste and basic usability on macOS ([1ddf8f0](https://github.com/Fabio286/antares/commit/1ddf8f0dbe22f94d6bffddf70636706d2d142ecf))
|
||||
* **PostgreSQL:** bigint fetched as string instead of number, closes [#134](https://github.com/Fabio286/antares/issues/134) ([39b9a59](https://github.com/Fabio286/antares/commit/39b9a59143b457a96f0711a3b8588c92dd80e28d))
|
||||
* row selection problems after a deletion fail, closes [#128](https://github.com/Fabio286/antares/issues/128) ([89fdd21](https://github.com/Fabio286/antares/commit/89fdd210ca48fc9ae399b195ea796c8523619627))
|
||||
* temporary solution on MacOS for unsigned app updates ([c00fd13](https://github.com/Fabio286/antares/commit/c00fd1381f451ba7aace7047b28b904ddcaf18f0))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* **UI:** improved function and routine parameters modals ([d19f475](https://github.com/Fabio286/antares/commit/d19f475fc28c0367ada569cb634769fa618b48b4))
|
||||
|
||||
### [0.3.8](https://github.com/Fabio286/antares/compare/v0.3.7...v0.3.8) (2021-10-23)
|
||||
|
||||
|
||||
|
18
README.md
@ -12,7 +12,7 @@
|
||||
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.
|
||||
Our target is to support as many databases as possible, and all major operating systems, including the ARM versions.
|
||||
|
||||
**At the moment this application is in development state, many features will come in future updates**, and supports only MySQL/MariaDB and PostgreSQL.
|
||||
**At the moment this application is in development state, many features will come in future updates**, and supports only MySQL/MariaDB, PostgreSQL and SQLite.
|
||||
At the moment, however, there are all the features necessary to have a pleasant database management experience, so give it a chance and send us your feedback, we would really appreciate it.
|
||||
We are actively working on it, hoping to provide new cool features, improvements and fixes as soon as possible.
|
||||
|
||||
@ -31,10 +31,9 @@ We are actively working on it, hoping to provide new cool features, improvements
|
||||
- Query suggestions and auto complete.
|
||||
- Query history: search through the last 1000 queries.
|
||||
- SSH tunnel support.
|
||||
- Manual commit mode.
|
||||
- Dark and light theme.
|
||||
- Editor themes.
|
||||
- Scratchpad.
|
||||
- Secure password storage.
|
||||
|
||||
## Philosophy
|
||||
|
||||
@ -68,12 +67,12 @@ On macOS you can run `.dmg` distribution following [this guide](https://support.
|
||||
|
||||
This is a roadmap with major features will come in near future.
|
||||
|
||||
- Support for other databases.
|
||||
- Database tools.
|
||||
- Users management (add/edit/delete).
|
||||
- More context menu shortcuts.
|
||||
- More keyboard shortcuts.
|
||||
- Import/export and migration.
|
||||
- Support for other databases.
|
||||
- Apple Silicon distribution
|
||||
|
||||
## Currently supported
|
||||
@ -82,7 +81,7 @@ This is a roadmap with major features will come in near future.
|
||||
|
||||
- [x] MySQL/MariaDB
|
||||
- [x] PostgreSQL
|
||||
- [ ] SQLite
|
||||
- [x] SQLite
|
||||
- [ ] MSSQL
|
||||
- [ ] OracleDB
|
||||
- [ ] More...
|
||||
@ -116,9 +115,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://fabiodistasio.it/"><img src="https://avatars.githubusercontent.com/u/31471771?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fabio Di Stasio</b></sub></a><br /><a href="https://github.com/Fabio286/Antares/commits?author=Fabio286" title="Code">💻</a> <a href="#translation-Fabio286" title="Translation">🌍</a> <a href="https://github.com/Fabio286/Antares/commits?author=Fabio286" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://www.linkedin.com/in/giulioganci/"><img src="https://avatars.githubusercontent.com/u/4192159?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Giulio Ganci</b></sub></a><br /><a href="https://github.com/Fabio286/Antares/commits?author=toriphes" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://christianratz.de/"><img src="https://avatars.githubusercontent.com/u/2630316?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christian Ratz</b></sub></a><br /><a href="https://github.com/Fabio286/Antares/commits?author=digitalgopnik" title="Code">💻</a> <a href="#translation-digitalgopnik" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://fabiodistasio.it/"><img src="https://avatars.githubusercontent.com/u/31471771?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fabio Di Stasio</b></sub></a><br /><a href="https://github.com/Fabio286/antares/commits?author=Fabio286" title="Code">💻</a> <a href="#translation-Fabio286" title="Translation">🌍</a> <a href="https://github.com/Fabio286/antares/commits?author=Fabio286" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://www.linkedin.com/in/giulioganci/"><img src="https://avatars.githubusercontent.com/u/4192159?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Giulio Ganci</b></sub></a><br /><a href="https://github.com/Fabio286/antares/commits?author=toriphes" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://christianratz.de/"><img src="https://avatars.githubusercontent.com/u/2630316?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christian Ratz</b></sub></a><br /><a href="https://github.com/Fabio286/antares/commits?author=digitalgopnik" title="Code">💻</a> <a href="#translation-digitalgopnik" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://reverb6821.github.io/"><img src="https://avatars.githubusercontent.com/u/55198803?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Giuseppe Gigliotti</b></sub></a><br /><a href="#translation-reverb6821" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/Mohd-PH"><img src="https://avatars.githubusercontent.com/u/9362157?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mohd-PH</b></sub></a><br /><a href="#translation-Mohd-PH" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/hongkfui"><img src="https://avatars.githubusercontent.com/u/37477191?v=4?s=100" width="100px;" alt=""/><br /><sub><b>hongkfui</b></sub></a><br /><a href="#translation-hongkfui" title="Translation">🌍</a></td>
|
||||
@ -129,6 +128,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center"><a href="https://ngoquocdat.com/"><img src="https://avatars.githubusercontent.com/u/56961917?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ngô Quốc Đạt</b></sub></a><br /><a href="#translation-datlechin" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/IsamuSugi"><img src="https://avatars.githubusercontent.com/u/7746658?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Isamu Sugiura</b></sub></a><br /><a href="#translation-IsamuSugi" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="http://rsacchetto.nexxontech.it/"><img src="https://avatars.githubusercontent.com/u/18429412?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Riccardo Sacchetto</b></sub></a><br /><a href="#platform-Occhioverde" title="Packaging/porting to new platform">📦</a></td>
|
||||
<td align="center"><a href="https://kilianstallinger.com"><img src="https://avatars.githubusercontent.com/u/5290318?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kilian Stallinger</b></sub></a><br /><a href="https://github.com/Fabio286/antares/commits?author=kilianstallz" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/wenj91"><img src="https://avatars.githubusercontent.com/u/12549338?v=4?s=100" width="100px;" alt=""/><br /><sub><b>文杰</b></sub></a><br /><a href="https://github.com/Fabio286/antares/commits?author=wenj91" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/goYou"><img src="https://avatars.githubusercontent.com/u/62732795?v=4?s=100" width="100px;" alt=""/><br /><sub><b>goYou</b></sub></a><br /><a href="#translation-goYou" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 7.4 KiB |
43
package.json
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "antares",
|
||||
"productName": "Antares",
|
||||
"version": "0.3.8",
|
||||
"description": "A cross-platform easy to use SQL client.",
|
||||
"version": "0.4.3",
|
||||
"description": "A modern, fast and productivity driven SQL client with a focus in UX.",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/Fabio286/antares.git",
|
||||
"scripts": {
|
||||
@ -14,11 +14,11 @@
|
||||
"build": "cross-env NODE_ENV=production npm run compile",
|
||||
"build:local": "npm run build && electron-builder",
|
||||
"build:appx": "npm run build:local -- --win appx",
|
||||
"rebuild:electron": "npm run postinstall && electron-rebuild",
|
||||
"rebuild:electron": "npm run postinstall",
|
||||
"release": "standard-version",
|
||||
"release:pre": "npm run release -- --prerelease alpha",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"test": "npm run lint",
|
||||
"test": "npm run compile && node tests/app.spec.js",
|
||||
"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",
|
||||
"contributors:add": "all-contributors add",
|
||||
@ -30,6 +30,7 @@
|
||||
"appId": "com.fabio286.antares",
|
||||
"artifactName": "${productName}-${version}-${os}_${arch}.${ext}",
|
||||
"asar": true,
|
||||
"buildDependenciesFromSource": true,
|
||||
"directories": {
|
||||
"output": "build",
|
||||
"buildResources": "assets"
|
||||
@ -50,7 +51,8 @@
|
||||
"target": {
|
||||
"target": "default",
|
||||
"arch": [
|
||||
"x64"
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -79,7 +81,9 @@
|
||||
"artifactName": "${productName}-${version}-portable.exe"
|
||||
},
|
||||
"appx": {
|
||||
"displayName": "Antares SQL Client",
|
||||
"displayName": "Antares SQL",
|
||||
"backgroundColor": "transparent",
|
||||
"showNameOnTiles": true,
|
||||
"identityName": "62514FabioDiStasio.AntaresSQLClient",
|
||||
"publisher": "CN=1A2729ED-865C-41D2-9038-39AE2A63AA52",
|
||||
"applicationId": "FabioDiStasio.AntaresSQLClient"
|
||||
@ -102,12 +106,17 @@
|
||||
"dependencies": {
|
||||
"@electron/remote": "^2.0.1",
|
||||
"@mdi/font": "^6.1.95",
|
||||
"@turf/helpers": "^6.5.0",
|
||||
"@vscode/vscode-languagedetection": "^1.0.21",
|
||||
"ace-builds": "^1.4.13",
|
||||
"better-sqlite3": "^7.4.4",
|
||||
"electron-log": "^4.4.1",
|
||||
"electron-store": "^8.0.1",
|
||||
"electron-updater": "^4.3.9",
|
||||
"electron-updater": "^4.6.1",
|
||||
"electron-window-state": "^5.0.3",
|
||||
"faker": "^5.5.3",
|
||||
"marked": "^3.0.4",
|
||||
"leaflet": "^1.7.1",
|
||||
"marked": "^4.0.0",
|
||||
"moment": "^2.29.1",
|
||||
"mysql2": "^2.3.2",
|
||||
"pg": "^8.7.1",
|
||||
@ -130,23 +139,23 @@
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"css-loader": "^6.5.0",
|
||||
"electron": "^15.3.0",
|
||||
"electron-builder": "^22.13.1",
|
||||
"electron": "^17.0.1",
|
||||
"electron-builder": "^22.14.11",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"electron-rebuild": "^3.2.3",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
"eslint-plugin-import": "^2.24.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-vue": "^7.18.0",
|
||||
"eslint-plugin-promise": "^5.2.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"mini-css-extract-plugin": "^2.4.3",
|
||||
"mini-css-extract-plugin": "~2.4.5",
|
||||
"node-loader": "^2.0.0",
|
||||
"playwright": "^1.18.1",
|
||||
"progress-webpack-plugin": "^1.0.12",
|
||||
"sass": "^1.42.1",
|
||||
"sass-loader": "^10.2.0",
|
||||
"sass-loader": "^12.3.0",
|
||||
"standard-version": "^9.3.1",
|
||||
"style-loader": "^3.3.1",
|
||||
"stylelint": "^13.13.1",
|
||||
@ -158,6 +167,6 @@
|
||||
"vue-template-compiler": "^2.6.14",
|
||||
"webpack": "^5.60.0",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"webpack-dev-server": "^3.11.2"
|
||||
"webpack-dev-server": "^4.4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,14 +113,15 @@ function startRenderer (callback) {
|
||||
});
|
||||
|
||||
const server = new WebpackDevServer(compiler, {
|
||||
contentBase: path.join(__dirname, '../'),
|
||||
hot: true,
|
||||
noInfo: true,
|
||||
overlay: true,
|
||||
clientLogLevel: 'warning'
|
||||
port: 9080,
|
||||
client: {
|
||||
overlay: true,
|
||||
logging: 'warn'
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(9080, '', err => {
|
||||
server.startCallback(err => {
|
||||
if (err) console.error(chalk.red(err));
|
||||
|
||||
callback();
|
||||
|
@ -134,7 +134,7 @@ export default class {
|
||||
{ name: 'phoneNumberFormat', group: 'phone', types: ['string'] },
|
||||
{ name: 'phoneFormats', group: 'phone', types: ['string'] },
|
||||
|
||||
{ name: 'number', group: 'random', types: ['string', 'number'], params: ['min', 'max'] },
|
||||
{ name: 'number', group: 'datatype', types: ['string', 'number'], params: ['min', 'max'] },
|
||||
{ name: 'float', group: 'random', types: ['string', 'float'], params: ['min', 'max'] },
|
||||
{ name: 'arrayElement', group: 'random', types: ['string'] },
|
||||
{ name: 'arrayElements', group: 'random', types: ['string'] },
|
||||
|
@ -8,6 +8,10 @@ module.exports = {
|
||||
collations: false,
|
||||
engines: false,
|
||||
connectionSchema: false,
|
||||
sslConnection: false,
|
||||
sshConnection: false,
|
||||
fileConnection: false,
|
||||
cancelQueries: false,
|
||||
// Tools
|
||||
processesList: false,
|
||||
usersManagement: false,
|
||||
@ -33,7 +37,12 @@ module.exports = {
|
||||
schedulerAdd: false,
|
||||
databaseEdit: false,
|
||||
schemaEdit: false,
|
||||
schemaDrop: false,
|
||||
schemaExport: false,
|
||||
tableSettings: false,
|
||||
tableOptions: false,
|
||||
tableArray: false,
|
||||
tableRealCount: false,
|
||||
viewSettings: false,
|
||||
triggerSettings: false,
|
||||
triggerFunctionSettings: false,
|
||||
@ -45,14 +54,13 @@ module.exports = {
|
||||
sortableFields: false,
|
||||
unsigned: false,
|
||||
nullable: false,
|
||||
nullablePrimary: false,
|
||||
zerofill: false,
|
||||
tableOptions: false,
|
||||
autoIncrement: false,
|
||||
comment: false,
|
||||
collation: false,
|
||||
definer: false,
|
||||
onUpdate: false,
|
||||
tableArray: false,
|
||||
viewAlgorithm: false,
|
||||
viewSqlSecurity: false,
|
||||
viewUpdateOption: false,
|
||||
@ -72,8 +80,10 @@ module.exports = {
|
||||
triggerTableInName: false,
|
||||
triggerUpdateColumns: false,
|
||||
triggerOnlyRename: false,
|
||||
triggerEnableDisable: false,
|
||||
triggerFunctionSql: false,
|
||||
triggerFunctionlanguages: false,
|
||||
parametersLength: false,
|
||||
languages: false
|
||||
languages: false,
|
||||
readOnlyMode: false
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
maria: require('./mysql'),
|
||||
mysql: require('./mysql'),
|
||||
pg: require('./postgresql')
|
||||
pg: require('./postgresql'),
|
||||
sqlite: require('./sqlite')
|
||||
};
|
||||
|
@ -10,6 +10,9 @@ module.exports = {
|
||||
connectionSchema: true,
|
||||
collations: true,
|
||||
engines: true,
|
||||
sslConnection: true,
|
||||
sshConnection: true,
|
||||
cancelQueries: true,
|
||||
// Tools
|
||||
processesList: true,
|
||||
// Structure
|
||||
@ -30,6 +33,7 @@ module.exports = {
|
||||
functionAdd: true,
|
||||
schedulerAdd: true,
|
||||
schemaEdit: true,
|
||||
schemaDrop: true,
|
||||
schemaExport: true,
|
||||
tableSettings: true,
|
||||
viewSettings: true,
|
||||
@ -60,5 +64,6 @@ module.exports = {
|
||||
functionDeterministic: true,
|
||||
functionDataAccess: true,
|
||||
functionSql: 'BEGIN\r\n\r\nEND',
|
||||
parametersLength: true
|
||||
parametersLength: true,
|
||||
readOnlyMode: true
|
||||
};
|
||||
|
@ -8,9 +8,13 @@ module.exports = {
|
||||
defaultDatabase: 'postgres',
|
||||
// Core
|
||||
database: true,
|
||||
sslConnection: true,
|
||||
sshConnection: true,
|
||||
cancelQueries: true,
|
||||
// Tools
|
||||
processesList: true,
|
||||
// Structure
|
||||
schemas: true,
|
||||
tables: true,
|
||||
views: true,
|
||||
triggers: true,
|
||||
@ -26,6 +30,7 @@ module.exports = {
|
||||
triggerFunctionAdd: true,
|
||||
routineAdd: true,
|
||||
functionAdd: true,
|
||||
schemaDrop: true,
|
||||
databaseEdit: false,
|
||||
schemaExport: true,
|
||||
tableSettings: true,
|
||||
@ -51,5 +56,7 @@ module.exports = {
|
||||
triggerMultipleEvents: true,
|
||||
triggerTableInName: true,
|
||||
triggerOnlyRename: false,
|
||||
languages: ['sql', 'plpgsql', 'c', 'internal']
|
||||
triggerEnableDisable: true,
|
||||
languages: ['sql', 'plpgsql', 'c', 'internal'],
|
||||
readOnlyMode: true
|
||||
};
|
||||
|
27
src/common/customizations/sqlite.js
Normal file
@ -0,0 +1,27 @@
|
||||
module.exports = {
|
||||
// Core
|
||||
fileConnection: true,
|
||||
// Structure
|
||||
schemas: false,
|
||||
tables: true,
|
||||
views: true,
|
||||
triggers: true,
|
||||
// Settings
|
||||
elementsWrapper: '"',
|
||||
stringsWrapper: '\'',
|
||||
tableAdd: true,
|
||||
viewAdd: true,
|
||||
triggerAdd: true,
|
||||
schemaEdit: false,
|
||||
tableSettings: true,
|
||||
tableRealCount: true,
|
||||
viewSettings: true,
|
||||
triggerSettings: true,
|
||||
indexes: true,
|
||||
foreigns: true,
|
||||
sortableFields: true,
|
||||
nullable: true,
|
||||
nullablePrimary: true,
|
||||
triggerSql: 'BEGIN\r\n\r\nEND',
|
||||
readOnlyMode: true
|
||||
};
|
@ -66,6 +66,7 @@ module.exports = [
|
||||
{
|
||||
name: 'DECIMAL',
|
||||
length: true,
|
||||
scale: true,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
@ -120,7 +121,7 @@ module.exports = [
|
||||
{
|
||||
name: 'JSON',
|
||||
length: false,
|
||||
collation: true,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
}
|
||||
@ -218,56 +219,56 @@ module.exports = [
|
||||
types: [
|
||||
{
|
||||
name: 'POINT',
|
||||
length: true,
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'LINESTRING',
|
||||
length: true,
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'POLYGON',
|
||||
length: true,
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'GEOMETRY',
|
||||
length: true,
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'MULTIPOINT',
|
||||
length: true,
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'MULTILINESTRING',
|
||||
length: true,
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'MULTIPOLYGON',
|
||||
length: true,
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'GEOMETRYCOLLECTION',
|
||||
length: true,
|
||||
name: 'GEOMCOLLECTION',
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
|
@ -22,11 +22,6 @@ module.exports = [
|
||||
length: false,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'NUMERIC',
|
||||
length: true,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'SMALLSERIAL',
|
||||
length: false,
|
||||
@ -52,6 +47,12 @@ module.exports = [
|
||||
length: false,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'NUMERIC',
|
||||
length: true,
|
||||
unsigned: true,
|
||||
scale: true
|
||||
},
|
||||
{
|
||||
name: 'DOUBLE PRECISION',
|
||||
length: false,
|
||||
|
137
src/common/data-types/sqlite.js
Normal file
@ -0,0 +1,137 @@
|
||||
module.exports = [
|
||||
{
|
||||
group: 'integer',
|
||||
types: [
|
||||
{
|
||||
name: 'INT',
|
||||
length: true,
|
||||
collation: false,
|
||||
unsigned: true,
|
||||
zerofill: true
|
||||
},
|
||||
{
|
||||
name: 'INTEGER',
|
||||
length: true,
|
||||
collation: false,
|
||||
unsigned: true,
|
||||
zerofill: true
|
||||
},
|
||||
{
|
||||
name: 'BIGINT',
|
||||
length: true,
|
||||
collation: false,
|
||||
unsigned: true,
|
||||
zerofill: true
|
||||
},
|
||||
{
|
||||
name: 'NUMERIC',
|
||||
length: true,
|
||||
collation: false,
|
||||
unsigned: true,
|
||||
zerofill: true
|
||||
},
|
||||
{
|
||||
name: 'BOOLEAN',
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: true,
|
||||
zerofill: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'float',
|
||||
types: [
|
||||
{
|
||||
name: 'FLOAT',
|
||||
length: true,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'REAL',
|
||||
length: true,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'string',
|
||||
types: [
|
||||
{
|
||||
name: 'CHAR',
|
||||
length: true,
|
||||
collation: true,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'VARCHAR',
|
||||
length: true,
|
||||
collation: true,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'TEXT',
|
||||
length: true,
|
||||
collation: true,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'binary',
|
||||
types: [
|
||||
{
|
||||
name: 'BLOB',
|
||||
length: true,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'time',
|
||||
types: [
|
||||
{
|
||||
name: 'DATE',
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'TIME',
|
||||
length: true,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'DATETIME',
|
||||
length: true,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'other',
|
||||
types: [
|
||||
{
|
||||
name: 'NONE',
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
@ -8,7 +8,9 @@ export const TEXT = [
|
||||
export const LONG_TEXT = [
|
||||
'TEXT',
|
||||
'MEDIUMTEXT',
|
||||
'LONGTEXT'
|
||||
'LONGTEXT',
|
||||
'JSON',
|
||||
'VARBINARY'
|
||||
];
|
||||
|
||||
export const ARRAY = [
|
||||
@ -82,3 +84,24 @@ export const BIT = [
|
||||
'BIT',
|
||||
'BIT VARYING'
|
||||
];
|
||||
|
||||
export const SPATIAL = [
|
||||
'POINT',
|
||||
'LINESTRING',
|
||||
'POLYGON',
|
||||
'GEOMETRY',
|
||||
'MULTIPOINT',
|
||||
'MULTILINESTRING',
|
||||
'MULTIPOLYGON',
|
||||
'GEOMCOLLECTION',
|
||||
'GEOMETRYCOLLECTION'
|
||||
];
|
||||
|
||||
// Used to check multi spatial fields only
|
||||
export const IS_MULTI_SPATIAL = [
|
||||
'MULTIPOINT',
|
||||
'MULTILINESTRING',
|
||||
'MULTIPOLYGON',
|
||||
'GEOMCOLLECTION',
|
||||
'GEOMETRYCOLLECTION'
|
||||
];
|
||||
|
5
src/common/index-types/sqlite.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = [
|
||||
'PRIMARY',
|
||||
'INDEX',
|
||||
'UNIQUE'
|
||||
];
|
10
src/common/libs/getArrayDepth.js
Normal file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
*
|
||||
* @param {any[]} array
|
||||
* @returns {number}
|
||||
*/
|
||||
export function getArrayDepth (array) {
|
||||
return Array.isArray(array)
|
||||
? 1 + Math.max(0, ...array.map(getArrayDepth))
|
||||
: 0;
|
||||
}
|
@ -9,12 +9,16 @@ export default connections => {
|
||||
port: +conn.port,
|
||||
user: conn.user,
|
||||
password: conn.password,
|
||||
application_name: 'Antares SQL'
|
||||
application_name: 'Antares SQL',
|
||||
readonly: conn.readonly
|
||||
};
|
||||
|
||||
if (conn.database)
|
||||
params.database = conn.database;
|
||||
|
||||
if (conn.databasePath)
|
||||
params.databasePath = conn.databasePath;
|
||||
|
||||
if (conn.ssl) {
|
||||
params.ssl = {
|
||||
key: conn.key ? fs.readFileSync(conn.key) : null,
|
||||
@ -48,7 +52,7 @@ export default connections => {
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err };
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
@ -62,12 +66,16 @@ export default connections => {
|
||||
port: +conn.port,
|
||||
user: conn.user,
|
||||
password: conn.password,
|
||||
application_name: 'Antares SQL'
|
||||
application_name: 'Antares SQL',
|
||||
readonly: conn.readonly
|
||||
};
|
||||
|
||||
if (conn.database)
|
||||
params.database = conn.database;
|
||||
|
||||
if (conn.databasePath)
|
||||
params.databasePath = conn.databasePath;
|
||||
|
||||
if (conn.schema)
|
||||
params.schema = conn.schema;
|
||||
|
||||
|
@ -40,4 +40,17 @@ export default (connections) => {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('toggle-scheduler', async (event, params) => {
|
||||
try {
|
||||
if (!params.enabled)
|
||||
await connections[params.uid].enableEvent({ ...params });
|
||||
else
|
||||
await connections[params.uid].disableEvent({ ...params });
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -149,7 +149,7 @@ export default connections => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('raw-query', async (event, { uid, query, schema }) => {
|
||||
ipcMain.handle('raw-query', async (event, { uid, query, schema, tabUid, autocommit }) => {
|
||||
if (!query) return;
|
||||
|
||||
try {
|
||||
@ -157,6 +157,8 @@ export default connections => {
|
||||
nest: true,
|
||||
details: true,
|
||||
schema,
|
||||
tabUid,
|
||||
autocommit,
|
||||
comments: false
|
||||
});
|
||||
|
||||
@ -263,4 +265,51 @@ export default connections => {
|
||||
|
||||
return { status: 'success', response: { willAbort } };
|
||||
});
|
||||
ipcMain.handle('kill-tab-query', async (event, { uid, tabUid }) => {
|
||||
if (!tabUid) return;
|
||||
|
||||
try {
|
||||
await connections[uid].killTabQuery(tabUid);
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('commit-tab', async (event, { uid, tabUid }) => {
|
||||
if (!tabUid) return;
|
||||
|
||||
try {
|
||||
await connections[uid].commitTab(tabUid);
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('rollback-tab', async (event, { uid, tabUid }) => {
|
||||
if (!tabUid) return;
|
||||
|
||||
try {
|
||||
await connections[uid].rollbackTab(tabUid);
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('destroy-connection-to-commit', async (event, { uid, tabUid }) => {
|
||||
if (!tabUid) return;
|
||||
|
||||
try {
|
||||
await connections[uid].destroyConnectionToCommit(tabUid);
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -3,6 +3,7 @@ import faker from 'faker';
|
||||
import moment from 'moment';
|
||||
import { sqlEscaper } from 'common/libs/sqlEscaper';
|
||||
import { TEXT, LONG_TEXT, ARRAY, TEXT_SEARCH, NUMBER, FLOAT, BLOB, BIT, DATE, DATETIME } from 'common/fieldTypes';
|
||||
import * as customizations from 'common/customizations';
|
||||
import fs from 'fs';
|
||||
|
||||
export default (connections) => {
|
||||
@ -84,12 +85,13 @@ export default (connections) => {
|
||||
});
|
||||
|
||||
ipcMain.handle('update-table-cell', async (event, params) => {
|
||||
delete params.row._id;
|
||||
delete params.row._antares_id;
|
||||
const { stringsWrapper: sw } = customizations[connections[params.uid]._client];
|
||||
|
||||
try { // TODO: move to client classes
|
||||
let escapedParam;
|
||||
let reload = false;
|
||||
const id = typeof params.id === 'number' ? params.id : `"${params.id}"`;
|
||||
const id = typeof params.id === 'number' ? params.id : `${sw}${params.id}${sw}`;
|
||||
|
||||
if ([...NUMBER, ...FLOAT].includes(params.type))
|
||||
escapedParam = params.content;
|
||||
@ -102,6 +104,9 @@ export default (connections) => {
|
||||
case 'pg':
|
||||
escapedParam = `'${params.content.replaceAll('\'', '\'\'')}'`;
|
||||
break;
|
||||
case 'sqlite':
|
||||
escapedParam = `'${params.content.replaceAll('\'', '\'\'')}'`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ARRAY.includes(params.type))
|
||||
@ -122,6 +127,10 @@ export default (connections) => {
|
||||
fileBlob = fs.readFileSync(params.content);
|
||||
escapedParam = `decode('${fileBlob.toString('hex')}', 'hex')`;
|
||||
break;
|
||||
case 'sqlite':
|
||||
fileBlob = fs.readFileSync(params.content);
|
||||
escapedParam = `X'${fileBlob.toString('hex')}'`;
|
||||
break;
|
||||
}
|
||||
reload = true;
|
||||
}
|
||||
@ -134,6 +143,9 @@ export default (connections) => {
|
||||
case 'pg':
|
||||
escapedParam = 'decode(\'\', \'hex\')';
|
||||
break;
|
||||
case 'sqlite':
|
||||
escapedParam = 'X\'\'';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -188,7 +200,7 @@ export default (connections) => {
|
||||
const fieldName = Object.keys(row)[0].includes('.') ? `${params.table}.${params.primary}` : params.primary;
|
||||
|
||||
return typeof row[fieldName] === 'string'
|
||||
? `"${row[fieldName]}"`
|
||||
? `'${row[fieldName]}'`
|
||||
: row[fieldName];
|
||||
}).join(',');
|
||||
|
||||
|
@ -40,4 +40,17 @@ export default (connections) => {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('toggle-trigger', async (event, params) => {
|
||||
try {
|
||||
if (!params.enabled)
|
||||
await connections[params.uid].enableTrigger(params);
|
||||
else
|
||||
await connections[params.uid].disableTrigger(params);
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -2,6 +2,7 @@ import { ipcMain } from 'electron';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
import Store from 'electron-store';
|
||||
const persistentStore = new Store({ name: 'settings' });
|
||||
const isMacOS = process.platform === 'darwin';
|
||||
|
||||
let mainWindow;
|
||||
autoUpdater.allowPrerelease = persistentStore.get('allow_prerelease', true);
|
||||
@ -11,6 +12,9 @@ export default () => {
|
||||
mainWindow = event;
|
||||
if (process.windowsStore || (process.platform === 'linux' && !process.env.APPIMAGE))
|
||||
mainWindow.reply('no-auto-update');
|
||||
else if (isMacOS) { // Temporary solution on MacOS for unsigned app updates
|
||||
autoUpdater.autoDownload = false;
|
||||
}
|
||||
else {
|
||||
autoUpdater.checkForUpdatesAndNotify().catch(() => {
|
||||
mainWindow.reply('check-failed');
|
||||
@ -28,7 +32,10 @@ export default () => {
|
||||
});
|
||||
|
||||
autoUpdater.on('update-available', () => {
|
||||
mainWindow.reply('update-available');
|
||||
if (isMacOS)
|
||||
mainWindow.reply('link-to-download');
|
||||
else
|
||||
mainWindow.reply('update-available');
|
||||
});
|
||||
|
||||
autoUpdater.on('update-not-available', () => {
|
||||
|
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
import { MySQLClient } from './clients/MySQLClient';
|
||||
import { PostgreSQLClient } from './clients/PostgreSQLClient';
|
||||
import { SQLiteClient } from './clients/SQLiteClient';
|
||||
|
||||
const queryLogger = sql => {
|
||||
// Remove comments, newlines and multiple spaces
|
||||
@ -37,6 +38,8 @@ export class ClientsFactory {
|
||||
return new MySQLClient(args);
|
||||
case 'pg':
|
||||
return new PostgreSQLClient(args);
|
||||
case 'sqlite':
|
||||
return new SQLiteClient(args);
|
||||
default:
|
||||
throw new Error(`Unknown database client: ${args.client}`);
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ export class MySQLClient extends AntaresCore {
|
||||
super(args);
|
||||
|
||||
this._schema = null;
|
||||
this._runningConnections = new Map();
|
||||
this._connectionsToCommit = new Map();
|
||||
|
||||
this.types = {
|
||||
0: 'DECIMAL',
|
||||
@ -100,9 +102,11 @@ export class MySQLClient extends AntaresCore {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns dbConfig
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async connect () {
|
||||
async getDbConfig () {
|
||||
delete this._params.application_name;
|
||||
|
||||
const dbConfig = {
|
||||
@ -133,20 +137,17 @@ export class MySQLClient extends AntaresCore {
|
||||
}
|
||||
}
|
||||
|
||||
return dbConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async connect () {
|
||||
if (!this._poolSize)
|
||||
this._connection = await mysql.createConnection(dbConfig);
|
||||
else {
|
||||
this._connection = mysql.createPool({
|
||||
...dbConfig,
|
||||
connectionLimit: this._poolSize,
|
||||
typeCast: (field, next) => {
|
||||
if (field.type === 'DATETIME')
|
||||
return field.string();
|
||||
else
|
||||
return next();
|
||||
}
|
||||
});
|
||||
}
|
||||
this._connection = await this.getConnection();
|
||||
else
|
||||
this._connection = await this.getConnectionPool();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,6 +158,64 @@ export class MySQLClient extends AntaresCore {
|
||||
if (this._ssh) this._ssh.close();
|
||||
}
|
||||
|
||||
async getConnection () {
|
||||
const dbConfig = await this.getDbConfig();
|
||||
const connection = await mysql.createConnection({
|
||||
...dbConfig,
|
||||
typeCast: (field, next) => {
|
||||
if (field.type === 'DATETIME')
|
||||
return field.string();
|
||||
else
|
||||
return next();
|
||||
}
|
||||
});
|
||||
|
||||
// ANSI_QUOTES check
|
||||
const [res] = await connection.query('SHOW GLOBAL VARIABLES LIKE \'%sql_mode%\'');
|
||||
const sqlMode = res[0]?.Variable_name?.split(',');
|
||||
const hasAnsiQuotes = sqlMode.includes('ANSI_QUOTES');
|
||||
|
||||
if (this._params.readonly)
|
||||
await connection.query('SET SESSION TRANSACTION READ ONLY');
|
||||
|
||||
if (hasAnsiQuotes)
|
||||
await connection.query(`SET SESSION sql_mode = "${sqlMode.filter(m => m !== 'ANSI_QUOTES').join(',')}"`);
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
async getConnectionPool () {
|
||||
const dbConfig = await this.getDbConfig();
|
||||
const connection = mysql.createPool({
|
||||
...dbConfig,
|
||||
connectionLimit: this._poolSize,
|
||||
typeCast: (field, next) => {
|
||||
if (field.type === 'DATETIME')
|
||||
return field.string();
|
||||
else
|
||||
return next();
|
||||
}
|
||||
});
|
||||
|
||||
// ANSI_QUOTES check
|
||||
const [res] = await connection.query('SHOW GLOBAL VARIABLES LIKE \'%sql_mode%\'');
|
||||
const sqlMode = res[0]?.Variable_name?.split(',');
|
||||
const hasAnsiQuotes = sqlMode.includes('ANSI_QUOTES');
|
||||
|
||||
if (hasAnsiQuotes)
|
||||
await connection.query(`SET SESSION sql_mode = "${sqlMode.filter(m => m !== 'ANSI_QUOTES').join(',')}"`);
|
||||
|
||||
connection.on('connection', conn => {
|
||||
if (this._params.readonly)
|
||||
conn.query('SET SESSION TRANSACTION READ ONLY');
|
||||
|
||||
if (hasAnsiQuotes)
|
||||
conn.query(`SET SESSION sql_mode = "${sqlMode.filter(m => m !== 'ANSI_QUOTES').join(',')}"`);
|
||||
});
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an USE query
|
||||
*
|
||||
@ -187,6 +246,7 @@ export class MySQLClient extends AntaresCore {
|
||||
|
||||
const tablesArr = [];
|
||||
const triggersArr = [];
|
||||
let schemaSize = 0;
|
||||
|
||||
for (const db of filteredDatabases) {
|
||||
if (!schemas.has(db.Database)) continue;
|
||||
@ -224,6 +284,9 @@ export class MySQLClient extends AntaresCore {
|
||||
break;
|
||||
}
|
||||
|
||||
const tableSize = table.Data_length + table.Index_length;
|
||||
schemaSize += tableSize;
|
||||
|
||||
return {
|
||||
name: table.Name,
|
||||
type: tableType,
|
||||
@ -232,7 +295,7 @@ export class MySQLClient extends AntaresCore {
|
||||
updated: table.Update_time,
|
||||
engine: table.Engine,
|
||||
comment: table.Comment,
|
||||
size: table.Data_length + table.Index_length,
|
||||
size: tableSize,
|
||||
autoIncrement: table.Auto_increment,
|
||||
collation: table.Collation
|
||||
};
|
||||
@ -276,7 +339,7 @@ export class MySQLClient extends AntaresCore {
|
||||
body: scheduler.EVENT_BODY,
|
||||
starts: scheduler.STARTS,
|
||||
ends: scheduler.ENDS,
|
||||
status: scheduler.STATUS,
|
||||
enabled: scheduler.STATUS === 'ENABLED',
|
||||
executeAt: scheduler.EXECUTE_AT,
|
||||
intervalField: scheduler.INTERVAL_FIELD,
|
||||
intervalValue: scheduler.INTERVAL_VALUE,
|
||||
@ -309,6 +372,7 @@ export class MySQLClient extends AntaresCore {
|
||||
|
||||
return {
|
||||
name: db.Database,
|
||||
size: schemaSize,
|
||||
tables: remappedTables,
|
||||
functions: remappedFunctions,
|
||||
procedures: remappedProcedures,
|
||||
@ -319,6 +383,7 @@ export class MySQLClient extends AntaresCore {
|
||||
else {
|
||||
return {
|
||||
name: db.Database,
|
||||
size: 0,
|
||||
tables: [],
|
||||
functions: [],
|
||||
procedures: [],
|
||||
@ -360,7 +425,7 @@ export class MySQLClient extends AntaresCore {
|
||||
return acc;
|
||||
}, '')
|
||||
.replaceAll('\n', '')
|
||||
.split(',')
|
||||
.split(/,\s?(?![^(]*\))/)
|
||||
.map(f => {
|
||||
try {
|
||||
const fieldArr = f.trim().split(' ');
|
||||
@ -400,18 +465,25 @@ export class MySQLClient extends AntaresCore {
|
||||
|
||||
return rows.map(field => {
|
||||
let numLength = field.COLUMN_TYPE.match(/int\(([^)]+)\)/);
|
||||
numLength = numLength ? +numLength.pop() : null;
|
||||
numLength = numLength ? +numLength.pop() : field.NUMERIC_PRECISION || null;
|
||||
const enumValues = /(enum|set)/.test(field.COLUMN_TYPE)
|
||||
? field.COLUMN_TYPE.match(/\(([^)]+)\)/)[0].slice(1, -1)
|
||||
: null;
|
||||
|
||||
const defaultValue = (remappedFields && remappedFields[field.COLUMN_NAME])
|
||||
? remappedFields[field.COLUMN_NAME].default
|
||||
: field.COLUMN_DEFAULT;
|
||||
|
||||
return {
|
||||
name: field.COLUMN_NAME,
|
||||
key: field.COLUMN_KEY.toLowerCase(),
|
||||
type: remappedFields ? remappedFields[field.COLUMN_NAME].type : field.DATA_TYPE,
|
||||
type: (remappedFields && remappedFields[field.COLUMN_NAME])
|
||||
? remappedFields[field.COLUMN_NAME].type
|
||||
: field.DATA_TYPE.toUpperCase(),
|
||||
schema: field.TABLE_SCHEMA,
|
||||
table: field.TABLE_NAME,
|
||||
numPrecision: field.NUMERIC_PRECISION,
|
||||
numScale: field.NUMERIC_SCALE,
|
||||
numLength,
|
||||
enumValues,
|
||||
datePrecision: field.DATETIME_PRECISION,
|
||||
@ -420,11 +492,13 @@ export class MySQLClient extends AntaresCore {
|
||||
unsigned: field.COLUMN_TYPE.includes('unsigned'),
|
||||
zerofill: field.COLUMN_TYPE.includes('zerofill'),
|
||||
order: field.ORDINAL_POSITION,
|
||||
default: remappedFields ? remappedFields[field.COLUMN_NAME].default : field.COLUMN_DEFAULT,
|
||||
default: defaultValue,
|
||||
charset: field.CHARACTER_SET_NAME,
|
||||
collation: field.COLLATION_NAME,
|
||||
autoIncrement: field.EXTRA.includes('auto_increment'),
|
||||
onUpdate: field.EXTRA.toLowerCase().includes('on update') ? field.EXTRA.replace('on update', '') : '',
|
||||
onUpdate: field.EXTRA.toLowerCase().includes('on update')
|
||||
? field.EXTRA.substr(field.EXTRA.indexOf('on update') + 9, field.EXTRA.length).trim()
|
||||
: '',
|
||||
comment: field.COLUMN_COMMENT
|
||||
};
|
||||
});
|
||||
@ -565,7 +639,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* CREATE DATABASE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async createSchema (params) {
|
||||
@ -575,7 +649,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* ALTER DATABASE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async alterSchema (params) {
|
||||
@ -585,7 +659,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* DROP DATABASE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropSchema (params) {
|
||||
@ -625,7 +699,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* DROP VIEW
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropView (params) {
|
||||
@ -636,7 +710,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* ALTER VIEW
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async alterView (params) {
|
||||
@ -657,7 +731,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* CREATE VIEW
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async createView (params) {
|
||||
@ -690,7 +764,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* DROP TRIGGER
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropTrigger (params) {
|
||||
@ -701,7 +775,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* ALTER TRIGGER
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async alterTrigger (params) {
|
||||
@ -723,7 +797,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* CREATE TRIGGER
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async createTrigger (params) {
|
||||
@ -797,7 +871,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* DROP PROCEDURE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropRoutine (params) {
|
||||
@ -808,7 +882,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* ALTER PROCEDURE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async alterRoutine (params) {
|
||||
@ -830,7 +904,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* CREATE PROCEDURE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async createRoutine (params) {
|
||||
@ -924,7 +998,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* DROP FUNCTION
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropFunction (params) {
|
||||
@ -935,7 +1009,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* ALTER FUNCTION
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async alterFunction (params) {
|
||||
@ -957,7 +1031,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* CREATE FUNCTION
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async createFunction (params) {
|
||||
@ -1018,7 +1092,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* DROP EVENT
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropEvent (params) {
|
||||
@ -1029,7 +1103,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* ALTER EVENT
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async alterEvent (params) {
|
||||
@ -1055,7 +1129,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* CREATE EVENT
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async createEvent (params) {
|
||||
@ -1072,6 +1146,16 @@ export class MySQLClient extends AntaresCore {
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
||||
async enableEvent ({ schema, scheduler }) {
|
||||
const sql = `ALTER EVENT \`${schema}\`.\`${scheduler}\` ENABLE`;
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
||||
async disableEvent ({ schema, scheduler }) {
|
||||
const sql = `ALTER EVENT \`${schema}\`.\`${scheduler}\` DISABLE`;
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* SHOW COLLATION
|
||||
*
|
||||
@ -1111,6 +1195,26 @@ export class MySQLClient extends AntaresCore {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* SHOW VARIABLES LIKE %variable%
|
||||
*
|
||||
* @param {String} variable
|
||||
* @param {'global'|'session'|null} level
|
||||
* @returns {Object} variable
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async getVariable (variable, level) {
|
||||
const sql = `SHOW${level ? ' ' + level.toUpperCase() : ''} VARIABLES LIKE '%${variable}%'`;
|
||||
const results = await this.raw(sql);
|
||||
|
||||
if (results.rows.length) {
|
||||
return {
|
||||
name: results.rows[0].Variable_name,
|
||||
value: results.rows[0].Value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SHOW ENGINES
|
||||
*
|
||||
@ -1182,14 +1286,60 @@ export class MySQLClient extends AntaresCore {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} id
|
||||
* @returns {Promise<null>}
|
||||
*/
|
||||
async killProcess (id) {
|
||||
return await this.raw(`KILL ${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tabUid
|
||||
* @returns {Promise<null>}
|
||||
*/
|
||||
async killTabQuery (tabUid) {
|
||||
const id = this._runningConnections.get(tabUid);
|
||||
if (id)
|
||||
return await this.killProcess(id);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tabUid
|
||||
* @returns {Promise<null>}
|
||||
*/
|
||||
async commitTab (tabUid) {
|
||||
const connection = this._connectionsToCommit.get(tabUid);
|
||||
if (connection)
|
||||
return await connection.query('COMMIT');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tabUid
|
||||
* @returns {Promise<null>}
|
||||
*/
|
||||
async rollbackTab (tabUid) {
|
||||
const connection = this._connectionsToCommit.get(tabUid);
|
||||
if (connection)
|
||||
return await connection.query('ROLLBACK');
|
||||
}
|
||||
|
||||
destroyConnectionToCommit (tabUid) {
|
||||
const connection = this._connectionsToCommit.get(tabUid);
|
||||
if (connection) {
|
||||
connection.destroy();
|
||||
this._connectionsToCommit.delete(tabUid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CREATE TABLE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async createTable (params) {
|
||||
@ -1212,7 +1362,7 @@ export class MySQLClient extends AntaresCore {
|
||||
const length = typeInfo.length ? field.enumValues || field.numLength || field.charLength || field.datePrecision : false;
|
||||
|
||||
newColumns.push(`\`${field.name}\`
|
||||
${field.type.toUpperCase()}${length ? `(${length})` : ''}
|
||||
${field.type.toUpperCase()}${length ? `(${length}${field.numScale ? `,${field.numScale}` : ''})` : ''}
|
||||
${field.unsigned ? 'UNSIGNED' : ''}
|
||||
${field.zerofill ? 'ZEROFILL' : ''}
|
||||
${field.nullable ? 'NULL' : 'NOT NULL'}
|
||||
@ -1251,7 +1401,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* ALTER TABLE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async alterTable (params) {
|
||||
@ -1281,7 +1431,7 @@ export class MySQLClient extends AntaresCore {
|
||||
const length = typeInfo.length ? addition.enumValues || addition.numLength || addition.charLength || addition.datePrecision : false;
|
||||
|
||||
alterColumns.push(`ADD COLUMN \`${addition.name}\`
|
||||
${addition.type.toUpperCase()}${length ? `(${length})` : ''}
|
||||
${addition.type.toUpperCase()}${length ? `(${length}${addition.numScale ? `,${addition.numScale}` : ''})` : ''}
|
||||
${addition.unsigned ? 'UNSIGNED' : ''}
|
||||
${addition.zerofill ? 'ZEROFILL' : ''}
|
||||
${addition.nullable ? 'NULL' : 'NOT NULL'}
|
||||
@ -1319,7 +1469,7 @@ export class MySQLClient extends AntaresCore {
|
||||
const length = typeInfo.length ? change.enumValues || change.numLength || change.charLength || change.datePrecision : false;
|
||||
|
||||
alterColumns.push(`CHANGE COLUMN \`${change.orgName}\` \`${change.name}\`
|
||||
${change.type.toUpperCase()}${length ? `(${length})` : ''}
|
||||
${change.type.toUpperCase()}${length ? `(${length}${change.numScale ? `,${change.numScale}` : ''})` : ''}
|
||||
${change.unsigned ? 'UNSIGNED' : ''}
|
||||
${change.zerofill ? 'ZEROFILL' : ''}
|
||||
${change.nullable ? 'NULL' : 'NOT NULL'}
|
||||
@ -1386,7 +1536,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* DUPLICATE TABLE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async duplicateTable (params) {
|
||||
@ -1397,7 +1547,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* TRUNCATE TABLE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async truncateTable (params) {
|
||||
@ -1408,7 +1558,7 @@ export class MySQLClient extends AntaresCore {
|
||||
/**
|
||||
* DROP TABLE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropTable (params) {
|
||||
@ -1490,6 +1640,7 @@ export class MySQLClient extends AntaresCore {
|
||||
details: false,
|
||||
split: true,
|
||||
comments: true,
|
||||
autocommit: true,
|
||||
...args
|
||||
};
|
||||
|
||||
@ -1504,8 +1655,24 @@ export class MySQLClient extends AntaresCore {
|
||||
.filter(Boolean)
|
||||
.map(q => q.trim())
|
||||
: [sql];
|
||||
|
||||
let connection;
|
||||
const isPool = typeof this._connection.getConnection === 'function';
|
||||
const connection = isPool ? await this._connection.getConnection() : this._connection;
|
||||
|
||||
if (!args.autocommit && args.tabUid) { // autocommit OFF
|
||||
if (this._connectionsToCommit.has(args.tabUid))
|
||||
connection = this._connectionsToCommit.get(args.tabUid);
|
||||
else {
|
||||
connection = await this.getConnection();
|
||||
await connection.query('SET SESSION autocommit=0');
|
||||
this._connectionsToCommit.set(args.tabUid, connection);
|
||||
}
|
||||
}
|
||||
else// autocommit ON
|
||||
connection = isPool ? await this._connection.getConnection() : this._connection;
|
||||
|
||||
if (args.tabUid && isPool)
|
||||
this._runningConnections.set(args.tabUid, connection.connection.connectionId);
|
||||
|
||||
if (args.schema)
|
||||
await connection.query(`USE \`${args.schema}\``);
|
||||
@ -1567,7 +1734,10 @@ export class MySQLClient extends AntaresCore {
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
if (isPool) connection.release();
|
||||
if (isPool && args.autocommit) {
|
||||
connection.release();
|
||||
this._runningConnections.delete(args.tabUid);
|
||||
}
|
||||
reject(err);
|
||||
}
|
||||
|
||||
@ -1576,7 +1746,10 @@ export class MySQLClient extends AntaresCore {
|
||||
keysArr = keysArr ? [...keysArr, ...response] : response;
|
||||
}
|
||||
catch (err) {
|
||||
if (isPool) connection.release();
|
||||
if (isPool && args.autocommit) {
|
||||
connection.release();
|
||||
this._runningConnections.delete(args.tabUid);
|
||||
}
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
@ -1591,7 +1764,10 @@ export class MySQLClient extends AntaresCore {
|
||||
keys: keysArr
|
||||
});
|
||||
}).catch((err) => {
|
||||
if (isPool) connection.release();
|
||||
if (isPool && args.autocommit) {
|
||||
connection.release();
|
||||
this._runningConnections.delete(args.tabUid);
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
@ -1599,7 +1775,10 @@ export class MySQLClient extends AntaresCore {
|
||||
resultsArr.push({ rows, report, fields, keys, duration });
|
||||
}
|
||||
|
||||
if (isPool) connection.release();
|
||||
if (isPool && args.autocommit) {
|
||||
connection.release();
|
||||
this._runningConnections.delete(args.tabUid);
|
||||
}
|
||||
|
||||
return resultsArr.length === 1 ? resultsArr[0] : resultsArr;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ function pgToString (value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
types.setTypeParser(20, a => parseInt(a));// bigint string to number
|
||||
types.setTypeParser(1082, pgToString); // date
|
||||
types.setTypeParser(1083, pgToString); // time
|
||||
types.setTypeParser(1114, pgToString); // timestamp
|
||||
@ -20,6 +21,8 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
super(args);
|
||||
|
||||
this._schema = null;
|
||||
this._runningConnections = new Map();
|
||||
this._connectionsToCommit = new Map();
|
||||
|
||||
this.types = {};
|
||||
for (const key in types.builtins)
|
||||
@ -69,9 +72,11 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns dbConfig
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async connect () {
|
||||
async getDbConfig () {
|
||||
const dbConfig = {
|
||||
host: this._params.host,
|
||||
port: this._params.port,
|
||||
@ -100,15 +105,43 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._poolSize) {
|
||||
const client = new Client(dbConfig);
|
||||
await client.connect();
|
||||
this._connection = client;
|
||||
}
|
||||
else {
|
||||
const pool = new Pool({ ...dbConfig, max: this._poolSize });
|
||||
this._connection = pool;
|
||||
return dbConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async connect () {
|
||||
if (!this._poolSize)
|
||||
this._connection = await this.getConnection();
|
||||
else
|
||||
this._connection = await this.getConnectionPool();
|
||||
}
|
||||
|
||||
async getConnection () {
|
||||
const dbConfig = await this.getDbConfig();
|
||||
const client = new Client(dbConfig);
|
||||
await client.connect();
|
||||
const connection = client;
|
||||
|
||||
if (this._params.readonly)
|
||||
await connection.query('SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY');
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
async getConnectionPool () {
|
||||
const dbConfig = await this.getDbConfig();
|
||||
const pool = new Pool({ ...dbConfig, max: this._poolSize });
|
||||
const connection = pool;
|
||||
|
||||
if (this._params.readonly) {
|
||||
connection.on('connect', conn => {
|
||||
conn.query('SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY');
|
||||
});
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,15 +153,23 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an "USE" query
|
||||
* Executes an 'SET search_path TO "${schema}"' query
|
||||
*
|
||||
* @param {String} schema
|
||||
* @param {Object?} connection optional
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
use (schema) {
|
||||
use (schema, connection) {
|
||||
this._schema = schema;
|
||||
if (schema)
|
||||
return this.raw(`SET search_path TO "${schema}"`);
|
||||
|
||||
if (schema) {
|
||||
const sql = `SET search_path TO "${schema}"`;
|
||||
|
||||
if (connection === undefined)
|
||||
return this.raw(sql);
|
||||
else
|
||||
return connection.query(sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -143,6 +184,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
|
||||
const tablesArr = [];
|
||||
const triggersArr = [];
|
||||
let schemaSize = 0;
|
||||
|
||||
for (const db of databases) {
|
||||
if (!schemas.has(db.database)) continue;
|
||||
@ -168,19 +210,20 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
}
|
||||
|
||||
let { rows: triggers } = 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
|
||||
SELECT
|
||||
pg_class.relname AS table_name,
|
||||
pg_trigger.tgname AS trigger_name,
|
||||
pg_namespace.nspname AS trigger_schema,
|
||||
(pg_trigger.tgenabled != 'D')::bool AS enabled
|
||||
FROM pg_trigger
|
||||
JOIN pg_class ON pg_trigger.tgrelid = pg_class.oid
|
||||
JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
|
||||
JOIN information_schema.triggers ON information_schema.triggers.trigger_schema = pg_namespace.nspname
|
||||
AND information_schema.triggers.event_object_table = pg_class.relname
|
||||
AND information_schema.triggers.trigger_name = pg_trigger.tgname
|
||||
WHERE trigger_schema = '${db.database}'
|
||||
GROUP BY 1,2,3,4,6,7,8
|
||||
ORDER BY table_schema,
|
||||
table_name
|
||||
GROUP BY 1, 2, 3, 4
|
||||
ORDER BY table_name
|
||||
`);
|
||||
|
||||
if (triggers.length) {
|
||||
@ -196,11 +239,14 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
if (schemas.has(db.database)) {
|
||||
// TABLES
|
||||
const remappedTables = tablesArr.filter(table => table.Db === db.database).map(table => {
|
||||
const tableSize = +table.data_length + table.index_length;
|
||||
schemaSize += tableSize;
|
||||
|
||||
return {
|
||||
name: table.table_name,
|
||||
type: table.table_type === 'VIEW' ? 'view' : 'table',
|
||||
rows: table.reltuples,
|
||||
size: +table.data_length + +table.index_length,
|
||||
size: tableSize,
|
||||
collation: table.Collation,
|
||||
comment: table.comment,
|
||||
engine: ''
|
||||
@ -239,17 +285,16 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
return {
|
||||
name: `${trigger.table_name}.${trigger.trigger_name}`,
|
||||
orgName: trigger.trigger_name,
|
||||
timing: trigger.activation,
|
||||
definer: '',
|
||||
definition: trigger.definition,
|
||||
event: trigger.event,
|
||||
table: trigger.table_name,
|
||||
sqlMode: ''
|
||||
sqlMode: '',
|
||||
enabled: trigger.enabled
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
name: db.database,
|
||||
size: schemaSize,
|
||||
tables: remappedTables,
|
||||
functions: remappedFunctions,
|
||||
procedures: remappedProcedures,
|
||||
@ -261,6 +306,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
else {
|
||||
return {
|
||||
name: db.database,
|
||||
size: 0,
|
||||
tables: [],
|
||||
functions: [],
|
||||
procedures: [],
|
||||
@ -302,6 +348,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
isArray,
|
||||
schema: field.table_schema,
|
||||
table: field.table_name,
|
||||
numScale: field.numeric_scale,
|
||||
numPrecision: field.numeric_precision,
|
||||
datePrecision: field.datetime_precision,
|
||||
charLength: field.character_maximum_length,
|
||||
@ -500,7 +547,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
/**
|
||||
* CREATE SCHEMA
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async createSchema (params) {
|
||||
@ -510,7 +557,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
/**
|
||||
* ALTER DATABASE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async alterSchema (params) {
|
||||
@ -520,7 +567,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
/**
|
||||
* DROP DATABASE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof MySQLClient
|
||||
*/
|
||||
async dropSchema (params) {
|
||||
@ -552,7 +599,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
/**
|
||||
* DROP VIEW
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async dropView (params) {
|
||||
@ -563,7 +610,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
/**
|
||||
* ALTER VIEW
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async alterView (params) {
|
||||
@ -579,7 +626,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
/**
|
||||
* CREATE VIEW
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async createView (params) {
|
||||
@ -597,19 +644,25 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
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,
|
||||
SELECT
|
||||
information_schema.triggers.event_object_schema AS table_schema,
|
||||
information_schema.triggers.event_object_table AS table_name,
|
||||
information_schema.triggers.trigger_schema,
|
||||
information_schema.triggers.trigger_name,
|
||||
string_agg(event_manipulation, ',') AS EVENT,
|
||||
action_timing AS activation,
|
||||
action_condition AS condition,
|
||||
action_statement AS definition
|
||||
FROM information_schema.triggers
|
||||
action_statement AS definition,
|
||||
(pg_trigger.tgenabled != 'D')::bool AS enabled
|
||||
FROM pg_trigger
|
||||
JOIN pg_class ON pg_trigger.tgrelid = pg_class.oid
|
||||
JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
|
||||
JOIN information_schema.triggers ON pg_namespace.nspname = information_schema.triggers.trigger_schema
|
||||
AND pg_class.relname = information_schema.triggers.event_object_table
|
||||
WHERE trigger_schema = '${schema}'
|
||||
AND trigger_name = '${triggerName}'
|
||||
AND event_object_table = '${table}'
|
||||
GROUP BY 1,2,3,4,6,7,8
|
||||
GROUP BY 1,2,3,4,6,7,8,9
|
||||
ORDER BY table_schema,
|
||||
table_name
|
||||
`);
|
||||
@ -619,7 +672,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
sql: row.definition,
|
||||
name: row.trigger_name,
|
||||
table: row.table_name,
|
||||
event: row.event.split(','),
|
||||
event: [...new Set(row.event.split(','))],
|
||||
activation: row.activation
|
||||
};
|
||||
})[0];
|
||||
@ -628,7 +681,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
/**
|
||||
* DROP TRIGGER
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async dropTrigger (params) {
|
||||
@ -640,7 +693,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
/**
|
||||
* ALTER TRIGGER
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async alterTrigger (params) {
|
||||
@ -662,7 +715,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
/**
|
||||
* CREATE TRIGGER
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async createTrigger (params) {
|
||||
@ -671,6 +724,18 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
||||
async enableTrigger ({ schema, trigger }) {
|
||||
const [table, triggerName] = trigger.split('.');
|
||||
const sql = `ALTER TABLE "${schema}"."${table}" ENABLE TRIGGER "${triggerName}"`;
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
||||
async disableTrigger ({ schema, trigger }) {
|
||||
const [table, triggerName] = trigger.split('.');
|
||||
const sql = `ALTER TABLE "${schema}"."${table}" DISABLE TRIGGER "${triggerName}"`;
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* SHOW CREATE PROCEDURE
|
||||
*
|
||||
@ -1055,14 +1120,64 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} id
|
||||
* @returns {Promise<null>}
|
||||
*/
|
||||
async killProcess (id) {
|
||||
return await this.raw(`SELECT pg_terminate_backend(${id})`);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tabUid
|
||||
* @returns {Promise<null>}
|
||||
*/
|
||||
async killTabQuery (tabUid) {
|
||||
const id = this._runningConnections.get(tabUid);
|
||||
if (id)
|
||||
return await this.raw(`SELECT pg_cancel_backend(${id})`);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tabUid
|
||||
* @returns {Promise<null>}
|
||||
*/
|
||||
async commitTab (tabUid) {
|
||||
const connection = this._connectionsToCommit.get(tabUid);
|
||||
if (connection) {
|
||||
await connection.query('COMMIT');
|
||||
return this.destroyConnectionToCommit(tabUid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tabUid
|
||||
* @returns {Promise<null>}
|
||||
*/
|
||||
async rollbackTab (tabUid) {
|
||||
const connection = this._connectionsToCommit.get(tabUid);
|
||||
if (connection) {
|
||||
await connection.query('ROLLBACK');
|
||||
return this.destroyConnectionToCommit(tabUid);
|
||||
}
|
||||
}
|
||||
|
||||
destroyConnectionToCommit (tabUid) {
|
||||
const connection = this._connectionsToCommit.get(tabUid);
|
||||
if (connection) {
|
||||
connection.end();
|
||||
this._connectionsToCommit.delete(tabUid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CREATE TABLE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async createTable (params) {
|
||||
@ -1086,7 +1201,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
const length = typeInfo.length ? field.enumValues || field.numLength || field.charLength || field.datePrecision : false;
|
||||
|
||||
newColumns.push(`"${field.name}"
|
||||
${field.type.toUpperCase()}${length ? `(${length})` : ''}
|
||||
${field.type.toUpperCase()}${length ? `(${length}${field.numScale !== null ? `,${field.numScale}` : ''})` : ''}
|
||||
${field.unsigned ? 'UNSIGNED' : ''}
|
||||
${field.zerofill ? 'ZEROFILL' : ''}
|
||||
${field.nullable ? 'NULL' : 'NOT NULL'}
|
||||
@ -1120,7 +1235,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
/**
|
||||
* ALTER TABLE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async alterTable (params) {
|
||||
@ -1150,7 +1265,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
const length = typeInfo.length ? addition.numLength || addition.charLength || addition.datePrecision : false;
|
||||
|
||||
alterColumns.push(`ADD COLUMN "${addition.name}"
|
||||
${addition.type.toUpperCase()}${length ? `(${length})` : ''}${addition.isArray ? '[]' : ''}
|
||||
${addition.type.toUpperCase()}${length ? `(${length}${addition.numScale !== null ? `,${addition.numScale}` : ''})` : ''}${addition.isArray ? '[]' : ''}
|
||||
${addition.unsigned ? 'UNSIGNED' : ''}
|
||||
${addition.zerofill ? 'ZEROFILL' : ''}
|
||||
${addition.nullable ? 'NULL' : 'NOT NULL'}
|
||||
@ -1196,7 +1311,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
localType = change.type.toLowerCase();
|
||||
}
|
||||
|
||||
alterColumns.push(`ALTER COLUMN "${change.name}" TYPE ${localType}${length ? `(${length})` : ''}${change.isArray ? '[]' : ''} USING "${change.name}"::${localType}`);
|
||||
alterColumns.push(`ALTER COLUMN "${change.name}" TYPE ${localType}${length ? `(${length}${change.numScale ? `,${change.numScale}` : ''})` : ''}${change.isArray ? '[]' : ''} USING "${change.name}"::${localType}`);
|
||||
alterColumns.push(`ALTER COLUMN "${change.name}" ${change.nullable ? 'DROP NOT NULL' : 'SET NOT NULL'}`);
|
||||
alterColumns.push(`ALTER COLUMN "${change.name}" ${change.default ? `SET DEFAULT ${change.default}` : 'DROP DEFAULT'}`);
|
||||
|
||||
@ -1266,7 +1381,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
/**
|
||||
* DUPLICATE TABLE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async duplicateTable (params) {
|
||||
@ -1277,7 +1392,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
/**
|
||||
* TRUNCATE TABLE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async truncateTable (params) {
|
||||
@ -1288,7 +1403,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
/**
|
||||
* DROP TABLE
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @returns {Promise<null>}
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async dropTable (params) {
|
||||
@ -1363,17 +1478,17 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async raw (sql, args) {
|
||||
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
||||
|
||||
args = {
|
||||
nest: false,
|
||||
details: false,
|
||||
split: true,
|
||||
comments: true,
|
||||
autocommit: true,
|
||||
...args
|
||||
};
|
||||
|
||||
if (args.schema && args.schema !== 'public')
|
||||
await this.use(args.schema);
|
||||
|
||||
if (!args.comments)
|
||||
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments
|
||||
|
||||
@ -1385,7 +1500,26 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
.map(q => q.trim())
|
||||
: [sql];
|
||||
|
||||
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
||||
let connection;
|
||||
const isPool = this._connection instanceof Pool;
|
||||
|
||||
if (!args.autocommit && args.tabUid) { // autocommit OFF
|
||||
if (this._connectionsToCommit.has(args.tabUid))
|
||||
connection = this._connectionsToCommit.get(args.tabUid);
|
||||
else {
|
||||
connection = await this.getConnection();
|
||||
await connection.query('START TRANSACTION');
|
||||
this._connectionsToCommit.set(args.tabUid, connection);
|
||||
}
|
||||
}
|
||||
else// autocommit ON
|
||||
connection = isPool ? await this._connection.connect() : this._connection;
|
||||
|
||||
if (args.tabUid && isPool)
|
||||
this._runningConnections.set(args.tabUid, connection.processID);
|
||||
|
||||
if (args.schema && args.schema !== 'public')
|
||||
await this.use(args.schema, connection);
|
||||
|
||||
for (const query of queries) {
|
||||
if (!query) continue;
|
||||
@ -1395,15 +1529,12 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
let keysArr = [];
|
||||
|
||||
const { rows, report, fields, keys, duration } = await new Promise((resolve, reject) => {
|
||||
this._connection.query({
|
||||
rowMode: args.nest ? 'array' : null,
|
||||
text: query
|
||||
}, async (err, res) => {
|
||||
timeStop = new Date();
|
||||
(async () => {
|
||||
try {
|
||||
const res = await connection.query({ rowMode: args.nest ? 'array' : null, text: query });
|
||||
|
||||
timeStop = new Date();
|
||||
|
||||
if (err)
|
||||
reject(err);
|
||||
else {
|
||||
let ast;
|
||||
|
||||
try {
|
||||
@ -1492,6 +1623,10 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
if (isPool && args.autocommit) {
|
||||
connection.release();
|
||||
this._runningConnections.delete(args.tabUid);
|
||||
}
|
||||
reject(err);
|
||||
}
|
||||
|
||||
@ -1500,6 +1635,10 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
keysArr = keysArr ? [...keysArr, ...response] : response;
|
||||
}
|
||||
catch (err) {
|
||||
if (isPool && args.autocommit) {
|
||||
connection.release();
|
||||
this._runningConnections.delete(args.tabUid);
|
||||
}
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
@ -1514,12 +1653,24 @@ export class PostgreSQLClient extends AntaresCore {
|
||||
keys: keysArr
|
||||
});
|
||||
}
|
||||
});
|
||||
catch (err) {
|
||||
if (isPool && args.autocommit) {
|
||||
connection.release();
|
||||
this._runningConnections.delete(args.tabUid);
|
||||
}
|
||||
reject(err);
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
resultsArr.push({ rows, report, fields, keys, duration });
|
||||
}
|
||||
|
||||
if (isPool && args.autocommit) {
|
||||
connection.release();
|
||||
this._runningConnections.delete(args.tabUid);
|
||||
}
|
||||
|
||||
return resultsArr.length === 1 ? resultsArr[0] : resultsArr;
|
||||
}
|
||||
}
|
||||
|
859
src/main/libs/clients/SQLiteClient.js
Normal file
@ -0,0 +1,859 @@
|
||||
'use strict';
|
||||
import sqlite from 'better-sqlite3';
|
||||
import { AntaresCore } from '../AntaresCore';
|
||||
import dataTypes from 'common/data-types/sqlite';
|
||||
import { NUMBER, FLOAT, TIME, DATETIME } from 'common/fieldTypes';
|
||||
|
||||
export class SQLiteClient extends AntaresCore {
|
||||
constructor (args) {
|
||||
super(args);
|
||||
|
||||
this._schema = null;
|
||||
this._connectionsToCommit = new Map();
|
||||
}
|
||||
|
||||
_getTypeInfo (type) {
|
||||
return dataTypes
|
||||
.reduce((acc, group) => [...acc, ...group.types], [])
|
||||
.filter(_type => _type.name === type.toUpperCase())[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async connect () {
|
||||
this._connection = this.getConnection();
|
||||
}
|
||||
|
||||
getConnection () {
|
||||
return sqlite(this._params.databasePath, {
|
||||
fileMustExist: true,
|
||||
readonly: this._params.readonly
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
destroy () {}
|
||||
|
||||
/**
|
||||
* Executes an USE query
|
||||
*
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
use () {}
|
||||
|
||||
/**
|
||||
* @param {Array} schemas list
|
||||
* @returns {Array.<Object>} databases scructure
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getStructure (schemas) {
|
||||
const { rows: databases } = await this.raw('SELECT * FROM pragma_database_list');
|
||||
|
||||
const filteredDatabases = databases;
|
||||
|
||||
const tablesArr = [];
|
||||
const triggersArr = [];
|
||||
let schemaSize = 0;
|
||||
|
||||
for (const db of filteredDatabases) {
|
||||
if (!schemas.has(db.name)) continue;
|
||||
|
||||
let { rows: tables } = await this.raw(`
|
||||
SELECT *
|
||||
FROM "${db.name}".sqlite_master
|
||||
WHERE type IN ('table', 'view')
|
||||
AND name NOT LIKE 'sqlite_%'
|
||||
ORDER BY name
|
||||
`);
|
||||
if (tables.length) {
|
||||
tables = tables.map(table => {
|
||||
table.Db = db.name;
|
||||
return table;
|
||||
});
|
||||
tablesArr.push(...tables);
|
||||
}
|
||||
|
||||
let { rows: triggers } = await this.raw(`SELECT * FROM "${db.name}".sqlite_master WHERE type='trigger'`);
|
||||
if (triggers.length) {
|
||||
triggers = triggers.map(trigger => {
|
||||
trigger.Db = db.name;
|
||||
return trigger;
|
||||
});
|
||||
triggersArr.push(...triggers);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredDatabases.map(db => {
|
||||
if (schemas.has(db.name)) {
|
||||
// TABLES
|
||||
const remappedTables = tablesArr.filter(table => table.Db === db.name).map(table => {
|
||||
const tableSize = 0;
|
||||
schemaSize += tableSize;
|
||||
|
||||
return {
|
||||
name: table.name,
|
||||
type: table.type,
|
||||
rows: false,
|
||||
size: false
|
||||
};
|
||||
});
|
||||
|
||||
// TRIGGERS
|
||||
const remappedTriggers = triggersArr.filter(trigger => trigger.Db === db.name).map(trigger => {
|
||||
return {
|
||||
name: trigger.name,
|
||||
table: trigger.tbl_name
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
name: db.name,
|
||||
size: schemaSize,
|
||||
tables: remappedTables,
|
||||
functions: [],
|
||||
procedures: [],
|
||||
triggers: remappedTriggers,
|
||||
schedulers: []
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
name: db.name,
|
||||
size: 0,
|
||||
tables: [],
|
||||
functions: [],
|
||||
procedures: [],
|
||||
triggers: [],
|
||||
schedulers: []
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} params
|
||||
* @param {String} params.schema
|
||||
* @param {String} params.table
|
||||
* @returns {Object} table scructure
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getTableColumns ({ schema, table }) {
|
||||
const { rows: fields } = await this.raw(`SELECT * FROM "${schema}".pragma_table_info('${table}')`);
|
||||
|
||||
return fields.map(field => {
|
||||
const [type, length] = field.type.includes('(')
|
||||
? field.type.replace(')', '').split('(').map(el => {
|
||||
if (!isNaN(el)) el = +el;
|
||||
return el;
|
||||
})
|
||||
: [field.type, null];
|
||||
|
||||
return {
|
||||
name: field.name,
|
||||
key: null,
|
||||
type: type.trim(),
|
||||
schema: schema,
|
||||
table: table,
|
||||
numPrecision: [...NUMBER, ...FLOAT].includes(type) ? length : null,
|
||||
datePrecision: null,
|
||||
charLength: ![...NUMBER, ...FLOAT].includes(type) ? length : null,
|
||||
nullable: !field.notnull,
|
||||
unsigned: null,
|
||||
zerofill: null,
|
||||
order: field.cid + 1,
|
||||
default: field.dflt_value,
|
||||
charset: null,
|
||||
collation: null,
|
||||
autoIncrement: false,
|
||||
onUpdate: null,
|
||||
comment: ''
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} params
|
||||
* @param {String} params.schema
|
||||
* @param {String} params.table
|
||||
* @returns {Object} table row count
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getTableApproximateCount ({ schema, table }) {
|
||||
const { rows } = await this.raw(`SELECT COUNT(*) AS count FROM "${schema}"."${table}"`);
|
||||
|
||||
return rows.length ? rows[0].count : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} params
|
||||
* @param {String} params.schema
|
||||
* @param {String} params.table
|
||||
* @returns {Object} table options
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getTableOptions ({ schema, table }) {
|
||||
return { name: table };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} params
|
||||
* @param {String} params.schema
|
||||
* @param {String} params.table
|
||||
* @returns {Object} table indexes
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getTableIndexes ({ schema, table }) {
|
||||
const remappedIndexes = [];
|
||||
const { rows: primaryKeys } = await this.raw(`SELECT * FROM "${schema}".pragma_table_info('${table}') WHERE pk != 0`);
|
||||
|
||||
for (const key of primaryKeys) {
|
||||
remappedIndexes.push({
|
||||
name: 'PRIMARY',
|
||||
column: key.name,
|
||||
indexType: null,
|
||||
type: 'PRIMARY',
|
||||
cardinality: null,
|
||||
comment: '',
|
||||
indexComment: ''
|
||||
});
|
||||
}
|
||||
|
||||
const { rows: indexes } = await this.raw(`SELECT * FROM "${schema}".pragma_index_list('${table}');`);
|
||||
|
||||
for (const index of indexes) {
|
||||
const { rows: details } = await this.raw(`SELECT * FROM "${schema}".pragma_index_info('${index.name}');`);
|
||||
|
||||
for (const detail of details) {
|
||||
remappedIndexes.push({
|
||||
name: index.name,
|
||||
column: detail.name,
|
||||
indexType: null,
|
||||
type: index.unique === 1 ? 'UNIQUE' : 'INDEX',
|
||||
cardinality: null,
|
||||
comment: '',
|
||||
indexComment: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return remappedIndexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} params
|
||||
* @param {String} params.schema
|
||||
* @param {String} params.table
|
||||
* @returns {Object} table key usage
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getKeyUsage ({ schema, table }) {
|
||||
const { rows } = await this.raw(`SELECT * FROM "${schema}".pragma_foreign_key_list('${table}');`);
|
||||
|
||||
return rows.map(field => {
|
||||
return {
|
||||
schema: schema,
|
||||
table: table,
|
||||
field: field.from,
|
||||
position: field.id + 1,
|
||||
constraintPosition: null,
|
||||
constraintName: field.id,
|
||||
refSchema: schema,
|
||||
refTable: field.table,
|
||||
refField: field.to,
|
||||
onUpdate: field.on_update,
|
||||
onDelete: field.on_delete
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async getUsers () {}
|
||||
|
||||
/**
|
||||
* SHOW CREATE VIEW
|
||||
*
|
||||
* @returns {Array.<Object>} view informations
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getViewInformations ({ schema, view }) {
|
||||
const sql = `SELECT "sql" FROM "${schema}".sqlite_master WHERE "type"='view' AND name='${view}'`;
|
||||
const results = await this.raw(sql);
|
||||
|
||||
return results.rows.map(row => {
|
||||
return {
|
||||
sql: row.sql.match(/(?<=AS ).*?$/gs)[0],
|
||||
name: view
|
||||
};
|
||||
})[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* DROP VIEW
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async dropView (params) {
|
||||
const sql = `DROP VIEW "${params.schema}"."${params.view}"`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* ALTER VIEW
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async alterView (params) {
|
||||
const { view } = params;
|
||||
try {
|
||||
await this.dropView({ schema: view.schema, view: view.oldName });
|
||||
await this.createView(view);
|
||||
}
|
||||
catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CREATE VIEW
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async createView (params) {
|
||||
const sql = `CREATE VIEW "${params.schema}"."${params.name}" AS ${params.sql}`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* SHOW CREATE TRIGGER
|
||||
*
|
||||
* @returns {Array.<Object>} view informations
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getTriggerInformations ({ schema, trigger }) {
|
||||
const sql = `SELECT "sql" FROM "${schema}".sqlite_master WHERE "type"='trigger' AND name='${trigger}'`;
|
||||
const results = await this.raw(sql);
|
||||
|
||||
return results.rows.map(row => {
|
||||
return {
|
||||
sql: row.sql.match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
||||
name: trigger,
|
||||
table: row.sql.match(/(?<=ON `).*?(?=`)/gs)[0],
|
||||
activation: row.sql.match(/(BEFORE|AFTER)/gs)[0],
|
||||
event: row.sql.match(/(INSERT|UPDATE|DELETE)/gs)[0]
|
||||
};
|
||||
})[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* DROP TRIGGER
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async dropTrigger (params) {
|
||||
const sql = `DROP TRIGGER \`${params.schema}\`.\`${params.trigger}\``;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* ALTER TRIGGER
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async alterTrigger (params) {
|
||||
const { trigger } = params;
|
||||
const tempTrigger = Object.assign({}, trigger);
|
||||
tempTrigger.name = `Antares_${tempTrigger.name}_tmp`;
|
||||
|
||||
try {
|
||||
await this.createTrigger(tempTrigger);
|
||||
await this.dropTrigger({ schema: trigger.schema, trigger: tempTrigger.name });
|
||||
await this.dropTrigger({ schema: trigger.schema, trigger: trigger.oldName });
|
||||
await this.createTrigger(trigger);
|
||||
}
|
||||
catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CREATE TRIGGER
|
||||
*
|
||||
* @returns {Array.<Object>} parameters
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async createTrigger (params) {
|
||||
const sql = `CREATE ${params.definer ? `DEFINER=${params.definer} ` : ''}TRIGGER \`${params.schema}\`.\`${params.name}\` ${params.activation} ${params.event} ON \`${params.table}\` FOR EACH ROW ${params.sql}`;
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* SHOW COLLATION
|
||||
*
|
||||
* @returns {Array.<Object>} collations list
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getCollations () {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* SHOW VARIABLES
|
||||
*
|
||||
* @returns {Array.<Object>} variables list
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getVariables () {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* SHOW ENGINES
|
||||
*
|
||||
* @returns {Array.<Object>} engines list
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getEngines () {
|
||||
return {
|
||||
name: 'SQLite',
|
||||
support: 'YES',
|
||||
comment: '',
|
||||
isDefault: true
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* SHOW VARIABLES LIKE '%vers%'
|
||||
*
|
||||
* @returns {Array.<Object>} version parameters
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getVersion () {
|
||||
const os = require('os');
|
||||
const sql = 'SELECT sqlite_version() AS version';
|
||||
const { rows } = await this.raw(sql);
|
||||
|
||||
return {
|
||||
number: rows[0].version,
|
||||
name: 'SQLite',
|
||||
arch: process.arch,
|
||||
os: `${os.type()} ${os.release()}`
|
||||
};
|
||||
}
|
||||
|
||||
async getProcesses () {}
|
||||
|
||||
async killProcess () {}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tabUid
|
||||
* @returns {Promise<null>}
|
||||
*/
|
||||
async commitTab (tabUid) {
|
||||
const connection = this._connectionsToCommit.get(tabUid);
|
||||
if (connection) {
|
||||
connection.prepare('COMMIT').run();
|
||||
return this.destroyConnectionToCommit(tabUid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tabUid
|
||||
* @returns {Promise<null>}
|
||||
*/
|
||||
async rollbackTab (tabUid) {
|
||||
const connection = this._connectionsToCommit.get(tabUid);
|
||||
if (connection) {
|
||||
connection.prepare('ROLLBACK').run();
|
||||
return this.destroyConnectionToCommit(tabUid);
|
||||
}
|
||||
}
|
||||
|
||||
destroyConnectionToCommit (tabUid) {
|
||||
const connection = this._connectionsToCommit.get(tabUid);
|
||||
if (connection) {
|
||||
connection.close();
|
||||
this._connectionsToCommit.delete(tabUid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CREATE TABLE
|
||||
*
|
||||
* @returns {Promise<null>}
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async createTable (params) {
|
||||
const {
|
||||
schema,
|
||||
fields,
|
||||
foreigns,
|
||||
indexes,
|
||||
options
|
||||
} = params;
|
||||
const newColumns = [];
|
||||
const newIndexes = [];
|
||||
const manageIndexes = [];
|
||||
const newForeigns = [];
|
||||
|
||||
let sql = `CREATE TABLE "${schema}"."${options.name}"`;
|
||||
|
||||
// ADD FIELDS
|
||||
fields.forEach(field => {
|
||||
const typeInfo = this._getTypeInfo(field.type);
|
||||
const length = typeInfo?.length ? field.enumValues || field.numLength || field.charLength || field.datePrecision : false;
|
||||
|
||||
newColumns.push(`"${field.name}"
|
||||
${field.type.toUpperCase()}${length && length !== true ? `(${length})` : ''}
|
||||
${field.unsigned ? 'UNSIGNED' : ''}
|
||||
${field.nullable ? 'NULL' : 'NOT NULL'}
|
||||
${field.autoIncrement ? 'AUTO_INCREMENT' : ''}
|
||||
${field.default ? `DEFAULT ${field.default}` : ''}
|
||||
${field.onUpdate ? `ON UPDATE ${field.onUpdate}` : ''}`);
|
||||
});
|
||||
|
||||
// ADD INDEX
|
||||
indexes.forEach(index => {
|
||||
const fields = index.fields.map(field => `"${field}"`).join(',');
|
||||
const type = index.type;
|
||||
|
||||
if (type === 'PRIMARY')
|
||||
newIndexes.push(`PRIMARY KEY (${fields})`);
|
||||
else
|
||||
manageIndexes.push(`CREATE ${type === 'UNIQUE' ? type : ''} INDEX "${index.name}" ON "${options.name}" (${fields})`);
|
||||
});
|
||||
|
||||
// ADD FOREIGN KEYS
|
||||
foreigns.forEach(foreign => {
|
||||
newForeigns.push(`CONSTRAINT "${foreign.constraintName}" FOREIGN KEY ("${foreign.field}") REFERENCES "${foreign.refTable}" ("${foreign.refField}") ON UPDATE ${foreign.onUpdate} ON DELETE ${foreign.onDelete}`);
|
||||
});
|
||||
|
||||
sql = `${sql} (${[...newColumns, ...newIndexes, ...newForeigns].join(', ')})`;
|
||||
if (manageIndexes.length) sql = `${sql}; ${manageIndexes.join(';')}`;
|
||||
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* ALTER TABLE
|
||||
*
|
||||
* @returns {Promise<null>}
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async alterTable (params) {
|
||||
try {
|
||||
await this.raw('BEGIN TRANSACTION');
|
||||
await this.raw('PRAGMA foreign_keys = 0');
|
||||
|
||||
const tmpName = `Antares_${params.table}_tmp`;
|
||||
await this.raw(`CREATE TABLE "${tmpName}" AS SELECT * FROM "${params.table}"`);
|
||||
await this.dropTable(params);
|
||||
|
||||
const createTableParams = {
|
||||
schema: params.schema,
|
||||
fields: params.tableStructure.fields,
|
||||
foreigns: params.tableStructure.foreigns,
|
||||
indexes: params.tableStructure.indexes.filter(index => !index.name.includes('sqlite_autoindex')),
|
||||
options: { name: params.tableStructure.name }
|
||||
};
|
||||
await this.createTable(createTableParams);
|
||||
const insertFields = createTableParams.fields
|
||||
.filter(field => {
|
||||
return (
|
||||
params.additions.every(add => add.name !== field.name) &&
|
||||
params.deletions.every(del => del.name !== field.name)
|
||||
);
|
||||
})
|
||||
.reduce((acc, curr) => {
|
||||
acc.push(`"${curr.name}"`);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const selectFields = insertFields.map(field => {
|
||||
const renamedField = params.changes.find(change => `"${change.name}"` === field);
|
||||
if (renamedField)
|
||||
return `"${renamedField.orgName}"`;
|
||||
return field;
|
||||
});
|
||||
|
||||
await this.raw(`INSERT INTO "${createTableParams.options.name}" (${insertFields.join(',')}) SELECT ${selectFields.join(',')} FROM "${tmpName}"`);
|
||||
|
||||
await this.dropTable({ schema: params.schema, table: tmpName });
|
||||
await this.raw('PRAGMA foreign_keys = 1');
|
||||
await this.raw('COMMIT');
|
||||
}
|
||||
catch (err) {
|
||||
await this.raw('ROLLBACK');
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DUPLICATE TABLE
|
||||
*
|
||||
* @returns {Promise<null>}
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async duplicateTable (params) { // TODO: retrive table informations and create a copy
|
||||
const sql = `CREATE TABLE "${params.schema}"."${params.table}_copy" AS SELECT * FROM "${params.schema}"."${params.table}"`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* TRUNCATE TABLE
|
||||
*
|
||||
* @returns {Promise<null>}
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async truncateTable (params) {
|
||||
const sql = `DELETE FROM "${params.schema}"."${params.table}"`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* DROP TABLE
|
||||
*
|
||||
* @returns {Promise<null>}
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async dropTable (params) {
|
||||
const sql = `DROP TABLE "${params.schema}"."${params.table}"`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {String} SQL string
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
getSQL () {
|
||||
// SELECT
|
||||
const selectArray = this._query.select.reduce(this._reducer, []);
|
||||
let selectRaw = '';
|
||||
|
||||
if (selectArray.length)
|
||||
selectRaw = selectArray.length ? `SELECT ${selectArray.join(', ')} ` : 'SELECT * ';
|
||||
|
||||
// FROM
|
||||
let fromRaw = '';
|
||||
|
||||
if (!this._query.update.length && !Object.keys(this._query.insert).length && !!this._query.from)
|
||||
fromRaw = 'FROM';
|
||||
else if (Object.keys(this._query.insert).length)
|
||||
fromRaw = 'INTO';
|
||||
|
||||
fromRaw += this._query.from ? ` ${this._query.schema ? `"${this._query.schema}".` : ''}"${this._query.from}" ` : '';
|
||||
|
||||
// WHERE
|
||||
const whereArray = this._query.where
|
||||
.reduce(this._reducer, [])
|
||||
?.map(clausole => clausole.replace('= null', 'IS NULL'));
|
||||
const whereRaw = whereArray.length ? `WHERE ${whereArray.join(' AND ')} ` : '';
|
||||
|
||||
// UPDATE
|
||||
const updateArray = this._query.update.reduce(this._reducer, []);
|
||||
const updateRaw = updateArray.length ? `SET ${updateArray.join(', ')} ` : '';
|
||||
|
||||
// INSERT
|
||||
let insertRaw = '';
|
||||
|
||||
if (this._query.insert.length) {
|
||||
const fieldsList = Object.keys(this._query.insert[0]);
|
||||
const rowsList = this._query.insert.map(el => `(${Object.values(el).join(', ')})`);
|
||||
|
||||
insertRaw = `(${fieldsList.join(', ')}) VALUES ${rowsList.join(', ')} `;
|
||||
}
|
||||
|
||||
// GROUP BY
|
||||
const groupByArray = this._query.groupBy.reduce(this._reducer, []);
|
||||
const groupByRaw = groupByArray.length ? `GROUP BY ${groupByArray.join(', ')} ` : '';
|
||||
|
||||
// ORDER BY
|
||||
const orderByArray = this._query.orderBy.reduce(this._reducer, []);
|
||||
const orderByRaw = orderByArray.length ? `ORDER BY ${orderByArray.join(', ')} ` : '';
|
||||
|
||||
// LIMIT
|
||||
const limitRaw = this._query.limit.length ? `LIMIT ${this._query.limit.join(', ')} ` : '';
|
||||
|
||||
// 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}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} sql raw SQL query
|
||||
* @param {object} args
|
||||
* @param {boolean} args.nest
|
||||
* @param {boolean} args.details
|
||||
* @param {boolean} args.split
|
||||
* @returns {Promise}
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async raw (sql, args) {
|
||||
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
||||
|
||||
args = {
|
||||
nest: false,
|
||||
details: false,
|
||||
split: true,
|
||||
comments: true,
|
||||
autocommit: true,
|
||||
...args
|
||||
};
|
||||
|
||||
if (!args.comments)
|
||||
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments
|
||||
|
||||
const resultsArr = [];
|
||||
let paramsArr = [];
|
||||
const queries = args.split
|
||||
? sql.split(/((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm)
|
||||
.filter(Boolean)
|
||||
.map(q => q.trim())
|
||||
: [sql];
|
||||
|
||||
let connection;
|
||||
|
||||
if (!args.autocommit && args.tabUid) { // autocommit OFF
|
||||
if (this._connectionsToCommit.has(args.tabUid))
|
||||
connection = this._connectionsToCommit.get(args.tabUid);
|
||||
else {
|
||||
connection = this.getConnection();
|
||||
connection.prepare('BEGIN TRANSACTION').run();
|
||||
this._connectionsToCommit.set(args.tabUid, connection);
|
||||
}
|
||||
}
|
||||
else// autocommit ON
|
||||
connection = this._connection;
|
||||
|
||||
for (const query of queries) {
|
||||
if (!query) continue;
|
||||
const timeStart = new Date();
|
||||
let timeStop;
|
||||
const keysArr = [];
|
||||
|
||||
const { rows, report, fields, keys, duration } = await new Promise((resolve, reject) => {
|
||||
(async () => {
|
||||
let queryResult;
|
||||
let affectedRows;
|
||||
let fields;
|
||||
const detectedTypes = {};
|
||||
|
||||
try {
|
||||
const stmt = connection.prepare(query);
|
||||
|
||||
if (stmt.reader) {
|
||||
queryResult = stmt.all();
|
||||
fields = stmt.columns();
|
||||
|
||||
if (queryResult.length) {
|
||||
fields.forEach(field => {
|
||||
detectedTypes[field.name] = typeof queryResult[0][field.name];
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
const info = queryResult = stmt.run();
|
||||
affectedRows = info.changes;
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
timeStop = new Date();
|
||||
|
||||
let remappedFields = fields
|
||||
? fields.map(field => {
|
||||
let [parsedType, length] = field.type?.includes('(')
|
||||
? field.type.replace(')', '').split('(').map(el => {
|
||||
if (!isNaN(el))
|
||||
el = +el;
|
||||
else
|
||||
el = el.trim();
|
||||
return el;
|
||||
})
|
||||
: [field.type, null];
|
||||
|
||||
if ([...TIME, ...DATETIME].includes(parsedType)) {
|
||||
const firstNotNull = queryResult.find(res => res[field.name] !== null);
|
||||
if (firstNotNull && firstNotNull[field.name].includes('.'))
|
||||
length = firstNotNull[field.name].split('.').pop().length;
|
||||
}
|
||||
|
||||
return {
|
||||
name: field.name,
|
||||
alias: field.name,
|
||||
orgName: field.column,
|
||||
schema: field.database,
|
||||
table: field.table,
|
||||
tableAlias: field.table,
|
||||
orgTable: field.table,
|
||||
type: field.type !== null ? parsedType : detectedTypes[field.name],
|
||||
length
|
||||
};
|
||||
}).filter(Boolean)
|
||||
: [];
|
||||
|
||||
if (args.details) {
|
||||
paramsArr = remappedFields.map(field => {
|
||||
return {
|
||||
table: field.table,
|
||||
schema: field.schema
|
||||
};
|
||||
}).filter((val, i, arr) => arr.findIndex(el => el.schema === val.schema && el.table === val.table) === i);
|
||||
|
||||
for (const paramObj of paramsArr) {
|
||||
if (!paramObj.table || !paramObj.schema) continue;
|
||||
|
||||
try {
|
||||
const indexes = await this.getTableIndexes(paramObj);
|
||||
|
||||
remappedFields = remappedFields.map(field => {
|
||||
// const detailedField = columns.find(f => f.name === field.name);
|
||||
const fieldIndex = indexes.find(i => i.column === field.name);
|
||||
if (field.table === paramObj.table && field.schema === paramObj.schema) {
|
||||
// if (detailedField) {
|
||||
// const length = detailedField.numPrecision || detailedField.charLength || detailedField.datePrecision || null;
|
||||
// field = { ...field, ...detailedField, length };
|
||||
// }
|
||||
|
||||
if (fieldIndex) {
|
||||
const key = fieldIndex.type === 'PRIMARY' ? 'pri' : fieldIndex.type === 'UNIQUE' ? 'uni' : 'mul';
|
||||
field = { ...field, key };
|
||||
};
|
||||
}
|
||||
|
||||
return field;
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolve({
|
||||
duration: timeStop - timeStart,
|
||||
rows: Array.isArray(queryResult) ? queryResult.some(el => Array.isArray(el)) ? [] : queryResult : false,
|
||||
report: affectedRows !== undefined ? { affectedRows } : null,
|
||||
fields: remappedFields,
|
||||
keys: keysArr
|
||||
});
|
||||
})();
|
||||
});
|
||||
|
||||
resultsArr.push({ rows, report, fields, keys, duration });
|
||||
}
|
||||
|
||||
return resultsArr.length === 1 ? resultsArr[0] : resultsArr;
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
import { app, BrowserWindow, /* session, */ nativeImage, Menu } from 'electron';
|
||||
import * as path from 'path';
|
||||
import Store from 'electron-store';
|
||||
import * as windowStateKeeper from 'electron-window-state';
|
||||
import * as remoteMain from '@electron/remote/main';
|
||||
|
||||
import ipcHandlers from './ipc-handlers';
|
||||
@ -18,15 +19,18 @@ process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true';
|
||||
|
||||
// global reference to mainWindow (necessary to prevent window from being garbage collected)
|
||||
let mainWindow;
|
||||
let mainWindowState;
|
||||
|
||||
async function createMainWindow () {
|
||||
const icon = require('../renderer/images/logo-32.png');
|
||||
const window = new BrowserWindow({
|
||||
width: 1024,
|
||||
height: 800,
|
||||
width: mainWindowState.width,
|
||||
height: mainWindowState.height,
|
||||
x: mainWindowState.x,
|
||||
y: mainWindowState.y,
|
||||
minWidth: 900,
|
||||
minHeight: 550,
|
||||
title: 'Antares',
|
||||
title: 'Antares SQL',
|
||||
autoHideMenuBar: true,
|
||||
icon: nativeImage.createFromDataURL(icon.default),
|
||||
webPreferences: {
|
||||
@ -41,6 +45,9 @@ async function createMainWindow () {
|
||||
backgroundColor: '#1d1d1d'
|
||||
});
|
||||
|
||||
mainWindowState.manage(window);
|
||||
window.on('moved', saveWindowState);
|
||||
|
||||
remoteMain.enable(window.webContents);
|
||||
|
||||
try {
|
||||
@ -70,16 +77,10 @@ async function createMainWindow () {
|
||||
}
|
||||
|
||||
window.on('closed', () => {
|
||||
window.removeListener('moved', saveWindowState);
|
||||
mainWindow = null;
|
||||
});
|
||||
|
||||
window.webContents.on('devtools-opened', () => {
|
||||
window.focus();
|
||||
setImmediate(() => {
|
||||
window.focus();
|
||||
});
|
||||
});
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
@ -104,17 +105,22 @@ else {
|
||||
|
||||
// create main BrowserWindow when electron is ready
|
||||
app.on('ready', async () => {
|
||||
mainWindowState = windowStateKeeper({
|
||||
defaultWidth: 1024,
|
||||
defaultHeight: 800
|
||||
});
|
||||
|
||||
mainWindow = await createMainWindow();
|
||||
createAppMenu();
|
||||
|
||||
if (isDevelopment)
|
||||
mainWindow.webContents.openDevTools();
|
||||
// if (isDevelopment)
|
||||
// mainWindow.webContents.openDevTools();
|
||||
|
||||
process.on('uncaughtException', (error) => {
|
||||
process.on('uncaughtException', error => {
|
||||
mainWindow.webContents.send('unhandled-exception', error);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (error) => {
|
||||
process.on('unhandledRejection', error => {
|
||||
mainWindow.webContents.send('unhandled-exception', error);
|
||||
});
|
||||
});
|
||||
@ -160,3 +166,7 @@ function createAppMenu () {
|
||||
|
||||
Menu.setApplicationMenu(menu);
|
||||
}
|
||||
|
||||
function saveWindowState () {
|
||||
mainWindowState.saveState(mainWindow);
|
||||
}
|
||||
|
108
src/renderer/components/BaseMap.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div id="map" class="map" />
|
||||
</template>
|
||||
<script>
|
||||
import L from 'leaflet';
|
||||
import {
|
||||
point,
|
||||
lineString,
|
||||
polygon
|
||||
} from '@turf/helpers';
|
||||
import { getArrayDepth } from 'common/libs/getArrayDepth';
|
||||
|
||||
export default {
|
||||
name: 'BaseMap',
|
||||
props: {
|
||||
points: [Object, Array],
|
||||
isMultiSpatial: Boolean
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
map: null,
|
||||
markers: [],
|
||||
center: null
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
if (this.isMultiSpatial) {
|
||||
for (const element of this.points)
|
||||
this.markers.push(this.getMarkers(element));
|
||||
}
|
||||
else {
|
||||
this.markers = this.getMarkers(this.points);
|
||||
|
||||
if (!Array.isArray(this.points))
|
||||
this.center = [this.points.y, this.points.x];
|
||||
}
|
||||
|
||||
this.map = L.map('map', {
|
||||
center: this.center || [0, 0],
|
||||
zoom: 15,
|
||||
minZoom: 1,
|
||||
attributionControl: false
|
||||
});
|
||||
|
||||
L.control.attribution({ prefix: '<b>Leaflet</b>' }).addTo(this.map);
|
||||
|
||||
const geoJsonObj = L.geoJSON(this.markers, {
|
||||
style: function () {
|
||||
return {
|
||||
weight: 2,
|
||||
fillColor: '#ff7800',
|
||||
color: '#ff7800',
|
||||
opacity: 0.8,
|
||||
fillOpacity: 0.4
|
||||
};
|
||||
},
|
||||
pointToLayer: function (feature, latlng) {
|
||||
return L.circleMarker(latlng, {
|
||||
radius: 7,
|
||||
weight: 2,
|
||||
fillColor: '#ff7800',
|
||||
color: '#ff7800',
|
||||
opacity: 0.8,
|
||||
fillOpacity: 0.4
|
||||
});
|
||||
}
|
||||
}).addTo(this.map);
|
||||
|
||||
const southWest = L.latLng(-90, -180);
|
||||
const northEast = L.latLng(90, 180);
|
||||
const bounds = L.latLngBounds(southWest, northEast);
|
||||
this.map.setMaxBounds(bounds);
|
||||
|
||||
if (!this.center) this.map.fitBounds(geoJsonObj.getBounds());
|
||||
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <b>OpenStreetMap</b>'
|
||||
}).addTo(this.map);
|
||||
},
|
||||
methods: {
|
||||
getMarkers (points) {
|
||||
if (Array.isArray(points)) {
|
||||
if (getArrayDepth(points) === 1)
|
||||
return lineString(points.reduce((acc, curr) => [...acc, [curr.x, curr.y]], []));
|
||||
else
|
||||
return polygon(points.map(arr => arr.reduce((acc, curr) => [...acc, [curr.x, curr.y]], [])));
|
||||
}
|
||||
else
|
||||
return point([points.x, points.y]);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.map{
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.marker-icon{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: $primary-color;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 5px 1px darken($body-font-color-dark, 40%);
|
||||
}
|
||||
</style>
|
@ -89,7 +89,7 @@
|
||||
:type="inputProps().type"
|
||||
:disabled="!isChecked"
|
||||
>
|
||||
<template v-if="methodData && 'params' in methodData" class="columns">
|
||||
<template v-if="methodData && 'params' in methodData">
|
||||
<input
|
||||
v-for="(option, key) in methodData.params"
|
||||
:key="key"
|
||||
|
@ -6,18 +6,18 @@
|
||||
@confirm="runRoutine"
|
||||
@hide="closeModal"
|
||||
>
|
||||
<template slot="header">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-play mr-1" />
|
||||
<span class="cut-text">{{ $t('word.parameters') }}: {{ localRoutine.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div slot="body">
|
||||
<template #body>
|
||||
<div class="content">
|
||||
<form class="form-horizontal">
|
||||
<div
|
||||
v-for="(parameter, i) in inParameters"
|
||||
:key="parameter._id"
|
||||
:key="parameter._antares_id"
|
||||
class="form-group"
|
||||
>
|
||||
<div class="col-4">
|
||||
@ -43,7 +43,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</template>
|
||||
|
||||
|
@ -5,16 +5,16 @@
|
||||
@confirm="$emit('confirm')"
|
||||
@hide="$emit('close')"
|
||||
>
|
||||
<template slot="header">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-content-save-alert mr-1" /> {{ $t('message.unsavedChanges') }}
|
||||
</div>
|
||||
</template>
|
||||
<div slot="body">
|
||||
<template #body>
|
||||
<div>
|
||||
{{ $t('message.discardUnsavedChanges') }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</template>
|
||||
|
||||
|
@ -170,7 +170,7 @@
|
||||
KiB
|
||||
</option>
|
||||
<option value="rows">
|
||||
{{ $t('word.rows') }}
|
||||
{{ $tc('word.row', 2) }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<div class="modal-title h6">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||
<span class="cut-text">{{ $t('message.tableFiller') }}</span>
|
||||
<span class="cut-text">{{ $tc('message.insertRow', 2) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||
@ -41,7 +41,7 @@
|
||||
<label class="form-checkbox ml-3" :title="$t('word.insert')">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="!field.autoIncrement"
|
||||
:checked="!fieldsToExclude.includes(field.name)"
|
||||
@change.prevent="toggleFields($event, field)"
|
||||
><i class="form-icon" />
|
||||
</label>
|
||||
@ -264,7 +264,7 @@ export default {
|
||||
else if (BIT.includes(field.type))
|
||||
fieldDefault = field.default.replaceAll('\'', '').replaceAll('b', '');
|
||||
else if (DATETIME.includes(field.type)) {
|
||||
if (field.default && ['current_timestamp', 'now()'].includes(field.default.toLowerCase())) {
|
||||
if (field.default && ['current_timestamp', 'now()'].some(term => field.default.toLowerCase().includes(term))) {
|
||||
let datePrecision = '';
|
||||
for (let i = 0; i < field.datePrecision; i++)
|
||||
datePrecision += i === 0 ? '.S' : 'S';
|
||||
@ -281,7 +281,7 @@ export default {
|
||||
|
||||
rowObj[field.name] = { value: fieldDefault };
|
||||
|
||||
if (field.autoIncrement)// Disable by default auto increment fields
|
||||
if (field.autoIncrement || !!field.onUpdate)// Disable by default auto increment or "on update" fields
|
||||
this.fieldsToExclude = [...this.fieldsToExclude, field.name];
|
||||
}
|
||||
|
||||
|
@ -253,6 +253,7 @@ export default {
|
||||
font-size: 100%;
|
||||
// color: $primary-color;
|
||||
opacity: 0.8;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tile-subtitle {
|
||||
|
@ -22,12 +22,12 @@
|
||||
:hide-footer="true"
|
||||
@hide="hideInfoModal"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-information-outline mr-1" /> {{ $t('message.processInfo') }}
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<template #body>
|
||||
<div>
|
||||
<div>
|
||||
<TextEditor
|
||||
@ -38,7 +38,7 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -396,7 +396,7 @@ export default {
|
||||
return locales;
|
||||
},
|
||||
hasUpdates () {
|
||||
return ['available', 'downloading', 'downloaded'].includes(this.updateStatus);
|
||||
return ['available', 'downloading', 'downloaded', 'link'].includes(this.updateStatus);
|
||||
},
|
||||
workspace () {
|
||||
return this.getWorkspace(this.selectedWorkspace);
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import marked from 'marked';
|
||||
import { marked } from 'marked';
|
||||
import BaseLoader from '@/components/BaseLoader';
|
||||
|
||||
export default {
|
||||
|
@ -29,12 +29,19 @@
|
||||
{{ $t('message.checkForUpdates') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="updateStatus === 'downloaded'"
|
||||
v-else-if="updateStatus === 'downloaded'"
|
||||
class="btn btn-primary"
|
||||
@click="restartToUpdate"
|
||||
>
|
||||
{{ $t('message.restartToInstall') }}
|
||||
</button>
|
||||
<button
|
||||
v-else-if="updateStatus === 'link'"
|
||||
class="btn btn-primary"
|
||||
@click="openOutside('https://antares-sql.app/download.html')"
|
||||
>
|
||||
{{ $t('message.goToDownloadPage') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group mt-4">
|
||||
<label class="form-switch d-inline-block disabled" @click.prevent="toggleAllowPrerelease">
|
||||
@ -47,7 +54,7 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { ipcRenderer, shell } from 'electron';
|
||||
|
||||
export default {
|
||||
name: 'ModalSettingsUpdate',
|
||||
@ -71,6 +78,8 @@ export default {
|
||||
return this.$t('message.downloadingUpdate');
|
||||
case 'downloaded':
|
||||
return this.$t('message.updateDownloaded');
|
||||
case 'link':
|
||||
return this.$t('message.updateAvailable');
|
||||
default:
|
||||
return this.updateStatus;
|
||||
}
|
||||
@ -80,6 +89,9 @@ export default {
|
||||
...mapActions({
|
||||
changeAllowPrerelease: 'settings/changeAllowPrerelease'
|
||||
}),
|
||||
openOutside (link) {
|
||||
shell.openExternal(link);
|
||||
},
|
||||
checkForUpdates () {
|
||||
ipcRenderer.send('check-for-updates');
|
||||
},
|
||||
|
@ -15,16 +15,16 @@
|
||||
@confirm="confirmDeleteConnection"
|
||||
@hide="hideConfirmModal"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-server-remove mr-1" /> {{ $t('message.deleteConnection') }}
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<template #body>
|
||||
<div class="mb-2">
|
||||
{{ $t('message.deleteCorfirm') }} <b>{{ connectionName }}</b>?
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</BaseContextMenu>
|
||||
</template>
|
||||
|
@ -11,9 +11,9 @@
|
||||
|
||||
<div class="footer-right-elements">
|
||||
<ul class="footer-elements">
|
||||
<li class="footer-element footer-link" @click="openOutside('https://www.treedom.net/en/user/fabio-di-stasio/event/antares-for-the-planet')">
|
||||
<i class="mdi mdi-18px mdi-tree mr-1" />
|
||||
<small>{{ $t('message.plantATree') }}</small>
|
||||
<li class="footer-element footer-link" @click="openOutside('https://www.paypal.com/paypalme/fabiodistasio')">
|
||||
<i class="mdi mdi-18px mdi-coffee mr-1" />
|
||||
<small>{{ $t('word.donate') }}</small>
|
||||
</li>
|
||||
<li class="footer-element footer-link" @click="openOutside('https://github.com/Fabio286/antares/issues')">
|
||||
<i class="mdi mdi-18px mdi-bug" />
|
||||
|
@ -40,15 +40,13 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
notifications: {
|
||||
deep: true,
|
||||
handler: function (notification) {
|
||||
if (notification.length) {
|
||||
this.timeouts[notification[0].uid] = setTimeout(() => {
|
||||
this.removeNotification(notification[0].uid);
|
||||
delete this.timeouts[notification.uid];
|
||||
}, this.notificationsTimeout * 1000);
|
||||
}
|
||||
'notifications.length': function (val) {
|
||||
if (val > 0) {
|
||||
const nUid = this.notifications[0].uid;
|
||||
this.timeouts[nUid] = setTimeout(() => {
|
||||
this.removeNotification(nUid);
|
||||
delete this.timeouts[nUid];
|
||||
}, this.notificationsTimeout * 1000);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -63,11 +61,14 @@ export default {
|
||||
}
|
||||
},
|
||||
rearmTimeouts () {
|
||||
const delay = 50;
|
||||
let i = this.notifications.length * delay;
|
||||
for (const notification of this.notifications) {
|
||||
this.timeouts[notification.uid] = setTimeout(() => {
|
||||
this.removeNotification(notification.uid);
|
||||
delete this.timeouts[notification.uid];
|
||||
}, this.notificationsTimeout * 1000);
|
||||
}, (this.notificationsTimeout * 1000) + i);
|
||||
i = i > delay ? i - delay : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,12 @@
|
||||
:hide-footer="true"
|
||||
@hide="hideScratchpad"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-notebook-edit-outline mr-1" /> {{ $t('word.scratchpad') }}
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<template #body>
|
||||
<div>
|
||||
<div>
|
||||
<TextEditor
|
||||
@ -24,7 +24,7 @@
|
||||
</div>
|
||||
<small class="text-gray">{{ $t('message.markdownSupported') }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</template>
|
||||
|
||||
|
@ -92,7 +92,7 @@ export default {
|
||||
}
|
||||
},
|
||||
hasUpdates () {
|
||||
return ['available', 'downloading', 'downloaded'].includes(this.updateStatus);
|
||||
return ['available', 'downloading', 'downloaded', 'link'].includes(this.updateStatus);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -200,7 +200,7 @@ export default {
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
content: "";
|
||||
height: 0;
|
||||
width: 3px;
|
||||
transition: height 0.2s;
|
||||
|
@ -18,14 +18,17 @@
|
||||
<li
|
||||
v-for="(tab, i) of draggableTabs"
|
||||
:key="i"
|
||||
:ref="selectedTab === tab.uid ? 'tab-selected' : ''"
|
||||
class="tab-item tab-draggable"
|
||||
draggable="true"
|
||||
:class="{'active': selectedTab === tab.uid}"
|
||||
@mousedown.left="selectTab({uid: workspace.uid, tab: tab.uid})"
|
||||
@mouseup.middle="closeTab(tab)"
|
||||
>
|
||||
<a v-if="tab.type === 'query'" class="tab-link">
|
||||
<a
|
||||
v-if="tab.type === 'query'"
|
||||
class="tab-link"
|
||||
:class="{'badge': tab.isChanged}"
|
||||
>
|
||||
<i class="mdi mdi-18px mdi-code-tags mr-1" />
|
||||
<span>
|
||||
<span>{{ tab.content || 'Query' | cutText }} #{{ tab.index }}</span>
|
||||
@ -256,52 +259,59 @@
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li slot="header" class="tab-item dropdown tools-dropdown">
|
||||
<a
|
||||
class="tab-link workspace-tools-link dropdown-toggle"
|
||||
tabindex="0"
|
||||
:title="$t('word.tools')"
|
||||
<template #header>
|
||||
<li
|
||||
v-if="workspace.customizations.processesList"
|
||||
class="tab-item dropdown tools-dropdown"
|
||||
>
|
||||
<i class="mdi mdi-24px mdi-tools" />
|
||||
</a>
|
||||
<ul class="menu text-left text-uppercase">
|
||||
<li v-if="workspace.customizations.processesList" class="menu-item">
|
||||
<a class="c-hand p-vcentered" @click="showProcessesModal">
|
||||
<i class="mdi mdi-memory mr-1 tool-icon" />
|
||||
<span>{{ $t('message.processesList') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
v-if="workspace.customizations.variables"
|
||||
class="menu-item"
|
||||
title="Coming..."
|
||||
<a
|
||||
class="tab-link workspace-tools-link dropdown-toggle"
|
||||
tabindex="0"
|
||||
:title="$t('word.tools')"
|
||||
>
|
||||
<a class="c-hand p-vcentered disabled">
|
||||
<i class="mdi mdi-shape mr-1 tool-icon" />
|
||||
<span>{{ $t('word.variables') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
v-if="workspace.customizations.usersManagement"
|
||||
class="menu-item"
|
||||
title="Coming..."
|
||||
<i class="mdi mdi-24px mdi-tools" />
|
||||
</a>
|
||||
<ul v-if="hasTools" class="menu text-left text-uppercase">
|
||||
<li class="menu-item">
|
||||
<a class="c-hand p-vcentered" @click="showProcessesModal">
|
||||
<i class="mdi mdi-memory mr-1 tool-icon" />
|
||||
<span>{{ $t('message.processesList') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
v-if="workspace.customizations.variables"
|
||||
class="menu-item"
|
||||
title="Coming..."
|
||||
>
|
||||
<a class="c-hand p-vcentered disabled">
|
||||
<i class="mdi mdi-shape mr-1 tool-icon" />
|
||||
<span>{{ $t('word.variables') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
v-if="workspace.customizations.usersManagement"
|
||||
class="menu-item"
|
||||
title="Coming..."
|
||||
>
|
||||
<a class="c-hand p-vcentered disabled">
|
||||
<i class="mdi mdi-account-group mr-1 tool-icon" />
|
||||
<span>{{ $t('message.manageUsers') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
<template #footer>
|
||||
<li class="tab-item">
|
||||
<a
|
||||
class="tab-add"
|
||||
:title="$t('message.openNewTab')"
|
||||
@click="addQueryTab"
|
||||
>
|
||||
<a class="c-hand p-vcentered disabled">
|
||||
<i class="mdi mdi-account-group mr-1 tool-icon" />
|
||||
<span>{{ $t('message.manageUsers') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li slot="footer" class="tab-item">
|
||||
<a
|
||||
class="tab-add"
|
||||
:title="$t('message.openNewTab')"
|
||||
@click="addQueryTab"
|
||||
>
|
||||
<i class="mdi mdi-24px mdi-plus" />
|
||||
</a>
|
||||
</li>
|
||||
<i class="mdi mdi-24px mdi-plus" />
|
||||
</a>
|
||||
</li>
|
||||
</template>
|
||||
</Draggable>
|
||||
<WorkspaceEmptyState v-if="!workspace.tabs.length" @new-tab="addQueryTab" />
|
||||
<template v-for="tab of workspace.tabs">
|
||||
@ -561,7 +571,7 @@ export default {
|
||||
return this.workspace ? this.workspace.selectedTab : null;
|
||||
},
|
||||
queryTabs () {
|
||||
return this.workspace.tabs.filter(tab => tab.type === 'query');
|
||||
return this.workspace ? this.workspace.tabs.filter(tab => tab.type === 'query') : [];
|
||||
},
|
||||
schemaChild () {
|
||||
for (const key in this.workspace.breadcrumbs) {
|
||||
@ -569,19 +579,20 @@ export default {
|
||||
if (this.workspace.breadcrumbs[key]) return this.workspace.breadcrumbs[key];
|
||||
}
|
||||
return false;
|
||||
},
|
||||
hasTools () {
|
||||
return this.workspace.customizations.processesList ||
|
||||
this.workspace.customizations.usersManagement ||
|
||||
this.workspace.customizations.variables;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedTab (newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
queryTabs: function (newVal, oldVal) {
|
||||
if (newVal.length > oldVal.length) {
|
||||
setTimeout(() => {
|
||||
const element = this.$refs['tab-selected'] ? this.$refs['tab-selected'][0] : null;
|
||||
if (element) {
|
||||
element.setAttribute('tabindex', '-1');
|
||||
element.focus();
|
||||
element.removeAttribute('tabindex');
|
||||
}
|
||||
}, 50);
|
||||
const scroller = this.$refs.tabWrap;
|
||||
if (scroller) scroller.$el.scrollLeft = scroller.$el.scrollWidth;
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -11,6 +11,7 @@
|
||||
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||
</li>
|
||||
<li
|
||||
v-if="customizations.sslConnection"
|
||||
class="tab-item c-hand"
|
||||
:class="{'active': selectedTab === 'ssl'}"
|
||||
@click="selectTab('ssl')"
|
||||
@ -18,6 +19,7 @@
|
||||
<a class="tab-link">{{ $t('word.ssl') }}</a>
|
||||
</li>
|
||||
<li
|
||||
v-if="customizations.sshConnection"
|
||||
class="tab-item c-hand"
|
||||
:class="{'active': selectedTab === 'ssh'}"
|
||||
@click="selectTab('ssh')"
|
||||
@ -49,25 +51,17 @@
|
||||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<select v-model="connection.client" class="form-select">
|
||||
<option value="mysql">
|
||||
MySQL
|
||||
<option
|
||||
v-for="client in clients"
|
||||
:key="client.slug"
|
||||
:value="client.slug"
|
||||
>
|
||||
{{ client.name }}
|
||||
</option>
|
||||
<option value="maria">
|
||||
MariaDB
|
||||
</option>
|
||||
<option value="pg">
|
||||
PostgreSQL
|
||||
</option>
|
||||
<!-- <option value="mssql">
|
||||
Microsoft SQL
|
||||
</option>
|
||||
<option value="oracledb">
|
||||
Oracle DB
|
||||
</option> -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group columns">
|
||||
<div v-if="!customizations.fileConnection" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||
</div>
|
||||
@ -79,7 +73,20 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group columns">
|
||||
<div v-if="customizations.fileConnection" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.database') }}</label>
|
||||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="connection.databasePath"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('databasePath')"
|
||||
@change="pathSelection($event, 'databasePath')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!customizations.fileConnection" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.port') }}</label>
|
||||
</div>
|
||||
@ -105,7 +112,7 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group columns">
|
||||
<div v-if="!customizations.fileConnection" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.user') }}</label>
|
||||
</div>
|
||||
@ -118,7 +125,7 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group columns">
|
||||
<div v-if="!customizations.fileConnection" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.password') }}</label>
|
||||
</div>
|
||||
@ -144,7 +151,15 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group columns">
|
||||
<div v-if="customizations.readOnlyMode" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12" />
|
||||
<div class="column col-8 col-sm-12">
|
||||
<label class="form-checkbox form-inline">
|
||||
<input v-model="connection.readonly" type="checkbox"><i class="form-icon" /> {{ $t('message.readOnlyMode') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!customizations.fileConnection" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12" />
|
||||
<div class="column col-8 col-sm-12">
|
||||
<label class="form-checkbox form-inline">
|
||||
@ -369,15 +384,23 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
clients: [
|
||||
{ name: 'MySQL', slug: 'mysql' },
|
||||
{ name: 'MariaDB', slug: 'maria' },
|
||||
{ name: 'PostgreSQL', slug: 'pg' },
|
||||
{ name: 'SQLite', slug: 'sqlite' }
|
||||
],
|
||||
connection: {
|
||||
name: '',
|
||||
client: 'mysql',
|
||||
host: '127.0.0.1',
|
||||
database: null,
|
||||
databasePath: '',
|
||||
port: null,
|
||||
user: null,
|
||||
password: '',
|
||||
ask: false,
|
||||
readonly: false,
|
||||
uid: uidGen('C'),
|
||||
ssl: false,
|
||||
cert: '',
|
||||
|
@ -11,6 +11,7 @@
|
||||
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||
</li>
|
||||
<li
|
||||
v-if="customizations.sslConnection"
|
||||
class="tab-item c-hand"
|
||||
:class="{'active': selectedTab === 'ssl'}"
|
||||
@click="selectTab('ssl')"
|
||||
@ -18,6 +19,7 @@
|
||||
<a class="tab-link">{{ $t('word.ssl') }}</a>
|
||||
</li>
|
||||
<li
|
||||
v-if="customizations.sshConnection"
|
||||
class="tab-item c-hand"
|
||||
:class="{'active': selectedTab === 'ssh'}"
|
||||
@click="selectTab('ssh')"
|
||||
@ -49,19 +51,17 @@
|
||||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<select v-model="localConnection.client" class="form-select">
|
||||
<option value="mysql">
|
||||
MySQL
|
||||
</option>
|
||||
<option value="maria">
|
||||
MariaDB
|
||||
</option>
|
||||
<option value="pg">
|
||||
PostgreSQL
|
||||
<option
|
||||
v-for="client in clients"
|
||||
:key="client.slug"
|
||||
:value="client.slug"
|
||||
>
|
||||
{{ client.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group columns">
|
||||
<div v-if="!customizations.fileConnection" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||
</div>
|
||||
@ -73,7 +73,20 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group columns">
|
||||
<div v-if="customizations.fileConnection" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.database') }}</label>
|
||||
</div>
|
||||
<div class="column col-8 col-sm-12">
|
||||
<BaseUploadInput
|
||||
:value="localConnection.databasePath"
|
||||
:message="$t('word.browse')"
|
||||
@clear="pathClear('databasePath')"
|
||||
@change="pathSelection($event, 'databasePath')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!customizations.fileConnection" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.port') }}</label>
|
||||
</div>
|
||||
@ -99,7 +112,7 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group columns">
|
||||
<div v-if="!customizations.fileConnection" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.user') }}</label>
|
||||
</div>
|
||||
@ -112,7 +125,7 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group columns">
|
||||
<div v-if="!customizations.fileConnection" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12">
|
||||
<label class="form-label">{{ $t('word.password') }}</label>
|
||||
</div>
|
||||
@ -138,7 +151,15 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group columns">
|
||||
<div v-if="customizations.readOnlyMode" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12" />
|
||||
<div class="column col-8 col-sm-12">
|
||||
<label class="form-checkbox form-inline">
|
||||
<input v-model="localConnection.readonly" type="checkbox"><i class="form-icon" /> {{ $t('message.readOnlyMode') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!customizations.fileConnection" class="form-group columns">
|
||||
<div class="column col-4 col-sm-12" />
|
||||
<div class="column col-8 col-sm-12">
|
||||
<label class="form-checkbox form-inline">
|
||||
@ -374,6 +395,12 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
clients: [
|
||||
{ name: 'MySQL', slug: 'mysql' },
|
||||
{ name: 'MariaDB', slug: 'maria' },
|
||||
{ name: 'PostgreSQL', slug: 'pg' },
|
||||
{ name: 'SQLite', slug: 'sqlite' }
|
||||
],
|
||||
isConnecting: false,
|
||||
isTesting: false,
|
||||
isAsking: false,
|
||||
@ -383,7 +410,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
customizations () {
|
||||
return customizations[this.connection.client];
|
||||
return customizations[this.localConnection.client];
|
||||
},
|
||||
isBusy () {
|
||||
return this.isConnecting || this.isTesting;
|
||||
|
@ -13,6 +13,7 @@
|
||||
<span class="workspace-explorebar-title">{{ connectionName }}</span>
|
||||
<span v-if="workspace.connectionStatus === 'connected'" class="workspace-explorebar-tools">
|
||||
<i
|
||||
v-if="customizations.schemas"
|
||||
class="mdi mdi-18px mdi-database-plus c-hand mr-2"
|
||||
:title="$t('message.createNewSchema')"
|
||||
@click="showNewDBModal"
|
||||
|
@ -10,6 +10,30 @@
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.run') }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedMisc.type === 'trigger' && customizations.triggerEnableDisable"
|
||||
class="context-element"
|
||||
@click="toggleTrigger"
|
||||
>
|
||||
<span v-if="!selectedMisc.enabled" class="d-flex">
|
||||
<i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.enable') }}
|
||||
</span>
|
||||
<span v-else class="d-flex">
|
||||
<i class="mdi mdi-18px mdi-pause text-light pr-1" /> {{ $t('word.disable') }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedMisc.type === 'scheduler'"
|
||||
class="context-element"
|
||||
@click="toggleScheduler"
|
||||
>
|
||||
<span v-if="!selectedMisc.enabled" class="d-flex">
|
||||
<i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.enable') }}
|
||||
</span>
|
||||
<span v-else class="d-flex">
|
||||
<i class="mdi mdi-18px mdi-pause text-light pr-1" /> {{ $t('word.disable') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="context-element" @click="showDeleteModal">
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-remove text-light pr-1" /> {{ $t('word.delete') }}</span>
|
||||
</div>
|
||||
@ -18,17 +42,17 @@
|
||||
@confirm="deleteMisc"
|
||||
@hide="hideDeleteModal"
|
||||
>
|
||||
<template slot="header">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-delete mr-1" />
|
||||
<span class="cut-text">{{ deleteMessage }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div slot="body">
|
||||
<template #body>
|
||||
<div class="mb-2">
|
||||
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedMisc.name }}</b>"?
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
<ModalAskParameters
|
||||
v-if="isAskingParameters"
|
||||
@ -78,6 +102,9 @@ export default {
|
||||
workspace () {
|
||||
return this.getWorkspace(this.selectedWorkspace);
|
||||
},
|
||||
customizations () {
|
||||
return this.getWorkspace(this.selectedWorkspace).customizations;
|
||||
},
|
||||
deleteMessage () {
|
||||
switch (this.selectedMisc.type) {
|
||||
case 'trigger':
|
||||
@ -98,6 +125,8 @@ export default {
|
||||
...mapActions({
|
||||
addNotification: 'notifications/addNotification',
|
||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||
addLoadingElement: 'workspaces/addLoadingElement',
|
||||
removeLoadingElement: 'workspaces/removeLoadingElement',
|
||||
removeTabs: 'workspaces/removeTabs',
|
||||
newTab: 'workspaces/newTab'
|
||||
}),
|
||||
@ -273,6 +302,68 @@ export default {
|
||||
|
||||
this.newTab({ uid: this.workspace.uid, content: sql, type: 'query', autorun: true });
|
||||
this.closeContext();
|
||||
},
|
||||
async toggleTrigger () {
|
||||
this.addLoadingElement({
|
||||
name: this.selectedMisc.name,
|
||||
schema: this.selectedSchema,
|
||||
type: 'trigger'
|
||||
});
|
||||
|
||||
try {
|
||||
const { status, response } = await Triggers.toggleTrigger({
|
||||
uid: this.selectedWorkspace,
|
||||
schema: this.selectedSchema,
|
||||
trigger: this.selectedMisc.name,
|
||||
enabled: this.selectedMisc.enabled
|
||||
});
|
||||
|
||||
if (status !== 'success')
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
this.removeLoadingElement({
|
||||
name: this.selectedMisc.name,
|
||||
schema: this.selectedSchema,
|
||||
type: 'trigger'
|
||||
});
|
||||
|
||||
this.closeContext();
|
||||
this.$emit('reload');
|
||||
},
|
||||
async toggleScheduler () {
|
||||
this.addLoadingElement({
|
||||
name: this.selectedMisc.name,
|
||||
schema: this.selectedSchema,
|
||||
type: 'scheduler'
|
||||
});
|
||||
|
||||
try {
|
||||
const { status, response } = await Schedulers.toggleScheduler({
|
||||
uid: this.selectedWorkspace,
|
||||
schema: this.selectedSchema,
|
||||
scheduler: this.selectedMisc.name,
|
||||
enabled: this.selectedMisc.enabled
|
||||
});
|
||||
|
||||
if (status !== 'success')
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
this.removeLoadingElement({
|
||||
name: this.selectedMisc.name,
|
||||
schema: this.selectedSchema,
|
||||
type: 'scheduler'
|
||||
});
|
||||
|
||||
this.closeContext();
|
||||
this.$emit('reload');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -9,7 +9,16 @@
|
||||
<div v-if="isLoading" class="icon loading" />
|
||||
<i v-else class="icon mdi mdi-18px mdi-chevron-right" />
|
||||
<i class="database-icon mdi mdi-18px mdi-database mr-1" />
|
||||
<span>{{ database.name }}</span>
|
||||
<div class="">
|
||||
<span>{{ database.name }}</span>
|
||||
<div
|
||||
v-if="database.size"
|
||||
class="schema-size tooltip tooltip-left mr-1"
|
||||
:data-tooltip="formatBytes(database.size)"
|
||||
>
|
||||
<i class="mdi mdi-information-outline pr-2" />
|
||||
</div>
|
||||
</div>
|
||||
</summary>
|
||||
<div class="accordion-body">
|
||||
<div class="database-tables">
|
||||
@ -34,7 +43,7 @@
|
||||
<span v-html="highlightWord(table.name)" />
|
||||
</a>
|
||||
<div
|
||||
v-if="table.type === 'table'"
|
||||
v-if="table.type === 'table' && table.size !== false"
|
||||
class="table-size tooltip tooltip-left mr-1"
|
||||
:data-tooltip="formatBytes(table.size)"
|
||||
>
|
||||
@ -68,9 +77,17 @@
|
||||
@contextmenu.prevent="showMiscContext($event, {...trigger, type: 'trigger'})"
|
||||
>
|
||||
<a class="table-name">
|
||||
<i class="table-icon mdi mdi-table-cog mdi-18px mr-1" />
|
||||
<div v-if="checkLoadingStatus(trigger.name, 'trigger')" class="icon loading mr-1" />
|
||||
<i v-else class="table-icon mdi mdi-table-cog mdi-18px mr-1" />
|
||||
<span v-html="highlightWord(trigger.name)" />
|
||||
</a>
|
||||
<div
|
||||
v-if="trigger.enabled === false"
|
||||
class="tooltip tooltip-left disabled-indicator"
|
||||
:data-tooltip="$t('word.disabled')"
|
||||
>
|
||||
<i class="table-icon mdi mdi-pause mdi-18px mr-1" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -204,9 +221,17 @@
|
||||
@contextmenu.prevent="showMiscContext($event, {...scheduler, type: 'scheduler'})"
|
||||
>
|
||||
<a class="table-name">
|
||||
<i class="table-icon mdi mdi-calendar-clock mdi-18px mr-1" />
|
||||
<div v-if="checkLoadingStatus(scheduler.name, 'scheduler')" class="icon loading mr-1" />
|
||||
<i v-else class="table-icon mdi mdi-calendar-clock mdi-18px mr-1" />
|
||||
<span v-html="highlightWord(scheduler.name)" />
|
||||
</a>
|
||||
<div
|
||||
v-if="scheduler.enabled === false"
|
||||
class="tooltip tooltip-left disabled-indicator"
|
||||
:data-tooltip="$t('word.disabled')"
|
||||
>
|
||||
<i class="table-icon mdi mdi-pause mdi-18px mr-1" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -426,6 +451,11 @@ export default {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
|
||||
.schema-size {
|
||||
visibility: hidden;
|
||||
width: 22.5px;
|
||||
}
|
||||
}
|
||||
|
||||
.database-name,
|
||||
@ -471,6 +501,10 @@ export default {
|
||||
.misc-name {
|
||||
&:hover {
|
||||
border-radius: $border-radius;
|
||||
|
||||
.schema-size {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -500,7 +534,9 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.table-size {
|
||||
.schema-size,
|
||||
.table-size,
|
||||
.disabled-indicator {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
|
@ -72,7 +72,11 @@
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-database-edit text-light pr-1" /> {{ $t('word.edit') }}</span>
|
||||
</div>
|
||||
<div class="context-element" @click="showDeleteModal">
|
||||
<div
|
||||
v-if="workspace.customizations.schemaDrop"
|
||||
class="context-element"
|
||||
@click="showDeleteModal"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-database-remove text-light pr-1" /> {{ $t('word.delete') }}</span>
|
||||
</div>
|
||||
|
||||
@ -81,17 +85,17 @@
|
||||
@confirm="deleteSchema"
|
||||
@hide="hideDeleteModal"
|
||||
>
|
||||
<template slot="header">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-database-remove mr-1" />
|
||||
<span class="cut-text">{{ $t('message.deleteSchema') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div slot="body">
|
||||
<template #body>
|
||||
<div class="mb-2">
|
||||
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedSchema }}</b>"?
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
<ModalEditSchema
|
||||
v-if="isEditModal"
|
||||
|
@ -40,33 +40,33 @@
|
||||
@confirm="emptyTable"
|
||||
@hide="hideEmptyModal"
|
||||
>
|
||||
<template slot="header">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-table-off mr-1" /> <span class="cut-text">{{ $t('message.emptyTable') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div slot="body">
|
||||
<template #body>
|
||||
<div class="mb-2">
|
||||
{{ $t('message.emptyCorfirm') }} "<b>{{ selectedTable.name }}</b>"?
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
<ConfirmModal
|
||||
v-if="isDeleteModal"
|
||||
@confirm="deleteTable"
|
||||
@hide="hideDeleteModal"
|
||||
>
|
||||
<template slot="header">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<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">
|
||||
<template #body>
|
||||
<div class="mb-2">
|
||||
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedTable.name }}</b>"?
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</BaseContextMenu>
|
||||
</template>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<div class="workspace-query-buttons">
|
||||
<button
|
||||
class="btn btn-primary btn-sm"
|
||||
:disabled="!isChanged"
|
||||
:disabled="!isChanged || !isValid"
|
||||
:class="{'loading':isSaving}"
|
||||
title="CTRL+S"
|
||||
@click="saveChanges"
|
||||
@ -242,6 +242,9 @@ export default {
|
||||
JSON.stringify(this.originalKeyUsage) !== JSON.stringify(this.localKeyUsage) ||
|
||||
JSON.stringify(this.originalIndexes) !== JSON.stringify(this.localIndexes) ||
|
||||
JSON.stringify(this.tableOptions) !== JSON.stringify(this.localOptions);
|
||||
},
|
||||
isValid () {
|
||||
return !!this.localFields.length && !!this.localOptions.name.trim().length;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -287,7 +290,7 @@ export default {
|
||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||
}),
|
||||
async saveChanges () {
|
||||
if (this.isSaving) return;
|
||||
if (this.isSaving || !this.isValid) return;
|
||||
this.isSaving = true;
|
||||
|
||||
const params = {
|
||||
@ -344,7 +347,7 @@ export default {
|
||||
},
|
||||
addField () {
|
||||
this.localFields.push({
|
||||
_id: uidGen(),
|
||||
_antares_id: uidGen(),
|
||||
name: `${this.$tc('word.field', 1)}_${++this.newFieldsCounter}`,
|
||||
key: '',
|
||||
type: this.workspace.dataTypes[0].types[0].name,
|
||||
@ -385,8 +388,8 @@ export default {
|
||||
});
|
||||
},
|
||||
duplicateField (uid) {
|
||||
const fieldToClone = Object.assign({}, this.localFields.find(field => field._id === uid));
|
||||
fieldToClone._id = uidGen();
|
||||
const fieldToClone = Object.assign({}, this.localFields.find(field => field._antares_id === uid));
|
||||
fieldToClone._antares_id = uidGen();
|
||||
fieldToClone.name = `${fieldToClone.name}_copy`;
|
||||
fieldToClone.order = this.localFields.length + 1;
|
||||
this.localFields = [...this.localFields, fieldToClone];
|
||||
@ -397,11 +400,11 @@ export default {
|
||||
}, 20);
|
||||
},
|
||||
removeField (uid) {
|
||||
this.localFields = this.localFields.filter(field => field._id !== uid);
|
||||
this.localFields = this.localFields.filter(field => field._antares_id !== uid);
|
||||
},
|
||||
addNewIndex (payload) {
|
||||
this.localIndexes = [...this.localIndexes, {
|
||||
_id: uidGen(),
|
||||
_antares_id: uidGen(),
|
||||
name: payload.index === 'PRIMARY' ? 'PRIMARY' : payload.field,
|
||||
fields: [payload.field],
|
||||
type: payload.index,
|
||||
@ -413,7 +416,7 @@ export default {
|
||||
},
|
||||
addToIndex (payload) {
|
||||
this.localIndexes = this.localIndexes.map(index => {
|
||||
if (index._id === payload.index) index.fields.push(payload.field);
|
||||
if (index._antares_id === payload.index) index.fields.push(payload.field);
|
||||
return index;
|
||||
});
|
||||
},
|
||||
|
@ -373,7 +373,7 @@ export default {
|
||||
this.originalFunction = response;
|
||||
|
||||
this.originalFunction.parameters = [...this.originalFunction.parameters.map(param => {
|
||||
param._id = uidGen();
|
||||
param._antares_id = uidGen();
|
||||
return param;
|
||||
})];
|
||||
|
||||
|
@ -6,13 +6,13 @@
|
||||
@confirm="confirmParametersChange"
|
||||
@hide="$emit('hide')"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||
<span class="cut-text">{{ $t('word.parameters') }} "{{ func }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<template #body>
|
||||
<div class="columns col-gapless">
|
||||
<div class="column col-5">
|
||||
<div class="panel" :style="{ height: modalInnerHeight + 'px'}">
|
||||
@ -36,10 +36,10 @@
|
||||
<div ref="parametersPanel" class="panel-body p-0 pr-1">
|
||||
<div
|
||||
v-for="param in parametersProxy"
|
||||
:key="param._id"
|
||||
:key="param._antares_id"
|
||||
class="tile tile-centered c-hand mb-1 p-1"
|
||||
:class="{'selected-element': selectedParam === param._id}"
|
||||
@click="selectParameter($event, param._id)"
|
||||
:class="{'selected-element': selectedParam === param._antares_id}"
|
||||
@click="selectParameter($event, param._antares_id)"
|
||||
>
|
||||
<div class="tile-icon">
|
||||
<div>
|
||||
@ -56,7 +56,7 @@
|
||||
<button
|
||||
class="btn btn-link remove-field p-0 mr-2"
|
||||
:title="$t('word.delete')"
|
||||
@click.prevent="removeParameter(param._id)"
|
||||
@click.prevent="removeParameter(param._antares_id)"
|
||||
>
|
||||
<i class="mdi mdi-close" />
|
||||
</button>
|
||||
@ -167,7 +167,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</template>
|
||||
|
||||
@ -196,7 +196,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
selectedParamObj () {
|
||||
return this.parametersProxy.find(param => param._id === this.selectedParam);
|
||||
return this.parametersProxy.find(param => param._antares_id === this.selectedParam);
|
||||
},
|
||||
isChanged () {
|
||||
return JSON.stringify(this.localParameters) !== JSON.stringify(this.parametersProxy);
|
||||
@ -237,10 +237,11 @@ export default {
|
||||
this.modalInnerHeight = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
||||
},
|
||||
addParameter () {
|
||||
const newUid = uidGen();
|
||||
this.parametersProxy = [...this.parametersProxy, {
|
||||
_id: uidGen(),
|
||||
name: `Param${this.i++}`,
|
||||
type: 'INT',
|
||||
_antares_id: newUid,
|
||||
name: `param${this.i++}`,
|
||||
type: this.workspace.dataTypes[0].types[0].name,
|
||||
context: 'IN',
|
||||
length: ''
|
||||
}];
|
||||
@ -250,12 +251,13 @@ export default {
|
||||
|
||||
setTimeout(() => {
|
||||
this.$refs.parametersPanel.scrollTop = this.$refs.parametersPanel.scrollHeight + 60;
|
||||
this.selectedParam = newUid;
|
||||
}, 20);
|
||||
},
|
||||
removeParameter (uid) {
|
||||
this.parametersProxy = this.parametersProxy.filter(param => param._id !== uid);
|
||||
this.parametersProxy = this.parametersProxy.filter(param => param._antares_id !== uid);
|
||||
|
||||
if (this.selectedParam === name && this.parametersProxy.length)
|
||||
if (this.parametersProxy.length && this.selectedParam === uid)
|
||||
this.resetSelectedID();
|
||||
},
|
||||
clearChanges () {
|
||||
@ -266,7 +268,7 @@ export default {
|
||||
this.resetSelectedID();
|
||||
},
|
||||
resetSelectedID () {
|
||||
this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0]._id : '';
|
||||
this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0]._antares_id : '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -318,7 +318,7 @@ export default {
|
||||
this.originalRoutine = response;
|
||||
|
||||
this.originalRoutine.parameters = [...this.originalRoutine.parameters.map(param => {
|
||||
param._id = uidGen();
|
||||
param._antares_id = uidGen();
|
||||
return param;
|
||||
})];
|
||||
|
||||
|
@ -6,13 +6,13 @@
|
||||
@confirm="confirmParametersChange"
|
||||
@hide="$emit('hide')"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||
<span class="cut-text">{{ $t('word.parameters') }} "{{ routine }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<template #body>
|
||||
<div class="columns col-gapless">
|
||||
<div class="column col-5">
|
||||
<div class="panel" :style="{ height: modalInnerHeight + 'px'}">
|
||||
@ -36,10 +36,10 @@
|
||||
<div ref="parametersPanel" class="panel-body p-0 pr-1">
|
||||
<div
|
||||
v-for="param in parametersProxy"
|
||||
:key="param._id"
|
||||
:key="param._antares_id"
|
||||
class="tile tile-centered c-hand mb-1 p-1"
|
||||
:class="{'selected-element': selectedParam === param._id}"
|
||||
@click="selectParameter($event, param._id)"
|
||||
:class="{'selected-element': selectedParam === param._antares_id}"
|
||||
@click="selectParameter($event, param._antares_id)"
|
||||
>
|
||||
<div class="tile-icon">
|
||||
<div>
|
||||
@ -56,7 +56,7 @@
|
||||
<button
|
||||
class="btn btn-link remove-field p-0 mr-2"
|
||||
:title="$t('word.delete')"
|
||||
@click.prevent="removeParameter(param._id)"
|
||||
@click.prevent="removeParameter(param._antares_id)"
|
||||
>
|
||||
<i class="mdi mdi-close" />
|
||||
</button>
|
||||
@ -167,7 +167,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</template>
|
||||
|
||||
@ -196,7 +196,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
selectedParamObj () {
|
||||
return this.parametersProxy.find(param => param._id === this.selectedParam);
|
||||
return this.parametersProxy.find(param => param._antares_id === this.selectedParam);
|
||||
},
|
||||
isChanged () {
|
||||
return JSON.stringify(this.localParameters) !== JSON.stringify(this.parametersProxy);
|
||||
@ -237,8 +237,9 @@ export default {
|
||||
this.modalInnerHeight = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
||||
},
|
||||
addParameter () {
|
||||
const newUid = uidGen();
|
||||
this.parametersProxy = [...this.parametersProxy, {
|
||||
_id: uidGen(),
|
||||
_antares_id: newUid,
|
||||
name: `param${this.i++}`,
|
||||
type: this.workspace.dataTypes[0].types[0].name,
|
||||
context: 'IN',
|
||||
@ -250,12 +251,13 @@ export default {
|
||||
|
||||
setTimeout(() => {
|
||||
this.$refs.parametersPanel.scrollTop = this.$refs.parametersPanel.scrollHeight + 60;
|
||||
this.selectedParam = newUid;
|
||||
}, 20);
|
||||
},
|
||||
removeParameter (uid) {
|
||||
this.parametersProxy = this.parametersProxy.filter(param => param._id !== uid);
|
||||
this.parametersProxy = this.parametersProxy.filter(param => param._antares_id !== uid);
|
||||
|
||||
if (this.selectedParam === name && this.parametersProxy.length)
|
||||
if (this.parametersProxy.length && this.selectedParam === uid)
|
||||
this.resetSelectedID();
|
||||
},
|
||||
clearChanges () {
|
||||
@ -266,7 +268,7 @@ export default {
|
||||
this.resetSelectedID();
|
||||
},
|
||||
resetSelectedID () {
|
||||
this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0]._id : '';
|
||||
this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0]._antares_id : '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -5,13 +5,13 @@
|
||||
@confirm="confirmOptionsChange"
|
||||
@hide="$emit('hide')"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-timer mr-1" />
|
||||
<span class="cut-text">{{ $t('word.timing') }} "{{ localOptions.name }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<template #body>
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="form-label col-4">
|
||||
@ -133,7 +133,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</template>
|
||||
|
||||
|
@ -342,7 +342,7 @@ export default {
|
||||
field.default = `'${field.default}'`;
|
||||
}
|
||||
|
||||
return { ...field, _id: uidGen() };
|
||||
return { ...field, _antares_id: uidGen() };
|
||||
});
|
||||
this.localFields = JSON.parse(JSON.stringify(this.originalFields));
|
||||
}
|
||||
@ -365,7 +365,7 @@ export default {
|
||||
|
||||
this.originalIndexes = Object.keys(indexesObj).map(index => {
|
||||
return {
|
||||
_id: uidGen(),
|
||||
_antares_id: uidGen(),
|
||||
name: index,
|
||||
fields: indexesObj[index].map(field => field.column),
|
||||
type: indexesObj[index][0].type,
|
||||
@ -391,7 +391,7 @@ export default {
|
||||
if (status === 'success') {
|
||||
this.originalKeyUsage = response.map(foreign => {
|
||||
return {
|
||||
_id: uidGen(),
|
||||
_antares_id: uidGen(),
|
||||
...foreign
|
||||
};
|
||||
});
|
||||
@ -411,25 +411,25 @@ export default {
|
||||
this.isSaving = true;
|
||||
|
||||
// FIELDS
|
||||
const originalIDs = this.originalFields.reduce((acc, curr) => [...acc, curr._id], []);
|
||||
const localIDs = this.localFields.reduce((acc, curr) => [...acc, curr._id], []);
|
||||
const originalIDs = this.originalFields.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||
const localIDs = this.localFields.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||
|
||||
// Fields Additions
|
||||
const additions = this.localFields.filter((field, i) => !originalIDs.includes(field._id)).map(field => {
|
||||
const lI = this.localFields.findIndex(localField => localField._id === field._id);
|
||||
const additions = this.localFields.filter((field, i) => !originalIDs.includes(field._antares_id)).map(field => {
|
||||
const lI = this.localFields.findIndex(localField => localField._antares_id === field._antares_id);
|
||||
const after = lI > 0 ? this.localFields[lI - 1].name : false;
|
||||
return { ...field, after };
|
||||
});
|
||||
|
||||
// Fields Deletions
|
||||
const deletions = this.originalFields.filter(field => !localIDs.includes(field._id));
|
||||
const deletions = this.originalFields.filter(field => !localIDs.includes(field._antares_id));
|
||||
|
||||
// Fields Changes
|
||||
const changes = [];
|
||||
this.originalFields.forEach((originalField, oI) => {
|
||||
const lI = this.localFields.findIndex(localField => localField._id === originalField._id);
|
||||
const originalSibling = oI > 0 ? this.originalFields[oI - 1]._id : false;
|
||||
const localSibling = lI > 0 ? this.localFields[lI - 1]._id : false;
|
||||
const lI = this.localFields.findIndex(localField => localField._antares_id === originalField._antares_id);
|
||||
const originalSibling = oI > 0 ? this.originalFields[oI - 1]._antares_id : false;
|
||||
const localSibling = lI > 0 ? this.localFields[lI - 1]._antares_id : false;
|
||||
const after = lI > 0 ? this.localFields[lI - 1].name : false;
|
||||
const orgName = originalField.name;
|
||||
|
||||
@ -450,15 +450,15 @@ export default {
|
||||
changes: [],
|
||||
deletions: []
|
||||
};
|
||||
const originalIndexIDs = this.originalIndexes.reduce((acc, curr) => [...acc, curr._id], []);
|
||||
const localIndexIDs = this.localIndexes.reduce((acc, curr) => [...acc, curr._id], []);
|
||||
const originalIndexIDs = this.originalIndexes.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||
const localIndexIDs = this.localIndexes.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||
|
||||
// Index Additions
|
||||
indexChanges.additions = this.localIndexes.filter(index => !originalIndexIDs.includes(index._id));
|
||||
indexChanges.additions = this.localIndexes.filter(index => !originalIndexIDs.includes(index._antares_id));
|
||||
|
||||
// Index Changes
|
||||
this.originalIndexes.forEach(originalIndex => {
|
||||
const lI = this.localIndexes.findIndex(localIndex => localIndex._id === originalIndex._id);
|
||||
const lI = this.localIndexes.findIndex(localIndex => localIndex._antares_id === originalIndex._antares_id);
|
||||
if (JSON.stringify(originalIndex) !== JSON.stringify(this.localIndexes[lI])) {
|
||||
if (this.localIndexes[lI]) {
|
||||
indexChanges.changes.push({
|
||||
@ -471,7 +471,7 @@ export default {
|
||||
});
|
||||
|
||||
// Index Deletions
|
||||
indexChanges.deletions = this.originalIndexes.filter(index => !localIndexIDs.includes(index._id));
|
||||
indexChanges.deletions = this.originalIndexes.filter(index => !localIndexIDs.includes(index._antares_id));
|
||||
|
||||
// FOREIGN KEYS
|
||||
const foreignChanges = {
|
||||
@ -479,15 +479,15 @@ export default {
|
||||
changes: [],
|
||||
deletions: []
|
||||
};
|
||||
const originalForeignIDs = this.originalKeyUsage.reduce((acc, curr) => [...acc, curr._id], []);
|
||||
const localForeignIDs = this.localKeyUsage.reduce((acc, curr) => [...acc, curr._id], []);
|
||||
const originalForeignIDs = this.originalKeyUsage.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||
const localForeignIDs = this.localKeyUsage.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||
|
||||
// Foreigns Additions
|
||||
foreignChanges.additions = this.localKeyUsage.filter(foreign => !originalForeignIDs.includes(foreign._id));
|
||||
foreignChanges.additions = this.localKeyUsage.filter(foreign => !originalForeignIDs.includes(foreign._antares_id));
|
||||
|
||||
// Foreigns Changes
|
||||
this.originalKeyUsage.forEach(originalForeign => {
|
||||
const lI = this.localKeyUsage.findIndex(localForeign => localForeign._id === originalForeign._id);
|
||||
const lI = this.localKeyUsage.findIndex(localForeign => localForeign._antares_id === originalForeign._antares_id);
|
||||
if (JSON.stringify(originalForeign) !== JSON.stringify(this.localKeyUsage[lI])) {
|
||||
if (this.localKeyUsage[lI]) {
|
||||
foreignChanges.changes.push({
|
||||
@ -499,13 +499,19 @@ export default {
|
||||
});
|
||||
|
||||
// Foreigns Deletions
|
||||
foreignChanges.deletions = this.originalKeyUsage.filter(foreign => !localForeignIDs.includes(foreign._id));
|
||||
foreignChanges.deletions = this.originalKeyUsage.filter(foreign => !localForeignIDs.includes(foreign._antares_id));
|
||||
|
||||
// ALTER
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
schema: this.schema,
|
||||
table: this.table,
|
||||
tableStructure: {
|
||||
name: this.localOptions.name,
|
||||
fields: this.localFields,
|
||||
foreigns: this.localKeyUsage,
|
||||
indexes: this.localIndexes
|
||||
},
|
||||
additions,
|
||||
changes,
|
||||
deletions,
|
||||
@ -555,7 +561,7 @@ export default {
|
||||
},
|
||||
addField () {
|
||||
this.localFields.push({
|
||||
_id: uidGen(),
|
||||
_antares_id: uidGen(),
|
||||
name: `${this.$tc('word.field', 1)}_${++this.newFieldsCounter}`,
|
||||
key: '',
|
||||
type: this.workspace.dataTypes[0].types[0].name,
|
||||
@ -597,8 +603,8 @@ export default {
|
||||
});
|
||||
},
|
||||
duplicateField (uid) {
|
||||
const fieldToClone = Object.assign({}, this.localFields.find(field => field._id === uid));
|
||||
fieldToClone._id = uidGen();
|
||||
const fieldToClone = Object.assign({}, this.localFields.find(field => field._antares_id === uid));
|
||||
fieldToClone._antares_id = uidGen();
|
||||
fieldToClone.name = `${fieldToClone.name}_copy`;
|
||||
fieldToClone.order = this.localFields.length + 1;
|
||||
this.localFields = [...this.localFields, fieldToClone];
|
||||
@ -609,11 +615,19 @@ export default {
|
||||
}, 20);
|
||||
},
|
||||
removeField (uid) {
|
||||
this.localFields = this.localFields.filter(field => field._id !== uid);
|
||||
this.localFields = this.localFields.filter(field => field._antares_id !== uid);
|
||||
this.localKeyUsage = this.localKeyUsage.filter(fk =>// Clear foreign keys
|
||||
this.localFields.some(field => field.name === fk.field)
|
||||
);
|
||||
this.localIndexes = this.localIndexes.filter(index =>// Clear indexes
|
||||
this.localFields.some(field =>
|
||||
index.fields.includes(field.name)
|
||||
)
|
||||
);
|
||||
},
|
||||
addNewIndex (payload) {
|
||||
this.localIndexes = [...this.localIndexes, {
|
||||
_id: uidGen(),
|
||||
_antares_id: uidGen(),
|
||||
name: payload.index === 'PRIMARY' ? 'PRIMARY' : payload.field,
|
||||
fields: [payload.field],
|
||||
type: payload.index,
|
||||
@ -625,7 +639,7 @@ export default {
|
||||
},
|
||||
addToIndex (payload) {
|
||||
this.localIndexes = this.localIndexes.map(index => {
|
||||
if (index._id === payload.index) index.fields.push(payload.field);
|
||||
if (index._antares_id === payload.index) index.fields.push(payload.field);
|
||||
return index;
|
||||
});
|
||||
},
|
||||
|
@ -27,7 +27,7 @@
|
||||
:key="index.name"
|
||||
class="context-element"
|
||||
:class="{'disabled': index.fields.includes(selectedField.name)}"
|
||||
@click="addToIndex(index._id)"
|
||||
@click="addToIndex(index._antares_id)"
|
||||
>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-key column-key pr-1" :class="`key-${index.type}`" /> {{ index.name }}</span>
|
||||
</div>
|
||||
|
@ -109,7 +109,7 @@
|
||||
>
|
||||
<TableRow
|
||||
v-for="row in fields"
|
||||
:key="row._id"
|
||||
:key="row._antares_id"
|
||||
:row="row"
|
||||
:indexes="getIndexes(row.name)"
|
||||
:foreigns="getForeigns(row.name)"
|
||||
@ -217,15 +217,15 @@ export default {
|
||||
this.resizeResults();
|
||||
},
|
||||
contextMenu (event, uid) {
|
||||
this.selectedField = this.fields.find(field => field._id === uid);
|
||||
this.selectedField = this.fields.find(field => field._antares_id === uid);
|
||||
this.contextEvent = event;
|
||||
this.isContext = true;
|
||||
},
|
||||
duplicateField () {
|
||||
this.$emit('duplicate-field', this.selectedField._id);
|
||||
this.$emit('duplicate-field', this.selectedField._antares_id);
|
||||
},
|
||||
removeField () {
|
||||
this.$emit('remove-field', this.selectedField._id);
|
||||
this.$emit('remove-field', this.selectedField._antares_id);
|
||||
},
|
||||
getIndexes (field) {
|
||||
return this.indexes.reduce((acc, curr) => {
|
||||
|
@ -6,13 +6,13 @@
|
||||
@confirm="confirmForeignsChange"
|
||||
@hide="$emit('hide')"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-key-link mr-1" />
|
||||
<span class="cut-text">{{ $t('word.foreignKeys') }} "{{ table }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<template #body>
|
||||
<div class="columns col-gapless">
|
||||
<div class="column col-5">
|
||||
<div class="panel" :style="{ height: modalInnerHeight + 'px'}">
|
||||
@ -36,10 +36,10 @@
|
||||
<div ref="indexesPanel" class="panel-body p-0 pr-1">
|
||||
<div
|
||||
v-for="foreign in foreignProxy"
|
||||
:key="foreign._id"
|
||||
:key="foreign._antares_id"
|
||||
class="tile tile-centered c-hand mb-1 p-1"
|
||||
:class="{'selected-element': selectedForeignID === foreign._id}"
|
||||
@click="selectForeign($event, foreign._id)"
|
||||
:class="{'selected-element': selectedForeignID === foreign._antares_id}"
|
||||
@click="selectForeign($event, foreign._antares_id)"
|
||||
>
|
||||
<div class="tile-icon">
|
||||
<div>
|
||||
@ -68,7 +68,7 @@
|
||||
<button
|
||||
class="btn btn-link remove-field p-0 mr-2"
|
||||
:title="$t('word.delete')"
|
||||
@click.prevent="removeIndex(foreign._id)"
|
||||
@click.prevent="removeIndex(foreign._antares_id)"
|
||||
>
|
||||
<i class="mdi mdi-close" />
|
||||
</button>
|
||||
@ -197,7 +197,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</template>
|
||||
|
||||
@ -238,7 +238,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
selectedForeignObj () {
|
||||
return this.foreignProxy.find(foreign => foreign._id === this.selectedForeignID);
|
||||
return this.foreignProxy.find(foreign => foreign._antares_id === this.selectedForeignID);
|
||||
},
|
||||
isChanged () {
|
||||
return JSON.stringify(this.localKeyUsage) !== JSON.stringify(this.foreignProxy);
|
||||
@ -288,8 +288,8 @@ export default {
|
||||
},
|
||||
addForeign () {
|
||||
this.foreignProxy = [...this.foreignProxy, {
|
||||
_id: uidGen(),
|
||||
constraintName: `FK_${this.foreignProxy.length + 1}`,
|
||||
_antares_id: uidGen(),
|
||||
constraintName: `FK_${uidGen()}`,
|
||||
refSchema: this.schema,
|
||||
table: this.table,
|
||||
refTable: '',
|
||||
@ -307,19 +307,19 @@ export default {
|
||||
}, 20);
|
||||
},
|
||||
removeIndex (id) {
|
||||
this.foreignProxy = this.foreignProxy.filter(foreign => foreign._id !== id);
|
||||
this.foreignProxy = this.foreignProxy.filter(foreign => foreign._antares_id !== id);
|
||||
|
||||
if (this.selectedForeignID === id && this.foreignProxy.length)
|
||||
this.resetSelectedID();
|
||||
},
|
||||
clearChanges () {
|
||||
this.foreignProxy = JSON.parse(JSON.stringify(this.localKeyUsage));
|
||||
if (!this.foreignProxy.some(foreign => foreign._id === this.selectedForeignID))
|
||||
if (!this.foreignProxy.some(foreign => foreign._antares_id === this.selectedForeignID))
|
||||
this.resetSelectedID();
|
||||
},
|
||||
toggleField (field) {
|
||||
this.foreignProxy = this.foreignProxy.map(foreign => {
|
||||
if (foreign._id === this.selectedForeignID)
|
||||
if (foreign._antares_id === this.selectedForeignID)
|
||||
foreign.field = field;
|
||||
|
||||
return foreign;
|
||||
@ -327,14 +327,14 @@ export default {
|
||||
},
|
||||
toggleRefField (field) {
|
||||
this.foreignProxy = this.foreignProxy.map(foreign => {
|
||||
if (foreign._id === this.selectedForeignID)
|
||||
if (foreign._antares_id === this.selectedForeignID)
|
||||
foreign.refField = field;
|
||||
|
||||
return foreign;
|
||||
});
|
||||
},
|
||||
resetSelectedID () {
|
||||
this.selectedForeignID = this.foreignProxy.length ? this.foreignProxy[0]._id : '';
|
||||
this.selectedForeignID = this.foreignProxy.length ? this.foreignProxy[0]._antares_id : '';
|
||||
},
|
||||
async getRefFields () {
|
||||
if (!this.selectedForeignObj.refTable) return;
|
||||
|
@ -6,13 +6,13 @@
|
||||
@confirm="confirmIndexesChange"
|
||||
@hide="$emit('hide')"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<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'">
|
||||
<template #body>
|
||||
<div class="columns col-gapless">
|
||||
<div class="column col-5">
|
||||
<div class="panel" :style="{ height: modalInnerHeight + 'px'}">
|
||||
@ -36,10 +36,10 @@
|
||||
<div ref="indexesPanel" class="panel-body p-0 pr-1">
|
||||
<div
|
||||
v-for="index in indexesProxy"
|
||||
:key="index._id"
|
||||
:key="index._antares_id"
|
||||
class="tile tile-centered c-hand mb-1 p-1"
|
||||
:class="{'selected-element': selectedIndexID === index._id}"
|
||||
@click="selectIndex($event, index._id)"
|
||||
:class="{'selected-element': selectedIndexID === index._antares_id}"
|
||||
@click="selectIndex($event, index._antares_id)"
|
||||
>
|
||||
<div class="tile-icon">
|
||||
<div>
|
||||
@ -56,7 +56,7 @@
|
||||
<button
|
||||
class="btn btn-link remove-field p-0 mr-2"
|
||||
:title="$t('word.delete')"
|
||||
@click.prevent="removeIndex(index._id)"
|
||||
@click.prevent="removeIndex(index._antares_id)"
|
||||
>
|
||||
<i class="mdi mdi-close" />
|
||||
</button>
|
||||
@ -133,7 +133,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</template>
|
||||
|
||||
@ -163,7 +163,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
selectedIndexObj () {
|
||||
return this.indexesProxy.find(index => index._id === this.selectedIndexID);
|
||||
return this.indexesProxy.find(index => index._antares_id === this.selectedIndexID);
|
||||
},
|
||||
isChanged () {
|
||||
return JSON.stringify(this.localIndexes) !== JSON.stringify(this.indexesProxy);
|
||||
@ -200,7 +200,7 @@ export default {
|
||||
},
|
||||
addIndex () {
|
||||
this.indexesProxy = [...this.indexesProxy, {
|
||||
_id: uidGen(),
|
||||
_antares_id: uidGen(),
|
||||
name: 'NEW_INDEX',
|
||||
fields: [],
|
||||
type: 'INDEX',
|
||||
@ -218,19 +218,19 @@ export default {
|
||||
}, 20);
|
||||
},
|
||||
removeIndex (id) {
|
||||
this.indexesProxy = this.indexesProxy.filter(index => index._id !== id);
|
||||
this.indexesProxy = this.indexesProxy.filter(index => index._antares_id !== id);
|
||||
|
||||
if (this.selectedIndexID === id && this.indexesProxy.length)
|
||||
this.resetSelectedID();
|
||||
},
|
||||
clearChanges () {
|
||||
this.indexesProxy = JSON.parse(JSON.stringify(this.localIndexes));
|
||||
if (!this.indexesProxy.some(index => index._id === this.selectedIndexID))
|
||||
if (!this.indexesProxy.some(index => index._antares_id === this.selectedIndexID))
|
||||
this.resetSelectedID();
|
||||
},
|
||||
toggleField (field) {
|
||||
this.indexesProxy = this.indexesProxy.map(index => {
|
||||
if (index._id === this.selectedIndexID) {
|
||||
if (index._antares_id === this.selectedIndexID) {
|
||||
if (index.fields.includes(field))
|
||||
index.fields = index.fields.filter(f => f !== field);
|
||||
else
|
||||
@ -240,7 +240,7 @@ export default {
|
||||
});
|
||||
},
|
||||
resetSelectedID () {
|
||||
this.selectedIndexID = this.indexesProxy.length ? this.indexesProxy[0]._id : '';
|
||||
this.selectedIndexID = this.indexesProxy.length ? this.indexesProxy[0]._antares_id : '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="tr" @contextmenu.prevent="$emit('contextmenu', $event, localRow._id)">
|
||||
<div class="tr" @contextmenu.prevent="$emit('contextmenu', $event, localRow._antares_id)">
|
||||
<div class="td p-0" tabindex="0">
|
||||
<div :class="customizations.sortableFields ? 'row-draggable' : 'text-center'">
|
||||
<i v-if="customizations.sortableFields" class="mdi mdi-drag-horizontal row-draggable-icon" />
|
||||
@ -99,6 +99,9 @@
|
||||
<span v-if="localRow.enumValues">
|
||||
{{ localRow.enumValues }}
|
||||
</span>
|
||||
<span v-else-if="localRow.numScale">
|
||||
{{ localLength }}, {{ localRow.numScale }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ localLength }}
|
||||
</span>
|
||||
@ -112,6 +115,16 @@
|
||||
class="editable-field form-input input-sm px-1"
|
||||
@blur="editOFF"
|
||||
>
|
||||
<input
|
||||
v-else-if="fieldType.scale"
|
||||
ref="editField"
|
||||
v-model="editingContent"
|
||||
type="text"
|
||||
autofocus
|
||||
class="editable-field form-input input-sm px-1"
|
||||
@keypress="checkLengthScale"
|
||||
@blur="editOFF"
|
||||
>
|
||||
<input
|
||||
v-else
|
||||
ref="editField"
|
||||
@ -230,13 +243,13 @@
|
||||
@confirm="editOFF"
|
||||
@hide="hideDefaultModal"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<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'">
|
||||
<template #body>
|
||||
<form class="form-horizontal">
|
||||
<div class="mb-2">
|
||||
<label class="form-radio form-inline">
|
||||
@ -324,7 +337,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</div>
|
||||
</template>
|
||||
@ -367,7 +380,8 @@ export default {
|
||||
getWorkspace: 'workspaces/getWorkspace'
|
||||
}),
|
||||
localLength () {
|
||||
return this.localRow.numLength || this.localRow.charLength || this.localRow.datePrecision || this.localRow.numPrecision || 0;
|
||||
const localLength = this.localRow.numLength || this.localRow.charLength || this.localRow.datePrecision || this.localRow.numPrecision || 0;
|
||||
return localLength === true ? null : localLength;
|
||||
},
|
||||
fieldType () {
|
||||
const fieldType = this.dataTypes.reduce((acc, group) => [...acc, ...group.types], []).filter(type =>
|
||||
@ -391,7 +405,7 @@ export default {
|
||||
return this.indexes.some(index => ['PRIMARY', 'UNIQUE'].includes(index.type));
|
||||
},
|
||||
isNullable () {
|
||||
return !this.indexes.some(index => ['PRIMARY'].includes(index.type));
|
||||
return this.customizations.nullablePrimary || !this.indexes.some(index => ['PRIMARY'].includes(index.type));
|
||||
},
|
||||
isInDataTypes () {
|
||||
let typeNames = [];
|
||||
@ -479,6 +493,11 @@ export default {
|
||||
this.editingContent = this.localRow.enumValues;
|
||||
this.originalContent = this.localRow.enumValues;
|
||||
}
|
||||
else if (this.fieldType.scale && field === 'length') {
|
||||
const scale = this.localRow.numScale !== null ? this.localRow.numScale : 0;
|
||||
this.editingContent = `${content}, ${scale}`;
|
||||
this.originalContent = `${content}, ${scale}`;
|
||||
}
|
||||
else {
|
||||
this.editingContent = content;
|
||||
this.originalContent = content;
|
||||
@ -501,10 +520,17 @@ export default {
|
||||
if (this.editingField === 'name')
|
||||
this.$emit('rename-field', { old: this.localRow[this.editingField], new: this.editingContent });
|
||||
|
||||
this.localRow[this.editingField] = this.editingContent;
|
||||
if (this.editingField === 'numLength' && this.fieldType.scale) {
|
||||
const [length, scale] = this.editingContent.split(',');
|
||||
this.localRow.numLength = +length;
|
||||
this.localRow.numScale = scale ? +scale : null;
|
||||
}
|
||||
else
|
||||
this.localRow[this.editingField] = this.editingContent;
|
||||
|
||||
if (this.editingField === 'type' && this.editingContent !== this.originalContent) {
|
||||
this.localRow.numLength = null;
|
||||
this.localRow.numScale = null;
|
||||
this.localRow.charLength = null;
|
||||
this.localRow.datePrecision = null;
|
||||
this.localRow.enumValues = '';
|
||||
@ -559,6 +585,15 @@ export default {
|
||||
this.originalContent = null;
|
||||
this.editingField = null;
|
||||
},
|
||||
checkLengthScale (e) {
|
||||
e = (e) || window.event;
|
||||
const charCode = (e.which) ? e.which : e.keyCode;
|
||||
|
||||
if (((charCode > 31 && (charCode < 48 || charCode > 57)) && charCode !== 44) || (charCode === 44 && e.target.value.includes(',')))
|
||||
e.preventDefault();
|
||||
else
|
||||
return true;
|
||||
},
|
||||
hideDefaultModal () {
|
||||
this.isDefaultModal = false;
|
||||
}
|
||||
|
@ -238,7 +238,7 @@ export default {
|
||||
this.originalFunction = response;
|
||||
|
||||
this.originalFunction.parameters = [...this.originalFunction.parameters.map(param => {
|
||||
param._id = uidGen();
|
||||
param._antares_id = uidGen();
|
||||
return param;
|
||||
})];
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
class="workspace-query-tab column col-12 columns col-gapless no-outline p-0"
|
||||
tabindex="0"
|
||||
@keydown.116="runQuery(query)"
|
||||
@keydown.75="killTabQuery"
|
||||
@keydown.ctrl.alt.87="clear"
|
||||
@keydown.ctrl.66="beautify"
|
||||
@keydown.ctrl.71="openHistoryModal"
|
||||
@ -22,15 +23,46 @@
|
||||
<div ref="resizer" class="query-area-resizer" />
|
||||
<div class="workspace-query-runner-footer">
|
||||
<div class="workspace-query-buttons">
|
||||
<div @mouseenter="setCancelButtonVisibility(true)" @mouseleave="setCancelButtonVisibility(false)">
|
||||
<button
|
||||
v-if="showCancel && isQuering"
|
||||
class="btn btn-primary btn-sm cancellable"
|
||||
:disabled="!query"
|
||||
:title="$t('word.cancel')"
|
||||
@click="killTabQuery()"
|
||||
>
|
||||
<i class="mdi mdi-24px mdi-window-close" />
|
||||
<span class="d-invisible pr-1">{{ $t('word.run') }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-primary btn-sm"
|
||||
:class="{'loading':isQuering}"
|
||||
:disabled="!query"
|
||||
title="F5"
|
||||
@click="runQuery(query)"
|
||||
>
|
||||
<i class="mdi mdi-24px mdi-play pr-1" />
|
||||
<span>{{ $t('word.run') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary btn-sm"
|
||||
v-if="!autocommit"
|
||||
class="btn btn-dark btn-sm"
|
||||
:class="{'loading':isQuering}"
|
||||
:disabled="!query"
|
||||
title="F5"
|
||||
@click="runQuery(query)"
|
||||
@click="commitTab()"
|
||||
>
|
||||
<i class="mdi mdi-24px mdi-play pr-1" />
|
||||
<span>{{ $t('word.run') }}</span>
|
||||
<i class="mdi mdi-24px mdi-cube-send pr-1" />
|
||||
<span>{{ $t('word.commit') }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="!autocommit"
|
||||
class="btn btn-dark btn-sm"
|
||||
:class="{'loading':isQuering}"
|
||||
@click="rollbackTab()"
|
||||
>
|
||||
<i class="mdi mdi-24px mdi-undo-variant pr-1" />
|
||||
<span>{{ $t('word.rollback') }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-link btn-sm mr-0"
|
||||
@ -64,7 +96,7 @@
|
||||
</button>
|
||||
<div class="dropdown table-dropdown pr-2">
|
||||
<button
|
||||
:disabled="!results.length || isQuering"
|
||||
:disabled="!hasResults || isQuering"
|
||||
class="btn btn-dark btn-sm dropdown-toggle mr-0 pr-0"
|
||||
tabindex="0"
|
||||
>
|
||||
@ -81,6 +113,17 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="input-group pr-2" :title="$t('message.commitMode')">
|
||||
<i class="input-group-addon addon-sm mdi mdi-24px mdi-source-commit p-0" />
|
||||
<select v-model="autocommit" class="form-select select-sm text-bold">
|
||||
<option :value="true">
|
||||
{{ $t('message.autoCommit') }}
|
||||
</option>
|
||||
<option :value="false">
|
||||
{{ $t('message.manualCommit') }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workspace-query-info">
|
||||
<div
|
||||
@ -90,11 +133,19 @@
|
||||
>
|
||||
<i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ durationsCount / 1000 }}s</b>
|
||||
</div>
|
||||
<div v-if="resultsCount">
|
||||
{{ $t('word.results') }}: <b>{{ resultsCount.toLocaleString() }}</b>
|
||||
<div
|
||||
v-if="resultsCount"
|
||||
class="d-flex"
|
||||
:title="$t('word.results')"
|
||||
>
|
||||
<i class="mdi mdi-equal pr-1" /> <b>{{ resultsCount.toLocaleString() }}</b>
|
||||
</div>
|
||||
<div v-if="affectedCount">
|
||||
{{ $t('message.affectedRows') }}: <b>{{ affectedCount }}</b>
|
||||
<div
|
||||
v-if="hasAffected"
|
||||
class="d-flex"
|
||||
:title="$t('message.affectedRows')"
|
||||
>
|
||||
<i class="mdi mdi-target pr-1" /> <b>{{ affectedCount }}</b>
|
||||
</div>
|
||||
<div class="input-group" :title="$t('word.schema')">
|
||||
<i class="input-group-addon addon-sm mdi mdi-24px mdi-database" />
|
||||
@ -110,7 +161,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<WorkspaceTabQueryEmptyState v-if="!results.length && !isQuering" />
|
||||
<WorkspaceTabQueryEmptyState v-if="!results.length && !isQuering" :customizations="workspace.customizations" />
|
||||
<div class="workspace-query-results p-relative column col-12">
|
||||
<BaseLoader v-if="isQuering" />
|
||||
<WorkspaceTabQueryTable
|
||||
@ -166,11 +217,14 @@ export default {
|
||||
query: '',
|
||||
lastQuery: '',
|
||||
isQuering: false,
|
||||
isCancelling: false,
|
||||
showCancel: false,
|
||||
autocommit: true,
|
||||
results: [],
|
||||
selectedSchema: null,
|
||||
resultsCount: 0,
|
||||
durationsCount: 0,
|
||||
affectedCount: 0,
|
||||
affectedCount: null,
|
||||
editorHeight: 200,
|
||||
isHistoryOpen: false
|
||||
};
|
||||
@ -184,6 +238,9 @@ export default {
|
||||
workspace () {
|
||||
return this.getWorkspace(this.connection.uid);
|
||||
},
|
||||
tabUid () {
|
||||
return this.$vnode.key;
|
||||
},
|
||||
breadcrumbsSchema () {
|
||||
return this.workspace.breadcrumbs.schema || null;
|
||||
},
|
||||
@ -198,12 +255,23 @@ export default {
|
||||
},
|
||||
history () {
|
||||
return this.getHistoryByWorkspace(this.connection.uid) || [];
|
||||
},
|
||||
hasResults () {
|
||||
return this.results.length && this.results[0].rows;
|
||||
},
|
||||
hasAffected () {
|
||||
return this.affectedCount || (!this.resultsCount && this.affectedCount !== null);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isSelected (val) {
|
||||
if (val)
|
||||
if (val) {
|
||||
this.changeBreadcrumbs({ schema: this.selectedSchema, query: `Query #${this.tab.index}` });
|
||||
setTimeout(() => {
|
||||
if (this.$refs.queryEditor)
|
||||
this.$refs.queryEditor.editor.focus();
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
selectedSchema () {
|
||||
this.changeBreadcrumbs({ schema: this.selectedSchema, query: `Query #${this.tab.index}` });
|
||||
@ -230,12 +298,18 @@ export default {
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('keydown', this.onKey);
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
tabUid: this.tab.uid
|
||||
};
|
||||
Schema.destroyConnectionToCommit(params);
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
addNotification: 'notifications/addNotification',
|
||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||
updateTabContent: 'workspaces/updateTabContent',
|
||||
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||
saveHistory: 'history/saveHistory'
|
||||
}),
|
||||
async runQuery (query) {
|
||||
@ -248,6 +322,8 @@ export default {
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
schema: this.selectedSchema,
|
||||
tabUid: this.tab.uid,
|
||||
autocommit: this.autocommit,
|
||||
query
|
||||
};
|
||||
|
||||
@ -255,9 +331,14 @@ export default {
|
||||
|
||||
if (status === 'success') {
|
||||
this.results = Array.isArray(response) ? response : [response];
|
||||
this.resultsCount += this.results.reduce((acc, curr) => acc + (curr.rows ? curr.rows.length : 0), 0);
|
||||
this.durationsCount += this.results.reduce((acc, curr) => acc + curr.duration, 0);
|
||||
this.affectedCount += this.results.reduce((acc, curr) => acc + (curr.report ? curr.report.affectedRows : 0), 0);
|
||||
this.resultsCount = this.results.reduce((acc, curr) => acc + (curr.rows ? curr.rows.length : 0), 0);
|
||||
this.durationsCount = this.results.reduce((acc, curr) => acc + curr.duration, 0);
|
||||
this.affectedCount = this.results
|
||||
.filter(result => result.report !== null)
|
||||
.reduce((acc, curr) => {
|
||||
if (acc === null) acc = 0;
|
||||
return acc + (curr.report ? curr.report.affectedRows : 0);
|
||||
}, null);
|
||||
|
||||
this.updateTabContent({
|
||||
uid: this.connection.uid,
|
||||
@ -267,6 +348,8 @@ export default {
|
||||
content: query
|
||||
});
|
||||
this.saveHistory(params);
|
||||
if (!this.autocommit)
|
||||
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: true });
|
||||
}
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
@ -278,6 +361,29 @@ export default {
|
||||
this.isQuering = false;
|
||||
this.lastQuery = query;
|
||||
},
|
||||
async killTabQuery () {
|
||||
if (this.isCancelling) return;
|
||||
|
||||
this.isCancelling = true;
|
||||
|
||||
try {
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
tabUid: this.tab.uid
|
||||
};
|
||||
|
||||
await Schema.killTabQuery(params);
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
this.isCancelling = false;
|
||||
},
|
||||
setCancelButtonVisibility (val) {
|
||||
if (this.workspace.customizations.cancelQueries)
|
||||
this.showCancel = val;
|
||||
},
|
||||
reloadTable () {
|
||||
this.runQuery(this.lastQuery);
|
||||
},
|
||||
@ -285,7 +391,7 @@ export default {
|
||||
this.results = [];
|
||||
this.resultsCount = 0;
|
||||
this.durationsCount = 0;
|
||||
this.affectedCount = 0;
|
||||
this.affectedCount = null;
|
||||
},
|
||||
resize (e) {
|
||||
const el = this.$refs.queryEditor.$el;
|
||||
@ -341,6 +447,42 @@ export default {
|
||||
},
|
||||
downloadTable (format) {
|
||||
this.$refs.queryTable.downloadTable(format, `${this.tab.type}-${this.tab.index}`);
|
||||
},
|
||||
async commitTab () {
|
||||
this.isQuering = true;
|
||||
try {
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
tabUid: this.tab.uid
|
||||
};
|
||||
|
||||
await Schema.commitTab(params);
|
||||
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: false });
|
||||
this.addNotification({ status: 'success', message: this.$t('message.actionSuccessful', { action: 'COMMIT' }) });
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
this.isQuering = false;
|
||||
},
|
||||
async rollbackTab () {
|
||||
this.isQuering = true;
|
||||
try {
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
tabUid: this.tab.uid
|
||||
};
|
||||
|
||||
await Schema.rollbackTab(params);
|
||||
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: false });
|
||||
this.addNotification({ status: 'success', message: this.$t('message.actionSuccessful', { action: 'ROLLBACK' }) });
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
this.isQuering = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -369,10 +511,12 @@ export default {
|
||||
|
||||
.workspace-query-runner-footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
row-gap: 0.4rem;
|
||||
justify-content: space-between;
|
||||
padding: 0.3rem 0.6rem 0.4rem;
|
||||
align-items: center;
|
||||
height: 42px;
|
||||
min-height: 42px;
|
||||
|
||||
.workspace-query-buttons,
|
||||
.workspace-query-info {
|
||||
|
@ -5,6 +5,9 @@
|
||||
<div class="mb-4">
|
||||
{{ $t('message.runQuery') }}
|
||||
</div>
|
||||
<div v-if="customizations.cancelQueries" class="mb-4">
|
||||
{{ $t('message.killQuery') }}
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
{{ $t('word.format') }}
|
||||
</div>
|
||||
@ -25,6 +28,9 @@
|
||||
<div class="mb-4">
|
||||
<code>F5</code>
|
||||
</div>
|
||||
<div v-if="customizations.cancelQueries" class="mb-4">
|
||||
<code>CTRL</code> + <code>K</code>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<code>CTRL</code> + <code>B</code>
|
||||
</div>
|
||||
@ -47,7 +53,10 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'WorkspaceTabQueryEmptyState'
|
||||
name: 'WorkspaceTabQueryEmptyState',
|
||||
props: {
|
||||
customizations: Object
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
tabindex="0"
|
||||
:style="{'height': resultsSize+'px'}"
|
||||
@keyup.46="showDeleteConfirmModal"
|
||||
@keydown.ctrl.65="selectAllRows"
|
||||
@keydown.ctrl.65="selectAllRows($event)"
|
||||
@keydown.esc="deselectRows"
|
||||
>
|
||||
<TableContext
|
||||
@ -53,6 +53,7 @@
|
||||
class="mdi sort-icon"
|
||||
:class="currentSortDir === 'asc' ? 'mdi-sort-ascending':'mdi-sort-descending'"
|
||||
/>
|
||||
<i v-else class="mdi sort-icon mdi-minus d-invisible" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -62,7 +63,7 @@
|
||||
v-if="resultsWithRows[resultsetIndex] && resultsWithRows[resultsetIndex].rows"
|
||||
ref="resultTable"
|
||||
:items="sortedResults"
|
||||
:item-height="22"
|
||||
:item-height="rowHeight"
|
||||
class="tbody"
|
||||
:visible-height="resultsSize"
|
||||
:scroll-element="scrollElement"
|
||||
@ -70,13 +71,14 @@
|
||||
<template slot-scope="{ items }">
|
||||
<WorkspaceTabQueryTableRow
|
||||
v-for="row in items"
|
||||
:key="row._id"
|
||||
:key="row._antares_id"
|
||||
:item-height="rowHeight"
|
||||
:row="row"
|
||||
:fields="fieldsObj"
|
||||
:key-usage="keyUsage"
|
||||
:element-type="elementType"
|
||||
:class="{'selected': selectedRows.includes(row._id)}"
|
||||
@select-row="selectRow($event, row._id)"
|
||||
:class="{'selected': selectedRows.includes(row._antares_id)}"
|
||||
@select-row="selectRow($event, row._antares_id)"
|
||||
@update-field="updateField($event, row)"
|
||||
@contextmenu="contextMenu"
|
||||
/>
|
||||
@ -89,17 +91,17 @@
|
||||
@confirm="deleteSelected"
|
||||
@hide="hideDeleteConfirmModal"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-delete mr-1" />
|
||||
<span class="cut-text">{{ $tc('message.deleteRows', selectedRows.length) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<template #body>
|
||||
<div class="mb-2">
|
||||
{{ $tc('message.confirmToDeleteRows', selectedRows.length) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</div>
|
||||
</template>
|
||||
@ -142,7 +144,8 @@ export default {
|
||||
currentSort: '',
|
||||
currentSortDir: 'asc',
|
||||
resultsetIndex: 0,
|
||||
scrollElement: null
|
||||
scrollElement: null,
|
||||
rowHeight: 23
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -196,7 +199,7 @@ export default {
|
||||
if (this.sortedResults.length) {
|
||||
const fieldsObj = {};
|
||||
for (const key in this.sortedResults[0]) {
|
||||
if (key === '_id') continue;
|
||||
if (key === '_antares_id') continue;
|
||||
|
||||
const fieldObj = this.fields.find(field => {
|
||||
let fieldNames = [
|
||||
@ -242,6 +245,11 @@ export default {
|
||||
|
||||
if (this.$refs.tableWrapper)
|
||||
this.scrollElement = this.$refs.tableWrapper;
|
||||
|
||||
document.querySelectorAll('.column-resizable').forEach(element => {
|
||||
if (element.clientWidth !== 0)
|
||||
element.style.width = element.clientWidth + 'px';
|
||||
});
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('resize', this.resizeResults);
|
||||
@ -272,6 +280,7 @@ export default {
|
||||
fieldLength (field) {
|
||||
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
||||
else if (TEXT.includes(field.type)) return field.charLength;
|
||||
else if (field.numScale) return `${field.numPrecision}, ${field.numScale}`;
|
||||
return field.length;
|
||||
},
|
||||
keyName (key) {
|
||||
@ -310,7 +319,7 @@ export default {
|
||||
setLocalResults () {
|
||||
this.localResults = this.resultsWithRows[this.resultsetIndex] && this.resultsWithRows[this.resultsetIndex].rows
|
||||
? this.resultsWithRows[this.resultsetIndex].rows.map(item => {
|
||||
return { ...item, _id: uidGen() };
|
||||
return { ...item, _antares_id: uidGen() };
|
||||
})
|
||||
: [];
|
||||
},
|
||||
@ -330,7 +339,7 @@ export default {
|
||||
this.resizeResults();
|
||||
},
|
||||
updateField (payload, row) {
|
||||
const orgRow = this.localResults.find(lr => lr._id === row._id);
|
||||
const orgRow = this.localResults.find(lr => lr._antares_id === row._antares_id);
|
||||
|
||||
Object.keys(orgRow).forEach(key => { // remap the row
|
||||
if (orgRow[key] instanceof Date && moment(orgRow[key]).isValid()) { // if datetime
|
||||
@ -367,8 +376,8 @@ export default {
|
||||
},
|
||||
deleteSelected () {
|
||||
this.closeContext();
|
||||
const rows = JSON.parse(JSON.stringify(this.localResults)).filter(row => this.selectedRows.includes(row._id)).map(row => {
|
||||
delete row._id;
|
||||
const rows = JSON.parse(JSON.stringify(this.localResults)).filter(row => this.selectedRows.includes(row._antares_id)).map(row => {
|
||||
delete row._antares_id;
|
||||
return row;
|
||||
});
|
||||
|
||||
@ -381,7 +390,7 @@ export default {
|
||||
this.$emit('delete-selected', params);
|
||||
},
|
||||
setNull () {
|
||||
const row = this.localResults.find(row => this.selectedRows.includes(row._id));
|
||||
const row = this.localResults.find(row => this.selectedRows.includes(row._antares_id));
|
||||
|
||||
const params = {
|
||||
primary: this.primaryField.name,
|
||||
@ -396,19 +405,22 @@ export default {
|
||||
this.$emit('update-field', params);
|
||||
},
|
||||
copyCell () {
|
||||
const row = this.localResults.find(row => this.selectedRows.includes(row._id));
|
||||
const row = this.localResults.find(row => this.selectedRows.includes(row._antares_id));
|
||||
const cellName = Object.keys(row).find(prop => [
|
||||
this.selectedCell.field,
|
||||
this.selectedCell.orgField,
|
||||
`${this.fields[0].table}.${this.selectedCell.field}`,
|
||||
`${this.fields[0].tableAlias}.${this.selectedCell.field}`
|
||||
].includes(prop));
|
||||
const valueToCopy = row[cellName];
|
||||
let valueToCopy = row[cellName];
|
||||
if (typeof valueToCopy === 'object')
|
||||
valueToCopy = JSON.stringify(valueToCopy);
|
||||
navigator.clipboard.writeText(valueToCopy);
|
||||
},
|
||||
copyRow () {
|
||||
const row = this.localResults.find(row => this.selectedRows.includes(row._id));
|
||||
const row = this.localResults.find(row => this.selectedRows.includes(row._antares_id));
|
||||
const rowToCopy = JSON.parse(JSON.stringify(row));
|
||||
delete rowToCopy._id;
|
||||
delete rowToCopy._antares_id;
|
||||
navigator.clipboard.writeText(JSON.stringify(rowToCopy));
|
||||
},
|
||||
applyUpdate (params) {
|
||||
@ -435,24 +447,26 @@ export default {
|
||||
this.selectedRows.push(row);
|
||||
else {
|
||||
const lastID = this.selectedRows.slice(-1)[0];
|
||||
const lastIndex = this.sortedResults.findIndex(el => el._id === lastID);
|
||||
const clickedIndex = this.sortedResults.findIndex(el => el._id === row);
|
||||
const lastIndex = this.sortedResults.findIndex(el => el._antares_id === lastID);
|
||||
const clickedIndex = this.sortedResults.findIndex(el => el._antares_id === row);
|
||||
if (lastIndex > clickedIndex) {
|
||||
for (let i = clickedIndex; i < lastIndex; i++)
|
||||
this.selectedRows.push(this.sortedResults[i]._id);
|
||||
this.selectedRows.push(this.sortedResults[i]._antares_id);
|
||||
}
|
||||
else if (lastIndex < clickedIndex) {
|
||||
for (let i = clickedIndex; i > lastIndex; i--)
|
||||
this.selectedRows.push(this.sortedResults[i]._id);
|
||||
this.selectedRows.push(this.sortedResults[i]._antares_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
this.selectedRows = [row];
|
||||
},
|
||||
selectAllRows () {
|
||||
selectAllRows (e) {
|
||||
if (e.target.classList.contains('editable-field')) return;
|
||||
|
||||
this.selectedRows = this.localResults.reduce((acc, curr) => {
|
||||
acc.push(curr._id);
|
||||
acc.push(curr._antares_id);
|
||||
return acc;
|
||||
}, []);
|
||||
},
|
||||
@ -501,7 +515,7 @@ export default {
|
||||
if (!this.sortedResults) return;
|
||||
|
||||
const rows = JSON.parse(JSON.stringify(this.sortedResults)).map(row => {
|
||||
delete row._id;
|
||||
delete row._antares_id;
|
||||
return row;
|
||||
});
|
||||
|
||||
|
@ -61,8 +61,6 @@ export default {
|
||||
selectedRows: Array,
|
||||
selectedCell: Object
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
methods: {
|
||||
showConfirmModal () {
|
||||
this.$emit('show-delete-modal');
|
||||
|
@ -1,14 +1,18 @@
|
||||
<template>
|
||||
<div class="tr" @click="selectRow($event, row._id)">
|
||||
<div
|
||||
class="tr"
|
||||
:style="{height: itemHeight+'px'}"
|
||||
@click="selectRow($event, row._antares_id)"
|
||||
>
|
||||
<div
|
||||
v-for="(col, cKey) in row"
|
||||
v-show="cKey !== '_id'"
|
||||
v-show="cKey !== '_antares_id'"
|
||||
:key="cKey"
|
||||
class="td p-0"
|
||||
tabindex="0"
|
||||
@contextmenu.prevent="openContext($event, { id: row._id, field: cKey })"
|
||||
@contextmenu.prevent="openContext($event, { id: row._antares_id, orgField: cKey })"
|
||||
>
|
||||
<template v-if="cKey !== '_id'">
|
||||
<template v-if="cKey !== '_antares_id'">
|
||||
<span
|
||||
v-if="!isInlineEditor[cKey] && fields[cKey]"
|
||||
class="cell-content"
|
||||
@ -72,12 +76,12 @@
|
||||
@confirm="editOFF"
|
||||
@hide="hideEditorModal"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> <span class="cut-text">{{ $t('word.edit') }} "{{ editingField }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<template #body>
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<TextEditor
|
||||
@ -96,23 +100,12 @@
|
||||
v-model="editorMode"
|
||||
class="form-select select-sm"
|
||||
>
|
||||
<option value="text">
|
||||
TEXT
|
||||
</option>
|
||||
<option value="html">
|
||||
HTML
|
||||
</option>
|
||||
<option value="xml">
|
||||
XML
|
||||
</option>
|
||||
<option value="json">
|
||||
JSON
|
||||
</option>
|
||||
<option value="svg">
|
||||
SVG
|
||||
</option>
|
||||
<option value="yaml">
|
||||
YAML
|
||||
<option
|
||||
v-for="language in availableLanguages"
|
||||
:key="language.slug"
|
||||
:value="language.slug"
|
||||
>
|
||||
{{ language.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
@ -128,7 +121,22 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
<ConfirmModal
|
||||
v-if="isMapModal"
|
||||
:hide-footer="true"
|
||||
size="medium"
|
||||
@hide="hideEditorModal"
|
||||
>
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-map mr-1" /> <span class="cut-text">"{{ editingField }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<BaseMap :points="editingContent" :is-multi-spatial="isMultiSpatial" />
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
<ConfirmModal
|
||||
v-if="isBlobEditor"
|
||||
@ -136,13 +144,13 @@
|
||||
@confirm="editOFF"
|
||||
@hide="hideEditorModal"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" />
|
||||
<span class="cut-text">{{ $t('word.edit') }} "{{ editingField }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<template #body>
|
||||
<div class="mb-2">
|
||||
<transition name="jump-down">
|
||||
<div v-if="contentInfo.size">
|
||||
@ -182,21 +190,39 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import { ModelOperations } from '@vscode/vscode-languagedetection';
|
||||
import { mimeFromHex } from 'common/libs/mimeFromHex';
|
||||
import { formatBytes } from 'common/libs/formatBytes';
|
||||
import { bufferToBase64 } from 'common/libs/bufferToBase64';
|
||||
import hexToBinary from 'common/libs/hexToBinary';
|
||||
import { TEXT, LONG_TEXT, ARRAY, TEXT_SEARCH, NUMBER, FLOAT, BOOLEAN, DATE, TIME, DATETIME, BLOB, BIT, HAS_TIMEZONE } from 'common/fieldTypes';
|
||||
import {
|
||||
TEXT,
|
||||
LONG_TEXT,
|
||||
ARRAY,
|
||||
TEXT_SEARCH,
|
||||
NUMBER,
|
||||
FLOAT,
|
||||
BOOLEAN,
|
||||
DATE,
|
||||
TIME,
|
||||
DATETIME,
|
||||
BLOB,
|
||||
BIT,
|
||||
HAS_TIMEZONE,
|
||||
SPATIAL,
|
||||
IS_MULTI_SPATIAL
|
||||
} from 'common/fieldTypes';
|
||||
import { VueMaskDirective } from 'v-mask';
|
||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||
import TextEditor from '@/components/BaseTextEditor';
|
||||
import BaseMap from '@/components/BaseMap';
|
||||
import ForeignKeySelect from '@/components/ForeignKeySelect';
|
||||
|
||||
export default {
|
||||
@ -204,7 +230,8 @@ export default {
|
||||
components: {
|
||||
ConfirmModal,
|
||||
TextEditor,
|
||||
ForeignKeySelect
|
||||
ForeignKeySelect,
|
||||
BaseMap
|
||||
},
|
||||
directives: {
|
||||
mask: VueMaskDirective
|
||||
@ -254,13 +281,17 @@ export default {
|
||||
return val;
|
||||
}
|
||||
|
||||
return val;
|
||||
if (SPATIAL.includes(type))
|
||||
return val;
|
||||
|
||||
return typeof val === 'object' ? JSON.stringify(val) : val;
|
||||
}
|
||||
},
|
||||
props: {
|
||||
row: Object,
|
||||
fields: Object,
|
||||
keyUsage: Array,
|
||||
itemHeight: Number,
|
||||
elementType: { type: String, default: 'table' }
|
||||
},
|
||||
data () {
|
||||
@ -268,6 +299,8 @@ export default {
|
||||
isInlineEditor: {},
|
||||
isTextareaEditor: false,
|
||||
isBlobEditor: false,
|
||||
isMapModal: false,
|
||||
isMultiSpatial: false,
|
||||
willBeDeleted: false,
|
||||
originalContent: null,
|
||||
editingContent: null,
|
||||
@ -280,7 +313,17 @@ export default {
|
||||
mime: '',
|
||||
size: null
|
||||
},
|
||||
fileToUpload: null
|
||||
fileToUpload: null,
|
||||
availableLanguages: [
|
||||
{ name: 'TEXT', slug: 'text', id: 'text' },
|
||||
{ name: 'HTML', slug: 'html', id: 'html' },
|
||||
{ name: 'XML', slug: 'xml', id: 'xml' },
|
||||
{ name: 'JSON', slug: 'json', id: 'json' },
|
||||
{ name: 'SVG', slug: 'svg', id: 'svg' },
|
||||
{ name: 'INI', slug: 'ini', id: 'ini' },
|
||||
{ name: 'MARKDOWN', slug: 'markdown', id: 'md' },
|
||||
{ name: 'YAML', slug: 'yaml', id: 'yaml' }
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -326,6 +369,9 @@ export default {
|
||||
if (BOOLEAN.includes(this.editingType))
|
||||
return { type: 'boolean', mask: false };
|
||||
|
||||
if (SPATIAL.includes(this.editingType))
|
||||
return { type: 'map', mask: false };
|
||||
|
||||
return { type: 'text', mask: false };
|
||||
},
|
||||
isImage () {
|
||||
@ -362,6 +408,21 @@ export default {
|
||||
Object.keys(this.fields).forEach(field => {
|
||||
this.isInlineEditor[field.name] = false;
|
||||
});
|
||||
},
|
||||
isTextareaEditor (val) {
|
||||
if (val) {
|
||||
const modelOperations = new ModelOperations();
|
||||
(async () => {
|
||||
const detected = await modelOperations.runModel(this.editingContent);
|
||||
const filteredLanguages = detected.filter(dLang =>
|
||||
this.availableLanguages.some(aLang => aLang.id === dLang.languageId) &&
|
||||
dLang.confidence > 0.1
|
||||
);
|
||||
|
||||
if (filteredLanguages.length)
|
||||
this.editorMode = this.availableLanguages.find(lang => lang.id === filteredLanguages[0].languageId).slug;
|
||||
})();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -383,7 +444,7 @@ export default {
|
||||
return bufferToBase64(val);
|
||||
},
|
||||
editON (event, content, field) {
|
||||
if (!this.isEditable) return;
|
||||
if (!this.isEditable || this.editingType === 'none') return;
|
||||
|
||||
window.addEventListener('keydown', this.onKey);
|
||||
|
||||
@ -399,6 +460,15 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SPATIAL.includes(type)) {
|
||||
if (content) {
|
||||
this.isMultiSpatial = IS_MULTI_SPATIAL.includes(type);
|
||||
this.isMapModal = true;
|
||||
this.editingContent = this.$options.filters.typeFormat(content, type);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (BLOB.includes(type)) {
|
||||
this.isBlobEditor = true;
|
||||
this.editingContent = content || '';
|
||||
@ -470,6 +540,8 @@ export default {
|
||||
hideEditorModal () {
|
||||
this.isTextareaEditor = false;
|
||||
this.isBlobEditor = false;
|
||||
this.isMapModal = false;
|
||||
this.isMultiSpatial = false;
|
||||
},
|
||||
downloadFile () {
|
||||
const downloadLink = document.createElement('a');
|
||||
@ -506,7 +578,7 @@ export default {
|
||||
return this.keyUsage.find(key => key.field === keyName);
|
||||
},
|
||||
openContext (event, payload) {
|
||||
payload.field = this.fields[payload.field].name;// Ensures field name only
|
||||
payload.field = this.fields[payload.orgField].name;// Ensures field name only
|
||||
payload.isEditable = this.isEditable;
|
||||
this.$emit('contextmenu', event, payload);
|
||||
},
|
||||
|
@ -85,7 +85,7 @@
|
||||
@click="showFakerModal"
|
||||
>
|
||||
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||
<span>{{ $t('message.tableFiller') }}</span>
|
||||
<span>{{ $tc('message.insertRow', 2) }}</span>
|
||||
</button>
|
||||
|
||||
<div class="dropdown table-dropdown pr-2">
|
||||
@ -120,7 +120,12 @@
|
||||
{{ $t('word.results') }}: <b>{{ results[0].rows.length | localeString }}</b>
|
||||
</div>
|
||||
<div v-if="hasApproximately || (page > 1 && approximateCount)">
|
||||
{{ $t('word.total') }}: <b :title="$t('word.approximately')">≈ {{ approximateCount | localeString }}</b>
|
||||
{{ $t('word.total') }}: <b
|
||||
:title="!customizations.tableRealCount ? $t('word.approximately') : ''"
|
||||
>
|
||||
<span v-if="!customizations.tableRealCount">≈</span>
|
||||
{{ approximateCount | localeString }}
|
||||
</b>
|
||||
</div>
|
||||
<div class="d-flex" :title="$t('word.schema')">
|
||||
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||
@ -231,6 +236,9 @@ export default {
|
||||
workspace () {
|
||||
return this.getWorkspace(this.connection.uid);
|
||||
},
|
||||
customizations () {
|
||||
return this.workspace.customizations;
|
||||
},
|
||||
isTable () {
|
||||
return !!this.workspace.breadcrumbs.table;
|
||||
},
|
||||
|
@ -130,9 +130,13 @@ module.exports = {
|
||||
schedulers: 'Schedulers',
|
||||
includes: 'Includes',
|
||||
drop: 'Drop',
|
||||
rows: 'Rows',
|
||||
completed: 'Completed',
|
||||
aborted: 'Aborted'
|
||||
aborted: 'Aborted',
|
||||
disabled: 'Disabled',
|
||||
enable: 'Enable',
|
||||
disable: 'Disable',
|
||||
commit: 'Commit',
|
||||
rollback: 'Rollback'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Welcome to Antares SQL Client!',
|
||||
@ -265,7 +269,15 @@ module.exports = {
|
||||
fechingTableExport: 'Fetching {table} data',
|
||||
writingTableExport: 'Writing {table} data',
|
||||
checkAllTables: 'Check all tables',
|
||||
uncheckAllTables: 'Uncheck all tables'
|
||||
uncheckAllTables: 'Uncheck all tables',
|
||||
goToDownloadPage: 'Go to download page',
|
||||
readOnlyMode: 'Read-only mode',
|
||||
killQuery: 'Kill query',
|
||||
insertRow: 'Insert row | Insert rows',
|
||||
commitMode: 'Commit mode',
|
||||
autoCommit: 'Auto commit',
|
||||
manualCommit: 'Manual commit',
|
||||
actionSuccessful: '{action} successful'
|
||||
},
|
||||
faker: {
|
||||
address: 'Address',
|
||||
|
@ -13,7 +13,8 @@ const i18n = new VueI18n({
|
||||
'pt-BR': require('./pt-BR'),
|
||||
'de-DE': require('./de-DE'),
|
||||
'vi-VN': require('./vi-VN'),
|
||||
'ja-JP': require('./ja-JP')
|
||||
'ja-JP': require('./ja-JP'),
|
||||
'zh-CN': require('./zh-CN')
|
||||
}
|
||||
});
|
||||
export default i18n;
|
||||
|
@ -130,9 +130,11 @@ module.exports = {
|
||||
schedulers: 'Scheduler',
|
||||
includes: 'Includi',
|
||||
drop: 'Drop',
|
||||
rows: 'Righe',
|
||||
completed: 'Completato',
|
||||
aborted: 'Annullato'
|
||||
aborted: 'Annullato',
|
||||
disabled: 'Disabilitato',
|
||||
enable: 'Abilita',
|
||||
disable: 'Disabilita'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Benvenuto in Antares SQL Client!',
|
||||
@ -252,7 +254,22 @@ module.exports = {
|
||||
fechingTableExport: 'Ricavo i dati {table}',
|
||||
writingTableExport: 'Scrittura dati {table}',
|
||||
checkAllTables: 'Seleziona tutte le tabelle',
|
||||
uncheckAllTables: 'Deseleziona tutte le tabelle'
|
||||
uncheckAllTables: 'Deseleziona tutte le tabelle',
|
||||
runQuery: 'Esegui query',
|
||||
thereAreNoTableFields: 'Non ci sono campi della tabella',
|
||||
newTable: 'Nuova tabella',
|
||||
newView: 'Nuova vista',
|
||||
newTrigger: 'Nuovo trigger',
|
||||
newRoutine: 'Nuova routine',
|
||||
newFunction: 'Nuova funzione',
|
||||
newScheduler: 'Nuovo scheduler',
|
||||
newTriggerFunction: 'Nuova funzione di trigger',
|
||||
thereIsNoQueriesYet: 'Non ci sono ancora query',
|
||||
searchForQueries: 'Cerca query',
|
||||
killProcess: 'Uccidi processo',
|
||||
closeTab: 'Chiudi tab',
|
||||
goToDownloadPage: 'Vai alla pagina di download',
|
||||
readOnlyMode: 'Modalità sola lettura'
|
||||
},
|
||||
faker: {
|
||||
address: 'Indirizzo',
|
||||
|
@ -7,5 +7,6 @@ export default {
|
||||
'pt-BR': 'Português (Brasil)',
|
||||
'de-DE': 'Deutsch (Deutschland)',
|
||||
'vi-VN': 'Tiếng Việt',
|
||||
'ja-JP': '日本語'
|
||||
'ja-JP': '日本語',
|
||||
'zh-CN': '简体中文'
|
||||
};
|
||||
|
@ -120,7 +120,11 @@ module.exports = {
|
||||
new: 'Mới',
|
||||
history: 'Lịch sử',
|
||||
select: 'Chọn',
|
||||
passphrase: 'Cụm mật khẩu'
|
||||
passphrase: 'Cụm mật khẩu',
|
||||
filter: 'Bộ lọc',
|
||||
disabled: 'Đã tắt',
|
||||
enable: 'Bật',
|
||||
disable: 'Tắt'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Chào bạn đến với Antares SQL Client!',
|
||||
@ -244,7 +248,10 @@ module.exports = {
|
||||
newTriggerFunction: 'Chức năng kích hoạt mới',
|
||||
thereIsNoQueriesYet: 'Không có truy vấn nào',
|
||||
searchForQueries: 'Tìm kiếm truy vấn',
|
||||
killProcess: 'Huỷ quá trình'
|
||||
killProcess: 'Huỷ quá trình',
|
||||
closeTab: 'Đóng tab',
|
||||
goToDownloadPage: 'Tới trang tải về',
|
||||
readOnlyMode: 'Chế độ chỉ đọc'
|
||||
},
|
||||
faker: {
|
||||
address: 'Địa chỉ',
|
||||
|
421
src/renderer/i18n/zh-CN.js
Normal file
@ -0,0 +1,421 @@
|
||||
module.exports = {
|
||||
word: {
|
||||
edit: '编辑',
|
||||
save: '保存',
|
||||
close: '关闭',
|
||||
delete: '删除',
|
||||
confirm: '确定',
|
||||
cancel: '取消',
|
||||
send: '发送',
|
||||
connectionName: '连接名称',
|
||||
client: 'Client',
|
||||
hostName: '主机名',
|
||||
port: '端口',
|
||||
user: '用户',
|
||||
password: '密码',
|
||||
credentials: '凭据',
|
||||
connect: '连接',
|
||||
connected: '已连接',
|
||||
disconnect: '断开连接',
|
||||
disconnected: '已断开',
|
||||
refresh: '刷新',
|
||||
settings: '设置',
|
||||
general: '一般',
|
||||
themes: '主题',
|
||||
update: '更新',
|
||||
about: '关于',
|
||||
language: '语言',
|
||||
version: '版本',
|
||||
donate: '捐赠',
|
||||
run: '运行',
|
||||
schema: 'schema',
|
||||
results: '结果',
|
||||
size: '尺寸',
|
||||
seconds: '秒',
|
||||
type: '类型',
|
||||
mimeType: 'MIME类型',
|
||||
download: '下载',
|
||||
add: '新增',
|
||||
data: '数据',
|
||||
properties: '属性',
|
||||
insert: '插入',
|
||||
connecting: '连接中',
|
||||
name: '名称',
|
||||
collation: '排序规则',
|
||||
clear: '清除',
|
||||
options: '选项',
|
||||
autoRefresh: '自动刷新',
|
||||
indexes: '索引',
|
||||
foreignKeys: '外键',
|
||||
length: '长度',
|
||||
unsigned: '无符号',
|
||||
default: '默认',
|
||||
comment: '注释',
|
||||
key: '键',
|
||||
order: 'Order',
|
||||
expression: '表达式',
|
||||
autoIncrement: '自动增量',
|
||||
engine: 'Engine',
|
||||
field: '字段',
|
||||
approximately: '大约',
|
||||
total: '总计',
|
||||
table: '表',
|
||||
discard: '弃置',
|
||||
stay: '等待',
|
||||
author: '作者',
|
||||
light: 'Light',
|
||||
dark: 'Dark',
|
||||
autoCompletion: '自动完成',
|
||||
application: '应用程序',
|
||||
editor: '编辑器',
|
||||
view: '视图',
|
||||
definer: '定义者',
|
||||
algorithm: 'Algorithm',
|
||||
trigger: '触发器',
|
||||
storedRoutine: '存储例程',
|
||||
scheduler: '调度器',
|
||||
event: '事件',
|
||||
parameters: '参数',
|
||||
function: '函数',
|
||||
deterministic: 'Deterministic',
|
||||
context: '上下文',
|
||||
export: '导出',
|
||||
returns: '返回',
|
||||
timing: '定时器',
|
||||
state: '状态',
|
||||
execution: '执行',
|
||||
starts: '开始',
|
||||
ends: '结束',
|
||||
ssl: 'SSL',
|
||||
privateKey: '私钥',
|
||||
certificate: '证书',
|
||||
caCertificate: 'CA 证书',
|
||||
ciphers: 'Ciphers',
|
||||
upload: '上传',
|
||||
browse: '浏览',
|
||||
faker: 'Faker',
|
||||
content: '内容',
|
||||
cut: '剪切',
|
||||
copy: '复制',
|
||||
paste: '粘贴',
|
||||
tools: '工具',
|
||||
variables: '变量',
|
||||
processes: '进程',
|
||||
database: '数据库',
|
||||
scratchpad: 'Scratchpad',
|
||||
array: '数组',
|
||||
changelog: '更改日志',
|
||||
format: '格式',
|
||||
sshTunnel: 'SSH 隧道',
|
||||
structure: '结构',
|
||||
small: '小',
|
||||
medium: '中',
|
||||
large: '大',
|
||||
row: '行',
|
||||
cell: '单元格',
|
||||
triggerFunction: '触发函数',
|
||||
all: '全部',
|
||||
duplicate: '重复',
|
||||
routine: '例程',
|
||||
new: 'New',
|
||||
history: '历史记录',
|
||||
select: '选择',
|
||||
passphrase: '密码',
|
||||
filter: '过滤器',
|
||||
disabled: '禁用',
|
||||
enable: '启用',
|
||||
disable: '是否禁用'
|
||||
},
|
||||
message: {
|
||||
appWelcome: '欢迎来到Antares SQL Client!',
|
||||
appFirstStep: '你的第一步: 创建一个新的数据库连接.',
|
||||
addConnection: '添加连接',
|
||||
createConnection: '创建连接',
|
||||
createNewConnection: '创建新的连接',
|
||||
askCredentials: '询问凭证',
|
||||
testConnection: '测试连接',
|
||||
editConnection: '编辑连接',
|
||||
deleteConnection: '删除连接',
|
||||
deleteCorfirm: '您是否确认取消',
|
||||
connectionSuccessfullyMade: '连接成功建立!',
|
||||
madeWithJS: '用💛和JavaScript制造!',
|
||||
checkForUpdates: '检查更新',
|
||||
noUpdatesAvailable: '没有可用的更新',
|
||||
checkingForUpdate: '正在检查更新',
|
||||
checkFailure: '检查失败,请稍后再试',
|
||||
updateAvailable: '可用的更新',
|
||||
downloadingUpdate: '正在下载更新',
|
||||
updateDownloaded: '更新已下载',
|
||||
restartToInstall: '重启Antares完成更新',
|
||||
unableEditFieldWithoutPrimary: '无法编辑一个在结果集中没有主键的字段',
|
||||
editCell: '编辑单元格',
|
||||
deleteRows: '删除行 | 删除{count}行',
|
||||
confirmToDeleteRows: '你是否确认要删除一行? | 您是否确认要删除{count}行?',
|
||||
notificationsTimeout: '通知超时',
|
||||
uploadFile: '上传文件',
|
||||
addNewRow: '添加新行',
|
||||
numberOfInserts: '插入的数量',
|
||||
openNewTab: '打开一个新标签',
|
||||
affectedRows: '受影响的行',
|
||||
createNewDatabase: '创建新的数据库',
|
||||
databaseName: '数据库名称',
|
||||
serverDefault: '默认服务器',
|
||||
deleteDatabase: '删除数据库',
|
||||
editDatabase: '编辑数据库',
|
||||
clearChanges: '清除变化',
|
||||
addNewField: '添加新字段',
|
||||
manageIndexes: '管理索引',
|
||||
manageForeignKeys: '管理外键',
|
||||
allowNull: '允许NULL',
|
||||
zeroFill: '填充零',
|
||||
customValue: '自定义值',
|
||||
onUpdate: '在更新时',
|
||||
deleteField: '删除字段',
|
||||
createNewIndex: '创建新的索引',
|
||||
addToIndex: '添加到索引',
|
||||
createNewTable: '创建新表',
|
||||
emptyTable: '清空表',
|
||||
deleteTable: '删除表',
|
||||
emptyCorfirm: '你是否确认清空',
|
||||
unsavedChanges: '未保存的更改',
|
||||
discardUnsavedChanges: '你有一些未保存的修改。关闭这个标签,这些变化将被丢弃.',
|
||||
thereAreNoIndexes: '没有索引',
|
||||
thereAreNoForeign: '没有外键',
|
||||
createNewForeign: '创建新的外键',
|
||||
referenceTable: '参考表',
|
||||
referenceField: '参考字段',
|
||||
foreignFields: '外键字段',
|
||||
invalidDefault: '无效的默认值',
|
||||
onDelete: '在删除时',
|
||||
applicationTheme: '应用主题',
|
||||
editorTheme: '编辑器主题',
|
||||
wrapLongLines: '超出换行显示',
|
||||
selectStatement: '选择语句',
|
||||
triggerStatement: '触发器语句',
|
||||
sqlSecurity: 'SQL安全',
|
||||
updateOption: '更新选项',
|
||||
deleteView: '删除视图',
|
||||
createNewView: '创建新视图',
|
||||
deleteTrigger: '删除触发器',
|
||||
createNewTrigger: '创建新的触发器',
|
||||
currentUser: '当前用户',
|
||||
routineBody: '例程主体',
|
||||
dataAccess: '数据访问',
|
||||
thereAreNoParameters: '没有参数',
|
||||
createNewParameter: '创建新参数',
|
||||
createNewRoutine: '创建新的例程',
|
||||
deleteRoutine: '删除例程',
|
||||
functionBody: '函数体',
|
||||
createNewFunction: '创建新函数',
|
||||
deleteFunction: '删除函数',
|
||||
schedulerBody: '调度器主体',
|
||||
createNewScheduler: '创建新的调度器',
|
||||
deleteScheduler: '删除调度器',
|
||||
preserveOnCompletion: '完成时保存',
|
||||
enableSsl: '启用SSL',
|
||||
manualValue: '手动值',
|
||||
tableFiller: '表填充器',
|
||||
fakeDataLanguage: '伪造的数据语言',
|
||||
searchForElements: '搜索元素',
|
||||
selectAll: '选择所有',
|
||||
queryDuration: '查询时间',
|
||||
includeBetaUpdates: '包括测试版更新',
|
||||
setNull: '设置NULL',
|
||||
processesList: '进程列表',
|
||||
processInfo: '进程信息',
|
||||
manageUsers: '管理用户',
|
||||
createNewSchema: '创建新模式',
|
||||
schemaName: '模式名称',
|
||||
editSchema: '编辑模式',
|
||||
deleteSchema: '删除模式',
|
||||
markdownSupported: '支持Markdown',
|
||||
plantATree: '种植一棵树',
|
||||
dataTabPageSize: '数据标签的页面大小',
|
||||
enableSsh: '启用SSH',
|
||||
pageNumber: '页数',
|
||||
duplicateTable: '重复的表格',
|
||||
noOpenTabs: '没有打开的标签,在左栏导航或:',
|
||||
noSchema: '没有模式',
|
||||
restorePreviourSession: '恢复以前的会话',
|
||||
runQuery: '运行查询',
|
||||
thereAreNoTableFields: '没有表的字段',
|
||||
newTable: '新表',
|
||||
newView: '新视图',
|
||||
newTrigger: '新触发器',
|
||||
newRoutine: '新例程',
|
||||
newFunction: '新函数',
|
||||
newScheduler: '新调度器',
|
||||
newTriggerFunction: '新触发函数',
|
||||
thereIsNoQueriesYet: '还没有查询',
|
||||
searchForQueries: '搜索查询',
|
||||
killProcess: '杀死进程',
|
||||
closeTab: '关闭标签',
|
||||
goToDownloadPage: '跳转到下载页面',
|
||||
readOnlyMode: '只读模式',
|
||||
killQuery: '停止查询'
|
||||
},
|
||||
faker: {
|
||||
address: '地址',
|
||||
commerce: '商业',
|
||||
company: '公司',
|
||||
database: '数据库',
|
||||
date: '日期',
|
||||
finance: '财务',
|
||||
git: 'Git',
|
||||
hacker: '黑客',
|
||||
internet: '互联网',
|
||||
lorem: 'Lorem',
|
||||
name: '姓名',
|
||||
music: '音乐',
|
||||
phone: '电话',
|
||||
random: '随机',
|
||||
system: '系统',
|
||||
time: '时间',
|
||||
vehicle: '车辆',
|
||||
zipCode: '邮政编码',
|
||||
zipCodeByState: '按州的邮编',
|
||||
city: '城市',
|
||||
cityPrefix: '城市前缀',
|
||||
citySuffix: '城市后缀',
|
||||
streetName: '街道名称',
|
||||
streetAddress: '街道地址',
|
||||
streetSuffix: '街道前缀',
|
||||
streetPrefix: '街道后缀',
|
||||
secondaryAddress: '次要地址',
|
||||
county: '县',
|
||||
country: '国家',
|
||||
countryCode: '国家代码',
|
||||
state: '州',
|
||||
stateAbbr: '州的缩写',
|
||||
latitude: '纬度',
|
||||
longitude: '经度',
|
||||
direction: '方向',
|
||||
cardinalDirection: 'Cardinal direction',
|
||||
ordinalDirection: 'Ordinal direction',
|
||||
nearbyGPSCoordinate: '附近的GPS坐标',
|
||||
timeZone: '时区',
|
||||
color: '颜色',
|
||||
department: '部门',
|
||||
productName: '产品名称',
|
||||
price: '价格',
|
||||
productAdjective: '产品形容词',
|
||||
productMaterial: '产品材料',
|
||||
product: '产品',
|
||||
productDescription: '产品描述',
|
||||
suffixes: '后缀',
|
||||
companyName: '公司名称',
|
||||
companySuffix: '公司后缀',
|
||||
catchPhrase: 'Catch phrase',
|
||||
bs: 'BS',
|
||||
catchPhraseAdjective: 'Catch phrase adjective',
|
||||
catchPhraseDescriptor: 'Catch phrase descriptor',
|
||||
catchPhraseNoun: 'Catch phrase noun',
|
||||
bsAdjective: 'BS adjective',
|
||||
bsBuzz: 'BS buzz',
|
||||
bsNoun: 'BS noun',
|
||||
column: '列',
|
||||
type: '类型',
|
||||
collation: '校对',
|
||||
engine: 'Engine',
|
||||
past: '过去',
|
||||
future: '未来',
|
||||
between: '之间',
|
||||
recent: '最近',
|
||||
soon: '很快',
|
||||
month: '月',
|
||||
weekday: '工作日',
|
||||
account: '账户',
|
||||
accountName: '账户名称',
|
||||
routingNumber: '路由号码',
|
||||
mask: '掩码',
|
||||
amount: '金额',
|
||||
transactionType: '交易类型',
|
||||
currencyCode: '货币代码',
|
||||
currencyName: '货币名称',
|
||||
currencySymbol: '货币符号',
|
||||
bitcoinAddress: '比特币地址',
|
||||
litecoinAddress: '莱特币地址',
|
||||
creditCardNumber: '信用卡号码',
|
||||
creditCardCVV: '信用卡CVV',
|
||||
ethereumAddress: '以太坊地址',
|
||||
iban: 'Iban',
|
||||
bic: 'Bic',
|
||||
transactionDescription: '交易描述',
|
||||
branch: '分支',
|
||||
commitEntry: '提交条目',
|
||||
commitMessage: '提交信息',
|
||||
commitSha: '提交 SHA',
|
||||
shortSha: 'Short SHA',
|
||||
abbreviation: '缩写',
|
||||
adjective: '形容词',
|
||||
noun: '名词',
|
||||
verb: '动词',
|
||||
ingverb: 'Ingverb',
|
||||
phrase: '短语',
|
||||
avatar: '头像',
|
||||
email: '电子邮箱',
|
||||
exampleEmail: '电子邮件例子',
|
||||
userName: '用户名',
|
||||
protocol: '协议',
|
||||
url: 'Url',
|
||||
domainName: 'Domin name',
|
||||
domainSuffix: '域名后缀',
|
||||
domainWord: 'Domain word',
|
||||
ip: 'Ip',
|
||||
ipv6: 'Ipv6',
|
||||
userAgent: 'User agent',
|
||||
mac: 'Mac',
|
||||
password: '密码',
|
||||
word: 'Word',
|
||||
words: 'Words',
|
||||
sentence: '句子',
|
||||
slug: 'Slug',
|
||||
sentences: '句子',
|
||||
paragraph: '段落',
|
||||
paragraphs: '段落',
|
||||
text: '文本',
|
||||
lines: '行',
|
||||
genre: 'Genre',
|
||||
firstName: '名',
|
||||
lastName: '姓氏',
|
||||
middleName: '中间名',
|
||||
findName: '全名',
|
||||
jobTitle: '职位名称',
|
||||
gender: '性别',
|
||||
prefix: '前缀',
|
||||
suffix: '后缀',
|
||||
title: '标题',
|
||||
jobDescriptor: '工作描述',
|
||||
jobArea: '工作领域',
|
||||
jobType: '工作类型',
|
||||
phoneNumber: '电话号码',
|
||||
phoneNumberFormat: '电话号码格式',
|
||||
phoneFormats: '电话格式',
|
||||
number: 'Number',
|
||||
float: 'Float',
|
||||
arrayElement: '数组元素',
|
||||
arrayElements: '数组元素',
|
||||
objectElement: '对象元素',
|
||||
uuid: 'Uuid',
|
||||
boolean: 'Boolean',
|
||||
image: 'Image',
|
||||
locale: 'Locale',
|
||||
alpha: 'Alpha',
|
||||
alphaNumeric: 'Alphanumeric',
|
||||
hexaDecimal: 'Hexadecimal',
|
||||
fileName: '文件名',
|
||||
commonFileName: '普通文件名',
|
||||
mimeType: 'MIME类型',
|
||||
commonFileType: '常见的文件类型',
|
||||
commonFileExt: '常见的文件扩展名',
|
||||
fileType: '文件类型',
|
||||
fileExt: '文件扩展名',
|
||||
directoryPath: '目录路径',
|
||||
filePath: '文件路径',
|
||||
semver: 'Semver',
|
||||
manufacturer: '制造商',
|
||||
model: '型号',
|
||||
fuel: 'Fuel',
|
||||
vin: 'Vin'
|
||||
}
|
||||
};
|
Before Width: | Height: | Size: 1004 B After Width: | Height: | Size: 1004 B |
@ -3,6 +3,7 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
import '@mdi/font/css/materialdesignicons.css';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import '@/scss/main.scss';
|
||||
|
||||
import App from '@/App.vue';
|
||||
|
@ -17,4 +17,8 @@ export default class {
|
||||
static createScheduler (params) {
|
||||
return ipcRenderer.invoke('create-scheduler', params);
|
||||
}
|
||||
|
||||
static toggleScheduler (params) {
|
||||
return ipcRenderer.invoke('toggle-scheduler', params);
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,22 @@ export default class {
|
||||
return ipcRenderer.invoke('kill-process', params);
|
||||
}
|
||||
|
||||
static killTabQuery (params) {
|
||||
return ipcRenderer.invoke('kill-tab-query', params);
|
||||
}
|
||||
|
||||
static commitTab (params) {
|
||||
return ipcRenderer.invoke('commit-tab', params);
|
||||
}
|
||||
|
||||
static rollbackTab (params) {
|
||||
return ipcRenderer.invoke('rollback-tab', params);
|
||||
}
|
||||
|
||||
static destroyConnectionToCommit (params) {
|
||||
return ipcRenderer.invoke('destroy-connection-to-commit', params);
|
||||
}
|
||||
|
||||
static useSchema (params) {
|
||||
return ipcRenderer.invoke('use-schema', params);
|
||||
}
|
||||
|
@ -17,4 +17,8 @@ export default class {
|
||||
static createTrigger (params) {
|
||||
return ipcRenderer.invoke('create-trigger', params);
|
||||
}
|
||||
|
||||
static toggleTrigger (params) {
|
||||
return ipcRenderer.invoke('toggle-trigger', params);
|
||||
}
|
||||
}
|
||||
|
@ -17,10 +17,12 @@
|
||||
(
|
||||
"char": $string-color,
|
||||
"varchar": $string-color,
|
||||
"longvarchar": $string-color,
|
||||
"text": $string-color,
|
||||
"tinytext": $string-color,
|
||||
"mediumtext": $string-color,
|
||||
"longtext": $string-color,
|
||||
"string": $string-color,
|
||||
"json": $string-color,
|
||||
"name": $string-color,
|
||||
"character": $string-color,
|
||||
@ -50,6 +52,7 @@
|
||||
"oid": $number-color,
|
||||
"xid": $number-color,
|
||||
"money": $number-color,
|
||||
"number": $number-color,
|
||||
"datetime": $date-color,
|
||||
"date": $date-color,
|
||||
"time": $date-color,
|
||||
@ -70,6 +73,7 @@
|
||||
"bytea": $blob-color,
|
||||
"enum": $enum-color,
|
||||
"set": $enum-color,
|
||||
"bool": $enum-color,
|
||||
"boolean": $enum-color,
|
||||
"interval": $array-color,
|
||||
"array": $array-color,
|
||||
@ -77,6 +81,15 @@
|
||||
"tsvector": $array-color,
|
||||
"tsquery": $array-color,
|
||||
"pg_node_tree": $array-color,
|
||||
"point": $array-color,
|
||||
"linestring": $array-color,
|
||||
"polygon": $array-color,
|
||||
"geometry": $array-color,
|
||||
"multipoint": $array-color,
|
||||
"multilinestring": $array-color,
|
||||
"multipolygon": $array-color,
|
||||
"geomcollection": $array-color,
|
||||
"geometrycollection": $array-color,
|
||||
"aclitem": $array-color,
|
||||
"unknown": $unknown-color,
|
||||
)
|
||||
|
@ -20,6 +20,10 @@
|
||||
background-image: url("../images/svg/pg.svg");
|
||||
}
|
||||
|
||||
&.dbi-sqlite {
|
||||
background-image: url("../images/svg/sqlite.svg");
|
||||
}
|
||||
|
||||
&.dbi-oracledb {
|
||||
background-image: url("../images/svg/oracledb.svg");
|
||||
}
|
||||
|
@ -59,6 +59,34 @@ option:checked {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.cancellable {
|
||||
color: transparent !important;
|
||||
min-height: 0.8rem;
|
||||
position: relative;
|
||||
|
||||
> .mdi,
|
||||
> .span {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "\2715";
|
||||
color: $light-color;
|
||||
font-weight: 700;
|
||||
top: 36%;
|
||||
display: block;
|
||||
height: 0.8rem;
|
||||
left: 50%;
|
||||
margin-left: -0.4rem;
|
||||
margin-top: -0.4rem;
|
||||
opacity: 1;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 0.8rem;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-tabs {
|
||||
align-content: baseline;
|
||||
|
||||
|
@ -142,7 +142,7 @@
|
||||
|
||||
code {
|
||||
background-color: #000;
|
||||
color: $body-font-color-dark;
|
||||
color: rgba($body-font-color-dark, 0.7);
|
||||
}
|
||||
|
||||
// Antares
|
||||
|
@ -24,12 +24,24 @@ export default {
|
||||
getConnections: state => state.connections,
|
||||
getConnectionName: state => uid => {
|
||||
const connection = state.connections.filter(connection => connection.uid === uid)[0];
|
||||
if (!connection) return '';
|
||||
return connection.name
|
||||
? connection.name
|
||||
: connection.ask
|
||||
? `${connection.host}:${connection.port}`
|
||||
: `${connection.user + '@'}${connection.host}:${connection.port}`;
|
||||
let connectionName = '';
|
||||
|
||||
if (connection.name)
|
||||
connectionName = connection.name;
|
||||
else if (connection.ask)
|
||||
connectionName = `${connection.host}:${connection.port}`;
|
||||
else if (connection.databasePath) {
|
||||
let string = connection.databasePath.split(/[/\\]+/).pop();
|
||||
|
||||
if (string.length >= 30)
|
||||
string = `...${string.slice(-30)}`;
|
||||
|
||||
connectionName = string;
|
||||
}
|
||||
else
|
||||
connectionName = `${connection.user + '@'}${connection.host}:${connection.port}`;
|
||||
|
||||
return connectionName;
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
|
@ -412,6 +412,11 @@ export default {
|
||||
indexTypes = require('common/index-types/postgresql');
|
||||
customizations = require('common/customizations/postgresql');
|
||||
break;
|
||||
case 'sqlite':
|
||||
dataTypes = require('common/data-types/sqlite');
|
||||
indexTypes = require('common/index-types/sqlite');
|
||||
customizations = require('common/customizations/sqlite');
|
||||
break;
|
||||
}
|
||||
|
||||
const { status, response: version } = await Schema.getVersion(connection.uid);
|
||||
|