mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
Compare commits
538 Commits
Author | SHA1 | Date | |
---|---|---|---|
0e7b93c2df | |||
843c15e428 | |||
2982b6cb96 | |||
c08946e932 | |||
55c1604e7f | |||
d64fbbad0f | |||
|
c88d734bc0 | ||
|
99d94ea92c | ||
e4f620c5a1 | |||
99f9a9e188 | |||
|
2236c8fe75 | ||
b9a097e2f5 | |||
|
966446afd6 | ||
|
b681adc632 | ||
15d0158993 | |||
e76d324810 | |||
|
897795ddbb | ||
|
c32f463ea5 | ||
|
25e1ba4384 | ||
1bd26ceaa6 | |||
56a0361ed2 | |||
|
c0dd3e0941 | ||
|
33ab5d7491 | ||
72e6a23fd6 | |||
|
fd129a2ad1 | ||
2c63cbc4e8 | |||
c9a33936a0 | |||
a6bdf69a28 | |||
dd971d70e0 | |||
669d7e8d4d | |||
02f204a01d | |||
|
023d7aa92d | ||
4ca40c07d6 | |||
cf5247bf35 | |||
19db29663b | |||
d010d5aa8f | |||
0a1f50a9b9 | |||
e7da5a7040 | |||
2d126d521c | |||
0fca70ebec | |||
36358584fd | |||
672896414e | |||
b06bafe06c | |||
4fe9dfc4d7 | |||
7af178a1e4 | |||
212b2bdba9 | |||
72accb7b0e | |||
321b387083 | |||
ece6c6401d | |||
5a028a4ea2 | |||
83f9b12be0 | |||
1c1403f586 | |||
038cf68253 | |||
3580faebba | |||
ae312efbbc | |||
8e422e3f07 | |||
7d1967a609 | |||
7ff8e2149e | |||
1b5cc315dd | |||
27566c1dfa | |||
03777a2ea3 | |||
d91251d7cb | |||
0827a04d61 | |||
2c8509ff41 | |||
76df6319c2 | |||
e6f6a022d1 | |||
95bb41e9db | |||
7ab84bde57 | |||
d190a2dd61 | |||
d8a298fd20 | |||
369622d5af | |||
a40d722d7c | |||
440f74dfc1 | |||
8621ca5333 | |||
24edc82b1b | |||
0a2124f2c2 | |||
a8521317a5 | |||
88408da745 | |||
d52b7af297 | |||
e4a4696dd3 | |||
f0255c0065 | |||
9991173685 | |||
|
874dc6298b | ||
5b1b8cf4cc | |||
a521274d01 | |||
86e4c1d58f | |||
ba5a1b68ab | |||
2c45bce1ee | |||
|
2e05fa4bdc | ||
dd9c089d27 | |||
e6955550fa | |||
fe39c1d388 | |||
d114f8a651 | |||
84168d1d75 | |||
498a9b48e2 | |||
01f607cd40 | |||
efe134a059 | |||
40cb4dd98d | |||
a142d3c4d7 | |||
89da957a49 | |||
05c2f9836c | |||
ebc325ae0c | |||
39326eb52e | |||
df681147aa | |||
ffc645ba5e | |||
1fb1205319 | |||
c90ab0e880 | |||
9dc700e13e | |||
6950d0ce5a | |||
4df14c3693 | |||
c05be8304f | |||
31c575dad9 | |||
040657d5ca | |||
5043fafa93 | |||
8eb127e458 | |||
d044a02cb7 | |||
8cb2c197c8 | |||
c50d17e82b | |||
|
7c186d2dee | ||
0f219cf9b7 | |||
75c5a34095 | |||
48877534d1 | |||
c22413fde9 | |||
77ab8d8a03 | |||
4386c6ab95 | |||
19205e0736 | |||
4fc4ddd1d6 | |||
49b63bc6f2 | |||
44eb507a12 | |||
1590ffaff0 | |||
3c1bae540f | |||
44bb75bc60 | |||
8bb5bb93cf | |||
f64a12a8e9 | |||
da25823868 | |||
a9fcfd57ec | |||
e2307341f3 | |||
09a372e96d | |||
f4da28cca0 | |||
89745b7391 | |||
104b7c928b | |||
|
427360d826 | ||
e29d86b409 | |||
|
0bfa14e1c9 | ||
88ba55ec02 | |||
aaff4cf4fe | |||
35c54aee84 | |||
be2e9b21f5 | |||
2262278393 | |||
531e17889a | |||
a07ed58004 | |||
00dc59a76d | |||
2f883bfeb2 | |||
7ff16fccce | |||
|
3625fbc1b0 | ||
|
deee0d637b | ||
8c6950cebd | |||
46167e4473 | |||
c32a4415d1 | |||
1c3d7aa30b | |||
664d18efc1 | |||
cc941dfc04 | |||
1d151e9349 | |||
addd9fba28 | |||
a00c19d300 | |||
9551afbd2d | |||
1ead76c028 | |||
d3da15aa13 | |||
f3b5de38c4 | |||
|
d4b6d2e9d1 | ||
|
e2c106e4e0 | ||
eb60899e6e | |||
1d367d468d | |||
8ecaedbf6c | |||
|
dd1eebd4ec | ||
8c83b3f144 | |||
985e5d3527 | |||
78902639eb | |||
cb038b374a | |||
eafdb1cc3d | |||
91e0630513 | |||
bf768c3800 | |||
0b1aa3dd29 | |||
ec75f9546a | |||
9bc9adb7cf | |||
b4d14d98db | |||
c21bd6075c | |||
44647f5b55 | |||
3f9e6d85ca | |||
6a6f43a718 | |||
f12a04b052 | |||
abf829867e | |||
b71f04e5aa | |||
7725fafe85 | |||
ed3d35f131 | |||
e0946f04f7 | |||
0891e7be8c | |||
f312cf5f85 | |||
05e0d310ec | |||
|
1e0b2b4cae | ||
5ee728cfe4 | |||
a91fa8ff54 | |||
|
2c13433900 | ||
dcc2a4c51c | |||
7537dff401 | |||
853ee1f572 | |||
cf9c7c600a | |||
73b88cad9e | |||
d2eb31a63d | |||
d6b36b1f80 | |||
|
d66b932683 | ||
20f5497034 | |||
d4ed886489 | |||
|
aaa14f112a | ||
|
e0f3ff6939 | ||
72a328785c | |||
4c2a1998a9 | |||
8e705706ae | |||
56b0a4815c | |||
a9a4344a71 | |||
ec5ab73b19 | |||
71a5b5c828 | |||
4519829aa2 | |||
a703dcc53e | |||
36e98e0742 | |||
a45d76e8b4 | |||
7702ca025f | |||
f632a0f189 | |||
e97da37103 | |||
6573fe69ac | |||
902c29ffa5 | |||
5f57a9f60d | |||
822af44a47 | |||
e42c424a13 | |||
0a3a4827dd | |||
a80d227400 | |||
cfd82c8f41 | |||
91d0735a5f | |||
93ce619782 | |||
8f01740475 | |||
869c75f654 | |||
|
14aff67d2d | ||
174579bf8c | |||
0eab0a7140 | |||
a103617ce8 | |||
61ec3cbd4e | |||
1765d9fd0b | |||
329656ff1d | |||
|
067a6f3507 | ||
d12c6f5210 | |||
89e8d9fcdb | |||
ee623b0a0f | |||
33a4663694 | |||
c37138c6f5 | |||
e754877ee6 | |||
ed38a9e7ff | |||
6bad032f0d | |||
d214c1f35b | |||
77d9cac092 | |||
cba2ce2e37 | |||
bd46d17424 | |||
5b33419b64 | |||
00242697a1 | |||
85cec05f70 | |||
5fa8bf38e4 | |||
23acf00def | |||
be70b5be7f | |||
7fc01227e7 | |||
1c666a07d8 | |||
|
49abd1ea7f | ||
|
d3b9e08446 | ||
20b814378b | |||
8ce1d1a964 | |||
d151c7254e | |||
26aad519df | |||
31b7999bba | |||
caf776bd55 | |||
a7d5e1973c | |||
8870304c15 | |||
2007305ff0 | |||
e97401e27d | |||
62f6fd16d5 | |||
cdca6eaa35 | |||
34e8d3e5b1 | |||
6c8a36e947 | |||
99b1c1be12 | |||
3991382153 | |||
|
3b57b7ef3b | ||
|
45d599ad7f | ||
9082960310 | |||
|
5398964190 | ||
5d5f1da97b | |||
84826ff4c0 | |||
c95c593c74 | |||
5a50ba88e8 | |||
c5baf2b0d3 | |||
a082514f88 | |||
c826888b0d | |||
8a55b36527 | |||
b0d464952f | |||
|
7c45203636 | ||
|
71b0736d0d | ||
|
42bc9196ff | ||
|
f7e04d6333 | ||
45b2eb2934 | |||
9ee1b3023d | |||
79d9acb471 | |||
e02565c0d9 | |||
ff272440bd | |||
e62f280528 | |||
6d6151814e | |||
58611bf07f | |||
d494b17df7 | |||
ae377a6c3c | |||
cc5910b88f | |||
|
2b436d8613 | ||
|
1869e6a148 | ||
d1bfa282c3 | |||
|
302c66457d | ||
|
0043d07708 | ||
|
e0f85f469f | ||
|
a037d0cc01 | ||
|
5582a12bbf | ||
|
22622df2cf | ||
|
745d551cc9 | ||
a93e71d751 | |||
2a7433da16 | |||
fd5dca2450 | |||
da6481f458 | |||
c7417809f4 | |||
5cb8ec65ad | |||
e4c5d9b404 | |||
bfdb463390 | |||
9fcc7d1cef | |||
70cb92521f | |||
981d31957f | |||
619478c072 | |||
eb1afed108 | |||
0fa22fbe72 | |||
5e4abd3e81 | |||
|
81d897c7b3 | ||
|
b148c210a6 | ||
75aa299f8d | |||
f4795eb92a | |||
18b66b5032 | |||
cdbadbb11a | |||
47e1f27bb8 | |||
4be0f6bbbc | |||
ff8bb45d6d | |||
0d52282aa8 | |||
e9dedfaf32 | |||
9f0ec5e0ce | |||
|
1174bab0cc | ||
9dd685b062 | |||
0821586bb3 | |||
350d53642e | |||
|
88eb9f7ab8 | ||
|
b0195260c3 | ||
|
dfe3728269 | ||
daef6f91b0 | |||
d61acf8a00 | |||
8b04a2b7dd | |||
6780401cb7 | |||
b8e734d827 | |||
4f8bc26349 | |||
|
3eb781021c | ||
|
3cca3ed2b3 | ||
434b9f8284 | |||
690cdcb2eb | |||
c50cbc577f | |||
6af0c33461 | |||
e60789f320 | |||
afa23f3ef1 | |||
7c4b605467 | |||
443170bbb1 | |||
3450497010 | |||
|
eaf38df1b7 | ||
a9f88e5784 | |||
472fa6f430 | |||
f82db96f34 | |||
79f32ca442 | |||
ce0f278caf | |||
6adc93e1cd | |||
5e2ad8c377 | |||
d85662cb7d | |||
e3907914f2 | |||
c6c14fbf2b | |||
3975359292 | |||
9a0f982723 | |||
b4d9821300 | |||
75a41769bf | |||
5dbc127b51 | |||
a315eeae6c | |||
25a6fded2e | |||
42fcded9f1 | |||
186cb85d2e | |||
fc1d6fba7f | |||
cbef7489b2 | |||
96d6913a7a | |||
|
744c623914 | ||
0b861d962d | |||
6cc098c6f0 | |||
40828cb3ff | |||
6086ca4a80 | |||
d92facf518 | |||
|
e9643a0d6b | ||
|
cc609a7051 | ||
796f61bf2f | |||
0f9c991f53 | |||
638a88a1fb | |||
026d74c8c8 | |||
77f8cac6cf | |||
d6fadf5db0 | |||
408ddeda56 | |||
88cd097ec0 | |||
f12e6a96dd | |||
a8ca8f2f76 | |||
|
6fcae957e1 | ||
|
1cd2d8abf3 | ||
ed3c5fe559 | |||
|
9601c59392 | ||
|
14d20a30c1 | ||
42376b4bc6 | |||
e9079adb25 | |||
86f011f34f | |||
bb02479b71 | |||
a67071e284 | |||
8f3efabb69 | |||
db628f7722 | |||
e4bd747381 | |||
88eb113e53 | |||
9109b2c328 | |||
dd070d008d | |||
ee415da127 | |||
f0d368e3e3 | |||
4be55f3fe9 | |||
fd00ea42ee | |||
50b4411e9a | |||
|
cd0a5dc034 | ||
|
9fd427c4ff | ||
f3759b6541 | |||
191a354020 | |||
|
7dc314eecf | ||
|
330a80fe70 | ||
|
b2ce533b82 | ||
|
12ce6b1135 | ||
|
71c5829702 | ||
1c4d5b05b3 | |||
e85197a2cc | |||
|
abf2b92e6e | ||
|
3be826df4b | ||
9db6bfd255 | |||
|
110adf90de | ||
|
40e4fbda15 | ||
8e983ad2cb | |||
|
6305752ad1 | ||
cc02e2c5a8 | |||
|
f4a63eae2a | ||
763be8532d | |||
|
a6f5645a22 | ||
|
bbe13f27dc | ||
f444746f46 | |||
b4af645941 | |||
a5c8daa5b8 | |||
1a9fc37285 | |||
f0351e5b94 | |||
9bda4e71b7 | |||
7c00055034 | |||
4479a9600b | |||
|
7a6bd8bdbd | ||
ad713fcf35 | |||
251795e2d2 | |||
45cda7a7cc | |||
b7039553cc | |||
ddee68b4c2 | |||
573ac6d42e | |||
265f28b4d9 | |||
1990d9a3d4 | |||
748d44977e | |||
4051eff382 | |||
4276586e11 | |||
832fb0fb03 | |||
328ab61757 | |||
95d15de1bd | |||
7dcd4441c4 | |||
d81e0911ab | |||
5bfff649e9 | |||
76743e8f7c | |||
4ed2f9a939 | |||
c5eb73ed3f | |||
fa3f3e1fd8 | |||
2c7c97852f | |||
48ebf23bd1 | |||
64deedc5ad | |||
9f033fb994 | |||
401cb49687 | |||
1356011ba3 | |||
0cfd7938ee | |||
745b55a942 | |||
eef7c1dcec | |||
aa8fc545d7 | |||
a780c7e0ff | |||
|
9a1bb0599f | ||
|
d847870f67 | ||
cd24371576 | |||
|
6ef565cf07 | ||
46b45c8ab6 | |||
f28531a225 | |||
8fb1f0803e | |||
020ce36312 | |||
b4545b178f | |||
d9a3eab015 | |||
2ab49c218d | |||
|
8f9385d508 | ||
|
4e9f8d16ee | ||
0c002918eb | |||
3d56ec7b49 | |||
48c3e6afc4 | |||
|
50a5c8fe1e | ||
b0d1f115c9 | |||
a59f77f618 | |||
e7a1858091 | |||
648a51efe8 | |||
38962c4807 | |||
|
43e4cdd4cf | ||
|
91046c1ac1 | ||
|
63f8b9b6a1 | ||
|
ed476d9b5c | ||
|
f41d8c0480 | ||
e3b54a8be1 | |||
|
c2c0394624 | ||
|
d25c62b4da | ||
|
8cf738bac8 | ||
409ed54608 | |||
d9d3bf2bc9 | |||
9e9de7b5c5 | |||
|
b2a5b40c03 | ||
|
0de2321920 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"projectName": "antares",
|
||||
"projectOwner": "Fabio286",
|
||||
"projectOwner": "antares-sql",
|
||||
"repoType": "github",
|
||||
"repoHost": "https://github.com",
|
||||
"files": [
|
||||
@@ -120,6 +120,79 @@
|
||||
"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"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "raliqala",
|
||||
"name": "Topollo",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/30502407?v=4",
|
||||
"profile": "https://github.com/raliqala",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SmileYzn",
|
||||
"name": "Cleverson",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5851851?v=4",
|
||||
"profile": "https://github.com/SmileYzn",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fredatgithub",
|
||||
"name": "fred",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6720055?v=4",
|
||||
"profile": "https://github.com/fredatgithub",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "xak666",
|
||||
"name": "xaka_xak",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/38811437?v=4",
|
||||
"profile": "https://github.com/xak666",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "brdtheo",
|
||||
"name": "Théo Billardey",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/48206778?v=4",
|
||||
"profile": "https://codepen.io/theo-billardey",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dyaskur",
|
||||
"name": "Muhammad Dyas Yaskur",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9539970?v=4",
|
||||
"profile": "http://yaskur.net",
|
||||
"contributions": [
|
||||
"translation",
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
@@ -1,4 +1,5 @@
|
||||
node_modules
|
||||
assets
|
||||
out
|
||||
dist
|
||||
dist
|
||||
build
|
28
.eslintrc
28
.eslintrc
@@ -6,15 +6,23 @@
|
||||
},
|
||||
"extends": [
|
||||
"standard",
|
||||
"plugin:vue/recommended"
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:vue/vue3-recommended"
|
||||
],
|
||||
"parser": "vue-eslint-parser",
|
||||
"parserOptions": {
|
||||
"parser": "@babel/eslint-parser",
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"ecmaVersion": 9,
|
||||
"sourceType": "module",
|
||||
"requireConfigFile": false
|
||||
},
|
||||
"plugins": [
|
||||
"vue",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"space-infix-ops": "off",
|
||||
"object-curly-newline": "off",
|
||||
"indent": [
|
||||
"error",
|
||||
3,
|
||||
@@ -69,6 +77,20 @@
|
||||
"max": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"@typescript-eslint/member-delimiter-style": [
|
||||
"warn",
|
||||
{
|
||||
"multiline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": true
|
||||
},
|
||||
"singleline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-var-requires": "off"
|
||||
}
|
||||
}
|
13
.github/ISSUE_TEMPLATE/bug_report.md
vendored
13
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -26,13 +26,16 @@ If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Application (please complete the following information):**
|
||||
|
||||
- Version [e.g. 0.14.0]
|
||||
- Distribution: [e.g. exe, Linux Store, AppImage, dmg]
|
||||
- App client [e.g. MySQL]
|
||||
- App version [e.g. 0.5.2]
|
||||
- Installation source: [e.g. exe, Linux Store, AppImage, dmg]
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
**Environment (please complete the following information):**
|
||||
|
||||
- OS: [e.g. iOS]
|
||||
- Version [e.g. 22]
|
||||
- OS name: [e.g. Windows 11]
|
||||
- OS version [e.g. 21H2]
|
||||
- DB name [e.g. MariaDB]
|
||||
- DB version [e.g. 10.3.34]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -6,6 +6,8 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm"
|
||||
allow:
|
||||
- dependency-type: "production"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
interval: "monthly"
|
||||
|
32
.github/workflows/build-linux.yml
vendored
32
.github/workflows/build-linux.yml
vendored
@@ -1,32 +0,0 @@
|
||||
name: Build/release [linux]
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test
|
||||
|
||||
- name: Build/release Electron app
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
32
.github/workflows/build-mac.yml
vendored
32
.github/workflows/build-mac.yml
vendored
@@ -1,32 +0,0 @@
|
||||
name: Build/release [mac]
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test
|
||||
|
||||
- name: Build/release Electron app
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
32
.github/workflows/build-win.yml
vendored
32
.github/workflows/build-win.yml
vendored
@@ -1,32 +0,0 @@
|
||||
name: Build/release [windows]
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test
|
||||
|
||||
- name: Build/release Electron app
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
37
.github/workflows/build.yml
vendored
Normal file
37
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Build & release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-11, ubuntu-20.04, windows-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i
|
||||
|
||||
- name: "Build"
|
||||
run: npm run build
|
||||
|
||||
- name: Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
artifacts: "build/*.AppImage,build/*.yml,build/*.deb,build/*.dmg,build/*.blockmap,build/*.zip,build/*.exe"
|
||||
allowUpdates: true
|
||||
draft: true
|
||||
generateReleaseNotes: true
|
31
.github/workflows/create-artifact-linux.yml
vendored
Normal file
31
.github/workflows/create-artifact-linux.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Create artifact [LINUX]
|
||||
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: npm install & build
|
||||
run: |
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux-build
|
||||
retention-days: 3
|
||||
path: |
|
||||
build
|
||||
!build/*-unpacked
|
||||
!build/.icon-ico
|
31
.github/workflows/create-artifact-macos.yml
vendored
Normal file
31
.github/workflows/create-artifact-macos.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Create artifact [MAC]
|
||||
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: npm install & build
|
||||
run: |
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: macos-build
|
||||
retention-days: 3
|
||||
path: |
|
||||
build
|
||||
!build/*-unpacked
|
||||
!build/.icon-ico
|
26
.github/workflows/test-e2e-win.yml
vendored
Normal file
26
.github/workflows/test-e2e-win.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Test end-to-end [WINDOWS]
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test:e2e
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,8 +1,10 @@
|
||||
.DS_Store
|
||||
dist
|
||||
build
|
||||
misc/*
|
||||
!misc/.gitkeep
|
||||
node_modules
|
||||
thumbs.db
|
||||
NOTES.md
|
||||
*.txt
|
||||
package-lock.json
|
||||
*.heapsnapshot
|
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"extends": [
|
||||
"stylelint-config-standard"
|
||||
"stylelint-config-standard",
|
||||
"stylelint-config-recommended-vue"
|
||||
],
|
||||
"fix": true,
|
||||
"formatter": "verbose",
|
||||
@@ -10,6 +11,7 @@
|
||||
"rules": {
|
||||
"at-rule-no-unknown": null,
|
||||
"no-descending-specificity": null,
|
||||
"font-family-no-missing-generic-family-keyword": null,
|
||||
"declaration-colon-newline-after": "always-multi-line"
|
||||
},
|
||||
"syntax": "scss"
|
||||
|
12
.vscode/launch.json
vendored
12
.vscode/launch.json
vendored
@@ -2,10 +2,9 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Electron: Main",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"port": 9222,
|
||||
"protocol": "inspector",
|
||||
"request": "attach",
|
||||
"sourceMaps": true,
|
||||
"type": "node",
|
||||
@@ -18,6 +17,15 @@
|
||||
"sourceMaps": true,
|
||||
"type": "chrome",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Electron: Worker",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"port": 9224,
|
||||
"request": "attach",
|
||||
"sourceMaps": true,
|
||||
"type": "node",
|
||||
"timeout": 1000000
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
|
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -4,7 +4,13 @@
|
||||
"core",
|
||||
"MySQL",
|
||||
"PostgreSQL",
|
||||
"SQLite"
|
||||
"SQLite",
|
||||
"Firebird SQL",
|
||||
"Windows",
|
||||
"translation",
|
||||
"Linux",
|
||||
"MacOS",
|
||||
"deps"
|
||||
],
|
||||
"svg.preview.background": "transparent"
|
||||
}
|
555
CHANGELOG.md
555
CHANGELOG.md
@@ -2,6 +2,561 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
### [0.7.1](https://github.com/antares-sql/antares/compare/v0.7.0...v0.7.1) (2022-12-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Copy rows as html table, so we can paste it to spreadsheet ([c32f463](https://github.com/antares-sql/antares/commit/c32f463ea5ac3f54cba32929f77442f1e0ba934a))
|
||||
* option to disable selected query execution, closes [#477](https://github.com/antares-sql/antares/issues/477) ([1bd26ce](https://github.com/antares-sql/antares/commit/1bd26ceaa68fe66f26c76b3b60fa6eeccea91729))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* bahasa indonesia typos ([897795d](https://github.com/antares-sql/antares/commit/897795ddbb4ade2652b0471f18288b8b3aaf0eb9))
|
||||
* connection default icon not change after client change ([a6bdf69](https://github.com/antares-sql/antares/commit/a6bdf69a281c8614c41274b6dc2f3563aa89c57e))
|
||||
* context submenu outside view when near the edge, fixes [#506](https://github.com/antares-sql/antares/issues/506) ([c08946e](https://github.com/antares-sql/antares/commit/c08946e932884e5f0253df2545f98315ab7e5219))
|
||||
* **i18n:** add missing keys for french translation ([fd129a2](https://github.com/antares-sql/antares/commit/fd129a2ad1c3401372c9172b38f4406254d134df))
|
||||
* **MySQL:** not every connection gets read-only option ([843c15e](https://github.com/antares-sql/antares/commit/843c15e428c4a0412f19a93ab05d2fcbb60da09b))
|
||||
* **UI:** white background dragging connections inside folder on Linux ([dd971d7](https://github.com/antares-sql/antares/commit/dd971d70e04faf0d5b239586b12e4a9a42407433))
|
||||
* white background dragging connections or tabs on Linux, fixes [#486](https://github.com/antares-sql/antares/issues/486) ([669d7e8](https://github.com/antares-sql/antares/commit/669d7e8d4d062ed5bdafe1d5cde8ec51a2f68b26))
|
||||
|
||||
## [0.7.0](https://github.com/antares-sql/antares/compare/v0.6.0...v0.7.0) (2022-11-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **UI:** connections customization ([212b2bd](https://github.com/antares-sql/antares/commit/212b2bdba933db1af22967a057d64f41c96b9930))
|
||||
* **UI:** folders customization ([72accb7](https://github.com/antares-sql/antares/commit/72accb7b0e62977dde2c82312aee5b9d025e5182))
|
||||
* **UI:** folders implementation ([ece6c64](https://github.com/antares-sql/antares/commit/ece6c6401def4a9b42280f23efaa038b9ad98de8))
|
||||
* **UI:** footer color based on folder color ([4fe9dfc](https://github.com/antares-sql/antares/commit/4fe9dfc4d7ca19a798b8a8a74d979ab88aebeaac))
|
||||
* **UI:** new settimgbar tooltips ([6728964](https://github.com/antares-sql/antares/commit/672896414e901635c83ca037663e355a31ce013b))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* deletion of connections inside folder ([b06bafe](https://github.com/antares-sql/antares/commit/b06bafe06c060233ebe901979b9fc4455a25cb89))
|
||||
* missing sidebar data after update ([0a1f50a](https://github.com/antares-sql/antares/commit/0a1f50a9b92c9705784b93f14be40a01a78cb0da))
|
||||
* **UI:** folder to folder drag glitches ([0fca70e](https://github.com/antares-sql/antares/commit/0fca70ebec1af3d594db4e1ce159e52e12224850))
|
||||
* **UI:** wrong copnnection icons color with light theme ([d010d5a](https://github.com/antares-sql/antares/commit/d010d5aa8f07a5def1183567ee767fd144696ed3))
|
||||
* wrong position moving elements outside folder ([7af178a](https://github.com/antares-sql/antares/commit/7af178a1e400876e6c45dbe31a198a12d29227a8))
|
||||
|
||||
## [0.6.0](https://github.com/antares-sql/antares/compare/v0.5.19...v0.6.0) (2022-11-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Firebird SQL:** connections pool ([76df631](https://github.com/antares-sql/antares/commit/76df6319c242ea42f93d4e5d811d96ec2c282aa8))
|
||||
* **Firebird SQL:** display table content and query results ([95bb41e](https://github.com/antares-sql/antares/commit/95bb41e9db255a780aae1ae32ce4a53ee3bab20e))
|
||||
* **Firebird SQL:** manual commit mode ([27566c1](https://github.com/antares-sql/antares/commit/27566c1dfae55f72d3f870c50410e5ecda256037))
|
||||
* **Firebird SQL:** procedure add/edit/delete support ([ae312ef](https://github.com/antares-sql/antares/commit/ae312efbbc3a9941380477b9849bdd8edc5b9fbf))
|
||||
* **Firebird SQL:** support to blob fields ([0827a04](https://github.com/antares-sql/antares/commit/0827a04d61af75b4366394e5f0289df461d02c98))
|
||||
* **Firebird SQL:** support to indexes and foreign keys ([2c8509f](https://github.com/antares-sql/antares/commit/2c8509ff4173fbebeff92ab472a37edd3d80a2ac))
|
||||
* **Firebird SQL:** table add/edit/delete support ([1b5cc31](https://github.com/antares-sql/antares/commit/1b5cc315dddca6b753fb6fe6e196e29441ffed79))
|
||||
* **Firebird SQL:** trigger add/edit/delete support ([8e422e3](https://github.com/antares-sql/antares/commit/8e422e3f07323f388523621a05f0403a87f19e47))
|
||||
* **Firebird SQL:** view add/edit/delete support ([7d1967a](https://github.com/antares-sql/antares/commit/7d1967a60977b2ce1095a37b7135f429a83f163d))
|
||||
* support to text blob fields ([e6f6a02](https://github.com/antares-sql/antares/commit/e6f6a022d1a5bbc3f5303f635a2115813601c61a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Firebird SQL:** connection pool issue ([7ff8e21](https://github.com/antares-sql/antares/commit/7ff8e2149ef911a235b4a1dcc329775af1d2a72b))
|
||||
* incomplete list of collations, fixes [#478](https://github.com/antares-sql/antares/issues/478) ([1c1403f](https://github.com/antares-sql/antares/commit/1c1403f58641f7b5f8a7c29fc430673ffa88f969))
|
||||
* loss of precision updating BIGINT values, fixes [#467](https://github.com/antares-sql/antares/issues/467) ([d190a2d](https://github.com/antares-sql/antares/commit/d190a2dd61040d1748dfb97403f9d56015d938fe))
|
||||
|
||||
### [0.5.19](https://github.com/antares-sql/antares/compare/v0.5.18...v0.5.19) (2022-10-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* context menu option to fill cell with random values ([0a2124f](https://github.com/antares-sql/antares/commit/0a2124f2c2bdadc7c753db11d1e29f8acb9ddcac))
|
||||
* uuid fill for string cells ([24edc82](https://github.com/antares-sql/antares/commit/24edc82b1be7299a09df18611b2a0ba361a6b46f))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* app stuck inserting a random value if field length high ([440f74d](https://github.com/antares-sql/antares/commit/440f74dfc1f4942ba585b9bdae7517fe6ab04a81))
|
||||
* error joining tables with different schema ([88408da](https://github.com/antares-sql/antares/commit/88408da745e45c70de977bc4270e5f61825be65f))
|
||||
* **SQLite:** save boolean as integer to improve compativility, fixes [#463](https://github.com/antares-sql/antares/issues/463) ([d52b7af](https://github.com/antares-sql/antares/commit/d52b7af2978bc8beafd2d22078c72abb62e9e532))
|
||||
* unable to edit text fields if value is NULL, fixes [#466](https://github.com/antares-sql/antares/issues/466) ([8621ca5](https://github.com/antares-sql/antares/commit/8621ca5333b5c51dc7a2ea1fcc0c5ec7f752a00a))
|
||||
|
||||
### [0.5.18](https://github.com/antares-sql/antares/compare/v0.5.17...v0.5.18) (2022-10-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **PostgreSQL:** UUID random generation option on UUID fields, closes [#424](https://github.com/antares-sql/antares/issues/424) ([a521274](https://github.com/antares-sql/antares/commit/a521274d01031c1411bbbb136369802d43368089))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* auto-scroll on sidebar not working, fixes [#447](https://github.com/antares-sql/antares/issues/447) ([dd9c089](https://github.com/antares-sql/antares/commit/dd9c089d27c61ab76d49887c7de2849cbb6e88a6))
|
||||
* trackpad horizontal scroll on tabs not working properly ([ba5a1b6](https://github.com/antares-sql/antares/commit/ba5a1b68ab2d56777a5c94eede26e9bded5819e6))
|
||||
|
||||
### [0.5.17](https://github.com/antares-sql/antares/compare/v0.5.16...v0.5.17) (2022-09-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* added more editor font sizes, closes [#440](https://github.com/antares-sql/antares/issues/440) ([d114f8a](https://github.com/antares-sql/antares/commit/d114f8a65164f702b23175095e6fc2b021e0e038))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* "run or reload" shortcut triggers on all connections open ([01f607c](https://github.com/antares-sql/antares/commit/01f607cd40c18ab0f9761b2a05705a966aaae43a))
|
||||
* cant run procedures with parameters from leftbar ([efe134a](https://github.com/antares-sql/antares/commit/efe134a059700ca87333dc6e66166d6ec8d289e8))
|
||||
* editor font size doesn't change on new tabs, fixes [#442](https://github.com/antares-sql/antares/issues/442) ([84168d1](https://github.com/antares-sql/antares/commit/84168d1d75460acc2c844bfece7d85f0c977e74c))
|
||||
* empty definer when editing a view, fixes [#437](https://github.com/antares-sql/antares/issues/437) ([498a9b4](https://github.com/antares-sql/antares/commit/498a9b48e25ee061960f5f649c953cdaf6ff1a58))
|
||||
* **MacOS:** empty options on macos menubar ([a142d3c](https://github.com/antares-sql/antares/commit/a142d3c4d77e31375dfbea148eec54ce1f635192))
|
||||
|
||||
### [0.5.16](https://github.com/antares-sql/antares/compare/v0.5.15...v0.5.16) (2022-08-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* CTRL+Right/Left not working on text editor, closes [#427](https://github.com/antares-sql/antares/issues/427) ([ffc645b](https://github.com/antares-sql/antares/commit/ffc645ba5efb1c52670096e4f8c7f992b7335dae))
|
||||
* issue updating datetime cells with null value, closes [#423](https://github.com/antares-sql/antares/issues/423) ([ebc325a](https://github.com/antares-sql/antares/commit/ebc325ae0c656dca2eb8f7544ab271beaee9b47e))
|
||||
* ts exceptions ([df68114](https://github.com/antares-sql/antares/commit/df681147aaf0bfca69f3ffdc474cc1846541b1d8))
|
||||
* **UI:** editor themes group not visible in select element ([9dc700e](https://github.com/antares-sql/antares/commit/9dc700e13ea65bb8c6feac4ff4ffeadd32053614))
|
||||
* **UI:** wrong position of fields resizable area ([c90ab0e](https://github.com/antares-sql/antares/commit/c90ab0e8807ff30a9ab58e9aa3515cf427dd6e86))
|
||||
* unable to set null or delete rows without primary key ([39326eb](https://github.com/antares-sql/antares/commit/39326eb52e038728b5419d4a8de8024c7ead3002))
|
||||
|
||||
### [0.5.15](https://github.com/antares-sql/antares/compare/v0.5.14...v0.5.15) (2022-08-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* ability to add new shortcuts ([d044a02](https://github.com/antares-sql/antares/commit/d044a02cb79a9d06aadc34cdbf6e81da84360559))
|
||||
* ability to edit shortcuts ([8eb127e](https://github.com/antares-sql/antares/commit/8eb127e45838bc01ba12f0740fec077fcd975532))
|
||||
* added more events in shortcuts setting ([5043faf](https://github.com/antares-sql/antares/commit/5043fafa934844ebc2f59cabcec830c6a4d5ca8e))
|
||||
* delete shortcuts and restore defaults ([c22413f](https://github.com/antares-sql/antares/commit/c22413fde9dfe5501a5f220070cfe552a318c70b))
|
||||
* dynamic shortcut suggestions on empty query tabs ([4df14c3](https://github.com/antares-sql/antares/commit/4df14c3693955bd7801b4b99103fca85f00f3e8c))
|
||||
* list of available shortcuts in settings window ([44bb75b](https://github.com/antares-sql/antares/commit/44bb75bc60d7d31bbd99a9ba57f30fd354f7581c))
|
||||
* **UI:** connection name on left bar, closes [#382](https://github.com/antares-sql/antares/issues/382) [#414](https://github.com/antares-sql/antares/issues/414) ([4887753](https://github.com/antares-sql/antares/commit/48877534d1a41d351b267c0dab925046ca984179))
|
||||
* **UI:** shortcuts setting UI improved ([49b63bc](https://github.com/antares-sql/antares/commit/49b63bc6f28fc6031e6a892d0a48cd35ae2f26cd))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* startup exception ([c50d17e](https://github.com/antares-sql/antares/commit/c50d17e82b7fd337d4037ddf646cd1a8fc765bae))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* improved keypress detector ([0f219cf](https://github.com/antares-sql/antares/commit/0f219cf9b796b4369c609fb0e8e3b84346a30b07))
|
||||
* **translation:** updated italian translation ([c05be83](https://github.com/antares-sql/antares/commit/c05be8304f3cf299cf338f67c00184305e022919))
|
||||
|
||||
### [0.5.14](https://github.com/antares-sql/antares/compare/v0.5.13...v0.5.14) (2022-08-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* unable to open settingbar context menu ([44eb507](https://github.com/antares-sql/antares/commit/44eb507a12bad028a4fa8a8bb0f6442a3e8dde91))
|
||||
|
||||
### [0.5.13](https://github.com/antares-sql/antares/compare/v0.5.12...v0.5.13) (2022-08-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* copy row as CSV, closes [#394](https://github.com/antares-sql/antares/issues/394) ([1c3d7aa](https://github.com/antares-sql/antares/commit/1c3d7aa30bb9c2bd900a764ee6b97960729e9263))
|
||||
* new macos icon ([0bfa14e](https://github.com/antares-sql/antares/commit/0bfa14e1c90320578597df030941530b670a4131))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **MySQL:** error with ANSI sql_mode ([f64a12a](https://github.com/antares-sql/antares/commit/f64a12a8e9c5f764c3a692f1a032736e008058b5))
|
||||
* set legacy: false ([104b7c9](https://github.com/antares-sql/antares/commit/104b7c928b9c2abfc056880f16c606a0b1fa7c67))
|
||||
|
||||
### [0.5.12](https://github.com/antares-sql/antares/compare/v0.5.11...v0.5.12) (2022-07-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* ability to copy multiple selected rows ([9551afb](https://github.com/antares-sql/antares/commit/9551afbd2d7e525c81f28e98e788b92609ce9de4))
|
||||
* context menu option to duplicate a table row ([985e5d3](https://github.com/antares-sql/antares/commit/985e5d352793d1b3e1981d004b6f494bfbb049bf))
|
||||
* copy row as SQL INSERT ([d3da15a](https://github.com/antares-sql/antares/commit/d3da15aa1377dcba73927047563f1d0c2d1284ca))
|
||||
* execute selected query ([7890263](https://github.com/antares-sql/antares/commit/78902639ebb29a8c53f8aa0d2045c74e0646febc))
|
||||
* export table content as SQL INSERT ([f3b5de3](https://github.com/antares-sql/antares/commit/f3b5de38c4abfd2c1d738e179fc22e6c8b6f9080))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disable ctrl+alt+(left/right) shortcut on linux ([8ecaedb](https://github.com/antares-sql/antares/commit/8ecaedbf6c2fc0dc56ff2177a87dd6ede74bdd22))
|
||||
* error on schema export ([1d151e9](https://github.com/antares-sql/antares/commit/1d151e9349fd97576ccd8ef7f88ca789a1f28b65))
|
||||
* issue with logger on import/export ([cb038b3](https://github.com/antares-sql/antares/commit/cb038b374a4fe85ad569e42eee7af123c925e775))
|
||||
* missing defaults on insert row window ([1ead76c](https://github.com/antares-sql/antares/commit/1ead76c02889f48bd91cae702820b082ca2ff54b))
|
||||
* missing table on insert new records on session restored tabs ([8c83b3f](https://github.com/antares-sql/antares/commit/8c83b3f1447354ec63b2a308db05ad4d54659aa7))
|
||||
* **MySQL:** missing quoted identifier for column names in table filter, closes [#380](https://github.com/antares-sql/antares/issues/380) ([eb60899](https://github.com/antares-sql/antares/commit/eb60899e6e17879c79a7ee7108061e9aca8596f7))
|
||||
* prevent ctrl+a in console ([a00c19d](https://github.com/antares-sql/antares/commit/a00c19d3003cd43d3ee6e3132728122bb2b24c97))
|
||||
|
||||
### [0.5.11](https://github.com/antares-sql/antares/compare/v0.5.10...v0.5.11) (2022-07-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* console events disabled in production ([0b1aa3d](https://github.com/antares-sql/antares/commit/0b1aa3dd299db641df3d4c56c7ee56a187fc3ab3))
|
||||
* filter persists switching temporary table tabs ([bf768c3](https://github.com/antares-sql/antares/commit/bf768c380087b65604b5b571a9858a7f07bd681d))
|
||||
* unable to edit table fields content on tables with datetime fields ([91e0630](https://github.com/antares-sql/antares/commit/91e06305133c97ea02dcfdc4e739a4b0a7e7049d))
|
||||
|
||||
### [0.5.10](https://github.com/antares-sql/antares/compare/v0.5.9...v0.5.10) (2022-07-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* context menu to copy queries from console ([c21bd60](https://github.com/antares-sql/antares/commit/c21bd6075c1203607c05e45b76233d57e3008190))
|
||||
* Ctrl+PgUp & Ctrl+PgDn to navigate between tabs ([abf8298](https://github.com/antares-sql/antares/commit/abf829867e567354e534cff3e02a6d43f4c7a262))
|
||||
* field names suggestion for tables in the query ([b71f04e](https://github.com/antares-sql/antares/commit/b71f04e5aa3c37eaa160dfbc76d1b84789e3543e))
|
||||
* initial console implementation ([6a6f43a](https://github.com/antares-sql/antares/commit/6a6f43a718561e0abd2cb89048b7fe45d08736ae))
|
||||
* ipc event channel to send logs to renderer ([f12a04b](https://github.com/antares-sql/antares/commit/f12a04b0524f1172334c89afeb27675c19ff68d2))
|
||||
* open/close console on single connection ([44647f5](https://github.com/antares-sql/antares/commit/44647f5b5508965bf5a7264add89e175c725e877))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* exception on QueryEditor with null modelValue ([9bc9adb](https://github.com/antares-sql/antares/commit/9bc9adb7cff19b86a99491d968485a4cd7b47b99))
|
||||
* fields content language detection not working properly ([a91fa8f](https://github.com/antares-sql/antares/commit/a91fa8ff54bbf1f8475666efd3a268a3a4f07f0c))
|
||||
* **Linux:** ctrl+space shortcut not working ([ed3d35f](https://github.com/antares-sql/antares/commit/ed3d35f1319a1e2edcb8104f2045a71b9e9754a2))
|
||||
* unable to delete by select all in left bar search, closes [#368](https://github.com/antares-sql/antares/issues/368) ([7725faf](https://github.com/antares-sql/antares/commit/7725fafe852479720fa619ced0970f2fa0099191))
|
||||
* unable to update data on tables missing primary or unique key ([e0946f0](https://github.com/antares-sql/antares/commit/e0946f04f792d25c187ea56d4714bdacc016ada3))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* improved resize of text editor resizing console height ([3f9e6d8](https://github.com/antares-sql/antares/commit/3f9e6d85ca445eea1028effa32418eee4980f87d))
|
||||
* **UI:** improved visibility of explore bar tooltips ([f312cf5](https://github.com/antares-sql/antares/commit/f312cf5f855deddd562c26d1835f78d16499b93b))
|
||||
|
||||
### [0.5.9](https://github.com/antares-sql/antares/compare/v0.5.8...v0.5.9) (2022-07-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* ability to pin/unpin and delete connections from the "all connections" modal ([8e70570](https://github.com/antares-sql/antares/commit/8e705706aecc5c9790329e63e61a1c02fa5d0342))
|
||||
* connections sorted by last usage by default and option to pin them ([36e98e0](https://github.com/antares-sql/antares/commit/36e98e0742657e25df7768aa5b3b7cb350df5509))
|
||||
* ctrl/cmd+space to open all connections modal ([a9a4344](https://github.com/antares-sql/antares/commit/a9a4344a71cc0f8f156b839733f6ddc200a26268))
|
||||
* modal with all connections ([a703dcc](https://github.com/antares-sql/antares/commit/a703dcc53eb920117bc346a3c21f0c729c0ad96d))
|
||||
* option to disable scratchpad ([56b0a48](https://github.com/antares-sql/antares/commit/56b0a4815c6f54eef164d849f6ca25af1e142b16))
|
||||
* search form in all connections modal ([ec5ab73](https://github.com/antares-sql/antares/commit/ec5ab73b19d99e9971ae87e5f0a8d1bd1c34ef00))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* error on export schema ([cf9c7c6](https://github.com/antares-sql/antares/commit/cf9c7c600aa915cef1ec3777866badb7ab1312ee))
|
||||
* missing option for untrusted ssl connection on connections edit panel ([71a5b5c](https://github.com/antares-sql/antares/commit/71a5b5c8285fb777c43e7f6516006bfe9f52591c))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* **UI:** improved focus visibility for buttons ([d2eb31a](https://github.com/antares-sql/antares/commit/d2eb31a63d612323f8738eded1e1ce7b23554001))
|
||||
|
||||
### [0.5.8](https://github.com/antares-sql/antares/compare/v0.5.7...v0.5.8) (2022-07-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add max visible options prop ([067a6f3](https://github.com/antares-sql/antares/commit/067a6f350757c1e6b4df51f801ae832b47bd3484))
|
||||
* context shortcut to disconnect from left bar ([e97da37](https://github.com/antares-sql/antares/commit/e97da3710385690b85391938e40145a1591bc2e8))
|
||||
* **MySQL:** option to disable foreign key checks when empty a table ([902c29f](https://github.com/antares-sql/antares/commit/902c29ffa551bc3489fa1d9136ee926d135ea14f))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* connection string field doesn't appear switching to postgre when editing a connection ([6573fe6](https://github.com/antares-sql/antares/commit/6573fe69aca2b99c7a700879fb0d0930e864cbe6))
|
||||
* ctrl+a on results doesn't work properly ([5f57a9f](https://github.com/antares-sql/antares/commit/5f57a9f60d281e24e5bee4330c081fa5d8651b36))
|
||||
* double context menu on table settings rows ([91d0735](https://github.com/antares-sql/antares/commit/91d0735a5f4861bc6ad13b9285ea7a9bd7be9538))
|
||||
* editor gutter pin not working ([cfd82c8](https://github.com/antares-sql/antares/commit/cfd82c8f419952879b386187eb146847098263fe))
|
||||
* error on modals missing focusable elements ([7702ca0](https://github.com/antares-sql/antares/commit/7702ca025fcae6209ae3851d0ccd25579f93e243))
|
||||
* exception on new scheduler tab ([a45d76e](https://github.com/antares-sql/antares/commit/a45d76e8b4ecdecf53438fe174f61ea32f4e10ac))
|
||||
* focus goes outside modals with tab key navigation ([e42c424](https://github.com/antares-sql/antares/commit/e42c424a13a6901414a1a1c4e2f68cb4ddef7d59))
|
||||
* reactivity problem on BaseVirtualScroll component ([45b2eb2](https://github.com/antares-sql/antares/commit/45b2eb2934b9f7a08f379ad4d7a44b1c89585449))
|
||||
* result table cells/rows not loses focus clicking outside ([0a3a482](https://github.com/antares-sql/antares/commit/0a3a4827dd75539666fa2c827415af3bfa224543))
|
||||
* **UI:** wrong tables scrollable height after switching tabs ([8f01740](https://github.com/antares-sql/antares/commit/8f01740475ea6d5d9b5eefabdbf27099df76f2cf))
|
||||
* **Windows:** white window buttons with dark theme ([a80d227](https://github.com/antares-sql/antares/commit/a80d22740045a61fd14fd5da401c0d123d54f4de))
|
||||
* **Windows:** Windows 7 style window frame at startup ([93ce619](https://github.com/antares-sql/antares/commit/93ce619782d58cfb8fb1ecce2ca2137a61ec6181))
|
||||
|
||||
### [0.5.7](https://github.com/antares-sql/antares/compare/v0.5.4...v0.5.7) (2022-06-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* added dropdown animation ([5398964](https://github.com/antares-sql/antares/commit/539896419064db9127f6a72acdbb11af2c4aa60a))
|
||||
* dynamic app window title ([0024269](https://github.com/antares-sql/antares/commit/00242697a102f82dd0c731a3529c984fbdf83b3e))
|
||||
* hotkeys to navigate forward or backward between tabs ([d3b9e08](https://github.com/antares-sql/antares/commit/d3b9e08446708654b3c6fad565b734d93effe683))
|
||||
* hotkeys to navigate inside a table resultset ([49abd1e](https://github.com/antares-sql/antares/commit/49abd1ea7f5ec368e9a9201f8fd5b6520c4bd0a8))
|
||||
* **translation:** russian translation, closes [#266](https://github.com/antares-sql/antares/issues/266) ([9082960](https://github.com/antares-sql/antares/commit/9082960310573a6e4d14bfbe82ed2eb1489f308d))
|
||||
* **UI:** BaseSelect disabled state ([2b436d8](https://github.com/antares-sql/antares/commit/2b436d8613a1e3dff55d73adbddf5d2cd2452f27))
|
||||
* **UI:** BaseSelect in table filters ([a037d0c](https://github.com/antares-sql/antares/commit/a037d0cc0148444e8e6c5b87c79f6ba9c2a6f0fe))
|
||||
* **UI:** BaseSelect option list scrolls automatically using up/down keys ([0043d07](https://github.com/antares-sql/antares/commit/0043d077081fc49724722a5d5a74986d990c539d))
|
||||
* **UI:** BaseSelect small variant ([5582a12](https://github.com/antares-sql/antares/commit/5582a12bbfade75dbcc7f9d71ada7190ed08d3c2))
|
||||
* **UI:** BaseSelect supports disabled options ([f7e04d6](https://github.com/antares-sql/antares/commit/f7e04d633340a53420ce1c434e906c9434620e6e))
|
||||
* **UI:** BaseSelect supports option groups ([1869e6a](https://github.com/antares-sql/antares/commit/1869e6a1482daf9381d9ac2244bf0aeffa758edc))
|
||||
* **UI:** ForeignKeySelect implements BaseSelect component ([302c664](https://github.com/antares-sql/antares/commit/302c66457deeb50facf4735291640fcf48b78f66))
|
||||
* **UI:** initial BaseSelect integration ([22622df](https://github.com/antares-sql/antares/commit/22622df2cfcb71054c6f6110b7ad9d4f635553dc))
|
||||
* **UI:** new BaseSelect component ([745d551](https://github.com/antares-sql/antares/commit/745d551cc9253eae4e39e5d3406ceee051a7d6c1))
|
||||
* **UI:** select tab replace with BaseSelect component ([42bc919](https://github.com/antares-sql/antares/commit/42bc9196ffc2f64b77f9cb42136255fc74815034))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* empty query tab schema select if no schema selected ([31b7999](https://github.com/antares-sql/antares/commit/31b7999bba5d115913d42087614b9888bc761068))
|
||||
* exception on app start setting window title ([5b33419](https://github.com/antares-sql/antares/commit/5b33419b6421d7d198a978e79e22d0a76306cdb4))
|
||||
* fields sorting in table setting tabs ([77d9cac](https://github.com/antares-sql/antares/commit/77d9cac092fbb806810c3463ca066395fcab5307))
|
||||
* inline field update not working with tables missing primary key ([caf776b](https://github.com/antares-sql/antares/commit/caf776bd55606c793c9763c204aa9f05d1feb27f))
|
||||
* **Linux:** setting bar tooltip position ([6bad032](https://github.com/antares-sql/antares/commit/6bad032f0d1094736f651b6c06a60d2a0df36c98))
|
||||
* main process not closed after window close on some conditions ([23acf00](https://github.com/antares-sql/antares/commit/23acf00def77b5662e48b84591a31760737774a7))
|
||||
* **PostgreSQL:** idle timeout disabled ([a082514](https://github.com/antares-sql/antares/commit/a082514f88040c7e0ffdf4e8357bab45370a4c39))
|
||||
* query tab content disappears reordering or closing other tabs, closes [#261](https://github.com/antares-sql/antares/issues/261) ([c5baf2b](https://github.com/antares-sql/antares/commit/c5baf2b0d379fdd28ee8cb907628bbfca940e2f6))
|
||||
* reload tab content on tab sort ([d214c1f](https://github.com/antares-sql/antares/commit/d214c1f35ba231a8a01dbe8c0faad07d4b337752))
|
||||
* selected foreign key value not visible in the insert row modal ([cba2ce2](https://github.com/antares-sql/antares/commit/cba2ce2e37cedbf0b242cc474b37bf052009ae62))
|
||||
* **SQLite:** unable to insert rows with TEXT fields ([a7d5e19](https://github.com/antares-sql/antares/commit/a7d5e1973cd59d7d0ef1e74bdcf44d87fba43559))
|
||||
* SSH tunnel connection error with private key, closes [#260](https://github.com/antares-sql/antares/issues/260) ([c826888](https://github.com/antares-sql/antares/commit/c826888b0dd0908958a4f727ddfa642e846269cf))
|
||||
* **UI:** BaseSelect keyboard navigation ([7c45203](https://github.com/antares-sql/antares/commit/7c452036368fa0db6b9cde7c35e60a8e57bfece7))
|
||||
* **UI:** BaseSelect style ([71b0736](https://github.com/antares-sql/antares/commit/71b0736d0ddbd599ab41cde0a6b0823e2bb7da2f))
|
||||
* **UI:** select closes clicking on scrollbar ([8870304](https://github.com/antares-sql/antares/commit/8870304c15346257a90193807b9ae07c1393e3e2))
|
||||
* unable to add new table fields ([ee623b0](https://github.com/antares-sql/antares/commit/ee623b0a0f121df0ac53d49d8be437c76ddb8539))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* improved precision of MariaDB or MySQL auto detection ([26aad51](https://github.com/antares-sql/antares/commit/26aad519df6ea1bbc7dffbf540193a7b2ed9ae2a))
|
||||
* **Linux:** title bar improvements ([85cec05](https://github.com/antares-sql/antares/commit/85cec05f7037a1339ee223554cf127693a527aa1))
|
||||
* **UI:** max height for query text area increased ([5d5f1da](https://github.com/antares-sql/antares/commit/5d5f1da97b9adfa743197d8fa0bbb6addd565a7a))
|
||||
* **Windows:** title bar improvements ([5fa8bf3](https://github.com/antares-sql/antares/commit/5fa8bf38e433ef2fb31bcb893cd9e75549bd6a49))
|
||||
|
||||
### [0.5.6](https://github.com/antares-sql/antares/compare/v0.5.4...v0.5.6) (2022-06-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* empty query tab schema select if no schema selected ([31b7999](https://github.com/antares-sql/antares/commit/31b7999bba5d115913d42087614b9888bc761068))
|
||||
* inline field update not working with tables missing primary key ([caf776b](https://github.com/antares-sql/antares/commit/caf776bd55606c793c9763c204aa9f05d1feb27f))
|
||||
* **SQLite:** unable to insert rows with TEXT fields ([a7d5e19](https://github.com/antares-sql/antares/commit/a7d5e1973cd59d7d0ef1e74bdcf44d87fba43559))
|
||||
* **UI:** select closes clicking on scrollbar ([8870304](https://github.com/antares-sql/antares/commit/8870304c15346257a90193807b9ae07c1393e3e2))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* improved precision of MariaDB or MySQL auto detection ([26aad51](https://github.com/antares-sql/antares/commit/26aad519df6ea1bbc7dffbf540193a7b2ed9ae2a))
|
||||
|
||||
### [0.5.5](https://github.com/antares-sql/antares/compare/v0.5.4...v0.5.5) (2022-05-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* added dropdown animation ([5398964](https://github.com/antares-sql/antares/commit/539896419064db9127f6a72acdbb11af2c4aa60a))
|
||||
* **translation:** russian translation, closes [#266](https://github.com/antares-sql/antares/issues/266) ([9082960](https://github.com/antares-sql/antares/commit/9082960310573a6e4d14bfbe82ed2eb1489f308d))
|
||||
* **UI:** BaseSelect disabled state ([2b436d8](https://github.com/antares-sql/antares/commit/2b436d8613a1e3dff55d73adbddf5d2cd2452f27))
|
||||
* **UI:** BaseSelect in table filters ([a037d0c](https://github.com/antares-sql/antares/commit/a037d0cc0148444e8e6c5b87c79f6ba9c2a6f0fe))
|
||||
* **UI:** BaseSelect option list scrolls automatically using up/down keys ([0043d07](https://github.com/antares-sql/antares/commit/0043d077081fc49724722a5d5a74986d990c539d))
|
||||
* **UI:** BaseSelect small variant ([5582a12](https://github.com/antares-sql/antares/commit/5582a12bbfade75dbcc7f9d71ada7190ed08d3c2))
|
||||
* **UI:** BaseSelect supports disabled options ([f7e04d6](https://github.com/antares-sql/antares/commit/f7e04d633340a53420ce1c434e906c9434620e6e))
|
||||
* **UI:** BaseSelect supports option groups ([1869e6a](https://github.com/antares-sql/antares/commit/1869e6a1482daf9381d9ac2244bf0aeffa758edc))
|
||||
* **UI:** ForeignKeySelect implements BaseSelect component ([302c664](https://github.com/antares-sql/antares/commit/302c66457deeb50facf4735291640fcf48b78f66))
|
||||
* **UI:** initial BaseSelect integration ([22622df](https://github.com/antares-sql/antares/commit/22622df2cfcb71054c6f6110b7ad9d4f635553dc))
|
||||
* **UI:** new BaseSelect component ([745d551](https://github.com/antares-sql/antares/commit/745d551cc9253eae4e39e5d3406ceee051a7d6c1))
|
||||
* **UI:** select tab replace with BaseSelect component ([42bc919](https://github.com/antares-sql/antares/commit/42bc9196ffc2f64b77f9cb42136255fc74815034))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **PostgreSQL:** idle timeout disabled ([a082514](https://github.com/antares-sql/antares/commit/a082514f88040c7e0ffdf4e8357bab45370a4c39))
|
||||
* query tab content disappears reordering or closing other tabs, closes [#261](https://github.com/antares-sql/antares/issues/261) ([c5baf2b](https://github.com/antares-sql/antares/commit/c5baf2b0d379fdd28ee8cb907628bbfca940e2f6))
|
||||
* SSH tunnel connection error with private key, closes [#260](https://github.com/antares-sql/antares/issues/260) ([c826888](https://github.com/antares-sql/antares/commit/c826888b0dd0908958a4f727ddfa642e846269cf))
|
||||
* **UI:** BaseSelect keyboard navigation ([7c45203](https://github.com/antares-sql/antares/commit/7c452036368fa0db6b9cde7c35e60a8e57bfece7))
|
||||
* **UI:** BaseSelect style ([71b0736](https://github.com/antares-sql/antares/commit/71b0736d0ddbd599ab41cde0a6b0823e2bb7da2f))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* **UI:** max height for query text area increased ([5d5f1da](https://github.com/antares-sql/antares/commit/5d5f1da97b9adfa743197d8fa0bbb6addd565a7a))
|
||||
|
||||
### [0.5.4](https://github.com/antares-sql/antares/compare/v0.5.3...v0.5.4) (2022-05-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* app blocked by BIT fields with no default, closes [#256](https://github.com/antares-sql/antares/issues/256) ([e62f280](https://github.com/antares-sql/antares/commit/e62f280528edb0ff4550ee75038ea216e81e4f10))
|
||||
* file upload input not working ([58611bf](https://github.com/antares-sql/antares/commit/58611bf07f343e6899a7446bfcd1247b0c75fc7f))
|
||||
* SSH tunnel not working ([6d61518](https://github.com/antares-sql/antares/commit/6d6151814e5006935d493b9b83dbda1aa5b35391))
|
||||
* unable to insert auto-generated datetime fields ([ff27244](https://github.com/antares-sql/antares/commit/ff272440bdc2a7fe699e04f8809bd5af8f9529c0))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* **UI:** left alignment for numbers in result tables, closes [#249](https://github.com/antares-sql/antares/issues/249) ([e02565c](https://github.com/antares-sql/antares/commit/e02565c0d9bb63efa76a79f38e3ed3586a30ad1c))
|
||||
|
||||
### [0.5.3](https://github.com/antares-sql/antares/compare/v0.5.2...v0.5.3) (2022-05-08)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* default open schema if only one present ([0fa22fb](https://github.com/antares-sql/antares/commit/0fa22fbe72fd4e3b2cfff6fc1847347f69fafda6))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* can not use vue teleport with props as root element ([88eb9f7](https://github.com/antares-sql/antares/commit/88eb9f7ab80c407ec6517206c27cce715c41eb24))
|
||||
* importer webpack config ([f82db96](https://github.com/antares-sql/antares/commit/f82db96f34579c5f5f0c025ba82aff13047eb045))
|
||||
* importer webpack config ([b4d9821](https://github.com/antares-sql/antares/commit/b4d9821300991bf000846ae38ed18d79564db67b))
|
||||
* LIKE operator in table filter not properly working, closes [#242](https://github.com/antares-sql/antares/issues/242) ([eb1afed](https://github.com/antares-sql/antares/commit/eb1afed108d97e2acb7ea5279aabd582014c3c58))
|
||||
* locale change ([6af0c33](https://github.com/antares-sql/antares/commit/6af0c33461dfab868bca757e55ae62e6c6094c6c))
|
||||
* missing storeToRefs imports ([ff8bb45](https://github.com/antares-sql/antares/commit/ff8bb45d6d19303b2e7701639d22e52e4ae2976c))
|
||||
* missing tabs uid ([18b66b5](https://github.com/antares-sql/antares/commit/18b66b50323e69ed9d0090a4cbbe0ada9e10838d))
|
||||
* **MySQL:** creating temporary tables on export to overcome view dependency errors ([e4c5d9b](https://github.com/antares-sql/antares/commit/e4c5d9b404c2b20cf113607a80ee928b810e6f05))
|
||||
* **MySQL:** support to ANSI_QUOTES mode not working, closes [#243](https://github.com/antares-sql/antares/issues/243) ([9fcc7d1](https://github.com/antares-sql/antares/commit/9fcc7d1cefc3c035a63e843616c3138c772037f1))
|
||||
* prevents query tabs targets previous schema if not in explorebar, closes [#236](https://github.com/antares-sql/antares/issues/236) ([5e4abd3](https://github.com/antares-sql/antares/commit/5e4abd3e81d70bcb9b3a897d38d6f8ec3ff0706b))
|
||||
* query editor table fields suggestion ([e9dedfa](https://github.com/antares-sql/antares/commit/e9dedfaf3203f5621e54fd707003a47e6e5a5c4d))
|
||||
* **SQLite:** tables with sqlite in name not visible, closes [#239](https://github.com/antares-sql/antares/issues/239) ([690cdcb](https://github.com/antares-sql/antares/commit/690cdcb2eb1c7bf514271000c31211d1c0b33e54))
|
||||
* unable to add parameters to new routines/functions ([350d536](https://github.com/antares-sql/antares/commit/350d53642e2491fd2dedbbf999579ba9c9bc59a0))
|
||||
* unable to disconnect from database ([47e1f27](https://github.com/antares-sql/antares/commit/47e1f27bb83b99288157185ad8b62d190a9e7881))
|
||||
* unable to set an empty string as field default ([bfdb463](https://github.com/antares-sql/antares/commit/bfdb463390453a55963af29ef0e7cd3aea26f4cf))
|
||||
* undefined uid variable in the setting bar ([3cca3ed](https://github.com/antares-sql/antares/commit/3cca3ed2b31c5763794c52cce34ccba1e848efe2))
|
||||
* various warnings and exceptions ([c50cbc5](https://github.com/antares-sql/antares/commit/c50cbc577f667c17ba676960cf2c840d24366c80))
|
||||
* verbose devtool loggin with missing sourcemap ([1174bab](https://github.com/antares-sql/antares/commit/1174bab0cc895c4e294804d810b0789166602725))
|
||||
* wrong path module importation ([472fa6f](https://github.com/antares-sql/antares/commit/472fa6f4300b7e3fbf8c8079a548ca34d941f5fb))
|
||||
* wrong path module importation ([9a0f982](https://github.com/antares-sql/antares/commit/9a0f98272342d201e9bc979d5bc5768cfd20f3fb))
|
||||
|
||||
### [0.5.2](https://github.com/antares-sql/antares/compare/v0.5.1...v0.5.2) (2022-04-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** option to allow untrusted SSL connections ([6cc098c](https://github.com/antares-sql/antares/commit/6cc098c6f02fb52cc71c0141431ab75f12744a1c))
|
||||
* french translation updated, closes [#222](https://github.com/antares-sql/antares/issues/222) ([796f61b](https://github.com/antares-sql/antares/commit/796f61bf2feab0da515901e2137dc7bf04371d7d))
|
||||
* **PostgreSQL:** export functions and procedures ([a8ca8f2](https://github.com/antares-sql/antares/commit/a8ca8f2f76ab36c4afe84d602709386315f4b7d1))
|
||||
* **PostgreSQL:** export tables ([a67071e](https://github.com/antares-sql/antares/commit/a67071e28470bcbd0ec26780bb86f3c65750ded8))
|
||||
* **PostgreSQL:** export triggers ([42376b4](https://github.com/antares-sql/antares/commit/42376b4bc6dd8b630402d09b026d9fbc0b8646bb))
|
||||
* **PostgreSQL:** export user-defined types before tables ([bb02479](https://github.com/antares-sql/antares/commit/bb02479b71bf75a6e69e28af57c5fe213d3f30bc))
|
||||
* **PostgreSQL:** export views ([86f011f](https://github.com/antares-sql/antares/commit/86f011f34fec9d6829bce324493fea888a863ffc))
|
||||
* **PostgreSQL:** sql dump importer ([6086ca4](https://github.com/antares-sql/antares/commit/6086ca4a80b9ad6a07086446253d781f052d3abc))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **PostgreSQL:** wrong values exporting table content ([0f9c991](https://github.com/antares-sql/antares/commit/0f9c991f539560913fa0e9361a16e6448a066a27))
|
||||
* ssh tunnel not properly working, closes [#220](https://github.com/antares-sql/antares/issues/220) ([026d74c](https://github.com/antares-sql/antares/commit/026d74c8c88c605a3c8c963c211078f5b3dcfda1))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* **PostgreSQL:** improved dump file ([408dded](https://github.com/antares-sql/antares/commit/408ddeda5634ab6bf41eff760271669170b60eb6))
|
||||
* **PostgreSQL:** improved views exportation ([638a88a](https://github.com/antares-sql/antares/commit/638a88a1fb35c048ff4c6d120aaaef831c846f58))
|
||||
|
||||
### [0.5.1](https://github.com/Fabio286/antares/compare/v0.5.0...v0.5.1) (2022-03-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* export database as zip sql file ([8f3efab](https://github.com/Fabio286/antares/commit/8f3efabb6962c55c23a43c8da1433185dbc3fb41))
|
||||
* **UI:** option to disable blur effects, closes [#209](https://github.com/Fabio286/antares/issues/209) ([e9079ad](https://github.com/Fabio286/antares/commit/e9079adb25ec28e9546acd54bc2565b8d6e28120))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* numeric scale displayed on non decimal fields ([db628f7](https://github.com/Fabio286/antares/commit/db628f77226b161c3df31b7450b92a6e58754ab7))
|
||||
* **UI:** connection buttons out of screen on small displays, closes [#213](https://github.com/Fabio286/antares/issues/213) ([f12e6a9](https://github.com/Fabio286/antares/commit/f12e6a96dd66140b06c55eda775af48a666627dd))
|
||||
|
||||
## [0.5.0](https://github.com/Fabio286/antares/compare/v0.4.4...v0.5.0) (2022-03-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* delete dump file when the export is canceled ([d25c62b](https://github.com/Fabio286/antares/commit/d25c62b4da9480e040d0bfac8b76732a4c69a5f1))
|
||||
* initial db export implementation ([0de2321](https://github.com/Fabio286/antares/commit/0de232192076d1de827424c593ac9dff63903531))
|
||||
* initial mysql import support ([4e9f8d1](https://github.com/Fabio286/antares/commit/4e9f8d16ee3c204d5f0c2bed081206f8b38207a6))
|
||||
* mysql export for trigger, views, schedulers, functions and routines ([b2a5b40](https://github.com/Fabio286/antares/commit/b2a5b40c03d56bced5a7968c3454f36060e56dd0))
|
||||
* **MySQL:** enhance export characters escaping ([3be826d](https://github.com/Fabio286/antares/commit/3be826df4b02ff0df0aa922d96755b31b7155784))
|
||||
* **MySQL:** support to multi spatial fields export ([4be55f3](https://github.com/Fabio286/antares/commit/4be55f3fe9bb48324b780734762f2ff6da2ccb61))
|
||||
* **PostgreSQL:** :sparkles: Postgress connection string feature for local and server connection string ([6305752](https://github.com/Fabio286/antares/commit/6305752ad117cc29c04bce3ce3df321f743cdc44))
|
||||
* **PostgreSQL:** :sparkles: Postgress connection string feature for local and server connection string ([f4a63ea](https://github.com/Fabio286/antares/commit/f4a63eae2aca2a84647a5027137614950aef1eac))
|
||||
* **UI:** auto-refresh schema at the end of the import process ([abf2b92](https://github.com/Fabio286/antares/commit/abf2b92e6e66b6668e698c5addf4e3c00ae5157b))
|
||||
* **UI:** better real-time import stats ([a6f5645](https://github.com/Fabio286/antares/commit/a6f5645a226454cc2c415311ac321ba3d4db4454))
|
||||
* **UI:** toggle tables checkbox by column on export modal ([1c4d5b0](https://github.com/Fabio286/antares/commit/1c4d5b05b3f94b3e7bef930aa7f89bdaa596c0b9))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **MySQL:** exception exporting empty procedures/functions ([ee415da](https://github.com/Fabio286/antares/commit/ee415da127d6d0de95aac901a2a01af863736344))
|
||||
* **MySQL:** export crash with large databases ([8cf738b](https://github.com/Fabio286/antares/commit/8cf738bac85698fddd0504eef7844279e8c11f44))
|
||||
* **MySQL:** missing functions and procedures definer escapes on exporter ([f0351e5](https://github.com/Fabio286/antares/commit/f0351e5b94830f9f52256096c2601b0ca9cd811d))
|
||||
* **MySQL:** missing initial delimiter for exported procedures ([1a9fc37](https://github.com/Fabio286/antares/commit/1a9fc3728580f789727256d7893ca4bb90c16a50))
|
||||
* **MySQL:** procedures exportation ([4276586](https://github.com/Fabio286/antares/commit/4276586e1141500401ff1ab570b29e485f459987))
|
||||
* sql parser hangs during import ([7a6bd8b](https://github.com/Fabio286/antares/commit/7a6bd8bdbd69e3b5fe265d0bb0be844699dd77c2))
|
||||
* wrong schema and table size on explore bar ([4479a96](https://github.com/Fabio286/antares/commit/4479a9600b5e59ef1bcf9135d661b4d7900a4bde))
|
||||
* wrong soft sort algorithm for numeric fields, closes [#199](https://github.com/Fabio286/antares/issues/199) ([763be85](https://github.com/Fabio286/antares/commit/763be8532d2b61d0b4d45e72343f6a2e5fee1db9))
|
||||
|
||||
|
||||
### Improvements
|
||||
|
||||
* avoid to load schema elements if already loaded in export modal ([d9d3bf2](https://github.com/Fabio286/antares/commit/d9d3bf2bc9d39ce8eec5dffbecbf767fbcf47782))
|
||||
* **MySQL:** import performance improvement ([f444746](https://github.com/Fabio286/antares/commit/f444746f465ed0e8bd2e4c007faf17e167814278))
|
||||
* **MySQL:** import tasks managed with async queue ([bbe13f2](https://github.com/Fabio286/antares/commit/bbe13f27dc29f997898f8c13f36b5d582770b21d))
|
||||
* **MySQL:** improved several field types support on exporter ([1990d9a](https://github.com/Fabio286/antares/commit/1990d9a3d441f0e2075ac7e893d5b166275c48c0))
|
||||
* **MySQL:** prevent memory leak on large dump import ([f3759b6](https://github.com/Fabio286/antares/commit/f3759b65411a40d92b98208176cdf8e6dd8230ce))
|
||||
* **PostgreSQL:** :zap: Postgres connection update, better error handling and connection string accommodation. ([330a80f](https://github.com/Fabio286/antares/commit/330a80fe70b81f466f5e883029f42087b4b5c411))
|
||||
* split the export select query to avoid running out of memory ([409ed54](https://github.com/Fabio286/antares/commit/409ed54608ad402b63fcc26a6e724bc447ba89d2))
|
||||
* use fork() for the export process ([748d449](https://github.com/Fabio286/antares/commit/748d44977e76c6c8d6344df52e8e3ccfab84f670))
|
||||
* use fork() for the import process ([573ac6d](https://github.com/Fabio286/antares/commit/573ac6d42ef833f250d102e5b30ae6cf5877f330))
|
||||
|
||||
### [0.4.4](https://github.com/Fabio286/antares/compare/v0.4.3...v0.4.4) (2022-02-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* execution notification for ROLLBACK and COMMIT ([76743e8](https://github.com/Fabio286/antares/commit/76743e8f7c02b824cb21540bfbcbe66ba43de8fa))
|
||||
* **MySQL:** manual commit mode ([4ed2f9a](https://github.com/Fabio286/antares/commit/4ed2f9a93937b4293436a64318b7d6ae3a0d93c2))
|
||||
* **PostgreSQL:** manual commit mode ([d81e091](https://github.com/Fabio286/antares/commit/d81e0911ab82fb75745ab11e67e867a00d8ac273))
|
||||
* reminder for uncommitted changes closing a tab ([5bfff64](https://github.com/Fabio286/antares/commit/5bfff649e92f6fe5aba4b16aa4c8d5a5a70b31b2))
|
||||
* **SQLite:** manual commit mode ([7dcd444](https://github.com/Fabio286/antares/commit/7dcd4441c49fafc0f47e12c2129708fe1092e1a4))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* bigint support, closes [#197](https://github.com/Fabio286/antares/issues/197) ([b703955](https://github.com/Fabio286/antares/commit/b7039553ccaac4fd59e521530c4a053922854130))
|
||||
* **MySQL:** default value not displayed for DECIMAL fields ([fa3f3e1](https://github.com/Fabio286/antares/commit/fa3f3e1fd8101f19f18df71e90d60fd37cdddaee))
|
||||
* zero-padded bit fields beyond length ([265f28b](https://github.com/Fabio286/antares/commit/265f28b4d94cde4608a1d6d3d306824134808ec2))
|
||||
|
||||
### [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)
|
||||
|
||||
|
||||
|
133
CODE_OF_CONDUCT.md
Normal file
133
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,133 @@
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
fabio286@gmail.com.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
@@ -2,7 +2,7 @@
|
||||
|
||||
Antares SQL is an application based on [Electron.js](https://www.electronjs.org/) that uses [Vue.js](https://vuejs.org/) and [Spectre.css](https://picturepan2.github.io/spectre/) as frontend frameworks.
|
||||
For the build process it takes advantage of [electron-builder](https://www.electron.build/).
|
||||
This application uses [Vuex](https://vuex.vuejs.org/) as application state manager and [electron-store](https://github.com/sindresorhus/electron-store) to save the various settings on disc.
|
||||
This application uses [Pinia🍍](https://pinia.vuejs.org/) as application state manager and [electron-store](https://github.com/sindresorhus/electron-store) to save the various settings on disc.
|
||||
This guide aims to provide useful information and guidelines to everyone wants to contribute with this open-source project.
|
||||
For every other question related to this project please [contact me](https://github.com/Fabio286).
|
||||
|
||||
@@ -14,7 +14,7 @@ The main files of the application are located inside `src` folder and are groupp
|
||||
|
||||
This folder contains small libraries, classes and objects. The purpose of `common` folder is to group together utilities used by **renderer** and **main** processes.
|
||||
Noteworthy is the `customizations` folder that contains clients related customizations. Those settings are merged with `default.js` that lists every option.
|
||||
Client related customizations are stored on Vuex and can be accessed by `customizations` property of current workspace object, or importing `common/customizations`.
|
||||
Client related customizations are stored on Pinia and can be accessed by `customizations` property of current workspace object, or importing `common/customizations`.
|
||||
|
||||
An use case of customizations object can be the following:
|
||||
|
||||
@@ -44,8 +44,7 @@ In this folder is located the structure of Vue frontend application.
|
||||
|
||||
## Build
|
||||
|
||||
The command to build Antares SQL locally is `npm run build:local`.
|
||||
`build` command (without `:local`) is used exclusively by the GitHub Action.
|
||||
The command to build Antares SQL locally is `npm run build`.
|
||||
|
||||
## Conventions
|
||||
|
||||
@@ -62,12 +61,6 @@ The command to build Antares SQL locally is `npm run build:local`.
|
||||
- [Order of words in component names](https://vuejs.org/v2/style-guide/#Order-of-words-in-component-names-strongly-recommended).
|
||||
- **kebab-case** in templates for property and event names.
|
||||
|
||||
### Vuex
|
||||
|
||||
- **snake_case** for state names.
|
||||
- **camelCase** for getter and action names.
|
||||
- **SNAKE_CASE (all caps)** for mutation names.
|
||||
|
||||
### Code Style
|
||||
|
||||
The project includes [ESlint](https://eslint.org/) and [StyleLint](https://stylelint.io/) config files with style rules. I recommend to set the lint on-save option in your code editor.
|
||||
|
75
README.md
75
README.md
@@ -12,13 +12,14 @@
|
||||
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, 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.
|
||||
**At the moment this application is in development state, many features will come in future updates**, and supports only MySQL/MariaDB, PostgreSQL, SQLite and Firebird SQL.
|
||||
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.
|
||||
|
||||
🔗 If you are curious to try Antares you can download and install the [latest release](https://github.com/Fabio286/antares/releases/latest).
|
||||
👁 To stay tuned for new releases [follow Antares SQL](https://twitter.com/AntaresSQL) on Twitter.
|
||||
🌟 Don't forget to **leave a star** if you appreciate this project.
|
||||
🌟 Don't forget to **leave a star** if you appreciate this project.
|
||||
🗳️ Poll: **[Which is the main OS you use Antares on?](https://github.com/antares-sql/antares/discussions/379)**
|
||||
|
||||
## Current key features
|
||||
|
||||
@@ -31,29 +32,30 @@ 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.
|
||||
- Import and export database dumps.
|
||||
- Customizable keyboard shortcuts.
|
||||
- Dark and light theme.
|
||||
- Editor themes.
|
||||
- Scratchpad.
|
||||
- Secure password storage.
|
||||
|
||||
## Philosophy
|
||||
|
||||
Why are we developing an SQL client when there are a lot of them on the market?
|
||||
The main goal is to develop a totally free, full featured, cross platform and open source alternative, empowered by JavaScript's ecosystem.
|
||||
A modern application created with minimalism and semplicity in mind, with features in the right places, not hundreds of tiny buttons, nested tabs or submenu; productivity comes first.
|
||||
The main goal is to develop a **forever 100% free (without paid premium feature)**, full featured, as possible community driven, cross platform and open source alternative, empowered by JavaScript ecosystem.
|
||||
A modern application created with minimalism and simplicity in mind, with features in the right places, not hundreds of tiny buttons, nested tabs or submenues; productivity comes first.
|
||||
|
||||
## Installation
|
||||
|
||||
Based on your operating system you can have one or more distribution formats to choose based on your preferences.
|
||||
Since Antares SQL is a free software we haven't a budget to spend in annual licenses or certificates. This can result that on some platforms you need some additional passages to install this app.
|
||||
Since Antares SQL is a free software we don't have a budget to spend on annual licenses or certificates. This can result that on some platforms you might need to put in some additional work to install this app.
|
||||
|
||||
### Linux
|
||||
|
||||
On Linux you can simply download and run `.AppImage` distributions, install from Snap Store or from AUR.
|
||||
On Linux you can simply download and run the `.AppImage` distribution, install from Snap Store, from AUR or from our [PPA repository](https://github.com/antares-sql/antares-ppa).
|
||||
|
||||
### Windows
|
||||
|
||||
On Windows you can choose between Microsoft Store and download `.exe` distribution. The latter lacks of a certificate, so to install you need to click on "More info" and then "Run anyway" on SmartScreen prompt.
|
||||
On Windows you can choose between downloading the app from Microsoft Store or downloading the `.exe` from our [website](https://antares-sql.app/downloads) or [this github repo](https://github.com/Fabio286/antares/releases/latest). Distributions that are not from Microsoft Store are not signed with a certificate, so to install you need to click on "More info" and then "Run anyway" on SmartScreen prompt.
|
||||
|
||||
### MacOS
|
||||
|
||||
@@ -61,7 +63,7 @@ On macOS you can run `.dmg` distribution following [this guide](https://support.
|
||||
|
||||
## Download
|
||||
|
||||
[](https://snapcraft.io/antares) [](https://aur.archlinux.org/packages/antares-sql/) [](https://www.microsoft.com/p/antares-sql-client/9nhtb9sq51r1?cid=storebadge&ocid=badge&rtc=1&activetab=pivot:overviewtab)
|
||||
[](https://snapcraft.io/antares) [](https://aur.archlinux.org/packages/antares-sql/) [<img src="https://developer.microsoft.com/store/badges/images/English_get-it-from-MS.png" style="height: 56px">](https://www.microsoft.com/p/antares-sql-client/9nhtb9sq51r1?cid=storebadge&ocid=badge&rtc=1&activetab=pivot:overviewtab)
|
||||
🚀 **[Other Downloads](https://github.com/Fabio286/antares/releases/latest)**
|
||||
|
||||
## Coming soon
|
||||
@@ -72,7 +74,6 @@ This is a roadmap with major features will come in near future.
|
||||
- Users management (add/edit/delete).
|
||||
- More context menu shortcuts.
|
||||
- More keyboard shortcuts.
|
||||
- Import/export and migration.
|
||||
- Support for other databases.
|
||||
- Apple Silicon distribution
|
||||
|
||||
@@ -83,8 +84,8 @@ This is a roadmap with major features will come in near future.
|
||||
- [x] MySQL/MariaDB
|
||||
- [x] PostgreSQL
|
||||
- [x] SQLite
|
||||
- [ ] MSSQL
|
||||
- [ ] OracleDB
|
||||
- [x] Firebird SQL
|
||||
- [ ] SQL Server
|
||||
- [ ] More...
|
||||
|
||||
### Operating Systems
|
||||
@@ -105,7 +106,7 @@ This is a roadmap with major features will come in near future.
|
||||
|
||||
- 🌍 [Translate Antares](https://github.com/Fabio286/antares/wiki/Translate-Antares)
|
||||
- 📖 [Contributors Guide](https://github.com/Fabio286/antares/wiki/Contributors-Guide)
|
||||
- 🚧 [Project Board](https://github.com/users/Fabio286/projects/1)
|
||||
- 🚧 [Project Board](https://github.com/antares-sql/antares/projects/1)
|
||||
|
||||
## Contributors ✨
|
||||
|
||||
@@ -115,22 +116,34 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- 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://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>
|
||||
<td align="center"><a href="https://github.com/MrAnyx"><img src="https://avatars.githubusercontent.com/u/44176707?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Robin</b></sub></a><br /><a href="#translation-MrAnyx" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/daeleduardo"><img src="https://avatars.githubusercontent.com/u/8599078?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Eduardo</b></sub></a><br /><a href="#translation-daeleduardo" title="Translation">🌍</a></td>
|
||||
<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>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center"><a href="https://fabiodistasio.it/"><img src="https://avatars.githubusercontent.com/u/31471771?v=4?s=100" width="100px;" alt="Fabio Di Stasio"/><br /><sub><b>Fabio Di Stasio</b></sub></a><br /><a href="https://github.com/antares-sql/antares/commits?author=Fabio286" title="Code">💻</a> <a href="#translation-Fabio286" title="Translation">🌍</a> <a href="https://github.com/antares-sql/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="Giulio Ganci"/><br /><sub><b>Giulio Ganci</b></sub></a><br /><a href="https://github.com/antares-sql/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="Christian Ratz"/><br /><sub><b>Christian Ratz</b></sub></a><br /><a href="https://github.com/antares-sql/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="Giuseppe Gigliotti"/><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="Mohd-PH"/><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="hongkfui"/><br /><sub><b>hongkfui</b></sub></a><br /><a href="#translation-hongkfui" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/MrAnyx"><img src="https://avatars.githubusercontent.com/u/44176707?v=4?s=100" width="100px;" alt="Robin"/><br /><sub><b>Robin</b></sub></a><br /><a href="#translation-MrAnyx" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/daeleduardo"><img src="https://avatars.githubusercontent.com/u/8599078?v=4?s=100" width="100px;" alt="Daniel Eduardo"/><br /><sub><b>Daniel Eduardo</b></sub></a><br /><a href="#translation-daeleduardo" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://ngoquocdat.com/"><img src="https://avatars.githubusercontent.com/u/56961917?v=4?s=100" width="100px;" alt="Ngô Quốc Đạt"/><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="Isamu Sugiura"/><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="Riccardo Sacchetto"/><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="Kilian Stallinger"/><br /><sub><b>Kilian Stallinger</b></sub></a><br /><a href="https://github.com/antares-sql/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/antares-sql/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="goYou"/><br /><sub><b>goYou</b></sub></a><br /><a href="#translation-goYou" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/raliqala"><img src="https://avatars.githubusercontent.com/u/30502407?v=4?s=100" width="100px;" alt="Topollo"/><br /><sub><b>Topollo</b></sub></a><br /><a href="https://github.com/antares-sql/antares/commits?author=raliqala" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/SmileYzn"><img src="https://avatars.githubusercontent.com/u/5851851?v=4?s=100" width="100px;" alt="Cleverson"/><br /><sub><b>Cleverson</b></sub></a><br /><a href="#translation-SmileYzn" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/fredatgithub"><img src="https://avatars.githubusercontent.com/u/6720055?v=4?s=100" width="100px;" alt="fred"/><br /><sub><b>fred</b></sub></a><br /><a href="#translation-fredatgithub" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/xak666"><img src="https://avatars.githubusercontent.com/u/38811437?v=4?s=100" width="100px;" alt="xaka_xak"/><br /><sub><b>xaka_xak</b></sub></a><br /><a href="#translation-xak666" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://codepen.io/theo-billardey"><img src="https://avatars.githubusercontent.com/u/48206778?v=4?s=100" width="100px;" alt="Théo Billardey"/><br /><sub><b>Théo Billardey</b></sub></a><br /><a href="#translation-brdtheo" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="http://yaskur.net"><img src="https://avatars.githubusercontent.com/u/9539970?v=4?s=100" width="100px;" alt="Muhammad Dyas Yaskur"/><br /><sub><b>Muhammad Dyas Yaskur</b></sub></a><br /><a href="#translation-dyaskur" title="Translation">🌍</a> <a href="https://github.com/antares-sql/antares/commits?author=dyaskur" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-restore -->
|
||||
|
BIN
assets/appx/Square44x44Logo.targetsize-256_altform-unplated.png
Normal file
BIN
assets/appx/Square44x44Logo.targetsize-256_altform-unplated.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
BIN
assets/icon.icns
BIN
assets/icon.icns
Binary file not shown.
BIN
assets/linux/128x128.png
Normal file
BIN
assets/linux/128x128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
assets/linux/16x16.png
Normal file
BIN
assets/linux/16x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 889 B |
BIN
assets/linux/256x256.png
Normal file
BIN
assets/linux/256x256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
BIN
assets/linux/32x32.png
Normal file
BIN
assets/linux/32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
BIN
assets/linux/64x64.png
Normal file
BIN
assets/linux/64x64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
0
misc/.gitkeep
Normal file
0
misc/.gitkeep
Normal file
27986
package-lock.json
generated
Normal file
27986
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
180
package.json
180
package.json
@@ -1,31 +1,36 @@
|
||||
{
|
||||
"name": "antares",
|
||||
"productName": "Antares",
|
||||
"version": "0.4.1",
|
||||
"version": "0.7.1",
|
||||
"description": "A modern, fast and productivity driven SQL client with a focus in UX.",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/Fabio286/antares.git",
|
||||
"repository": "https://github.com/antares-sql/antares.git",
|
||||
"scripts": {
|
||||
"debug": "npm run rebuild:electron && npm run debug-runner",
|
||||
"debug-runner": "node scripts/devRunner.js --remote-debug",
|
||||
"compile": "npm run compile:main && npm run compile:renderer",
|
||||
"compile": "npm run compile:main && npm run compile:workers && npm run compile:renderer",
|
||||
"compile:main": "webpack --mode=production --config webpack.main.config.js",
|
||||
"compile:workers": "webpack --mode=production --config webpack.workers.config.js",
|
||||
"compile:renderer": "webpack --mode=production --config webpack.renderer.config.js",
|
||||
"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",
|
||||
"build": "cross-env NODE_ENV=production npm run compile && electron-builder --publish never",
|
||||
"build:appx": "npm run build -- --win appx",
|
||||
"rebuild:electron": "rimraf ./dist && npm run postinstall && npm run devtools:install",
|
||||
"release": "standard-version",
|
||||
"release:pre": "npm run release -- --prerelease alpha",
|
||||
"devtools:install": "node scripts/devtoolsInstaller",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"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",
|
||||
"test:e2e": "npm run compile && npm run test:e2e-dry",
|
||||
"test:e2e-dry": "xvfb-maybe -- playwright test",
|
||||
"lint": "eslint . --ext .js,.ts,.vue && stylelint \"./src/**/*.{css,scss,sass,vue}\"",
|
||||
"lint:fix": "eslint . --ext .js,.ts,.vue --fix && stylelint \"./src/**/*.{css,scss,sass,vue}\" --fix",
|
||||
"contributors:add": "all-contributors add",
|
||||
"contributors:generate": "all-contributors generate"
|
||||
},
|
||||
"author": "Fabio Di Stasio <fabio286@gmail.com>",
|
||||
"main": "./dist/main.js",
|
||||
"antares": {
|
||||
"devtoolsId": "nhdogjmejiglipccpnnnanhbledajbpd"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.fabio286.antares",
|
||||
"artifactName": "${productName}-${version}-${os}_${arch}.${ext}",
|
||||
@@ -59,7 +64,11 @@
|
||||
"target": [
|
||||
{
|
||||
"target": "deb",
|
||||
"arch": "x64"
|
||||
"arch": [
|
||||
"x64",
|
||||
"armv7l",
|
||||
"arm64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "AppImage",
|
||||
@@ -70,12 +79,19 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"icon": "assets/linux",
|
||||
"category": "Development"
|
||||
},
|
||||
"appImage": {
|
||||
"license": "./LICENSE",
|
||||
"category": "Development"
|
||||
},
|
||||
"nsis": {
|
||||
"license": "./LICENSE",
|
||||
"installerIcon": "assets/icon.ico",
|
||||
"uninstallerIcon": "assets/icon.ico",
|
||||
"installerHeader": "assets/icon.ico"
|
||||
},
|
||||
"portable": {
|
||||
"artifactName": "${productName}-${version}-portable.exe"
|
||||
},
|
||||
@@ -103,66 +119,90 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/remote": "^2.0.1",
|
||||
"@mdi/font": "^6.1.95",
|
||||
"@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",
|
||||
"faker": "^5.5.3",
|
||||
"marked": "^4.0.0",
|
||||
"moment": "^2.29.1",
|
||||
"mysql2": "^2.3.2",
|
||||
"pg": "^8.7.1",
|
||||
"pgsql-ast-parser": "^7.2.1",
|
||||
"source-map-support": "^0.5.20",
|
||||
"spectre.css": "^0.5.9",
|
||||
"sql-formatter": "^4.0.2",
|
||||
"ssh2-promise": "^1.0.2",
|
||||
"v-mask": "^2.3.0",
|
||||
"vue-i18n": "^8.26.5",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"vuex": "^3.6.2"
|
||||
"@electron/remote": "~2.0.1",
|
||||
"@faker-js/faker": "~6.1.2",
|
||||
"@mdi/font": "~7.0.96",
|
||||
"@turf/helpers": "~6.5.0",
|
||||
"@vueuse/core": "~8.7.5",
|
||||
"ace-builds": "~1.14.0",
|
||||
"better-sqlite3": "~8.0.0",
|
||||
"electron-log": "~4.4.1",
|
||||
"electron-store": "~8.0.1",
|
||||
"electron-updater": "~4.6.5",
|
||||
"electron-window-state": "~5.0.3",
|
||||
"encoding": "~0.1.13",
|
||||
"floating-vue": "~2.0.0-beta.20",
|
||||
"leaflet": "~1.7.1",
|
||||
"marked": "~4.0.19",
|
||||
"moment": "~2.29.4",
|
||||
"mysql2": "~2.3.2",
|
||||
"node-firebird": "~1.1.4",
|
||||
"pg": "~8.7.1",
|
||||
"pg-connection-string": "~2.5.0",
|
||||
"pg-query-stream": "~4.2.3",
|
||||
"pgsql-ast-parser": "~7.2.1",
|
||||
"pinia": "~2.0.23",
|
||||
"source-map-support": "~0.5.20",
|
||||
"spectre.css": "~0.5.9",
|
||||
"sql-formatter": "~12.0.3",
|
||||
"ssh2-promise": "~1.0.2",
|
||||
"v-mask": "~2.3.0",
|
||||
"vue": "~3.2.45",
|
||||
"vue-i18n": "~9.2.2",
|
||||
"vuedraggable": "~4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.15.7",
|
||||
"@babel/preset-env": "^7.15.8",
|
||||
"all-contributors-cli": "^6.20.0",
|
||||
"babel-loader": "^8.2.3",
|
||||
"chalk": "^4.1.2",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"css-loader": "^6.5.0",
|
||||
"electron": "^16.0.1",
|
||||
"electron-builder": "^22.14.10",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"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": "^8.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"mini-css-extract-plugin": "^2.4.3",
|
||||
"node-loader": "^2.0.0",
|
||||
"playwright": "^1.16.3",
|
||||
"progress-webpack-plugin": "^1.0.12",
|
||||
"sass": "^1.42.1",
|
||||
"sass-loader": "^12.3.0",
|
||||
"standard-version": "^9.3.1",
|
||||
"style-loader": "^3.3.1",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
"stylelint-scss": "^3.21.0",
|
||||
"tree-kill": "^1.2.2",
|
||||
"vue": "^2.6.14",
|
||||
"vue-loader": "^15.9.8",
|
||||
"vue-template-compiler": "^2.6.14",
|
||||
"webpack": "^5.60.0",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"webpack-dev-server": "^4.4.0"
|
||||
"@babel/eslint-parser": "~7.15.7",
|
||||
"@babel/preset-env": "~7.15.8",
|
||||
"@babel/preset-typescript": "~7.16.7",
|
||||
"@playwright/test": "~1.28.1",
|
||||
"@types/better-sqlite3": "~7.5.0",
|
||||
"@types/leaflet": "~1.7.9",
|
||||
"@types/marked": "~4.0.7",
|
||||
"@types/node": "~17.0.23",
|
||||
"@types/pg": "~8.6.5",
|
||||
"@typescript-eslint/eslint-plugin": "~5.18.0",
|
||||
"@typescript-eslint/parser": "~5.18.0",
|
||||
"@vue/compiler-sfc": "~3.2.33",
|
||||
"all-contributors-cli": "~6.20.0",
|
||||
"babel-loader": "~8.2.3",
|
||||
"chalk": "~4.1.2",
|
||||
"cross-env": "~7.0.2",
|
||||
"css-loader": "~6.5.0",
|
||||
"electron": "~22.0.0",
|
||||
"electron-builder": "~22.10.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.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.5",
|
||||
"node-loader": "~2.0.0",
|
||||
"playwright": "~1.28.1",
|
||||
"playwright-core": "~1.28.1",
|
||||
"postcss-html": "~1.5.0",
|
||||
"progress-webpack-plugin": "~1.0.12",
|
||||
"rimraf": "~3.0.2",
|
||||
"sass": "~1.42.1",
|
||||
"sass-loader": "~12.3.0",
|
||||
"standard-version": "~9.3.1",
|
||||
"style-loader": "~3.3.1",
|
||||
"stylelint": "~14.9.1",
|
||||
"stylelint-config-recommended-vue": "~1.4.0",
|
||||
"stylelint-config-standard": "~26.0.0",
|
||||
"stylelint-scss": "~4.3.0",
|
||||
"tree-kill": "~1.2.2",
|
||||
"ts-loader": "~9.2.8",
|
||||
"typescript": "~4.6.3",
|
||||
"unzip-crx-3": "~0.2.0",
|
||||
"vue-eslint-parser": "~8.3.0",
|
||||
"vue-loader": "~16.8.3",
|
||||
"webpack": "~5.72.0",
|
||||
"webpack-cli": "~4.9.1",
|
||||
"webpack-dev-server": "~4.11.1",
|
||||
"xvfb-maybe": "~0.2.1"
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
process.env.NODE_ENV = 'development';
|
||||
// process.env.ELECTRON_ENABLE_LOGGING = true
|
||||
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = false;
|
||||
|
||||
const chalk = require('chalk');
|
||||
const electron = require('electron');
|
||||
@@ -12,14 +13,14 @@ const { spawn } = require('child_process');
|
||||
|
||||
const mainConfig = require('../webpack.main.config');
|
||||
const rendererConfig = require('../webpack.renderer.config');
|
||||
// const workersConfig = require('../webpack.workers.config');
|
||||
const workersConfig = require('../webpack.workers.config');
|
||||
|
||||
let electronProcess = null;
|
||||
let manualRestart = null;
|
||||
const remoteDebugging = process.argv.includes('--remote-debug');
|
||||
|
||||
if (remoteDebugging) {
|
||||
// disable dvtools open in electron
|
||||
// disable devtools open in electron
|
||||
process.env.RENDERER_REMOTE_DEBUGGING = true;
|
||||
}
|
||||
|
||||
@@ -58,13 +59,13 @@ async function restartElectron () {
|
||||
console.error(chalk.red(data.toString()));
|
||||
});
|
||||
|
||||
electronProcess.on('exit', (code, signal) => {
|
||||
electronProcess.on('exit', () => {
|
||||
if (!manualRestart) process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
function startMain () {
|
||||
const webpackSetup = webpack(mainConfig);
|
||||
const webpackSetup = webpack([mainConfig, workersConfig]);
|
||||
|
||||
webpackSetup.compilers.forEach((compiler) => {
|
||||
const { name } = compiler;
|
||||
@@ -113,7 +114,6 @@ function startRenderer (callback) {
|
||||
});
|
||||
|
||||
const server = new WebpackDevServer(compiler, {
|
||||
hot: true,
|
||||
port: 9080,
|
||||
client: {
|
||||
overlay: true,
|
||||
|
49
scripts/devtoolsInstaller.js
Normal file
49
scripts/devtoolsInstaller.js
Normal file
@@ -0,0 +1,49 @@
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-nocheck
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
const unzip = require('unzip-crx-3');
|
||||
const { antares } = require('../package.json');
|
||||
|
||||
const extensionID = antares.devtoolsId;
|
||||
const destFolder = path.resolve(__dirname, `../misc/${extensionID}`);
|
||||
const filePath = path.resolve(__dirname, `${destFolder}${extensionID}.crx`);
|
||||
const fileUrl = `https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D${extensionID}%26uc&prodversion=32`;
|
||||
const fileStream = fs.createWriteStream(filePath);
|
||||
|
||||
const downloadFile = url => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = https.get(url);
|
||||
|
||||
request.on('response', response => {
|
||||
if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
||||
return downloadFile(response.headers.location)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
}
|
||||
|
||||
response.pipe(fileStream);
|
||||
|
||||
response.on('close', () => {
|
||||
console.log('Devtools download completed!');
|
||||
resolve();
|
||||
});
|
||||
response.on('error', reject);
|
||||
});
|
||||
request.on('error', reject);
|
||||
request.end();
|
||||
});
|
||||
};
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await downloadFile(fileUrl);
|
||||
await unzip(filePath, destFolder);
|
||||
fs.unlinkSync(filePath);
|
||||
fs.unlinkSync(`${destFolder}/package.json`);// <- Avoid to display annoyng npm script in vscode
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
})();
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export default class {
|
||||
static get _methods () {
|
||||
return [
|
||||
@@ -139,7 +140,7 @@ export default class {
|
||||
{ name: 'arrayElement', group: 'random', types: ['string'] },
|
||||
{ name: 'arrayElements', group: 'random', types: ['string'] },
|
||||
{ name: 'objectElement', group: 'random', types: ['string'] },
|
||||
{ name: 'uuid', group: 'random', types: ['string'] },
|
||||
{ name: 'uuid', group: 'random', types: ['string', 'uuid'] },
|
||||
{ name: 'boolean', group: 'random', types: ['string'] },
|
||||
{ name: 'word', group: 'random', types: ['string'] },
|
||||
{ name: 'words', group: 'random', types: ['string'] },
|
||||
@@ -180,7 +181,7 @@ export default class {
|
||||
acc[curr.group] = new Set(curr.types);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}, {} as any);
|
||||
|
||||
const groupsArr = [];
|
||||
|
||||
@@ -195,15 +196,15 @@ export default class {
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}); ;
|
||||
});
|
||||
}
|
||||
|
||||
static getGroupsByType (type) {
|
||||
static getGroupsByType (type: string) {
|
||||
if (!type) return [];
|
||||
return this.getGroups().filter(group => group.types.includes(type));
|
||||
}
|
||||
|
||||
static getMethods ({ type, group }) {
|
||||
static getMethods ({ type, group }: {type: string; group: string}) {
|
||||
return this._methods.filter(method => method.group === group && method.types.includes(type)).sort((a, b) => {
|
||||
if (a.name < b.name)
|
||||
return -1;
|
@@ -1,8 +1,14 @@
|
||||
module.exports = {
|
||||
import { Customizations } from '../interfaces/customizations';
|
||||
|
||||
// Everything OFF
|
||||
export const defaults: Customizations = {
|
||||
// Defaults
|
||||
defaultPort: null,
|
||||
defaultUser: null,
|
||||
defaultDatabase: null,
|
||||
dataTypes: [],
|
||||
indexTypes: [],
|
||||
foreignActions: [],
|
||||
// Core
|
||||
database: false,
|
||||
collations: false,
|
||||
@@ -11,6 +17,7 @@ module.exports = {
|
||||
sslConnection: false,
|
||||
sshConnection: false,
|
||||
fileConnection: false,
|
||||
cancelQueries: false,
|
||||
// Tools
|
||||
processesList: false,
|
||||
usersManagement: false,
|
||||
@@ -28,6 +35,7 @@ module.exports = {
|
||||
elementsWrapper: '',
|
||||
stringsWrapper: '"',
|
||||
tableAdd: false,
|
||||
tableTruncateDisableFKCheck: false,
|
||||
viewAdd: false,
|
||||
triggerAdd: false,
|
||||
triggerFunctionAdd: false,
|
||||
@@ -37,10 +45,13 @@ module.exports = {
|
||||
databaseEdit: false,
|
||||
schemaEdit: false,
|
||||
schemaDrop: false,
|
||||
schemaExport: false,
|
||||
exportByChunks: false,
|
||||
schemaImport: false,
|
||||
tableSettings: false,
|
||||
tableOptions: false,
|
||||
tableArray: false,
|
||||
tableRealCount: false,
|
||||
tableDuplicate: false,
|
||||
viewSettings: false,
|
||||
triggerSettings: false,
|
||||
triggerFunctionSettings: false,
|
||||
@@ -64,24 +75,25 @@ module.exports = {
|
||||
viewUpdateOption: false,
|
||||
procedureDeterministic: false,
|
||||
procedureDataAccess: false,
|
||||
procedureSql: false,
|
||||
procedureSql: null,
|
||||
procedureContext: false,
|
||||
procedureContextValues: [],
|
||||
procedureLanguage: false,
|
||||
functionDeterministic: false,
|
||||
functionDataAccess: false,
|
||||
functionSql: false,
|
||||
functionSql: null,
|
||||
functionContext: false,
|
||||
functionLanguage: false,
|
||||
triggerSql: false,
|
||||
triggerSql: null,
|
||||
triggerStatementInCreation: false,
|
||||
triggerMultipleEvents: false,
|
||||
triggerTableInName: false,
|
||||
triggerUpdateColumns: false,
|
||||
triggerOnlyRename: false,
|
||||
triggerEnableDisable: false,
|
||||
triggerFunctionSql: false,
|
||||
triggerFunctionlanguages: false,
|
||||
triggerFunctionSql: null,
|
||||
triggerFunctionlanguages: null,
|
||||
parametersLength: false,
|
||||
languages: false,
|
||||
languages: null,
|
||||
readOnlyMode: false
|
||||
};
|
63
src/common/customizations/firebird.ts
Normal file
63
src/common/customizations/firebird.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Customizations } from '../interfaces/customizations';
|
||||
import { defaults } from './defaults';
|
||||
import firebirdTypes from '../data-types/firebird';
|
||||
|
||||
export const customizations: Customizations = {
|
||||
...defaults,
|
||||
// Defaults
|
||||
defaultPort: 3050,
|
||||
defaultUser: 'SYSDBA',
|
||||
defaultDatabase: null,
|
||||
dataTypes: firebirdTypes,
|
||||
indexTypes: [
|
||||
'PRIMARY',
|
||||
// 'CHECK',
|
||||
'UNIQUE'
|
||||
],
|
||||
foreignActions: [
|
||||
'RESTRICT',
|
||||
'NO ACTION',
|
||||
'CASCADE',
|
||||
'SET NULL',
|
||||
'SET DEFAULT'
|
||||
],
|
||||
// Core
|
||||
database: true,
|
||||
collations: false,
|
||||
engines: false,
|
||||
connectionSchema: false,
|
||||
sslConnection: false,
|
||||
sshConnection: false,
|
||||
fileConnection: false,
|
||||
cancelQueries: false,
|
||||
// Tools
|
||||
processesList: false,
|
||||
usersManagement: false,
|
||||
variables: false,
|
||||
// Structure
|
||||
schemas: false,
|
||||
tables: true,
|
||||
views: true,
|
||||
triggers: true,
|
||||
routines: true,
|
||||
functions: false,
|
||||
// Settings
|
||||
elementsWrapper: '"',
|
||||
stringsWrapper: '\'',
|
||||
tableAdd: true,
|
||||
tableSettings: true,
|
||||
tableRealCount: true,
|
||||
viewAdd: true,
|
||||
viewSettings: true,
|
||||
triggerAdd: true,
|
||||
triggerMultipleEvents: true,
|
||||
triggerSql: 'BEGIN\r\n\r\nEND',
|
||||
routineAdd: true,
|
||||
procedureContext: true,
|
||||
procedureContextValues: ['IN', 'OUT'],
|
||||
procedureSql: 'BEGIN\r\n\r\nEND',
|
||||
parametersLength: true,
|
||||
indexes: true,
|
||||
foreigns: true,
|
||||
nullable: true
|
||||
};
|
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
maria: require('./mysql'),
|
||||
mysql: require('./mysql'),
|
||||
pg: require('./postgresql'),
|
||||
sqlite: require('./sqlite')
|
||||
};
|
19
src/common/customizations/index.ts
Normal file
19
src/common/customizations/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import * as mysql from 'common/customizations/mysql';
|
||||
import * as postgresql from 'common/customizations/postgresql';
|
||||
import * as sqlite from 'common/customizations/sqlite';
|
||||
import * as firebird from 'common/customizations/firebird';
|
||||
import { Customizations } from 'common/interfaces/customizations';
|
||||
|
||||
export default {
|
||||
maria: mysql.customizations,
|
||||
mysql: mysql.customizations,
|
||||
pg: postgresql.customizations,
|
||||
sqlite: sqlite.customizations,
|
||||
firebird: firebird.customizations
|
||||
} as {
|
||||
maria: Customizations;
|
||||
mysql: Customizations;
|
||||
pg: Customizations;
|
||||
sqlite: Customizations;
|
||||
firebird: Customizations;
|
||||
};
|
@@ -1,17 +1,33 @@
|
||||
const defaults = require('./defaults');
|
||||
import { Customizations } from '../interfaces/customizations';
|
||||
import { defaults } from './defaults';
|
||||
import mysqlTypes from '../data-types/mysql';
|
||||
|
||||
module.exports = {
|
||||
export const customizations: Customizations = {
|
||||
...defaults,
|
||||
// Defaults
|
||||
defaultPort: 3306,
|
||||
defaultUser: 'root',
|
||||
defaultDatabase: null,
|
||||
dataTypes: mysqlTypes,
|
||||
indexTypes: [
|
||||
'PRIMARY',
|
||||
'INDEX',
|
||||
'UNIQUE',
|
||||
'FULLTEXT'
|
||||
],
|
||||
foreignActions: [
|
||||
'RESTRICT',
|
||||
'CASCADE',
|
||||
'SET NULL',
|
||||
'NO ACTION'
|
||||
],
|
||||
// Core
|
||||
connectionSchema: true,
|
||||
collations: true,
|
||||
engines: true,
|
||||
sslConnection: true,
|
||||
sshConnection: true,
|
||||
cancelQueries: true,
|
||||
// Tools
|
||||
processesList: true,
|
||||
// Structure
|
||||
@@ -23,9 +39,11 @@ module.exports = {
|
||||
functions: true,
|
||||
schedulers: true,
|
||||
// Settings
|
||||
elementsWrapper: '',
|
||||
elementsWrapper: '`',
|
||||
stringsWrapper: '"',
|
||||
tableAdd: true,
|
||||
tableTruncateDisableFKCheck: true,
|
||||
tableDuplicate: true,
|
||||
viewAdd: true,
|
||||
triggerAdd: true,
|
||||
routineAdd: true,
|
||||
@@ -33,6 +51,9 @@ module.exports = {
|
||||
schedulerAdd: true,
|
||||
schemaEdit: true,
|
||||
schemaDrop: true,
|
||||
schemaExport: true,
|
||||
exportByChunks: true,
|
||||
schemaImport: true,
|
||||
tableSettings: true,
|
||||
viewSettings: true,
|
||||
triggerSettings: true,
|
||||
@@ -45,7 +66,6 @@ module.exports = {
|
||||
unsigned: true,
|
||||
nullable: true,
|
||||
zerofill: true,
|
||||
tableOptions: true,
|
||||
autoIncrement: true,
|
||||
comment: true,
|
||||
collation: true,
|
||||
@@ -58,6 +78,7 @@ module.exports = {
|
||||
procedureDataAccess: true,
|
||||
procedureSql: 'BEGIN\r\n\r\nEND',
|
||||
procedureContext: true,
|
||||
procedureContextValues: ['IN', 'OUT', 'INOUT'],
|
||||
triggerSql: 'BEGIN\r\n\r\nEND',
|
||||
functionDeterministic: true,
|
||||
functionDataAccess: true,
|
@@ -1,15 +1,30 @@
|
||||
const defaults = require('./defaults');
|
||||
import { Customizations } from '../interfaces/customizations';
|
||||
import { defaults } from './defaults';
|
||||
import postgresqlTypes from '../data-types/postgresql';
|
||||
|
||||
module.exports = {
|
||||
export const customizations: Customizations = {
|
||||
...defaults,
|
||||
// Defaults
|
||||
defaultPort: 5432,
|
||||
defaultUser: 'postgres',
|
||||
defaultDatabase: 'postgres',
|
||||
dataTypes: postgresqlTypes,
|
||||
indexTypes: [
|
||||
'PRIMARY',
|
||||
'INDEX',
|
||||
'UNIQUE'
|
||||
],
|
||||
foreignActions: [
|
||||
'RESTRICT',
|
||||
'CASCADE',
|
||||
'SET NULL',
|
||||
'NO ACTION'
|
||||
],
|
||||
// Core
|
||||
database: true,
|
||||
sslConnection: true,
|
||||
sshConnection: true,
|
||||
cancelQueries: true,
|
||||
// Tools
|
||||
processesList: true,
|
||||
// Structure
|
||||
@@ -24,12 +39,15 @@ module.exports = {
|
||||
elementsWrapper: '"',
|
||||
stringsWrapper: '\'',
|
||||
tableAdd: true,
|
||||
tableDuplicate: true,
|
||||
viewAdd: true,
|
||||
triggerAdd: true,
|
||||
triggerFunctionAdd: true,
|
||||
routineAdd: true,
|
||||
functionAdd: true,
|
||||
schemaDrop: true,
|
||||
schemaExport: true,
|
||||
schemaImport: true,
|
||||
databaseEdit: false,
|
||||
tableSettings: true,
|
||||
viewSettings: true,
|
||||
@@ -43,6 +61,7 @@ module.exports = {
|
||||
tableArray: true,
|
||||
procedureSql: '$procedure$\r\n\r\n$procedure$',
|
||||
procedureContext: true,
|
||||
procedureContextValues: ['IN', 'OUT', 'INOUT'],
|
||||
procedureLanguage: true,
|
||||
functionSql: '$function$\r\n\r\n$function$',
|
||||
triggerFunctionSql: '$function$\r\nBEGIN\r\n\r\nEND\r\n$function$',
|
@@ -1,4 +1,21 @@
|
||||
module.exports = {
|
||||
import { Customizations } from '../interfaces/customizations';
|
||||
import { defaults } from './defaults';
|
||||
import sqliteTypes from '../data-types/sqlite';
|
||||
|
||||
export const customizations: Customizations = {
|
||||
...defaults,
|
||||
dataTypes: sqliteTypes,
|
||||
indexTypes: [
|
||||
'PRIMARY',
|
||||
'INDEX',
|
||||
'UNIQUE'
|
||||
],
|
||||
foreignActions: [
|
||||
'RESTRICT',
|
||||
'CASCADE',
|
||||
'SET NULL',
|
||||
'NO ACTION'
|
||||
],
|
||||
// Core
|
||||
fileConnection: true,
|
||||
// Structure
|
||||
@@ -10,6 +27,7 @@ module.exports = {
|
||||
elementsWrapper: '"',
|
||||
stringsWrapper: '\'',
|
||||
tableAdd: true,
|
||||
tableDuplicate: true,
|
||||
viewAdd: true,
|
||||
triggerAdd: true,
|
||||
schemaEdit: false,
|
136
src/common/data-types/firebird.ts
Normal file
136
src/common/data-types/firebird.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { TypesGroup } from 'common/interfaces/antares';
|
||||
|
||||
export default [
|
||||
{
|
||||
group: 'integer',
|
||||
types: [
|
||||
{
|
||||
name: 'SMALLINT',
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: true,
|
||||
zerofill: true
|
||||
},
|
||||
{
|
||||
name: 'INTEGER',
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: true,
|
||||
zerofill: true
|
||||
},
|
||||
{
|
||||
name: 'BIGINT',
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: true,
|
||||
zerofill: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'float',
|
||||
types: [
|
||||
{
|
||||
name: 'DECIMAL',
|
||||
length: true,
|
||||
scale: true,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'NUMERIC',
|
||||
length: true,
|
||||
scale: true,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'FLOAT',
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'DOUBLE PRECISION',
|
||||
length: false,
|
||||
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: 'BLOB-TEXT',
|
||||
length: false,
|
||||
collation: true,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'binary',
|
||||
types: [
|
||||
{
|
||||
name: 'BLOB',
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
},
|
||||
{
|
||||
name: 'CHAR-BINARY',
|
||||
length: false,
|
||||
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: 'TIMESTAMP',
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
}
|
||||
]
|
||||
}
|
||||
] as TypesGroup[];
|
@@ -1,4 +1,6 @@
|
||||
module.exports = [
|
||||
import { TypesGroup } from 'common/interfaces/antares';
|
||||
|
||||
export default [
|
||||
{
|
||||
group: 'integer',
|
||||
types: [
|
||||
@@ -58,7 +60,7 @@ module.exports = [
|
||||
},
|
||||
{
|
||||
name: 'DOUBLE',
|
||||
length: true,
|
||||
length: false,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
@@ -66,6 +68,7 @@ module.exports = [
|
||||
{
|
||||
name: 'DECIMAL',
|
||||
length: true,
|
||||
scale: true,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
@@ -120,7 +123,7 @@ module.exports = [
|
||||
{
|
||||
name: 'JSON',
|
||||
length: false,
|
||||
collation: true,
|
||||
collation: false,
|
||||
unsigned: false,
|
||||
zerofill: false
|
||||
}
|
||||
@@ -218,56 +221,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
|
||||
@@ -305,4 +308,4 @@ module.exports = [
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
] as TypesGroup[];
|
@@ -1,4 +1,6 @@
|
||||
module.exports = [
|
||||
import { TypesGroup } from 'common/interfaces/antares';
|
||||
|
||||
export default [
|
||||
{
|
||||
group: 'integer',
|
||||
types: [
|
||||
@@ -22,11 +24,6 @@ module.exports = [
|
||||
length: false,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'NUMERIC',
|
||||
length: true,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'SMALLSERIAL',
|
||||
length: false,
|
||||
@@ -52,6 +49,12 @@ module.exports = [
|
||||
length: false,
|
||||
unsigned: true
|
||||
},
|
||||
{
|
||||
name: 'NUMERIC',
|
||||
length: true,
|
||||
unsigned: true,
|
||||
scale: true
|
||||
},
|
||||
{
|
||||
name: 'DOUBLE PRECISION',
|
||||
length: false,
|
||||
@@ -289,4 +292,4 @@ module.exports = [
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
] as TypesGroup[];
|
@@ -1,4 +1,6 @@
|
||||
module.exports = [
|
||||
import { TypesGroup } from 'common/interfaces/antares';
|
||||
|
||||
export default [
|
||||
{
|
||||
group: 'integer',
|
||||
types: [
|
||||
@@ -134,4 +136,4 @@ module.exports = [
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
] as TypesGroup[];
|
@@ -8,7 +8,10 @@ export const TEXT = [
|
||||
export const LONG_TEXT = [
|
||||
'TEXT',
|
||||
'MEDIUMTEXT',
|
||||
'LONGTEXT'
|
||||
'LONGTEXT',
|
||||
'JSON',
|
||||
'VARBINARY',
|
||||
'BLOB-TEXT'
|
||||
];
|
||||
|
||||
export const ARRAY = [
|
||||
@@ -27,24 +30,31 @@ export const NUMBER = [
|
||||
'SMALLINT',
|
||||
'MEDIUMINT',
|
||||
'BIGINT',
|
||||
'DECIMAL',
|
||||
'NUMERIC',
|
||||
'INTEGER',
|
||||
'SMALLSERIAL',
|
||||
'SERIAL',
|
||||
'BIGSERIAL',
|
||||
'OID',
|
||||
'XID'
|
||||
'XID',
|
||||
'INT64'
|
||||
];
|
||||
|
||||
export const FLOAT = [
|
||||
'FLOAT',
|
||||
'DECIMAL',
|
||||
'DOUBLE',
|
||||
'REAL',
|
||||
'DOUBLE PRECISION',
|
||||
'MONEY'
|
||||
];
|
||||
|
||||
export const IS_BIGINT = [
|
||||
'BIGINT',
|
||||
'BIGSERIAL',
|
||||
'DOUBLE PRECISION'
|
||||
];
|
||||
|
||||
export const BOOLEAN = [
|
||||
'BOOL',
|
||||
'BOOLEAN'
|
||||
@@ -75,10 +85,41 @@ export const BLOB = [
|
||||
'TINYBLOB',
|
||||
'MEDIUMBLOB',
|
||||
'LONGBLOB',
|
||||
'BYTEA'
|
||||
'LONG_BLOB',
|
||||
'BYTEA',
|
||||
'CHAR-BINARY'
|
||||
];
|
||||
|
||||
export const BIT = [
|
||||
'BIT',
|
||||
'BIT VARYING'
|
||||
];
|
||||
|
||||
export const BINARY = [
|
||||
'BINARY'
|
||||
];
|
||||
|
||||
export const UUID = [
|
||||
'UUID'
|
||||
];
|
||||
|
||||
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'
|
||||
];
|
@@ -1,6 +0,0 @@
|
||||
module.exports = [
|
||||
'PRIMARY',
|
||||
'INDEX',
|
||||
'UNIQUE',
|
||||
'FULLTEXT'
|
||||
];
|
@@ -1,5 +0,0 @@
|
||||
module.exports = [
|
||||
'PRIMARY',
|
||||
'INDEX',
|
||||
'UNIQUE'
|
||||
];
|
@@ -1,5 +0,0 @@
|
||||
module.exports = [
|
||||
'PRIMARY',
|
||||
'INDEX',
|
||||
'UNIQUE'
|
||||
];
|
407
src/common/interfaces/antares.ts
Normal file
407
src/common/interfaces/antares.ts
Normal file
@@ -0,0 +1,407 @@
|
||||
import * as mysql from 'mysql2/promise';
|
||||
import * as pg from 'pg';
|
||||
import MysqlExporter from 'src/main/libs/exporters/sql/MysqlExporter';
|
||||
import PostgreSQLExporter from 'src/main/libs/exporters/sql/PostgreSQLExporter';
|
||||
import MySQLImporter from 'src/main/libs/importers/sql/MySQLlImporter';
|
||||
import PostgreSQLImporter from 'src/main/libs/importers/sql/PostgreSQLImporter';
|
||||
import SSHConfig from 'ssh2-promise/lib/sshConfig';
|
||||
import { MySQLClient } from '../../main/libs/clients/MySQLClient';
|
||||
import { PostgreSQLClient } from '../../main/libs/clients/PostgreSQLClient';
|
||||
import { SQLiteClient } from '../../main/libs/clients/SQLiteClient';
|
||||
import { FirebirdSQLClient } from 'src/main/libs/clients/FirebirdSQLClient';
|
||||
|
||||
export type Client = MySQLClient | PostgreSQLClient | SQLiteClient | FirebirdSQLClient
|
||||
export type ClientCode = 'mysql' | 'maria' | 'pg' | 'sqlite' | 'firebird'
|
||||
export type Exporter = MysqlExporter | PostgreSQLExporter
|
||||
export type Importer = MySQLImporter | PostgreSQLImporter
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export interface IpcResponse<T = any> {
|
||||
status: 'success' | 'error';
|
||||
response?: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pasameters needed to create a new Antares connection to a database
|
||||
*/
|
||||
export interface ClientParams {
|
||||
client: ClientCode;
|
||||
uid?: string;
|
||||
params:
|
||||
mysql.ConnectionOptions & {schema: string; ssl?: mysql.SslOptions; ssh?: SSHConfig; readonly: boolean}
|
||||
| pg.ClientConfig & {schema: string; ssl?: mysql.SslOptions; ssh?: SSHConfig; readonly: boolean}
|
||||
| { databasePath: string; readonly: boolean };
|
||||
poolSize?: number;
|
||||
logger?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Paramenets insered by user in connection mask
|
||||
*/
|
||||
export interface ConnectionParams {
|
||||
uid: string;
|
||||
name?: string;
|
||||
client: ClientCode;
|
||||
host: string;
|
||||
database?: string;
|
||||
schema?: string;
|
||||
databasePath?: string;
|
||||
port: number;
|
||||
user: string;
|
||||
password: string;
|
||||
ask: boolean;
|
||||
readonly: boolean;
|
||||
ssl: boolean;
|
||||
cert?: string;
|
||||
key?: string;
|
||||
ca?: string;
|
||||
untrustedConnection: boolean;
|
||||
ciphers?: string;
|
||||
ssh: boolean;
|
||||
sshHost?: string;
|
||||
sshUser?: string;
|
||||
sshPass?: string;
|
||||
sshKey?: string;
|
||||
sshPort?: number;
|
||||
sshPassphrase?: string;
|
||||
}
|
||||
|
||||
export interface TypeInformations {
|
||||
name: string;
|
||||
length: boolean;
|
||||
scale: boolean;
|
||||
collation: boolean;
|
||||
unsigned: boolean;
|
||||
zerofill: boolean;
|
||||
}
|
||||
|
||||
export interface TypesGroup {
|
||||
group: string;
|
||||
types: TypeInformations[];
|
||||
}
|
||||
|
||||
// Tables
|
||||
export interface TableInfos {
|
||||
name: string;
|
||||
type: string;
|
||||
rows: number;
|
||||
created: Date;
|
||||
updated: Date;
|
||||
engine: string;
|
||||
comment: string;
|
||||
size: number | false;
|
||||
autoIncrement: number;
|
||||
collation: string;
|
||||
}
|
||||
|
||||
export type TableOptions = Partial<TableInfos>;
|
||||
|
||||
export interface TableField {
|
||||
// eslint-disable-next-line camelcase
|
||||
_antares_id?: string;
|
||||
name: string;
|
||||
type: string;
|
||||
schema: string;
|
||||
table?: string;
|
||||
numPrecision?: number;
|
||||
numLength?: number;
|
||||
datePrecision?: number;
|
||||
charLength?: number;
|
||||
numScale?: number;
|
||||
nullable?: boolean;
|
||||
unsigned?: boolean;
|
||||
zerofill?: boolean;
|
||||
order?: number;
|
||||
default?: string;
|
||||
defaultType?: string;
|
||||
enumValues?: string;
|
||||
charset?: string;
|
||||
collation?: string;
|
||||
autoIncrement?: boolean;
|
||||
isArray?: boolean;
|
||||
onUpdate?: string;
|
||||
comment?: string;
|
||||
after?: string;
|
||||
orgName?: string;
|
||||
length?: number | false;
|
||||
alias: string;
|
||||
tableAlias: string;
|
||||
orgTable: string;
|
||||
key?: 'pri' | 'uni' | '';
|
||||
}
|
||||
|
||||
export interface TableIndex {
|
||||
// eslint-disable-next-line camelcase
|
||||
_antares_id?: string;
|
||||
name: string;
|
||||
fields: string[];
|
||||
type: string;
|
||||
comment?: string;
|
||||
indexType?: string;
|
||||
indexComment?: string;
|
||||
cardinality?: number;
|
||||
oldType?: string;
|
||||
oldName?: string;
|
||||
}
|
||||
|
||||
export interface TableForeign {
|
||||
// eslint-disable-next-line camelcase
|
||||
_antares_id?: string;
|
||||
constraintName: string;
|
||||
refSchema: string;
|
||||
table: string;
|
||||
refTable: string;
|
||||
field: string;
|
||||
refField: string;
|
||||
onUpdate: string;
|
||||
onDelete: string;
|
||||
oldName?: string;
|
||||
}
|
||||
|
||||
export interface CreateTableParams {
|
||||
/** Connection UID */
|
||||
uid?: string;
|
||||
schema: string;
|
||||
fields: TableField[];
|
||||
foreigns: TableForeign[];
|
||||
indexes: TableIndex[];
|
||||
options: TableOptions;
|
||||
}
|
||||
|
||||
export interface AlterTableParams {
|
||||
/** Connection UID */
|
||||
uid?: string;
|
||||
schema: string;
|
||||
table: string;
|
||||
additions: TableField[];
|
||||
changes: TableField[];
|
||||
deletions: TableField[];
|
||||
tableStructure: {
|
||||
name: string;
|
||||
fields: TableField[];
|
||||
foreigns: TableForeign[];
|
||||
indexes: TableIndex[];
|
||||
};
|
||||
indexChanges: {
|
||||
additions: TableIndex[];
|
||||
changes: TableIndex[];
|
||||
deletions: TableIndex[];
|
||||
};
|
||||
foreignChanges: {
|
||||
additions: TableForeign[];
|
||||
changes: TableForeign[];
|
||||
deletions: TableForeign[];
|
||||
};
|
||||
options: TableOptions;
|
||||
}
|
||||
|
||||
// Views
|
||||
export type ViewInfos = TableInfos
|
||||
export interface CreateViewParams {
|
||||
schema: string;
|
||||
name: string;
|
||||
algorithm: string;
|
||||
definer: string;
|
||||
security: string;
|
||||
sql: string;
|
||||
updateOption: string;
|
||||
}
|
||||
|
||||
export interface AlterViewParams extends CreateViewParams {
|
||||
oldName?: string;
|
||||
}
|
||||
|
||||
// Triggers
|
||||
export interface TriggerInfos {
|
||||
name: string;
|
||||
statement: string;
|
||||
timing: string;
|
||||
definer: string;
|
||||
event: string;
|
||||
table: string;
|
||||
sqlMode: string;
|
||||
created: Date;
|
||||
charset: string;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export interface CreateTriggerParams {
|
||||
definer?: string;
|
||||
schema: string;
|
||||
name: string;
|
||||
activation: string;
|
||||
event: string;
|
||||
table: string;
|
||||
sql: string;
|
||||
}
|
||||
|
||||
export interface AlterTriggerParams extends CreateTriggerParams {
|
||||
oldName?: string;
|
||||
}
|
||||
|
||||
// Routines & Functions
|
||||
|
||||
export interface FunctionParam {
|
||||
// eslint-disable-next-line camelcase
|
||||
_antares_id: string;
|
||||
context: string;
|
||||
name: string;
|
||||
type: string;
|
||||
length: number;
|
||||
}
|
||||
|
||||
export interface RoutineInfos {
|
||||
name: string;
|
||||
type?: string;
|
||||
definer: string;
|
||||
created?: string;
|
||||
sql?: string;
|
||||
updated?: string;
|
||||
comment?: string;
|
||||
charset?: string;
|
||||
security?: string;
|
||||
language?: string;
|
||||
dataAccess?: string;
|
||||
deterministic?: boolean;
|
||||
parameters?: FunctionParam[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
returns?: any;
|
||||
returnsLength?: number;
|
||||
}
|
||||
|
||||
export type FunctionInfos = RoutineInfos
|
||||
export type TriggerFunctionInfos = FunctionInfos
|
||||
|
||||
export interface CreateRoutineParams {
|
||||
name: string;
|
||||
parameters?: FunctionParam[];
|
||||
definer: string;
|
||||
schema: string;
|
||||
deterministic: boolean;
|
||||
dataAccess: string;
|
||||
security: string;
|
||||
comment?: string;
|
||||
language?: string;
|
||||
sql: string;
|
||||
}
|
||||
|
||||
export interface AlterRoutineParams extends CreateRoutineParams {
|
||||
oldName?: string;
|
||||
}
|
||||
|
||||
export interface CreateFunctionParams {
|
||||
name: string;
|
||||
parameters?: FunctionParam[];
|
||||
definer: string;
|
||||
schema: string;
|
||||
deterministic: boolean;
|
||||
dataAccess: string;
|
||||
security: string;
|
||||
comment?: string;
|
||||
sql: string;
|
||||
returns: string;
|
||||
returnsLength: number;
|
||||
language?: string;
|
||||
}
|
||||
|
||||
export interface AlterFunctionParams extends CreateFunctionParams {
|
||||
oldName?: string;
|
||||
}
|
||||
|
||||
// Events
|
||||
export interface EventInfos {
|
||||
definer?: string;
|
||||
schema: string;
|
||||
name: string;
|
||||
execution: string;
|
||||
every: string[];
|
||||
starts: string;
|
||||
ends: string;
|
||||
at: string;
|
||||
preserve: boolean;
|
||||
state: string;
|
||||
comment: string;
|
||||
enabled?: boolean;
|
||||
sql: string;
|
||||
}
|
||||
|
||||
export type CreateEventParams = EventInfos;
|
||||
|
||||
export interface AlterEventParams extends CreateEventParams {
|
||||
oldName?: string;
|
||||
}
|
||||
|
||||
// Schema
|
||||
export interface SchemaInfos {
|
||||
name: string;
|
||||
size: number;
|
||||
tables: TableInfos[];
|
||||
functions: FunctionInfos[];
|
||||
procedures: RoutineInfos[];
|
||||
triggers: TriggerInfos[];
|
||||
schedulers: EventInfos[];
|
||||
}
|
||||
|
||||
export interface CollationInfos {
|
||||
charset: string;
|
||||
collation: string;
|
||||
compiled: boolean;
|
||||
default: boolean;
|
||||
id: string | number;
|
||||
sortLen: number;
|
||||
}
|
||||
|
||||
// Query
|
||||
export interface QueryBuilderObject {
|
||||
schema: string;
|
||||
select: string[];
|
||||
from: string;
|
||||
where: string[];
|
||||
groupBy: string[];
|
||||
orderBy: string[];
|
||||
limit: number;
|
||||
offset: number;
|
||||
join: string[];
|
||||
update: string[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
insert: {[key: string]: any}[];
|
||||
delete: boolean;
|
||||
}
|
||||
|
||||
export interface QueryParams {
|
||||
nest?: boolean;
|
||||
details?: boolean;
|
||||
split?: boolean;
|
||||
comments?: boolean;
|
||||
autocommit?: boolean;
|
||||
schema?: string;
|
||||
tabUid?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use TableFIeld
|
||||
*/
|
||||
export type QueryField = TableField
|
||||
|
||||
export interface QueryForeign {
|
||||
schema: string;
|
||||
table: string;
|
||||
field: string;
|
||||
position: number;
|
||||
constraintPosition: number;
|
||||
constraintName: string;
|
||||
refSchema: string;
|
||||
refTable: string;
|
||||
refField: string;
|
||||
onUpdate: string;
|
||||
onDelete: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export interface QueryResult<T = any> {
|
||||
rows: T[];
|
||||
report: { affectedRows: number };
|
||||
fields: QueryField[];
|
||||
keys: QueryForeign[];
|
||||
duration: number;
|
||||
}
|
98
src/common/interfaces/customizations.ts
Normal file
98
src/common/interfaces/customizations.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { TypesGroup } from './antares';
|
||||
|
||||
export interface Customizations {
|
||||
// Defaults
|
||||
defaultPort?: number;
|
||||
defaultUser?: string;
|
||||
defaultDatabase?: string;
|
||||
dataTypes?: TypesGroup[];
|
||||
indexTypes?: string[];
|
||||
foreignActions?: string[];
|
||||
// Core
|
||||
database?: boolean;
|
||||
collations?: boolean;
|
||||
engines?: boolean;
|
||||
connectionSchema?: boolean;
|
||||
sslConnection?: boolean;
|
||||
sshConnection?: boolean;
|
||||
fileConnection?: boolean;
|
||||
cancelQueries?: boolean;
|
||||
// Tools
|
||||
processesList?: boolean;
|
||||
usersManagement?: boolean;
|
||||
variables?: boolean;
|
||||
// Structure
|
||||
schemas?: boolean;
|
||||
tables?: boolean;
|
||||
views?: boolean;
|
||||
triggers?: boolean;
|
||||
triggerFunctions?: boolean;
|
||||
routines?: boolean;
|
||||
functions?: boolean;
|
||||
schedulers?: boolean;
|
||||
// Settings
|
||||
elementsWrapper: string;
|
||||
stringsWrapper: string;
|
||||
tableAdd?: boolean;
|
||||
tableSettings?: boolean;
|
||||
tableDuplicate?: boolean;
|
||||
tableArray?: boolean;
|
||||
tableRealCount?: boolean;
|
||||
tableTruncateDisableFKCheck?: boolean;
|
||||
viewAdd?: boolean;
|
||||
viewSettings?: boolean;
|
||||
triggerAdd?: boolean;
|
||||
triggerFunctionAdd?: boolean;
|
||||
routineAdd?: boolean;
|
||||
functionAdd?: boolean;
|
||||
schedulerAdd?: boolean;
|
||||
databaseEdit?: boolean;
|
||||
schemaEdit?: boolean;
|
||||
schemaDrop?: boolean;
|
||||
schemaExport?: boolean;
|
||||
exportByChunks?: boolean;
|
||||
schemaImport?: boolean;
|
||||
triggerSettings?: boolean;
|
||||
triggerFunctionSettings?: boolean;
|
||||
routineSettings?: boolean;
|
||||
functionSettings?: boolean;
|
||||
schedulerSettings?: boolean;
|
||||
indexes?: boolean;
|
||||
foreigns?: boolean;
|
||||
sortableFields?: boolean;
|
||||
unsigned?: boolean;
|
||||
nullable?: boolean;
|
||||
nullablePrimary?: boolean;
|
||||
zerofill?: boolean;
|
||||
autoIncrement?: boolean;
|
||||
comment?: boolean;
|
||||
collation?: boolean;
|
||||
definer?: boolean;
|
||||
onUpdate?: boolean;
|
||||
viewAlgorithm?: boolean;
|
||||
viewSqlSecurity?: boolean;
|
||||
viewUpdateOption?: boolean;
|
||||
procedureDeterministic?: boolean;
|
||||
procedureDataAccess?: boolean;
|
||||
procedureSql?: string;
|
||||
procedureContext?: boolean;
|
||||
procedureContextValues?: string[];
|
||||
procedureLanguage?: boolean;
|
||||
functionDeterministic?: boolean;
|
||||
functionDataAccess?: boolean;
|
||||
functionSql?: string;
|
||||
functionContext?: boolean;
|
||||
functionLanguage?: boolean;
|
||||
triggerSql?: string;
|
||||
triggerStatementInCreation?: boolean;
|
||||
triggerMultipleEvents?: boolean;
|
||||
triggerTableInName?: boolean;
|
||||
triggerUpdateColumns?: boolean;
|
||||
triggerOnlyRename?: boolean;
|
||||
triggerEnableDisable?: boolean;
|
||||
triggerFunctionSql?: string;
|
||||
triggerFunctionlanguages?: string[];
|
||||
parametersLength?: boolean;
|
||||
languages?: string[];
|
||||
readOnlyMode?: boolean;
|
||||
}
|
28
src/common/interfaces/exporter.ts
Normal file
28
src/common/interfaces/exporter.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export interface TableParams {
|
||||
table: string;
|
||||
includeStructure: boolean;
|
||||
includeContent: boolean;
|
||||
includeDropStatement: boolean;
|
||||
}
|
||||
|
||||
export interface ExportOptions {
|
||||
schema: string;
|
||||
tables: {
|
||||
table: string;
|
||||
includeStructure: boolean;
|
||||
includeContent: boolean;
|
||||
includeDropStatement: boolean;
|
||||
}[];
|
||||
includes: {[key: string]: boolean};
|
||||
outputFormat: 'sql' | 'sql.zip';
|
||||
outputFile: string;
|
||||
sqlInsertAfter: number;
|
||||
sqlInsertDivider: 'bytes' | 'rows';
|
||||
}
|
||||
|
||||
export interface ExportState {
|
||||
totalItems?: number;
|
||||
currentItemIndex?: number;
|
||||
currentItem?: string;
|
||||
op?: string;
|
||||
}
|
16
src/common/interfaces/importer.ts
Normal file
16
src/common/interfaces/importer.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import * as antares from './antares';
|
||||
|
||||
export interface ImportOptions {
|
||||
uid: string;
|
||||
schema: string;
|
||||
type: antares.ClientCode;
|
||||
file: string;
|
||||
}
|
||||
|
||||
export interface ImportState {
|
||||
fileSize?: number;
|
||||
readPosition?: number;
|
||||
percentage?: number;
|
||||
queryCount?: number;
|
||||
op?: string;
|
||||
}
|
0
src/common/interfaces/parser.ts
Normal file
0
src/common/interfaces/parser.ts
Normal file
49
src/common/interfaces/tableApis.ts
Normal file
49
src/common/interfaces/tableApis.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { UsableLocale } from '@faker-js/faker';
|
||||
|
||||
export interface TableUpdateParams {
|
||||
uid: string;
|
||||
schema: string;
|
||||
table: string;
|
||||
primary?: string;
|
||||
id: number | string;
|
||||
content: number | string | boolean | Date | Blob | null;
|
||||
type: string;
|
||||
field: string;
|
||||
}
|
||||
|
||||
export interface TableDeleteParams {
|
||||
uid: string;
|
||||
schema: string;
|
||||
table: string;
|
||||
primary?: string;
|
||||
field: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
rows: {[key: string]: any};
|
||||
}
|
||||
|
||||
export interface TableFilterClausole {
|
||||
active: boolean;
|
||||
field: string;
|
||||
op: '=' | '!=' | '>' | '<' | '>=' | '<=' | 'IN' | 'NOT IN' | 'LIKE' | 'BETWEEN' | 'IS NULL' | 'IS NOT NULL';
|
||||
value: '';
|
||||
value2: '';
|
||||
}
|
||||
|
||||
export interface InsertRowsParams {
|
||||
uid: string;
|
||||
schema: string;
|
||||
table: string;
|
||||
row: {[key: string]: {
|
||||
group: string;
|
||||
method: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
params: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any;
|
||||
length: number;
|
||||
};
|
||||
};
|
||||
repeat: number;
|
||||
fields: {[key: string]: string};
|
||||
locale: UsableLocale;
|
||||
}
|
7
src/common/interfaces/workers.ts
Normal file
7
src/common/interfaces/workers.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export type WorkerEvent = 'export-progress' | 'import-progress' | 'query-error' | 'end' | 'cancel' | 'error'
|
||||
|
||||
export interface WorkerIpcMessage {
|
||||
type: WorkerEvent;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
payload: any;
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
'use strict';
|
||||
export function bufferToBase64 (buf) {
|
||||
const binstr = Array.prototype.map.call(buf, ch => {
|
||||
return String.fromCharCode(ch);
|
||||
}).join('');
|
||||
return btoa(binstr);
|
||||
}
|
6
src/common/libs/bufferToBase64.ts
Normal file
6
src/common/libs/bufferToBase64.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export function bufferToBase64 (buf: Buffer) {
|
||||
const binstr = Array.prototype.map.call(buf, (ch: number) => {
|
||||
return String.fromCharCode(ch);
|
||||
}).join('');
|
||||
return Buffer.from(binstr, 'binary').toString('base64');
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
'use strict';
|
||||
export function formatBytes (bytes, decimals = 2) {
|
||||
export function formatBytes (bytes: number, decimals = 2) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
|
||||
const k = 1024;
|
6
src/common/libs/getArrayDepth.ts
Normal file
6
src/common/libs/getArrayDepth.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export function getArrayDepth (array: any[]): number {
|
||||
return Array.isArray(array)
|
||||
? 1 + Math.max(0, ...array.map(getArrayDepth))
|
||||
: 0;
|
||||
}
|
@@ -1,5 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
const lookup = {
|
||||
0: '0000',
|
||||
1: '0001',
|
||||
@@ -23,17 +21,13 @@ const lookup = {
|
||||
D: '1101',
|
||||
E: '1110',
|
||||
F: '1111'
|
||||
};
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Converts hexadecimal string to binary string
|
||||
*
|
||||
* @param {string} hex Hexadecimal string
|
||||
* @returns {string} Binary string
|
||||
*/
|
||||
export default function hexToBinary (hex) {
|
||||
export type HexChar = keyof typeof lookup
|
||||
|
||||
export default function hexToBinary (hex: HexChar[]) {
|
||||
let binary = '';
|
||||
for (let i = 0, len = hex.length; i < len; i++)
|
||||
for (let i = 0; i < hex.length; i++)
|
||||
binary += lookup[hex[i]];
|
||||
|
||||
return binary;
|
192
src/common/libs/langDetector.ts
Normal file
192
src/common/libs/langDetector.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
function isJSON (str: string) {
|
||||
try {
|
||||
if (!['{', '['].includes(str.trim()[0]))
|
||||
return false;
|
||||
|
||||
JSON.parse(str);
|
||||
return true;
|
||||
}
|
||||
catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isHTML (str: string) {
|
||||
const tags = [
|
||||
'a',
|
||||
'abbr',
|
||||
'address',
|
||||
'area',
|
||||
'article',
|
||||
'aside',
|
||||
'audio',
|
||||
'b',
|
||||
'base',
|
||||
'bdi',
|
||||
'bdo',
|
||||
'blockquote',
|
||||
'body',
|
||||
'br',
|
||||
'button',
|
||||
'canvas',
|
||||
'caption',
|
||||
'cite',
|
||||
'code',
|
||||
'col',
|
||||
'colgroup',
|
||||
'data',
|
||||
'datalist',
|
||||
'dd',
|
||||
'del',
|
||||
'details',
|
||||
'dfn',
|
||||
'dialog',
|
||||
'div',
|
||||
'dl',
|
||||
'dt',
|
||||
'em',
|
||||
'embed',
|
||||
'fieldset',
|
||||
'figcaption',
|
||||
'figure',
|
||||
'footer',
|
||||
'form',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'head',
|
||||
'header',
|
||||
'hgroup',
|
||||
'hr',
|
||||
'html',
|
||||
'i',
|
||||
'iframe',
|
||||
'img',
|
||||
'input',
|
||||
'ins',
|
||||
'kbd',
|
||||
'label',
|
||||
'legend',
|
||||
'li',
|
||||
'link',
|
||||
'main',
|
||||
'map',
|
||||
'mark',
|
||||
'math',
|
||||
'menu',
|
||||
'menuitem',
|
||||
'meta',
|
||||
'meter',
|
||||
'nav',
|
||||
'noscript',
|
||||
'object',
|
||||
'ol',
|
||||
'optgroup',
|
||||
'option',
|
||||
'output',
|
||||
'p',
|
||||
'param',
|
||||
'picture',
|
||||
'pre',
|
||||
'progress',
|
||||
'q',
|
||||
'rb',
|
||||
'rp',
|
||||
'rt',
|
||||
'rtc',
|
||||
'ruby',
|
||||
's',
|
||||
'samp',
|
||||
'script',
|
||||
'section',
|
||||
'select',
|
||||
'slot',
|
||||
'small',
|
||||
'source',
|
||||
'span',
|
||||
'strong',
|
||||
'style',
|
||||
'sub',
|
||||
'summary',
|
||||
'sup',
|
||||
'svg',
|
||||
'table',
|
||||
'tbody',
|
||||
'td',
|
||||
'template',
|
||||
'textarea',
|
||||
'tfoot',
|
||||
'th',
|
||||
'thead',
|
||||
'time',
|
||||
'title',
|
||||
'tr',
|
||||
'track',
|
||||
'u',
|
||||
'ul',
|
||||
'var',
|
||||
'video',
|
||||
'wbr'
|
||||
];
|
||||
const doc = new DOMParser().parseFromString(str, 'text/html');
|
||||
const lowerStr = str.toLowerCase();
|
||||
if (Array.from(doc.body.childNodes).some(node => node.nodeType === 1))
|
||||
return tags.some((tag) => lowerStr.includes(`<${tag}>`));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isSVG (str: string) {
|
||||
const doc = new DOMParser().parseFromString(str, 'text/xml');
|
||||
const lowerStr = str.toLowerCase();
|
||||
const errorNode = doc.querySelector('parsererror');
|
||||
if (!errorNode)
|
||||
return lowerStr.includes('<svg');
|
||||
return false;
|
||||
}
|
||||
|
||||
function isXML (str: string) {
|
||||
const doc = new DOMParser().parseFromString(str, 'text/xml');
|
||||
const errorNode = doc.querySelector('parsererror');
|
||||
return !errorNode;
|
||||
}
|
||||
|
||||
function isMD (str: string) {
|
||||
const mdChecks = [
|
||||
'# ',
|
||||
'`',
|
||||
'- ',
|
||||
'+ ',
|
||||
'* ',
|
||||
'1. ',
|
||||
'**',
|
||||
'__',
|
||||
'~~',
|
||||
'>> ',
|
||||
'](http',
|
||||
'![',
|
||||
'[ ]',
|
||||
'[x]'
|
||||
];
|
||||
|
||||
return mdChecks.some((tag) => str.includes(tag));
|
||||
}
|
||||
|
||||
export function langDetector (str: string) {
|
||||
if (!str || !str.trim().length)
|
||||
return 'text';
|
||||
if (isJSON(str))
|
||||
return 'json';
|
||||
if (isHTML(str))
|
||||
return 'html';
|
||||
if (isSVG(str))
|
||||
return 'svg';
|
||||
if (isXML(str))
|
||||
return 'xml';
|
||||
if (isMD(str))
|
||||
return 'markdown';
|
||||
return 'text';
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
'use strict';
|
||||
export function mimeFromHex (hex) {
|
||||
export function mimeFromHex (hex: string) {
|
||||
switch (hex.substring(0, 4)) { // 2 bytes
|
||||
case '424D':
|
||||
return { ext: 'bmp', mime: 'image/bmp' };
|
||||
@@ -43,4 +42,4 @@ export function mimeFromHex (hex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
/* eslint-disable no-useless-escape */
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const pattern = /[\0\x08\x09\x1a\n\r"'\\\%]/gm;
|
||||
const regex = new RegExp(pattern);
|
||||
|
||||
/**
|
||||
* Escapes a string
|
||||
*
|
||||
* @param {String} string
|
||||
* @returns {String}
|
||||
*/
|
||||
function sqlEscaper (string) {
|
||||
return string.replace(regex, char => {
|
||||
const m = ['\\0', '\\x08', '\\x09', '\\x1a', '\\n', '\\r', '\'', '\"', '\\', '\\\\', '%'];
|
||||
const r = ['\\\\0', '\\\\b', '\\\\t', '\\\\z', '\\\\n', '\\\\r', '\\\'', '\\\"', '\\\\', '\\\\\\\\', '\%'];
|
||||
return r[m.indexOf(char)] || char;
|
||||
});
|
||||
}
|
||||
|
||||
export { sqlEscaper };
|
162
src/common/libs/sqlUtils.ts
Normal file
162
src/common/libs/sqlUtils.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable no-useless-escape */
|
||||
import * as moment from 'moment';
|
||||
import { lineString, point, polygon } from '@turf/helpers';
|
||||
import customizations from '../customizations';
|
||||
import { ClientCode } from '../interfaces/antares';
|
||||
import { BLOB, BIT, DATE, DATETIME, FLOAT, SPATIAL, IS_MULTI_SPATIAL, NUMBER, TEXT_SEARCH } from 'common/fieldTypes';
|
||||
import hexToBinary, { HexChar } from './hexToBinary';
|
||||
import { getArrayDepth } from './getArrayDepth';
|
||||
|
||||
/**
|
||||
* Escapes a string fo SQL use
|
||||
*
|
||||
* @param { String } string
|
||||
* @returns { String } Escaped string
|
||||
*/
|
||||
export const sqlEscaper = (string: string): string => {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const pattern = /[\0\x08\x09\x1a\n\r"'\\\%]/gm;
|
||||
const regex = new RegExp(pattern);
|
||||
return string.replace(regex, char => {
|
||||
const m = ['\\0', '\\x08', '\\x09', '\\x1a', '\\n', '\\r', '\'', '\"', '\\', '\\\\', '%'];
|
||||
const r = ['\\\\0', '\\\\b', '\\\\t', '\\\\z', '\\\\n', '\\\\r', '\\\'', '\\\"', '\\\\', '\\\\\\\\', '\%'];
|
||||
return r[m.indexOf(char)] || char;
|
||||
});
|
||||
};
|
||||
|
||||
export const objectToGeoJSON = (val: any) => {
|
||||
if (Array.isArray(val)) {
|
||||
if (getArrayDepth(val) === 1)
|
||||
return lineString(val.reduce((acc, curr) => [...acc, [curr.x, curr.y]], []));
|
||||
else
|
||||
return polygon(val.map(arr => arr.reduce((acc: any, curr: any) => [...acc, [curr.x, curr.y]], [])));
|
||||
}
|
||||
else
|
||||
return point([val.x, val.y]);
|
||||
};
|
||||
|
||||
export const escapeAndQuote = (val: string, client: ClientCode) => {
|
||||
const { stringsWrapper: sw } = customizations[client];
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const CHARS_TO_ESCAPE = /[\0\b\t\n\r\x1a"'\\]/g;
|
||||
const CHARS_ESCAPE_MAP: {[key: string]: string} = {
|
||||
'\0': '\\0',
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\r': '\\r',
|
||||
'\x1a': '\\Z',
|
||||
'"': '\\"',
|
||||
'\'': '\\\'',
|
||||
'\\': '\\\\'
|
||||
};
|
||||
let chunkIndex = CHARS_TO_ESCAPE.lastIndex = 0;
|
||||
let escapedVal = '';
|
||||
let match;
|
||||
|
||||
while ((match = CHARS_TO_ESCAPE.exec(val))) {
|
||||
escapedVal += val.slice(chunkIndex, match.index) + CHARS_ESCAPE_MAP[match[0]];
|
||||
chunkIndex = CHARS_TO_ESCAPE.lastIndex;
|
||||
}
|
||||
|
||||
if (chunkIndex === 0)
|
||||
return `${sw}${val}${sw}`;
|
||||
|
||||
if (chunkIndex < val.length)
|
||||
return `${sw}${escapedVal + val.slice(chunkIndex)}${sw}`;
|
||||
|
||||
return `${sw}${escapedVal}${sw}`;
|
||||
};
|
||||
|
||||
export const valueToSqlString = (args: {
|
||||
val: any;
|
||||
client: ClientCode;
|
||||
field: {type: string; datePrecision: number};
|
||||
}): string => {
|
||||
let parsedValue;
|
||||
const { val, client, field } = args;
|
||||
const { stringsWrapper: sw } = customizations[client];
|
||||
|
||||
if (val === null)
|
||||
parsedValue = 'NULL';
|
||||
else if (DATE.includes(field.type)) {
|
||||
parsedValue = moment(val).isValid()
|
||||
? escapeAndQuote(moment(val).format('YYYY-MM-DD'), client)
|
||||
: val;
|
||||
}
|
||||
else if (DATETIME.includes(field.type)) {
|
||||
let datePrecision = '';
|
||||
for (let i = 0; i < field.datePrecision; i++)
|
||||
datePrecision += i === 0 ? '.S' : 'S';
|
||||
|
||||
parsedValue = moment(val).isValid()
|
||||
? escapeAndQuote(moment(val).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`), client)
|
||||
: escapeAndQuote(val, client);
|
||||
}
|
||||
else if ('isArray' in field) {
|
||||
let localVal;
|
||||
if (Array.isArray(val))
|
||||
localVal = JSON.stringify(val).replaceAll('[', '{').replaceAll(']', '}');
|
||||
else
|
||||
localVal = typeof val === 'string' ? val.replaceAll('[', '{').replaceAll(']', '}') : '';
|
||||
parsedValue = `'${localVal}'`;
|
||||
}
|
||||
else if (TEXT_SEARCH.includes(field.type))
|
||||
parsedValue = `'${val.replaceAll('\'', '\'\'')}'`;
|
||||
else if (BIT.includes(field.type))
|
||||
parsedValue = `b'${hexToBinary(Buffer.from(val).toString('hex') as undefined as HexChar[])}'`;
|
||||
else if (BLOB.includes(field.type)) {
|
||||
if (['mysql', 'maria'].includes(client))
|
||||
parsedValue = `X'${val.toString('hex').toUpperCase()}'`;
|
||||
else if (client === 'pg')
|
||||
parsedValue = `decode('${val.toString('hex').toUpperCase()}', 'hex')`;
|
||||
}
|
||||
else if (NUMBER.includes(field.type))
|
||||
parsedValue = val;
|
||||
else if (FLOAT.includes(field.type))
|
||||
parsedValue = parseFloat(val);
|
||||
else if (SPATIAL.includes(field.type)) {
|
||||
let geoJson;
|
||||
if (IS_MULTI_SPATIAL.includes(field.type)) {
|
||||
const features = [];
|
||||
for (const element of val)
|
||||
features.push(objectToGeoJSON(element));
|
||||
|
||||
geoJson = {
|
||||
type: 'FeatureCollection',
|
||||
features
|
||||
};
|
||||
}
|
||||
else
|
||||
geoJson = objectToGeoJSON(val);
|
||||
|
||||
parsedValue = `ST_GeomFromGeoJSON('${JSON.stringify(geoJson)}')`;
|
||||
}
|
||||
else if (val === '') parsedValue = `${sw}${sw}`;
|
||||
else {
|
||||
parsedValue = typeof val === 'string'
|
||||
? escapeAndQuote(val, client)
|
||||
: typeof val === 'object'
|
||||
? escapeAndQuote(JSON.stringify(val), client)
|
||||
: val;
|
||||
}
|
||||
|
||||
return parsedValue;
|
||||
};
|
||||
|
||||
export const jsonToSqlInsert = (args: {
|
||||
json: { [key: string]: any};
|
||||
client: ClientCode;
|
||||
fields: { [key: string]: {type: string; datePrecision: number}};
|
||||
table: string;
|
||||
}) => {
|
||||
const { client, json, fields, table } = args;
|
||||
const { elementsWrapper: ew } = customizations[client];
|
||||
const fieldNames = Object.keys(json).map(key => `${ew}${key}${ew}`);
|
||||
const values = Object.keys(json).map(key => (
|
||||
valueToSqlString({ val: json[key], client, field: fields[key] })
|
||||
));
|
||||
|
||||
return `INSERT INTO ${ew}${table}${ew} (${fieldNames.join(', ')}) VALUES (${values.join(', ')});`;
|
||||
};
|
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @export
|
||||
* @param {String} [prefix]
|
||||
* @returns {String} Unique ID
|
||||
*/
|
||||
export function uidGen (prefix) {
|
||||
return (prefix ? `${prefix}:` : '') + Math.random().toString(36).substr(2, 9).toUpperCase();
|
||||
};
|
3
src/common/libs/uidGen.ts
Normal file
3
src/common/libs/uidGen.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function uidGen (prefix?: string) {
|
||||
return (prefix ? `${prefix}:` : '') + Math.random().toString(36).substring(2, 11).toUpperCase();
|
||||
}
|
138
src/common/shortcuts.ts
Normal file
138
src/common/shortcuts.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
export const shortcutEvents: { [key: string]: { l18n: string; l18nParam?: string | number; context?: 'tab' }} = {
|
||||
'run-or-reload': { l18n: 'message.runOrReload', context: 'tab' },
|
||||
'open-new-tab': { l18n: 'message.openNewTab', context: 'tab' },
|
||||
'close-tab': { l18n: 'message.closeTab', context: 'tab' },
|
||||
'format-query': { l18n: 'message.formatQuery', context: 'tab' },
|
||||
'kill-query': { l18n: 'message.killQuery', context: 'tab' },
|
||||
'query-history': { l18n: 'message.queryHistory', context: 'tab' },
|
||||
'clear-query': { l18n: 'message.clearQuery', context: 'tab' },
|
||||
'next-tab': { l18n: 'message.nextTab' },
|
||||
'prev-tab': { l18n: 'message.previousTab' },
|
||||
'open-all-connections': { l18n: 'message.openAllConnections' },
|
||||
'open-filter': { l18n: 'message.openFilter' },
|
||||
'next-page': { l18n: 'message.nextResultsPage' },
|
||||
'prev-page': { l18n: 'message.previousResultsPage' },
|
||||
'toggle-console': { l18n: 'message.toggleConsole' },
|
||||
'save-content': { l18n: 'message.saveContent' },
|
||||
'create-connection': { l18n: 'message.createNewConnection' },
|
||||
'open-settings': { l18n: 'message.openSettings' },
|
||||
'open-scratchpad': { l18n: 'message.openScratchpad' }
|
||||
};
|
||||
|
||||
interface ShortcutRecord {
|
||||
event: string;
|
||||
keys: Electron.Accelerator[] | string[];
|
||||
/** Needed for default shortcuts */
|
||||
os: NodeJS.Platform[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Default shortcuts
|
||||
*/
|
||||
const shortcuts: ShortcutRecord[] = [
|
||||
{
|
||||
event: 'run-or-reload',
|
||||
keys: ['F5'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'save-content',
|
||||
keys: ['CommandOrControl+S'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'kill-query',
|
||||
keys: ['CommandOrControl+K'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'format-query',
|
||||
keys: ['CommandOrControl+B'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'clear-query',
|
||||
keys: ['CommandOrControl+Alt+W'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'query-history',
|
||||
keys: ['CommandOrControl+G'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'open-new-tab',
|
||||
keys: ['CommandOrControl+T'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'close-tab',
|
||||
keys: ['CommandOrControl+W'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'next-tab',
|
||||
keys: ['Alt+CommandOrControl+Right'],
|
||||
os: ['darwin', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'prev-tab',
|
||||
keys: ['Alt+CommandOrControl+Left'],
|
||||
os: ['darwin', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'next-tab',
|
||||
keys: ['CommandOrControl+PageDown'],
|
||||
os: ['linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'prev-tab',
|
||||
keys: ['CommandOrControl+PageUp'],
|
||||
os: ['linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'open-filter',
|
||||
keys: ['CommandOrControl+F'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'next-page',
|
||||
keys: ['CommandOrControl+Right'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'prev-page',
|
||||
keys: ['CommandOrControl+Left'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'open-all-connections',
|
||||
keys: ['Shift+CommandOrControl+Space'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'toggle-console',
|
||||
keys: ['CommandOrControl+F12'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
},
|
||||
{
|
||||
event: 'toggle-console',
|
||||
keys: ['CommandOrControl+`'],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
}
|
||||
];
|
||||
|
||||
for (let i = 1; i <= 9; i++) {
|
||||
shortcutEvents[`select-tab-${i}`] = {
|
||||
l18n: 'message.selectTabNumber',
|
||||
l18nParam: i
|
||||
};
|
||||
|
||||
shortcuts.push({
|
||||
event: `select-tab-${i}`,
|
||||
keys: [`CommandOrControl+${i}`],
|
||||
os: ['darwin', 'linux', 'win32']
|
||||
});
|
||||
}
|
||||
|
||||
export { shortcuts, ShortcutRecord };
|
@@ -1,12 +0,0 @@
|
||||
import { app, ipcMain } from 'electron';
|
||||
|
||||
export default () => {
|
||||
ipcMain.on('close-app', () => {
|
||||
app.exit();
|
||||
});
|
||||
|
||||
ipcMain.on('get-key', async event => {
|
||||
const key = false;
|
||||
event.returnValue = key;
|
||||
});
|
||||
};
|
36
src/main/ipc-handlers/application.ts
Normal file
36
src/main/ipc-handlers/application.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { app, ipcMain, dialog } from 'electron';
|
||||
import { ShortcutRegister } from '../libs/ShortcutRegister';
|
||||
|
||||
export default () => {
|
||||
ipcMain.on('close-app', () => {
|
||||
app.exit();
|
||||
});
|
||||
|
||||
ipcMain.handle('show-open-dialog', (event, options) => {
|
||||
return dialog.showOpenDialog(options);
|
||||
});
|
||||
|
||||
ipcMain.handle('get-download-dir-path', () => {
|
||||
return app.getPath('downloads');
|
||||
});
|
||||
|
||||
ipcMain.handle('resotre-default-shortcuts', () => {
|
||||
const shortCutRegister = ShortcutRegister.getInstance();
|
||||
shortCutRegister.restoreDefaults();
|
||||
});
|
||||
|
||||
ipcMain.handle('reload-shortcuts', () => {
|
||||
const shortCutRegister = ShortcutRegister.getInstance();
|
||||
shortCutRegister.reload();
|
||||
});
|
||||
|
||||
ipcMain.handle('update-shortcuts', (event, shortcuts) => {
|
||||
const shortCutRegister = ShortcutRegister.getInstance();
|
||||
shortCutRegister.updateShortcuts(shortcuts);
|
||||
});
|
||||
|
||||
ipcMain.handle('unregister-shortcuts', () => {
|
||||
const shortCutRegister = ShortcutRegister.getInstance();
|
||||
shortCutRegister.unregister();
|
||||
});
|
||||
};
|
@@ -1,16 +1,29 @@
|
||||
import fs from 'fs';
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
import * as fs from 'fs';
|
||||
import { ipcMain } from 'electron';
|
||||
import { ClientsFactory } from '../libs/ClientsFactory';
|
||||
import { SslOptions } from 'mysql2';
|
||||
|
||||
export default connections => {
|
||||
ipcMain.handle('test-connection', async (event, conn) => {
|
||||
export default (connections: {[key: string]: antares.Client}) => {
|
||||
ipcMain.handle('test-connection', async (event, conn: antares.ConnectionParams) => {
|
||||
const params = {
|
||||
host: conn.host,
|
||||
port: +conn.port,
|
||||
user: conn.user,
|
||||
password: conn.password,
|
||||
application_name: 'Antares SQL',
|
||||
readonly: conn.readonly
|
||||
readonly: conn.readonly,
|
||||
database: '',
|
||||
schema: '',
|
||||
databasePath: '',
|
||||
ssl: undefined as SslOptions,
|
||||
ssh: undefined as {
|
||||
host: string;
|
||||
username: string;
|
||||
password: string;
|
||||
port: number;
|
||||
privateKey: string;
|
||||
passphrase: string;
|
||||
}
|
||||
};
|
||||
|
||||
if (conn.database)
|
||||
@@ -21,10 +34,11 @@ export default connections => {
|
||||
|
||||
if (conn.ssl) {
|
||||
params.ssl = {
|
||||
key: conn.key ? fs.readFileSync(conn.key) : null,
|
||||
cert: conn.cert ? fs.readFileSync(conn.cert) : null,
|
||||
ca: conn.ca ? fs.readFileSync(conn.ca) : null,
|
||||
ciphers: conn.ciphers
|
||||
key: conn.key ? fs.readFileSync(conn.key).toString() : null,
|
||||
cert: conn.cert ? fs.readFileSync(conn.cert).toString() : null,
|
||||
ca: conn.ca ? fs.readFileSync(conn.ca).toString() : null,
|
||||
ciphers: conn.ciphers,
|
||||
rejectUnauthorized: !conn.untrustedConnection
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,19 +48,24 @@ export default connections => {
|
||||
username: conn.sshUser,
|
||||
password: conn.sshPass,
|
||||
port: conn.sshPort ? conn.sshPort : 22,
|
||||
privateKey: conn.sshKey ? fs.readFileSync(conn.sshKey) : null,
|
||||
privateKey: conn.sshKey ? fs.readFileSync(conn.sshKey).toString() : null,
|
||||
passphrase: conn.sshPassphrase
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const connection = await ClientsFactory.getConnection({
|
||||
const connection = await ClientsFactory.getClient({
|
||||
uid: conn.uid,
|
||||
client: conn.client,
|
||||
params
|
||||
});
|
||||
await connection.connect();
|
||||
|
||||
await connection.select('1+1').run();
|
||||
if (conn.client === 'firebird')
|
||||
connection.raw('SELECT rdb$get_context(\'SYSTEM\', \'DB_NAME\') FROM rdb$database');
|
||||
else
|
||||
await connection.select('1+1').run();
|
||||
|
||||
connection.destroy();
|
||||
|
||||
return { status: 'success' };
|
||||
@@ -60,14 +79,26 @@ export default connections => {
|
||||
return uid in connections;
|
||||
});
|
||||
|
||||
ipcMain.handle('connect', async (event, conn) => {
|
||||
ipcMain.handle('connect', async (event, conn: antares.ConnectionParams) => {
|
||||
const params = {
|
||||
host: conn.host,
|
||||
port: +conn.port,
|
||||
user: conn.user,
|
||||
password: conn.password,
|
||||
application_name: 'Antares SQL',
|
||||
readonly: conn.readonly
|
||||
readonly: conn.readonly,
|
||||
database: '',
|
||||
schema: '',
|
||||
databasePath: '',
|
||||
ssl: undefined as SslOptions,
|
||||
ssh: undefined as {
|
||||
host: string;
|
||||
username: string;
|
||||
password: string;
|
||||
port: number;
|
||||
privateKey: string;
|
||||
passphrase: string;
|
||||
}
|
||||
};
|
||||
|
||||
if (conn.database)
|
||||
@@ -81,10 +112,11 @@ export default connections => {
|
||||
|
||||
if (conn.ssl) {
|
||||
params.ssl = {
|
||||
key: conn.key ? fs.readFileSync(conn.key) : null,
|
||||
cert: conn.cert ? fs.readFileSync(conn.cert) : null,
|
||||
ca: conn.ca ? fs.readFileSync(conn.ca) : null,
|
||||
ciphers: conn.ciphers
|
||||
key: conn.key ? fs.readFileSync(conn.key).toString() : null,
|
||||
cert: conn.cert ? fs.readFileSync(conn.cert).toString() : null,
|
||||
ca: conn.ca ? fs.readFileSync(conn.ca).toString() : null,
|
||||
ciphers: conn.ciphers,
|
||||
rejectUnauthorized: !conn.untrustedConnection
|
||||
};
|
||||
}
|
||||
|
||||
@@ -94,13 +126,14 @@ export default connections => {
|
||||
username: conn.sshUser,
|
||||
password: conn.sshPass,
|
||||
port: conn.sshPort ? conn.sshPort : 22,
|
||||
privateKey: conn.sshKey ? fs.readFileSync(conn.sshKey) : null,
|
||||
privateKey: conn.sshKey ? fs.readFileSync(conn.sshKey).toString() : null,
|
||||
passphrase: conn.sshPassphrase
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const connection = ClientsFactory.getConnection({
|
||||
const connection = ClientsFactory.getClient({
|
||||
uid: conn.uid,
|
||||
client: conn.client,
|
||||
params,
|
||||
poolSize: 5
|
@@ -1,6 +1,7 @@
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
import { ipcMain } from 'electron';
|
||||
|
||||
export default (connections) => {
|
||||
export default (connections: {[key: string]: antares.Client}) => {
|
||||
ipcMain.handle('get-function-informations', async (event, params) => {
|
||||
try {
|
||||
const result = await connections[params.uid].getFunctionInformations(params);
|
@@ -1,3 +1,5 @@
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
|
||||
import connection from './connection';
|
||||
import tables from './tables';
|
||||
import views from './views';
|
||||
@@ -10,7 +12,7 @@ import application from './application';
|
||||
import schema from './schema';
|
||||
import users from './users';
|
||||
|
||||
const connections = {};
|
||||
const connections: {[key: string]: antares.Client} = {};
|
||||
|
||||
export default () => {
|
||||
connection(connections);
|
@@ -1,6 +1,7 @@
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
import { ipcMain } from 'electron';
|
||||
|
||||
export default (connections) => {
|
||||
export default (connections: {[key: string]: antares.Client}) => {
|
||||
ipcMain.handle('get-routine-informations', async (event, params) => {
|
||||
try {
|
||||
const result = await connections[params.uid].getRoutineInformations(params);
|
@@ -1,6 +1,7 @@
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
import { ipcMain } from 'electron';
|
||||
|
||||
export default (connections) => {
|
||||
export default (connections: {[key: string]: antares.Client}) => {
|
||||
ipcMain.handle('get-scheduler-informations', async (event, params) => {
|
||||
try {
|
||||
const result = await connections[params.uid].getEventInformations(params);
|
@@ -1,155 +0,0 @@
|
||||
|
||||
import { ipcMain } from 'electron';
|
||||
|
||||
export default connections => {
|
||||
ipcMain.handle('create-schema', async (event, params) => {
|
||||
try {
|
||||
await connections[params.uid].createSchema(params);
|
||||
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('update-schema', async (event, params) => {
|
||||
try {
|
||||
await connections[params.uid].alterSchema(params);
|
||||
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('delete-schema', async (event, params) => {
|
||||
try {
|
||||
await connections[params.uid].dropSchema(params);
|
||||
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-schema-collation', async (event, params) => {
|
||||
try {
|
||||
const collation = await connections[params.uid].getDatabaseCollation(params);
|
||||
|
||||
return { status: 'success', response: collation.rows.length ? collation.rows[0].DEFAULT_COLLATION_NAME : '' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-structure', async (event, params) => {
|
||||
try {
|
||||
const structure = await connections[params.uid].getStructure(params.schemas);
|
||||
|
||||
return { status: 'success', response: structure };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-collations', async (event, uid) => {
|
||||
try {
|
||||
const result = await connections[uid].getCollations();
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-variables', async (event, uid) => {
|
||||
try {
|
||||
const result = await connections[uid].getVariables();
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-engines', async (event, uid) => {
|
||||
try {
|
||||
const result = await connections[uid].getEngines();
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-version', async (event, uid) => {
|
||||
try {
|
||||
const result = await connections[uid].getVersion();
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-processes', async (event, uid) => {
|
||||
try {
|
||||
const result = await connections[uid].getProcesses();
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('kill-process', async (event, { uid, pid }) => {
|
||||
try {
|
||||
const result = await connections[uid].killProcess(pid);
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('use-schema', async (event, { uid, schema }) => {
|
||||
if (!schema) return;
|
||||
|
||||
try {
|
||||
await connections[uid].use(schema);
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('raw-query', async (event, { uid, query, schema }) => {
|
||||
if (!query) return;
|
||||
|
||||
try {
|
||||
const result = await connections[uid].raw(query, {
|
||||
nest: true,
|
||||
details: true,
|
||||
schema,
|
||||
comments: false
|
||||
});
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
};
|
390
src/main/ipc-handlers/schema.ts
Normal file
390
src/main/ipc-handlers/schema.ts
Normal file
@@ -0,0 +1,390 @@
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
import * as workers from 'common/interfaces/workers';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
import { ipcMain, dialog } from 'electron';
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||
|
||||
export default (connections: {[key: string]: antares.Client}) => {
|
||||
let exporter: ChildProcess = null;
|
||||
let importer: ChildProcess = null;
|
||||
|
||||
ipcMain.handle('create-schema', async (event, params) => {
|
||||
try {
|
||||
await connections[params.uid].createSchema(params);
|
||||
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('update-schema', async (event, params) => {
|
||||
try {
|
||||
await connections[params.uid].alterSchema(params);
|
||||
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('delete-schema', async (event, params) => {
|
||||
try {
|
||||
await connections[params.uid].dropSchema(params);
|
||||
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-schema-collation', async (event, params) => {
|
||||
try {
|
||||
const collation = await connections[params.uid].getDatabaseCollation(
|
||||
params
|
||||
);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
response: collation
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-structure', async (event, params) => {
|
||||
try {
|
||||
const structure = await connections[params.uid].getStructure(
|
||||
params.schemas
|
||||
);
|
||||
|
||||
return { status: 'success', response: structure };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-collations', async (event, uid) => {
|
||||
try {
|
||||
const result = await connections[uid].getCollations();
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-variables', async (event, uid) => {
|
||||
try {
|
||||
const result = await connections[uid].getVariables();
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-engines', async (event, uid) => {
|
||||
try {
|
||||
const result: unknown = await connections[uid].getEngines();
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-version', async (event, uid) => {
|
||||
try {
|
||||
const result = await connections[uid].getVersion();
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-processes', async (event, uid) => {
|
||||
try {
|
||||
const result = await connections[uid].getProcesses();
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('kill-process', async (event, { uid, pid }) => {
|
||||
try {
|
||||
const result = await connections[uid].killProcess(pid);
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('use-schema', async (event, { uid, schema }) => {
|
||||
if (!schema) return;
|
||||
|
||||
try {
|
||||
await connections[uid].use(schema);
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('raw-query', async (event, { uid, query, schema, tabUid, autocommit }) => {
|
||||
if (!query) return;
|
||||
|
||||
try {
|
||||
const result = await connections[uid].raw(query, {
|
||||
nest: true,
|
||||
details: true,
|
||||
schema,
|
||||
tabUid,
|
||||
autocommit,
|
||||
comments: false
|
||||
});
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('export', (event, { uid, type, tables, ...rest }) => {
|
||||
if (exporter !== null) {
|
||||
exporter.kill();
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise((resolve/*, reject */) => {
|
||||
(async () => {
|
||||
if (fs.existsSync(rest.outputFile)) { // If file exists ask for replace
|
||||
const result = await dialog.showMessageBox({
|
||||
type: 'warning',
|
||||
message: `File ${rest.outputFile} already exists. Do you want to replace it?`,
|
||||
detail: 'A file with the same name already exists in the target folder. Replacing it will overwrite its current contents.',
|
||||
buttons: ['Cancel', 'Replace'],
|
||||
defaultId: 0,
|
||||
cancelId: 0
|
||||
});
|
||||
|
||||
if (result.response !== 1) {
|
||||
resolve({
|
||||
type: 'error',
|
||||
response: 'Operation aborted'
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Init exporter process
|
||||
exporter = fork(isDevelopment ? './dist/exporter.js' : path.resolve(__dirname, './exporter.js'), [], {
|
||||
execArgv: isDevelopment ? ['--inspect=9224'] : undefined
|
||||
});
|
||||
exporter.send({
|
||||
type: 'init',
|
||||
client: {
|
||||
name: type,
|
||||
config: await connections[uid].getDbConfig()
|
||||
},
|
||||
tables,
|
||||
options: rest
|
||||
});
|
||||
|
||||
// Exporter message listener
|
||||
exporter.on('message', ({ type, payload }: workers.WorkerIpcMessage) => {
|
||||
switch (type) {
|
||||
case 'export-progress':
|
||||
event.sender.send('export-progress', payload);
|
||||
break;
|
||||
case 'end':
|
||||
setTimeout(() => { // Ensures that writing process has finished
|
||||
exporter.kill();
|
||||
exporter = null;
|
||||
}, 2000);
|
||||
resolve({ status: 'success', response: payload });
|
||||
break;
|
||||
case 'cancel':
|
||||
exporter.kill();
|
||||
exporter = null;
|
||||
resolve({ status: 'error', response: 'Operation cancelled' });
|
||||
break;
|
||||
case 'error':
|
||||
exporter.kill();
|
||||
exporter = null;
|
||||
resolve({ status: 'error', response: payload });
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
exporter.on('exit', code => {
|
||||
exporter = null;
|
||||
resolve({ status: 'error', response: `Operation ended with code: ${code}` });
|
||||
});
|
||||
})();
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.handle('abort-export', async () => {
|
||||
let willAbort = false;
|
||||
|
||||
if (exporter) {
|
||||
const result = await dialog.showMessageBox({
|
||||
type: 'warning',
|
||||
message: 'Are you sure you want to abort the export',
|
||||
buttons: ['Cancel', 'Abort'],
|
||||
defaultId: 0,
|
||||
cancelId: 0
|
||||
});
|
||||
|
||||
if (result.response === 1) {
|
||||
willAbort = true;
|
||||
exporter.send({ type: 'cancel' });
|
||||
}
|
||||
}
|
||||
|
||||
return { status: 'success', response: { willAbort } };
|
||||
});
|
||||
|
||||
ipcMain.handle('import-sql', async (event, options) => {
|
||||
if (importer !== null) {
|
||||
importer.kill();
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise((resolve/*, reject */) => {
|
||||
(async () => {
|
||||
const dbConfig = await connections[options.uid].getDbConfig();
|
||||
|
||||
// Init importer process
|
||||
importer = fork(isDevelopment ? './dist/importer.js' : path.resolve(__dirname, './importer.js'), [], {
|
||||
execArgv: isDevelopment ? ['--inspect=9224'] : undefined
|
||||
});
|
||||
importer.send({
|
||||
type: 'init',
|
||||
dbConfig,
|
||||
options
|
||||
});
|
||||
|
||||
// Importer message listener
|
||||
importer.on('message', ({ type, payload }: workers.WorkerIpcMessage) => {
|
||||
switch (type) {
|
||||
case 'import-progress':
|
||||
event.sender.send('import-progress', payload);
|
||||
break;
|
||||
case 'query-error':
|
||||
event.sender.send('query-error', payload);
|
||||
break;
|
||||
case 'end':
|
||||
setTimeout(() => { // Ensures that writing process has finished
|
||||
importer?.kill();
|
||||
importer = null;
|
||||
}, 2000);
|
||||
resolve({ status: 'success', response: payload });
|
||||
break;
|
||||
case 'cancel':
|
||||
importer.kill();
|
||||
importer = null;
|
||||
resolve({ status: 'error', response: 'Operation cancelled' });
|
||||
break;
|
||||
case 'error':
|
||||
importer.kill();
|
||||
importer = null;
|
||||
resolve({ status: 'error', response: payload });
|
||||
break;
|
||||
}
|
||||
});
|
||||
})();
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.handle('abort-import-sql', async () => {
|
||||
let willAbort = false;
|
||||
|
||||
if (importer) {
|
||||
const result = await dialog.showMessageBox({
|
||||
type: 'warning',
|
||||
message: 'Are you sure you want to abort the import',
|
||||
buttons: ['Cancel', 'Abort'],
|
||||
defaultId: 0,
|
||||
cancelId: 0
|
||||
});
|
||||
|
||||
if (result.response === 1) {
|
||||
willAbort = true;
|
||||
importer.send({ type: 'cancel' });
|
||||
}
|
||||
}
|
||||
|
||||
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() };
|
||||
}
|
||||
});
|
||||
};
|
@@ -1,12 +1,14 @@
|
||||
import * as fs from 'fs';
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
import { InsertRowsParams } from 'common/interfaces/tableApis';
|
||||
import { ipcMain } from 'electron';
|
||||
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';
|
||||
import { faker } from '@faker-js/faker';
|
||||
import * as moment from 'moment';
|
||||
import { sqlEscaper } from 'common/libs/sqlUtils';
|
||||
import { TEXT, LONG_TEXT, ARRAY, TEXT_SEARCH, NUMBER, FLOAT, BLOB, BIT, DATE, DATETIME, BOOLEAN } from 'common/fieldTypes';
|
||||
import customizations from 'common/customizations';
|
||||
|
||||
export default (connections) => {
|
||||
export default (connections: {[key: string]: antares.Client}) => {
|
||||
ipcMain.handle('get-table-columns', async (event, params) => {
|
||||
try {
|
||||
const result = await connections[params.uid].getTableColumns(params);
|
||||
@@ -102,9 +104,8 @@ export default (connections) => {
|
||||
escapedParam = `"${sqlEscaper(params.content)}"`;
|
||||
break;
|
||||
case 'pg':
|
||||
escapedParam = `'${params.content.replaceAll('\'', '\'\'')}'`;
|
||||
break;
|
||||
case 'sqlite':
|
||||
case 'firebird':
|
||||
escapedParam = `'${params.content.replaceAll('\'', '\'\'')}'`;
|
||||
break;
|
||||
}
|
||||
@@ -124,6 +125,7 @@ export default (connections) => {
|
||||
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||
break;
|
||||
case 'pg':
|
||||
case 'firebird':
|
||||
fileBlob = fs.readFileSync(params.content);
|
||||
escapedParam = `decode('${fileBlob.toString('hex')}', 'hex')`;
|
||||
break;
|
||||
@@ -141,6 +143,7 @@ export default (connections) => {
|
||||
escapedParam = '\'\'';
|
||||
break;
|
||||
case 'pg':
|
||||
case 'firebird':
|
||||
escapedParam = 'decode(\'\', \'hex\')';
|
||||
break;
|
||||
case 'sqlite':
|
||||
@@ -149,10 +152,23 @@ export default (connections) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ([...BIT].includes(params.type)) {
|
||||
else if (BIT.includes(params.type)) {
|
||||
escapedParam = `b'${sqlEscaper(params.content)}'`;
|
||||
reload = true;
|
||||
}
|
||||
else if (BOOLEAN.includes(params.type)) {
|
||||
switch (connections[params.uid]._client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
case 'pg':
|
||||
case 'firebird':
|
||||
escapedParam = params.content;
|
||||
break;
|
||||
case 'sqlite':
|
||||
escapedParam = Number(params.content === 'true');
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (params.content === null)
|
||||
escapedParam = 'NULL';
|
||||
else
|
||||
@@ -169,13 +185,18 @@ export default (connections) => {
|
||||
}
|
||||
else {
|
||||
const { orgRow } = params;
|
||||
delete orgRow._antares_id;
|
||||
|
||||
reload = true;
|
||||
|
||||
for (const key in orgRow) {
|
||||
if (typeof orgRow[key] === 'string')
|
||||
orgRow[key] = `'${orgRow[key]}'`;
|
||||
|
||||
orgRow[key] = `= ${orgRow[key]}`;
|
||||
if (orgRow[key] === null)
|
||||
orgRow[key] = `IS ${orgRow[key]}`;
|
||||
else
|
||||
orgRow[key] = `= ${orgRow[key]}`;
|
||||
}
|
||||
|
||||
await connections[params.uid]
|
||||
@@ -196,7 +217,8 @@ export default (connections) => {
|
||||
|
||||
ipcMain.handle('delete-table-rows', async (event, params) => {
|
||||
if (params.primary) {
|
||||
const idString = params.rows.map(row => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const idString = params.rows.map((row: {[key: string]: any}) => {
|
||||
const fieldName = Object.keys(row)[0].includes('.') ? `${params.table}.${params.primary}` : params.primary;
|
||||
|
||||
return typeof row[fieldName] === 'string'
|
||||
@@ -205,10 +227,11 @@ export default (connections) => {
|
||||
}).join(',');
|
||||
|
||||
try {
|
||||
const result = await connections[params.uid]
|
||||
const result: unknown = await connections[params.uid]
|
||||
.schema(params.schema)
|
||||
.delete(params.table)
|
||||
.where({ [params.primary]: `IN (${idString})` })
|
||||
.limit(params.rows.length)
|
||||
.run();
|
||||
|
||||
return { status: 'success', response: result };
|
||||
@@ -243,81 +266,12 @@ export default (connections) => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('insert-table-rows', async (event, params) => {
|
||||
ipcMain.handle('insert-table-fake-rows', async (event, params: InsertRowsParams) => {
|
||||
try { // TODO: move to client classes
|
||||
const insertObj = {};
|
||||
for (const key in params.row) {
|
||||
const type = params.fields[key];
|
||||
let escapedParam;
|
||||
|
||||
if (params.row[key] === null)
|
||||
escapedParam = 'NULL';
|
||||
else if ([...NUMBER, ...FLOAT].includes(type))
|
||||
escapedParam = +params.row[key];
|
||||
else if ([...TEXT, ...LONG_TEXT].includes(type)) {
|
||||
switch (connections[params.uid]._client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
escapedParam = `"${sqlEscaper(params.row[key].value)}"`;
|
||||
break;
|
||||
case 'pg':
|
||||
escapedParam = `'${params.row[key].value.replaceAll('\'', '\'\'')}'`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (BLOB.includes(type)) {
|
||||
if (params.row[key].value) {
|
||||
let fileBlob;
|
||||
|
||||
switch (connections[params.uid]._client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
fileBlob = fs.readFileSync(params.row[key].value);
|
||||
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||
break;
|
||||
case 'pg':
|
||||
fileBlob = fs.readFileSync(params.row[key].value);
|
||||
escapedParam = `decode('${fileBlob.toString('hex')}', 'hex')`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (connections[params.uid]._client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
escapedParam = '""';
|
||||
break;
|
||||
case 'pg':
|
||||
escapedParam = 'decode(\'\', \'hex\')';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
insertObj[key] = escapedParam;
|
||||
}
|
||||
|
||||
const rows = new Array(+params.repeat).fill(insertObj);
|
||||
|
||||
await connections[params.uid]
|
||||
.schema(params.schema)
|
||||
.into(params.table)
|
||||
.insert(rows)
|
||||
.run();
|
||||
|
||||
return { status: 'success' };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('insert-table-fake-rows', async (event, params) => {
|
||||
try { // TODO: move to client classes
|
||||
const rows = [];
|
||||
const rows: {[key: string]: string | number | boolean | Date | Buffer}[] = [];
|
||||
|
||||
for (let i = 0; i < +params.repeat; i++) {
|
||||
const insertObj = {};
|
||||
const insertObj: {[key: string]: string | number | boolean | Date | Buffer} = {};
|
||||
|
||||
for (const key in params.row) {
|
||||
const type = params.fields[key];
|
||||
@@ -335,6 +289,8 @@ export default (connections) => {
|
||||
escapedParam = `"${sqlEscaper(params.row[key].value)}"`;
|
||||
break;
|
||||
case 'pg':
|
||||
case 'sqlite':
|
||||
case 'firebird':
|
||||
escapedParam = `'${params.row[key].value.replaceAll('\'', '\'\'')}'`;
|
||||
break;
|
||||
}
|
||||
@@ -375,7 +331,7 @@ export default (connections) => {
|
||||
insertObj[key] = escapedParam;
|
||||
}
|
||||
else { // Faker value
|
||||
const parsedParams = {};
|
||||
const parsedParams: {[key: string]: string | number | boolean | Date | Buffer} = {};
|
||||
let fakeValue;
|
||||
|
||||
if (params.locale)
|
||||
@@ -386,14 +342,16 @@ export default (connections) => {
|
||||
if (!isNaN(params.row[key].params[param]))
|
||||
parsedParams[param] = +params.row[key].params[param];
|
||||
});
|
||||
fakeValue = faker[params.row[key].group][params.row[key].method](parsedParams);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
fakeValue = (faker as any)[params.row[key].group][params.row[key].method](parsedParams);
|
||||
}
|
||||
else
|
||||
fakeValue = faker[params.row[key].group][params.row[key].method]();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
fakeValue = (faker as any)[params.row[key].group][params.row[key].method]();
|
||||
|
||||
if (typeof fakeValue === 'string') {
|
||||
if (params.row[key].length)
|
||||
fakeValue = fakeValue.substr(0, params.row[key].length);
|
||||
fakeValue = fakeValue.substring(0, params.row[key].length);
|
||||
fakeValue = `'${sqlEscaper(fakeValue)}'`;
|
||||
}
|
||||
else if ([...DATE, ...DATETIME].includes(type))
|
||||
@@ -430,7 +388,20 @@ export default (connections) => {
|
||||
if (description)
|
||||
query.select(`LEFT(${description}, 20) AS foreign_description`);
|
||||
|
||||
const results = await query.run();
|
||||
const results = await query.run<{[key: string]: string}>();
|
||||
|
||||
const parsedResults: {[key: string]: string}[] = [];
|
||||
|
||||
for (const row of results.rows) {
|
||||
const remappedRow: {[key: string]: string} = {};
|
||||
|
||||
for (const key in row)
|
||||
remappedRow[key.toLowerCase()] = row[key];// Thanks Firebird -.-
|
||||
|
||||
parsedResults.push(remappedRow);
|
||||
}
|
||||
|
||||
results.rows = parsedResults;
|
||||
|
||||
return { status: 'success', response: results };
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
import { ipcMain } from 'electron';
|
||||
|
||||
export default (connections) => {
|
||||
export default (connections: {[key: string]: antares.Client}) => {
|
||||
ipcMain.handle('get-trigger-informations', async (event, params) => {
|
||||
try {
|
||||
const result = await connections[params.uid].getTriggerInformations(params);
|
@@ -1,11 +1,11 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
import Store from 'electron-store';
|
||||
const persistentStore = new Store({ name: 'settings' });
|
||||
import * as Store from 'electron-store';
|
||||
const persistentStore = new Store({ name: 'settings', clearInvalidConfig: true });
|
||||
const isMacOS = process.platform === 'darwin';
|
||||
|
||||
let mainWindow;
|
||||
autoUpdater.allowPrerelease = persistentStore.get('allow_prerelease', true);
|
||||
let mainWindow: Electron.IpcMainEvent;
|
||||
autoUpdater.allowPrerelease = persistentStore.get('allow_prerelease', true) as boolean;
|
||||
|
||||
export default () => {
|
||||
ipcMain.on('check-for-updates', event => {
|
||||
@@ -50,7 +50,7 @@ export default () => {
|
||||
mainWindow.reply('update-downloaded');
|
||||
});
|
||||
|
||||
autoUpdater.logger = require('electron-log');
|
||||
autoUpdater.logger.transports.console.format = '{h}:{i}:{s} {text}';
|
||||
autoUpdater.logger.transports.file.level = 'info';
|
||||
// autoUpdater.logger = require('electron-log');
|
||||
// autoUpdater.logger.transports.console.format = '{h}:{i}:{s} {text}';
|
||||
// autoUpdater.logger.transports.file.level = 'info';
|
||||
};
|
@@ -1,6 +1,7 @@
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
import { ipcMain } from 'electron';
|
||||
|
||||
export default (connections) => {
|
||||
export default (connections: {[key: string]: antares.Client}) => {
|
||||
ipcMain.handle('get-users', async (event, uid) => {
|
||||
try {
|
||||
const result = await connections[uid].getUsers();
|
@@ -1,6 +1,7 @@
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
import { ipcMain } from 'electron';
|
||||
|
||||
export default (connections) => {
|
||||
export default (connections: {[key: string]: antares.Client}) => {
|
||||
ipcMain.handle('get-view-informations', async (event, params) => {
|
||||
try {
|
||||
const result = await connections[params.uid].getViewInformations(params);
|
@@ -1,149 +0,0 @@
|
||||
'use strict';
|
||||
/**
|
||||
* As Simple As Possible Query Builder Core
|
||||
*
|
||||
* @class AntaresCore
|
||||
*/
|
||||
export class AntaresCore {
|
||||
/**
|
||||
* Creates an instance of AntaresCore.
|
||||
*
|
||||
* @param {Object} args connection params
|
||||
* @memberof AntaresCore
|
||||
*/
|
||||
constructor (args) {
|
||||
this._client = args.client;
|
||||
this._params = args.params;
|
||||
this._poolSize = args.poolSize || false;
|
||||
this._connection = null;
|
||||
this._ssh = null;
|
||||
this._logger = args.logger || console.log;
|
||||
|
||||
this._queryDefaults = {
|
||||
schema: '',
|
||||
select: [],
|
||||
from: '',
|
||||
where: [],
|
||||
groupBy: [],
|
||||
orderBy: [],
|
||||
limit: [],
|
||||
offset: [],
|
||||
join: [],
|
||||
update: [],
|
||||
insert: [],
|
||||
delete: false
|
||||
};
|
||||
this._query = Object.assign({}, this._queryDefaults);
|
||||
}
|
||||
|
||||
_reducer (acc, curr) {
|
||||
const type = typeof curr;
|
||||
|
||||
switch (type) {
|
||||
case 'number':
|
||||
case 'string':
|
||||
return [...acc, curr];
|
||||
case 'object':
|
||||
if (Array.isArray(curr))
|
||||
return [...acc, ...curr];
|
||||
else {
|
||||
const clausoles = [];
|
||||
for (const key in curr)
|
||||
clausoles.push(`${key} ${curr[key]}`);
|
||||
|
||||
return clausoles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the query object after a query
|
||||
*
|
||||
* @memberof AntaresCore
|
||||
*/
|
||||
_resetQuery () {
|
||||
this._query = Object.assign({}, this._queryDefaults);
|
||||
}
|
||||
|
||||
schema (schema) {
|
||||
this._query.schema = schema;
|
||||
return this;
|
||||
}
|
||||
|
||||
select (...args) {
|
||||
this._query.select = [...this._query.select, ...args];
|
||||
return this;
|
||||
}
|
||||
|
||||
from (table) {
|
||||
this._query.from = table;
|
||||
return this;
|
||||
}
|
||||
|
||||
into (table) {
|
||||
this._query.from = table;
|
||||
return this;
|
||||
}
|
||||
|
||||
delete (table) {
|
||||
this._query.delete = true;
|
||||
this.from(table);
|
||||
return this;
|
||||
}
|
||||
|
||||
where (...args) {
|
||||
this._query.where = [...this._query.where, ...args];
|
||||
return this;
|
||||
}
|
||||
|
||||
groupBy (...args) {
|
||||
this._query.groupBy = [...this._query.groupBy, ...args];
|
||||
return this;
|
||||
}
|
||||
|
||||
orderBy (...args) {
|
||||
this._query.orderBy = [...this._query.orderBy, ...args];
|
||||
return this;
|
||||
}
|
||||
|
||||
limit (...args) {
|
||||
this._query.limit = args;
|
||||
return this;
|
||||
}
|
||||
|
||||
offset (...args) {
|
||||
this._query.offset = args;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String | Array} args field = value
|
||||
* @returns
|
||||
* @memberof AntaresCore
|
||||
*/
|
||||
update (...args) {
|
||||
this._query.update = [...this._query.update, ...args];
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} arr Array of row objects
|
||||
* @returns
|
||||
* @memberof AntaresCore
|
||||
*/
|
||||
insert (arr) {
|
||||
this._query.insert = [...this._query.insert, ...arr];
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @returns {Promise}
|
||||
* @memberof AntaresCore
|
||||
*/
|
||||
run (args) {
|
||||
const rawQuery = this.getSQL();
|
||||
this._resetQuery();
|
||||
return this.raw(rawQuery, args);
|
||||
}
|
||||
}
|
266
src/main/libs/AntaresCore.ts
Normal file
266
src/main/libs/AntaresCore.ts
Normal file
@@ -0,0 +1,266 @@
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
import mysql from 'mysql2/promise';
|
||||
import * as pg from 'pg';
|
||||
import SSH2Promise from 'ssh2-promise';
|
||||
|
||||
const queryLogger = ({ sql, cUid }: {sql: string; cUid: string}) => {
|
||||
// Remove comments, newlines and multiple spaces
|
||||
const escapedSql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '').replace(/\s\s+/g, ' ');
|
||||
if (process.type !== undefined) {
|
||||
const mainWindow = require('electron').webContents.fromId(1);
|
||||
mainWindow.send('query-log', { cUid, sql: escapedSql, date: new Date() });
|
||||
}
|
||||
if (process.env.NODE_ENV === 'development') console.log(escapedSql);
|
||||
};
|
||||
|
||||
/**
|
||||
* As Simple As Possible Query Builder Core
|
||||
*/
|
||||
export abstract class AntaresCore {
|
||||
_client: antares.ClientCode;
|
||||
protected _cUid: string
|
||||
protected _params: mysql.ConnectionOptions | pg.ClientConfig | { databasePath: string; readonly: boolean};
|
||||
protected _poolSize: number;
|
||||
protected _ssh?: SSH2Promise;
|
||||
protected _logger: (args: {sql: string; cUid: string}) => void;
|
||||
protected _queryDefaults: antares.QueryBuilderObject;
|
||||
protected _query: antares.QueryBuilderObject;
|
||||
|
||||
constructor (args: antares.ClientParams) {
|
||||
this._client = args.client;
|
||||
this._cUid = args.uid;
|
||||
this._params = args.params;
|
||||
this._poolSize = args.poolSize || undefined;
|
||||
this._logger = args.logger || queryLogger;
|
||||
|
||||
this._queryDefaults = {
|
||||
schema: '',
|
||||
select: [],
|
||||
from: '',
|
||||
where: [],
|
||||
groupBy: [],
|
||||
orderBy: [],
|
||||
limit: null,
|
||||
offset: null,
|
||||
join: [],
|
||||
update: [],
|
||||
insert: [],
|
||||
delete: false
|
||||
};
|
||||
this._query = Object.assign({}, this._queryDefaults);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
protected _reducer (acc: string[], curr: any) {
|
||||
const type = typeof curr;
|
||||
|
||||
switch (type) {
|
||||
case 'number':
|
||||
case 'string':
|
||||
return [...acc, curr];
|
||||
case 'object':
|
||||
if (Array.isArray(curr))
|
||||
return [...acc, ...curr];
|
||||
else {
|
||||
const clausoles = [];
|
||||
for (const key in curr)
|
||||
clausoles.push(`${key} ${curr[key]}`);
|
||||
|
||||
return clausoles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _resetQuery () {
|
||||
this._query = Object.assign({}, this._queryDefaults);
|
||||
}
|
||||
|
||||
schema (schema: string) {
|
||||
this._query.schema = schema;
|
||||
return this;
|
||||
}
|
||||
|
||||
select (...args: string[]) {
|
||||
this._query.select = [...this._query.select, ...args];
|
||||
return this;
|
||||
}
|
||||
|
||||
from (table: string) {
|
||||
this._query.from = table;
|
||||
return this;
|
||||
}
|
||||
|
||||
into (table: string) {
|
||||
this._query.from = table;
|
||||
return this;
|
||||
}
|
||||
|
||||
delete (table: string) {
|
||||
this._query.delete = true;
|
||||
this.from(table);
|
||||
return this;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
where (...args: any) {
|
||||
this._query.where = [...this._query.where, ...args];
|
||||
return this;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
groupBy (...args: any) {
|
||||
this._query.groupBy = [...this._query.groupBy, ...args];
|
||||
return this;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
orderBy (...args: any) {
|
||||
this._query.orderBy = [...this._query.orderBy, ...args];
|
||||
return this;
|
||||
}
|
||||
|
||||
limit (limit: number) {
|
||||
this._query.limit = limit;
|
||||
return this;
|
||||
}
|
||||
|
||||
offset (offset: number) {
|
||||
this._query.offset = offset;
|
||||
return this;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
update (...args: any) {
|
||||
this._query.update = [...this._query.update, ...args];
|
||||
return this;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
insert (arr: {[key: string]: any}[]) {
|
||||
this._query.insert = [...this._query.insert, ...arr];
|
||||
return this;
|
||||
}
|
||||
|
||||
getSQL (): string {
|
||||
throw new Error('Client must implement the "getSQL" method');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
raw<T = antares.QueryResult> (_sql: string, _args?: antares.QueryParams): Promise<T> {
|
||||
throw new Error('Client must implement the "raw" method');
|
||||
}
|
||||
|
||||
run<RowType> (args?: antares.QueryParams) {
|
||||
const rawQuery = this.getSQL();
|
||||
this._resetQuery();
|
||||
return this.raw<antares.QueryResult<RowType>>(rawQuery, args);
|
||||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
getDbConfig () {
|
||||
throw new Error('Method "getDbConfig" not implemented');
|
||||
}
|
||||
|
||||
createSchema (...args: any) {
|
||||
throw new Error('Method "createSchema" not implemented');
|
||||
}
|
||||
|
||||
alterSchema (...args: any) {
|
||||
throw new Error('Method "alterSchema" not implemented');
|
||||
}
|
||||
|
||||
dropSchema (...args: any) {
|
||||
throw new Error('Method "dropSchema" not implemented');
|
||||
}
|
||||
|
||||
getDatabaseCollation (...args: any) {
|
||||
throw new Error('Method "getDatabaseCollation" not implemented');
|
||||
}
|
||||
|
||||
getFunctionInformations (...args: any) {
|
||||
throw new Error('Method "getFunctionInformations" not implemented');
|
||||
}
|
||||
|
||||
alterFunction (...args: any) {
|
||||
throw new Error('Method "alterFunction" not implemented');
|
||||
}
|
||||
|
||||
createTriggerFunction (...args: any) {
|
||||
throw new Error('Method "createTriggerFunction" not implemented');
|
||||
}
|
||||
|
||||
alterTriggerFunction (...args: any) {
|
||||
throw new Error('Method "alterTriggerFunction" not implemented');
|
||||
}
|
||||
|
||||
createFunction (...args: any) {
|
||||
throw new Error('Method "createFunction" not implemented');
|
||||
}
|
||||
|
||||
dropFunction (...args: any) {
|
||||
throw new Error('Method "dropFunction" not implemented');
|
||||
}
|
||||
|
||||
getCollations () {
|
||||
throw new Error('Method "getCollations" not implemented');
|
||||
}
|
||||
|
||||
getRoutineInformations (...args: any) {
|
||||
throw new Error('Method "getRoutineInformations" not implemented');
|
||||
}
|
||||
|
||||
dropRoutine (...args: any) {
|
||||
throw new Error('Method "dropRoutine" not implemented');
|
||||
}
|
||||
|
||||
alterRoutine (...args: any) {
|
||||
throw new Error('Method "alterRoutine" not implemented');
|
||||
}
|
||||
|
||||
createRoutine (...args: any) {
|
||||
throw new Error('Method "createRoutine" not implemented');
|
||||
}
|
||||
|
||||
getVariables () {
|
||||
throw new Error('Method "getVariables" not implemented');
|
||||
}
|
||||
|
||||
getEventInformations (...args: any) {
|
||||
throw new Error('Method "getEventInformations" not implemented');
|
||||
}
|
||||
|
||||
dropEvent (...args: any) {
|
||||
throw new Error('Method "dropEvent" not implemented');
|
||||
}
|
||||
|
||||
alterEvent (...args: any) {
|
||||
throw new Error('Method "alterEvent" not implemented');
|
||||
}
|
||||
|
||||
createEvent (...args: any) {
|
||||
throw new Error('Method "createEvent" not implemented');
|
||||
}
|
||||
|
||||
enableEvent (...args: any) {
|
||||
throw new Error('Method "enableEvent" not implemented');
|
||||
}
|
||||
|
||||
disableEvent (...args: any) {
|
||||
throw new Error('Method "disableEvent" not implemented');
|
||||
}
|
||||
|
||||
enableTrigger (...args: any) {
|
||||
throw new Error('Method "enableTrigger" not implemented');
|
||||
}
|
||||
|
||||
disableTrigger (...args: any) {
|
||||
throw new Error('Method "disableTrigger" not implemented');
|
||||
}
|
||||
|
||||
killTabQuery (...args: any) {
|
||||
throw new Error('Method "killTabQuery" not implemented');
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
}
|
@@ -1,47 +0,0 @@
|
||||
'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
|
||||
const escapedSql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '').replace(/\s\s+/g, ' ');
|
||||
console.log(escapedSql);
|
||||
};
|
||||
|
||||
export class ClientsFactory {
|
||||
/**
|
||||
* Returns a database connection based on received args.
|
||||
*
|
||||
* @param {Object} args
|
||||
* @param {String} args.client
|
||||
* @param {Object} args.params
|
||||
* @param {String} args.params.host
|
||||
* @param {Number} args.params.port
|
||||
* @param {String} args.params.password
|
||||
* @param {String=} args.params.database
|
||||
* @param {String=} args.params.schema
|
||||
* @param {String} args.params.ssh.host
|
||||
* @param {String} args.params.ssh.username
|
||||
* @param {String} args.params.ssh.password
|
||||
* @param {Number} args.params.ssh.port
|
||||
* @param {Number=} args.poolSize
|
||||
* @returns Database Connection
|
||||
* @memberof ClientsFactory
|
||||
*/
|
||||
static getConnection (args) {
|
||||
args.logger = queryLogger;
|
||||
|
||||
switch (args.client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
}
|
23
src/main/libs/ClientsFactory.ts
Normal file
23
src/main/libs/ClientsFactory.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
import { MySQLClient } from './clients/MySQLClient';
|
||||
import { PostgreSQLClient } from './clients/PostgreSQLClient';
|
||||
import { SQLiteClient } from './clients/SQLiteClient';
|
||||
import { FirebirdSQLClient } from './clients/FirebirdSQLClient';
|
||||
|
||||
export class ClientsFactory {
|
||||
static getClient (args: antares.ClientParams) {
|
||||
switch (args.client) {
|
||||
case 'mysql':
|
||||
case 'maria':
|
||||
return new MySQLClient(args);
|
||||
case 'pg':
|
||||
return new PostgreSQLClient(args);
|
||||
case 'sqlite':
|
||||
return new SQLiteClient(args);
|
||||
case 'firebird':
|
||||
return new FirebirdSQLClient(args);
|
||||
default:
|
||||
throw new Error(`Unknown database client: ${args.client}`);
|
||||
}
|
||||
}
|
||||
}
|
139
src/main/libs/ShortcutRegister.ts
Normal file
139
src/main/libs/ShortcutRegister.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { BrowserWindow, globalShortcut, Menu, MenuItem, MenuItemConstructorOptions } from 'electron';
|
||||
import * as Store from 'electron-store';
|
||||
import { ShortcutRecord, shortcuts } from 'common/shortcuts';
|
||||
|
||||
const shortcutsStore = new Store({ name: 'shortcuts' });
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||
const defaultShortcuts = shortcuts.filter(s => s.os.includes(process.platform));
|
||||
|
||||
export type ShortcutMode = 'local' | 'global'
|
||||
export type OsMenu = {
|
||||
[key in NodeJS.Platform]?: MenuItemConstructorOptions[];
|
||||
};
|
||||
|
||||
export class ShortcutRegister {
|
||||
private _shortcuts: ShortcutRecord[];
|
||||
private _mainWindow: BrowserWindow;
|
||||
private _menu: Menu;
|
||||
private _menuTemplate: OsMenu;
|
||||
private _mode: ShortcutMode;
|
||||
private static _instance: ShortcutRegister;
|
||||
|
||||
private constructor (args: { mainWindow: BrowserWindow; menuTemplate?: OsMenu; mode: ShortcutMode }) {
|
||||
this._mainWindow = args.mainWindow;
|
||||
this._menuTemplate = args.menuTemplate || {};
|
||||
this._mode = args.mode;
|
||||
this.shortcuts = shortcutsStore.get('shortcuts', defaultShortcuts) as ShortcutRecord[];
|
||||
}
|
||||
|
||||
public static getInstance (args?: { mainWindow?: BrowserWindow; menuTemplate?: OsMenu; mode?: ShortcutMode }) {
|
||||
if (!ShortcutRegister._instance && args.menuTemplate !== undefined && args.mode !== undefined) {
|
||||
ShortcutRegister._instance = new ShortcutRegister({
|
||||
mainWindow: args.mainWindow,
|
||||
menuTemplate: args.menuTemplate,
|
||||
mode: args.mode
|
||||
});
|
||||
}
|
||||
|
||||
return ShortcutRegister._instance;
|
||||
}
|
||||
|
||||
get shortcuts () {
|
||||
return this._shortcuts;
|
||||
}
|
||||
|
||||
private set shortcuts (value: ShortcutRecord[]) {
|
||||
this._shortcuts = value;
|
||||
shortcutsStore.set('shortcuts', value);
|
||||
}
|
||||
|
||||
init () {
|
||||
this._mainWindow.webContents.send('update-shortcuts', this.shortcuts);
|
||||
|
||||
this.buildBaseMenu();
|
||||
|
||||
if (this._mode === 'global')
|
||||
this.setGlobalShortcuts();
|
||||
else if (this._mode === 'local')
|
||||
this.setLocalShortcuts();
|
||||
else
|
||||
throw new Error(`Unknown mode "${this._mode}"`);
|
||||
|
||||
Menu.setApplicationMenu(this._menu);
|
||||
}
|
||||
|
||||
private buildBaseMenu () {
|
||||
if (Object.keys(this._menuTemplate).includes(process.platform))
|
||||
this._menu = Menu.buildFromTemplate(this._menuTemplate[process.platform]);
|
||||
else
|
||||
this._menu = new Menu();
|
||||
}
|
||||
|
||||
private setLocalShortcuts () {
|
||||
for (const shortcut of this.shortcuts) {
|
||||
if (shortcut.os.includes(process.platform)) {
|
||||
for (const key of shortcut.keys) {
|
||||
try {
|
||||
this._menu.append(new MenuItem({
|
||||
label: 'Shortcuts',
|
||||
visible: false,
|
||||
submenu: [{
|
||||
label: String(key),
|
||||
accelerator: key,
|
||||
visible: false,
|
||||
click: () => {
|
||||
this._mainWindow.webContents.send(shortcut.event);
|
||||
if (isDevelopment) console.log('LOCAL EVENT:', shortcut);
|
||||
}
|
||||
}]
|
||||
}));
|
||||
}
|
||||
catch (error) {
|
||||
if (isDevelopment) console.log(error);
|
||||
this.restoreDefaults();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setGlobalShortcuts () {
|
||||
for (const shortcut of this.shortcuts) {
|
||||
if (shortcut.os.includes(process.platform)) {
|
||||
for (const key of shortcut.keys) {
|
||||
try {
|
||||
globalShortcut.register(key, () => {
|
||||
this._mainWindow.webContents.send(shortcut.event);
|
||||
if (isDevelopment) console.log('GLOBAL EVENT:', shortcut);
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if (isDevelopment) console.log(error);
|
||||
this.restoreDefaults();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reload () {
|
||||
this.unregister();
|
||||
this.init();
|
||||
}
|
||||
|
||||
updateShortcuts (shortcuts: ShortcutRecord[]) {
|
||||
this.shortcuts = shortcuts;
|
||||
this.reload();
|
||||
}
|
||||
|
||||
restoreDefaults () {
|
||||
this.shortcuts = defaultShortcuts;
|
||||
this.reload();
|
||||
}
|
||||
|
||||
unregister () {
|
||||
if (this._mode === 'global') globalShortcut.unregisterAll();
|
||||
}
|
||||
}
|
1247
src/main/libs/clients/FirebirdSQLClient.ts
Normal file
1247
src/main/libs/clients/FirebirdSQLClient.ts
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,66 +1,77 @@
|
||||
'use strict';
|
||||
import sqlite from 'better-sqlite3';
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
import * as sqlite from 'better-sqlite3';
|
||||
import { AntaresCore } from '../AntaresCore';
|
||||
import dataTypes from 'common/data-types/mysql';
|
||||
import dataTypes from 'common/data-types/sqlite';
|
||||
import { NUMBER, FLOAT, TIME, DATETIME } from 'common/fieldTypes';
|
||||
|
||||
export class SQLiteClient extends AntaresCore {
|
||||
constructor (args) {
|
||||
private _schema?: string;
|
||||
private _connectionsToCommit: Map<string, sqlite.Database>;
|
||||
protected _connection?: sqlite.Database;
|
||||
_params: { databasePath: string; readonly: boolean};
|
||||
|
||||
constructor (args: antares.ClientParams) {
|
||||
super(args);
|
||||
|
||||
this._schema = null;
|
||||
this._connectionsToCommit = new Map();
|
||||
}
|
||||
|
||||
_getTypeInfo (type) {
|
||||
getTypeInfo (type: string): antares.TypeInformations {
|
||||
return dataTypes
|
||||
.reduce((acc, group) => [...acc, ...group.types], [])
|
||||
.filter(_type => _type.name === type.toUpperCase())[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async connect () {
|
||||
this._connection = sqlite(this._params.databasePath, {
|
||||
this._connection = this.getConnection();
|
||||
}
|
||||
|
||||
getConnection () {
|
||||
return sqlite(this._params.databasePath, {
|
||||
fileMustExist: true,
|
||||
readonly: this._params.readonly
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
destroy () {}
|
||||
destroy (): void {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an USE query
|
||||
*
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
use () {}
|
||||
use (): void {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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');
|
||||
async getStructure (schemas: Set<string>) {
|
||||
/* eslint-disable camelcase */
|
||||
interface ShowTableResult {
|
||||
Db?: string;
|
||||
type: string;
|
||||
name: string;
|
||||
tbl_name: string;
|
||||
rootpage:4;
|
||||
sql: string;
|
||||
}
|
||||
|
||||
type ShowTriggersResult = ShowTableResult
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
const { rows: databases } = await this.raw<antares.QueryResult<{ name: string}>>('SELECT * FROM pragma_database_list');
|
||||
|
||||
const filteredDatabases = databases;
|
||||
|
||||
const tablesArr = [];
|
||||
const triggersArr = [];
|
||||
const tablesArr: ShowTableResult[] = [];
|
||||
const triggersArr: ShowTriggersResult[] = [];
|
||||
let schemaSize = 0;
|
||||
|
||||
for (const db of filteredDatabases) {
|
||||
if (!schemas.has(db.name)) continue;
|
||||
|
||||
let { rows: tables } = await this.raw(`
|
||||
let { rows: tables } = await this.raw<antares.QueryResult<ShowTableResult>>(`
|
||||
SELECT *
|
||||
FROM "${db.name}".sqlite_master
|
||||
WHERE type IN ('table', 'view')
|
||||
AND name NOT LIKE 'sqlite_%'
|
||||
AND name NOT LIKE 'sqlite\\_%' ESCAPE '\\'
|
||||
ORDER BY name
|
||||
`);
|
||||
if (tables.length) {
|
||||
@@ -71,7 +82,7 @@ export class SQLiteClient extends AntaresCore {
|
||||
tablesArr.push(...tables);
|
||||
}
|
||||
|
||||
let { rows: triggers } = await this.raw(`SELECT * FROM "${db.name}".sqlite_master WHERE type='trigger'`);
|
||||
let { rows: triggers } = await this.raw<antares.QueryResult<ShowTriggersResult>>(`SELECT * FROM "${db.name}".sqlite_master WHERE type='trigger'`);
|
||||
if (triggers.length) {
|
||||
triggers = triggers.map(trigger => {
|
||||
trigger.Db = db.name;
|
||||
@@ -128,22 +139,24 @@ export class SQLiteClient extends AntaresCore {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @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}')`);
|
||||
async getTableColumns ({ schema, table }: { schema: string; table: string }) {
|
||||
interface TableColumnsResult {
|
||||
cid: number;
|
||||
name: string;
|
||||
type: string;
|
||||
notnull: 0 | 1;
|
||||
// eslint-disable-next-line camelcase
|
||||
dflt_value: string;
|
||||
pk: 0 | 1;
|
||||
}
|
||||
const { rows: fields } = await this.raw<antares.QueryResult<TableColumnsResult>>(`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;
|
||||
const [type, length]: [string, number?] = field.type.includes('(')
|
||||
? field.type.replace(')', '').split('(').map((el: string | number) => {
|
||||
if (!isNaN(Number(el))) el = Number(el);
|
||||
return el;
|
||||
})
|
||||
}) as [string, number?]
|
||||
: [field.type, null];
|
||||
|
||||
return {
|
||||
@@ -158,7 +171,7 @@ export class SQLiteClient extends AntaresCore {
|
||||
nullable: !field.notnull,
|
||||
unsigned: null,
|
||||
zerofill: null,
|
||||
order: field.cid + 1,
|
||||
order: typeof field.cid === 'string' ? +field.cid + 1 : field.cid + 1,
|
||||
default: field.dflt_value,
|
||||
charset: null,
|
||||
collation: null,
|
||||
@@ -169,54 +182,46 @@ export class SQLiteClient extends AntaresCore {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} params
|
||||
* @param {String} params.schema
|
||||
* @param {String} params.table
|
||||
* @returns {Object} table row count
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getTableApproximateCount ({ schema, table }) {
|
||||
async getTableApproximateCount ({ schema, table }: { schema: string; table: string }): Promise<number> {
|
||||
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 }) {
|
||||
async getTableOptions ({ table }: { table: string }) {
|
||||
return { name: table };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} params
|
||||
* @param {String} params.schema
|
||||
* @param {String} params.table
|
||||
* @returns {Object} table indexes
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getTableIndexes ({ schema, table }) {
|
||||
async getTableIndexes ({ schema, table }: { schema: string; table: string }) {
|
||||
interface TableColumnsResult {
|
||||
type: string;
|
||||
name: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
tbl_name: string;
|
||||
rootpage:4;
|
||||
sql: string;
|
||||
}
|
||||
|
||||
interface ShowIndexesResult {
|
||||
seq: number;
|
||||
name: string;
|
||||
unique: 0 | 1;
|
||||
origin: string;
|
||||
partial: 0 | 1;
|
||||
}
|
||||
|
||||
const remappedIndexes = [];
|
||||
const { rows: primaryKeys } = await this.raw(`SELECT * FROM "${schema}".pragma_table_info('${table}') WHERE pk != 0`);
|
||||
const { rows: primaryKeys } = await this.raw<antares.QueryResult<TableColumnsResult>>(`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: ''
|
||||
type: 'PRIMARY'
|
||||
});
|
||||
}
|
||||
|
||||
const { rows: indexes } = await this.raw(`SELECT * FROM "${schema}".pragma_index_list('${table}');`);
|
||||
const { rows: indexes } = await this.raw<antares.QueryResult<ShowIndexesResult>>(`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}');`);
|
||||
@@ -225,9 +230,9 @@ export class SQLiteClient extends AntaresCore {
|
||||
remappedIndexes.push({
|
||||
name: index.name,
|
||||
column: detail.name,
|
||||
indexType: null,
|
||||
indexType: null as never,
|
||||
type: index.unique === 1 ? 'UNIQUE' : 'INDEX',
|
||||
cardinality: null,
|
||||
cardinality: null as never,
|
||||
comment: '',
|
||||
indexComment: ''
|
||||
});
|
||||
@@ -237,15 +242,19 @@ export class SQLiteClient extends AntaresCore {
|
||||
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}');`);
|
||||
async getKeyUsage ({ schema, table }: { schema: string; table: string }) {
|
||||
/* eslint-disable camelcase */
|
||||
interface KeyResult {
|
||||
from: string;
|
||||
id: number;
|
||||
table: string;
|
||||
to: string;
|
||||
on_update: string;
|
||||
on_delete: string;
|
||||
}
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
const { rows } = await this.raw<antares.QueryResult<KeyResult>>(`SELECT * FROM "${schema}".pragma_foreign_key_list('${table}');`);
|
||||
|
||||
return rows.map(field => {
|
||||
return {
|
||||
@@ -264,195 +273,11 @@ export class SQLiteClient extends AntaresCore {
|
||||
});
|
||||
}
|
||||
|
||||
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];
|
||||
async getUsers (): Promise<void> {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 () {}
|
||||
|
||||
/**
|
||||
* CREATE TABLE
|
||||
*
|
||||
* @returns {Promise<null>}
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async createTable (params) {
|
||||
async createTable (params: antares.CreateTableParams) {
|
||||
const {
|
||||
schema,
|
||||
fields,
|
||||
@@ -460,24 +285,24 @@ export class SQLiteClient extends AntaresCore {
|
||||
indexes,
|
||||
options
|
||||
} = params;
|
||||
const newColumns = [];
|
||||
const newIndexes = [];
|
||||
const manageIndexes = [];
|
||||
const newForeigns = [];
|
||||
const newColumns: string[] = [];
|
||||
const newIndexes: string[] = [];
|
||||
const manageIndexes: string[] = [];
|
||||
const newForeigns: string[] = [];
|
||||
|
||||
let sql = `CREATE TABLE "${schema}"."${options.name}"`;
|
||||
|
||||
// ADD FIELDS
|
||||
fields.forEach(field => {
|
||||
const typeInfo = this._getTypeInfo(field.type);
|
||||
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.type.toUpperCase()}${length ? `(${length})` : ''}
|
||||
${field.unsigned ? 'UNSIGNED' : ''}
|
||||
${field.nullable ? 'NULL' : 'NOT NULL'}
|
||||
${field.autoIncrement ? 'AUTO_INCREMENT' : ''}
|
||||
${field.default ? `DEFAULT ${field.default}` : ''}
|
||||
${field.default !== null ? `DEFAULT ${field.default || '\'\''}` : ''}
|
||||
${field.onUpdate ? `ON UPDATE ${field.onUpdate}` : ''}`);
|
||||
});
|
||||
|
||||
@@ -503,34 +328,37 @@ export class SQLiteClient extends AntaresCore {
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* ALTER TABLE
|
||||
*
|
||||
* @returns {Promise<null>}
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async alterTable (params) {
|
||||
async alterTable (params: antares.AlterTableParams) {
|
||||
const {
|
||||
table,
|
||||
schema,
|
||||
additions,
|
||||
deletions,
|
||||
changes,
|
||||
tableStructure
|
||||
} = 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}"`);
|
||||
const tmpName = `Antares_${table}_tmp`;
|
||||
await this.raw(`CREATE TABLE "${tmpName}" AS SELECT * FROM "${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 }
|
||||
schema: schema,
|
||||
fields: tableStructure.fields,
|
||||
foreigns: tableStructure.foreigns,
|
||||
indexes: tableStructure.indexes.filter(index => !index.name.includes('sqlite_autoindex')),
|
||||
options: { name: 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)
|
||||
additions.every(add => add.name !== field.name) &&
|
||||
deletions.every(del => del.name !== field.name)
|
||||
);
|
||||
})
|
||||
.reduce((acc, curr) => {
|
||||
@@ -539,7 +367,7 @@ export class SQLiteClient extends AntaresCore {
|
||||
}, []);
|
||||
|
||||
const selectFields = insertFields.map(field => {
|
||||
const renamedField = params.changes.find(change => `"${change.name}"` === field);
|
||||
const renamedField = changes.find(change => `"${change.name}"` === field);
|
||||
if (renamedField)
|
||||
return `"${renamedField.orgName}"`;
|
||||
return field;
|
||||
@@ -547,7 +375,7 @@ export class SQLiteClient extends AntaresCore {
|
||||
|
||||
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.dropTable({ schema: schema, table: tmpName });
|
||||
await this.raw('PRAGMA foreign_keys = 1');
|
||||
await this.raw('COMMIT');
|
||||
}
|
||||
@@ -557,43 +385,147 @@ export class SQLiteClient extends AntaresCore {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DUPLICATE TABLE
|
||||
*
|
||||
* @returns {Promise<null>}
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async duplicateTable (params) { // TODO: retrive table informations and create a copy
|
||||
async duplicateTable (params: { schema: string; table: string }) { // 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) {
|
||||
async truncateTable (params: { schema: string; table: string }) {
|
||||
const sql = `DELETE FROM "${params.schema}"."${params.table}"`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* DROP TABLE
|
||||
*
|
||||
* @returns {Promise<null>}
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async dropTable (params) {
|
||||
async dropTable (params: { schema: string; table: string }) {
|
||||
const sql = `DROP TABLE "${params.schema}"."${params.table}"`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {String} SQL string
|
||||
* @memberof SQLiteClient
|
||||
*/
|
||||
async getViewInformations ({ schema, view }: { schema: string; view: string }) {
|
||||
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];
|
||||
}
|
||||
|
||||
async dropView (params: { schema: string; view: string }) {
|
||||
const sql = `DROP VIEW "${params.schema}"."${params.view}"`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
async alterView ({ view }: { view: antares.AlterViewParams }) {
|
||||
try {
|
||||
await this.dropView({ schema: view.schema, view: view.oldName });
|
||||
await this.createView(view);
|
||||
}
|
||||
catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
async createView (params: antares.CreateViewParams) {
|
||||
const sql = `CREATE VIEW "${params.schema}"."${params.name}" AS ${params.sql}`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
async getTriggerInformations ({ schema, trigger }: { schema: string; trigger: string }) {
|
||||
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];
|
||||
}
|
||||
|
||||
async dropTrigger (params: { schema: string; trigger: string }) {
|
||||
const sql = `DROP TRIGGER \`${params.schema}\`.\`${params.trigger}\``;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
async alterTrigger ({ trigger } : {trigger: antares.AlterTriggerParams}) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
async createTrigger (params: antares.CreateTriggerParams) {
|
||||
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 });
|
||||
}
|
||||
|
||||
async getEngines () {
|
||||
return {
|
||||
name: 'SQLite',
|
||||
support: 'YES',
|
||||
comment: '',
|
||||
isDefault: true
|
||||
};
|
||||
}
|
||||
|
||||
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 (): Promise<void> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async killProcess (): Promise<void> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async commitTab (tabUid: string) {
|
||||
const connection = this._connectionsToCommit.get(tabUid);
|
||||
if (connection) {
|
||||
connection.prepare('COMMIT').run();
|
||||
return this.destroyConnectionToCommit(tabUid);
|
||||
}
|
||||
}
|
||||
|
||||
async rollbackTab (tabUid: string) {
|
||||
const connection = this._connectionsToCommit.get(tabUid);
|
||||
if (connection) {
|
||||
connection.prepare('ROLLBACK').run();
|
||||
return this.destroyConnectionToCommit(tabUid);
|
||||
}
|
||||
}
|
||||
|
||||
destroyConnectionToCommit (tabUid: string) {
|
||||
const connection = this._connectionsToCommit.get(tabUid);
|
||||
if (connection) {
|
||||
connection.close();
|
||||
this._connectionsToCommit.delete(tabUid);
|
||||
}
|
||||
}
|
||||
|
||||
getSQL () {
|
||||
// SELECT
|
||||
const selectArray = this._query.select.reduce(this._reducer, []);
|
||||
@@ -641,31 +573,23 @@ export class SQLiteClient extends AntaresCore {
|
||||
const orderByRaw = orderByArray.length ? `ORDER BY ${orderByArray.join(', ')} ` : '';
|
||||
|
||||
// LIMIT
|
||||
const limitRaw = this._query.limit.length ? `LIMIT ${this._query.limit.join(', ')} ` : '';
|
||||
const limitRaw = this._query.limit ? `LIMIT ${this._query.limit} ` : '';
|
||||
|
||||
// OFFSET
|
||||
const offsetRaw = this._query.offset.length ? `OFFSET ${this._query.offset.join(', ')} ` : '';
|
||||
const offsetRaw = this._query.offset ? `OFFSET ${this._query.offset} ` : '';
|
||||
|
||||
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
|
||||
async raw<T = antares.QueryResult> (sql: string, args?: antares.QueryParams) {
|
||||
this._logger({ cUid: this._cUid, sql });// TODO: replace BLOB content with a placeholder
|
||||
|
||||
args = {
|
||||
nest: false,
|
||||
details: false,
|
||||
split: true,
|
||||
comments: true,
|
||||
autocommit: true,
|
||||
...args
|
||||
};
|
||||
|
||||
@@ -679,37 +603,53 @@ export class SQLiteClient extends AntaresCore {
|
||||
.filter(Boolean)
|
||||
.map(q => q.trim())
|
||||
: [sql];
|
||||
const connection = this._connection;
|
||||
|
||||
let connection: sqlite.Database;
|
||||
|
||||
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 keysArr: antares.QueryForeign[] = [];
|
||||
|
||||
const { rows, report, fields, keys, duration } = await new Promise((resolve, reject) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const { rows, report, fields, keys, duration }: any = await new Promise((resolve, reject) => {
|
||||
(async () => {
|
||||
let queryResult;
|
||||
let queryRunResult: sqlite.RunResult;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let queryAllResult: any[];
|
||||
let affectedRows;
|
||||
let fields;
|
||||
const detectedTypes = {};
|
||||
const detectedTypes: {[key: string]: string} = {};
|
||||
|
||||
try {
|
||||
const stmt = connection.prepare(query);
|
||||
|
||||
if (stmt.reader) {
|
||||
queryResult = stmt.all();
|
||||
queryAllResult = stmt.all();
|
||||
fields = stmt.columns();
|
||||
|
||||
if (queryResult.length) {
|
||||
if (queryAllResult.length) {
|
||||
fields.forEach(field => {
|
||||
detectedTypes[field.name] = typeof queryResult[0][field.name];
|
||||
detectedTypes[field.name] = typeof queryAllResult[0][field.name];
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
const info = queryResult = stmt.run();
|
||||
affectedRows = info.changes;
|
||||
queryRunResult = stmt.run();
|
||||
affectedRows = queryRunResult.changes;
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
@@ -720,19 +660,19 @@ export class SQLiteClient extends AntaresCore {
|
||||
|
||||
let remappedFields = fields
|
||||
? fields.map(field => {
|
||||
let [parsedType, length] = field.type?.includes('(')
|
||||
? field.type.replace(')', '').split('(').map(el => {
|
||||
if (!isNaN(el))
|
||||
el = +el;
|
||||
let [parsedType, length]: [string, number?] = field.type?.includes('(')
|
||||
? field.type.replace(')', '').split('(').map((el: string | number) => {
|
||||
if (!isNaN(Number(el)))
|
||||
el = Number(el);
|
||||
else
|
||||
el = el.trim();
|
||||
el = (el as string).trim();
|
||||
return el;
|
||||
})
|
||||
}) as [string, number?]
|
||||
: [field.type, null];
|
||||
|
||||
if ([...TIME, ...DATETIME].includes(parsedType)) {
|
||||
const firstNotNull = queryResult.find(res => res[field.name] !== null);
|
||||
if (firstNotNull[field.name].includes('.'))
|
||||
const firstNotNull = queryAllResult.find(res => res[field.name] !== null);
|
||||
if (firstNotNull && firstNotNull[field.name].includes('.'))
|
||||
length = firstNotNull[field.name].split('.').pop().length;
|
||||
}
|
||||
|
||||
@@ -745,7 +685,8 @@ export class SQLiteClient extends AntaresCore {
|
||||
tableAlias: field.table,
|
||||
orgTable: field.table,
|
||||
type: field.type !== null ? parsedType : detectedTypes[field.name],
|
||||
length
|
||||
length,
|
||||
key: undefined as string
|
||||
};
|
||||
}).filter(Boolean)
|
||||
: [];
|
||||
@@ -765,18 +706,12 @@ export class SQLiteClient extends AntaresCore {
|
||||
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;
|
||||
@@ -789,8 +724,8 @@ export class SQLiteClient extends AntaresCore {
|
||||
}
|
||||
|
||||
resolve({
|
||||
duration: timeStop - timeStart,
|
||||
rows: Array.isArray(queryResult) ? queryResult.some(el => Array.isArray(el)) ? [] : queryResult : false,
|
||||
duration: timeStop.getTime() - timeStart.getTime(),
|
||||
rows: Array.isArray(queryAllResult) ? queryAllResult.some(el => Array.isArray(el)) ? [] : queryAllResult : false,
|
||||
report: affectedRows !== undefined ? { affectedRows } : null,
|
||||
fields: remappedFields,
|
||||
keys: keysArr
|
||||
@@ -801,6 +736,16 @@ export class SQLiteClient extends AntaresCore {
|
||||
resultsArr.push({ rows, report, fields, keys, duration });
|
||||
}
|
||||
|
||||
return resultsArr.length === 1 ? resultsArr[0] : resultsArr;
|
||||
const result = resultsArr.length === 1 ? resultsArr[0] : resultsArr;
|
||||
|
||||
return result as unknown as T;
|
||||
}
|
||||
|
||||
getVariables (): null[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
getCollations (): null[] {
|
||||
return [];
|
||||
}
|
||||
}
|
93
src/main/libs/exporters/BaseExporter.ts
Normal file
93
src/main/libs/exporters/BaseExporter.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import * as exporter from 'common/interfaces/exporter';
|
||||
import * as fs from 'fs';
|
||||
import { createGzip, Gzip } from 'zlib';
|
||||
import * as path from 'path';
|
||||
import * as EventEmitter from 'events';
|
||||
|
||||
export class BaseExporter extends EventEmitter {
|
||||
protected _tables;
|
||||
protected _options;
|
||||
protected _isCancelled;
|
||||
protected _outputFileStream: fs.WriteStream;
|
||||
protected _processedStream: fs.WriteStream | Gzip;
|
||||
protected _state;
|
||||
|
||||
constructor (tables: exporter.TableParams[], options: exporter.ExportOptions) {
|
||||
super();
|
||||
this._tables = tables;
|
||||
this._options = options;
|
||||
this._isCancelled = false;
|
||||
this._outputFileStream = fs.createWriteStream(this._options.outputFile, { flags: 'w' });
|
||||
this._processedStream = null;
|
||||
this._state = {};
|
||||
|
||||
if (this._options.outputFormat === 'sql.zip') {
|
||||
const outputZipStream = createGzip();
|
||||
outputZipStream.pipe(this._outputFileStream);
|
||||
this._processedStream = outputZipStream;
|
||||
}
|
||||
else
|
||||
this._processedStream = this._outputFileStream;
|
||||
|
||||
this._processedStream.once('error', err => {
|
||||
this._isCancelled = true;
|
||||
this.emit('error', err);
|
||||
});
|
||||
}
|
||||
|
||||
async run () {
|
||||
try {
|
||||
this.emit('start', this);
|
||||
await this.dump();
|
||||
}
|
||||
catch (err) {
|
||||
this.emit('error', err);
|
||||
throw err;
|
||||
}
|
||||
finally {
|
||||
this._processedStream.end();
|
||||
this.emit('end');
|
||||
}
|
||||
}
|
||||
|
||||
get isCancelled () {
|
||||
return this._isCancelled;
|
||||
}
|
||||
|
||||
get outputFile () {
|
||||
return this._options.outputFile;
|
||||
}
|
||||
|
||||
outputFileExists () {
|
||||
return fs.existsSync(this._options.outputFile);
|
||||
}
|
||||
|
||||
cancel () {
|
||||
this._isCancelled = true;
|
||||
this.emit('cancel');
|
||||
this.emitUpdate({ op: 'cancelling' });
|
||||
}
|
||||
|
||||
emitUpdate (state: exporter.ExportState) {
|
||||
this.emit('progress', { ...this._state, ...state });
|
||||
}
|
||||
|
||||
writeString (data: string) {
|
||||
if (this._isCancelled) return;
|
||||
|
||||
try {
|
||||
fs.accessSync(this._options.outputFile);
|
||||
}
|
||||
catch (err) {
|
||||
this._isCancelled = true;
|
||||
|
||||
const fileName = path.basename(this._options.outputFile);
|
||||
this.emit('error', `The file ${fileName} is not accessible`);
|
||||
}
|
||||
this._processedStream.write(data);
|
||||
}
|
||||
|
||||
dump () {
|
||||
throw new Error('Exporter must implement the "dump" method');
|
||||
}
|
||||
}
|
387
src/main/libs/exporters/sql/MysqlExporter.ts
Normal file
387
src/main/libs/exporters/sql/MysqlExporter.ts
Normal file
@@ -0,0 +1,387 @@
|
||||
import * as exporter from 'common/interfaces/exporter';
|
||||
import * as mysql from 'mysql2/promise';
|
||||
import { SqlExporter } from './SqlExporter';
|
||||
import { MySQLClient } from '../../clients/MySQLClient';
|
||||
import { valueToSqlString } from 'common/libs/sqlUtils';
|
||||
|
||||
export default class MysqlExporter extends SqlExporter {
|
||||
protected _client: MySQLClient;
|
||||
|
||||
constructor (client: MySQLClient, tables: exporter.TableParams[], options: exporter.ExportOptions) {
|
||||
super(tables, options);
|
||||
|
||||
this._client = client;
|
||||
this._commentChar = '#';
|
||||
}
|
||||
|
||||
async getSqlHeader () {
|
||||
let dump = await super.getSqlHeader();
|
||||
dump += `
|
||||
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
SET NAMES utf8mb4;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;`;
|
||||
|
||||
return dump;
|
||||
}
|
||||
|
||||
async getFooter () {
|
||||
const footer = await super.getFooter();
|
||||
|
||||
return `/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
|
||||
${footer}
|
||||
`;
|
||||
}
|
||||
|
||||
async getCreateTable (tableName: string) {
|
||||
const { rows } = await this._client.raw(
|
||||
`SHOW CREATE TABLE \`${this.schemaName}\`.\`${tableName}\``
|
||||
);
|
||||
|
||||
if (rows.length !== 1) return '';
|
||||
|
||||
const col = 'Create View' in rows[0] ? 'Create View' : 'Create Table';
|
||||
|
||||
return rows[0][col] + ';';
|
||||
}
|
||||
|
||||
getDropTable (tableName: string) {
|
||||
return `DROP TABLE IF EXISTS \`${tableName}\`;`;
|
||||
}
|
||||
|
||||
async * getTableInsert (tableName: string) {
|
||||
let rowCount = 0;
|
||||
let sqlStr = '';
|
||||
|
||||
const countResults = await this._client.raw(`SELECT COUNT(1) as count FROM \`${this.schemaName}\`.\`${tableName}\``);
|
||||
if (countResults.rows.length === 1) rowCount = countResults.rows[0].count;
|
||||
|
||||
if (rowCount > 0) {
|
||||
let queryLength = 0;
|
||||
let rowsWritten = 0;
|
||||
let rowIndex = 0;
|
||||
const { sqlInsertDivider, sqlInsertAfter } = this._options;
|
||||
const columns = await this._client.getTableColumns({
|
||||
table: tableName,
|
||||
schema: this.schemaName
|
||||
});
|
||||
|
||||
const notGeneratedColumns = columns.filter(col => !col.generated);
|
||||
const columnNames = notGeneratedColumns.map(col => '`' + col.name + '`');
|
||||
const insertStmt = `INSERT INTO \`${tableName}\` (${columnNames.join(
|
||||
', '
|
||||
)}) VALUES`;
|
||||
|
||||
sqlStr += `LOCK TABLES \`${tableName}\` WRITE;\n`;
|
||||
sqlStr += `/*!40000 ALTER TABLE \`${tableName}\` DISABLE KEYS */;`;
|
||||
sqlStr += '\n\n';
|
||||
yield sqlStr;
|
||||
|
||||
yield insertStmt;
|
||||
|
||||
const stream = await this._queryStream(
|
||||
`SELECT ${columnNames.join(', ')} FROM \`${this.schemaName}\`.\`${tableName}\``
|
||||
);
|
||||
|
||||
for await (const row of stream) {
|
||||
if (this.isCancelled) {
|
||||
stream.destroy();
|
||||
yield null;
|
||||
return;
|
||||
}
|
||||
|
||||
let sqlInsertString = '';
|
||||
|
||||
if (
|
||||
(sqlInsertDivider === 'bytes' && queryLength >= sqlInsertAfter * 1024) ||
|
||||
(sqlInsertDivider === 'rows' && rowsWritten === sqlInsertAfter)
|
||||
) {
|
||||
sqlInsertString += `;\n${insertStmt}\n\t(`;
|
||||
queryLength = 0;
|
||||
rowsWritten = 0;
|
||||
}
|
||||
else if (rowIndex === 0) sqlInsertString += '\n\t(';
|
||||
else sqlInsertString += ',\n\t(';
|
||||
|
||||
for (const i in notGeneratedColumns) {
|
||||
const column = notGeneratedColumns[i];
|
||||
const val = row[column.name];
|
||||
|
||||
sqlInsertString += valueToSqlString({ val, client: 'mysql', field: column });
|
||||
|
||||
if (parseInt(i) !== notGeneratedColumns.length - 1)
|
||||
sqlInsertString += ', ';
|
||||
}
|
||||
|
||||
sqlInsertString += ')';
|
||||
|
||||
queryLength += sqlInsertString.length;
|
||||
rowsWritten++;
|
||||
rowIndex++;
|
||||
yield sqlInsertString;
|
||||
}
|
||||
|
||||
sqlStr = ';\n\n';
|
||||
sqlStr += `/*!40000 ALTER TABLE \`${tableName}\` ENABLE KEYS */;\n`;
|
||||
sqlStr += 'UNLOCK TABLES;';
|
||||
|
||||
yield sqlStr;
|
||||
}
|
||||
}
|
||||
|
||||
async getViews () {
|
||||
const { rows: views } = await this._client.raw(
|
||||
`SHOW TABLE STATUS FROM \`${this.schemaName}\` WHERE Comment = 'VIEW'`
|
||||
);
|
||||
let sqlString = '';
|
||||
|
||||
sqlString += this.buildComment('Creating temporary tables to overcome VIEW dependency errors');
|
||||
|
||||
// Temporary tables
|
||||
for (const view of views) {
|
||||
const viewFields = await this._client.getTableColumns({ schema: this.schemaName, table: view.Name });
|
||||
const tableFields: string[] = [];
|
||||
|
||||
for (const field of viewFields) {
|
||||
const typeInfo = this._client.getTypeInfo(field.type);
|
||||
const length = typeInfo.length ? field.enumValues || field.numLength || field.charLength || field.datePrecision : false;
|
||||
|
||||
tableFields.push(`\`${field.name}\` ${field.type.toUpperCase()}${length ? `(${length}${field.numScale ? `,${field.numScale}` : ''})` : ''} ${field.unsigned ? 'UNSIGNED' : ''} ${field.zerofill ? 'ZEROFILL' : ''} ${field.nullable ? 'NULL' : 'NOT NULL'} ${field.autoIncrement ? 'AUTO_INCREMENT' : ''} ${field.collation ? `COLLATE ${field.collation}` : ''}`);
|
||||
}
|
||||
sqlString +=
|
||||
`
|
||||
CREATE TABLE \`${view.Name}\`(
|
||||
${tableFields.join(',\n\t')}
|
||||
);`;
|
||||
|
||||
sqlString += '\n';
|
||||
}
|
||||
|
||||
sqlString += '\n';
|
||||
|
||||
for (const view of views) {
|
||||
sqlString += `DROP TABLE IF EXISTS \`${view.Name}\`;\n`;
|
||||
const viewSyntax = await this.getCreateTable(view.Name);
|
||||
sqlString += viewSyntax.replaceAll('`' + this.schemaName + '`.', '');
|
||||
sqlString += '\n\n';
|
||||
}
|
||||
|
||||
return sqlString;
|
||||
}
|
||||
|
||||
async getTriggers () {
|
||||
const { rows: triggers } = await this._client.raw(
|
||||
`SHOW TRIGGERS FROM \`${this.schemaName}\``
|
||||
);
|
||||
const generatedTables = this._tables
|
||||
.filter(t => t.includeStructure)
|
||||
.map(t => t.table);
|
||||
|
||||
let sqlString = '';
|
||||
|
||||
for (const trigger of triggers) {
|
||||
const {
|
||||
Trigger: name,
|
||||
Timing: timing,
|
||||
Event: event,
|
||||
Table: table,
|
||||
Statement: statement,
|
||||
sql_mode: sqlMode
|
||||
} = trigger;
|
||||
|
||||
if (!generatedTables.includes(table)) continue;
|
||||
|
||||
const definer = this.getEscapedDefiner(trigger.Definer);
|
||||
sqlString += '/*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/;;\n';
|
||||
sqlString += `/*!50003 SET SQL_MODE="${sqlMode}" */;\n`;
|
||||
sqlString += 'DELIMITER ;;\n';
|
||||
sqlString += '/*!50003 CREATE*/ ';
|
||||
sqlString += `/*!50017 DEFINER=${definer}*/ `;
|
||||
sqlString += `/*!50003 TRIGGER \`${name}\` ${timing} ${event} ON \`${table}\` FOR EACH ROW ${statement}*/;;\n`;
|
||||
sqlString += 'DELIMITER ;\n';
|
||||
sqlString += '/*!50003 SET SQL_MODE=@OLD_SQL_MODE */;\n\n';
|
||||
}
|
||||
|
||||
return sqlString;
|
||||
}
|
||||
|
||||
async getSchedulers () {
|
||||
const { rows: schedulers } = await this._client.raw(
|
||||
`SELECT *, EVENT_SCHEMA AS \`Db\`, EVENT_NAME AS \`Name\` FROM information_schema.\`EVENTS\` WHERE EVENT_SCHEMA = '${this.schemaName}'`
|
||||
);
|
||||
let sqlString = '';
|
||||
|
||||
for (const scheduler of schedulers) {
|
||||
const {
|
||||
EVENT_NAME: name,
|
||||
SQL_MODE: sqlMode,
|
||||
EVENT_TYPE: type,
|
||||
INTERVAL_VALUE: intervalValue,
|
||||
INTERVAL_FIELD: intervalField,
|
||||
STARTS: starts,
|
||||
ENDS: ends,
|
||||
EXECUTE_AT: at,
|
||||
ON_COMPLETION: onCompletion,
|
||||
STATUS: status,
|
||||
EVENT_DEFINITION: definition
|
||||
} = scheduler;
|
||||
|
||||
const definer = this.getEscapedDefiner(scheduler.DEFINER);
|
||||
const comment = this.escapeAndQuote(scheduler.EVENT_COMMENT);
|
||||
|
||||
sqlString += `/*!50106 DROP EVENT IF EXISTS \`${name}\` */;\n`;
|
||||
sqlString += '/*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/;;\n';
|
||||
sqlString += `/*!50003 SET SQL_MODE='${sqlMode}' */;\n`;
|
||||
sqlString += 'DELIMITER ;;\n';
|
||||
sqlString += '/*!50106 CREATE*/ ';
|
||||
sqlString += `/*!50117 DEFINER=${definer}*/ `;
|
||||
sqlString += `/*!50106 EVENT \`${name}\` ON SCHEDULE `;
|
||||
if (type === 'RECURRING') {
|
||||
sqlString += `EVERY ${intervalValue} ${intervalField} STARTS '${starts}' `;
|
||||
|
||||
if (ends) sqlString += `ENDS '${ends}' `;
|
||||
}
|
||||
else sqlString += `AT '${at}' `;
|
||||
sqlString += `ON COMPLETION ${onCompletion} ${
|
||||
status === 'disabled' ? 'DISABLE' : 'ENABLE'
|
||||
} COMMENT ${comment || '\'\''} DO ${definition}*/;;\n`;
|
||||
sqlString += 'DELIMITER ;\n';
|
||||
sqlString += '/*!50003 SET SQL_MODE=@OLD_SQL_MODE*/;;\n';
|
||||
}
|
||||
|
||||
return sqlString;
|
||||
}
|
||||
|
||||
async getFunctions () {
|
||||
const { rows: functions } = await this._client.raw(
|
||||
`SHOW FUNCTION STATUS WHERE \`Db\` = '${this.schemaName}';`
|
||||
);
|
||||
|
||||
let sqlString = '';
|
||||
|
||||
for (const func of functions) {
|
||||
const definer = this.getEscapedDefiner(func.Definer);
|
||||
sqlString += await this.getRoutineSyntax(
|
||||
func.Name,
|
||||
func.Type,
|
||||
definer
|
||||
);
|
||||
}
|
||||
|
||||
return sqlString;
|
||||
}
|
||||
|
||||
async getRoutines () {
|
||||
const { rows: routines } = await this._client.raw(
|
||||
`SHOW PROCEDURE STATUS WHERE \`Db\` = '${this.schemaName}';`
|
||||
);
|
||||
|
||||
let sqlString = '';
|
||||
|
||||
for (const routine of routines) {
|
||||
const definer = this.getEscapedDefiner(routine.Definer);
|
||||
|
||||
sqlString += await this.getRoutineSyntax(
|
||||
routine.Name,
|
||||
routine.Type,
|
||||
definer
|
||||
);
|
||||
}
|
||||
|
||||
return sqlString;
|
||||
}
|
||||
|
||||
async getRoutineSyntax (name: string, type: string, definer: string) {
|
||||
const { rows: routines } = await this._client.raw(
|
||||
`SHOW CREATE ${type} \`${this.schemaName}\`.\`${name}\``
|
||||
);
|
||||
|
||||
if (routines.length === 0) return '';
|
||||
|
||||
const routine = routines[0];
|
||||
|
||||
const fieldName = `Create ${type === 'PROCEDURE' ? 'Procedure' : 'Function'}`;
|
||||
const sqlMode = routine.sql_mode;
|
||||
const createProcedure = routine[fieldName];
|
||||
let sqlString = '';
|
||||
|
||||
if (createProcedure) { // If procedure body not empty
|
||||
const startOffset = createProcedure.indexOf(type);
|
||||
const procedureBody = createProcedure.substring(startOffset);
|
||||
|
||||
sqlString += `/*!50003 DROP ${type} IF EXISTS ${name}*/;;\n`;
|
||||
sqlString += '/*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/;;\n';
|
||||
sqlString += `/*!50003 SET SQL_MODE="${sqlMode}"*/;;\n`;
|
||||
sqlString += 'DELIMITER ;;\n';
|
||||
sqlString += `/*!50003 CREATE*/ /*!50020 DEFINER=${definer}*/ /*!50003 ${procedureBody}*/;;\n`;
|
||||
sqlString += 'DELIMITER ;\n';
|
||||
sqlString += '/*!50003 SET SQL_MODE=@OLD_SQL_MODE*/;\n';
|
||||
}
|
||||
|
||||
return sqlString;
|
||||
}
|
||||
|
||||
async _queryStream (sql: string) {
|
||||
if (process.env.NODE_ENV === 'development') console.log('EXPORTER:', sql);
|
||||
const isPool = 'getConnection' in this._client._connection;
|
||||
const connection = isPool ? await (this._client._connection as mysql.Pool).getConnection() : this._client._connection;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const stream = (connection as any).connection.query(sql).stream();
|
||||
const dispose = () => (connection as mysql.PoolConnection).release();
|
||||
|
||||
stream.on('end', dispose);
|
||||
stream.on('error', dispose);
|
||||
stream.on('close', dispose);
|
||||
return stream;
|
||||
}
|
||||
|
||||
getEscapedDefiner (definer: string) {
|
||||
return definer
|
||||
.split('@')
|
||||
.map(part => '`' + part + '`')
|
||||
.join('@');
|
||||
}
|
||||
|
||||
escapeAndQuote (val: string) {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const CHARS_TO_ESCAPE = /[\0\b\t\n\r\x1a"'\\]/g;
|
||||
const CHARS_ESCAPE_MAP: {[key: string]: string} = {
|
||||
'\0': '\\0',
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\r': '\\r',
|
||||
'\x1a': '\\Z',
|
||||
'"': '\\"',
|
||||
'\'': '\\\'',
|
||||
'\\': '\\\\'
|
||||
};
|
||||
let chunkIndex = CHARS_TO_ESCAPE.lastIndex = 0;
|
||||
let escapedVal = '';
|
||||
let match;
|
||||
|
||||
while ((match = CHARS_TO_ESCAPE.exec(val))) {
|
||||
escapedVal += val.slice(chunkIndex, match.index) + CHARS_ESCAPE_MAP[match[0]];
|
||||
chunkIndex = CHARS_TO_ESCAPE.lastIndex;
|
||||
}
|
||||
|
||||
if (chunkIndex === 0)
|
||||
return `'${val}'`;
|
||||
|
||||
if (chunkIndex < val.length)
|
||||
return `'${escapedVal + val.slice(chunkIndex)}'`;
|
||||
|
||||
return `'${escapedVal}'`;
|
||||
}
|
||||
}
|
471
src/main/libs/exporters/sql/PostgreSQLExporter.ts
Normal file
471
src/main/libs/exporters/sql/PostgreSQLExporter.ts
Normal file
@@ -0,0 +1,471 @@
|
||||
import * as antares from 'common/interfaces/antares';
|
||||
import * as exporter from 'common/interfaces/exporter';
|
||||
import { SqlExporter } from './SqlExporter';
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import * as QueryStream from 'pg-query-stream';
|
||||
import { PostgreSQLClient } from '../../clients/PostgreSQLClient';
|
||||
import { valueToSqlString } from 'common/libs/sqlUtils';
|
||||
|
||||
export default class PostgreSQLExporter extends SqlExporter {
|
||||
constructor (client: PostgreSQLClient, tables: exporter.TableParams[], options: exporter.ExportOptions) {
|
||||
super(tables, options);
|
||||
|
||||
this._client = client;
|
||||
}
|
||||
|
||||
async getSqlHeader () {
|
||||
let dump = await super.getSqlHeader();
|
||||
dump += `
|
||||
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
SET idle_in_transaction_session_timeout = 0;
|
||||
SET client_encoding = 'UTF8';
|
||||
SET standard_conforming_strings = on;
|
||||
SELECT pg_catalog.set_config('search_path', '', false);
|
||||
SET check_function_bodies = false;
|
||||
SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;\n\n\n`;
|
||||
|
||||
if (this.schemaName !== 'public') dump += `CREATE SCHEMA "${this.schemaName}";\n\n`;
|
||||
|
||||
dump += await this.getCreateTypes();
|
||||
|
||||
return dump;
|
||||
}
|
||||
|
||||
async getCreateTable (tableName: string) {
|
||||
/* eslint-disable camelcase */
|
||||
interface SequenceRecord {
|
||||
sequence_catalog: string;
|
||||
sequence_schema: string;
|
||||
sequence_name: string;
|
||||
data_type: string;
|
||||
numeric_precision: number;
|
||||
numeric_precision_radix: number;
|
||||
numeric_scale: number;
|
||||
start_value: string;
|
||||
minimum_value: string;
|
||||
maximum_value: string;
|
||||
increment: string;
|
||||
cycle_option: string;
|
||||
}
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
let createSql = '';
|
||||
const sequences = [];
|
||||
const columnsSql = [];
|
||||
const arrayTypes: {[key: string]: string} = {
|
||||
_int2: 'smallint',
|
||||
_int4: 'integer',
|
||||
_int8: 'bigint',
|
||||
_float4: 'real',
|
||||
_float8: 'double precision',
|
||||
_char: '"char"',
|
||||
_varchar: 'character varying'
|
||||
};
|
||||
|
||||
// Table columns
|
||||
const { rows } = await this._client.raw(`
|
||||
SELECT *
|
||||
FROM "information_schema"."columns"
|
||||
WHERE "table_schema" = '${this.schemaName}'
|
||||
AND "table_name" = '${tableName}'
|
||||
ORDER BY "ordinal_position" ASC
|
||||
`, { schema: 'information_schema' });
|
||||
|
||||
if (!rows.length) return '';
|
||||
|
||||
for (const column of rows) {
|
||||
let fieldType = column.data_type;
|
||||
if (fieldType === 'USER-DEFINED') fieldType = `"${this.schemaName}".${column.udt_name}`;
|
||||
else if (fieldType === 'ARRAY') {
|
||||
if (Object.keys(arrayTypes).includes(fieldType))
|
||||
fieldType = arrayTypes[column.udt_name] + '[]';
|
||||
else
|
||||
fieldType = column.udt_name.replaceAll('_', '') + '[]';
|
||||
}
|
||||
|
||||
const columnArr = [
|
||||
`"${column.column_name}"`,
|
||||
`${fieldType}${column.character_maximum_length ? `(${column.character_maximum_length})` : ''}`
|
||||
];
|
||||
|
||||
if (column.column_default) {
|
||||
columnArr.push(`DEFAULT ${column.column_default}`);
|
||||
if (column.column_default.includes('nextval')) {
|
||||
const sequenceName = column.column_default.split('\'')[1];
|
||||
sequences.push(sequenceName);
|
||||
}
|
||||
}
|
||||
if (column.is_nullable === 'NO') columnArr.push('NOT NULL');
|
||||
|
||||
columnsSql.push(columnArr.join(' '));
|
||||
}
|
||||
|
||||
// Table sequences
|
||||
for (let sequence of sequences) {
|
||||
if (sequence.includes('.')) sequence = sequence.split('.')[1];
|
||||
|
||||
const { rows } = await this._client
|
||||
.select('*')
|
||||
.schema('information_schema')
|
||||
.from('sequences')
|
||||
.where({ sequence_schema: `= '${this.schemaName}'`, sequence_name: `= '${sequence}'` })
|
||||
.run<SequenceRecord>();
|
||||
|
||||
if (rows.length) {
|
||||
createSql += `CREATE SEQUENCE "${this.schemaName}"."${sequence}"
|
||||
START WITH ${rows[0].start_value}
|
||||
INCREMENT BY ${rows[0].increment}
|
||||
MINVALUE ${rows[0].minimum_value}
|
||||
MAXVALUE ${rows[0].maximum_value}
|
||||
CACHE 1;\n`;
|
||||
|
||||
// createSql += `\nALTER TABLE "${sequence}" OWNER TO ${this._client._params.user};\n\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// Table create
|
||||
createSql += `\nCREATE TABLE "${this.schemaName}"."${tableName}"(
|
||||
${columnsSql.join(',\n ')}
|
||||
);\n`;
|
||||
|
||||
// createSql += `\nALTER TABLE "${tableName}" OWNER TO ${this._client._params.user};\n\n`;
|
||||
|
||||
// Table indexes
|
||||
createSql += '\n';
|
||||
const { rows: indexes } = await this._client
|
||||
.select('*')
|
||||
.schema('pg_catalog')
|
||||
.from('pg_indexes')
|
||||
.where({ schemaname: `= '${this.schemaName}'`, tablename: `= '${tableName}'` })
|
||||
.run<{indexdef: string}>();
|
||||
|
||||
for (const index of indexes)
|
||||
createSql += `${index.indexdef};\n`;
|
||||
|
||||
// Table foreigns
|
||||
const { rows: foreigns } = await this._client.raw(`
|
||||
SELECT
|
||||
tc.table_schema,
|
||||
tc.constraint_name,
|
||||
tc.table_name,
|
||||
kcu.column_name,
|
||||
ccu.table_schema AS foreign_table_schema,
|
||||
ccu.table_name AS foreign_table_name,
|
||||
ccu.column_name AS foreign_column_name,
|
||||
rc.update_rule,
|
||||
rc.delete_rule
|
||||
FROM information_schema.table_constraints AS tc
|
||||
JOIN information_schema.key_column_usage AS kcu
|
||||
ON tc.constraint_name = kcu.constraint_name
|
||||
AND tc.table_schema = kcu.table_schema
|
||||
JOIN information_schema.constraint_column_usage AS ccu
|
||||
ON ccu.constraint_name = tc.constraint_name
|
||||
AND ccu.table_schema = tc.table_schema
|
||||
JOIN information_schema.referential_constraints AS rc
|
||||
ON rc.constraint_name = kcu.constraint_name
|
||||
WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema = '${this.schemaName}'
|
||||
AND tc.table_name = '${tableName}'
|
||||
`);
|
||||
|
||||
for (const foreign of foreigns) {
|
||||
this._postTablesSql += `\nALTER TABLE ONLY "${this.schemaName}"."${tableName}"
|
||||
ADD CONSTRAINT "${foreign.constraint_name}" FOREIGN KEY ("${foreign.column_name}") REFERENCES "${this.schemaName}"."${foreign.foreign_table_name}" ("${foreign.foreign_column_name}") ON UPDATE ${foreign.update_rule} ON DELETE ${foreign.delete_rule};\n`;
|
||||
}
|
||||
|
||||
return createSql;
|
||||
}
|
||||
|
||||
getDropTable (tableName: string) {
|
||||
return `DROP TABLE IF EXISTS "${this.schemaName}"."${tableName}";`;
|
||||
}
|
||||
|
||||
async * getTableInsert (tableName: string) {
|
||||
let rowCount = 0;
|
||||
const sqlStr = '';
|
||||
|
||||
const countResults = await this._client.raw(`SELECT COUNT(1) as count FROM "${this.schemaName}"."${tableName}"`);
|
||||
if (countResults.rows.length === 1) rowCount = countResults.rows[0].count;
|
||||
|
||||
if (rowCount > 0) {
|
||||
const columns = await this._client.getTableColumns({
|
||||
table: tableName,
|
||||
schema: this.schemaName
|
||||
});
|
||||
|
||||
const columnNames = columns.map(col => '"' + col.name + '"').join(', ');
|
||||
|
||||
yield sqlStr;
|
||||
|
||||
const stream = await this._queryStream(
|
||||
`SELECT ${columnNames} FROM "${this.schemaName}"."${tableName}"`
|
||||
);
|
||||
|
||||
for await (const row of stream) {
|
||||
if (this.isCancelled) {
|
||||
stream.destroy();
|
||||
yield null;
|
||||
return;
|
||||
}
|
||||
|
||||
let sqlInsertString = `INSERT INTO "${this.schemaName}"."${tableName}" (${columnNames}) VALUES`;
|
||||
|
||||
sqlInsertString += ' (';
|
||||
|
||||
for (const i in columns) {
|
||||
const column = columns[i];
|
||||
const val = row[column.name];
|
||||
|
||||
sqlInsertString += valueToSqlString({ val, client: 'pg', field: column });
|
||||
|
||||
if (parseInt(i) !== columns.length - 1)
|
||||
sqlInsertString += ', ';
|
||||
}
|
||||
|
||||
sqlInsertString += ');\n';
|
||||
|
||||
yield sqlInsertString;
|
||||
}
|
||||
|
||||
yield sqlStr;
|
||||
}
|
||||
}
|
||||
|
||||
async getCreateTypes () {
|
||||
let sqlString = '';
|
||||
const { rows: types } = await this._client.raw<antares.QueryResult<{typname: string; enumlabel: string}>>(`
|
||||
SELECT pg_type.typname, pg_enum.enumlabel
|
||||
FROM pg_type
|
||||
JOIN pg_enum ON pg_enum.enumtypid = pg_type.oid;
|
||||
`);
|
||||
|
||||
if (types.length) { // TODO: refactor
|
||||
sqlString += this.buildComment('Dump of types\n------------------------------------------------------------') + '\n\n';
|
||||
|
||||
const typesArr = types.reduce((arr, type) => {
|
||||
if (arr.every(el => el.name !== type.typname))
|
||||
arr.push({ name: type.typname, enums: [this.escapeAndQuote(type.enumlabel)] });
|
||||
else {
|
||||
const i = arr.findIndex(el => el.name === type.typname);
|
||||
arr[i].enums.push(this.escapeAndQuote(type.enumlabel));
|
||||
}
|
||||
|
||||
return arr;
|
||||
}, []);
|
||||
|
||||
for (const type of typesArr) {
|
||||
sqlString += `CREATE TYPE "${this.schemaName}"."${type.name}" AS ENUM (
|
||||
${type.enums.join(',\n\t')}
|
||||
);`;
|
||||
}
|
||||
|
||||
// sqlString += `\nALTER TYPE "${tableName}" OWNER TO ${this._client._params.user};\n`
|
||||
}
|
||||
|
||||
return sqlString;
|
||||
}
|
||||
|
||||
async getCreateAggregates () {
|
||||
let sqlString = '';
|
||||
|
||||
const { rows: aggregates } = await this._client.raw(`
|
||||
SELECT proname
|
||||
FROM pg_proc
|
||||
WHERE prokind = 'a'
|
||||
AND pronamespace::regnamespace::text = '${this.schemaName}'
|
||||
ORDER BY 1;
|
||||
`);
|
||||
|
||||
if (aggregates.length) {
|
||||
for (const aggregate of aggregates) {
|
||||
const { rows: aggregateDef } = await this._client.raw(
|
||||
`SELECT
|
||||
format(
|
||||
E'CREATE AGGREGATE %s (\n%s\n);'
|
||||
, (pg_identify_object('pg_proc'::regclass, aggfnoid, 0)).identity
|
||||
, array_to_string(
|
||||
ARRAY[
|
||||
format(E'\tSFUNC = %s', aggtransfn::regproc)
|
||||
, format(E'\tSTYPE = %s', format_type(aggtranstype, NULL))
|
||||
, CASE aggfinalfn WHEN '-'::regproc THEN NULL ELSE format(E'\tFINALFUNC = %s',aggfinalfn::text) END
|
||||
, CASE aggsortop WHEN 0 THEN NULL ELSE format(E'\tSORTOP = %s', oprname) END
|
||||
, CASE WHEN agginitval IS NULL THEN NULL ELSE format(E'\tINITCOND = %s', agginitval) END
|
||||
]
|
||||
, E',\n'
|
||||
)
|
||||
)
|
||||
FROM pg_aggregate
|
||||
LEFT JOIN pg_operator ON pg_operator.oid = aggsortop
|
||||
WHERE aggfnoid = '${this.schemaName}.${aggregate.proname}'::regproc;`
|
||||
);
|
||||
|
||||
if (aggregateDef.length)
|
||||
sqlString += '\n\n' + aggregateDef[0].format;
|
||||
}
|
||||
}
|
||||
|
||||
return sqlString + '\n\n\n';
|
||||
}
|
||||
|
||||
async getViews () {
|
||||
const { rows: views } = await this._client.raw(`SELECT * FROM "pg_views" WHERE "schemaname"='${this.schemaName}'`);
|
||||
let sqlString = '';
|
||||
|
||||
for (const view of views) {
|
||||
sqlString += `\nDROP VIEW IF EXISTS "${view.viewname}";\n`;
|
||||
|
||||
// const { rows: columns } = await this._client
|
||||
// .select('*')
|
||||
// .schema('information_schema')
|
||||
// .from('columns')
|
||||
// .where({ table_schema: `= '${this.schemaName}'`, table_name: `= '${view.viewname}'` })
|
||||
// .orderBy({ ordinal_position: 'ASC' })
|
||||
// .run();
|
||||
|
||||
// sqlString += `
|
||||
// CREATE VIEW "${this.schemaName}"."${view.viewname}" AS
|
||||
// SELECT
|
||||
// ${columns.reduce((acc, curr) => {
|
||||
// const fieldType = curr.data_type === 'USER-DEFINED' ? curr.udt_name : curr.data_type;
|
||||
// acc.push(`NULL::${fieldType}${curr.character_maximum_length ? `(${curr.character_maximum_length})` : ''} AS "${curr.column_name}"`);
|
||||
// return acc;
|
||||
// }, []).join(',\n ')};
|
||||
// `;
|
||||
|
||||
sqlString += `\nCREATE OR REPLACE VIEW "${this.schemaName}"."${view.viewname}" AS \n${view.definition}\n`;
|
||||
}
|
||||
|
||||
return sqlString;
|
||||
}
|
||||
|
||||
async getTriggers () {
|
||||
/* eslint-disable camelcase */
|
||||
interface TriggersResult {
|
||||
event_object_table: string;
|
||||
table_name: string;
|
||||
trigger_name: string;
|
||||
events: string[];
|
||||
event_manipulation: string;
|
||||
}
|
||||
/* eslint-enable camelcase */
|
||||
let sqlString = '';
|
||||
|
||||
// Trigger functions
|
||||
const { rows: triggerFunctions } = await this._client.raw(
|
||||
`SELECT DISTINCT routine_name AS name FROM information_schema.routines WHERE routine_type = 'FUNCTION' AND routine_schema = '${this.schemaName}' AND data_type = 'trigger'`
|
||||
);
|
||||
|
||||
for (const func of triggerFunctions) {
|
||||
const { rows: functionDef } = await this._client.raw(
|
||||
`SELECT pg_get_functiondef((SELECT oid FROM pg_proc WHERE proname = '${func.name}')) AS definition`
|
||||
);
|
||||
sqlString += `\n${functionDef[0].definition};\n`;
|
||||
}
|
||||
|
||||
const { rows: triggers } = await this._client.raw<antares.QueryResult<TriggersResult>>(
|
||||
`SELECT * FROM "information_schema"."triggers" WHERE "trigger_schema"='${this.schemaName}'`
|
||||
);
|
||||
|
||||
const remappedTriggers = triggers.reduce((acc, trigger) => {
|
||||
const i = acc.findIndex(t => t.trigger_name === trigger.trigger_name && t.event_object_table === trigger.event_object_table);
|
||||
if (i === -1) {
|
||||
trigger.events = [trigger.event_manipulation];
|
||||
acc.push(trigger);
|
||||
}
|
||||
else
|
||||
acc[i].events.push(trigger.event_manipulation);
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
for (const trigger of remappedTriggers)
|
||||
sqlString += `\nCREATE TRIGGER "${trigger.trigger_name}" ${trigger.action_timing} ${trigger.events.join(' OR ')} ON "${this.schemaName}"."${trigger.event_object_table}" FOR EACH ${trigger.action_orientation} ${trigger.action_statement};\n`;
|
||||
|
||||
return sqlString;
|
||||
}
|
||||
|
||||
async getFunctions () {
|
||||
let sqlString = '';
|
||||
const { rows: functions } = await this._client.raw(
|
||||
`SELECT DISTINCT routine_name AS name FROM information_schema.routines WHERE routine_type = 'FUNCTION' AND routine_schema = '${this.schemaName}' AND data_type != 'trigger'`
|
||||
);
|
||||
|
||||
for (const func of functions) {
|
||||
const { rows: functionDef } = await this._client.raw(
|
||||
`SELECT pg_get_functiondef((SELECT oid FROM pg_proc WHERE proname = '${func.name}')) AS definition`
|
||||
);
|
||||
sqlString += `\n${functionDef[0].definition};\n`;
|
||||
}
|
||||
|
||||
sqlString += await this.getCreateAggregates();
|
||||
|
||||
return sqlString;
|
||||
}
|
||||
|
||||
async getRoutines () {
|
||||
let sqlString = '';
|
||||
const { rows: functions } = await this._client.raw(
|
||||
`SELECT DISTINCT routine_name AS name FROM information_schema.routines WHERE routine_type = 'PROCEDURE' AND routine_schema = '${this.schemaName}'`
|
||||
);
|
||||
|
||||
for (const func of functions) {
|
||||
const { rows: functionDef } = await this._client.raw(
|
||||
`SELECT pg_get_functiondef((SELECT oid FROM pg_proc WHERE proname = '${func.name}')) AS definition`
|
||||
);
|
||||
sqlString += `\n${functionDef[0].definition};\n`;
|
||||
}
|
||||
|
||||
return sqlString;
|
||||
}
|
||||
|
||||
async _queryStream (sql: string) {
|
||||
if (process.env.NODE_ENV === 'development') console.log('EXPORTER:', sql);
|
||||
const connection = await this._client.getConnection();
|
||||
const query = new QueryStream(sql, null);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const stream = (connection as any).query(query);
|
||||
const dispose = () => connection.end();
|
||||
|
||||
stream.on('end', dispose);
|
||||
stream.on('error', dispose);
|
||||
stream.on('close', dispose);
|
||||
return stream;
|
||||
}
|
||||
|
||||
escapeAndQuote (val: string) {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const CHARS_TO_ESCAPE = /[\0\b\t\n\r\x1a"'\\]/g;
|
||||
const CHARS_ESCAPE_MAP: {[key: string]: string} = {
|
||||
'\0': '\\0',
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\r': '\\r',
|
||||
'\x1a': '\\Z',
|
||||
'"': '\\"',
|
||||
'\'': '\\\'',
|
||||
'\\': '\\\\'
|
||||
};
|
||||
let chunkIndex = CHARS_TO_ESCAPE.lastIndex = 0;
|
||||
let escapedVal = '';
|
||||
let match;
|
||||
|
||||
while ((match = CHARS_TO_ESCAPE.exec(val))) {
|
||||
escapedVal += val.slice(chunkIndex, match.index) + CHARS_ESCAPE_MAP[match[0]];
|
||||
chunkIndex = CHARS_TO_ESCAPE.lastIndex;
|
||||
}
|
||||
|
||||
if (chunkIndex === 0)
|
||||
return `'${val}'`;
|
||||
|
||||
if (chunkIndex < val.length)
|
||||
return `'${escapedVal + val.slice(chunkIndex)}'`;
|
||||
|
||||
return `'${escapedVal}'`;
|
||||
}
|
||||
}
|
187
src/main/libs/exporters/sql/SqlExporter.ts
Normal file
187
src/main/libs/exporters/sql/SqlExporter.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
import * as moment from 'moment';
|
||||
import { MySQLClient } from '../../clients/MySQLClient';
|
||||
import { PostgreSQLClient } from '../../clients/PostgreSQLClient';
|
||||
import { BaseExporter } from '../BaseExporter';
|
||||
|
||||
export class SqlExporter extends BaseExporter {
|
||||
protected _client: MySQLClient | PostgreSQLClient;
|
||||
protected _commentChar = '--'
|
||||
protected _postTablesSql = ''
|
||||
|
||||
get schemaName () {
|
||||
return this._options.schema;
|
||||
}
|
||||
|
||||
get host () {
|
||||
return this._client._params.host;
|
||||
}
|
||||
|
||||
async getServerVersion () {
|
||||
const version = await this._client.getVersion();
|
||||
return `${version.name} ${version.number}`;
|
||||
}
|
||||
|
||||
async dump () {
|
||||
const { includes } = this._options;
|
||||
const extraItems = Object.keys(includes).filter((key: 'functions' | 'views' | 'triggers' | 'routines' | 'schedulers') => includes[key]);
|
||||
const totalTableToProcess = this._tables.filter(
|
||||
t => t.includeStructure || t.includeContent || t.includeDropStatement
|
||||
).length;
|
||||
const processingItemCount = totalTableToProcess + extraItems.length;
|
||||
|
||||
const exportState = {
|
||||
totalItems: processingItemCount,
|
||||
currentItemIndex: 0,
|
||||
currentItem: '',
|
||||
op: ''
|
||||
};
|
||||
|
||||
const header = await this.getSqlHeader();
|
||||
this.writeString(header);
|
||||
this.writeString('\n\n\n');
|
||||
|
||||
for (const item of this._tables) {
|
||||
// user abort operation
|
||||
if (this.isCancelled) return;
|
||||
|
||||
// skip item if not set to output any detail for them
|
||||
if (
|
||||
!item.includeStructure &&
|
||||
!item.includeContent &&
|
||||
!item.includeDropStatement
|
||||
)
|
||||
continue;
|
||||
|
||||
exportState.currentItemIndex++;
|
||||
exportState.currentItem = item.table;
|
||||
exportState.op = 'FETCH';
|
||||
|
||||
this.emitUpdate(exportState);
|
||||
|
||||
const tableHeader = this.buildComment(
|
||||
`Dump of table ${item.table}\n------------------------------------------------------------`
|
||||
);
|
||||
this.writeString(tableHeader);
|
||||
this.writeString('\n\n');
|
||||
|
||||
if (item.includeDropStatement) {
|
||||
const dropTableSyntax = this.getDropTable(item.table);
|
||||
this.writeString(dropTableSyntax);
|
||||
this.writeString('\n\n');
|
||||
}
|
||||
|
||||
if (item.includeStructure) {
|
||||
const createTableSyntax = await this.getCreateTable(item.table);
|
||||
this.writeString(createTableSyntax);
|
||||
this.writeString('\n\n');
|
||||
}
|
||||
|
||||
if (item.includeContent) {
|
||||
exportState.op = 'WRITE';
|
||||
this.emitUpdate(exportState);
|
||||
for await (const sqlStr of this.getTableInsert(item.table)) {
|
||||
if (this.isCancelled) return;
|
||||
this.writeString(sqlStr);
|
||||
}
|
||||
|
||||
this.writeString('\n\n');
|
||||
}
|
||||
|
||||
this.writeString('\n\n');
|
||||
}
|
||||
|
||||
// SQL to execute after tables creation
|
||||
if (this._postTablesSql) {
|
||||
this.writeString(this._postTablesSql);
|
||||
this.writeString('\n\n');
|
||||
}
|
||||
|
||||
for (const item of extraItems) {
|
||||
type exporterMethods = 'getViews' | 'getTriggers' | 'getSchedulers' | 'getFunctions' | 'getRoutines'
|
||||
const processingMethod = `get${item.charAt(0).toUpperCase() + item.slice(1)}` as exporterMethods;
|
||||
exportState.currentItemIndex++;
|
||||
exportState.currentItem = item;
|
||||
exportState.op = 'PROCESSING';
|
||||
this.emitUpdate(exportState);
|
||||
|
||||
if (this[processingMethod]) {
|
||||
const data = await this[processingMethod]() as unknown as string;
|
||||
if (data !== '') {
|
||||
const header =
|
||||
this.buildComment(
|
||||
`Dump of ${item}\n------------------------------------------------------------`
|
||||
) + '\n\n';
|
||||
|
||||
this.writeString(header);
|
||||
this.writeString(data);
|
||||
this.writeString('\n\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const footer = await this.getFooter();
|
||||
this.writeString(footer);
|
||||
}
|
||||
|
||||
buildComment (text: string) {
|
||||
return text
|
||||
.split('\n')
|
||||
.map(txt => `${this._commentChar} ${txt}`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
async getSqlHeader () {
|
||||
const serverVersion = await this.getServerVersion();
|
||||
const header = `************************************************************
|
||||
Antares - SQL Client
|
||||
Version ${process.env.PACKAGE_VERSION}
|
||||
|
||||
https://antares-sql.app/
|
||||
https://github.com/antares-sql/antares
|
||||
|
||||
Host: ${this.host} (${serverVersion})
|
||||
Database: ${this.schemaName}
|
||||
Generation time: ${moment().format()}
|
||||
************************************************************`;
|
||||
|
||||
return this.buildComment(header);
|
||||
}
|
||||
|
||||
async getFooter () {
|
||||
return this.buildComment(`Dump completed on ${moment().format()}`);
|
||||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
getCreateTable (_tableName: string): Promise<string> {
|
||||
throw new Error('Sql Exporter must implement the "getCreateTable" method');
|
||||
}
|
||||
|
||||
getDropTable (_tableName: string): string {
|
||||
throw new Error('Sql Exporter must implement the "getDropTable" method');
|
||||
}
|
||||
|
||||
getTableInsert (_tableName: string): AsyncGenerator<string> {
|
||||
throw new Error('Sql Exporter must implement the "getTableInsert" method');
|
||||
}
|
||||
|
||||
getViews () {
|
||||
throw new Error('Method "getViews" not implemented');
|
||||
}
|
||||
|
||||
getTriggers () {
|
||||
throw new Error('Method "getTriggers" not implemented');
|
||||
}
|
||||
|
||||
getSchedulers () {
|
||||
throw new Error('Method "getSchedulers" not implemented');
|
||||
}
|
||||
|
||||
getFunctions () {
|
||||
throw new Error('Method "getFunctions" not implemented');
|
||||
}
|
||||
|
||||
getRoutines () {
|
||||
throw new Error('Method "getRoutines" not implemented');
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||
}
|
59
src/main/libs/importers/BaseImporter.ts
Normal file
59
src/main/libs/importers/BaseImporter.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import * as importer from 'common/interfaces/importer';
|
||||
import * as fs from 'fs';
|
||||
import * as EventEmitter from 'events';
|
||||
|
||||
export class BaseImporter extends EventEmitter {
|
||||
protected _options;
|
||||
protected _isCancelled;
|
||||
protected _fileHandler;
|
||||
protected _state;
|
||||
|
||||
constructor (options: importer.ImportOptions) {
|
||||
super();
|
||||
this._options = options;
|
||||
this._isCancelled = false;
|
||||
this._fileHandler = fs.createReadStream(this._options.file, {
|
||||
flags: 'r',
|
||||
highWaterMark: 4 * 1024
|
||||
});
|
||||
this._state = {};
|
||||
|
||||
this._fileHandler.once('error', err => {
|
||||
this._isCancelled = true;
|
||||
this.emit('error', err);
|
||||
});
|
||||
}
|
||||
|
||||
async run () {
|
||||
try {
|
||||
this.emit('start', this);
|
||||
await this.import();
|
||||
}
|
||||
catch (err) {
|
||||
this.emit('error', err);
|
||||
throw err;
|
||||
}
|
||||
finally {
|
||||
this._fileHandler.close();
|
||||
this.emit('end');
|
||||
}
|
||||
}
|
||||
|
||||
get isCancelled () {
|
||||
return this._isCancelled;
|
||||
}
|
||||
|
||||
cancel () {
|
||||
this._isCancelled = true;
|
||||
this.emit('cancel');
|
||||
this.emitUpdate({ op: 'cancelling' });
|
||||
}
|
||||
|
||||
emitUpdate (state: importer.ImportState) {
|
||||
this.emit('progress', { ...this._state, ...state });
|
||||
}
|
||||
|
||||
import () {
|
||||
throw new Error('Exporter must implement the "import" method');
|
||||
}
|
||||
}
|
85
src/main/libs/importers/sql/MySQLlImporter.ts
Normal file
85
src/main/libs/importers/sql/MySQLlImporter.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import * as mysql from 'mysql2';
|
||||
import * as importer from 'common/interfaces/importer';
|
||||
import * as fs from 'fs/promises';
|
||||
import MySQLParser from '../../parsers/MySQLParser';
|
||||
import { BaseImporter } from '../BaseImporter';
|
||||
|
||||
export default class MySQLImporter extends BaseImporter {
|
||||
protected _client: mysql.Pool
|
||||
|
||||
constructor (client: mysql.Pool, options: importer.ImportOptions) {
|
||||
super(options);
|
||||
this._client = client;
|
||||
}
|
||||
|
||||
async import (): Promise<void> {
|
||||
try {
|
||||
const { size: totalFileSize } = await fs.stat(this._options.file);
|
||||
const parser = new MySQLParser();
|
||||
let readPosition = 0;
|
||||
let queryCount = 0;
|
||||
|
||||
this.emitUpdate({
|
||||
fileSize: totalFileSize,
|
||||
readPosition: 0,
|
||||
percentage: 0,
|
||||
queryCount: 0
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this._fileHandler.pipe(parser);
|
||||
|
||||
parser.on('error', reject);
|
||||
|
||||
parser.on('close', async () => {
|
||||
console.log('TOTAL QUERIES', queryCount);
|
||||
console.log('import end');
|
||||
resolve();
|
||||
});
|
||||
|
||||
parser.on('data', async (query) => {
|
||||
queryCount++;
|
||||
parser.pause();
|
||||
|
||||
try {
|
||||
await this._client.query(query);
|
||||
}
|
||||
catch (error) {
|
||||
this.emit('query-error', {
|
||||
sql: query,
|
||||
message: error.sqlMessage,
|
||||
sqlSnippet: error.sql,
|
||||
time: new Date().getTime()
|
||||
});
|
||||
}
|
||||
|
||||
this.emitUpdate({
|
||||
queryCount,
|
||||
readPosition,
|
||||
percentage: readPosition / totalFileSize * 100
|
||||
});
|
||||
this._fileHandler.pipe(parser);
|
||||
parser.resume();
|
||||
});
|
||||
|
||||
parser.on('pause', () => {
|
||||
this._fileHandler.unpipe(parser);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(this._fileHandler as any).readableFlowing = false;
|
||||
});
|
||||
|
||||
this._fileHandler.on('data', (chunk) => {
|
||||
readPosition += chunk.length;
|
||||
});
|
||||
|
||||
this._fileHandler.on('error', (err) => {
|
||||
console.log(err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
}
|
85
src/main/libs/importers/sql/PostgreSQLImporter.ts
Normal file
85
src/main/libs/importers/sql/PostgreSQLImporter.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import * as pg from 'pg';
|
||||
import * as importer from 'common/interfaces/importer';
|
||||
import * as fs from 'fs/promises';
|
||||
import PostgreSQLParser from '../../parsers/PostgreSQLParser';
|
||||
import { BaseImporter } from '../BaseImporter';
|
||||
|
||||
export default class PostgreSQLImporter extends BaseImporter {
|
||||
protected _client: pg.PoolClient;
|
||||
|
||||
constructor (client: pg.PoolClient, options: importer.ImportOptions) {
|
||||
super(options);
|
||||
this._client = client;
|
||||
}
|
||||
|
||||
async import (): Promise<void> {
|
||||
try {
|
||||
const { size: totalFileSize } = await fs.stat(this._options.file);
|
||||
const parser = new PostgreSQLParser();
|
||||
let readPosition = 0;
|
||||
let queryCount = 0;
|
||||
|
||||
this.emitUpdate({
|
||||
fileSize: totalFileSize,
|
||||
readPosition: 0,
|
||||
percentage: 0,
|
||||
queryCount: 0
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this._fileHandler.pipe(parser);
|
||||
|
||||
parser.on('error', reject);
|
||||
|
||||
parser.on('close', async () => {
|
||||
console.log('TOTAL QUERIES', queryCount);
|
||||
console.log('import end');
|
||||
resolve();
|
||||
});
|
||||
|
||||
parser.on('data', async (query) => {
|
||||
queryCount++;
|
||||
parser.pause();
|
||||
|
||||
try {
|
||||
await this._client.query(query);
|
||||
}
|
||||
catch (error) {
|
||||
this.emit('query-error', {
|
||||
sql: query,
|
||||
message: error.hint || error.toString(),
|
||||
sqlSnippet: error.sql,
|
||||
time: new Date().getTime()
|
||||
});
|
||||
}
|
||||
|
||||
this.emitUpdate({
|
||||
queryCount,
|
||||
readPosition,
|
||||
percentage: readPosition / totalFileSize * 100
|
||||
});
|
||||
this._fileHandler.pipe(parser);
|
||||
parser.resume();
|
||||
});
|
||||
|
||||
parser.on('pause', () => {
|
||||
this._fileHandler.unpipe(parser);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(this._fileHandler as any).readableFlowing = false;
|
||||
});
|
||||
|
||||
this._fileHandler.on('data', (chunk) => {
|
||||
readPosition += chunk.length;
|
||||
});
|
||||
|
||||
this._fileHandler.on('error', (err) => {
|
||||
console.log(err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
}
|
103
src/main/libs/parsers/MySQLParser.ts
Normal file
103
src/main/libs/parsers/MySQLParser.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { Transform, TransformCallback, TransformOptions } from 'stream';
|
||||
|
||||
export default class MySQLParser extends Transform {
|
||||
private _buffer: string;
|
||||
private _lastChar: string;
|
||||
private _last9Chars: string;
|
||||
|
||||
encoding: BufferEncoding;
|
||||
delimiter: string;
|
||||
isEscape: boolean;
|
||||
currentQuote: string;
|
||||
isDelimiter: boolean;
|
||||
|
||||
constructor (opts?: TransformOptions & { delimiter: string }) {
|
||||
opts = {
|
||||
delimiter: ';',
|
||||
encoding: 'utf8',
|
||||
writableObjectMode: true,
|
||||
readableObjectMode: true,
|
||||
...opts
|
||||
};
|
||||
super(opts);
|
||||
this._buffer = '';
|
||||
this._lastChar = '';
|
||||
this._last9Chars = '';
|
||||
this.encoding = opts.encoding;
|
||||
this.delimiter = opts.delimiter;
|
||||
|
||||
this.isEscape = false;
|
||||
this.currentQuote = null;
|
||||
this.isDelimiter = false;
|
||||
}
|
||||
|
||||
_transform (chunk: Buffer, encoding: BufferEncoding, next: TransformCallback) {
|
||||
for (const char of chunk.toString(this.encoding)) {
|
||||
this.checkEscape();
|
||||
this._buffer += char;
|
||||
this._lastChar = char;
|
||||
this._last9Chars += char.trim().toLocaleLowerCase();
|
||||
|
||||
if (this._last9Chars.length > 9)
|
||||
this._last9Chars = this._last9Chars.slice(-9);
|
||||
|
||||
this.checkNewDelimiter(char);
|
||||
this.checkQuote(char);
|
||||
const query = this.getQuery();
|
||||
|
||||
if (query)
|
||||
this.push(query);
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
checkEscape () {
|
||||
if (this._buffer.length > 0) {
|
||||
this.isEscape = this._lastChar === '\\'
|
||||
? !this.isEscape
|
||||
: false;
|
||||
}
|
||||
}
|
||||
|
||||
checkNewDelimiter (char: string) {
|
||||
if (this.currentQuote === null && this._last9Chars === 'delimiter') {
|
||||
this.isDelimiter = true;
|
||||
this._buffer = '';
|
||||
}
|
||||
else {
|
||||
const isNewLine = char === '\n' || char === '\r';
|
||||
if (isNewLine && this.isDelimiter) {
|
||||
this.isDelimiter = false;
|
||||
this.delimiter = this._buffer.trim();
|
||||
this._buffer = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkQuote (char: string) {
|
||||
const isQuote = !this.isEscape && (char === '\'' || char === '"');
|
||||
if (isQuote && this.currentQuote === char)
|
||||
this.currentQuote = null;
|
||||
|
||||
else if (isQuote && this.currentQuote === null)
|
||||
this.currentQuote = char;
|
||||
}
|
||||
|
||||
getQuery () {
|
||||
if (this.isDelimiter)
|
||||
return false;
|
||||
|
||||
let query: false | string = false;
|
||||
let demiliterFound = false;
|
||||
if (this.currentQuote === null && this._buffer.length >= this.delimiter.length)
|
||||
demiliterFound = this._last9Chars.slice(-this.delimiter.length) === this.delimiter;
|
||||
|
||||
if (demiliterFound) {
|
||||
const parsedStr = this._buffer.trim();
|
||||
query = parsedStr.slice(0, parsedStr.length - this.delimiter.length);
|
||||
this._buffer = '';
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
}
|
158
src/main/libs/parsers/PostgreSQLParser.ts
Normal file
158
src/main/libs/parsers/PostgreSQLParser.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import { Transform, TransformCallback, TransformOptions } from 'stream';
|
||||
|
||||
export default class PostgreSQLParser extends Transform {
|
||||
private _buffer: string;
|
||||
private _lastChar: string;
|
||||
private _lastChars: string;
|
||||
private _bodyWrapper: string;
|
||||
private _bodyWrapperBuffer: string;
|
||||
private _firstDollarFound: boolean;
|
||||
private _isBody: boolean;
|
||||
private _isSingleLineComment: boolean;
|
||||
private _isMultiLineComment: boolean;
|
||||
|
||||
encoding: BufferEncoding;
|
||||
delimiter: string;
|
||||
isEscape: boolean;
|
||||
currentQuote: string;
|
||||
isDelimiter: boolean;
|
||||
|
||||
constructor (opts?: TransformOptions & { delimiter: string }) {
|
||||
opts = {
|
||||
delimiter: ';',
|
||||
encoding: 'utf8',
|
||||
writableObjectMode: true,
|
||||
readableObjectMode: true,
|
||||
...opts
|
||||
};
|
||||
super(opts);
|
||||
this._buffer = '';
|
||||
this._lastChar = '';
|
||||
this._lastChars = '';
|
||||
this.encoding = opts.encoding;
|
||||
this.delimiter = opts.delimiter;// ';'
|
||||
this._bodyWrapper = '';
|
||||
this._bodyWrapperBuffer = '';
|
||||
|
||||
this.isEscape = false;
|
||||
this.currentQuote = null;
|
||||
this._firstDollarFound = false;
|
||||
this._isBody = false;
|
||||
this._isSingleLineComment = false;
|
||||
this._isMultiLineComment = false;
|
||||
}
|
||||
|
||||
get _isComment () {
|
||||
return this._isSingleLineComment || this._isMultiLineComment;
|
||||
}
|
||||
|
||||
_transform (chunk: Buffer, encoding: BufferEncoding, next: TransformCallback) {
|
||||
for (const char of chunk.toString(this.encoding)) {
|
||||
this.checkEscape();
|
||||
this._buffer += char;
|
||||
this._lastChar = char;
|
||||
this._lastChars += char;
|
||||
|
||||
if (this._lastChars.length > this._bodyWrapper.length)
|
||||
this._lastChars = this._lastChars.slice(-(this._bodyWrapper.length || 2));
|
||||
|
||||
this.checkBodyWrapper(char);
|
||||
this.checkQuote(char);
|
||||
this.checkCommentRow();
|
||||
const query = this.getQuery();
|
||||
|
||||
if (query)
|
||||
this.push(query);
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
checkEscape () {
|
||||
if (this._buffer.length > 0) {
|
||||
this.isEscape = this._lastChar === '\\'
|
||||
? !this.isEscape
|
||||
: false;
|
||||
}
|
||||
}
|
||||
|
||||
checkCommentRow () {
|
||||
if (this._isBody) return;
|
||||
|
||||
if (!this._isComment) {
|
||||
if (this.currentQuote === null && this._lastChars.includes('--'))
|
||||
this._isSingleLineComment = true;
|
||||
|
||||
if (this.currentQuote === null && this._lastChars.includes('/*'))
|
||||
this._isMultiLineComment = true;
|
||||
}
|
||||
else {
|
||||
if (this._isSingleLineComment && (this._lastChar === '\n' || this._lastChar === '\r')) {
|
||||
this._buffer = '';
|
||||
this._isSingleLineComment = false;
|
||||
}
|
||||
|
||||
if (this._isMultiLineComment && this._lastChars.includes('*/')) {
|
||||
this._buffer = '';
|
||||
this._isMultiLineComment = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkBodyWrapper (char: string) {
|
||||
if (this._isBody)
|
||||
this._isBody = this._lastChars !== this._bodyWrapper;
|
||||
|
||||
if (this.currentQuote === null && char === '$' && !this._firstDollarFound && !this._bodyWrapper) {
|
||||
this._firstDollarFound = true;
|
||||
this._bodyWrapperBuffer += char;
|
||||
this._isBody = true;
|
||||
}
|
||||
else if (this._firstDollarFound) {
|
||||
if (char === '\n' || char === ' ') {
|
||||
this._firstDollarFound = false;
|
||||
this._bodyWrapperBuffer = '';
|
||||
this._bodyWrapper = '';
|
||||
this._isBody = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this._bodyWrapperBuffer += char;
|
||||
const isEndDollar = char === '$';
|
||||
|
||||
if (isEndDollar) {
|
||||
this._firstDollarFound = false;
|
||||
this._bodyWrapper = this._bodyWrapperBuffer;
|
||||
this._bodyWrapperBuffer = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkQuote (char: string) {
|
||||
const isQuote = !this.isEscape && (char === '\'' || char === '"');
|
||||
if (isQuote && this.currentQuote === char)
|
||||
this.currentQuote = null;
|
||||
|
||||
else if (isQuote && this.currentQuote === null)
|
||||
this.currentQuote = char;
|
||||
}
|
||||
|
||||
getQuery () {
|
||||
if (this._isBody || this._isComment)
|
||||
return false;
|
||||
|
||||
let query: false | string = false;
|
||||
let demiliterFound = false;
|
||||
|
||||
if (this.currentQuote === null && this._buffer.length >= this.delimiter.length)
|
||||
demiliterFound = this._lastChars.slice(-this.delimiter.length) === this.delimiter;
|
||||
|
||||
if (demiliterFound) {
|
||||
const parsedStr = this._buffer.trim();
|
||||
query = parsedStr;
|
||||
this._buffer = '';
|
||||
this._bodyWrapper = '';
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user