Compare commits
648 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be9df93a7e | |||
| 8ff30e426e | |||
| 7cdbef609e | |||
| f6195a468d | |||
| c23bf72d55 | |||
| c02441402b | |||
| b9a8764b55 | |||
| a2374c1981 | |||
| 9cfd5af704 | |||
| a6f473b8ed | |||
| e0a74402cb | |||
| c6e57b278e | |||
| e63f1f8f09 | |||
| 57da9c9885 | |||
| e6a3acf4c2 | |||
| 537869e862 | |||
| ae2ff7b3b1 | |||
| 1db01dbdb1 | |||
| 7988438dc7 | |||
| 3b32823f94 | |||
| 3370c754f2 | |||
| 2d84e5a611 | |||
| 8d5f73849e | |||
| 7a5019164a | |||
| f5733ea2d7 | |||
| 92e13220d8 | |||
| 2a5fdd852a | |||
| 7759fd862f | |||
| ca5dd0ac30 | |||
| 18be29fd88 | |||
| 6ea54a5b0a | |||
| 5d294f6236 | |||
| 5544b6291b | |||
| bf4841bca4 | |||
| 358a641449 | |||
| 7e1ceb69ae | |||
| 939f04ae62 | |||
| 2fc2ac491c | |||
| 20a5a50516 | |||
| 0932f4c537 | |||
| c46c9a4e16 | |||
| a20b4b3339 | |||
| dc302f89c7 | |||
| f5a2d142e2 | |||
| 1020a5820b | |||
| deb13505b8 | |||
| 71c06e31f7 | |||
| a203480a72 | |||
| c0fcd681be | |||
| 7402bb6823 | |||
| 35d791bee4 | |||
| ad4a599800 | |||
| 33db85f03b | |||
| ec5d05fc26 | |||
| face7ecdb5 | |||
| 6035319035 | |||
| e698da71fb | |||
| b466de781a | |||
| e5d583310d | |||
| f72dbf19c2 | |||
| 10538a04b4 | |||
| d71452a397 | |||
| 3f45bfcdd0 | |||
| 545e9863b6 | |||
| a37f2a5240 | |||
| d6b4c0a96b | |||
| d56e917b3f | |||
| 860c811504 | |||
| c93e8c35ec | |||
| 5278e5da0c | |||
| e7e3c307fc | |||
| e9af85038e | |||
| bf04721e36 | |||
| a810dc4204 | |||
| faad82fc34 | |||
| b8ae53db7d | |||
| 1571295ab6 | |||
| d3cc3a92c1 | |||
| 381dc6a535 | |||
| 6b9df571af | |||
| 897547371e | |||
| bf85a922ca | |||
| 00525f6b81 | |||
| 6dd27eb34f | |||
| 0b30386fee | |||
| 1f7f0ea8a2 | |||
| 0ae0cee766 | |||
| 4cbc7f3ae5 | |||
| f1cd0ab689 | |||
| 7f367a1f84 | |||
| 5405b9bf72 | |||
| 75f75d95a6 | |||
| ae5c539e31 | |||
| e7797cedc1 | |||
| f3c3ddd73a | |||
| c201f06103 | |||
| 111a3a678f | |||
| 51c4964003 | |||
| 5fac064a48 | |||
| e84f45ae39 | |||
| 7e119d40a4 | |||
| 22b47d1066 | |||
| ac0ea6a937 | |||
| ed7bfe0c21 | |||
| 55cac14ecf | |||
| 691635c5d1 | |||
| 9209a2d7d3 | |||
| 05b044d965 | |||
| e231a3a41a | |||
| 216614a4b1 | |||
| ae19d14951 | |||
| 7f639361b8 | |||
| ba706b85d3 | |||
| 3b91d921e8 | |||
| 660555d664 | |||
| 3550710f23 | |||
| 1429c29537 | |||
| 95113490f1 | |||
| 1eafdb944a | |||
| a32cd0b2ae | |||
| 249fbf3f96 | |||
| 64877af64a | |||
| ff94e46179 | |||
| 921bd4613a | |||
| 6f4a49ea97 | |||
| 9029fccad4 | |||
| edf3a072c5 | |||
| 0fde8c49a7 | |||
| 767c835a8e | |||
| 38f0223dc0 | |||
| ec707b5af3 | |||
| 462d5e3187 | |||
| 5cc4c07941 | |||
| 20de78f88a | |||
| a63d70ca7e | |||
| 672d6b88b2 | |||
| add1612c92 | |||
| 96b964e609 | |||
| 6b40190097 | |||
| f5b0bc5605 | |||
| 64a58252e5 | |||
| 46571684f6 | |||
| 710022539b | |||
| bc75d559b0 | |||
| a82ee5cc65 | |||
| 6647dd16f8 | |||
| 6f4e1e07f7 | |||
| 5cd951a9c1 | |||
| f492a215b4 | |||
| bbd2d74a28 | |||
| 2b697e21ba | |||
| 3a4e4ecbdc | |||
| 05cbb915d6 | |||
| 26a2bb75fa | |||
| 71b0bb78ec | |||
| 4e4eb39a19 | |||
| b6399c8271 | |||
| eb4a764407 | |||
| 27188eb2c5 | |||
| 57a997adc3 | |||
| a72a03cc3a | |||
| bb185d9e9f | |||
| 0298660714 | |||
| 8bf1dbb10d | |||
| 8e5ef98a7c | |||
| 72bd536aec | |||
| 1a4009a6b2 | |||
| e40357c052 | |||
| 222ea07cf2 | |||
| 6b3f398de3 | |||
| d796fa7ff4 | |||
| 6fdfd8717f | |||
| 81cea4c0f2 | |||
| 6a64633650 | |||
| 21702f1593 | |||
| 32ddb9c4c7 | |||
| 10fc62ceb7 | |||
| d3018a3136 | |||
| f9bcbd588b | |||
| 5c7d2bfd85 | |||
| 788f0ebf77 | |||
| 0eca5dd95d | |||
| 47322b0bbb | |||
| 518a05a6f0 | |||
| 352e426e17 | |||
| 666122f265 | |||
| 61e32f6d95 | |||
| 7aabc8f0be | |||
| a66dc03b99 | |||
| cf5ecb3150 | |||
| 46c365c5cd | |||
| ea76751e4a | |||
| 0bef3f8e71 | |||
| c26c9fae12 | |||
| 446c615bb8 | |||
| c516873541 | |||
| d4326de087 | |||
| eca966bb90 | |||
| 262b4732e3 | |||
| 7f9a30f568 | |||
| a89d2e1365 | |||
| fcd6f6c8fc | |||
| 8313d7f9f1 | |||
| 3a12601103 | |||
| 926949dc89 | |||
| 6b155083ef | |||
| 2b2ecac3ab | |||
| 35e9ff607d | |||
| ae037834f2 | |||
| 3ac24436ba | |||
| 2ca17e826c | |||
| 4fb6128499 | |||
| c359332746 | |||
| 1cd8e8e376 | |||
| 48ec2bdac8 | |||
| 2283e91532 | |||
| 647894ad60 | |||
| 574573abbb | |||
| a735a03cd7 | |||
| 83881a0dac | |||
| c04c6bbd2c | |||
| 42bbbc7ff4 | |||
| 1ecffeda71 | |||
| 92992d1e95 | |||
| bc9df9750f | |||
| 27e70e8031 | |||
| c823b8d19a | |||
| 170ff77eec | |||
| c241f5c562 | |||
| e06d964de4 | |||
| dfdb86de6f | |||
| a37b74f693 | |||
| 398d9f15df | |||
| ab7c2d7a31 | |||
| 090549ff91 | |||
| 10330c6597 | |||
| da2fe6a891 | |||
| 01b88221c5 | |||
| 46d25710b8 | |||
| a40ec7e66b | |||
| c8d2031d24 | |||
| 4f838e0ae3 | |||
| c7cc1b7611 | |||
| bf67a5f13d | |||
| e6bbe66873 | |||
| 1a930acf0a | |||
| a497467137 | |||
| b463416633 | |||
| ccf6240d65 | |||
| 5ccc12019d | |||
| f57fa9aee9 | |||
| 80e841a43d | |||
| e1d759041d | |||
| fd6df055c0 | |||
| 669d0b9dac | |||
| b9f9501e67 | |||
| 4b1c021871 | |||
| 1f79627dbe | |||
| dc18be07ce | |||
| 83ac45f8cf | |||
| c6cd865663 | |||
| 477636e0d7 | |||
| 77414ba934 | |||
| 86186072ed | |||
| 1216bcf9bf | |||
| 788ea70d32 | |||
| 18de37c4e4 | |||
| aeb81bd97f | |||
| a68660f1ab | |||
| 5abfa85a0e | |||
| 794f43d9ae | |||
| 4b2f762200 | |||
| fc3664571b | |||
| 5db8f11fd6 | |||
| 598674a7e0 | |||
| af17eceb27 | |||
| 90946c582d | |||
| d619e0f961 | |||
| 08311145c8 | |||
| a80e37a208 | |||
| c88114cabe | |||
| e33f3a1492 | |||
| 8328fdad33 | |||
| 8035380e7b | |||
| b4ea528643 | |||
| b0012872fa | |||
| 274fb595a2 | |||
| c7ef4b9231 | |||
| e64cfce423 | |||
| c0c9c7be20 | |||
| 2ae98d0c2d | |||
| a129834c16 | |||
| 71a9d6c5c0 | |||
| 1ce8f6bd1f | |||
| 85c4821606 | |||
| 06ed9d7dfc | |||
| 90de5edc99 | |||
| 72786e5dbb | |||
| d92c08548b | |||
| b1893234c7 | |||
| 534deff274 | |||
| bdeffea79c | |||
| ab2fdf26d2 | |||
| 0c902f037b | |||
| c0595aec0a | |||
| 6fdb20fc34 | |||
| 8e74031fb1 | |||
| 81e4b947b6 | |||
| c5d23410f4 | |||
| f23575c405 | |||
| 8cb98cf643 | |||
| b09a558670 | |||
| abc3c5f880 | |||
| 1c0d966c3c | |||
| 20d7264aab | |||
| 4cf987b89a | |||
| 5bcd3f807d | |||
| 9e04f2b9c6 | |||
| a7ceb8951c | |||
| 7392b223f4 | |||
| 51ce4f1bb5 | |||
| 059c310aaf | |||
| 11dad8ced3 | |||
| 18ed0fc020 | |||
| eaa1b73851 | |||
| 5c2c24e009 | |||
| 1957531600 | |||
| e47a165e11 | |||
| 3ca1adcb48 | |||
| 66bf1c847a | |||
| 47eea9b9b3 | |||
| 5c5a5f3b53 | |||
| 1c7729a797 | |||
| cd06f13fcb | |||
| 632870d448 | |||
| 762055379a | |||
| 7374749340 | |||
| e379be0107 | |||
| 2784053b83 | |||
| 12c4a0d498 | |||
| 35b5ea138d | |||
| d75e1fc660 | |||
| 3ab6df5da2 | |||
| 8d4fc391a4 | |||
| 35f9fc3741 | |||
| b465f3eb99 | |||
| c4e6a90722 | |||
| 9cc4af2b56 | |||
| 5ccdd7633b | |||
| 880f4403cb | |||
| cba391904a | |||
| 0fc397ace5 | |||
| 56a241b7f4 | |||
| 97eb999e4c | |||
| 9deaa89f21 | |||
| 74bae65e32 | |||
| 7091917578 | |||
| bbf72d9ed7 | |||
| 552d10ef48 | |||
| 7c5479157a | |||
| 2463dba380 | |||
| 2f9209a92d | |||
| 370bd92518 | |||
| ec083924fc | |||
| 22b450f7e0 | |||
| 1dd73e7319 | |||
| c4fe4b40dd | |||
| 251137ac60 | |||
| 0ad7c99274 | |||
| f53142d98a | |||
| 1f868523b0 | |||
| 94db02db2e | |||
| 9f0e06e663 | |||
| c6dab85fc2 | |||
| 59aa2e3f33 | |||
| 21b26773e6 | |||
| f308c5f6b0 | |||
| 2763b6028a | |||
| a3df6d6e7d | |||
| 473bfcbec5 | |||
| 8976c9e653 | |||
| 86795dcc63 | |||
| 9b90f15621 | |||
| 7d0d9d3e22 | |||
| 17f0248a3e | |||
| 25d3dcad59 | |||
| cbd857422f | |||
| e65b4d0c2a | |||
| 6bcebb63e4 | |||
| ac7708138c | |||
| 9d8ec9cc6b | |||
| 1b8a2cb923 | |||
| a97ab9c09e | |||
| 9a73eb3620 | |||
| f50e460335 | |||
| fa72d9a39f | |||
| 75b4f49e31 | |||
| a069093f6b | |||
| 62c741198a | |||
| 0266d912e0 | |||
| 55bc0fc93f | |||
| 47c00d7eb0 | |||
| ad9fac861e | |||
| 14afd08fcb | |||
| 319580554f | |||
| c750bd04ad | |||
| bdd55d8432 | |||
| 98464e414b | |||
| 2f2d9c45a3 | |||
| 3665a0d064 | |||
| c19c69266a | |||
| bafa2c2fff | |||
| 2d823140b9 | |||
| 1fb4a06092 | |||
| cb450a0313 | |||
| 7aaf6bb024 | |||
| 6a02ba3220 | |||
| 83610783e0 | |||
| cec26b0614 | |||
| fbf288198b | |||
| 193940fd63 | |||
| bd169c316a | |||
| 5315f65cfb | |||
| 5a859d81d3 | |||
| 904fc4d500 | |||
| 634fe18127 | |||
| 89a9cc4380 | |||
| 57c62fbe27 | |||
| 590eff1e3b | |||
| a71309a604 | |||
| 343e983b64 | |||
| e31a52b659 | |||
| 41162ee2c3 | |||
| 55745c18e9 | |||
| 7d6b77ad2a | |||
| 90813b23d8 | |||
| a999d29b1d | |||
| f6f9b0a61a | |||
| 724edf44cb | |||
| 07248ca49f | |||
| 3a068c37b5 | |||
| df44e5f6e9 | |||
| 9328d966ba | |||
| 1a293deec7 | |||
| 665a70ba3d | |||
| 967587c8e4 | |||
| 4927c13e55 | |||
| 1f9f997748 | |||
| 521e4ea3a2 | |||
| 941843e4c0 | |||
| 1434a42421 | |||
| 8f57d3a316 | |||
| a74b789a8c | |||
| ac4dd37249 | |||
| 75b3b4e012 | |||
| 188ab4c483 | |||
| f9a562808d | |||
| 4ea763124b | |||
| 836d15c68f | |||
| 8ce4c0a7ce | |||
| 9613c2c410 | |||
| e5d4bbadc1 | |||
| 5d4d2a447a | |||
| d905962298 | |||
| 4ab9ad6881 | |||
| 2ce20b5fac | |||
| 81297383cb | |||
| 2aed60390c | |||
| 67386da136 | |||
| bdc40c2c02 | |||
| 1d916e43d5 | |||
| 98bff4925a | |||
| 9af2c80b05 | |||
| 3a03c82f8d | |||
| de66e75eb2 | |||
| 10d79dca4d | |||
| 460f511bf6 | |||
| 81207f95d8 | |||
| 3d3aca3290 | |||
| d661b9f6a4 | |||
| 7deeb78d69 | |||
| 4dcf47b81f | |||
| a674b9b3a1 | |||
| b2aa4d9377 | |||
| bc4a595815 | |||
| 2704825d03 | |||
| 456d3ba42e | |||
| 803a272331 | |||
| 89e4bfe3e1 | |||
| 697440693e | |||
| c0cd04a96f | |||
| c92f02bdda | |||
| c4766d163c | |||
| 18889092aa | |||
| adafa3c5c4 | |||
| 91994016a7 | |||
| dfcf253217 | |||
| bd3503912f | |||
| b062c5fd66 | |||
| b1cd60d0dd | |||
| 67ac1a1c8d | |||
| c166eab2e8 | |||
| c6b3ced493 | |||
| 4e922e806d | |||
| 0e087565b3 | |||
| 104b25d898 | |||
| 9bdff41ec1 | |||
| d27b0a7be3 | |||
| 076b20ef6d | |||
| d93d107039 | |||
| 708dcfd088 | |||
| 14501a70b9 | |||
| 3b82679c2d | |||
| 48d4fb4fec | |||
| a03ca73d93 | |||
| a46e592cfb | |||
| 634bea1bda | |||
| 3dfa23a30c | |||
| 24408dd7c2 | |||
| 32c7919885 | |||
| 967615b6e5 | |||
| aee3a28465 | |||
| 3fdf27f820 | |||
| e9302c7d6f | |||
| c38fe83e48 | |||
| adfc427d25 | |||
| 3912a58127 | |||
| 720d25e838 | |||
| bc1d77a6f8 | |||
| 00a8d472ff | |||
| 5076ee0463 | |||
| 4a5ddd65f4 | |||
| ec3c224f44 | |||
| 39b99ecf8f | |||
| 50232f9b90 | |||
| 66e8cc6e1d | |||
| f6b4c94d00 | |||
| f3ce240bcd | |||
| 7be9bf1bab | |||
| d39871be70 | |||
| 3324eec011 | |||
| dd9790fca5 | |||
| 41a3769c5f | |||
| 42601ff960 | |||
| e54cffbaf3 | |||
| f8dbad362c | |||
| 925db50418 | |||
| f40db68579 | |||
| 3afde5f1fa | |||
| b631519009 | |||
| f05c60c628 | |||
| e235de6d56 | |||
| da47c437e0 | |||
| f0048bc6cf | |||
| f80ae284fa | |||
| 06753ff312 | |||
| 1e07614306 | |||
| 2d716ba43a | |||
| c39dc6295d | |||
| 7557666135 | |||
| 5ee65124cb | |||
| ab79617377 | |||
| 3b2a47a4ef | |||
| 7b4d9d8717 | |||
| 4c9734ac7f | |||
| 152e8a80ab | |||
| 98a348b091 | |||
| e448f63ec3 | |||
| 0b13850eca | |||
| de97404602 | |||
| 2c0b76fb3f | |||
| cbd6ce7872 | |||
| 3c479eb33c | |||
| beaff158cc | |||
| 6806620d90 | |||
| 4459347169 | |||
| 98e01497e9 | |||
| 4ed4c8c32c | |||
| 620acecdff | |||
| 2d214cfdb3 | |||
| cd36259739 | |||
| d049d8c571 | |||
| 7c51fcad96 | |||
| 1948c8ef89 | |||
| f2b2ac6fd0 | |||
| 4098f63ce2 | |||
| 4d903abd85 | |||
| f00eb2d3ef | |||
| 6752dcfd39 | |||
| c3022eb80a | |||
| 2f6cbf25df | |||
| 8dfc2e7bcd | |||
| fa0ad477cc | |||
| ac6a68c38d | |||
| 25223471e7 | |||
| 56535b1e6f | |||
| 131c51f7c4 | |||
| a27a2077ed | |||
| 7758fabc89 | |||
| c9da9bdd23 | |||
| 9520b053af | |||
| 8a1e717c1b | |||
| 21127f661a | |||
| 7d614a2395 | |||
| 669ae024f9 | |||
| 9d8dd558e2 | |||
| 67f58a8dfe | |||
| 52d230b9e2 | |||
| fd2a35fb4a | |||
| d61b5e135f | |||
| ef23b786ac | |||
| 29a66bfcb0 | |||
| ef5e30df3d | |||
| ab28a06bef | |||
| 9910c54aa6 | |||
| 6ec431f471 | |||
| 3ec7f651c1 | |||
| 87aa60bc3e | |||
| 73874aa5a1 | |||
| 976438f860 | |||
| eb095b7c44 | |||
| 3ca745e74b | |||
| 040de84d93 | |||
| 4f1d63440e | |||
| 7c3cf1bb67 | |||
| cbf1b0a3cc | |||
| 5287a86397 | |||
| ae599ac6f6 | |||
| a08a8ef208 | |||
| 19a4d97765 | |||
| 6f1f5f84c6 | |||
| e2f352149d | |||
| 1fa39b20d2 | |||
| 53dc2e6f03 | |||
| 555f30c0b3 | |||
| 7549d37a04 | |||
| 29072eb71b | |||
| 48e9e77be5 | |||
| 4dd3f15ba3 | |||
| 3603501ae2 | |||
| 338180a21a | |||
| 28193ed6f3 | |||
| 0509710602 | |||
| a4872b4159 | |||
| 888f5c6260 | |||
| 7a5abb5f47 | |||
| 61a9f02899 | |||
| 354c4201f7 |
@@ -12,7 +12,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-12, windows-2022, ubuntu-22.04]
|
||||
os: [macos-14, windows-2022, ubuntu-22.04]
|
||||
# os: [macOS-10.15]
|
||||
|
||||
steps:
|
||||
@@ -47,9 +47,6 @@ jobs:
|
||||
yarn printSecrets
|
||||
env:
|
||||
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
|
||||
- name: fillNativeModulesElectron
|
||||
run: |
|
||||
yarn fillNativeModulesElectron
|
||||
- name: fillPackagedPlugins
|
||||
run: |
|
||||
yarn fillPackagedPlugins
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
# os: [windows-2022]
|
||||
# os: [ubuntu-22.04]
|
||||
# os: [windows-2022, ubuntu-22.04]
|
||||
os: [macos-12, windows-2022, ubuntu-22.04]
|
||||
os: [macos-14, windows-2022, ubuntu-22.04]
|
||||
# os: [macOS-10.15]
|
||||
|
||||
steps:
|
||||
@@ -55,11 +55,11 @@ jobs:
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
yarn adjustPackageJson
|
||||
- name: adjustPackageJsonPremium
|
||||
- name: adjustAppPackageJsonPremium
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
node adjustPackageJsonPremium
|
||||
node adjustAppPackageJsonPremium
|
||||
- name: setUpdaterChannel premium-beta
|
||||
run: |
|
||||
cd ..
|
||||
@@ -87,11 +87,6 @@ jobs:
|
||||
yarn printSecrets
|
||||
env:
|
||||
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
|
||||
- name: fillNativeModulesElectron
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
yarn fillNativeModulesElectron
|
||||
- name: fillPackagedPlugins
|
||||
run: |
|
||||
cd ..
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# os: [ubuntu-22.04, windows-2016]
|
||||
os: [macos-12, windows-2022, ubuntu-22.04]
|
||||
os: [macos-14, windows-2022, ubuntu-22.04]
|
||||
|
||||
steps:
|
||||
- name: Context
|
||||
@@ -56,11 +56,11 @@ jobs:
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
yarn adjustPackageJson
|
||||
- name: yarn adjustPackageJsonPremium
|
||||
- name: adjustAppPackageJsonPremium
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
node adjustPackageJsonPremium
|
||||
node adjustAppPackageJsonPremium
|
||||
- name: setUpdaterChannel premium
|
||||
run: |
|
||||
cd ..
|
||||
@@ -88,11 +88,6 @@ jobs:
|
||||
yarn printSecrets
|
||||
env:
|
||||
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
|
||||
- name: fillNativeModulesElectron
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
yarn fillNativeModulesElectron
|
||||
- name: fillPackagedPlugins
|
||||
run: |
|
||||
cd ..
|
||||
@@ -128,6 +123,7 @@ jobs:
|
||||
cp ../dbgate-merged/app/dist/*win_arm64.zip artifacts/dbgate-premium-windows-latest-arm64.zip || true
|
||||
cp ../dbgate-merged/app/dist/*-mac_universal.dmg artifacts/dbgate-premium-latest.dmg || true
|
||||
cp ../dbgate-merged/app/dist/*-mac_x64.dmg artifacts/dbgate-premium-latest-x64.dmg || true
|
||||
cp ../dbgate-merged/app/dist/*-mac_arm64.dmg artifacts/dbgate-premium-latest-arm64.dmg || true
|
||||
|
||||
mv ../dbgate-merged/app/dist/*.exe artifacts/ || true
|
||||
mv ../dbgate-merged/app/dist/*.zip artifacts/ || true
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# os: [ubuntu-22.04, windows-2016]
|
||||
os: [macos-12, windows-2022, ubuntu-22.04]
|
||||
os: [macos-14, windows-2022, ubuntu-22.04]
|
||||
|
||||
steps:
|
||||
- name: Context
|
||||
@@ -50,9 +50,6 @@ jobs:
|
||||
yarn printSecrets
|
||||
env:
|
||||
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
|
||||
- name: fillNativeModulesElectron
|
||||
run: |
|
||||
yarn fillNativeModulesElectron
|
||||
- name: fillPackagedPlugins
|
||||
run: |
|
||||
yarn fillPackagedPlugins
|
||||
@@ -101,6 +98,7 @@ jobs:
|
||||
cp app/dist/*win_arm64.zip artifacts/dbgate-windows-latest-arm64.zip || true
|
||||
cp app/dist/*-mac_universal.dmg artifacts/dbgate-latest.dmg || true
|
||||
cp app/dist/*-mac_x64.dmg artifacts/dbgate-latest-x64.dmg || true
|
||||
cp app/dist/*-mac_arm64.dmg artifacts/dbgate-latest-arm64.dmg || true
|
||||
|
||||
mv app/dist/*.exe artifacts/ || true
|
||||
mv app/dist/*.zip artifacts/ || true
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
name: AWS image PREMIUM
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
# - 'v[0-9]+.[0-9]+.[0-9]+-premium-beta.[0-9]+'
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+-packer-beta.[0-9]+'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04]
|
||||
|
||||
steps:
|
||||
- name: Context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18.x
|
||||
|
||||
- name: Setup `packer`
|
||||
uses: hashicorp/setup-packer@main
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Checkout dbgate/dbgate-pro
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
mv dbgate-pro/* ../dbgate-pro/
|
||||
cd ..
|
||||
mkdir dbgate-merged
|
||||
cd dbgate-pro
|
||||
cd sync
|
||||
yarn
|
||||
node sync.js --nowatch
|
||||
cd ..
|
||||
|
||||
- name: yarn adjustPackageJson
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
yarn adjustPackageJson
|
||||
|
||||
- name: yarn install
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
yarn install
|
||||
|
||||
- name: setCurrentVersion
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
yarn setCurrentVersion
|
||||
|
||||
- name: printSecrets
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
yarn printSecrets
|
||||
env:
|
||||
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
|
||||
|
||||
- name: Prepare packer build
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
yarn run prepare:packer
|
||||
cd packer
|
||||
zip -r cloud-build.zip build
|
||||
|
||||
- name: Copy artifacts
|
||||
run: |
|
||||
mkdir artifacts
|
||||
cp ../dbgate-merged/packer/cloud-build.zip artifacts/cloud-build.zip || true
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.os }}
|
||||
path: artifacts
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
files: 'artifacts/**'
|
||||
prerelease: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run `packer init`
|
||||
run: |
|
||||
cd ../dbgate-merged/packer
|
||||
packer init ./aws-ubuntu.pkr.hcl
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}}
|
||||
AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}}
|
||||
AWS_DEFAULT_REGION: ${{secrets.AWS_DEFAULT_REGION}}
|
||||
|
||||
- name: Run `packer build`
|
||||
run: |
|
||||
cd ../dbgate-merged/packer
|
||||
packer build ./aws-ubuntu.pkr.hcl
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}}
|
||||
AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}}
|
||||
AWS_DEFAULT_REGION: ${{secrets.AWS_DEFAULT_REGION}}
|
||||
|
||||
# - name: Install AWS CLI
|
||||
# run: |
|
||||
# curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
|
||||
# unzip awscliv2.zip
|
||||
# sudo ./aws/install
|
||||
# sudo apt-get install jq -y
|
||||
|
||||
- name: Install jq
|
||||
run: |
|
||||
sudo apt-get install jq -y
|
||||
|
||||
- name: Delete old AMIs
|
||||
run: |
|
||||
cd ../dbgate-merged/packer
|
||||
chmod +x delete-old-amis.sh
|
||||
./delete-old-amis.sh
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}}
|
||||
AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}}
|
||||
AWS_DEFAULT_REGION: ${{secrets.AWS_DEFAULT_REGION}}
|
||||
@@ -62,6 +62,12 @@ jobs:
|
||||
node sync.js --nowatch
|
||||
cd ..
|
||||
|
||||
- name: yarn adjustPackageJson
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
yarn adjustPackageJson
|
||||
|
||||
- name: yarn install
|
||||
run: |
|
||||
cd ..
|
||||
|
||||
@@ -56,6 +56,11 @@ jobs:
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18.x
|
||||
|
||||
- name: yarn adjustPackageJson
|
||||
run: |
|
||||
yarn adjustPackageJson
|
||||
|
||||
- name: yarn install
|
||||
run: |
|
||||
# yarn --version
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
name: NPM packages PREMIUM
|
||||
|
||||
# on: [push]
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+'
|
||||
|
||||
# on:
|
||||
# push:
|
||||
# branches:
|
||||
# - production
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04]
|
||||
|
||||
steps:
|
||||
- name: Context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18.x
|
||||
|
||||
- name: Checkout dbgate/dbgate-pro
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: dbgate/dbgate-pro
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
path: dbgate-pro
|
||||
|
||||
- name: Merge dbgate/dbgate-pro
|
||||
run: |
|
||||
mkdir ../dbgate-pro
|
||||
mv dbgate-pro/* ../dbgate-pro/
|
||||
cd ..
|
||||
mkdir dbgate-merged
|
||||
cd dbgate-pro
|
||||
cd sync
|
||||
yarn
|
||||
node sync.js --nowatch
|
||||
cd ..
|
||||
|
||||
- name: adjustNpmPackageJsonPremium
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
node adjustNpmPackageJsonPremium
|
||||
|
||||
- name: Configure NPM token
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
|
||||
|
||||
- name: Remove dbmodel - should be not published
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
rm -rf packages/dbmodel
|
||||
|
||||
- name: yarn install
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
yarn install
|
||||
|
||||
- name: setCurrentVersion
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
yarn setCurrentVersion
|
||||
|
||||
- name: printSecrets
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged
|
||||
yarn printSecrets
|
||||
env:
|
||||
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
|
||||
|
||||
- name: Publish dbgate-api-premium
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged/packages/api
|
||||
npm publish
|
||||
|
||||
- name: Publish dbgate-web-premium
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged/packages/web
|
||||
npm publish
|
||||
|
||||
- name: Publish dbgate-serve-premium
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged/packages/serve
|
||||
npm publish
|
||||
|
||||
- name: Publish dbgate-plugin-cosmosdb
|
||||
run: |
|
||||
cd ..
|
||||
cd dbgate-merged/plugins/dbgate-plugin-cosmosdb
|
||||
npm publish
|
||||
@@ -90,11 +90,6 @@ jobs:
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish dbgate (obsolete)
|
||||
working-directory: packages/dbgate
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish dbgate-serve
|
||||
working-directory: packages/serve
|
||||
run: |
|
||||
|
||||
@@ -12,16 +12,27 @@ jobs:
|
||||
container: node:18
|
||||
|
||||
steps:
|
||||
- name: Context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Install dependencies for cypress
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y xvfb libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: yarn install
|
||||
run: |
|
||||
yarn install
|
||||
- name: Build packer dist for cypress
|
||||
run: |
|
||||
yarn prepare:packer
|
||||
- name: yarn install cypress
|
||||
run: |
|
||||
cd e2e-tests
|
||||
yarn install
|
||||
- name: Run Cypress tests
|
||||
run: |
|
||||
cd e2e-tests
|
||||
yarn test:ci
|
||||
- name: Integration tests
|
||||
run: |
|
||||
cd integration-tests
|
||||
@@ -84,5 +95,10 @@ jobs:
|
||||
env:
|
||||
CLICKHOUSE_ADMIN_PASSWORD: Pwd2020Db
|
||||
|
||||
oracle:
|
||||
image: gvenzl/oracle-xe:21-slim
|
||||
env:
|
||||
ORACLE_PASSWORD: Pwd2020Db
|
||||
|
||||
# cockroachdb:
|
||||
# image: cockroachdb/cockroach
|
||||
|
||||
@@ -28,8 +28,6 @@ docker/plugins
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
app/src/nativeModulesContent.js
|
||||
packages/api/src/nativeModulesContent.js
|
||||
packages/api/src/packagedPluginsContent.js
|
||||
.VSCodeCounter
|
||||
|
||||
|
||||
+89
-1
@@ -8,11 +8,99 @@ Builds:
|
||||
- linux - application for linux
|
||||
- win - application for Windows
|
||||
|
||||
### 6.1.0
|
||||
- ADDED: Fulltext search in DB model and connections, highlight searched names
|
||||
- ADDED: Tab preview mode configuration #963
|
||||
- CHANGED: Single-click to open server connection/database + ability to configure this #959
|
||||
- ADDED: Option to align numbers to right in data grid #957
|
||||
- FIXED: Cursor Becomes Stuck When Escaping "Case" #954
|
||||
- ADDED: Postgres GEOGRAPHY types are shown on map, event when executing query #948
|
||||
- FIXED: Error displaying CLOB and NCLOB in Oracle
|
||||
- FIXED: Analysing of foreign keys in Postgres and MS SQL, when the same FKS are used across different schemas
|
||||
- ADDED: Support of views, procedures, functions to Oracle. Added integration tests for Oracle
|
||||
- ADDED: Display "No rows" message, quick add new row
|
||||
- ADDED: Choose default database from list
|
||||
- ADDED: Default database is automatically selected on connect
|
||||
- ADDED: Apple-Silicon-only build for Mac #949
|
||||
- ADDED: Display comment into tables and column list #755
|
||||
|
||||
### 6.0.0
|
||||
- ADDED: Order or filter the indexes for huge tables #922
|
||||
- ADDED: Empty string filters
|
||||
- CHANGED: (Premium) Workflow for new installation (used in Docker and AWS distribution)
|
||||
- ADDED: Show stored procedure and function parameters (MySQL, PostgreSQL, SQL Server, MariaDB) #348
|
||||
- FIXED: Selected database has changed when closing database grouped tab #983
|
||||
- ADDED: Add line break option to editor #823
|
||||
- ADDED: Order or filter the indexes for huge tables #922
|
||||
- ADDED: Preview mode for the top bar tab like vscode #767
|
||||
- ADDED: Keyboard navigatioon between connections, databases and tables
|
||||
- FIXED: Fixed some issues in connection search
|
||||
- FIXED: Schema selection in Export does not provide all schemas #924
|
||||
- CHANGED: Standardized Window menu in MacOS app
|
||||
- FIXED: Typecast ::date is treated as a parameter #925
|
||||
- FIXED: App crashes when trying to 'Open Structure' in a readonly connection #926
|
||||
- FIXED: Selected database has changed when closing database grouped tab #938
|
||||
- CHANGED: (Premium) Query designer and Query perspective designer moved to Premium editioin
|
||||
- CHANGED: (Premium) Compare database tool - many improvements, moved to Premium edition
|
||||
- ADDED: (Premium) Export DB model - exporting model to YAML folder, JSON or SQL folder
|
||||
- CHANGED: Model deployer - many improvements, support of rename missing objects
|
||||
- ADDED: (Premium) Premium NPM distribution
|
||||
- CHANGED: (Premium) Amazon Redshift driver moved to Premium edition
|
||||
- ADDED: Generated API documentation https://dbgate.org/docs/apidoc.html
|
||||
- ADDED: NPM distribution now supports all dbgate database connectors, many improvements NPM packages
|
||||
- CHANGED: Optimalized size of NPM plugins (eg. dbgate-plugin-mssql from 1.34 MB to 71 kB)
|
||||
- CHANGED: Unsaved connections are now shown in "Recent and unsaved" folder after disconnect
|
||||
- FIXED: Correctly show focused control, as defined by UX standards
|
||||
- ADDED: Data duplicator - weak references
|
||||
- ADDED: View JSON detail of log messages from export/import jobs and query executions
|
||||
- ADDED: Rename procedure/function context menu
|
||||
- ADDED: Show SQL quick view
|
||||
|
||||
### 5.5.6
|
||||
- FIXED: DbGate process consumes 100% after UI closed - Mac, Linux (#917, #915)
|
||||
- FIXED: Correctly closing connection behind SSH tunnel (#920)
|
||||
- FIXED: Updating MongoDB documents on MongoDB 4 (#916)
|
||||
- FIXED: (Premium) DbGate container correctly waits for underlying storage database, if database container is started after dbgate container is started
|
||||
- FIXED: (Premium) Better handling of connection storage errors
|
||||
|
||||
### 5.5.5
|
||||
- ADDED: AWS IAM authentication for MySQL, MariaDB, PostgreSQL (Premium)
|
||||
- FIXED: Datitme filtering #912
|
||||
- FIXED: Load redis keys
|
||||
- ADDED: Query parameters #913
|
||||
- FIXED: Data grid with hidden columns #911
|
||||
- ADDED: Added buttons for one-click authentification methods (Anonymous, OAuth) (Team Premium)
|
||||
- ADDED: Link for switching Admin/user login (Team Premium)
|
||||
- FIXED: Save connection params in administration for MS SQL and Postgres storages (Team Premium)
|
||||
|
||||
### 5.5.4
|
||||
- FIXED: correct handling when use LOGIN and PASSWORD env variables #903
|
||||
- FIXED: fixed problems in dbmodel commandline tool
|
||||
- ADDED: dbmodel - allow connection defined in environment variables
|
||||
- FIXED: Load postgres schema on Azure #906
|
||||
- FIXED: Oauth2 in combination with Google doesn't log payload #727
|
||||
- CHANGED: Improved error reporting for unhandler errors
|
||||
- CHANGED: Don't restart docker container in case of unhandler error
|
||||
- FIXED: Crash when displaying specific data values from MongoDB #908
|
||||
- ADDED: (Premium) Show purchase button after trial license is expired
|
||||
|
||||
### 5.5.3
|
||||
- FIXED: Separate schema mode #894 - for databases with many schemas
|
||||
- FIXED: Sort by UUID column in POstgreSQL #895
|
||||
- ADDED: Load pg_dump outputs #893
|
||||
- ADDED: Improved column mapping in import/export #330
|
||||
- FIXED: Fixed some errors in create-table workflow
|
||||
- CHANGED: Show single schema by default only if all objects are from default schema
|
||||
- FIXED: MS Entra authentication for Azure SQL
|
||||
|
||||
### 5.5.2
|
||||
- FIXED: MySQL, PostgreSQL readonly conections #900
|
||||
|
||||
### 5.5.1
|
||||
- ADDED: Clickhouse support (#532)
|
||||
- ADDED: MySQL - specify table engine, show table engine in table list
|
||||
- FIXED: Hidden primary key name in PK editor for DB engines with anonymous PK (MySQL)
|
||||
- CHANGED: Import/export dialog is now tab instead of modal
|
||||
- CHANGED: Import/export dialog is now tacub instead of modal
|
||||
- ADDED: Saving import/export job
|
||||
- REMOVED: Ability to reopen export/import wizard from generated script. This was a bit hack, now you could save import/export job instead
|
||||
- ADDED: Autodetect CSV delimited
|
||||
|
||||
@@ -17,6 +17,8 @@ DbGate is licensed under GPL-3.0 license and is free to use for any purpose.
|
||||
* Try it online - [demo.dbgate.org](https://demo.dbgate.org) - online demo application
|
||||
* **Download** application for Windows, Linux or Mac from [dbgate.org](https://dbgate.org/download/)
|
||||
* Run web version as [NPM package](https://www.npmjs.com/package/dbgate-serve) or as [docker image](https://hub.docker.com/r/dbgate/dbgate)
|
||||
* Use nodeJs [scripting interface](https://dbgate.org/docs/scripting.html) ([API documentation](https://dbgate.org/docs/apidoc.html))
|
||||
* [Recommend DbGate](https://testimonial.to/dbgate) | [Rate on G2](https://www.g2.com/products/dbgate/reviews)
|
||||
|
||||
## Supported databases
|
||||
* MySQL
|
||||
@@ -26,7 +28,7 @@ DbGate is licensed under GPL-3.0 license and is free to use for any purpose.
|
||||
* MongoDB
|
||||
* Redis
|
||||
* SQLite
|
||||
* Amazon Redshift
|
||||
* Amazon Redshift (Premium)
|
||||
* CockroachDB
|
||||
* MariaDB
|
||||
* CosmosDB (Premium)
|
||||
|
||||
+40
-2
@@ -1,12 +1,50 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const volatilePackages = require('./common/volatilePackages');
|
||||
|
||||
function adjustFile(file) {
|
||||
function adjustFile(file, isApp = false) {
|
||||
const json = JSON.parse(fs.readFileSync(file, { encoding: 'utf-8' }));
|
||||
|
||||
function processPackageFile(packageFile) {
|
||||
const pluginJson = JSON.parse(fs.readFileSync(packageFile, { encoding: 'utf-8' }));
|
||||
for (const depkey of ['dependencies', 'optionalDependencies']) {
|
||||
for (const dependency of Object.keys(pluginJson[depkey] || {})) {
|
||||
if (!volatilePackages.includes(dependency)) {
|
||||
// add only voletile packages
|
||||
continue;
|
||||
}
|
||||
if (!json[depkey]) {
|
||||
json[depkey] = {};
|
||||
}
|
||||
if (json[depkey][dependency]) {
|
||||
if (json[depkey][dependency] != pluginJson[depkey][dependency]) {
|
||||
console.log(`Dependency ${dependency} in ${packageName} is different from ${file}`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
json[depkey][dependency] = pluginJson[depkey][dependency];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const packageName of fs.readdirSync('plugins')) {
|
||||
if (!packageName.startsWith('dbgate-plugin-')) continue;
|
||||
processPackageFile(path.join('plugins', packageName, 'package.json'));
|
||||
}
|
||||
|
||||
if (isApp) {
|
||||
// add volatile dependencies from api to app
|
||||
processPackageFile(path.join('packages', 'api', 'package.json'));
|
||||
}
|
||||
|
||||
if (process.platform != 'win32') {
|
||||
delete json.optionalDependencies.msnodesqlv8;
|
||||
}
|
||||
|
||||
fs.writeFileSync(file, JSON.stringify(json, null, 2), 'utf-8');
|
||||
}
|
||||
|
||||
adjustFile('packages/api/package.json');
|
||||
adjustFile('app/package.json');
|
||||
adjustFile('app/package.json', true);
|
||||
|
||||
fs.writeFileSync('common/useBundleExternals.js', "module.exports = 'true';", 'utf-8');
|
||||
|
||||
+5
-8
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dbgate",
|
||||
"version": "5.0.0-alpha.1",
|
||||
"version": "6.0.0-alpha.1",
|
||||
"private": true,
|
||||
"author": "Jan Prochazka <jenasoft.database@gmail.com>",
|
||||
"description": "Opensource database administration tool",
|
||||
@@ -38,7 +38,8 @@
|
||||
"target": "default",
|
||||
"arch": [
|
||||
"universal",
|
||||
"x64"
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -109,7 +110,8 @@
|
||||
"files": [
|
||||
"packages",
|
||||
"src",
|
||||
"icon.png"
|
||||
"icon.png",
|
||||
"!node_modules/cpu-features/build/**"
|
||||
]
|
||||
},
|
||||
"homepage": "./",
|
||||
@@ -130,10 +132,5 @@
|
||||
"electron": "30.0.2",
|
||||
"electron-builder": "23.1.0",
|
||||
"electron-builder-notarize": "^1.5.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"better-sqlite3": "9.6.0",
|
||||
"msnodesqlv8": "^4.2.1",
|
||||
"oracledb": "^6.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -108,7 +108,7 @@ function commandItem(item) {
|
||||
}
|
||||
|
||||
function buildMenu() {
|
||||
let template = _cloneDeepWith(mainMenuDefinition({ editMenu: true }), item => {
|
||||
let template = _cloneDeepWith(mainMenuDefinition({ editMenu: true, isMac: isMac() }), item => {
|
||||
if (item.divider) {
|
||||
return { type: 'separator' };
|
||||
}
|
||||
@@ -430,7 +430,6 @@ function createWindow() {
|
||||
);
|
||||
|
||||
global.API_PACKAGE = apiPackage;
|
||||
global.NATIVE_MODULES = path.join(__dirname, 'nativeModules');
|
||||
|
||||
// console.log('global.API_PACKAGE', global.API_PACKAGE);
|
||||
const api = require(apiPackage);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = ({ editMenu }) => [
|
||||
module.exports = ({ editMenu, isMac }) => [
|
||||
{
|
||||
label: 'File',
|
||||
submenu: [
|
||||
@@ -9,9 +9,9 @@ module.exports = ({ editMenu }) => [
|
||||
{ command: 'new.queryDesign', hideDisabled: true },
|
||||
{ command: 'new.diagram', hideDisabled: true },
|
||||
{ command: 'new.perspective', hideDisabled: true },
|
||||
{ command: 'new.freetable', hideDisabled: true },
|
||||
{ command: 'new.shell', hideDisabled: true },
|
||||
{ command: 'new.jsonl', hideDisabled: true },
|
||||
{ command: 'new.modelTransform', hideDisabled: true },
|
||||
{ divider: true },
|
||||
{ command: 'file.open', hideDisabled: true },
|
||||
{ command: 'file.openArchive', hideDisabled: true },
|
||||
@@ -24,20 +24,6 @@ module.exports = ({ editMenu }) => [
|
||||
{ command: 'app.disconnect', hideDisabled: true, skipInApp: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Window',
|
||||
submenu: [
|
||||
{ command: 'tabs.closeTab', hideDisabled: false },
|
||||
{ command: 'tabs.closeAll', hideDisabled: false },
|
||||
{ command: 'tabs.closeTabsWithCurrentDb', hideDisabled: false },
|
||||
{ command: 'tabs.closeTabsButCurrentDb', hideDisabled: false },
|
||||
{ divider: true },
|
||||
{ command: 'app.zoomIn', hideDisabled: true },
|
||||
{ command: 'app.zoomOut', hideDisabled: true },
|
||||
{ command: 'app.zoomReset', hideDisabled: true },
|
||||
],
|
||||
},
|
||||
|
||||
editMenu
|
||||
? {
|
||||
label: 'Edit',
|
||||
@@ -75,6 +61,15 @@ module.exports = ({ editMenu }) => [
|
||||
{ divider: true },
|
||||
{ command: 'theme.changeTheme', hideDisabled: true },
|
||||
{ command: 'settings.show' },
|
||||
{ divider: true },
|
||||
{ command: 'tabs.closeTab', hideDisabled: false },
|
||||
{ command: 'tabs.closeAll', hideDisabled: false },
|
||||
{ command: 'tabs.closeTabsWithCurrentDb', hideDisabled: false },
|
||||
{ command: 'tabs.closeTabsButCurrentDb', hideDisabled: false },
|
||||
{ divider: true },
|
||||
{ command: 'app.zoomIn', hideDisabled: true },
|
||||
{ command: 'app.zoomOut', hideDisabled: true },
|
||||
{ command: 'app.zoomReset', hideDisabled: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -94,6 +89,14 @@ module.exports = ({ editMenu }) => [
|
||||
{ command: 'app.resetSettings', hideDisabled: true },
|
||||
],
|
||||
},
|
||||
...(isMac
|
||||
? [
|
||||
{
|
||||
role: 'window',
|
||||
submenu: [{ role: 'minimize' }, { role: 'zoom' }, { type: 'separator' }, { role: 'front' }],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
label: 'Help',
|
||||
submenu: [
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
const content = require('./nativeModulesContent');
|
||||
|
||||
module.exports = content;
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
// this file is generated automatically by script fillNativeModules.js, do not edit it manually
|
||||
const content = {};
|
||||
|
||||
content['better-sqlite3'] = () => require('better-sqlite3');
|
||||
content['oracledb'] = () => require('oracledb');
|
||||
|
||||
|
||||
module.exports = content;
|
||||
@@ -0,0 +1,19 @@
|
||||
const useBundleExternals = require('./useBundleExternals');
|
||||
const getBundleExternals = require('./getBundleExternals');
|
||||
|
||||
function buildExternalsFromDependencies(packageJson) {
|
||||
if (useBundleExternals == 'true') {
|
||||
return getBundleExternals();
|
||||
}
|
||||
const { dependencies, optionalDependencies } = packageJson;
|
||||
const externals = {};
|
||||
for (let dep in dependencies || {}) {
|
||||
externals[dep] = `commonjs ${dep}`;
|
||||
}
|
||||
for (let dep in optionalDependencies || {}) {
|
||||
externals[dep] = `commonjs ${dep}`;
|
||||
}
|
||||
return externals;
|
||||
}
|
||||
|
||||
module.exports = buildExternalsFromDependencies;
|
||||
@@ -0,0 +1,33 @@
|
||||
const directory = process.argv[2];
|
||||
const fs = require('fs');
|
||||
|
||||
const volatilePackages = require('./volatilePackages');
|
||||
const apiPackageJson = JSON.parse(fs.readFileSync(`packages/api/package.json`, { encoding: 'utf-8' }));
|
||||
|
||||
const dependencies = {};
|
||||
const optionalDependencies = {};
|
||||
for (const pkg of volatilePackages) {
|
||||
if (pkg == 'msnodesqlv8' && process.platform != 'win32') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (apiPackageJson.dependencies[pkg]) {
|
||||
dependencies[pkg] = apiPackageJson.dependencies[pkg];
|
||||
}
|
||||
if (apiPackageJson.optionalDependencies?.[pkg]) {
|
||||
optionalDependencies[pkg] = apiPackageJson.optionalDependencies[pkg];
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
`${directory}/package.json`,
|
||||
JSON.stringify(
|
||||
{
|
||||
dependencies,
|
||||
optionalDependencies,
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
'utf-8'
|
||||
);
|
||||
@@ -0,0 +1,10 @@
|
||||
const volatilePackages = require('./volatilePackages');
|
||||
|
||||
function getBundleExternals() {
|
||||
return volatilePackages.reduce((acc, item) => {
|
||||
acc[item] = `commonjs ${item}`;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
module.exports = getBundleExternals;
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = 'false';
|
||||
@@ -0,0 +1,25 @@
|
||||
// these packages will be never bundled with webpack
|
||||
|
||||
const volatilePackages = [
|
||||
'@clickhouse/client',
|
||||
'bson', // this package is already bundled and is used in mongodb
|
||||
'mongodb',
|
||||
'mongodb-client-encryption',
|
||||
'tedious',
|
||||
'msnodesqlv8',
|
||||
'mysql2',
|
||||
'oracledb',
|
||||
'pg-copy-streams',
|
||||
'pg',
|
||||
'ioredis',
|
||||
'node-redis-dump2',
|
||||
'better-sqlite3',
|
||||
'@azure/cosmos',
|
||||
'@aws-sdk/rds-signer',
|
||||
'activedirectory2',
|
||||
'axios',
|
||||
'ssh2',
|
||||
'wkx',
|
||||
];
|
||||
|
||||
module.exports = volatilePackages;
|
||||
@@ -0,0 +1,29 @@
|
||||
const { defineConfig } = require('cypress');
|
||||
const killPort = require('kill-port');
|
||||
const { clearTestingData } = require('./e2eTestTools');
|
||||
const waitOn = require('wait-on');
|
||||
const { exec } = require('child_process');
|
||||
|
||||
module.exports = defineConfig({
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
// implement node event listeners here
|
||||
|
||||
on('before:spec', async details => {
|
||||
await clearTestingData();
|
||||
|
||||
if (config.isInteractive) {
|
||||
await killPort(3000);
|
||||
serverProcess = exec('yarn start');
|
||||
await waitOn({ resources: ['http://localhost:3000'] });
|
||||
serverProcess.stdout.on('data', data => {
|
||||
console.log(data.toString());
|
||||
});
|
||||
serverProcess.stderr.on('data', data => {
|
||||
console.error(data.toString());
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
describe('Initialization', () => {
|
||||
it('successfully loads', () => {
|
||||
cy.visit('http://localhost:3000');
|
||||
cy.contains('Database not selected');
|
||||
});
|
||||
|
||||
it('adds connection', () => {
|
||||
const runOnCI = Cypress.env('runOnCI');
|
||||
|
||||
cy.visit('http://localhost:3000');
|
||||
// cy.get('[data-testid=ConnectionList_buttonNewConnection]').click();
|
||||
cy.get('[data-testid=ConnectionDriverFields_connectionType]').select('MySQL');
|
||||
cy.get('[data-testid=ConnectionDriverFields_user]').clear().type('root');
|
||||
cy.get('[data-testid=ConnectionDriverFields_password]').clear().type('Pwd2020Db');
|
||||
if (runOnCI) {
|
||||
cy.get('[data-testid=ConnectionDriverFields_server]').clear().type('mysql');
|
||||
} else {
|
||||
cy.get('[data-testid=ConnectionDriverFields_port]').clear().type('16004');
|
||||
}
|
||||
cy.get('[data-testid=ConnectionDriverFields_displayName]').clear().type('test-mysql-1');
|
||||
cy.get('[data-testid=ConnectionTab_buttonSave]').click();
|
||||
cy.get('[data-testid=ConnectionTab_buttonConnect]').click();
|
||||
cy.contains('performance_schema');
|
||||
});
|
||||
|
||||
// it('import chinook DB', () => {
|
||||
// cy.visit('http://localhost:3000');
|
||||
// cy.get('[data-testid=ConnectionTab_buttonConnect]').click();
|
||||
// });
|
||||
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
@@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example support/e2e.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
@@ -0,0 +1,18 @@
|
||||
version: '3'
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_PASSWORD: Pwd2020Db
|
||||
ports:
|
||||
- 16000:5432
|
||||
|
||||
mariadb:
|
||||
image: mariadb
|
||||
command: --default-authentication-plugin=mysql_native_password
|
||||
restart: always
|
||||
ports:
|
||||
- 16004:3306
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=Pwd2020Db
|
||||
@@ -0,0 +1,29 @@
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
|
||||
const baseDir = path.join(os.homedir(), '.dbgate');
|
||||
|
||||
// function createTimeStamp() {
|
||||
// const now = new Date();
|
||||
// const year = now.getFullYear();
|
||||
// const month = String(now.getMonth() + 1).padStart(2, '0'); // měsíc je 0-indexovaný
|
||||
// const day = String(now.getDate()).padStart(2, '0');
|
||||
// const hours = String(now.getHours()).padStart(2, '0');
|
||||
// const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
// const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
|
||||
// // Poskládáme datum a čas do názvu souboru
|
||||
// const ts = `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`;
|
||||
// return ts;
|
||||
// }
|
||||
|
||||
function clearTestingData() {
|
||||
if (fs.existsSync(path.join(baseDir, 'connections-e2etests.jsonl'))) {
|
||||
fs.unlinkSync(path.join(baseDir, 'connections-e2etests.jsonl'));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
clearTestingData,
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "e2e-tests",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "GPL",
|
||||
"devDependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"cross-env": "^7.0.3",
|
||||
"cypress": "^13.16.1",
|
||||
"kill-port": "^2.0.1",
|
||||
"start-server-and-test": "^2.0.8"
|
||||
},
|
||||
"scripts": {
|
||||
"cy:open": "cypress open --config experimentalInteractiveRunEvents=true",
|
||||
"cy:run": "cypress run",
|
||||
"cy:run:ci": "cypress run --env runOnCI=true",
|
||||
"start": "cd .. && node packer/build/bundle.js --listen-api --run-e2e-tests",
|
||||
"test:ci": "start-server-and-test start http://localhost:3000 cy:run:ci",
|
||||
"test": "start-server-and-test start http://localhost:3000 cy:run"
|
||||
}
|
||||
}
|
||||
+1363
File diff suppressed because it is too large
Load Diff
@@ -1,24 +0,0 @@
|
||||
const fs = require('fs');
|
||||
|
||||
let fillContent = '';
|
||||
|
||||
if (process.platform == 'win32') {
|
||||
fillContent += `content.msnodesqlv8 = () => require('msnodesqlv8');\n`;
|
||||
}
|
||||
fillContent += `content['better-sqlite3'] = () => require('better-sqlite3');\n`;
|
||||
fillContent += `content['oracledb'] = () => require('oracledb');\n`;
|
||||
|
||||
const getContent = empty => `
|
||||
// this file is generated automatically by script fillNativeModules.js, do not edit it manually
|
||||
const content = {};
|
||||
|
||||
${empty ? '' : fillContent}
|
||||
|
||||
module.exports = content;
|
||||
`;
|
||||
|
||||
fs.writeFileSync(
|
||||
'packages/api/src/nativeModulesContent.js',
|
||||
getContent(process.argv.includes('--electron') ? true : false)
|
||||
);
|
||||
fs.writeFileSync('app/src/nativeModulesContent.js', getContent(false));
|
||||
@@ -3,24 +3,33 @@ const _ = require('lodash');
|
||||
const fp = require('lodash/fp');
|
||||
const { testWrapper } = require('../tools');
|
||||
const engines = require('../engines');
|
||||
const { getAlterDatabaseScript, extendDatabaseInfo, generateDbPairingId } = require('dbgate-tools');
|
||||
const {
|
||||
getAlterDatabaseScript,
|
||||
extendDatabaseInfo,
|
||||
generateDbPairingId,
|
||||
formatQueryWithoutParams,
|
||||
runCommandOnDriver,
|
||||
} = require('dbgate-tools');
|
||||
|
||||
function flatSource() {
|
||||
const initSql = ['CREATE TABLE ~t1 (~id int primary key)', 'CREATE TABLE ~t2 (~id int primary key)'];
|
||||
|
||||
function flatSource(engineCond = x => !x.skipReferences) {
|
||||
return _.flatten(
|
||||
engines
|
||||
.filter(x => !x.skipReferences)
|
||||
.filter(engineCond)
|
||||
.map(engine => (engine.objects || []).map(object => [engine.label, object.type, object, engine]))
|
||||
);
|
||||
}
|
||||
|
||||
async function testDatabaseDiff(conn, driver, mangle, createObject = null) {
|
||||
await driver.query(conn, `create table t1 (id int not null primary key)`);
|
||||
await runCommandOnDriver(conn, driver, `create table ~t1 (~id int not null primary key)`);
|
||||
|
||||
await driver.query(
|
||||
await runCommandOnDriver(
|
||||
conn,
|
||||
`create table t2 (
|
||||
id int not null primary key,
|
||||
t1_id int null references t1(id)
|
||||
driver,
|
||||
`create table ~t2 (
|
||||
~id int not null primary key,
|
||||
~t1_id int null references ~t1(~id)
|
||||
)`
|
||||
);
|
||||
|
||||
@@ -61,10 +70,29 @@ describe('Alter database', () => {
|
||||
db => {
|
||||
_.remove(db[type], x => x.pureName == 'obj1');
|
||||
},
|
||||
object.create1
|
||||
formatQueryWithoutParams(driver, object.create1)
|
||||
);
|
||||
expect(db[type].length).toEqual(0);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test.each(flatSource(x => x.supportRenameSqlObject))(
|
||||
'Rename object - %s - %s',
|
||||
testWrapper(async (conn, driver, type, object, engine) => {
|
||||
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||
|
||||
await runCommandOnDriver(conn, driver, object.create1);
|
||||
|
||||
const structure = extendDatabaseInfo(await driver.analyseFull(conn));
|
||||
|
||||
const dmp = driver.createDumper();
|
||||
dmp.renameSqlObject(structure[type][0], 'renamed1');
|
||||
|
||||
await driver.query(conn, dmp.s);
|
||||
|
||||
const structure2 = await driver.analyseFull(conn);
|
||||
expect(structure2[type].length).toEqual(1);
|
||||
expect(structure2[type][0].pureName).toEqual('renamed1');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -4,15 +4,26 @@ const fp = require('lodash/fp');
|
||||
const { testWrapper } = require('../tools');
|
||||
const engines = require('../engines');
|
||||
const crypto = require('crypto');
|
||||
const { getAlterTableScript, extendDatabaseInfo, generateDbPairingId } = require('dbgate-tools');
|
||||
const {
|
||||
getAlterTableScript,
|
||||
extendDatabaseInfo,
|
||||
generateDbPairingId,
|
||||
formatQueryWithoutParams,
|
||||
} = require('dbgate-tools');
|
||||
|
||||
function pickImportantTableInfo(engine, table) {
|
||||
const props = ['columnName'];
|
||||
const props = ['columnName', 'defaultValue'];
|
||||
if (!engine.skipNullability) props.push('notNull');
|
||||
if (!engine.skipAutoIncrement) props.push('autoIncrement');
|
||||
return {
|
||||
pureName: table.pureName,
|
||||
columns: table.columns.filter(x => x.columnName != 'rowid').map(fp.pick(props)),
|
||||
columns: table.columns
|
||||
.filter(x => x.columnName != 'rowid')
|
||||
.map(fp.pick(props))
|
||||
.map(props => _.omitBy(props, x => x == null))
|
||||
.map(props =>
|
||||
_.omitBy(props, (v, k) => k == 'defaultValue' && v == 'NULL' && engine.setNullDefaultInsteadOfDrop)
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,27 +33,36 @@ function checkTableStructure(engine, t1, t2) {
|
||||
}
|
||||
|
||||
async function testTableDiff(engine, conn, driver, mangle) {
|
||||
await driver.query(conn, `create table t0 (id int not null primary key)`);
|
||||
await driver.query(conn, formatQueryWithoutParams(driver, `create table ~t0 (~id int not null primary key)`));
|
||||
|
||||
await driver.query(
|
||||
conn,
|
||||
`create table t1 (
|
||||
col_pk int not null primary key,
|
||||
col_std int,
|
||||
col_def int default 12,
|
||||
${engine.skipReferences ? '' : 'col_fk int references t0(id),'}
|
||||
col_idx int,
|
||||
col_uq int ${engine.skipUnique ? '' : 'unique'} ,
|
||||
col_ref int ${engine.skipUnique ? '' : 'unique'}
|
||||
formatQueryWithoutParams(
|
||||
driver,
|
||||
`create table ~t1 (
|
||||
~col_pk int not null primary key,
|
||||
~col_std int,
|
||||
~col_def int default 12,
|
||||
${engine.skipReferences ? '' : '~col_fk int references ~t0(~id),'}
|
||||
~col_idx int,
|
||||
~col_uq int ${engine.skipUnique ? '' : 'unique'} ,
|
||||
~col_ref int ${engine.skipUnique ? '' : 'unique'}
|
||||
)`
|
||||
)
|
||||
);
|
||||
|
||||
if (!engine.skipIndexes) {
|
||||
await driver.query(conn, `create index idx1 on t1(col_idx)`);
|
||||
await driver.query(conn, formatQueryWithoutParams(driver, `create index ~idx1 on ~t1(~col_idx)`));
|
||||
}
|
||||
|
||||
if (!engine.skipReferences) {
|
||||
await driver.query(conn, `create table t2 (id int not null primary key, fkval int null references t1(col_ref))`);
|
||||
await driver.query(
|
||||
conn,
|
||||
formatQueryWithoutParams(
|
||||
driver,
|
||||
`create table ~t2 (~id int not null primary key, ~fkval int null references ~t1(~col_ref))`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const tget = x => x.tables.find(y => y.pureName == 't1');
|
||||
@@ -136,4 +156,40 @@ describe('Alter table', () => {
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Add default value - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testTableDiff(engine, conn, driver, tbl => {
|
||||
tbl.columns.find(x => x.columnName == 'col_std').defaultValue = '123';
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Unset default value - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testTableDiff(engine, conn, driver, tbl => {
|
||||
tbl.columns.find(x => x.columnName == 'col_def').defaultValue = undefined;
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Change default value - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testTableDiff(engine, conn, driver, tbl => {
|
||||
tbl.columns.find(x => x.columnName == 'col_def').defaultValue = '567';
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
// test.each(engines.map(engine => [engine.label, engine]))(
|
||||
// 'Change autoincrement - %s',
|
||||
// testWrapper(async (conn, driver, engine) => {
|
||||
// await testTableDiff(engine, conn, driver, tbl => {
|
||||
// tbl.columns.find(x => x.columnName == 'col_pk').autoIncrement = true;
|
||||
// });
|
||||
// })
|
||||
// );
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ const engines = require('../engines');
|
||||
const stream = require('stream');
|
||||
const { testWrapper } = require('../tools');
|
||||
const dataDuplicator = require('dbgate-api/src/shell/dataDuplicator');
|
||||
const { runCommandOnDriver } = require('dbgate-tools');
|
||||
const { runCommandOnDriver, runQueryOnDriver } = require('dbgate-tools');
|
||||
|
||||
describe('Data duplicator', () => {
|
||||
test.each(engines.filter(x => !x.skipDataDuplicator).map(engine => [engine.label, engine]))(
|
||||
@@ -84,11 +84,77 @@ describe('Data duplicator', () => {
|
||||
],
|
||||
});
|
||||
|
||||
const res1 = await driver.query(conn, `select count(*) as cnt from t1`);
|
||||
const res1 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t1`));
|
||||
expect(res1.rows[0].cnt.toString()).toEqual('6');
|
||||
|
||||
const res2 = await driver.query(conn, `select count(*) as cnt from t2`);
|
||||
const res2 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t2`));
|
||||
expect(res2.rows[0].cnt.toString()).toEqual('6');
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(x => !x.skipDataDuplicator).map(engine => [engine.label, engine]))(
|
||||
'Skip nullable weak refs - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
runCommandOnDriver(conn, driver, dmp =>
|
||||
dmp.createTable({
|
||||
pureName: 't1',
|
||||
columns: [
|
||||
{ columnName: 'id', dataType: 'int', notNull: true },
|
||||
{ columnName: 'val', dataType: 'varchar(50)' },
|
||||
],
|
||||
primaryKey: {
|
||||
columns: [{ columnName: 'id' }],
|
||||
},
|
||||
})
|
||||
);
|
||||
runCommandOnDriver(conn, driver, dmp =>
|
||||
dmp.createTable({
|
||||
pureName: 't2',
|
||||
columns: [
|
||||
{ columnName: 'id', dataType: 'int', autoIncrement: true, notNull: true },
|
||||
{ columnName: 'val', dataType: 'varchar(50)' },
|
||||
{ columnName: 'valfk', dataType: 'int', notNull: false },
|
||||
],
|
||||
primaryKey: {
|
||||
columns: [{ columnName: 'id' }],
|
||||
},
|
||||
foreignKeys: [{ refTableName: 't1', columns: [{ columnName: 'valfk', refColumnName: 'id' }] }],
|
||||
})
|
||||
);
|
||||
runCommandOnDriver(conn, driver, dmp => dmp.put("insert into ~t1 (~id, ~val) values (1, 'first')"));
|
||||
|
||||
const gett2 = () =>
|
||||
stream.Readable.from([
|
||||
{ __isStreamHeader: true, __isDynamicStructure: true },
|
||||
{ id: 1, val: 'v1', valfk: 1 },
|
||||
{ id: 2, val: 'v2', valfk: 2 },
|
||||
]);
|
||||
|
||||
await dataDuplicator({
|
||||
systemConnection: conn,
|
||||
driver,
|
||||
items: [
|
||||
{
|
||||
name: 't2',
|
||||
operation: 'copy',
|
||||
openStream: gett2,
|
||||
},
|
||||
],
|
||||
options: {
|
||||
setNullForUnresolvedNullableRefs: true,
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t1`));
|
||||
expect(res1.rows[0].cnt.toString()).toEqual('1');
|
||||
|
||||
const res2 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t2`));
|
||||
expect(res2.rows[0].cnt.toString()).toEqual('2');
|
||||
|
||||
const res3 = await runQueryOnDriver(conn, driver, dmp =>
|
||||
dmp.put(`select count(*) as ~cnt from ~t2 where ~valfk is not null`)
|
||||
);
|
||||
expect(res3.rows[0].cnt.toString()).toEqual('1');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3,7 +3,9 @@ const stream = require('stream');
|
||||
const { testWrapper } = require('../tools');
|
||||
const tableWriter = require('dbgate-api/src/shell/tableWriter');
|
||||
const copyStream = require('dbgate-api/src/shell/copyStream');
|
||||
const importDatabase = require('dbgate-api/src/shell/importDatabase');
|
||||
const fakeObjectReader = require('dbgate-api/src/shell/fakeObjectReader');
|
||||
const { runQueryOnDriver } = require('dbgate-tools');
|
||||
|
||||
function createImportStream() {
|
||||
const pass = new stream.PassThrough({
|
||||
@@ -36,7 +38,7 @@ describe('DB Import', () => {
|
||||
});
|
||||
await copyStream(reader, writer);
|
||||
|
||||
const res = await driver.query(conn, `select count(*) as cnt from t1`);
|
||||
const res = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t1`));
|
||||
expect(res.rows[0].cnt.toString()).toEqual('6');
|
||||
})
|
||||
);
|
||||
@@ -64,12 +66,37 @@ describe('DB Import', () => {
|
||||
});
|
||||
await copyStream(reader2, writer2);
|
||||
|
||||
const res1 = await driver.query(conn, `select count(*) as cnt from t1`);
|
||||
const res1 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t1`));
|
||||
expect(res1.rows[0].cnt.toString()).toEqual('6');
|
||||
|
||||
const res2 = await driver.query(conn, `select count(*) as cnt from t2`);
|
||||
const res2 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t2`));
|
||||
expect(res2.rows[0].cnt.toString()).toEqual('6');
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(x => x.dumpFile).map(engine => [engine.label, engine]))(
|
||||
'Import SQL dump - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
// const reader = await fakeObjectReader({ delay: 10 });
|
||||
// const reader = await fakeObjectReader();
|
||||
await importDatabase({
|
||||
systemConnection: conn,
|
||||
driver,
|
||||
inputFile: engine.dumpFile,
|
||||
});
|
||||
|
||||
const structure = await driver.analyseFull(conn);
|
||||
|
||||
for (const check of engine.dumpChecks || []) {
|
||||
const res = await driver.query(conn, check.sql);
|
||||
expect(res.rows[0].res.toString()).toEqual(check.res);
|
||||
}
|
||||
|
||||
// const res1 = await driver.query(conn, `select count(*) as cnt from t1`);
|
||||
// expect(res1.rows[0].cnt.toString()).toEqual('6');
|
||||
|
||||
// const res2 = await driver.query(conn, `select count(*) as cnt from t2`);
|
||||
// expect(res2.rows[0].cnt.toString()).toEqual('6');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,58 +1,176 @@
|
||||
/// TODO
|
||||
|
||||
const { testWrapper } = require('../tools');
|
||||
const { testWrapper, testWrapperPrepareOnly } = require('../tools');
|
||||
const _ = require('lodash');
|
||||
const engines = require('../engines');
|
||||
const deployDb = require('dbgate-api/src/shell/deployDb');
|
||||
const { databaseInfoFromYamlModel } = require('dbgate-tools');
|
||||
const { databaseInfoFromYamlModel, runQueryOnDriver, formatQueryWithoutParams } = require('dbgate-tools');
|
||||
const generateDeploySql = require('dbgate-api/src/shell/generateDeploySql');
|
||||
const connectUtility = require('dbgate-api/src/utility/connectUtility');
|
||||
|
||||
function checkStructure(structure, model) {
|
||||
function checkStructure(
|
||||
engine,
|
||||
structure,
|
||||
model,
|
||||
{ checkRenameDeletedObjects = false, disallowExtraObjects = false } = {}
|
||||
) {
|
||||
const expected = databaseInfoFromYamlModel(model);
|
||||
expect(structure.tables.length).toEqual(expected.tables.length);
|
||||
|
||||
for (const [realTable, expectedTable] of _.zip(
|
||||
_.sortBy(structure.tables, 'pureName'),
|
||||
_.sortBy(expected.tables, 'pureName')
|
||||
)) {
|
||||
expect(realTable.columns.length).toBeGreaterThanOrEqual(expectedTable.columns.length);
|
||||
for (const expectedTable of expected.tables) {
|
||||
const realTable = structure.tables.find(x => x.pureName == expectedTable.pureName);
|
||||
|
||||
for (const column of expectedTable.columns) {
|
||||
const realColumn = realTable.columns.find(x => x.columnName == column.columnName);
|
||||
expect(realColumn).toBeTruthy();
|
||||
if (!engine.skipNullability) {
|
||||
expect(realColumn.notNull).toEqual(column.notNull);
|
||||
}
|
||||
}
|
||||
|
||||
for (const realColumn of realTable.columns) {
|
||||
const column = expectedTable.columns.find(x => x.columnName == realColumn.columnName);
|
||||
if (!column) {
|
||||
if (checkRenameDeletedObjects) {
|
||||
expect(realColumn.columnName).toMatch(/^_deleted_/);
|
||||
}
|
||||
|
||||
if (disallowExtraObjects) {
|
||||
expect(realColumn).toBeFalsy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const realTable of structure.tables) {
|
||||
const expectedTable = expected.tables.find(x => x.pureName == realTable.pureName);
|
||||
if (!expectedTable) {
|
||||
if (checkRenameDeletedObjects) {
|
||||
expect(realTable.pureName).toMatch(/^_deleted_/);
|
||||
}
|
||||
|
||||
if (disallowExtraObjects) {
|
||||
expect(realTable).toBeFalsy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const expectedView of expected.views) {
|
||||
const realView = structure.views.find(x => x.pureName == expectedView.pureName);
|
||||
expect(realView).toBeTruthy();
|
||||
}
|
||||
|
||||
for (const realView of structure.views) {
|
||||
const expectedView = expected.views.find(x => x.pureName == realView.pureName);
|
||||
if (!expectedView) {
|
||||
if (disallowExtraObjects) {
|
||||
expect(realView).toBeFalsy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function testDatabaseDeploy(conn, driver, dbModelsYaml, testEmptyLastScript) {
|
||||
// function convertObjectText(text, driver) {
|
||||
// if (!text) return undefined;
|
||||
// text = formatQueryWithoutParams(driver, text);
|
||||
// if (driver.dialect.requireFromDual && text.startsWith('create view ') && !text.includes('from')) {
|
||||
// text = text + ' from dual';
|
||||
// }
|
||||
// return text;
|
||||
// }
|
||||
|
||||
// function convertModelToEngine(model, driver) {
|
||||
// return model.map(x => ({
|
||||
// ...x,
|
||||
// text: convertObjectText(x.text, driver),
|
||||
// }));
|
||||
// }
|
||||
|
||||
function convertModelToEngine(model, driver) {
|
||||
return model.map(x => ({
|
||||
...x,
|
||||
text: x.text ? formatQueryWithoutParams(driver, x.text) : undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
async function testDatabaseDeploy(engine, conn, driver, dbModelsYaml, options) {
|
||||
const { testEmptyLastScript, finalCheckAgainstModel, markDeleted, allowDropStatements } = options || {};
|
||||
let index = 0;
|
||||
const dbdiffOptionsExtra = markDeleted
|
||||
? {
|
||||
deletedTablePrefix: '_deleted_',
|
||||
deletedColumnPrefix: '_deleted_',
|
||||
deletedSqlObjectPrefix: '_deleted_',
|
||||
}
|
||||
: {};
|
||||
dbdiffOptionsExtra.schemaMode = 'ignore';
|
||||
|
||||
for (const loadedDbModel of dbModelsYaml) {
|
||||
const { sql, isEmpty } = await generateDeploySql({
|
||||
systemConnection: conn,
|
||||
driver,
|
||||
loadedDbModel,
|
||||
});
|
||||
console.debug('Generated deploy script:', sql);
|
||||
expect(sql.toUpperCase().includes('DROP ')).toBeFalsy();
|
||||
if (_.isString(loadedDbModel)) {
|
||||
await driver.script(conn, formatQueryWithoutParams(driver, loadedDbModel));
|
||||
} else {
|
||||
const { sql, isEmpty } = await generateDeploySql({
|
||||
systemConnection: conn.isPreparedOnly ? undefined : conn,
|
||||
connection: conn.isPreparedOnly ? conn : undefined,
|
||||
driver,
|
||||
loadedDbModel: convertModelToEngine(loadedDbModel, driver),
|
||||
dbdiffOptionsExtra,
|
||||
});
|
||||
console.debug('Generated deploy script:', sql);
|
||||
if (!allowDropStatements) {
|
||||
expect(sql.toUpperCase().includes('DROP ')).toBeFalsy();
|
||||
}
|
||||
|
||||
console.log('dbModelsYaml.length', dbModelsYaml.length, index);
|
||||
if (testEmptyLastScript && index == dbModelsYaml.length - 1) {
|
||||
expect(isEmpty).toBeTruthy();
|
||||
console.log('dbModelsYaml.length', dbModelsYaml.length, index);
|
||||
if (testEmptyLastScript && index == dbModelsYaml.length - 1) {
|
||||
expect(isEmpty).toBeTruthy();
|
||||
}
|
||||
|
||||
await deployDb({
|
||||
systemConnection: conn.isPreparedOnly ? undefined : conn,
|
||||
connection: conn.isPreparedOnly ? conn : undefined,
|
||||
driver,
|
||||
loadedDbModel: convertModelToEngine(loadedDbModel, driver),
|
||||
dbdiffOptionsExtra,
|
||||
});
|
||||
}
|
||||
|
||||
await deployDb({
|
||||
systemConnection: conn,
|
||||
driver,
|
||||
loadedDbModel,
|
||||
});
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
const structure = await driver.analyseFull(conn);
|
||||
checkStructure(structure, dbModelsYaml[dbModelsYaml.length - 1]);
|
||||
const dbhan = conn.isPreparedOnly ? await connectUtility(driver, conn, 'read') : conn;
|
||||
const structure = await driver.analyseFull(dbhan);
|
||||
if (conn.isPreparedOnly) await driver.close(dbhan);
|
||||
checkStructure(
|
||||
engine,
|
||||
structure,
|
||||
convertModelToEngine(finalCheckAgainstModel ?? _.findLast(dbModelsYaml, x => _.isArray(x)), driver),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
describe('Deploy database', () => {
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Deploy database simple - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(conn, driver, [
|
||||
await testDatabaseDeploy(engine, conn, driver, [
|
||||
[
|
||||
{
|
||||
name: 't1.table.yaml',
|
||||
json: {
|
||||
name: 't1',
|
||||
columns: [{ name: 'id', type: 'int' }],
|
||||
primaryKey: ['id'],
|
||||
},
|
||||
},
|
||||
],
|
||||
]);
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Deploy database simple - %s - not connected',
|
||||
testWrapperPrepareOnly(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(engine, conn, driver, [
|
||||
[
|
||||
{
|
||||
name: 't1.table.yaml',
|
||||
@@ -71,6 +189,7 @@ describe('Deploy database', () => {
|
||||
'Deploy database simple twice - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(
|
||||
engine,
|
||||
conn,
|
||||
driver,
|
||||
[
|
||||
@@ -95,7 +214,7 @@ describe('Deploy database', () => {
|
||||
},
|
||||
],
|
||||
],
|
||||
true
|
||||
{ testEmptyLastScript: true }
|
||||
);
|
||||
})
|
||||
);
|
||||
@@ -103,7 +222,7 @@ describe('Deploy database', () => {
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Add column - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(conn, driver, [
|
||||
await testDatabaseDeploy(engine, conn, driver, [
|
||||
[
|
||||
{
|
||||
name: 't1.table.yaml',
|
||||
@@ -135,6 +254,7 @@ describe('Deploy database', () => {
|
||||
'Dont drop column - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(
|
||||
engine,
|
||||
conn,
|
||||
driver,
|
||||
[
|
||||
@@ -162,7 +282,7 @@ describe('Deploy database', () => {
|
||||
},
|
||||
],
|
||||
],
|
||||
true
|
||||
{ testEmptyLastScript: true }
|
||||
);
|
||||
})
|
||||
);
|
||||
@@ -171,6 +291,7 @@ describe('Deploy database', () => {
|
||||
'Foreign keys - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(
|
||||
engine,
|
||||
conn,
|
||||
driver,
|
||||
[
|
||||
@@ -217,7 +338,7 @@ describe('Deploy database', () => {
|
||||
},
|
||||
],
|
||||
],
|
||||
true
|
||||
{ testEmptyLastScript: true }
|
||||
);
|
||||
})
|
||||
);
|
||||
@@ -225,7 +346,7 @@ describe('Deploy database', () => {
|
||||
test.each(engines.filter(x => !x.skipDataModifications).map(engine => [engine.label, engine]))(
|
||||
'Deploy preloaded data - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(conn, driver, [
|
||||
await testDatabaseDeploy(engine, conn, driver, [
|
||||
[
|
||||
{
|
||||
name: 't1.table.yaml',
|
||||
@@ -246,7 +367,7 @@ describe('Deploy database', () => {
|
||||
],
|
||||
]);
|
||||
|
||||
const res = await driver.query(conn, `select count(*) as cnt from t1`);
|
||||
const res = await runQueryOnDriver(conn, driver, `select count(*) as ~cnt from ~t1`);
|
||||
expect(res.rows[0].cnt.toString()).toEqual('3');
|
||||
})
|
||||
);
|
||||
@@ -254,7 +375,7 @@ describe('Deploy database', () => {
|
||||
test.each(engines.filter(x => !x.skipDataModifications).map(engine => [engine.label, engine]))(
|
||||
'Deploy preloaded data - update - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(conn, driver, [
|
||||
await testDatabaseDeploy(engine, conn, driver, [
|
||||
[
|
||||
{
|
||||
name: 't1.table.yaml',
|
||||
@@ -293,7 +414,7 @@ describe('Deploy database', () => {
|
||||
],
|
||||
]);
|
||||
|
||||
const res = await driver.query(conn, `select val from t1 where id = 2`);
|
||||
const res = await runQueryOnDriver(conn, driver, `select ~val from ~t1 where ~id = 2`);
|
||||
expect(res.rows[0].val.toString()).toEqual('5');
|
||||
})
|
||||
);
|
||||
@@ -301,7 +422,7 @@ describe('Deploy database', () => {
|
||||
test.each(engines.enginesPostgre.map(engine => [engine.label, engine]))(
|
||||
'Current timestamp default value - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(conn, driver, [
|
||||
await testDatabaseDeploy(engine, conn, driver, [
|
||||
[
|
||||
{
|
||||
name: 't1.table.yaml',
|
||||
@@ -321,9 +442,354 @@ describe('Deploy database', () => {
|
||||
],
|
||||
]);
|
||||
|
||||
await driver.query(conn, `insert into t1 (id) values (1)`);
|
||||
const res = await driver.query(conn, ` select val from t1 where id = 1`);
|
||||
await runQueryOnDriver(conn, driver, `insert into ~t1 (~id) values (1)`);
|
||||
const res = await runQueryOnDriver(conn, driver, ` select ~val from ~t1 where ~id = 1`);
|
||||
expect(res.rows[0].val.toString().substring(0, 2)).toEqual('20');
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(x => !x.skipChangeColumn).map(engine => [engine.label, engine]))(
|
||||
'Change column to NOT NULL column with default - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(engine, conn, driver, [
|
||||
[
|
||||
{
|
||||
name: 't1.table.yaml',
|
||||
json: {
|
||||
name: 't1',
|
||||
columns: [
|
||||
{ name: 'id', type: 'int', notNull: true },
|
||||
{ name: 'val', type: 'int' },
|
||||
],
|
||||
|
||||
primaryKey: ['id'],
|
||||
},
|
||||
},
|
||||
],
|
||||
'insert into ~t1 (~id, ~val) values (1, 1); insert into ~t1 (~id) values (2)',
|
||||
[
|
||||
{
|
||||
name: 't1.table.yaml',
|
||||
json: {
|
||||
name: 't1',
|
||||
columns: [
|
||||
{ name: 'id', type: 'int', notNull: true },
|
||||
{ name: 'val', type: 'int', notNull: true, default: '20' },
|
||||
],
|
||||
primaryKey: ['id'],
|
||||
},
|
||||
},
|
||||
],
|
||||
'insert into ~t1 (~id) values (3);',
|
||||
]);
|
||||
|
||||
const res1 = await runQueryOnDriver(conn, driver, `select ~val from ~t1 where ~id = 1`);
|
||||
expect(res1.rows[0].val).toEqual(1);
|
||||
|
||||
const res2 = await runQueryOnDriver(conn, driver, `select ~val from ~t1 where ~id = 2`);
|
||||
expect(res2.rows[0].val).toEqual(20);
|
||||
|
||||
const res3 = await runQueryOnDriver(conn, driver, `select ~val from ~t1 where ~id = 3`);
|
||||
expect(res2.rows[0].val).toEqual(20);
|
||||
})
|
||||
);
|
||||
|
||||
const T1 = {
|
||||
name: 't1.table.yaml',
|
||||
json: {
|
||||
name: 't1',
|
||||
columns: [
|
||||
{ name: 'id', type: 'int' },
|
||||
{ name: 'val', type: 'int' },
|
||||
],
|
||||
primaryKey: ['id'],
|
||||
},
|
||||
};
|
||||
|
||||
const T2 = {
|
||||
name: 't2.table.yaml',
|
||||
json: {
|
||||
name: 't2',
|
||||
columns: [
|
||||
{ name: 'id', type: 'int' },
|
||||
{ name: 'val', type: 'int' },
|
||||
],
|
||||
primaryKey: ['id'],
|
||||
},
|
||||
};
|
||||
|
||||
const T1_DELETED = {
|
||||
name: '_deleted_t1.table.yaml',
|
||||
json: {
|
||||
name: '_deleted_t1',
|
||||
columns: [
|
||||
{ name: 'id', type: 'int' },
|
||||
{ name: 'val', type: 'int' },
|
||||
],
|
||||
primaryKey: ['id'],
|
||||
},
|
||||
};
|
||||
|
||||
const T1_NO_VAL = {
|
||||
name: 't1.table.yaml',
|
||||
json: {
|
||||
name: 't1',
|
||||
columns: [{ name: 'id', type: 'int' }],
|
||||
primaryKey: ['id'],
|
||||
},
|
||||
};
|
||||
|
||||
const T1_DELETED_VAL = {
|
||||
name: 't1.table.yaml',
|
||||
json: {
|
||||
name: 't1',
|
||||
columns: [
|
||||
{ name: 'id', type: 'int' },
|
||||
{ name: '_deleted_val', type: 'int' },
|
||||
],
|
||||
primaryKey: ['id'],
|
||||
},
|
||||
};
|
||||
|
||||
const V1 = {
|
||||
name: 'v1.view.sql',
|
||||
text: 'create view ~v1 as select * from ~t1',
|
||||
};
|
||||
|
||||
const V1_VARIANT2 = {
|
||||
name: 'v1.view.sql',
|
||||
text: 'create view ~v1 as select ~id + ~id ~idsum from ~t1',
|
||||
};
|
||||
|
||||
const V1_DELETED = {
|
||||
name: '_deleted_v1.view.sql',
|
||||
text: 'create view ~_deleted_v1 as select * from ~t1',
|
||||
};
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Dont remove column - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(engine, conn, driver, [[T1], [T1_NO_VAL]], {
|
||||
finalCheckAgainstModel: [T1],
|
||||
disallowExtraObjects: true,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Dont remove table - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(engine, conn, driver, [[T1], []], {
|
||||
finalCheckAgainstModel: [T1],
|
||||
disallowExtraObjects: true,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Mark table removed - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(engine, conn, driver, [[T1], [], []], {
|
||||
markDeleted: true,
|
||||
disallowExtraObjects: true,
|
||||
finalCheckAgainstModel: [T1_DELETED],
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(engine => engine.supportRenameSqlObject).map(engine => [engine.label, engine]))(
|
||||
'Mark view removed - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(engine, conn, driver, [[T1, V1], [T1], [T1]], {
|
||||
markDeleted: true,
|
||||
disallowExtraObjects: true,
|
||||
finalCheckAgainstModel: [T1, V1_DELETED],
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Mark column removed - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(engine, conn, driver, [[T1], [T1_NO_VAL]], {
|
||||
markDeleted: true,
|
||||
disallowExtraObjects: true,
|
||||
finalCheckAgainstModel: [T1_DELETED_VAL],
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Undelete table - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(
|
||||
engine,
|
||||
conn,
|
||||
driver,
|
||||
[
|
||||
[T1],
|
||||
// delete table
|
||||
[],
|
||||
// undelete table
|
||||
[T1],
|
||||
],
|
||||
{
|
||||
markDeleted: true,
|
||||
disallowExtraObjects: true,
|
||||
}
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(engine => engine.supportRenameSqlObject).map(engine => [engine.label, engine]))(
|
||||
'Undelete view - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(engine, conn, driver, [[T1, V1], [T1], [T1, V1]], {
|
||||
markDeleted: true,
|
||||
disallowExtraObjects: true,
|
||||
allowDropStatements: true,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Undelete column - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(engine, conn, driver, [[T1], [T1_NO_VAL], [T1]], {
|
||||
markDeleted: true,
|
||||
disallowExtraObjects: true,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'View redeploy - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(
|
||||
engine,
|
||||
conn,
|
||||
driver,
|
||||
[
|
||||
[T1, V1],
|
||||
[T1, V1],
|
||||
[T1, V1],
|
||||
],
|
||||
{
|
||||
markDeleted: true,
|
||||
disallowExtraObjects: true,
|
||||
allowDropStatements: true,
|
||||
}
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Change view - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(
|
||||
engine,
|
||||
conn,
|
||||
driver,
|
||||
[
|
||||
[T1, V1],
|
||||
[T1, V1_VARIANT2],
|
||||
],
|
||||
{
|
||||
markDeleted: true,
|
||||
disallowExtraObjects: true,
|
||||
allowDropStatements: true,
|
||||
}
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(x => !x.skipDataModifications).map(engine => [engine.label, engine]))(
|
||||
'Script drived deploy - basic predeploy - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(engine, conn, driver, [
|
||||
[
|
||||
{
|
||||
name: '1.predeploy.sql',
|
||||
text: 'create table ~t1 (~id int primary key); insert into ~t1 (~id) values (1);',
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
const res1 = await runQueryOnDriver(conn, driver, 'SELECT COUNT(*) AS ~cnt FROM ~t1');
|
||||
expect(res1.rows[0].cnt == 1).toBeTruthy();
|
||||
|
||||
const res2 = await runQueryOnDriver(conn, driver, 'SELECT COUNT(*) AS ~cnt FROM ~dbgate_deploy_journal');
|
||||
expect(res2.rows[0].cnt == 1).toBeTruthy();
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(x => !x.skipDataModifications).map(engine => [engine.label, engine]))(
|
||||
'Script drived deploy - install+uninstall - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(engine, conn, driver, [
|
||||
[
|
||||
{
|
||||
name: 't1.uninstall.sql',
|
||||
text: 'drop table ~t1',
|
||||
},
|
||||
{
|
||||
name: 't1.install.sql',
|
||||
text: 'create table ~t1 (~id int primary key); insert into ~t1 (~id) values (1)',
|
||||
},
|
||||
{
|
||||
name: 't2.once.sql',
|
||||
text: 'create table ~t2 (~id int primary key); insert into ~t2 (~id) values (1)',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 't1.uninstall.sql',
|
||||
text: 'drop table ~t1',
|
||||
},
|
||||
{
|
||||
name: 't1.install.sql',
|
||||
text: 'create table ~t1 (~id int primary key, ~val int); insert into ~t1 (~id, ~val) values (1, 11)',
|
||||
},
|
||||
{
|
||||
name: 't2.once.sql',
|
||||
text: 'insert into ~t2 (~id) values (2)',
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
const res1 = await runQueryOnDriver(conn, driver, 'SELECT ~val from ~t1 where ~id = 1');
|
||||
expect(res1.rows[0].val == 11).toBeTruthy();
|
||||
|
||||
const res2 = await runQueryOnDriver(conn, driver, 'SELECT COUNT(*) AS ~cnt FROM ~t2');
|
||||
expect(res2.rows[0].cnt == 1).toBeTruthy();
|
||||
|
||||
const res3 = await runQueryOnDriver(conn, driver, 'SELECT COUNT(*) AS ~cnt FROM ~dbgate_deploy_journal');
|
||||
expect(res3.rows[0].cnt == 3).toBeTruthy();
|
||||
|
||||
const res4 = await runQueryOnDriver(
|
||||
conn,
|
||||
driver,
|
||||
"SELECT ~run_count from ~dbgate_deploy_journal where ~name = 't2.once.sql'"
|
||||
);
|
||||
expect(res4.rows[0].run_count == 1).toBeTruthy();
|
||||
|
||||
const res5 = await runQueryOnDriver(
|
||||
conn,
|
||||
driver,
|
||||
"SELECT ~run_count from ~dbgate_deploy_journal where ~name = 't1.install.sql'"
|
||||
);
|
||||
expect(res5.rows[0].run_count == 2).toBeTruthy();
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Mark table removed, one remains - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await testDatabaseDeploy(engine, conn, driver, [[T1, T2], [T2], [T2]], {
|
||||
markDeleted: true,
|
||||
disallowExtraObjects: true,
|
||||
finalCheckAgainstModel: [T1_DELETED, T2],
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
const { testWrapper } = require('../tools');
|
||||
const engines = require('../engines');
|
||||
const _ = require('lodash');
|
||||
const { formatQueryWithoutParams, runCommandOnDriver } = require('dbgate-tools');
|
||||
|
||||
const initSql = ['CREATE TABLE t1 (id int primary key)', 'CREATE TABLE t2 (id int primary key)'];
|
||||
const initSql = ['CREATE TABLE ~t1 (~id int primary key)', 'CREATE TABLE ~t2 (~id int primary key)'];
|
||||
|
||||
function flatSource() {
|
||||
return _.flatten(
|
||||
@@ -10,6 +11,14 @@ function flatSource() {
|
||||
);
|
||||
}
|
||||
|
||||
function flatSourceParameters() {
|
||||
return _.flatten(
|
||||
engines.map(engine =>
|
||||
(engine.parameters || []).map(parameter => [engine.label, parameter.testName, parameter, engine])
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const obj1Match = expect.objectContaining({
|
||||
pureName: 'obj1',
|
||||
});
|
||||
@@ -26,9 +35,9 @@ describe('Object analyse', () => {
|
||||
test.each(flatSource())(
|
||||
'Full analysis - %s - %s',
|
||||
testWrapper(async (conn, driver, type, object, engine) => {
|
||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
||||
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||
|
||||
await driver.query(conn, object.create1, { discardResult: true });
|
||||
await runCommandOnDriver(conn, driver, object.create1);
|
||||
const structure = await driver.analyseFull(conn);
|
||||
|
||||
expect(structure[type].length).toEqual(1);
|
||||
@@ -39,11 +48,11 @@ describe('Object analyse', () => {
|
||||
test.each(flatSource())(
|
||||
'Incremental analysis - add - %s - %s',
|
||||
testWrapper(async (conn, driver, type, object, engine) => {
|
||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
||||
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||
|
||||
await driver.query(conn, object.create2, { discardResult: true });
|
||||
await runCommandOnDriver(conn, driver, object.create2);
|
||||
const structure1 = await driver.analyseFull(conn);
|
||||
await driver.query(conn, object.create1, { discardResult: true });
|
||||
await runCommandOnDriver(conn, driver, object.create1);
|
||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||
|
||||
expect(structure2[type].length).toEqual(2);
|
||||
@@ -54,12 +63,12 @@ describe('Object analyse', () => {
|
||||
test.each(flatSource())(
|
||||
'Incremental analysis - drop - %s - %s',
|
||||
testWrapper(async (conn, driver, type, object, engine) => {
|
||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
||||
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||
|
||||
await driver.query(conn, object.create1, { discardResult: true });
|
||||
await driver.query(conn, object.create2, { discardResult: true });
|
||||
await runCommandOnDriver(conn, driver, object.create1);
|
||||
await runCommandOnDriver(conn, driver, object.create2);
|
||||
const structure1 = await driver.analyseFull(conn);
|
||||
await driver.query(conn, object.drop2, { discardResult: true });
|
||||
await runCommandOnDriver(conn, driver, object.drop2);
|
||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||
|
||||
expect(structure2[type].length).toEqual(1);
|
||||
@@ -70,15 +79,15 @@ describe('Object analyse', () => {
|
||||
test.each(flatSource())(
|
||||
'Create SQL - add - %s - %s',
|
||||
testWrapper(async (conn, driver, type, object, engine) => {
|
||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
||||
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||
|
||||
await driver.query(conn, object.create1, { discardResult: true });
|
||||
await runCommandOnDriver(conn, driver, object.create1);
|
||||
const structure1 = await driver.analyseFull(conn);
|
||||
await driver.query(conn, object.drop1, { discardResult: true });
|
||||
await runCommandOnDriver(conn, driver, object.drop1);
|
||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||
expect(structure2[type].length).toEqual(0);
|
||||
|
||||
await driver.query(conn, structure1[type][0].createSql, { discardResult: true });
|
||||
await driver.script(conn, structure1[type][0].createSql);
|
||||
|
||||
const structure3 = await driver.analyseIncremental(conn, structure2);
|
||||
|
||||
@@ -86,4 +95,45 @@ describe('Object analyse', () => {
|
||||
expect(structure3[type][0]).toEqual(type.includes('views') ? view1Match : obj1Match);
|
||||
})
|
||||
);
|
||||
|
||||
test.each(flatSourceParameters())(
|
||||
'Test parameters simple analyse - %s - %s',
|
||||
testWrapper(async (conn, driver, testName, parameter, engine) => {
|
||||
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||
for (const sql of engine.parametersOtherSql) await runCommandOnDriver(conn, driver, sql);
|
||||
|
||||
await runCommandOnDriver(conn, driver, parameter.create);
|
||||
const structure = await driver.analyseFull(conn);
|
||||
|
||||
const parameters = structure[parameter.objectTypeField].find(x => x.pureName == 'obj1').parameters;
|
||||
|
||||
expect(parameters.length).toEqual(parameter.list.length);
|
||||
for (let i = 0; i < parameters.length; i += 1) {
|
||||
expect(parameters[i]).toEqual(expect.objectContaining(parameter.list[i]));
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
test.each(flatSourceParameters())(
|
||||
'Test parameters create SQL - %s - %s',
|
||||
testWrapper(async (conn, driver, testName, parameter, engine) => {
|
||||
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
|
||||
for (const sql of engine.parametersOtherSql) await runCommandOnDriver(conn, driver, sql);
|
||||
|
||||
await runCommandOnDriver(conn, driver, parameter.create);
|
||||
const structure1 = await driver.analyseFull(conn);
|
||||
await runCommandOnDriver(conn, driver, parameter.drop);
|
||||
|
||||
const obj = structure1[parameter.objectTypeField].find(x => x.pureName == 'obj1');
|
||||
await driver.script(conn, obj.createSql, { discardResult: true });
|
||||
|
||||
const structure2 = await driver.analyseFull(conn);
|
||||
const parameters = structure2[parameter.objectTypeField].find(x => x.pureName == 'obj1').parameters;
|
||||
|
||||
expect(parameters.length).toEqual(parameter.list.length);
|
||||
for (let i = 0; i < parameters.length; i += 1) {
|
||||
expect(parameters[i]).toEqual(expect.objectContaining(parameter.list[i]));
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
const engines = require('../engines');
|
||||
const { splitQuery } = require('dbgate-query-splitter');
|
||||
const { testWrapper } = require('../tools');
|
||||
const { runQueryOnDriver, runCommandOnDriver, formatQueryWithoutParams } = require('dbgate-tools');
|
||||
|
||||
const initSql = [
|
||||
'CREATE TABLE t1 (id int primary key)',
|
||||
'INSERT INTO t1 (id) VALUES (1)',
|
||||
'INSERT INTO t1 (id) VALUES (2)',
|
||||
'CREATE TABLE ~t1 (~id int primary key)',
|
||||
'INSERT INTO ~t1 (~id) VALUES (1)',
|
||||
'INSERT INTO ~t1 (~id) VALUES (2)',
|
||||
];
|
||||
|
||||
expect.extend({
|
||||
@@ -51,7 +52,7 @@ class StreamHandler {
|
||||
function executeStreamItem(driver, conn, sql) {
|
||||
return new Promise(resolve => {
|
||||
const handler = new StreamHandler(resolve);
|
||||
driver.stream(conn, sql, handler);
|
||||
driver.stream(conn, formatQueryWithoutParams(driver, sql), handler);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,9 +69,11 @@ describe('Query', () => {
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Simple query - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
||||
for (const sql of initSql) {
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(sql));
|
||||
}
|
||||
|
||||
const res = await driver.query(conn, 'SELECT id FROM t1 ORDER BY id');
|
||||
const res = await runQueryOnDriver(conn, driver, dmp => dmp.put('SELECT ~id FROM ~t1 ORDER BY ~id'));
|
||||
expect(res.columns).toEqual([
|
||||
expect.objectContaining({
|
||||
columnName: 'id',
|
||||
@@ -91,8 +94,11 @@ describe('Query', () => {
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Simple stream query - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
||||
const results = await executeStream(driver, conn, 'SELECT id FROM t1 ORDER BY id');
|
||||
for (const sql of initSql) {
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(sql));
|
||||
}
|
||||
|
||||
const results = await executeStream(driver, conn, 'SELECT ~id FROM ~t1 ORDER BY ~id');
|
||||
expect(results.length).toEqual(1);
|
||||
const res = results[0];
|
||||
|
||||
@@ -104,11 +110,14 @@ describe('Query', () => {
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'More queries - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
||||
for (const sql of initSql) {
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(sql));
|
||||
}
|
||||
|
||||
const results = await executeStream(
|
||||
driver,
|
||||
conn,
|
||||
'SELECT id FROM t1 ORDER BY id; SELECT id FROM t1 ORDER BY id DESC'
|
||||
'SELECT ~id FROM ~t1 ORDER BY ~id; SELECT ~id FROM ~t1 ORDER BY ~id DESC'
|
||||
);
|
||||
expect(results.length).toEqual(2);
|
||||
|
||||
@@ -128,7 +137,7 @@ describe('Query', () => {
|
||||
const results = await executeStream(
|
||||
driver,
|
||||
conn,
|
||||
'CREATE TABLE t1 (id int primary key); INSERT INTO t1 (id) VALUES (1); INSERT INTO t1 (id) VALUES (2); SELECT id FROM t1 ORDER BY id; '
|
||||
'CREATE TABLE ~t1 (~id int primary key); INSERT INTO ~t1 (~id) VALUES (1); INSERT INTO ~t1 (~id) VALUES (2); SELECT ~id FROM ~t1 ORDER BY ~id; '
|
||||
);
|
||||
expect(results.length).toEqual(1);
|
||||
|
||||
@@ -144,7 +153,7 @@ describe('Query', () => {
|
||||
const results = await executeStream(
|
||||
driver,
|
||||
conn,
|
||||
'CREATE TABLE t1 (id int); INSERT INTO t1 (id) VALUES (1); INSERT INTO t1 (id) VALUES (2) '
|
||||
'CREATE TABLE ~t1 (~id int); INSERT INTO ~t1 (~id) VALUES (1); INSERT INTO ~t1 (~id) VALUES (2) '
|
||||
);
|
||||
expect(results.length).toEqual(0);
|
||||
})
|
||||
@@ -153,16 +162,57 @@ describe('Query', () => {
|
||||
test.each(engines.filter(x => !x.skipDataModifications).map(engine => [engine.label, engine]))(
|
||||
'Save data query - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
|
||||
for (const sql of initSql) {
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(sql));
|
||||
}
|
||||
|
||||
await driver.script(
|
||||
conn,
|
||||
'INSERT INTO t1 (id) VALUES (3);INSERT INTO t1 (id) VALUES (4);UPDATE t1 SET id=10 WHERE id=1;DELETE FROM t1 WHERE id=2;',
|
||||
formatQueryWithoutParams(
|
||||
driver,
|
||||
'INSERT INTO ~t1 (~id) VALUES (3);INSERT INTO ~t1 (~id) VALUES (4);UPDATE ~t1 SET ~id=10 WHERE ~id=1;DELETE FROM ~t1 WHERE ~id=2;'
|
||||
),
|
||||
{ discardResult: true }
|
||||
);
|
||||
const res = await driver.query(conn, 'SELECT COUNT(*) AS cnt FROM t1');
|
||||
const res = await runQueryOnDriver(conn, driver, dmp => dmp.put('SELECT COUNT(*) AS ~cnt FROM ~t1'));
|
||||
// console.log(res);
|
||||
expect(res.rows[0].cnt == 3).toBeTruthy();
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.filter(x => !x.skipDataDuplicator).map(engine => [engine.label, engine]))(
|
||||
'Select scope identity - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await runCommandOnDriver(conn, driver, dmp =>
|
||||
dmp.createTable({
|
||||
pureName: 't1',
|
||||
columns: [
|
||||
{ columnName: 'id', dataType: 'int', notNull: true, autoIncrement: true },
|
||||
{ columnName: 'val', dataType: 'varchar(50)' },
|
||||
],
|
||||
primaryKey: {
|
||||
columns: [{ columnName: 'id' }],
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const structure = await driver.analyseFull(conn);
|
||||
const table = structure.tables.find(x => x.pureName == 't1');
|
||||
|
||||
let res;
|
||||
if (driver.dialect.requireStandaloneSelectForScopeIdentity) {
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put("INSERT INTO ~t1 (~val) VALUES ('aaa')"));
|
||||
res = await runQueryOnDriver(conn, driver, dmp => dmp.selectScopeIdentity(table));
|
||||
} else {
|
||||
res = await runQueryOnDriver(conn, driver, dmp => {
|
||||
dmp.putCmd("INSERT INTO ~t1 (~val) VALUES ('aaa')");
|
||||
dmp.selectScopeIdentity(table);
|
||||
});
|
||||
}
|
||||
const row = res.rows[0];
|
||||
const keys = Object.keys(row);
|
||||
expect(keys.length).toEqual(1);
|
||||
expect(row[keys[0]] == 1).toBeTruthy();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -76,7 +76,7 @@ describe('Schema tests', () => {
|
||||
});
|
||||
|
||||
describe('Base analyser test', () => {
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))(
|
||||
'Structure without change - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await baseStructure(conn, driver);
|
||||
|
||||
@@ -1,28 +1,34 @@
|
||||
const { runCommandOnDriver } = require('dbgate-tools');
|
||||
const engines = require('../engines');
|
||||
const { testWrapper } = require('../tools');
|
||||
|
||||
const t1Sql = 'CREATE TABLE t1 (id int not null primary key, val1 varchar(50))';
|
||||
const ix1Sql = 'CREATE index ix1 ON t1(val1, id)';
|
||||
const t1Sql = 'CREATE TABLE ~t1 (~id int not null primary key, ~val1 varchar(50))';
|
||||
const ix1Sql = 'CREATE index ~ix1 ON ~t1(~val1, ~id)';
|
||||
const t2Sql = engine =>
|
||||
`CREATE TABLE t2 (id int not null primary key, val2 varchar(50) ${engine.skipUnique ? '' : 'unique'})`;
|
||||
const t3Sql = 'CREATE TABLE t3 (id int not null primary key, valfk int, foreign key (valfk) references t2(id))';
|
||||
`CREATE TABLE ~t2 (~id int not null primary key, ~val2 varchar(50) ${engine.skipUnique ? '' : 'unique'})`;
|
||||
const t3Sql = 'CREATE TABLE ~t3 (~id int not null primary key, ~valfk int, foreign key (~valfk) references ~t2(~id))';
|
||||
const t4Sql = 'CREATE TABLE ~t4 (~id int not null primary key, ~valdef int default 12 not null)';
|
||||
// const fkSql = 'ALTER TABLE t3 ADD FOREIGN KEY (valfk) REFERENCES t2(id)'
|
||||
|
||||
const txMatch = (engine, tname, vcolname, nextcol) =>
|
||||
const txMatch = (engine, tname, vcolname, nextcol, defaultValue) =>
|
||||
expect.objectContaining({
|
||||
pureName: tname,
|
||||
columns: [
|
||||
expect.objectContaining({
|
||||
columnName: 'id',
|
||||
dataType: expect.stringMatching(/int.*/i),
|
||||
dataType: expect.stringMatching(/int.*|number/i),
|
||||
...(engine.skipNullability ? {} : { notNull: true }),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
columnName: vcolname,
|
||||
...(engine.skipNullability ? {} : { notNull: false }),
|
||||
dataType: engine.skipStringLength
|
||||
? expect.stringMatching(/.*string|char.*/i)
|
||||
: expect.stringMatching(/.*char.*\(50\)/i),
|
||||
...(engine.skipNullability ? {} : { notNull: !!defaultValue }),
|
||||
...(defaultValue
|
||||
? { defaultValue }
|
||||
: {
|
||||
dataType: engine.skipStringLength
|
||||
? expect.stringMatching(/.*string|char.*/i)
|
||||
: expect.stringMatching(/.*char.*\(50\)/i),
|
||||
}),
|
||||
}),
|
||||
...(nextcol
|
||||
? [
|
||||
@@ -48,14 +54,16 @@ const txMatch = (engine, tname, vcolname, nextcol) =>
|
||||
const t1Match = engine => txMatch(engine, 't1', 'val1');
|
||||
const t2Match = engine => txMatch(engine, 't2', 'val2');
|
||||
const t2NextColMatch = engine => txMatch(engine, 't2', 'val2', true);
|
||||
const t4Match = engine => txMatch(engine, 't4', 'valdef', null, '12');
|
||||
|
||||
describe('Table analyse', () => {
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Table structure - full analysis - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await driver.query(conn, t1Sql);
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
|
||||
|
||||
const structure = await driver.analyseFull(conn);
|
||||
console.log(JSON.stringify(structure, null, 2));
|
||||
|
||||
expect(structure.tables.length).toEqual(1);
|
||||
expect(structure.tables[0]).toEqual(t1Match(engine));
|
||||
@@ -65,13 +73,13 @@ describe('Table analyse', () => {
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Table add - incremental analysis - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await driver.query(conn, t2Sql(engine));
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
|
||||
|
||||
const structure1 = await driver.analyseFull(conn);
|
||||
expect(structure1.tables.length).toEqual(1);
|
||||
expect(structure1.tables[0]).toEqual(t2Match(engine));
|
||||
|
||||
await driver.query(conn, t1Sql);
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
|
||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||
|
||||
expect(structure2.tables.length).toEqual(2);
|
||||
@@ -83,14 +91,14 @@ describe('Table analyse', () => {
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Table remove - incremental analysis - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await driver.query(conn, t1Sql);
|
||||
await driver.query(conn, t2Sql(engine));
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
|
||||
const structure1 = await driver.analyseFull(conn);
|
||||
expect(structure1.tables.length).toEqual(2);
|
||||
expect(structure1.tables.find(x => x.pureName == 't1')).toEqual(t1Match(engine));
|
||||
expect(structure1.tables.find(x => x.pureName == 't2')).toEqual(t2Match(engine));
|
||||
|
||||
await driver.query(conn, 'DROP TABLE t2');
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put('DROP TABLE ~t2'));
|
||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||
|
||||
expect(structure2.tables.length).toEqual(1);
|
||||
@@ -101,15 +109,14 @@ describe('Table analyse', () => {
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Table change - incremental analysis - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await driver.query(conn, t1Sql);
|
||||
await driver.query(conn, t2Sql(engine));
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
|
||||
const structure1 = await driver.analyseFull(conn);
|
||||
|
||||
if (engine.dbSnapshotBySeconds) await new Promise(resolve => setTimeout(resolve, 1100));
|
||||
|
||||
await driver.query(
|
||||
conn,
|
||||
`ALTER TABLE t2 ADD ${engine.alterTableAddColumnSyntax ? 'COLUMN' : ''} nextcol varchar(50)`
|
||||
await runCommandOnDriver(conn, driver, dmp =>
|
||||
dmp.put(`ALTER TABLE ~t2 ADD ${engine.alterTableAddColumnSyntax ? 'COLUMN' : ''} ~nextcol varchar(50)`)
|
||||
);
|
||||
const structure2 = await driver.analyseIncremental(conn, structure1);
|
||||
|
||||
@@ -124,8 +131,8 @@ describe('Table analyse', () => {
|
||||
test.each(engines.filter(x => !x.skipIndexes).map(engine => [engine.label, engine]))(
|
||||
'Index - full analysis - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await driver.query(conn, t1Sql);
|
||||
await driver.query(conn, ix1Sql);
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(ix1Sql));
|
||||
const structure = await driver.analyseFull(conn);
|
||||
|
||||
const t1 = structure.tables.find(x => x.pureName == 't1');
|
||||
@@ -139,7 +146,7 @@ describe('Table analyse', () => {
|
||||
test.each(engines.filter(x => !x.skipUnique).map(engine => [engine.label, engine]))(
|
||||
'Unique - full analysis - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await driver.query(conn, t2Sql(engine));
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
|
||||
const structure = await driver.analyseFull(conn);
|
||||
|
||||
const t2 = structure.tables.find(x => x.pureName == 't2');
|
||||
@@ -153,8 +160,8 @@ describe('Table analyse', () => {
|
||||
test.each(engines.filter(x => !x.skipReferences).map(engine => [engine.label, engine]))(
|
||||
'Foreign key - full analysis - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await driver.query(conn, t2Sql(engine));
|
||||
await driver.query(conn, t3Sql);
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t3Sql));
|
||||
// await driver.query(conn, fkSql);
|
||||
|
||||
const structure = await driver.analyseFull(conn);
|
||||
@@ -169,4 +176,16 @@ describe('Table analyse', () => {
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
test.each(engines.map(engine => [engine.label, engine]))(
|
||||
'Table structure - default value - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put(t4Sql));
|
||||
|
||||
const structure = await driver.analyseFull(conn);
|
||||
|
||||
expect(structure.tables.length).toEqual(1);
|
||||
expect(structure.tables[0]).toEqual(t4Match(engine));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ const _ = require('lodash');
|
||||
const fp = require('lodash/fp');
|
||||
const engines = require('../engines');
|
||||
const { testWrapper } = require('../tools');
|
||||
const { extendDatabaseInfo } = require('dbgate-tools');
|
||||
const { extendDatabaseInfo, runCommandOnDriver } = require('dbgate-tools');
|
||||
|
||||
function createExpector(value) {
|
||||
return _.cloneDeepWith(value, x => {
|
||||
@@ -25,7 +25,7 @@ function checkTableStructure2(t1, t2) {
|
||||
}
|
||||
|
||||
async function testTableCreate(conn, driver, table) {
|
||||
await driver.query(conn, `create table t0 (id int not null primary key)`);
|
||||
await runCommandOnDriver(conn, driver, dmp => dmp.put('create table ~t0 (~id int not null primary key)'));
|
||||
|
||||
const dmp = driver.createDumper();
|
||||
const table1 = {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,14 +8,14 @@ services:
|
||||
ports:
|
||||
- 15000:5432
|
||||
|
||||
# mariadb:
|
||||
# image: mariadb
|
||||
# command: --default-authentication-plugin=mysql_native_password
|
||||
# restart: always
|
||||
# ports:
|
||||
# - 15004:3306
|
||||
# environment:
|
||||
# - MYSQL_ROOT_PASSWORD=Pwd2020Db
|
||||
mariadb:
|
||||
image: mariadb
|
||||
command: --default-authentication-plugin=mysql_native_password
|
||||
restart: always
|
||||
ports:
|
||||
- 15004:3306
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=Pwd2020Db
|
||||
|
||||
# mysql:
|
||||
# image: mysql:8.0.18
|
||||
@@ -70,3 +70,9 @@ services:
|
||||
# - cockroachdb
|
||||
# restart: on-failure
|
||||
|
||||
oracle:
|
||||
image: gvenzl/oracle-xe:21-slim
|
||||
environment:
|
||||
ORACLE_PASSWORD: Pwd2020Db
|
||||
ports:
|
||||
- 15006:1521
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const views = {
|
||||
type: 'views',
|
||||
create1: 'CREATE VIEW obj1 AS SELECT id FROM t1',
|
||||
create2: 'CREATE VIEW obj2 AS SELECT id FROM t2',
|
||||
drop1: 'DROP VIEW obj1',
|
||||
drop2: 'DROP VIEW obj2',
|
||||
create1: 'CREATE VIEW ~obj1 AS SELECT ~id FROM ~t1',
|
||||
create2: 'CREATE VIEW ~obj2 AS SELECT ~id FROM ~t2',
|
||||
drop1: 'DROP VIEW ~obj1',
|
||||
drop2: 'DROP VIEW ~obj2',
|
||||
};
|
||||
const matviews = {
|
||||
type: 'matviews',
|
||||
@@ -28,8 +28,86 @@ const engines = [
|
||||
port: 15001,
|
||||
},
|
||||
// skipOnCI: true,
|
||||
objects: [views],
|
||||
objects: [
|
||||
views,
|
||||
{
|
||||
type: 'procedures',
|
||||
create1: 'CREATE PROCEDURE obj1() BEGIN SELECT * FROM t1; END',
|
||||
create2: 'CREATE PROCEDURE obj2() BEGIN SELECT * FROM t2; END',
|
||||
drop1: 'DROP PROCEDURE obj1',
|
||||
drop2: 'DROP PROCEDURE obj2',
|
||||
},
|
||||
],
|
||||
dbSnapshotBySeconds: true,
|
||||
dumpFile: 'data/chinook-mysql.sql',
|
||||
dumpChecks: [
|
||||
{
|
||||
sql: 'select count(*) as res from genre',
|
||||
res: '25',
|
||||
},
|
||||
],
|
||||
parametersOtherSql: ['CREATE PROCEDURE obj2(a int, b int) BEGIN SELECT * FROM t1; END'],
|
||||
parameters: [
|
||||
{
|
||||
testName: 'simple',
|
||||
create: 'CREATE PROCEDURE obj1(a int) BEGIN SELECT * FROM t1; END',
|
||||
drop: 'DROP PROCEDURE obj1',
|
||||
objectTypeField: 'procedures',
|
||||
list: [
|
||||
{
|
||||
parameterName: 'a',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'int',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
testName: 'paramTypes',
|
||||
create: 'CREATE PROCEDURE obj1(a int, b varchar(50), c numeric(10,2)) BEGIN SELECT * FROM t1; END',
|
||||
drop: 'DROP PROCEDURE obj1',
|
||||
objectTypeField: 'procedures',
|
||||
list: [
|
||||
{
|
||||
parameterName: 'a',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'int',
|
||||
},
|
||||
{
|
||||
parameterName: 'b',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'varchar(50)',
|
||||
},
|
||||
{
|
||||
parameterName: 'c',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'decimal(10,2)',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
testName: 'paramModes',
|
||||
create: 'CREATE PROCEDURE obj1(IN a int, OUT b int, INOUT c int) BEGIN SELECT * FROM t1; END',
|
||||
drop: 'DROP PROCEDURE obj1',
|
||||
objectTypeField: 'procedures',
|
||||
list: [
|
||||
{
|
||||
parameterName: 'a',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'int',
|
||||
},
|
||||
{
|
||||
parameterName: 'b',
|
||||
parameterMode: 'OUT',
|
||||
dataType: 'int',
|
||||
},
|
||||
{
|
||||
parameterName: 'c',
|
||||
parameterMode: 'INOUT',
|
||||
dataType: 'int',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'MariaDB',
|
||||
@@ -47,6 +125,13 @@ const engines = [
|
||||
skipOnCI: true,
|
||||
objects: [views],
|
||||
dbSnapshotBySeconds: true,
|
||||
dumpFile: 'data/chinook-mysql.sql',
|
||||
dumpChecks: [
|
||||
{
|
||||
sql: 'select count(*) as res from genre',
|
||||
res: '25',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'PostgreSQL',
|
||||
@@ -82,7 +167,103 @@ const engines = [
|
||||
},
|
||||
],
|
||||
supportSchemas: true,
|
||||
supportRenameSqlObject: true,
|
||||
defaultSchemaName: 'public',
|
||||
dumpFile: 'data/chinook-postgre.sql',
|
||||
dumpChecks: [
|
||||
{
|
||||
sql: 'select count(*) as res from "public"."Genre"',
|
||||
res: '25',
|
||||
},
|
||||
],
|
||||
|
||||
parametersOtherSql: ['CREATE PROCEDURE obj2(a integer, b integer) LANGUAGE SQL AS $$ select * from t1 $$'],
|
||||
parameters: [
|
||||
{
|
||||
testName: 'simple',
|
||||
create: 'CREATE PROCEDURE obj1(a integer) LANGUAGE SQL AS $$ select * from t1 $$',
|
||||
drop: 'DROP PROCEDURE obj1',
|
||||
objectTypeField: 'procedures',
|
||||
list: [
|
||||
{
|
||||
parameterName: 'a',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'integer',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
testName: 'dataTypes',
|
||||
create:
|
||||
'CREATE PROCEDURE obj1(a integer, b varchar(20), c numeric(18,2)) LANGUAGE SQL AS $$ select * from t1 $$',
|
||||
drop: 'DROP PROCEDURE obj1',
|
||||
objectTypeField: 'procedures',
|
||||
list: [
|
||||
{
|
||||
parameterName: 'a',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'integer',
|
||||
},
|
||||
{
|
||||
parameterName: 'b',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'varchar',
|
||||
},
|
||||
{
|
||||
parameterName: 'c',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'numeric',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
testName: 'paramModes',
|
||||
create: 'CREATE PROCEDURE obj1(IN a integer, INOUT b integer) LANGUAGE SQL AS $$ select * from t1 $$',
|
||||
drop: 'DROP PROCEDURE obj1',
|
||||
objectTypeField: 'procedures',
|
||||
list: [
|
||||
{
|
||||
parameterName: 'a',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'integer',
|
||||
},
|
||||
{
|
||||
parameterName: 'b',
|
||||
parameterMode: 'INOUT',
|
||||
dataType: 'integer',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
testName: 'paramModesFunction',
|
||||
objectTypeField: 'functions',
|
||||
create: `
|
||||
create or replace function obj1(
|
||||
out min_len int,
|
||||
out max_len int)
|
||||
language plpgsql
|
||||
as $$
|
||||
begin
|
||||
select min(id),
|
||||
max(id)
|
||||
into min_len, max_len
|
||||
from t1;
|
||||
end;$$`,
|
||||
drop: 'DROP FUNCTION obj1',
|
||||
list: [
|
||||
{
|
||||
parameterName: 'min_len',
|
||||
parameterMode: 'OUT',
|
||||
dataType: 'integer',
|
||||
},
|
||||
{
|
||||
parameterName: 'max_len',
|
||||
parameterMode: 'OUT',
|
||||
dataType: 'integer',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'SQL Server',
|
||||
@@ -107,7 +288,65 @@ const engines = [
|
||||
drop2: 'DROP PROCEDURE obj2',
|
||||
},
|
||||
],
|
||||
parametersOtherSql: ['CREATE PROCEDURE obj2 (@p1 int, @p2 int) AS SELECT id from t1'],
|
||||
parameters: [
|
||||
{
|
||||
testName: 'simple',
|
||||
create: 'CREATE PROCEDURE obj1 (@param1 int) AS SELECT id from t1',
|
||||
drop: 'DROP PROCEDURE obj1',
|
||||
objectTypeField: 'procedures',
|
||||
list: [
|
||||
{
|
||||
parameterName: '@param1',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'int',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
testName: 'dataTypes',
|
||||
create: 'CREATE PROCEDURE obj1 (@p1 bit, @p2 nvarchar(20), @p3 decimal(18,2), @p4 float) AS SELECT id from t1',
|
||||
drop: 'DROP PROCEDURE obj1',
|
||||
objectTypeField: 'procedures',
|
||||
list: [
|
||||
{
|
||||
parameterName: '@p1',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'bit',
|
||||
},
|
||||
{
|
||||
parameterName: '@p2',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'nvarchar(20)',
|
||||
},
|
||||
{
|
||||
parameterName: '@p3',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'decimal(18,2)',
|
||||
},
|
||||
{
|
||||
parameterName: '@p4',
|
||||
parameterMode: 'IN',
|
||||
dataType: 'float',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
testName: 'outputParam',
|
||||
create: 'CREATE PROCEDURE obj1 (@p1 int OUTPUT) AS SELECT id from t1',
|
||||
drop: 'DROP PROCEDURE obj1',
|
||||
objectTypeField: 'procedures',
|
||||
list: [
|
||||
{
|
||||
parameterName: '@p1',
|
||||
parameterMode: 'OUT',
|
||||
dataType: 'int',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
supportSchemas: true,
|
||||
supportRenameSqlObject: true,
|
||||
defaultSchemaName: 'dbo',
|
||||
// skipSeparateSchemas: true,
|
||||
},
|
||||
@@ -119,6 +358,7 @@ const engines = [
|
||||
},
|
||||
objects: [views],
|
||||
skipOnCI: false,
|
||||
skipChangeColumn: true,
|
||||
},
|
||||
{
|
||||
label: 'CockroachDB',
|
||||
@@ -158,6 +398,43 @@ const engines = [
|
||||
skipStringLength: true,
|
||||
alterTableAddColumnSyntax: true,
|
||||
dbSnapshotBySeconds: true,
|
||||
skipChangeColumn: true,
|
||||
},
|
||||
{
|
||||
label: 'Oracle',
|
||||
connection: {
|
||||
engine: 'oracle@dbgate-plugin-oracle',
|
||||
password: 'Pwd2020Db',
|
||||
user: 'system',
|
||||
server: 'oracle',
|
||||
port: 1521,
|
||||
serviceName: 'xe',
|
||||
},
|
||||
local: {
|
||||
server: 'localhost',
|
||||
port: 15006,
|
||||
},
|
||||
skipOnCI: false,
|
||||
dbSnapshotBySeconds: true,
|
||||
setNullDefaultInsteadOfDrop: true,
|
||||
skipIncrementalAnalysis: true,
|
||||
objects: [
|
||||
views,
|
||||
{
|
||||
type: 'procedures',
|
||||
create1: 'CREATE PROCEDURE ~obj1 AS BEGIN SELECT ~id FROM ~t1 END',
|
||||
create2: 'CREATE PROCEDURE ~obj2 AS BEGIN SELECT ~id FROM ~t2 END',
|
||||
drop1: 'DROP PROCEDURE ~obj1',
|
||||
drop2: 'DROP PROCEDURE ~obj2',
|
||||
},
|
||||
{
|
||||
type: 'functions',
|
||||
create1: 'CREATE FUNCTION ~obj1 RETURN NUMBER IS v_count NUMBER; \n BEGIN SELECT COUNT(*) INTO v_count FROM ~t1;\n RETURN v_count;\n END ~obj1',
|
||||
create2: 'CREATE FUNCTION ~obj2 RETURN NUMBER IS v_count NUMBER; \n BEGIN SELECT COUNT(*) INTO v_count FROM ~t2;\n RETURN v_count;\n END ~obj2',
|
||||
drop1: 'DROP FUNCTION ~obj1',
|
||||
drop2: 'DROP FUNCTION ~obj2',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -167,9 +444,10 @@ const filterLocal = [
|
||||
'-MariaDB',
|
||||
'-PostgreSQL',
|
||||
'-SQL Server',
|
||||
'SQLite',
|
||||
'-SQLite',
|
||||
'-CockroachDB',
|
||||
'-ClickHouse',
|
||||
'Oracle',
|
||||
];
|
||||
|
||||
const enginesPostgre = engines.filter(x => x.label == 'PostgreSQL');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dbgate-integration-tests",
|
||||
"version": "5.0.0-alpha.1",
|
||||
"version": "6.0.0-alpha.1",
|
||||
"homepage": "https://dbgate.org/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
const requireEngineDriver = require('dbgate-api/src/utility/requireEngineDriver');
|
||||
const crypto = require('crypto');
|
||||
|
||||
function randomDbName() {
|
||||
function randomDbName(dialect) {
|
||||
const generatedKey = crypto.randomBytes(6);
|
||||
const newKey = generatedKey.toString('hex');
|
||||
return `db${newKey}`;
|
||||
const res = `db${newKey}`;
|
||||
if (dialect.upperCaseAllDbObjectNames) return res.toUpperCase();
|
||||
return res;
|
||||
}
|
||||
|
||||
function extractConnection(engine) {
|
||||
@@ -32,23 +34,50 @@ async function connect(engine, database) {
|
||||
return conn;
|
||||
} else {
|
||||
const conn = await driver.connect(connection);
|
||||
await driver.query(conn, `CREATE DATABASE ${database}`);
|
||||
const dmp = driver.createDumper();
|
||||
dmp.createDatabase(database);
|
||||
await driver.query(conn, dmp.s);
|
||||
await driver.close(conn);
|
||||
|
||||
const res = await driver.connect({
|
||||
...connection,
|
||||
database,
|
||||
database: (driver.dialect.userDatabaseNamePrefix ?? '') + database,
|
||||
});
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
async function prepareConnection(engine, database) {
|
||||
const connection = extractConnection(engine);
|
||||
const driver = requireEngineDriver(connection);
|
||||
|
||||
if (engine.generateDbFile) {
|
||||
return {
|
||||
...connection,
|
||||
databaseFile: `dbtemp/${database}`,
|
||||
isPreparedOnly: true,
|
||||
};
|
||||
} else {
|
||||
const conn = await driver.connect(connection);
|
||||
const dmp = driver.createDumper();
|
||||
dmp.createDatabase(database);
|
||||
await driver.query(conn, dmp.s);
|
||||
await driver.close(conn);
|
||||
|
||||
return {
|
||||
...connection,
|
||||
database: (driver.dialect.userDatabaseNamePrefix ?? '') + database,
|
||||
isPreparedOnly: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const testWrapper =
|
||||
body =>
|
||||
async (label, ...other) => {
|
||||
const engine = other[other.length - 1];
|
||||
const driver = requireEngineDriver(engine.connection);
|
||||
const conn = await connect(engine, randomDbName());
|
||||
const conn = await connect(engine, randomDbName(driver.dialect));
|
||||
try {
|
||||
await body(conn, driver, ...other);
|
||||
} finally {
|
||||
@@ -56,9 +85,19 @@ const testWrapper =
|
||||
}
|
||||
};
|
||||
|
||||
const testWrapperPrepareOnly =
|
||||
body =>
|
||||
async (label, ...other) => {
|
||||
const engine = other[other.length - 1];
|
||||
const driver = requireEngineDriver(engine.connection);
|
||||
const conn = await prepareConnection(engine, randomDbName(driver.dialect));
|
||||
await body(conn, driver, ...other);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
randomDbName,
|
||||
connect,
|
||||
extractConnection,
|
||||
testWrapper,
|
||||
testWrapperPrepareOnly,
|
||||
};
|
||||
|
||||
+10
-7
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "5.5.2",
|
||||
"version": "6.1.0",
|
||||
"name": "dbgate-all",
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
@@ -21,6 +21,7 @@
|
||||
"start:api:auth": "yarn workspace dbgate-api start:auth | pino-pretty",
|
||||
"start:api:dblogin": "yarn workspace dbgate-api start:dblogin | pino-pretty",
|
||||
"start:api:storage": "yarn workspace dbgate-api start:storage | pino-pretty",
|
||||
"start:api:storage:built": "yarn workspace dbgate-api start:storage:built | pino-pretty",
|
||||
"sync:pro": "cd sync && yarn start",
|
||||
"start:web": "yarn workspace dbgate-web dev",
|
||||
"start:sqltree": "yarn workspace dbgate-sqltree start",
|
||||
@@ -34,7 +35,8 @@
|
||||
"build:lib": "yarn build:sqltree && yarn build:tools && yarn build:filterparser && yarn build:datalib",
|
||||
"build:app": "yarn plugins:copydist && cd app && yarn install && yarn build",
|
||||
"build:api": "yarn workspace dbgate-api build",
|
||||
"build:web:docker": "yarn workspace dbgate-web build",
|
||||
"build:api:doc": "yarn workspace dbgate-api build:doc",
|
||||
"build:web": "yarn workspace dbgate-web build",
|
||||
"build:plugins:frontend": "workspaces-run --only=\"dbgate-plugin-*\" -- yarn build:frontend",
|
||||
"build:plugins:backend": "workspaces-run --only=\"dbgate-plugin-*\" -- yarn build:backend",
|
||||
"build:plugins:frontend:watch": "workspaces-run --parallel --only=\"dbgate-plugin-*\" -- yarn build:frontend:watch",
|
||||
@@ -46,20 +48,21 @@
|
||||
"printSecrets": "node printSecrets",
|
||||
"generatePadFile": "node generatePadFile",
|
||||
"adjustPackageJson": "node adjustPackageJson",
|
||||
"fillNativeModules": "node fillNativeModules",
|
||||
"fillNativeModulesElectron": "node fillNativeModules --electron",
|
||||
"fillPackagedPlugins": "node fillPackagedPlugins",
|
||||
"resetPackagedPlugins": "node resetPackagedPlugins",
|
||||
"prettier": "prettier --write packages/api/src && prettier --write packages/datalib/src && prettier --write packages/filterparser/src && prettier --write packages/sqltree/src && prettier --write packages/tools/src && prettier --write packages/types && prettier --write packages/web/src && prettier --write app/src",
|
||||
"copy:docker:build": "copyfiles packages/api/dist/* docker -f && copyfiles packages/web/public/* docker -u 2 && copyfiles \"packages/web/public/**/*\" docker -u 2 && copyfiles \"plugins/dist/**/*\" docker/plugins -u 2",
|
||||
"install:drivers:docker": "cd docker && yarn init --yes && yarn add better-sqlite3 && yarn add oracledb && cd ..",
|
||||
"prepare:docker": "yarn plugins:copydist && yarn build:web:docker && yarn build:api && yarn copy:docker:build && yarn install:drivers:docker",
|
||||
"copy:packer:build": "copyfiles packages/api/dist/* packer/build -f && copyfiles packages/web/public/* packer/build -u 2 && copyfiles \"packages/web/public/**/*\" packer/build -u 2 && copyfiles \"plugins/dist/**/*\" packer/build/plugins -u 2 && copyfiles packer/install-packages.sh packer/build -f && yarn install:drivers:packer",
|
||||
"install:drivers:docker": "node common/defineVolatileDependencies.js docker && cd docker && yarn install && cd ..",
|
||||
"install:drivers:packer": "node common/defineVolatileDependencies.js packer/build",
|
||||
"prepare:docker": "yarn plugins:copydist && yarn build:web && yarn build:api && yarn copy:docker:build && yarn install:drivers:docker",
|
||||
"prepare:packer": "yarn plugins:copydist && yarn build:web && yarn build:api && yarn copy:packer:build",
|
||||
"start": "concurrently --kill-others-on-fail \"yarn start:api\" \"yarn start:web\"",
|
||||
"lib": "concurrently --kill-others-on-fail \"yarn start:sqltree\" \"yarn start:filterparser\" \"yarn start:datalib\" \"yarn start:tools\" \"yarn build:plugins:frontend:watch\"",
|
||||
"ts:api": "yarn workspace dbgate-api ts",
|
||||
"ts:web": "yarn workspace dbgate-web ts",
|
||||
"ts": "yarn ts:api && yarn ts:web",
|
||||
"postinstall": "yarn resetPackagedPlugins && yarn build:lib && patch-package && yarn fillNativeModules && yarn build:plugins:frontend",
|
||||
"postinstall": "yarn resetPackagedPlugins && yarn build:lib && patch-package && yarn build:plugins:frontend",
|
||||
"dbgate-serve": "node packages/dbgate/bin/dbgate-serve.js"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
DEVMODE=1
|
||||
SHELL_SCRIPTING=1
|
||||
|
||||
# CLOUD_UPGRADE_FILE=c:\test\upg\upgrade.zip
|
||||
|
||||
# PERMISSIONS=~widgets/app,~widgets/plugins
|
||||
# DISABLE_SHELL=1
|
||||
# HIDE_APP_EDITOR=1
|
||||
|
||||
@@ -12,15 +12,14 @@ This example exports table Customer info CSV file.
|
||||
|
||||
```javascript
|
||||
const dbgateApi = require('dbgate-api');
|
||||
const dbgatePluginMssql = require("dbgate-plugin-mssql");
|
||||
const dbgatePluginMysql = require("dbgate-plugin-mysql");
|
||||
const dbgatePluginCsv = require("dbgate-plugin-csv");
|
||||
|
||||
dbgateApi.registerPlugins(dbgatePluginMssql);
|
||||
dbgateApi.registerPlugins(dbgatePluginMysql);
|
||||
|
||||
async function run() {
|
||||
const reader = await dbgateApi.tableReader({
|
||||
connection: { server: 'localhost', engine: 'mssql', user: 'sa', password: 'xxxx', database: 'Chinook' },
|
||||
schemaName: 'dbo',
|
||||
connection: { server: 'localhost', engine: 'mysql@dbgate-plugin-mysql', user: 'root', password: 'xxxx', database: 'Chinook' },
|
||||
pureName: 'Customer',
|
||||
});
|
||||
const writer = await dbgatePluginCsv.shellApi.writer({ fileName: 'Customer.csv' });
|
||||
@@ -59,8 +58,8 @@ Copies data from reader into writer. Reader and writer should be created from fu
|
||||
Reads table or view.
|
||||
```js
|
||||
const reader = await dbgateApi.tableReader({
|
||||
connection: { server: 'localhost', engine: 'mssql' | 'postgres' | 'mysql', user: 'root', password: 'xxxx', database: 'DB_NAME' },
|
||||
schemaName: 'dbo',
|
||||
connection: { server: 'localhost', engine: 'postgres@dbgate-plugin-postgres', user: 'root', password: 'xxxx', database: 'DB_NAME' },
|
||||
schemaName: 'public',
|
||||
pureName: 'Customer',
|
||||
});
|
||||
```
|
||||
@@ -69,7 +68,7 @@ Reads table or view.
|
||||
Executes query and reads its result.
|
||||
```js
|
||||
const reader = await dbgateApi.tableReader({
|
||||
connection: { server: 'localhost', engine: 'mssql' | 'postgres' | 'mysql', user: 'root', password: 'xxxx', database: 'DB_NAME' },
|
||||
connection: { server: 'localhost', engine: 'mysql@dbgate-plugin-mysql', user: 'root', password: 'xxxx', database: 'DB_NAME' },
|
||||
sql: 'SELECT * FROM Album',
|
||||
});
|
||||
```
|
||||
@@ -81,8 +80,7 @@ Imports data into table. Options are optional, default values are false.
|
||||
- createIfNotExists - create table, if not exists
|
||||
```js
|
||||
const reader = await dbgateApi.tableWriter({
|
||||
connection: { server: 'localhost', engine: 'mssql' | 'postgres' | 'mysql', user: 'root', password: 'xxxx', database: 'DB_NAME' },
|
||||
schemaName: 'dbo',
|
||||
connection: { server: 'localhost', engine: 'mysql@dbgate-plugin-mysql', user: 'root', password: 'xxxx', database: 'DB_NAME' },
|
||||
pureName: 'Customer',
|
||||
options: {
|
||||
dropIfExists: false,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
layout: docs
|
||||
title: API documentation
|
||||
order: 21
|
||||
docs_left: true
|
||||
hide_hero: true
|
||||
---
|
||||
|
||||
# API Documentation
|
||||
|
||||
{{>main}}
|
||||
Vendored
+29
-42
@@ -1,60 +1,47 @@
|
||||
DEVMODE=1
|
||||
|
||||
CONNECTIONS=mysql,postgres,postgres1,mongo,mongo2,mysqlssh,sqlite,relational
|
||||
CONNECTIONS=mysql,postgres,mongo,redis,mssql,oracle
|
||||
|
||||
LABEL_mysql=MySql localhost
|
||||
SERVER_mysql=localhost
|
||||
LABEL_mysql=MySql
|
||||
SERVER_mysql=dbgatedckstage1.sprinx.cz
|
||||
USER_mysql=root
|
||||
PASSWORD_mysql=test
|
||||
PORT_mysql=3307
|
||||
PASSWORD_mysql=Pwd2020Db
|
||||
PORT_mysql=3306
|
||||
ENGINE_mysql=mysql@dbgate-plugin-mysql
|
||||
|
||||
LABEL_postgres=Postgres localhost
|
||||
SERVER_postgres=localhost
|
||||
LABEL_postgres=Postgres
|
||||
SERVER_postgres=dbgatedckstage1.sprinx.cz
|
||||
USER_postgres=postgres
|
||||
PASSWORD_postgres=Pwd2020Db
|
||||
PORT_postgres=5432
|
||||
ENGINE_postgres=postgres@dbgate-plugin-postgres
|
||||
|
||||
LABEL_postgres1=Postgres localhost test DB
|
||||
SERVER_postgres1=localhost
|
||||
USER_postgres1=postgres
|
||||
PASSWORD_postgres1=Pwd2020Db
|
||||
PORT_postgres1=5432
|
||||
ENGINE_postgres1=postgres@dbgate-plugin-postgres
|
||||
DATABASE_postgres1=test
|
||||
|
||||
LABEL_mongo=Mongo URL
|
||||
URL_mongo=mongodb://localhost:27017
|
||||
LABEL_mongo=Mongo
|
||||
SERVER_mongo=dbgatedckstage1.sprinx.cz
|
||||
USER_mongo=root
|
||||
PASSWORD_mongo=Pwd2020Db
|
||||
PORT_mongo=27017
|
||||
ENGINE_mongo=mongo@dbgate-plugin-mongo
|
||||
|
||||
LABEL_mongo2=Mongo Server
|
||||
SERVER_mongo2=localhost
|
||||
ENGINE_mongo2=mongo@dbgate-plugin-mongo
|
||||
LABEL_redis=Redis
|
||||
SERVER_redis=dbgatedckstage1.sprinx.cz
|
||||
ENGINE_redis=redis@dbgate-plugin-redis
|
||||
PORT_redis=6379
|
||||
|
||||
LABEL_mysqlssh=MySql SSH
|
||||
SERVER_mysqlssh=localhost
|
||||
USER_mysqlssh=root
|
||||
PASSWORD_mysqlssh=xxx
|
||||
PORT_mysqlssh=3316
|
||||
ENGINE_mysqlssh=mysql@dbgate-plugin-mysql
|
||||
USE_SSH_mysqlssh=1
|
||||
SSH_HOST_mysqlssh=demo.dbgate.org
|
||||
SSH_PORT_mysqlssh=22
|
||||
SSH_MODE_mysqlssh=userPassword
|
||||
SSH_LOGIN_mysqlssh=root
|
||||
SSH_PASSWORD_mysqlssh=xxx
|
||||
LABEL_mssql=SQL Server
|
||||
SERVER_mssql=dbgatedckstage1.sprinx.cz
|
||||
USER_mssql=sa
|
||||
PASSWORD_mssql=Pwd2020Db
|
||||
PORT_mssql=1433
|
||||
ENGINE_mssql=mssql@dbgate-plugin-mssql
|
||||
|
||||
LABEL_sqlite=sqlite
|
||||
FILE_sqlite=/home/jena/.dbgate/files/sqlite/feeds.sqlite
|
||||
ENGINE_sqlite=sqlite@dbgate-plugin-sqlite
|
||||
|
||||
LABEL_relational=Relational dataset repo
|
||||
SERVER_relational=relational.fit.cvut.cz
|
||||
USER_relational=guest
|
||||
PASSWORD_relational=relational
|
||||
ENGINE_relational=mariadb@dbgate-plugin-mysql
|
||||
READONLY_relational=1
|
||||
LABEL_oracle=Oracle
|
||||
SERVER_oracle=dbgatedckstage1.sprinx.cz
|
||||
USER_oracle=system
|
||||
PASSWORD_oracle=Pwd2020Db
|
||||
PORT_oracle=1521
|
||||
ENGINE_oracle=oracle@dbgate-plugin-oracle
|
||||
SERVICE_NAME_oracle=xe
|
||||
|
||||
# SETTINGS_dataGrid.showHintColumns=1
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -12,6 +12,6 @@ DBCONFIG_mysql=[{"name":"Chinook","connectionColor":"cyan"}]
|
||||
|
||||
|
||||
SINGLE_CONNECTION=mysql
|
||||
SINGLE_DATABASE=Chinook
|
||||
# SINGLE_DATABASE=Chinook
|
||||
|
||||
PERMISSIONS=files/charts/read
|
||||
|
||||
+16
-14
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "dbgate-api",
|
||||
"main": "src/index.js",
|
||||
"version": "5.0.0-alpha.1",
|
||||
"version": "6.0.0-alpha.1",
|
||||
"homepage": "https://dbgate.org/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -16,20 +16,23 @@
|
||||
"export",
|
||||
"dbgate"
|
||||
],
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"dependencies": {
|
||||
"@aws-sdk/rds-signer": "^3.665.0",
|
||||
"activedirectory2": "^2.1.0",
|
||||
"async-lock": "^1.2.4",
|
||||
"async-lock": "^1.2.6",
|
||||
"axios": "^0.21.1",
|
||||
"body-parser": "^1.19.0",
|
||||
"bufferutil": "^4.0.1",
|
||||
"byline": "^5.0.0",
|
||||
"compare-versions": "^3.6.0",
|
||||
"cors": "^2.8.5",
|
||||
"cross-env": "^6.0.3",
|
||||
"dbgate-datalib": "^5.0.0-alpha.1",
|
||||
"dbgate-query-splitter": "^4.10.3",
|
||||
"dbgate-sqltree": "^5.0.0-alpha.1",
|
||||
"dbgate-tools": "^5.0.0-alpha.1",
|
||||
"dbgate-datalib": "^6.0.0-alpha.1",
|
||||
"dbgate-query-splitter": "^4.11.2",
|
||||
"dbgate-sqltree": "^6.0.0-alpha.1",
|
||||
"dbgate-tools": "^6.0.0-alpha.1",
|
||||
"debug": "^4.3.4",
|
||||
"diff": "^5.0.0",
|
||||
"diff2html": "^3.4.13",
|
||||
@@ -55,6 +58,7 @@
|
||||
"pinomin": "^1.0.4",
|
||||
"portfinder": "^1.0.28",
|
||||
"rimraf": "^3.0.0",
|
||||
"semver": "^7.6.3",
|
||||
"simple-encryptor": "^4.0.0",
|
||||
"ssh2": "^1.11.0",
|
||||
"stream-json": "^1.8.0",
|
||||
@@ -68,24 +72,22 @@
|
||||
"start:dblogin": "env-cmd -f env/dblogin/.env node src/index.js --listen-api",
|
||||
"start:filedb": "env-cmd node src/index.js /home/jena/test/chinook/Chinook.db --listen-api",
|
||||
"start:storage": "env-cmd -f env/storage/.env node src/index.js --listen-api",
|
||||
"start:storage:built": "env-cmd -f env/storage/.env cross-env DEVMODE= BUILTWEBMODE=1 node dist/bundle.js --listen-api",
|
||||
"start:singleconn": "env-cmd node src/index.js --server localhost --user root --port 3307 --engine mysql@dbgate-plugin-mysql --password test --listen-api",
|
||||
"ts": "tsc",
|
||||
"build": "webpack"
|
||||
"build": "webpack",
|
||||
"build:doc": "jsdoc2md --template doctpl.hbs ./src/shell/* > ../../../dbgate.github.io/_docs/apidoc.md"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^9.0.11",
|
||||
"@types/lodash": "^4.14.149",
|
||||
"dbgate-types": "^5.0.0-alpha.1",
|
||||
"dbgate-types": "^6.0.0-alpha.1",
|
||||
"env-cmd": "^10.1.0",
|
||||
"jsdoc-to-markdown": "^9.0.5",
|
||||
"node-loader": "^1.0.2",
|
||||
"nodemon": "^2.0.2",
|
||||
"typescript": "^4.4.3",
|
||||
"webpack": "^5.91.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"better-sqlite3": "9.6.0",
|
||||
"msnodesqlv8": "^4.2.1",
|
||||
"oracledb": "^6.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,9 +83,16 @@ class OAuthProvider extends AuthProviderBase {
|
||||
)}&client_id=${process.env.OAUTH_CLIENT_ID}&client_secret=${process.env.OAUTH_CLIENT_SECRET}${scopeParam}`
|
||||
);
|
||||
|
||||
const { access_token, refresh_token } = resp.data;
|
||||
const { access_token, refresh_token, id_token } = resp.data;
|
||||
|
||||
const payload = jwt.decode(access_token);
|
||||
let payload = jwt.decode(access_token);
|
||||
|
||||
// Fallback to id_token in case the access_token is not a JWT
|
||||
// https://www.oauth.com/oauth2-servers/access-tokens/
|
||||
// https://github.com/dbgate/dbgate/issues/727
|
||||
if (!payload && id_token) {
|
||||
payload = jwt.decode(id_token);
|
||||
}
|
||||
|
||||
logger.info({ payload }, 'User payload returned from OAUTH');
|
||||
|
||||
@@ -198,6 +205,19 @@ class LoginsProvider extends AuthProviderBase {
|
||||
amoid = 'logins';
|
||||
|
||||
async login(login, password, options = undefined) {
|
||||
if (login && password && process.env['LOGIN'] == login && process.env['PASSWORD'] == password) {
|
||||
return {
|
||||
accessToken: jwt.sign(
|
||||
{
|
||||
amoid: this.amoid,
|
||||
login,
|
||||
},
|
||||
getTokenSecret(),
|
||||
{ expiresIn: getTokenLifetime() }
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (password == process.env[`LOGIN_PASSWORD_${login}`]) {
|
||||
return {
|
||||
accessToken: jwt.sign(
|
||||
@@ -210,6 +230,7 @@ class LoginsProvider extends AuthProviderBase {
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return { error: 'Invalid credentials' };
|
||||
}
|
||||
|
||||
@@ -250,11 +271,10 @@ function hasEnvLogins() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function detectEnvAuthProvider() {
|
||||
function detectEnvAuthProviderCore() {
|
||||
if (process.env.AUTH_PROVIDER) {
|
||||
return process.env.AUTH_PROVIDER;
|
||||
}
|
||||
|
||||
if (process.env.STORAGE_DATABASE) {
|
||||
return 'denyall';
|
||||
}
|
||||
@@ -270,6 +290,14 @@ function detectEnvAuthProvider() {
|
||||
return 'none';
|
||||
}
|
||||
|
||||
function detectEnvAuthProvider() {
|
||||
const authProvider = detectEnvAuthProviderCore();
|
||||
if (process.env.BASIC_AUTH && authProvider != 'logins' && authProvider != 'ad') {
|
||||
throw new Error(`BASIC_AUTH is not supported with ${authProvider} auth provider`);
|
||||
}
|
||||
return authProvider;
|
||||
}
|
||||
|
||||
function createEnvAuthProvider() {
|
||||
const authProvider = detectEnvAuthProvider();
|
||||
switch (authProvider) {
|
||||
|
||||
@@ -6,7 +6,7 @@ const { archivedir, clearArchiveLinksCache, resolveArchiveFolder } = require('..
|
||||
const socket = require('../utility/socket');
|
||||
const loadFilesRecursive = require('../utility/loadFilesRecursive');
|
||||
const getJslFileName = require('../utility/getJslFileName');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
const dbgateApi = require('../shell');
|
||||
const jsldata = require('./jsldata');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
@@ -74,7 +74,7 @@ module.exports = {
|
||||
...fileType('.matview.sql', 'matview.sql'),
|
||||
];
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error reading archive files');
|
||||
logger.error(extractErrorLogData(err), 'Error reading archive files');
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const axios = require('axios');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const getExpressPath = require('../utility/getExpressPath');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
const AD = require('activedirectory2').promiseWrapper;
|
||||
const crypto = require('crypto');
|
||||
const { getTokenSecret, getTokenLifetime } = require('../auth/authCommon');
|
||||
@@ -22,7 +22,8 @@ function unauthorizedResponse(req, res, text) {
|
||||
// if (req.path == getExpressPath('/connections/list')) {
|
||||
// return res.json([]);
|
||||
// }
|
||||
return res.sendStatus(401).send(text);
|
||||
|
||||
return res.status(401).send(text);
|
||||
}
|
||||
|
||||
function authMiddleware(req, res, next) {
|
||||
@@ -35,8 +36,9 @@ function authMiddleware(req, res, next) {
|
||||
'/auth/login',
|
||||
'/auth/redirect',
|
||||
'/stream',
|
||||
'storage/get-connections-for-login-page',
|
||||
'auth/get-providers',
|
||||
'/storage/get-connections-for-login-page',
|
||||
'/storage/set-admin-password',
|
||||
'/auth/get-providers',
|
||||
'/connections/dblogin-web',
|
||||
'/connections/dblogin-app',
|
||||
'/connections/dblogin-auth',
|
||||
@@ -68,10 +70,11 @@ function authMiddleware(req, res, next) {
|
||||
return next();
|
||||
} catch (err) {
|
||||
if (skipAuth) {
|
||||
req.isInvalidToken = true;
|
||||
return next();
|
||||
}
|
||||
|
||||
logger.error({ err }, 'Sending invalid token error');
|
||||
logger.error(extractErrorLogData(err), 'Sending invalid token error');
|
||||
|
||||
return unauthorizedResponse(req, res, 'invalid token');
|
||||
}
|
||||
@@ -88,7 +91,12 @@ module.exports = {
|
||||
const { amoid, login, password, isAdminPage } = params;
|
||||
|
||||
if (isAdminPage) {
|
||||
if (process.env.ADMIN_PASSWORD && process.env.ADMIN_PASSWORD == password) {
|
||||
let adminPassword = process.env.ADMIN_PASSWORD;
|
||||
if (!adminPassword) {
|
||||
const adminConfig = await storage.readConfig({ group: 'admin' });
|
||||
adminPassword = adminConfig?.adminPassword;
|
||||
}
|
||||
if (adminPassword && adminPassword == password) {
|
||||
return {
|
||||
accessToken: jwt.sign(
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ const _ = require('lodash');
|
||||
const AsyncLock = require('async-lock');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const processArgs = require('../utility/processArgs');
|
||||
const currentVersion = require('../currentVersion');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
const connections = require('../controllers/connections');
|
||||
@@ -17,6 +18,7 @@ const { checkLicense, checkLicenseKey } = require('../utility/checkLicense');
|
||||
const storage = require('./storage');
|
||||
const { getAuthProxyUrl } = require('../utility/authProxy');
|
||||
const { getPublicHardwareFingerprint } = require('../utility/hardwareFingerprint');
|
||||
const { extractErrorMessage } = require('dbgate-tools');
|
||||
|
||||
const lock = new AsyncLock();
|
||||
|
||||
@@ -39,10 +41,12 @@ module.exports = {
|
||||
const isUserLoggedIn = authProvider.isUserLoggedIn(req);
|
||||
|
||||
const singleConid = authProvider.getSingleConnectionId(req);
|
||||
const storageConnectionError = storage.getStorageConnectionError();
|
||||
|
||||
const singleConnection = singleConid
|
||||
? await connections.getCore({ conid: singleConid })
|
||||
: connections.singleConnection;
|
||||
const singleConnection =
|
||||
singleConid && !storageConnectionError
|
||||
? await connections.getCore({ conid: singleConid })
|
||||
: connections.singleConnection;
|
||||
|
||||
let configurationError = null;
|
||||
if (process.env.STORAGE_DATABASE && process.env.BASIC_AUTH) {
|
||||
@@ -50,10 +54,25 @@ module.exports = {
|
||||
'Basic authentization is not allowed, when using storage. Cannot use both STORAGE_DATABASE and BASIC_AUTH';
|
||||
}
|
||||
|
||||
const checkedLicense = await checkLicense();
|
||||
const isLicenseValid = checkedLicense?.status == 'ok';
|
||||
if (storageConnectionError && !configurationError) {
|
||||
configurationError = extractErrorMessage(storageConnectionError);
|
||||
}
|
||||
|
||||
return {
|
||||
const checkedLicense = storageConnectionError ? null : await checkLicense();
|
||||
const isLicenseValid = checkedLicense?.status == 'ok';
|
||||
const logoutUrl = storageConnectionError ? null : await authProvider.getLogoutUrl();
|
||||
const adminConfig = storageConnectionError ? null : await storage.readConfig({ group: 'admin' });
|
||||
|
||||
storage.startRefreshLicense();
|
||||
|
||||
const isAdminPasswordMissing = !!(
|
||||
process.env.STORAGE_DATABASE &&
|
||||
!process.env.ADMIN_PASSWORD &&
|
||||
!process.env.BASIC_AUTH &&
|
||||
!adminConfig?.adminPasswordState
|
||||
);
|
||||
|
||||
const configResult = {
|
||||
runAsPortal: !!connections.portalConnections,
|
||||
singleDbConnection: connections.singleDbConnection,
|
||||
singleConnection: singleConnection,
|
||||
@@ -64,24 +83,34 @@ module.exports = {
|
||||
isDocker: platformInfo.isDocker,
|
||||
isElectron: platformInfo.isElectron,
|
||||
isLicenseValid,
|
||||
isLicenseExpired: checkedLicense?.isExpired,
|
||||
trialDaysLeft:
|
||||
checkedLicense?.licenseTypeObj?.isTrial && !checkedLicense?.isExpired ? checkedLicense?.daysLeft : null,
|
||||
checkedLicense,
|
||||
configurationError,
|
||||
logoutUrl: await authProvider.getLogoutUrl(),
|
||||
logoutUrl,
|
||||
permissions,
|
||||
login,
|
||||
// ...additionalConfigProps,
|
||||
isBasicAuth: !!process.env.BASIC_AUTH,
|
||||
isAdminLoginForm: !!(
|
||||
process.env.STORAGE_DATABASE &&
|
||||
process.env.ADMIN_PASSWORD &&
|
||||
!process.env.BASIC_AUTH &&
|
||||
checkedLicense?.type == 'premium'
|
||||
(process.env.ADMIN_PASSWORD || adminConfig?.adminPasswordState == 'set') &&
|
||||
!process.env.BASIC_AUTH
|
||||
),
|
||||
isAdminPasswordMissing,
|
||||
isInvalidToken: req?.isInvalidToken,
|
||||
adminPasswordState: adminConfig?.adminPasswordState,
|
||||
storageDatabase: process.env.STORAGE_DATABASE,
|
||||
logsFilePath: getLogsFilePath(),
|
||||
connectionsFilePath: path.join(datadir(), 'connections.jsonl'),
|
||||
connectionsFilePath: path.join(
|
||||
datadir(),
|
||||
processArgs.runE2eTests ? 'connections-e2etests.jsonl' : 'connections.jsonl'
|
||||
),
|
||||
...currentVersion,
|
||||
};
|
||||
|
||||
return configResult;
|
||||
},
|
||||
|
||||
logout_meta: {
|
||||
|
||||
@@ -12,7 +12,7 @@ const { pickSafeConnectionInfo } = require('../utility/crypting');
|
||||
const JsonLinesDatabase = require('../utility/JsonLinesDatabase');
|
||||
|
||||
const processArgs = require('../utility/processArgs');
|
||||
const { safeJsonParse, getLogger } = require('dbgate-tools');
|
||||
const { safeJsonParse, getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
const { connectionHasPermission, testConnectionPermission } = require('../utility/hasPermission');
|
||||
const pipeForkLogs = require('../utility/pipeForkLogs');
|
||||
@@ -199,8 +199,11 @@ module.exports = {
|
||||
const dir = datadir();
|
||||
if (!portalConnections) {
|
||||
// @ts-ignore
|
||||
this.datastore = new JsonLinesDatabase(path.join(dir, 'connections.jsonl'));
|
||||
this.datastore = new JsonLinesDatabase(
|
||||
path.join(dir, processArgs.runE2eTests ? 'connections-e2etests.jsonl' : 'connections.jsonl')
|
||||
);
|
||||
}
|
||||
await this.checkUnsavedConnectionsLimit();
|
||||
},
|
||||
|
||||
list_meta: true,
|
||||
@@ -219,7 +222,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
test_meta: true,
|
||||
test(connection) {
|
||||
test({ connection, requestDbList }) {
|
||||
const subprocess = fork(
|
||||
global['API_PACKAGE'] || process.argv[1],
|
||||
[
|
||||
@@ -234,7 +237,7 @@ module.exports = {
|
||||
}
|
||||
);
|
||||
pipeForkLogs(subprocess);
|
||||
subprocess.send(connection);
|
||||
subprocess.send({ connection, requestDbList });
|
||||
return new Promise(resolve => {
|
||||
subprocess.on('message', resp => {
|
||||
if (handleProcessCommunication(resp, subprocess)) return;
|
||||
@@ -300,6 +303,32 @@ module.exports = {
|
||||
return res;
|
||||
},
|
||||
|
||||
async checkUnsavedConnectionsLimit() {
|
||||
if (!this.datastore) {
|
||||
return;
|
||||
}
|
||||
const MAX_UNSAVED_CONNECTIONS = 5;
|
||||
await this.datastore.transformAll(connections => {
|
||||
const count = connections.filter(x => x.unsaved).length;
|
||||
if (count > MAX_UNSAVED_CONNECTIONS) {
|
||||
const res = [];
|
||||
let unsavedToSkip = count - MAX_UNSAVED_CONNECTIONS;
|
||||
for (const item of connections) {
|
||||
if (item.unsaved) {
|
||||
if (unsavedToSkip > 0) {
|
||||
unsavedToSkip--;
|
||||
} else {
|
||||
res.push(item);
|
||||
}
|
||||
} else {
|
||||
res.push(item);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
update_meta: true,
|
||||
async update({ _id, values }, req) {
|
||||
if (portalConnections) return;
|
||||
@@ -368,6 +397,11 @@ module.exports = {
|
||||
|
||||
get_meta: true,
|
||||
async get({ conid }, req) {
|
||||
if (conid == '__model') {
|
||||
return {
|
||||
_id: '__model',
|
||||
};
|
||||
}
|
||||
testConnectionPermission(conid, req);
|
||||
return this.getCore({ conid, mask: true });
|
||||
},
|
||||
@@ -430,7 +464,7 @@ module.exports = {
|
||||
socket.emit('got-volatile-token', { strmid, savedConId: conid, volatileConId: volatile._id });
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error getting DB token');
|
||||
logger.error(extractErrorLogData(err), 'Error getting DB token');
|
||||
return { error: err.message };
|
||||
}
|
||||
},
|
||||
@@ -446,7 +480,7 @@ module.exports = {
|
||||
const resp = await authProvider.login(null, null, { conid: volatile._id });
|
||||
return resp;
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error getting DB token');
|
||||
logger.error(extractErrorLogData(err), 'Error getting DB token');
|
||||
return { error: err.message };
|
||||
}
|
||||
},
|
||||
@@ -478,4 +512,10 @@ module.exports = {
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
reloadConnectionList_meta: true,
|
||||
async reloadConnectionList() {
|
||||
if (portalConnections) return;
|
||||
await this.datastore.unload();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -12,6 +12,8 @@ const {
|
||||
extendDatabaseInfo,
|
||||
modelCompareDbDiffOptions,
|
||||
getLogger,
|
||||
extractErrorLogData,
|
||||
filterStructureBySchema,
|
||||
} = require('dbgate-tools');
|
||||
const { html, parse } = require('diff2html');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
@@ -30,6 +32,8 @@ const { testConnectionPermission } = require('../utility/hasPermission');
|
||||
const { MissingCredentialsError } = require('../utility/exceptions');
|
||||
const pipeForkLogs = require('../utility/pipeForkLogs');
|
||||
const crypto = require('crypto');
|
||||
const loadModelTransform = require('../utility/loadModelTransform');
|
||||
const exportDbModelSql = require('../utility/exportDbModelSql');
|
||||
|
||||
const logger = getLogger('databaseConnections');
|
||||
|
||||
@@ -146,7 +150,7 @@ module.exports = {
|
||||
try {
|
||||
conn.subprocess.send({ msgid, ...message });
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error sending request do process');
|
||||
logger.error(extractErrorLogData(err), 'Error sending request do process');
|
||||
this.close(conn.conid, conn.database);
|
||||
}
|
||||
});
|
||||
@@ -318,7 +322,7 @@ module.exports = {
|
||||
try {
|
||||
existing.subprocess.send({ msgtype: 'ping' });
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error pinging DB connection');
|
||||
logger.error(extractErrorLogData(err), 'Error pinging DB connection');
|
||||
this.close(conid, database);
|
||||
|
||||
return {
|
||||
@@ -348,6 +352,11 @@ module.exports = {
|
||||
|
||||
syncModel_meta: true,
|
||||
async syncModel({ conid, database, isFullRefresh }, req) {
|
||||
if (conid == '__model') {
|
||||
socket.emitChanged('database-structure-changed', { conid, database });
|
||||
return { status: 'ok' };
|
||||
}
|
||||
|
||||
testConnectionPermission(conid, req);
|
||||
const conn = await this.ensureOpened(conid, database);
|
||||
conn.subprocess.send({ msgtype: 'syncModel', isFullRefresh });
|
||||
@@ -362,7 +371,7 @@ module.exports = {
|
||||
try {
|
||||
existing.subprocess.kill();
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error killing subprocess');
|
||||
logger.error(extractErrorLogData(err), 'Error killing subprocess');
|
||||
}
|
||||
}
|
||||
this.opened = this.opened.filter(x => x.conid != conid || x.database != database);
|
||||
@@ -391,11 +400,12 @@ module.exports = {
|
||||
},
|
||||
|
||||
structure_meta: true,
|
||||
async structure({ conid, database }, req) {
|
||||
async structure({ conid, database, modelTransFile = null }, req) {
|
||||
testConnectionPermission(conid, req);
|
||||
if (conid == '__model') {
|
||||
const model = await importDbModel(database);
|
||||
return model;
|
||||
const trans = await loadModelTransform(modelTransFile);
|
||||
return trans ? trans(model) : model;
|
||||
}
|
||||
|
||||
const opened = await this.ensureOpened(conid, database);
|
||||
@@ -431,14 +441,35 @@ module.exports = {
|
||||
},
|
||||
|
||||
exportModel_meta: true,
|
||||
async exportModel({ conid, database }, req) {
|
||||
async exportModel({ conid, database, outputFolder, schema }, req) {
|
||||
testConnectionPermission(conid, req);
|
||||
const archiveFolder = await archive.getNewArchiveFolder({ database });
|
||||
await fs.mkdir(path.join(archivedir(), archiveFolder));
|
||||
|
||||
const realFolder = outputFolder.startsWith('archive:')
|
||||
? resolveArchiveFolder(outputFolder.substring('archive:'.length))
|
||||
: outputFolder;
|
||||
|
||||
const model = await this.structure({ conid, database });
|
||||
await exportDbModel(model, path.join(archivedir(), archiveFolder));
|
||||
socket.emitChanged(`archive-folders-changed`);
|
||||
return { archiveFolder };
|
||||
const filteredModel = schema ? filterStructureBySchema(model, schema) : model;
|
||||
await exportDbModel(extendDatabaseInfo(filteredModel), realFolder);
|
||||
|
||||
if (outputFolder.startsWith('archive:')) {
|
||||
socket.emitChanged(`archive-files-changed`, { folder: outputFolder.substring('archive:'.length) });
|
||||
}
|
||||
return { status: 'ok' };
|
||||
},
|
||||
|
||||
exportModelSql_meta: true,
|
||||
async exportModelSql({ conid, database, outputFolder, outputFile, schema }, req) {
|
||||
testConnectionPermission(conid, req);
|
||||
|
||||
const connection = await connections.getCore({ conid });
|
||||
const driver = requireEngineDriver(connection);
|
||||
|
||||
const model = await this.structure({ conid, database });
|
||||
const filteredModel = schema ? filterStructureBySchema(model, schema) : model;
|
||||
await exportDbModelSql(extendDatabaseInfo(filteredModel), driver, outputFolder, outputFile);
|
||||
|
||||
return { status: 'ok' };
|
||||
},
|
||||
|
||||
generateDeploySql_meta: true,
|
||||
|
||||
@@ -94,7 +94,7 @@ module.exports = {
|
||||
if (!manifest.keywords) {
|
||||
continue;
|
||||
}
|
||||
if (!manifest.keywords.includes('dbgateplugin')) {
|
||||
if (!manifest.keywords.includes('dbgateplugin') && !manifest.keywords.includes('dbgatebuiltin')) {
|
||||
continue;
|
||||
}
|
||||
const readmeFile = path.join(isPackaged ? packagedPluginsDir() : pluginsdir(), packageName, 'README.md');
|
||||
|
||||
@@ -12,6 +12,7 @@ const {
|
||||
jsonScriptToJavascript,
|
||||
getLogger,
|
||||
safeJsonParse,
|
||||
pinoLogRecordToMessageRecord,
|
||||
} = require('dbgate-tools');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const processArgs = require('../utility/processArgs');
|
||||
@@ -68,18 +69,20 @@ module.exports = {
|
||||
|
||||
dispatchMessage(runid, message) {
|
||||
if (message) {
|
||||
const json = safeJsonParse(message.message);
|
||||
if (_.isPlainObject(message)) logger.log(message);
|
||||
else logger.info(message);
|
||||
|
||||
if (json) logger.log(json);
|
||||
else logger.info(message.message);
|
||||
const toEmit = _.isPlainObject(message)
|
||||
? {
|
||||
time: new Date(),
|
||||
...message,
|
||||
}
|
||||
: {
|
||||
message,
|
||||
time: new Date(),
|
||||
};
|
||||
|
||||
const toEmit = {
|
||||
time: new Date(),
|
||||
...message,
|
||||
message: json ? json.msg : message.message,
|
||||
};
|
||||
|
||||
if (json && json.level >= 50) {
|
||||
if (toEmit.level >= 50) {
|
||||
toEmit.severity = 'error';
|
||||
}
|
||||
|
||||
@@ -108,7 +111,7 @@ module.exports = {
|
||||
const scriptFile = path.join(uploadsdir(), runid + '.js');
|
||||
fs.writeFileSync(`${scriptFile}`, scriptText);
|
||||
fs.mkdirSync(directory);
|
||||
const pluginNames = _.union(fs.readdirSync(pluginsdir()), packagedPluginList);
|
||||
const pluginNames = extractPlugins(scriptText);
|
||||
logger.info({ scriptFile }, 'Running script');
|
||||
// const subprocess = fork(scriptFile, ['--checkParent', '--max-old-space-size=8192'], {
|
||||
const subprocess = fork(
|
||||
@@ -131,7 +134,16 @@ module.exports = {
|
||||
}
|
||||
);
|
||||
const pipeDispatcher = severity => data => {
|
||||
return this.dispatchMessage(runid, { severity, message: data.toString().trim() });
|
||||
const json = safeJsonParse(data, null);
|
||||
|
||||
if (json) {
|
||||
return this.dispatchMessage(runid, pinoLogRecordToMessageRecord(json));
|
||||
} else {
|
||||
return this.dispatchMessage(runid, {
|
||||
message: json == null ? data.toString().trim() : null,
|
||||
severity,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
byline(subprocess.stdout).on('data', pipeDispatcher('info'));
|
||||
@@ -165,7 +177,7 @@ module.exports = {
|
||||
|
||||
start_meta: true,
|
||||
async start({ script }) {
|
||||
const runid = crypto.randomUUID()
|
||||
const runid = crypto.randomUUID();
|
||||
|
||||
if (script.type == 'json') {
|
||||
const js = jsonScriptToJavascript(script);
|
||||
|
||||
@@ -11,7 +11,7 @@ const processArgs = require('../utility/processArgs');
|
||||
const { testConnectionPermission } = require('../utility/hasPermission');
|
||||
const { MissingCredentialsError } = require('../utility/exceptions');
|
||||
const pipeForkLogs = require('../utility/pipeForkLogs');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
|
||||
const logger = getLogger('serverConnection');
|
||||
|
||||
@@ -52,7 +52,7 @@ module.exports = {
|
||||
if (existing) return existing;
|
||||
const connection = await connections.getCore({ conid });
|
||||
if (!connection) {
|
||||
throw new Error(`Connection with conid="${conid}" not fund`);
|
||||
throw new Error(`Connection with conid="${conid}" not found`);
|
||||
}
|
||||
if (connection.passwordMode == 'askPassword' || connection.passwordMode == 'askUser') {
|
||||
throw new MissingCredentialsError({ conid, passwordMode: connection.passwordMode });
|
||||
@@ -112,7 +112,7 @@ module.exports = {
|
||||
try {
|
||||
existing.subprocess.kill();
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error killing subprocess');
|
||||
logger.error(extractErrorLogData(err), 'Error killing subprocess');
|
||||
}
|
||||
}
|
||||
this.opened = this.opened.filter(x => x.conid != conid);
|
||||
@@ -134,6 +134,7 @@ module.exports = {
|
||||
listDatabases_meta: true,
|
||||
async listDatabases({ conid }, req) {
|
||||
if (!conid) return [];
|
||||
if (conid == '__model') return [];
|
||||
testConnectionPermission(conid, req);
|
||||
const opened = await this.ensureOpened(conid);
|
||||
return opened.databases;
|
||||
@@ -167,12 +168,12 @@ module.exports = {
|
||||
try {
|
||||
opened.subprocess.send({ msgtype: 'ping' });
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error pinging server connection');
|
||||
logger.error(extractErrorLogData(err), 'Error pinging server connection');
|
||||
this.close(conid);
|
||||
}
|
||||
})
|
||||
);
|
||||
socket.setStreamIdFilter(strmid, { conid: conidArray });
|
||||
socket.setStreamIdFilter(strmid, { conid: [...(conidArray ?? []), '__model'] });
|
||||
return { status: 'ok' };
|
||||
},
|
||||
|
||||
@@ -217,7 +218,7 @@ module.exports = {
|
||||
try {
|
||||
conn.subprocess.send({ msgid, ...message });
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error sending request');
|
||||
logger.error(extractErrorLogData(err), 'Error sending request');
|
||||
this.close(conn.conid);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ const path = require('path');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const processArgs = require('../utility/processArgs');
|
||||
const { appdir } = require('../utility/directories');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
const pipeForkLogs = require('../utility/pipeForkLogs');
|
||||
const config = require('./config');
|
||||
|
||||
@@ -222,7 +222,7 @@ module.exports = {
|
||||
try {
|
||||
session.subprocess.send({ msgtype: 'ping' });
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error pinging session');
|
||||
logger.error(extractErrorLogData(err), 'Error pinging session');
|
||||
|
||||
return {
|
||||
status: 'error',
|
||||
|
||||
@@ -17,4 +17,15 @@ module.exports = {
|
||||
async getConnectionsForLoginPage() {
|
||||
return null;
|
||||
},
|
||||
|
||||
getStorageConnectionError() {
|
||||
return null;
|
||||
},
|
||||
|
||||
readConfig_meta: true,
|
||||
async readConfig({ group }) {
|
||||
return {};
|
||||
},
|
||||
|
||||
startRefreshLicense() {},
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const crypto = require('crypto');
|
||||
const path = require('path');
|
||||
const { uploadsdir, getLogsFilePath } = require('../utility/directories');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
const logger = getLogger('uploads');
|
||||
const axios = require('axios');
|
||||
const os = require('os');
|
||||
@@ -110,7 +110,7 @@ module.exports = {
|
||||
|
||||
return response.data;
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error uploading gist');
|
||||
logger.error(extractErrorLogData(err), 'Error uploading gist');
|
||||
|
||||
return {
|
||||
apiErrorMessage: err.message,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
module.exports = {
|
||||
version: '5.0.0-alpha.1',
|
||||
buildTime: '2021-04-17T07:22:49.702Z'
|
||||
version: '6.0.0-alpha.1',
|
||||
buildTime: '2024-12-01T00:00:00Z'
|
||||
};
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
const { setLogConfig, getLogger, setLoggerName } = require('dbgate-tools');
|
||||
const { setLogConfig, getLogger, setLoggerName, extractErrorLogData } = require('dbgate-tools');
|
||||
const processArgs = require('./utility/processArgs');
|
||||
const fs = require('fs');
|
||||
const moment = require('moment');
|
||||
const path = require('path');
|
||||
const { logsdir, setLogsFilePath, getLogsFilePath } = require('./utility/directories');
|
||||
const { createLogger } = require('pinomin');
|
||||
const currentVersion = require('./currentVersion');
|
||||
|
||||
const logger = getLogger('apiIndex');
|
||||
|
||||
process.on('uncaughtException', err => {
|
||||
logger.fatal(extractErrorLogData(err), 'Uncaught exception, exiting process');
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
if (processArgs.startProcess) {
|
||||
setLoggerName(processArgs.startProcess.replace(/Process$/, ''));
|
||||
@@ -94,10 +101,17 @@ function configureLogger() {
|
||||
|
||||
if (processArgs.listenApi) {
|
||||
configureLogger();
|
||||
logger.info(`Starting API process version ${currentVersion.version}`);
|
||||
|
||||
if (process.env.DEBUG_PRINT_ENV_VARIABLES) {
|
||||
logger.info('Debug print environment variables:');
|
||||
for (const key of Object.keys(process.env)) {
|
||||
logger.info(` ${key}: ${JSON.stringify(process.env[key])}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const shell = require('./shell/index');
|
||||
const currentVersion = require('./currentVersion');
|
||||
|
||||
global.DBGATE_PACKAGES = {
|
||||
'dbgate-tools': require('dbgate-tools'),
|
||||
|
||||
@@ -28,6 +28,7 @@ const files = require('./controllers/files');
|
||||
const scheduler = require('./controllers/scheduler');
|
||||
const queryHistory = require('./controllers/queryHistory');
|
||||
const onFinished = require('on-finished');
|
||||
const processArgs = require('./utility/processArgs');
|
||||
|
||||
const { rundir } = require('./utility/directories');
|
||||
const platformInfo = require('./utility/platformInfo');
|
||||
@@ -35,6 +36,8 @@ const getExpressPath = require('./utility/getExpressPath');
|
||||
const _ = require('lodash');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const { getDefaultAuthProvider } = require('./auth/authProvider');
|
||||
const startCloudUpgradeTimer = require('./utility/cloudUpgrade');
|
||||
const { isProApp } = require('./utility/checkLicense');
|
||||
|
||||
const logger = getLogger('main');
|
||||
|
||||
@@ -73,8 +76,15 @@ function start() {
|
||||
if (platformInfo.isDocker) {
|
||||
// server static files inside docker container
|
||||
app.use(getExpressPath('/'), express.static('/home/dbgate-docker/public'));
|
||||
} else if (platformInfo.isAwsUbuntuLayout) {
|
||||
app.use(getExpressPath('/'), express.static('/home/ubuntu/build/public'));
|
||||
} else if (processArgs.runE2eTests) {
|
||||
app.use(getExpressPath('/'), express.static(path.resolve('packer/build/public')));
|
||||
} else if (platformInfo.isNpmDist) {
|
||||
app.use(getExpressPath('/'), express.static(path.join(__dirname, '../../dbgate-web/public')));
|
||||
app.use(
|
||||
getExpressPath('/'),
|
||||
express.static(path.join(__dirname, isProApp() ? '../../dbgate-web-premium/public' : '../../dbgate-web/public'))
|
||||
);
|
||||
} else if (process.env.DEVWEB) {
|
||||
// console.log('__dirname', __dirname);
|
||||
// console.log(path.join(__dirname, '../../web/public/build'));
|
||||
@@ -126,6 +136,10 @@ function start() {
|
||||
const port = process.env.PORT || 3000;
|
||||
logger.info(`DbGate API listening on port ${port} (docker build)`);
|
||||
server.listen(port);
|
||||
} else if (platformInfo.isAwsUbuntuLayout) {
|
||||
const port = process.env.PORT || 3000;
|
||||
logger.info(`DbGate API listening on port ${port} (AWS AMI build)`);
|
||||
server.listen(port);
|
||||
} else if (platformInfo.isNpmDist) {
|
||||
getPort({
|
||||
port: parseInt(
|
||||
@@ -162,6 +176,10 @@ function start() {
|
||||
process.on('SIGINT', shutdown);
|
||||
process.on('SIGTERM', shutdown);
|
||||
process.on('SIGBREAK', shutdown);
|
||||
|
||||
if (process.env.CLOUD_UPGRADE_FILE) {
|
||||
startCloudUpgradeTimer();
|
||||
}
|
||||
}
|
||||
|
||||
function useAllControllers(app, electron) {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
const argIndex = process.argv.indexOf('--native-modules');
|
||||
const redirectFile = global['NATIVE_MODULES'] || (argIndex > 0 ? process.argv[argIndex + 1] : null);
|
||||
|
||||
function requireDynamic(file) {
|
||||
try {
|
||||
// @ts-ignore
|
||||
return __non_webpack_require__(redirectFile);
|
||||
} catch (err) {
|
||||
return require(redirectFile);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = redirectFile ? requireDynamic(redirectFile) : require('./nativeModulesContent');
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
// this file is generated automatically by script fillNativeModules.js, do not edit it manually
|
||||
const content = {};
|
||||
|
||||
content['better-sqlite3'] = () => require('better-sqlite3');
|
||||
content['oracledb'] = () => require('oracledb');
|
||||
|
||||
|
||||
module.exports = content;
|
||||
@@ -16,13 +16,20 @@ Platform: ${process.platform}
|
||||
|
||||
function start() {
|
||||
childProcessChecker();
|
||||
process.on('message', async connection => {
|
||||
process.on('message', async args => {
|
||||
// @ts-ignore
|
||||
const { connection, requestDbList } = args;
|
||||
if (handleProcessCommunication(connection)) return;
|
||||
try {
|
||||
const driver = requireEngineDriver(connection);
|
||||
const conn = await connectUtility(driver, connection, 'app');
|
||||
const res = await driver.getVersion(conn);
|
||||
process.send({ msgtype: 'connected', ...res });
|
||||
const dbhan = await connectUtility(driver, connection, 'app');
|
||||
const res = await driver.getVersion(dbhan);
|
||||
let databases = undefined;
|
||||
if (requestDbList) {
|
||||
databases = await driver.listDatabases(dbhan);
|
||||
}
|
||||
process.send({ msgtype: 'connected', ...res, databases });
|
||||
await driver.close(dbhan);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.send({
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
const stableStringify = require('json-stable-stringify');
|
||||
const { splitQuery } = require('dbgate-query-splitter');
|
||||
const childProcessChecker = require('../utility/childProcessChecker');
|
||||
const { extractBoolSettingsValue, extractIntSettingsValue, getLogger } = require('dbgate-tools');
|
||||
const {
|
||||
extractBoolSettingsValue,
|
||||
extractIntSettingsValue,
|
||||
getLogger,
|
||||
isCompositeDbName,
|
||||
dbNameLogCategory,
|
||||
extractErrorMessage,
|
||||
extractErrorLogData,
|
||||
} = require('dbgate-tools');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
@@ -35,7 +43,7 @@ async function checkedAsyncCall(promise) {
|
||||
} catch (err) {
|
||||
setStatus({
|
||||
name: 'error',
|
||||
message: err.message,
|
||||
message: extractErrorMessage(err, 'Checked call error'),
|
||||
});
|
||||
// console.error(err);
|
||||
setTimeout(() => process.exit(1), 1000);
|
||||
@@ -46,6 +54,12 @@ async function checkedAsyncCall(promise) {
|
||||
let loadingModel = false;
|
||||
|
||||
async function handleFullRefresh() {
|
||||
if (storedConnection.useSeparateSchemas && !isCompositeDbName(dbhan?.database)) {
|
||||
resolveAnalysedPromises();
|
||||
// skip loading DB structure
|
||||
return;
|
||||
}
|
||||
|
||||
loadingModel = true;
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
setStatusName('loadStructure');
|
||||
@@ -60,6 +74,11 @@ async function handleFullRefresh() {
|
||||
}
|
||||
|
||||
async function handleIncrementalRefresh(forceSend) {
|
||||
if (storedConnection.useSeparateSchemas && !isCompositeDbName(dbhan?.database)) {
|
||||
resolveAnalysedPromises();
|
||||
// skip loading DB structure
|
||||
return;
|
||||
}
|
||||
loadingModel = true;
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
setStatusName('checkStructure');
|
||||
@@ -102,6 +121,7 @@ function setStatusName(name) {
|
||||
async function readVersion() {
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
const version = await driver.getVersion(dbhan);
|
||||
logger.debug(`Got server version: ${version.version}`);
|
||||
process.send({ msgtype: 'version', version });
|
||||
serverVersion = version;
|
||||
}
|
||||
@@ -113,6 +133,11 @@ async function handleConnect({ connection, structure, globalSettings }) {
|
||||
if (!structure) setStatusName('pending');
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
dbhan = await checkedAsyncCall(connectUtility(driver, storedConnection, 'app'));
|
||||
logger.debug(
|
||||
`Connected to database, driver: ${storedConnection.engine}, separate schemas: ${
|
||||
storedConnection.useSeparateSchemas ? 'YES' : 'NO'
|
||||
}, 'DB: ${dbNameLogCategory(dbhan.database)} }`
|
||||
);
|
||||
dbhan.feedback = feedback => setStatus({ feedback });
|
||||
await checkedAsyncCall(readVersion());
|
||||
if (structure) {
|
||||
@@ -164,7 +189,11 @@ async function handleRunScript({ msgid, sql, useTransaction }, skipReadonlyCheck
|
||||
await driver.script(dbhan, sql, { useTransaction });
|
||||
process.send({ msgtype: 'response', msgid });
|
||||
} catch (err) {
|
||||
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
||||
process.send({
|
||||
msgtype: 'response',
|
||||
msgid,
|
||||
errorMessage: extractErrorMessage(err, 'Error executing SQL script'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +205,11 @@ async function handleRunOperation({ msgid, operation, useTransaction }, skipRead
|
||||
await driver.operation(dbhan, operation, { useTransaction });
|
||||
process.send({ msgtype: 'response', msgid });
|
||||
} catch (err) {
|
||||
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
||||
process.send({
|
||||
msgtype: 'response',
|
||||
msgid,
|
||||
errorMessage: extractErrorMessage(err, 'Error executing DB operation'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +222,11 @@ async function handleQueryData({ msgid, sql }, skipReadonlyCheck = false) {
|
||||
const res = await driver.query(dbhan, sql);
|
||||
process.send({ msgtype: 'response', msgid, ...res });
|
||||
} catch (err) {
|
||||
process.send({ msgtype: 'response', msgid, errorMessage: err.message || 'Error executing SQL script' });
|
||||
process.send({
|
||||
msgtype: 'response',
|
||||
msgid,
|
||||
errorMessage: extractErrorMessage(err, 'Error executing SQL script'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,8 +244,8 @@ async function handleDriverDataCore(msgid, callMethod, { logName }) {
|
||||
const result = await callMethod(driver);
|
||||
process.send({ msgtype: 'response', msgid, result });
|
||||
} catch (err) {
|
||||
logger.error(err, `Error when handling message ${logName}`);
|
||||
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
||||
logger.error(extractErrorLogData(err, { logName }), `Error when handling message ${logName}`);
|
||||
process.send({ msgtype: 'response', msgid, errorMessage: extractErrorMessage(err, 'Error executing DB data') });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +314,7 @@ async function handleUpdateCollection({ msgid, changeSet }) {
|
||||
const result = await driver.updateCollection(dbhan, changeSet);
|
||||
process.send({ msgtype: 'response', msgid, result });
|
||||
} catch (err) {
|
||||
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
||||
process.send({ msgtype: 'response', msgid, errorMessage: extractErrorMessage(err, 'Error updating collection') });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,13 +329,19 @@ async function handleSqlPreview({ msgid, objects, options }) {
|
||||
await generator.dump();
|
||||
process.send({ msgtype: 'response', msgid, sql: dmp.s, isTruncated: generator.isTruncated });
|
||||
if (generator.isUnhandledException) {
|
||||
setTimeout(() => {
|
||||
setTimeout(async () => {
|
||||
logger.error('Exiting because of unhandled exception');
|
||||
await driver.close(dbhan);
|
||||
process.exit(0);
|
||||
}, 500);
|
||||
}
|
||||
} catch (err) {
|
||||
process.send({ msgtype: 'response', msgid, isError: true, errorMessage: err.message });
|
||||
process.send({
|
||||
msgtype: 'response',
|
||||
msgid,
|
||||
isError: true,
|
||||
errorMessage: extractErrorMessage(err, 'Error generating SQL preview'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +357,12 @@ async function handleGenerateDeploySql({ msgid, modelFolder }) {
|
||||
});
|
||||
process.send({ ...res, msgtype: 'response', msgid });
|
||||
} catch (err) {
|
||||
process.send({ msgtype: 'response', msgid, isError: true, errorMessage: err.message });
|
||||
process.send({
|
||||
msgtype: 'response',
|
||||
msgid,
|
||||
isError: true,
|
||||
errorMessage: extractErrorMessage(err, 'Error generating deploy SQL'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,10 +407,12 @@ async function handleMessage({ msgtype, ...other }) {
|
||||
function start() {
|
||||
childProcessChecker();
|
||||
|
||||
setInterval(() => {
|
||||
setInterval(async () => {
|
||||
const time = new Date().getTime();
|
||||
if (time - lastPing > 40 * 1000) {
|
||||
logger.info('Database connection not alive, exiting');
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
await driver.close(dbhan);
|
||||
process.exit(0);
|
||||
}
|
||||
}, 10 * 1000);
|
||||
@@ -372,8 +422,8 @@ function start() {
|
||||
try {
|
||||
await handleMessage(message);
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error in DB connection');
|
||||
process.send({ msgtype: 'error', error: err.message });
|
||||
logger.error(extractErrorLogData(err), 'Error in DB connection');
|
||||
process.send({ msgtype: 'error', error: extractErrorMessage(err, 'Error processing message') });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const stableStringify = require('json-stable-stringify');
|
||||
const { extractBoolSettingsValue, extractIntSettingsValue, getLogger } = require('dbgate-tools');
|
||||
const { extractBoolSettingsValue, extractIntSettingsValue, getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
const childProcessChecker = require('../utility/childProcessChecker');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const logger = getLogger('srvconnProcess');
|
||||
|
||||
let systemConnection;
|
||||
let dbhan;
|
||||
let storedConnection;
|
||||
let lastDatabases = null;
|
||||
let lastStatus = null;
|
||||
@@ -16,7 +16,7 @@ let afterConnectCallbacks = [];
|
||||
async function handleRefresh() {
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
try {
|
||||
let databases = await driver.listDatabases(systemConnection);
|
||||
let databases = await driver.listDatabases(dbhan);
|
||||
if (storedConnection?.allowedDatabases?.trim()) {
|
||||
const allowedDatabaseList = storedConnection.allowedDatabases
|
||||
.split('\n')
|
||||
@@ -39,14 +39,14 @@ async function handleRefresh() {
|
||||
name: 'error',
|
||||
message: err.message,
|
||||
});
|
||||
// console.error(err);
|
||||
logger.error(extractErrorLogData(err), 'Error refreshing server databases');
|
||||
setTimeout(() => process.exit(1), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
async function readVersion() {
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
const version = await driver.getVersion(systemConnection);
|
||||
const version = await driver.getVersion(dbhan);
|
||||
process.send({ msgtype: 'version', version });
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ async function handleConnect(connection) {
|
||||
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
try {
|
||||
systemConnection = await connectUtility(driver, storedConnection, 'app');
|
||||
dbhan = await connectUtility(driver, storedConnection, 'app');
|
||||
readVersion();
|
||||
handleRefresh();
|
||||
if (extractBoolSettingsValue(globalSettings, 'connection.autoRefresh', false)) {
|
||||
@@ -84,7 +84,7 @@ async function handleConnect(connection) {
|
||||
name: 'error',
|
||||
message: err.message,
|
||||
});
|
||||
// console.error(err);
|
||||
logger.error(extractErrorLogData(err), 'Error connecting to server');
|
||||
setTimeout(() => process.exit(1), 1000);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ async function handleConnect(connection) {
|
||||
}
|
||||
|
||||
function waitConnected() {
|
||||
if (systemConnection) return Promise.resolve();
|
||||
if (dbhan) return Promise.resolve();
|
||||
return new Promise((resolve, reject) => {
|
||||
afterConnectCallbacks.push([resolve, reject]);
|
||||
});
|
||||
@@ -108,14 +108,14 @@ function handlePing() {
|
||||
async function handleDatabaseOp(op, { msgid, name }) {
|
||||
try {
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
systemConnection = await connectUtility(driver, storedConnection, 'app');
|
||||
dbhan = await connectUtility(driver, storedConnection, 'app');
|
||||
if (driver[op]) {
|
||||
await driver[op](systemConnection, name);
|
||||
await driver[op](dbhan, name);
|
||||
} else {
|
||||
const dmp = driver.createDumper();
|
||||
dmp[op](name);
|
||||
logger.info({ sql: dmp.s }, 'Running script');
|
||||
await driver.query(systemConnection, dmp.s, { discardResult: true });
|
||||
await driver.query(dbhan, dmp.s, { discardResult: true });
|
||||
}
|
||||
await handleRefresh();
|
||||
|
||||
@@ -137,11 +137,11 @@ async function handleDriverDataCore(msgid, callMethod) {
|
||||
}
|
||||
|
||||
async function handleServerSummary({ msgid }) {
|
||||
return handleDriverDataCore(msgid, driver => driver.serverSummary(systemConnection));
|
||||
return handleDriverDataCore(msgid, driver => driver.serverSummary(dbhan));
|
||||
}
|
||||
|
||||
async function handleSummaryCommand({ msgid, command, row }) {
|
||||
return handleDriverDataCore(msgid, driver => driver.summaryCommand(systemConnection, command, row));
|
||||
return handleDriverDataCore(msgid, driver => driver.summaryCommand(dbhan, command, row));
|
||||
}
|
||||
|
||||
const messageHandlers = {
|
||||
@@ -161,10 +161,12 @@ async function handleMessage({ msgtype, ...other }) {
|
||||
function start() {
|
||||
childProcessChecker();
|
||||
|
||||
setInterval(() => {
|
||||
setInterval(async () => {
|
||||
const time = new Date().getTime();
|
||||
if (time - lastPing > 40 * 1000) {
|
||||
logger.info('Server connection not alive, exiting');
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
await driver.close(dbhan);
|
||||
process.exit(0);
|
||||
}
|
||||
}, 10 * 1000);
|
||||
@@ -178,6 +180,7 @@ function start() {
|
||||
name: 'error',
|
||||
message: err.message,
|
||||
});
|
||||
logger.error(extractErrorLogData(err), `Error processing message ${message?.['msgtype']}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ const { getLogger, extractIntSettingsValue, extractBoolSettingsValue } = require
|
||||
|
||||
const logger = getLogger('sessionProcess');
|
||||
|
||||
let systemConnection;
|
||||
let dbhan;
|
||||
let storedConnection;
|
||||
let afterConnectCallbacks = [];
|
||||
// let currentHandlers = [];
|
||||
@@ -177,7 +177,7 @@ function handleStream(driver, resultIndexHolder, sqlItem) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const start = sqlItem.trimStart || sqlItem.start;
|
||||
const handler = new StreamHandler(resultIndexHolder, resolve, start && start.line);
|
||||
driver.stream(systemConnection, sqlItem.text, handler);
|
||||
driver.stream(dbhan, sqlItem.text, handler);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ async function handleConnect(connection) {
|
||||
storedConnection = connection;
|
||||
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
systemConnection = await connectUtility(driver, storedConnection, 'app');
|
||||
dbhan = await connectUtility(driver, storedConnection, 'app');
|
||||
for (const [resolve] of afterConnectCallbacks) {
|
||||
resolve();
|
||||
}
|
||||
@@ -210,7 +210,7 @@ async function handleConnect(connection) {
|
||||
// }
|
||||
|
||||
function waitConnected() {
|
||||
if (systemConnection) return Promise.resolve();
|
||||
if (dbhan) return Promise.resolve();
|
||||
return new Promise((resolve, reject) => {
|
||||
afterConnectCallbacks.push([resolve, reject]);
|
||||
});
|
||||
@@ -230,7 +230,7 @@ async function handleStartProfiler({ jslid }) {
|
||||
const writer = new TableWriter();
|
||||
writer.initializeFromReader(jslid);
|
||||
|
||||
currentProfiler = await driver.startProfiler(systemConnection, {
|
||||
currentProfiler = await driver.startProfiler(dbhan, {
|
||||
row: data => writer.rowFromReader(data),
|
||||
});
|
||||
currentProfiler.writer = writer;
|
||||
@@ -241,7 +241,7 @@ async function handleStopProfiler({ jslid }) {
|
||||
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
currentProfiler.writer.close();
|
||||
driver.stopProfiler(systemConnection, currentProfiler);
|
||||
driver.stopProfiler(dbhan, currentProfiler);
|
||||
currentProfiler = null;
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ async function handleExecuteReader({ jslid, sql, fileName }) {
|
||||
const writer = new TableWriter();
|
||||
writer.initializeFromReader(jslid);
|
||||
|
||||
const reader = await driver.readQuery(systemConnection, sql);
|
||||
const reader = await driver.readQuery(dbhan, sql);
|
||||
|
||||
reader.on('data', data => {
|
||||
writer.rowFromReader(data);
|
||||
@@ -340,10 +340,12 @@ function start() {
|
||||
|
||||
lastPing = new Date().getTime();
|
||||
|
||||
setInterval(() => {
|
||||
setInterval(async () => {
|
||||
const time = new Date().getTime();
|
||||
if (time - lastPing > 25 * 1000) {
|
||||
logger.info('Session not alive, exiting');
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
await driver.close(dbhan);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
@@ -362,6 +364,8 @@ function start() {
|
||||
executingScripts == 0
|
||||
) {
|
||||
logger.info('Session not active, exiting');
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
await driver.close(dbhan);
|
||||
process.exit(0);
|
||||
}
|
||||
}, 10 * 1000);
|
||||
|
||||
@@ -3,7 +3,7 @@ const platformInfo = require('../utility/platformInfo');
|
||||
const childProcessChecker = require('../utility/childProcessChecker');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const { SSHConnection } = require('../utility/SSHConnection');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const { getLogger, extractErrorLogData, extractErrorMessage } = require('dbgate-tools');
|
||||
|
||||
const logger = getLogger('sshProcess');
|
||||
|
||||
@@ -40,13 +40,13 @@ async function handleStart({ connection, tunnelConfig }) {
|
||||
tunnelConfig,
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error creating SSH tunnel connection:');
|
||||
logger.error(extractErrorLogData(err), 'Error creating SSH tunnel connection:');
|
||||
|
||||
process.send({
|
||||
msgtype: 'error',
|
||||
connection,
|
||||
tunnelConfig,
|
||||
errorMessage: err.message,
|
||||
errorMessage: extractErrorMessage(err.message),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
const autoIndexForeignKeysTransform = () => database => {
|
||||
return {
|
||||
...database,
|
||||
tables: database.tables.map(table => {
|
||||
return {
|
||||
...table,
|
||||
indexes: [
|
||||
...(table.indexes || []),
|
||||
...table.foreignKeys.map(fk => ({
|
||||
constraintName: `IX_${fk.constraintName}`,
|
||||
columns: fk.columns.map(x => ({ columnName: x.columnName })),
|
||||
})),
|
||||
],
|
||||
};
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = autoIndexForeignKeysTransform;
|
||||
@@ -2,6 +2,13 @@ const EnsureStreamHeaderStream = require('../utility/EnsureStreamHeaderStream');
|
||||
const Stream = require('stream');
|
||||
const ColumnMapTransformStream = require('../utility/ColumnMapTransformStream');
|
||||
|
||||
/**
|
||||
* Copies reader to writer. Used for import, export tables and transfer data between tables
|
||||
* @param {readerType} input - reader object
|
||||
* @param {writerType} output - writer object
|
||||
* @param {object} options - options
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function copyStream(input, output, options) {
|
||||
const { columns } = options || {};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ const { resolveArchiveFolder } = require('../utility/directories');
|
||||
async function dataDuplicator({
|
||||
connection,
|
||||
archive,
|
||||
folder,
|
||||
items,
|
||||
options,
|
||||
analysedStructure = null,
|
||||
@@ -19,32 +20,44 @@ async function dataDuplicator({
|
||||
systemConnection,
|
||||
}) {
|
||||
if (!driver) driver = requireEngineDriver(connection);
|
||||
const pool = systemConnection || (await connectUtility(driver, connection, 'write'));
|
||||
|
||||
logger.info(`Connected.`);
|
||||
const dbhan = systemConnection || (await connectUtility(driver, connection, 'write'));
|
||||
|
||||
if (!analysedStructure) {
|
||||
analysedStructure = await driver.analyseFull(pool);
|
||||
try {
|
||||
logger.info(`Connected.`);
|
||||
|
||||
if (!analysedStructure) {
|
||||
analysedStructure = await driver.analyseFull(dbhan);
|
||||
}
|
||||
|
||||
const sourceDir = archive
|
||||
? resolveArchiveFolder(archive)
|
||||
: folder?.startsWith('archive:')
|
||||
? resolveArchiveFolder(folder.substring('archive:'.length))
|
||||
: folder;
|
||||
|
||||
const dupl = new DataDuplicator(
|
||||
dbhan,
|
||||
driver,
|
||||
analysedStructure,
|
||||
items.map(item => ({
|
||||
name: item.name,
|
||||
operation: item.operation,
|
||||
matchColumns: item.matchColumns,
|
||||
openStream:
|
||||
item.openStream || (() => jsonLinesReader({ fileName: path.join(sourceDir, `${item.name}.jsonl`) })),
|
||||
})),
|
||||
stream,
|
||||
copyStream,
|
||||
options
|
||||
);
|
||||
|
||||
await dupl.run();
|
||||
} finally {
|
||||
if (!systemConnection) {
|
||||
await driver.close(dbhan);
|
||||
}
|
||||
}
|
||||
|
||||
const dupl = new DataDuplicator(
|
||||
pool,
|
||||
driver,
|
||||
analysedStructure,
|
||||
items.map(item => ({
|
||||
name: item.name,
|
||||
operation: item.operation,
|
||||
matchColumns: item.matchColumns,
|
||||
openStream:
|
||||
item.openStream ||
|
||||
(() => jsonLinesReader({ fileName: path.join(resolveArchiveFolder(archive), `${item.name}.jsonl`) })),
|
||||
})),
|
||||
stream,
|
||||
copyStream,
|
||||
options
|
||||
);
|
||||
|
||||
await dupl.run();
|
||||
}
|
||||
|
||||
module.exports = dataDuplicator;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
const dataTypeMapperTransform = (oldType, newType) => database => {
|
||||
return {
|
||||
...database,
|
||||
tables: database.tables.map(table => {
|
||||
return {
|
||||
...table,
|
||||
columns: table.columns.map(column => {
|
||||
if (column.dataType?.toLowerCase() === oldType?.toLowerCase()) {
|
||||
return {
|
||||
...column,
|
||||
dataType: newType,
|
||||
};
|
||||
}
|
||||
return column;
|
||||
}),
|
||||
};
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = dataTypeMapperTransform;
|
||||
@@ -1,17 +1,73 @@
|
||||
const generateDeploySql = require('./generateDeploySql');
|
||||
const executeQuery = require('./executeQuery');
|
||||
const { ScriptDrivedDeployer } = require('dbgate-datalib');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const loadModelFolder = require('../utility/loadModelFolder');
|
||||
const crypto = require('crypto');
|
||||
|
||||
async function deployDb({ connection, systemConnection, driver, analysedStructure, modelFolder, loadedDbModel }) {
|
||||
const { sql } = await generateDeploySql({
|
||||
connection,
|
||||
systemConnection,
|
||||
driver,
|
||||
analysedStructure,
|
||||
modelFolder,
|
||||
loadedDbModel,
|
||||
});
|
||||
// console.log('RUNNING DEPLOY SCRIPT:', sql);
|
||||
await executeQuery({ connection, systemConnection, driver, sql });
|
||||
/**
|
||||
* Deploys database model stored in modelFolder (table as yamls) to database
|
||||
* @param {object} options
|
||||
* @param {connectionType} options.connection - connection object
|
||||
* @param {object} options.systemConnection - system connection (result of driver.connect). If not provided, new connection will be created
|
||||
* @param {object} options.driver - driver object. If not provided, it will be loaded from connection
|
||||
* @param {object} options.analysedStructure - analysed structure of the database. If not provided, it will be loaded
|
||||
* @param {string} options.modelFolder - folder with model files (YAML files for tables, SQL files for views, procedures, ...)
|
||||
* @param {import('dbgate-tools').DatabaseModelFile[]} options.loadedDbModel - loaded database model - collection of yaml and SQL files loaded into array
|
||||
* @param {function[]} options.modelTransforms - array of functions for transforming model
|
||||
* @param {object} options.dbdiffOptionsExtra - extra options for dbdiff
|
||||
* @param {string} options.ignoreNameRegex - regex for ignoring objects by name
|
||||
* @param {string} options.targetSchema - target schema for deployment
|
||||
* @param {number} options.maxMissingTablesRatio - maximum ratio of missing tables in database. Safety check, if missing ratio is highe, deploy is stopped (preventing accidental drop of all tables)
|
||||
*/
|
||||
async function deployDb({
|
||||
connection,
|
||||
systemConnection,
|
||||
driver,
|
||||
analysedStructure,
|
||||
modelFolder,
|
||||
loadedDbModel,
|
||||
modelTransforms,
|
||||
dbdiffOptionsExtra,
|
||||
ignoreNameRegex = '',
|
||||
targetSchema = null,
|
||||
maxMissingTablesRatio = undefined,
|
||||
}) {
|
||||
if (!driver) driver = requireEngineDriver(connection);
|
||||
const dbhan = systemConnection || (await connectUtility(driver, connection, 'read'));
|
||||
|
||||
try {
|
||||
const scriptDeployer = new ScriptDrivedDeployer(
|
||||
dbhan,
|
||||
driver,
|
||||
Array.isArray(loadedDbModel) ? loadedDbModel : modelFolder ? await loadModelFolder(modelFolder) : [],
|
||||
crypto
|
||||
);
|
||||
await scriptDeployer.runPre();
|
||||
|
||||
const { sql } = await generateDeploySql({
|
||||
connection,
|
||||
systemConnection: dbhan,
|
||||
driver,
|
||||
analysedStructure,
|
||||
modelFolder,
|
||||
loadedDbModel,
|
||||
modelTransforms,
|
||||
dbdiffOptionsExtra,
|
||||
ignoreNameRegex,
|
||||
targetSchema,
|
||||
maxMissingTablesRatio,
|
||||
});
|
||||
// console.log('RUNNING DEPLOY SCRIPT:', sql);
|
||||
await executeQuery({ connection, systemConnection: dbhan, driver, sql, logScriptItems: true });
|
||||
|
||||
await scriptDeployer.runPost();
|
||||
} finally {
|
||||
if (!systemConnection) {
|
||||
await driver.close(dbhan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = deployDb;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
const executeQuery = require('./executeQuery');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
const { getLogger, extendDatabaseInfo } = require('dbgate-tools');
|
||||
|
||||
const logger = getLogger('dropAllDbObjects');
|
||||
|
||||
/**
|
||||
* Drops all database objects
|
||||
* @param {object} options
|
||||
* @param {connectionType} options.connection - connection object
|
||||
* @param {object} options.systemConnection - system connection (result of driver.connect). If not provided, new connection will be created
|
||||
* @param {object} options.driver - driver object. If not provided, it will be loaded from connection
|
||||
* @param {object} options.analysedStructure - analysed structure of the database. If not provided, it will be loaded
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function dropAllDbObjects({ connection, systemConnection, driver, analysedStructure }) {
|
||||
if (!driver) driver = requireEngineDriver(connection);
|
||||
|
||||
const dbhan = systemConnection || (await connectUtility(driver, connection, 'write'));
|
||||
|
||||
logger.info(`Connected.`);
|
||||
|
||||
if (!analysedStructure) {
|
||||
analysedStructure = await driver.analyseFull(dbhan);
|
||||
}
|
||||
|
||||
analysedStructure = extendDatabaseInfo(analysedStructure);
|
||||
|
||||
const dmp = driver.createDumper();
|
||||
|
||||
for (const table of analysedStructure.tables) {
|
||||
for (const fk of table.foreignKeys) {
|
||||
dmp.dropForeignKey(fk);
|
||||
}
|
||||
}
|
||||
for (const table of analysedStructure.tables) {
|
||||
dmp.dropTable(table);
|
||||
}
|
||||
for (const field of Object.keys(analysedStructure)) {
|
||||
if (dmp.getSqlObjectSqlName(field)) {
|
||||
for (const obj of analysedStructure[field]) {
|
||||
dmp.dropSqlObject(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await executeQuery({ connection, systemConnection, driver, sql: dmp.s, logScriptItems: true });
|
||||
}
|
||||
|
||||
module.exports = dropAllDbObjects;
|
||||
@@ -27,15 +27,23 @@ async function dumpDatabase({
|
||||
logger.info(`Dumping database`);
|
||||
|
||||
if (!driver) driver = requireEngineDriver(connection);
|
||||
const pool = systemConnection || (await connectUtility(driver, connection, 'read', { forceRowsAsObjects: true }));
|
||||
logger.info(`Connected.`);
|
||||
|
||||
const dumper = await driver.createBackupDumper(pool, {
|
||||
outputFile,
|
||||
databaseName,
|
||||
schemaName,
|
||||
});
|
||||
await doDump(dumper);
|
||||
const dbhan = systemConnection || (await connectUtility(driver, connection, 'read', { forceRowsAsObjects: true }));
|
||||
|
||||
try {
|
||||
logger.info(`Connected.`);
|
||||
|
||||
const dumper = await driver.createBackupDumper(dbhan, {
|
||||
outputFile,
|
||||
databaseName,
|
||||
schemaName,
|
||||
});
|
||||
await doDump(dumper);
|
||||
} finally {
|
||||
if (!systemConnection) {
|
||||
await driver.close(dbhan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = dumpDatabase;
|
||||
|
||||
@@ -1,17 +1,49 @@
|
||||
const fs = require('fs-extra');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const { getLogger, getLimitedQuery } = require('dbgate-tools');
|
||||
|
||||
const logger = getLogger('execQuery');
|
||||
|
||||
async function executeQuery({ connection = undefined, systemConnection = undefined, driver = undefined, sql }) {
|
||||
logger.info({ sql }, `Execute query`);
|
||||
/**
|
||||
* Executes SQL query
|
||||
* @param {object} options
|
||||
* @param {connectionType} [options.connection] - connection object
|
||||
* @param {object} [options.systemConnection] - system connection (result of driver.connect). If not provided, new connection will be created
|
||||
* @param {object} [options.driver] - driver object. If not provided, it will be loaded from connection
|
||||
* @param {string} [options.sql] - SQL query
|
||||
* @param {string} [options.sqlFile] - SQL file
|
||||
* @param {boolean} [options.logScriptItems] - whether to log script items instead of whole script
|
||||
*/
|
||||
async function executeQuery({
|
||||
connection = undefined,
|
||||
systemConnection = undefined,
|
||||
driver = undefined,
|
||||
sql,
|
||||
sqlFile = undefined,
|
||||
logScriptItems = false,
|
||||
}) {
|
||||
if (!logScriptItems) {
|
||||
logger.info({ sql: getLimitedQuery(sql) }, `Execute query`);
|
||||
}
|
||||
|
||||
if (!driver) driver = requireEngineDriver(connection);
|
||||
const pool = systemConnection || (await connectUtility(driver, connection, 'script'));
|
||||
logger.info(`Connected.`);
|
||||
const dbhan = systemConnection || (await connectUtility(driver, connection, 'script'));
|
||||
|
||||
await driver.script(pool, sql);
|
||||
if (sqlFile) {
|
||||
logger.debug(`Loading SQL file ${sqlFile}`);
|
||||
sql = await fs.readFile(sqlFile, { encoding: 'utf-8' });
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(`Connected.`);
|
||||
|
||||
await driver.script(dbhan, sql, { logScriptItems });
|
||||
} finally {
|
||||
if (!systemConnection) {
|
||||
await driver.close(dbhan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = executeQuery;
|
||||
|
||||
@@ -6,11 +6,30 @@ const {
|
||||
extendDatabaseInfo,
|
||||
modelCompareDbDiffOptions,
|
||||
enrichWithPreloadedRows,
|
||||
skipNamesInStructureByRegex,
|
||||
replaceSchemaInStructure,
|
||||
filterStructureBySchema,
|
||||
skipDbGateInternalObjects,
|
||||
} = require('dbgate-tools');
|
||||
const importDbModel = require('../utility/importDbModel');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
|
||||
/**
|
||||
* Generates query for deploying model into database
|
||||
* @param {object} options
|
||||
* @param {connectionType} options.connection - connection object
|
||||
* @param {object} options.systemConnection - system connection (result of driver.connect). If not provided, new connection will be created
|
||||
* @param {object} options.driver - driver object. If not provided, it will be loaded from connection
|
||||
* @param {object} options.analysedStructure - analysed structure of the database. If not provided, it will be loaded
|
||||
* @param {string} options.modelFolder - folder with model files (YAML files for tables, SQL files for views, procedures, ...)
|
||||
* @param {import('dbgate-tools').DatabaseModelFile[]} options.loadedDbModel - loaded database model - collection of yaml and SQL files loaded into array
|
||||
* @param {function[]} options.modelTransforms - array of functions for transforming model
|
||||
* @param {object} options.dbdiffOptionsExtra - extra options for dbdiff
|
||||
* @param {string} options.ignoreNameRegex - regex for ignoring objects by name
|
||||
* @param {string} options.targetSchema - target schema for deployment
|
||||
* @param {number} options.maxMissingTablesRatio - maximum ratio of missing tables in database. Safety check, if missing ratio is highe, deploy is stopped (preventing accidental drop of all tables)
|
||||
*/
|
||||
async function generateDeploySql({
|
||||
connection,
|
||||
systemConnection = undefined,
|
||||
@@ -18,44 +37,95 @@ async function generateDeploySql({
|
||||
analysedStructure = undefined,
|
||||
modelFolder = undefined,
|
||||
loadedDbModel = undefined,
|
||||
modelTransforms = undefined,
|
||||
dbdiffOptionsExtra = {},
|
||||
ignoreNameRegex = '',
|
||||
targetSchema = null,
|
||||
maxMissingTablesRatio = undefined,
|
||||
}) {
|
||||
if (!driver) driver = requireEngineDriver(connection);
|
||||
|
||||
const pool = systemConnection || (await connectUtility(driver, connection, 'read'));
|
||||
if (!analysedStructure) {
|
||||
analysedStructure = await driver.analyseFull(pool);
|
||||
const dbhan = systemConnection || (await connectUtility(driver, connection, 'read'));
|
||||
if (
|
||||
driver?.dialect?.multipleSchema &&
|
||||
!targetSchema &&
|
||||
dbdiffOptionsExtra?.['schemaMode'] !== 'ignore' &&
|
||||
dbdiffOptionsExtra?.['schemaMode'] !== 'ignoreImplicit'
|
||||
) {
|
||||
throw new Error('targetSchema is required for databases with multiple schemas');
|
||||
}
|
||||
|
||||
const deployedModel = generateDbPairingId(
|
||||
extendDatabaseInfo(loadedDbModel ? databaseInfoFromYamlModel(loadedDbModel) : await importDbModel(modelFolder))
|
||||
);
|
||||
const currentModel = generateDbPairingId(extendDatabaseInfo(analysedStructure));
|
||||
const opts = {
|
||||
...modelCompareDbDiffOptions,
|
||||
try {
|
||||
if (!analysedStructure) {
|
||||
analysedStructure = await driver.analyseFull(dbhan);
|
||||
}
|
||||
|
||||
noDropTable: true,
|
||||
noDropColumn: true,
|
||||
noDropConstraint: true,
|
||||
noDropSqlObject: true,
|
||||
noRenameTable: true,
|
||||
noRenameColumn: true,
|
||||
};
|
||||
const currentModelPaired = matchPairedObjects(deployedModel, currentModel, opts);
|
||||
const currentModelPairedPreloaded = await enrichWithPreloadedRows(deployedModel, currentModelPaired, pool, driver);
|
||||
let deployedModelSource = loadedDbModel
|
||||
? databaseInfoFromYamlModel(loadedDbModel)
|
||||
: await importDbModel(modelFolder);
|
||||
|
||||
// console.log('currentModelPairedPreloaded', currentModelPairedPreloaded.tables[0]);
|
||||
// console.log('deployedModel', deployedModel.tables[0]);
|
||||
// console.log('currentModel', currentModel.tables[0]);
|
||||
// console.log('currentModelPaired', currentModelPaired.tables[0]);
|
||||
const res = getAlterDatabaseScript(
|
||||
currentModelPairedPreloaded,
|
||||
deployedModel,
|
||||
opts,
|
||||
currentModelPairedPreloaded,
|
||||
deployedModel,
|
||||
driver
|
||||
);
|
||||
return res;
|
||||
if (ignoreNameRegex) {
|
||||
analysedStructure = skipNamesInStructureByRegex(analysedStructure, new RegExp(ignoreNameRegex, 'i'));
|
||||
deployedModelSource = skipNamesInStructureByRegex(deployedModelSource, new RegExp(ignoreNameRegex, 'i'));
|
||||
}
|
||||
analysedStructure = skipDbGateInternalObjects(analysedStructure);
|
||||
|
||||
for (const transform of modelTransforms || []) {
|
||||
deployedModelSource = transform(deployedModelSource);
|
||||
}
|
||||
|
||||
if (targetSchema) {
|
||||
deployedModelSource = replaceSchemaInStructure(deployedModelSource, targetSchema);
|
||||
analysedStructure = filterStructureBySchema(analysedStructure, targetSchema);
|
||||
}
|
||||
|
||||
const deployedModel = generateDbPairingId(extendDatabaseInfo(deployedModelSource));
|
||||
const currentModel = generateDbPairingId(extendDatabaseInfo(analysedStructure));
|
||||
const opts = {
|
||||
...modelCompareDbDiffOptions,
|
||||
|
||||
noDropTable: true,
|
||||
noDropColumn: true,
|
||||
noDropConstraint: true,
|
||||
noDropSqlObject: true,
|
||||
noRenameTable: true,
|
||||
noRenameColumn: true,
|
||||
|
||||
...dbdiffOptionsExtra,
|
||||
};
|
||||
const currentModelPaired = matchPairedObjects(deployedModel, currentModel, opts);
|
||||
const currentModelPairedPreloaded = await enrichWithPreloadedRows(deployedModel, currentModelPaired, dbhan, driver);
|
||||
|
||||
if (maxMissingTablesRatio != null) {
|
||||
const missingTables = currentModelPaired.tables.filter(
|
||||
x => !deployedModel.tables.find(y => y.pairingId == x.pairingId)
|
||||
);
|
||||
const missingTableCount = missingTables.length;
|
||||
const missingTablesRatio = missingTableCount / (currentModelPaired.tables.length || 1);
|
||||
if (missingTablesRatio > maxMissingTablesRatio) {
|
||||
throw new Error(`Too many missing tables (${missingTablesRatio * 100}%), aborting deploy`);
|
||||
}
|
||||
}
|
||||
|
||||
// console.log('currentModelPairedPreloaded', currentModelPairedPreloaded.tables[0]);
|
||||
// console.log('deployedModel', deployedModel.tables[0]);
|
||||
// console.log('currentModel', currentModel.tables[0]);
|
||||
// console.log('currentModelPaired', currentModelPaired.tables[0]);
|
||||
const res = getAlterDatabaseScript(
|
||||
currentModelPairedPreloaded,
|
||||
deployedModel,
|
||||
opts,
|
||||
currentModelPairedPreloaded,
|
||||
deployedModel,
|
||||
driver
|
||||
);
|
||||
|
||||
return res;
|
||||
} finally {
|
||||
if (!systemConnection) {
|
||||
await driver.close(dbhan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = generateDeploySql;
|
||||
|
||||
@@ -9,14 +9,28 @@ const { getLogger } = require('dbgate-tools');
|
||||
const logger = getLogger('importDb');
|
||||
|
||||
class ImportStream extends stream.Transform {
|
||||
constructor(pool, driver) {
|
||||
constructor(dbhan, driver) {
|
||||
super({ objectMode: true });
|
||||
this.pool = pool;
|
||||
this.dbhan = dbhan;
|
||||
this.driver = driver;
|
||||
this.writeQueryStream = null;
|
||||
}
|
||||
async _transform(chunk, encoding, cb) {
|
||||
try {
|
||||
await this.driver.script(this.pool, chunk, { queryOptions: { importSqlDump: true } });
|
||||
if (chunk.specialMarker == 'copy_stdin_start') {
|
||||
this.writeQueryStream = await this.driver.writeQueryFromStream(this.dbhan, chunk.text);
|
||||
} else if (chunk.specialMarker == 'copy_stdin_line') {
|
||||
this.writeQueryStream.write(chunk.text);
|
||||
} else if (chunk.specialMarker == 'copy_stdin_end') {
|
||||
this.writeQueryStream.end();
|
||||
await new Promise((resolve, reject) => {
|
||||
this.writeQueryStream.on('finish', resolve);
|
||||
this.writeQueryStream.on('error', reject);
|
||||
});
|
||||
this.writeQueryStream = null;
|
||||
} else {
|
||||
await this.driver.script(this.dbhan, chunk.text, { queryOptions: { importSqlDump: true } });
|
||||
}
|
||||
} catch (err) {
|
||||
this.emit('error', err.message);
|
||||
}
|
||||
@@ -44,19 +58,28 @@ async function importDatabase({ connection = undefined, systemConnection = undef
|
||||
logger.info(`Importing database`);
|
||||
|
||||
if (!driver) driver = requireEngineDriver(connection);
|
||||
const pool = systemConnection || (await connectUtility(driver, connection, 'write'));
|
||||
logger.info(`Connected.`);
|
||||
const dbhan = systemConnection || (await connectUtility(driver, connection, 'write'));
|
||||
try {
|
||||
logger.info(`Connected.`);
|
||||
|
||||
logger.info(`Input file: ${inputFile}`);
|
||||
const downloadedFile = await download(inputFile);
|
||||
logger.info(`Downloaded file: ${downloadedFile}`);
|
||||
logger.info(`Input file: ${inputFile}`);
|
||||
const downloadedFile = await download(inputFile);
|
||||
logger.info(`Downloaded file: ${downloadedFile}`);
|
||||
|
||||
const fileStream = fs.createReadStream(downloadedFile, 'utf-8');
|
||||
const splittedStream = splitQueryStream(fileStream, driver.getQuerySplitterOptions('script'));
|
||||
const importStream = new ImportStream(pool, driver);
|
||||
// @ts-ignore
|
||||
splittedStream.pipe(importStream);
|
||||
await awaitStreamEnd(importStream);
|
||||
const fileStream = fs.createReadStream(downloadedFile, 'utf-8');
|
||||
const splittedStream = splitQueryStream(fileStream, {
|
||||
...driver.getQuerySplitterOptions('import'),
|
||||
returnRichInfo: true,
|
||||
});
|
||||
const importStream = new ImportStream(dbhan, driver);
|
||||
// @ts-ignore
|
||||
splittedStream.pipe(importStream);
|
||||
await awaitStreamEnd(importStream);
|
||||
} finally {
|
||||
if (!systemConnection) {
|
||||
await driver.close(dbhan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = importDatabase;
|
||||
|
||||
@@ -30,6 +30,11 @@ const dataDuplicator = require('./dataDuplicator');
|
||||
const dbModelToJson = require('./dbModelToJson');
|
||||
const jsonToDbModel = require('./jsonToDbModel');
|
||||
const jsonReader = require('./jsonReader');
|
||||
const dataTypeMapperTransform = require('./dataTypeMapperTransform');
|
||||
const sqlTextReplacementTransform = require('./sqlTextReplacementTransform');
|
||||
const autoIndexForeignKeysTransform = require('./autoIndexForeignKeysTransform');
|
||||
const generateDeploySql = require('./generateDeploySql');
|
||||
const dropAllDbObjects = require('./dropAllDbObjects');
|
||||
|
||||
const dbgateApi = {
|
||||
queryReader,
|
||||
@@ -63,6 +68,11 @@ const dbgateApi = {
|
||||
dataDuplicator,
|
||||
dbModelToJson,
|
||||
jsonToDbModel,
|
||||
dataTypeMapperTransform,
|
||||
sqlTextReplacementTransform,
|
||||
autoIndexForeignKeysTransform,
|
||||
generateDeploySql,
|
||||
dropAllDbObjects,
|
||||
};
|
||||
|
||||
requirePlugin.initializeDbgateApi(dbgateApi);
|
||||
|
||||
@@ -33,6 +33,14 @@ class ParseStream extends stream.Transform {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reader function, which reads JSNOL file or URL. JSONL format - text file, every line is JSON encoded row.
|
||||
* @param {Object} options
|
||||
* @param {string} options.fileName - file name or URL
|
||||
* @param {string} options.encoding - encoding of the file
|
||||
* @param {number} options.limitRows - maximum number of rows to read
|
||||
* @returns {Promise<readerType>} - reader object
|
||||
*/
|
||||
async function jsonLinesReader({ fileName, encoding = 'utf-8', limitRows = undefined }) {
|
||||
logger.info(`Reading file ${fileName}`);
|
||||
|
||||
|
||||
@@ -24,6 +24,14 @@ class StringifyStream extends stream.Transform {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns writer object for {@link copyStream} function. This writer object writes data to JSONL file. JSONL format - text file, every line is JSON encoded row, used eg. by MongoDB.
|
||||
* @param {object} options
|
||||
* @param {string} options.fileName - file name
|
||||
* @param {string} [options.encoding] - encoding of the file
|
||||
* @param {boolean} [options.header] - whether to write header. Header is JSON describing source table structure. Header is specific to DbGate, if you want eg. to import data to MongoDB, you should not write header.
|
||||
* @returns {Promise<writerType>} - writer object
|
||||
*/
|
||||
async function jsonLinesWriter({ fileName, encoding = 'utf-8', header = true }) {
|
||||
logger.info(`Writing file ${fileName}`);
|
||||
const stringify = new StringifyStream({ header });
|
||||
|
||||
@@ -45,6 +45,17 @@ class ParseStream extends stream.Transform {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates reader object for JSON file for {@link copyStream} function.
|
||||
* @param {object} options
|
||||
* @param {string} options.fileName - file name or URL
|
||||
* @param {string} options.jsonStyle - 'object' or 'array'
|
||||
* @param {string} [options.keyField] - key field for object style
|
||||
* @param {string} [options.rootField] - root field for object style
|
||||
* @param {string} [options.encoding] - encoding of the file
|
||||
* @param {number} [options.limitRows] - maximum number of rows to read
|
||||
* @returns {Promise<readerType>} - reader object
|
||||
*/
|
||||
async function jsonReader({
|
||||
fileName,
|
||||
jsonStyle,
|
||||
|
||||
@@ -85,6 +85,16 @@ class StringifyStream extends stream.Transform {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns writer object for {@link copyStream} function. This writer object writes data to JSON file.
|
||||
* @param {object} options
|
||||
* @param {string} options.fileName - file name
|
||||
* @param {string} [options.jsonStyle] - 'object' or 'array'
|
||||
* @param {string} [options.keyField] - key field for object style
|
||||
* @param {string} [options.rootField] - root field for object style
|
||||
* @param {string} [options.encoding] - encoding of the file
|
||||
* @returns {Promise<writerType>} - writer object
|
||||
*/
|
||||
async function jsonWriter({ fileName, jsonStyle, keyField = '_key', rootField, encoding = 'utf-8' }) {
|
||||
logger.info(`Writing file ${fileName}`);
|
||||
const stringify = new StringifyStream({ jsonStyle, keyField, rootField });
|
||||
|
||||
@@ -9,13 +9,19 @@ async function loadDatabase({ connection = undefined, systemConnection = undefin
|
||||
logger.info(`Analysing database`);
|
||||
|
||||
if (!driver) driver = requireEngineDriver(connection);
|
||||
const pool = systemConnection || (await connectUtility(driver, connection, 'read', { forceRowsAsObjects: true }));
|
||||
logger.info(`Connected.`);
|
||||
const dbhan = systemConnection || (await connectUtility(driver, connection, 'read', { forceRowsAsObjects: true }));
|
||||
try {
|
||||
logger.info(`Connected.`);
|
||||
|
||||
const dbInfo = await driver.analyseFull(pool);
|
||||
logger.info(`Analyse finished`);
|
||||
const dbInfo = await driver.analyseFull(dbhan);
|
||||
logger.info(`Analyse finished`);
|
||||
|
||||
await exportDbModel(dbInfo, outputDir);
|
||||
await exportDbModel(dbInfo, outputDir);
|
||||
} finally {
|
||||
if (!systemConnection) {
|
||||
await driver.close(dbhan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = loadDatabase;
|
||||
|
||||
@@ -3,6 +3,15 @@ const connectUtility = require('../utility/connectUtility');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const logger = getLogger('queryReader');
|
||||
|
||||
/**
|
||||
* Returns reader object for {@link copyStream} function. This reader object reads data from query.
|
||||
* @param {object} options
|
||||
* @param {connectionType} options.connection - connection object
|
||||
* @param {string} options.query - SQL query
|
||||
* @param {string} [options.queryType] - query type
|
||||
* @param {string} [options.sql] - SQL query. obsolete; use query instead
|
||||
* @returns {Promise<readerType>} - reader object
|
||||
*/
|
||||
async function queryReader({
|
||||
connection,
|
||||
query,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { pluginsdir, packagedPluginsDir, getPluginBackendPath } = require('../utility/directories');
|
||||
const nativeModules = require('../nativeModules');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
const authProxy = require('../utility/authProxy');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
@@ -11,7 +10,6 @@ const loadedPlugins = {};
|
||||
|
||||
const dbgateEnv = {
|
||||
dbgateApi: null,
|
||||
nativeModules,
|
||||
platformInfo,
|
||||
authProxy,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
const childProcessChecker = require('../utility/childProcessChecker');
|
||||
const processArgs = require('../utility/processArgs');
|
||||
const logger = getLogger();
|
||||
@@ -11,7 +11,7 @@ async function runScript(func) {
|
||||
await func();
|
||||
process.exit(0);
|
||||
} catch (err) {
|
||||
logger.error({ err }, `Error running script: ${err.message || err}`);
|
||||
logger.error(extractErrorLogData(err), `Error running script`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
function replaceInText(text, replacements) {
|
||||
let result = text;
|
||||
for (const key of Object.keys(replacements)) {
|
||||
result = result.split(key).join(replacements[key]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function replaceInCollection(collection, replacements) {
|
||||
if (!collection) return collection;
|
||||
return collection.map(item => {
|
||||
if (item.createSql) {
|
||||
return {
|
||||
...item,
|
||||
createSql: replaceInText(item.createSql, replacements),
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
const sqlTextReplacementTransform = replacements => database => {
|
||||
return {
|
||||
...database,
|
||||
views: replaceInCollection(database.views, replacements),
|
||||
matviews: replaceInCollection(database.matviews, replacements),
|
||||
procedures: replaceInCollection(database.procedures, replacements),
|
||||
functions: replaceInCollection(database.functions, replacements),
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = sqlTextReplacementTransform;
|
||||
@@ -3,9 +3,18 @@ const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
const logger = getLogger('tableReader');
|
||||
|
||||
async function tableReader({ connection, pureName, schemaName }) {
|
||||
/**
|
||||
* Creates reader object for {@link copyStream} function. This reader object reads data from table or view.
|
||||
* @param {object} options
|
||||
* @param {connectionType} options.connection - connection object
|
||||
* @param {object} options.systemConnection - system connection (result of driver.connect). If not provided, new connection will be created
|
||||
* @param {string} options.pureName - table name
|
||||
* @param {string} options.schemaName - schema name
|
||||
* @returns {Promise<readerType>} - reader object
|
||||
*/
|
||||
async function tableReader({ connection, systemConnection, pureName, schemaName }) {
|
||||
const driver = requireEngineDriver(connection);
|
||||
const pool = await connectUtility(driver, connection, 'read');
|
||||
const dbhan = systemConnection || (await connectUtility(driver, connection, 'read'));
|
||||
logger.info(`Connected.`);
|
||||
|
||||
const fullName = { pureName, schemaName };
|
||||
@@ -14,26 +23,26 @@ async function tableReader({ connection, pureName, schemaName }) {
|
||||
// @ts-ignore
|
||||
logger.info(`Reading collection ${fullNameToString(fullName)}`);
|
||||
// @ts-ignore
|
||||
return await driver.readQuery(pool, JSON.stringify(fullName));
|
||||
return await driver.readQuery(dbhan, JSON.stringify(fullName));
|
||||
}
|
||||
|
||||
const table = await driver.analyseSingleObject(pool, fullName, 'tables');
|
||||
const table = await driver.analyseSingleObject(dbhan, fullName, 'tables');
|
||||
const query = `select * from ${quoteFullName(driver.dialect, fullName)}`;
|
||||
if (table) {
|
||||
// @ts-ignore
|
||||
logger.info(`Reading table ${fullNameToString(table)}`);
|
||||
// @ts-ignore
|
||||
return await driver.readQuery(pool, query, table);
|
||||
return await driver.readQuery(dbhan, query, table);
|
||||
}
|
||||
const view = await driver.analyseSingleObject(pool, fullName, 'views');
|
||||
const view = await driver.analyseSingleObject(dbhan, fullName, 'views');
|
||||
if (view) {
|
||||
// @ts-ignore
|
||||
logger.info(`Reading view ${fullNameToString(view)}`);
|
||||
// @ts-ignore
|
||||
return await driver.readQuery(pool, query, view);
|
||||
return await driver.readQuery(dbhan, query, view);
|
||||
}
|
||||
|
||||
return await driver.readQuery(pool, query);
|
||||
return await driver.readQuery(dbhan, query);
|
||||
}
|
||||
|
||||
module.exports = tableReader;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user