@budibase/server 0.9.185-alpha.14 → 0.9.185-alpha.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +7 -1
- package/__mocks__/oracledb.ts +31 -0
- package/builder/assets/{index.8ed163fd.js → index.21479aff.js} +152 -152
- package/builder/assets/{index.4a7c5636.css → index.67ac3a72.css} +1 -1
- package/builder/assets/oracle.66592424.png +0 -0
- package/builder/index.html +2 -2
- package/coverage/clover.xml +84 -84
- package/coverage/coverage-final.json +4 -4
- package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
- package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/controllers/application.js.html +1 -1
- package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
- package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
- package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
- package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
- package/coverage/lcov-report/api/controllers/component.js.html +4 -4
- package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
- package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
- package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
- package/coverage/lcov-report/api/controllers/index.html +1 -1
- package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
- package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
- package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
- package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
- package/coverage/lcov-report/api/controllers/query.js.html +1 -1
- package/coverage/lcov-report/api/controllers/role.js.html +1 -1
- package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/index.html +1 -1
- package/coverage/lcov-report/api/controllers/row/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
- package/coverage/lcov-report/api/controllers/script.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/internal.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
- package/coverage/lcov-report/api/controllers/user.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
- package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
- package/coverage/lcov-report/api/index.html +1 -1
- package/coverage/lcov-report/api/index.js.html +1 -1
- package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
- package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/routes/application.js.html +1 -1
- package/coverage/lcov-report/api/routes/auth.js.html +1 -1
- package/coverage/lcov-report/api/routes/automation.js.html +1 -1
- package/coverage/lcov-report/api/routes/backup.js.html +1 -1
- package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
- package/coverage/lcov-report/api/routes/component.js.html +1 -1
- package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
- package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
- package/coverage/lcov-report/api/routes/dev.js.html +1 -1
- package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
- package/coverage/lcov-report/api/routes/index.html +1 -1
- package/coverage/lcov-report/api/routes/index.js.html +1 -1
- package/coverage/lcov-report/api/routes/integration.js.html +1 -1
- package/coverage/lcov-report/api/routes/layout.js.html +1 -1
- package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
- package/coverage/lcov-report/api/routes/permission.js.html +1 -1
- package/coverage/lcov-report/api/routes/query.js.html +1 -1
- package/coverage/lcov-report/api/routes/role.js.html +1 -1
- package/coverage/lcov-report/api/routes/routing.js.html +1 -1
- package/coverage/lcov-report/api/routes/row.js.html +1 -1
- package/coverage/lcov-report/api/routes/screen.js.html +1 -1
- package/coverage/lcov-report/api/routes/script.js.html +1 -1
- package/coverage/lcov-report/api/routes/static.js.html +1 -1
- package/coverage/lcov-report/api/routes/table.js.html +1 -1
- package/coverage/lcov-report/api/routes/templates.js.html +1 -1
- package/coverage/lcov-report/api/routes/user.js.html +1 -1
- package/coverage/lcov-report/api/routes/view.js.html +1 -1
- package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/actions.js.html +1 -1
- package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
- package/coverage/lcov-report/automations/bullboard.js.html +1 -1
- package/coverage/lcov-report/automations/index.html +1 -1
- package/coverage/lcov-report/automations/index.js.html +1 -1
- package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
- package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
- package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
- package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
- package/coverage/lcov-report/automations/steps/index.html +1 -1
- package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
- package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
- package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
- package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
- package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
- package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
- package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
- package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/triggers.js.html +1 -1
- package/coverage/lcov-report/automations/utils.js.html +1 -1
- package/coverage/lcov-report/constants/index.html +1 -1
- package/coverage/lcov-report/constants/index.js.html +22 -22
- package/coverage/lcov-report/constants/layouts.js.html +1 -1
- package/coverage/lcov-report/constants/screens.js.html +1 -1
- package/coverage/lcov-report/db/client.js.html +1 -1
- package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
- package/coverage/lcov-report/db/index.html +1 -1
- package/coverage/lcov-report/db/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
- package/coverage/lcov-report/db/newid.js.html +3 -3
- package/coverage/lcov-report/db/utils.js.html +57 -57
- package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
- package/coverage/lcov-report/events/index.html +1 -1
- package/coverage/lcov-report/events/index.js.html +1 -1
- package/coverage/lcov-report/events/utils.js.html +1 -1
- package/coverage/lcov-report/index.html +1 -1
- package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
- package/coverage/lcov-report/integrations/tests/index.html +1 -1
- package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
- package/coverage/lcov-report/middleware/authorized.js.html +1 -1
- package/coverage/lcov-report/middleware/builder.js.html +1 -1
- package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
- package/coverage/lcov-report/middleware/index.html +1 -1
- package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
- package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
- package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
- package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
- package/coverage/lcov-report/middleware/utils.js.html +1 -1
- package/coverage/lcov-report/threads/automation.js.html +1 -1
- package/coverage/lcov-report/threads/index.html +1 -1
- package/coverage/lcov-report/threads/index.js.html +1 -1
- package/coverage/lcov-report/threads/query.js.html +1 -1
- package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
- package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
- package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
- package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
- package/coverage/lcov-report/utilities/global.js.html +1 -1
- package/coverage/lcov-report/utilities/index.html +1 -1
- package/coverage/lcov-report/utilities/index.js.html +1 -1
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
- package/coverage/lcov-report/utilities/queue/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
- package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
- package/coverage/lcov-report/utilities/security.js.html +1 -1
- package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
- package/coverage/lcov-report/utilities/users.js.html +1 -1
- package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
- package/coverage/lcov.info +83 -83
- package/dist/definitions/datasource.js +1 -0
- package/dist/integrations/base/sql.js +4 -0
- package/dist/integrations/index.js +6 -0
- package/dist/integrations/oracle.js +345 -0
- package/dist/integrations/utils.js +10 -1
- package/dist/package.json +8 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -5
- package/scripts/integrations/oracle/docker-compose.yml +19 -0
- package/scripts/integrations/oracle/instantclient/linux/x86-64/basiclite-21.4.zip +0 -0
- package/scripts/integrations/oracle/instantclient/linux/x86-64/install.sh +23 -0
- package/scripts/integrations/oracle/oracle.md +90 -0
- package/scripts/integrations/postgres/docker-compose.yml +3 -3
- package/src/definitions/datasource.ts +1 -0
- package/src/integrations/base/IntegrationBase.ts +4 -4
- package/src/integrations/base/sql.ts +3 -0
- package/src/integrations/index.ts +7 -0
- package/src/integrations/oracle.ts +445 -0
- package/src/integrations/tests/oracle.spec.js +94 -0
- package/src/integrations/utils.ts +10 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/server",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "0.9.185-alpha.
|
|
4
|
+
"version": "0.9.185-alpha.18",
|
|
5
5
|
"description": "Budibase Web Server",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"repository": {
|
|
@@ -68,9 +68,9 @@
|
|
|
68
68
|
"author": "Budibase",
|
|
69
69
|
"license": "AGPL-3.0-or-later",
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@budibase/auth": "^0.9.185-alpha.
|
|
72
|
-
"@budibase/client": "^0.9.185-alpha.
|
|
73
|
-
"@budibase/string-templates": "^0.9.185-alpha.
|
|
71
|
+
"@budibase/auth": "^0.9.185-alpha.18",
|
|
72
|
+
"@budibase/client": "^0.9.185-alpha.18",
|
|
73
|
+
"@budibase/string-templates": "^0.9.185-alpha.18",
|
|
74
74
|
"@bull-board/api": "^3.7.0",
|
|
75
75
|
"@bull-board/koa": "^3.7.0",
|
|
76
76
|
"@elastic/elasticsearch": "7.10.0",
|
|
@@ -135,6 +135,7 @@
|
|
|
135
135
|
"@types/koa": "^2.13.3",
|
|
136
136
|
"@types/koa-router": "^7.4.2",
|
|
137
137
|
"@types/node": "^15.12.4",
|
|
138
|
+
"@types/oracledb": "^5.2.1",
|
|
138
139
|
"@typescript-eslint/parser": "4.28.0",
|
|
139
140
|
"babel-jest": "^27.0.2",
|
|
140
141
|
"copyfiles": "^2.4.1",
|
|
@@ -150,5 +151,8 @@
|
|
|
150
151
|
"typescript": "^4.3.5",
|
|
151
152
|
"update-dotenv": "^1.1.1"
|
|
152
153
|
},
|
|
153
|
-
"
|
|
154
|
+
"optionalDependencies": {
|
|
155
|
+
"oracledb": "^5.3.0"
|
|
156
|
+
},
|
|
157
|
+
"gitHead": "aefc0a46953807c460bfb6ab11c5e541ef17a741"
|
|
154
158
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# For more information see:
|
|
2
|
+
# https://container-registry.oracle.com/
|
|
3
|
+
# - Database > Express
|
|
4
|
+
version: "3.8"
|
|
5
|
+
services:
|
|
6
|
+
db:
|
|
7
|
+
container_name: oracle-xe
|
|
8
|
+
platform: linux/x86_64
|
|
9
|
+
image: container-registry.oracle.com/database/express:18.4.0-xe
|
|
10
|
+
environment:
|
|
11
|
+
ORACLE_PWD: oracle
|
|
12
|
+
ports:
|
|
13
|
+
- 1521:1521
|
|
14
|
+
- 5500:5500
|
|
15
|
+
volumes:
|
|
16
|
+
- oracle_data:/opt/oracle/oradata
|
|
17
|
+
|
|
18
|
+
volumes:
|
|
19
|
+
oracle_data:
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Must be root to continue
|
|
4
|
+
if [[ $(id -u) -ne 0 ]] ; then echo "Please run as root" ; exit 1 ; fi
|
|
5
|
+
|
|
6
|
+
# Allow for re-runs
|
|
7
|
+
rm -rf /opt/oracle
|
|
8
|
+
|
|
9
|
+
echo "Installing oracle instant client"
|
|
10
|
+
|
|
11
|
+
# copy and unzip package
|
|
12
|
+
mkdir -p /opt/oracle
|
|
13
|
+
cp scripts/integrations/oracle/instantclient/linux/x86-64/basiclite-21.4.zip /opt/oracle
|
|
14
|
+
cd /opt/oracle
|
|
15
|
+
unzip -qq basiclite-21.4.zip -d .
|
|
16
|
+
rm *.zip
|
|
17
|
+
mv instantclient* instantclient
|
|
18
|
+
|
|
19
|
+
# update runtime link path
|
|
20
|
+
sh -c "echo /opt/oracle/instantclient > /etc/ld.so.conf.d/oracle-instantclient.conf"
|
|
21
|
+
ldconfig /etc/ld.so.conf.d
|
|
22
|
+
|
|
23
|
+
echo "Installation complete"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
## Database
|
|
4
|
+
|
|
5
|
+
**Important**
|
|
6
|
+
- Oracle database is supported only on **x86-64 architecture**
|
|
7
|
+
- Oracle database is **not supported on Mac ARM architecture** (either via docker or linux virtualization)
|
|
8
|
+
|
|
9
|
+
To install oracle express edition simply run `docker-compose up`
|
|
10
|
+
|
|
11
|
+
- A single instance pluggable database (PDB) will be created named `xepdb`
|
|
12
|
+
- The default password is configured in the compose file as `oracle`
|
|
13
|
+
- The `system`, `sys` and `pdbadmin` users all share this password
|
|
14
|
+
|
|
15
|
+
## Instant Client
|
|
16
|
+
|
|
17
|
+
Before oracle can be connected to from nodejs, the oracle client must be installed.
|
|
18
|
+
For more information see https://www.oracle.com/database/technologies/instant-client/downloads.html
|
|
19
|
+
|
|
20
|
+
**Important**
|
|
21
|
+
- Oracle client is supported only on **x86-64 architecture**
|
|
22
|
+
- Oracle client is **not supported on Mac ARM architecture**
|
|
23
|
+
|
|
24
|
+
### Linux
|
|
25
|
+
Run the provided install script for linux from the `server` root path:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
sudo /bin/bash -e scripts/integrations/oracle/instantclient/linux/x86-64/install.sh
|
|
29
|
+
```
|
|
30
|
+
For more information see: https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html#ic_x64_inst
|
|
31
|
+
|
|
32
|
+
### Mac
|
|
33
|
+
**This has not yet been tested**
|
|
34
|
+
|
|
35
|
+
See: https://www.oracle.com/database/technologies/instant-client/macos-intel-x86-downloads.html#ic_osx_inst
|
|
36
|
+
|
|
37
|
+
# Management
|
|
38
|
+
To connect to oracle sql command line:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
docker exec -it oracle-xe sqlplus -l system/oracle@localhost/xepdb1
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
To create a new schema (where a user is the same as a schema in oracle) named `sales`:
|
|
45
|
+
|
|
46
|
+
```sql
|
|
47
|
+
define USERNAME = sales
|
|
48
|
+
|
|
49
|
+
create user &USERNAME;
|
|
50
|
+
|
|
51
|
+
alter user &USERNAME
|
|
52
|
+
default tablespace users
|
|
53
|
+
temporary tablespace temp
|
|
54
|
+
quota unlimited on users;
|
|
55
|
+
|
|
56
|
+
grant create session,
|
|
57
|
+
create view,
|
|
58
|
+
create sequence,
|
|
59
|
+
create procedure,
|
|
60
|
+
create table,
|
|
61
|
+
create trigger,
|
|
62
|
+
create type,
|
|
63
|
+
create materialized view
|
|
64
|
+
to &USERNAME;
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
To set the password for the sales schema use:
|
|
68
|
+
|
|
69
|
+
```sql
|
|
70
|
+
define USERNAME = sales
|
|
71
|
+
define PASSWORD = sales
|
|
72
|
+
|
|
73
|
+
alter user &USERNAME identified by &PASSWORD;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
As before the database schema can now be connected to using:
|
|
77
|
+
```bash
|
|
78
|
+
docker exec -it oracle-xe sqlplus -l sales/sales@localhost:1521/xepdb1
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## HR Schema
|
|
82
|
+
|
|
83
|
+
The `HR` schema is populated with dummy data by default in oracle for testing purposes.
|
|
84
|
+
To connect to the HR schema first update the user password and unlock the account by performing
|
|
85
|
+
```sql
|
|
86
|
+
ALTER USER hr ACCOUNT UNLOCK;
|
|
87
|
+
ALTER USER hr IDENTIFIED BY hr
|
|
88
|
+
```
|
|
89
|
+
You should now be able to connect to the hr schema using the credentials hr/hr
|
|
90
|
+
|
|
@@ -11,7 +11,7 @@ services:
|
|
|
11
11
|
ports:
|
|
12
12
|
- "5432:5432"
|
|
13
13
|
volumes:
|
|
14
|
-
|
|
14
|
+
- pg_data:/var/lib/postgresql/data/
|
|
15
15
|
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
|
|
16
16
|
|
|
17
17
|
pgadmin:
|
|
@@ -24,5 +24,5 @@ services:
|
|
|
24
24
|
ports:
|
|
25
25
|
- "5050:80"
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
volumes:
|
|
28
|
+
pg_data:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export interface IntegrationBase {
|
|
2
|
-
create?(query: any): Promise<[
|
|
3
|
-
read?(query: any): Promise<[
|
|
4
|
-
update?(query: any): Promise<[
|
|
5
|
-
delete?(query: any): Promise<[
|
|
2
|
+
create?(query: any): Promise<any[]>
|
|
3
|
+
read?(query: any): Promise<any[]>
|
|
4
|
+
update?(query: any): Promise<any[]>
|
|
5
|
+
delete?(query: any): Promise<any[]>
|
|
6
6
|
}
|
|
@@ -416,6 +416,8 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
|
|
|
416
416
|
id = results?.[0].id
|
|
417
417
|
} else if (sqlClient === SqlClients.MY_SQL) {
|
|
418
418
|
id = results?.insertId
|
|
419
|
+
} else if (sqlClient === SqlClients.ORACLE) {
|
|
420
|
+
id = response.outBinds[0][0]
|
|
419
421
|
}
|
|
420
422
|
row = processFn(
|
|
421
423
|
await this.getReturningRow(queryFn, this.checkLookupKeys(id, json))
|
|
@@ -428,4 +430,5 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
|
|
|
428
430
|
}
|
|
429
431
|
}
|
|
430
432
|
|
|
433
|
+
export default SqlQueryBuilder
|
|
431
434
|
module.exports = SqlQueryBuilder
|
|
@@ -39,6 +39,13 @@ const INTEGRATIONS = {
|
|
|
39
39
|
[SourceNames.REST]: rest.integration,
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
// optionally add oracle integration if the oracle binary can be installed
|
|
43
|
+
if (!(process.arch === 'arm64' && process.platform === 'darwin')) {
|
|
44
|
+
const oracle = require("./oracle")
|
|
45
|
+
DEFINITIONS[SourceNames.ORACLE] = oracle.schema
|
|
46
|
+
INTEGRATIONS[SourceNames.ORACLE] = oracle.integration
|
|
47
|
+
}
|
|
48
|
+
|
|
42
49
|
module.exports = {
|
|
43
50
|
definitions: DEFINITIONS,
|
|
44
51
|
integrations: INTEGRATIONS,
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Integration,
|
|
3
|
+
DatasourceFieldTypes,
|
|
4
|
+
QueryTypes,
|
|
5
|
+
SqlQuery,
|
|
6
|
+
QueryJson,
|
|
7
|
+
Operation,
|
|
8
|
+
} from "../definitions/datasource"
|
|
9
|
+
import {
|
|
10
|
+
finaliseExternalTables,
|
|
11
|
+
getSqlQuery,
|
|
12
|
+
buildExternalTableId,
|
|
13
|
+
convertSqlType,
|
|
14
|
+
SqlClients,
|
|
15
|
+
} from "./utils"
|
|
16
|
+
import oracledb, {
|
|
17
|
+
ExecuteOptions,
|
|
18
|
+
Result,
|
|
19
|
+
Connection,
|
|
20
|
+
ConnectionAttributes,
|
|
21
|
+
BindParameters,
|
|
22
|
+
} from "oracledb"
|
|
23
|
+
import Sql from "./base/sql"
|
|
24
|
+
import { Table } from "../definitions/common"
|
|
25
|
+
import { DatasourcePlus } from "./base/datasourcePlus"
|
|
26
|
+
import { FieldTypes } from "../constants"
|
|
27
|
+
|
|
28
|
+
module OracleModule {
|
|
29
|
+
oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT
|
|
30
|
+
|
|
31
|
+
interface OracleConfig {
|
|
32
|
+
host: string
|
|
33
|
+
port: number
|
|
34
|
+
database: string
|
|
35
|
+
user: string
|
|
36
|
+
password: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const SCHEMA: Integration = {
|
|
40
|
+
docs: "https://github.com/oracle/node-oracledb",
|
|
41
|
+
plus: true,
|
|
42
|
+
friendlyName: "Oracle",
|
|
43
|
+
description:
|
|
44
|
+
"Oracle Database is an object-relational database management system developed by Oracle Corporation",
|
|
45
|
+
datasource: {
|
|
46
|
+
host: {
|
|
47
|
+
type: DatasourceFieldTypes.STRING,
|
|
48
|
+
default: "localhost",
|
|
49
|
+
required: true,
|
|
50
|
+
},
|
|
51
|
+
port: {
|
|
52
|
+
type: DatasourceFieldTypes.NUMBER,
|
|
53
|
+
required: true,
|
|
54
|
+
default: 1521,
|
|
55
|
+
},
|
|
56
|
+
database: {
|
|
57
|
+
type: DatasourceFieldTypes.STRING,
|
|
58
|
+
required: true,
|
|
59
|
+
},
|
|
60
|
+
user: {
|
|
61
|
+
type: DatasourceFieldTypes.STRING,
|
|
62
|
+
required: true,
|
|
63
|
+
},
|
|
64
|
+
password: {
|
|
65
|
+
type: DatasourceFieldTypes.PASSWORD,
|
|
66
|
+
required: true,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
query: {
|
|
70
|
+
create: {
|
|
71
|
+
type: QueryTypes.SQL,
|
|
72
|
+
},
|
|
73
|
+
read: {
|
|
74
|
+
type: QueryTypes.SQL,
|
|
75
|
+
},
|
|
76
|
+
update: {
|
|
77
|
+
type: QueryTypes.SQL,
|
|
78
|
+
},
|
|
79
|
+
delete: {
|
|
80
|
+
type: QueryTypes.SQL,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const UNSUPPORTED_TYPES = ["BLOB", "CLOB", "NCLOB"]
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Raw query response
|
|
89
|
+
*/
|
|
90
|
+
interface ColumnsResponse {
|
|
91
|
+
TABLE_NAME: string
|
|
92
|
+
COLUMN_NAME: string
|
|
93
|
+
DATA_TYPE: string
|
|
94
|
+
DATA_DEFAULT: string | null
|
|
95
|
+
COLUMN_ID: number
|
|
96
|
+
CONSTRAINT_NAME: string | null
|
|
97
|
+
CONSTRAINT_TYPE: string | null
|
|
98
|
+
R_CONSTRAINT_NAME: string | null
|
|
99
|
+
SEARCH_CONDITION: string | null
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* An oracle constraint
|
|
104
|
+
*/
|
|
105
|
+
interface OracleConstraint {
|
|
106
|
+
name: string
|
|
107
|
+
type: string
|
|
108
|
+
relatedConstraintName: string | null
|
|
109
|
+
searchCondition: string | null
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* An oracle column and it's related constraints
|
|
114
|
+
*/
|
|
115
|
+
interface OracleColumn {
|
|
116
|
+
name: string
|
|
117
|
+
type: string
|
|
118
|
+
default: string | null
|
|
119
|
+
id: number
|
|
120
|
+
constraints: { [key: string]: OracleConstraint }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* An oracle table and it's related columns
|
|
125
|
+
*/
|
|
126
|
+
interface OracleTable {
|
|
127
|
+
name: string
|
|
128
|
+
columns: { [key: string]: OracleColumn }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const OracleContraintTypes = {
|
|
132
|
+
PRIMARY: "P",
|
|
133
|
+
NOT_NULL_OR_CHECK: "C",
|
|
134
|
+
FOREIGN_KEY: "R",
|
|
135
|
+
UNIQUE: "U",
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
class OracleIntegration extends Sql implements DatasourcePlus {
|
|
139
|
+
private readonly config: OracleConfig
|
|
140
|
+
|
|
141
|
+
public tables: Record<string, Table> = {}
|
|
142
|
+
public schemaErrors: Record<string, string> = {}
|
|
143
|
+
|
|
144
|
+
private readonly COLUMNS_SQL = `
|
|
145
|
+
SELECT
|
|
146
|
+
tabs.table_name,
|
|
147
|
+
cols.column_name,
|
|
148
|
+
cols.data_type,
|
|
149
|
+
cols.data_default,
|
|
150
|
+
cols.column_id,
|
|
151
|
+
cons.constraint_name,
|
|
152
|
+
cons.constraint_type,
|
|
153
|
+
cons.r_constraint_name,
|
|
154
|
+
cons.search_condition
|
|
155
|
+
FROM
|
|
156
|
+
user_tables tabs
|
|
157
|
+
JOIN
|
|
158
|
+
user_tab_columns cols
|
|
159
|
+
ON tabs.table_name = cols.table_name
|
|
160
|
+
LEFT JOIN
|
|
161
|
+
user_cons_columns col_cons
|
|
162
|
+
ON cols.column_name = col_cons.column_name
|
|
163
|
+
AND cols.table_name = col_cons.table_name
|
|
164
|
+
LEFT JOIN
|
|
165
|
+
user_constraints cons
|
|
166
|
+
ON col_cons.constraint_name = cons.constraint_name
|
|
167
|
+
AND cons.table_name = cols.table_name
|
|
168
|
+
WHERE
|
|
169
|
+
(cons.status = 'ENABLED'
|
|
170
|
+
OR cons.status IS NULL)
|
|
171
|
+
`
|
|
172
|
+
constructor(config: OracleConfig) {
|
|
173
|
+
super(SqlClients.ORACLE)
|
|
174
|
+
this.config = config
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Map the flat tabular columns and constraints data into a nested object
|
|
179
|
+
*/
|
|
180
|
+
private mapColumns(result: Result<ColumnsResponse>): {
|
|
181
|
+
[key: string]: OracleTable
|
|
182
|
+
} {
|
|
183
|
+
const oracleTables: { [key: string]: OracleTable } = {}
|
|
184
|
+
|
|
185
|
+
if (result.rows) {
|
|
186
|
+
result.rows.forEach(row => {
|
|
187
|
+
const tableName = row.TABLE_NAME
|
|
188
|
+
const columnName = row.COLUMN_NAME
|
|
189
|
+
const dataType = row.DATA_TYPE
|
|
190
|
+
const dataDefault = row.DATA_DEFAULT
|
|
191
|
+
const columnId = row.COLUMN_ID
|
|
192
|
+
const constraintName = row.CONSTRAINT_NAME
|
|
193
|
+
const constraintType = row.CONSTRAINT_TYPE
|
|
194
|
+
const relatedConstraintName = row.R_CONSTRAINT_NAME
|
|
195
|
+
const searchCondition = row.SEARCH_CONDITION
|
|
196
|
+
|
|
197
|
+
let table = oracleTables[tableName]
|
|
198
|
+
if (!table) {
|
|
199
|
+
table = {
|
|
200
|
+
name: tableName,
|
|
201
|
+
columns: {},
|
|
202
|
+
}
|
|
203
|
+
oracleTables[tableName] = table
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
let column = table.columns[columnName]
|
|
207
|
+
if (!column) {
|
|
208
|
+
column = {
|
|
209
|
+
name: columnName,
|
|
210
|
+
type: dataType,
|
|
211
|
+
default: dataDefault,
|
|
212
|
+
id: columnId,
|
|
213
|
+
constraints: {},
|
|
214
|
+
}
|
|
215
|
+
table.columns[columnName] = column
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (constraintName && constraintType) {
|
|
219
|
+
let constraint = column.constraints[constraintName]
|
|
220
|
+
if (!constraint) {
|
|
221
|
+
constraint = {
|
|
222
|
+
name: constraintName,
|
|
223
|
+
type: constraintType,
|
|
224
|
+
relatedConstraintName: relatedConstraintName,
|
|
225
|
+
searchCondition: searchCondition,
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
column.constraints[constraintName] = constraint
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return oracleTables
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private isSupportedColumn(column: OracleColumn) {
|
|
237
|
+
if (UNSUPPORTED_TYPES.includes(column.type)) {
|
|
238
|
+
return false
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return true
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private isAutoColumn(column: OracleColumn) {
|
|
245
|
+
if (column.default && column.default.toLowerCase().includes("nextval")) {
|
|
246
|
+
return true
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return false
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* No native boolean in oracle. Best we can do is to check if a manual 1 or 0 number constraint has been set up
|
|
254
|
+
* This matches the default behaviour for generating DDL used in knex.
|
|
255
|
+
*/
|
|
256
|
+
private isBooleanType(column: OracleColumn): boolean {
|
|
257
|
+
if (
|
|
258
|
+
column.type.toLowerCase() === "number" &&
|
|
259
|
+
Object.values(column.constraints).filter(c => {
|
|
260
|
+
if (
|
|
261
|
+
c.type === OracleContraintTypes.NOT_NULL_OR_CHECK &&
|
|
262
|
+
c.searchCondition
|
|
263
|
+
) {
|
|
264
|
+
const condition = c.searchCondition
|
|
265
|
+
.replace(/\s/g, "") // remove spaces
|
|
266
|
+
.replace(/[']+/g, "") // remove quotes
|
|
267
|
+
if (
|
|
268
|
+
condition.includes("in(0,1)") ||
|
|
269
|
+
condition.includes("in(1,0)")
|
|
270
|
+
) {
|
|
271
|
+
return true
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return false
|
|
275
|
+
}).length > 0
|
|
276
|
+
) {
|
|
277
|
+
return true
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return false
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
private internalConvertType(column: OracleColumn): string {
|
|
284
|
+
if (this.isBooleanType(column)) {
|
|
285
|
+
return FieldTypes.BOOLEAN
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return convertSqlType(column.type)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Fetches the tables from the oracle table and assigns them to the datasource.
|
|
293
|
+
* @param {*} datasourceId - datasourceId to fetch
|
|
294
|
+
* @param entities - the tables that are to be built
|
|
295
|
+
*/
|
|
296
|
+
async buildSchema(datasourceId: string, entities: Record<string, Table>) {
|
|
297
|
+
const columnsResponse = await this.internalQuery<ColumnsResponse>({
|
|
298
|
+
sql: this.COLUMNS_SQL,
|
|
299
|
+
})
|
|
300
|
+
const oracleTables = this.mapColumns(columnsResponse)
|
|
301
|
+
|
|
302
|
+
const tables: { [key: string]: Table } = {}
|
|
303
|
+
|
|
304
|
+
// iterate each table
|
|
305
|
+
Object.values(oracleTables).forEach(oracleTable => {
|
|
306
|
+
let table = tables[oracleTable.name]
|
|
307
|
+
if (!table) {
|
|
308
|
+
table = {
|
|
309
|
+
_id: buildExternalTableId(datasourceId, oracleTable.name),
|
|
310
|
+
primary: [],
|
|
311
|
+
name: oracleTable.name,
|
|
312
|
+
schema: {},
|
|
313
|
+
}
|
|
314
|
+
tables[oracleTable.name] = table
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// iterate each column on the table
|
|
318
|
+
Object.values(oracleTable.columns)
|
|
319
|
+
// remove columns that we can't read / save
|
|
320
|
+
.filter(oracleColumn => this.isSupportedColumn(oracleColumn))
|
|
321
|
+
// match the order of the columns in the db
|
|
322
|
+
.sort((c1, c2) => c1.id - c2.id)
|
|
323
|
+
.forEach(oracleColumn => {
|
|
324
|
+
const columnName = oracleColumn.name
|
|
325
|
+
let fieldSchema = table.schema[columnName]
|
|
326
|
+
if (!fieldSchema) {
|
|
327
|
+
fieldSchema = {
|
|
328
|
+
autocolumn: this.isAutoColumn(oracleColumn),
|
|
329
|
+
name: columnName,
|
|
330
|
+
type: this.internalConvertType(oracleColumn),
|
|
331
|
+
}
|
|
332
|
+
table.schema[columnName] = fieldSchema
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// iterate each constraint on the column
|
|
336
|
+
Object.values(oracleColumn.constraints).forEach(
|
|
337
|
+
oracleConstraint => {
|
|
338
|
+
if (oracleConstraint.type === OracleContraintTypes.PRIMARY) {
|
|
339
|
+
table.primary!.push(columnName)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
)
|
|
343
|
+
})
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
const final = finaliseExternalTables(tables, entities)
|
|
347
|
+
this.tables = final.tables
|
|
348
|
+
this.schemaErrors = final.errors
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Knex default returning behaviour does not work with oracle
|
|
353
|
+
* Manually add the behaviour for the return column
|
|
354
|
+
*/
|
|
355
|
+
private addReturning(query: SqlQuery, bindings: BindParameters, returnColumn: string) {
|
|
356
|
+
if (bindings instanceof Array) {
|
|
357
|
+
bindings.push({ dir: oracledb.BIND_OUT })
|
|
358
|
+
query.sql = query.sql + ` returning \"${returnColumn}\" into :${bindings.length}`
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private async internalQuery<T>(query: SqlQuery, returnColum?: string, operation?: string): Promise<Result<T>> {
|
|
363
|
+
let connection
|
|
364
|
+
try {
|
|
365
|
+
connection = await this.getConnection()
|
|
366
|
+
|
|
367
|
+
const options: ExecuteOptions = { autoCommit: true }
|
|
368
|
+
const bindings: BindParameters = query.bindings || []
|
|
369
|
+
|
|
370
|
+
if (returnColum && (operation === Operation.CREATE || operation === Operation.UPDATE)) {
|
|
371
|
+
this.addReturning(query, bindings, returnColum)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const result: Result<T> = await connection.execute<T>(
|
|
375
|
+
query.sql,
|
|
376
|
+
bindings,
|
|
377
|
+
options
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
return result
|
|
381
|
+
} finally {
|
|
382
|
+
if (connection) {
|
|
383
|
+
try {
|
|
384
|
+
await connection.close()
|
|
385
|
+
} catch (err) {
|
|
386
|
+
console.error(err)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private getConnection = async (): Promise<Connection> => {
|
|
393
|
+
//connectString : "(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))(CONNECT_DATA =(SID= ORCL)))"
|
|
394
|
+
const connectString = `${this.config.host}:${this.config.port || 1521}/${
|
|
395
|
+
this.config.database
|
|
396
|
+
}`
|
|
397
|
+
const attributes: ConnectionAttributes = {
|
|
398
|
+
user: this.config.user,
|
|
399
|
+
password: this.config.user,
|
|
400
|
+
connectString,
|
|
401
|
+
}
|
|
402
|
+
return oracledb.getConnection(attributes)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
async create(query: SqlQuery | string): Promise<any[]> {
|
|
406
|
+
const response = await this.internalQuery<any>(getSqlQuery(query))
|
|
407
|
+
return response.rows && response.rows.length
|
|
408
|
+
? response.rows
|
|
409
|
+
: [{ created: true }]
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async read(query: SqlQuery | string): Promise<any[]> {
|
|
413
|
+
const response = await this.internalQuery<any>(getSqlQuery(query))
|
|
414
|
+
return response.rows ? response.rows : []
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
async update(query: SqlQuery | string): Promise<any[]> {
|
|
418
|
+
const response = await this.internalQuery(getSqlQuery(query))
|
|
419
|
+
return response.rows && response.rows.length
|
|
420
|
+
? response.rows
|
|
421
|
+
: [{ updated: true }]
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async delete(query: SqlQuery | string): Promise<any[]> {
|
|
425
|
+
const response = await this.internalQuery(getSqlQuery(query))
|
|
426
|
+
return response.rows && response.rows.length
|
|
427
|
+
? response.rows
|
|
428
|
+
: [{ deleted: true }]
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
async query(json: QueryJson) {
|
|
432
|
+
const primaryKeys = json.meta!.table!.primary
|
|
433
|
+
const primaryKey = primaryKeys ? primaryKeys[0] : undefined
|
|
434
|
+
const queryFn = (query: any, operation: string) => this.internalQuery(query, primaryKey, operation)
|
|
435
|
+
const processFn = (response: any) => response.rows ? response.rows : []
|
|
436
|
+
const output = await this.queryWithReturning(json, queryFn, processFn)
|
|
437
|
+
return output
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
module.exports = {
|
|
442
|
+
schema: SCHEMA,
|
|
443
|
+
integration: OracleIntegration,
|
|
444
|
+
}
|
|
445
|
+
}
|