feat: add oracle db support on ee (#5062)

This commit is contained in:
wendrul
2025-01-14 21:21:24 +01:00
committed by GitHub
parent 175379fbe9
commit a8f49d5661
92 changed files with 784 additions and 1001 deletions
+2 -2
View File
@@ -64,7 +64,7 @@ jobs:
platforms: linux/amd64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core,license,http_trigger,zip,oauth2,kafka,nats,php,mysql,mssql,bigquery,websocket,python,smtp,csharp,static_frontend,rust
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core,license,http_trigger,zip,oauth2,kafka,nats,php,mysql,mssql,bigquery,oracledb,websocket,python,smtp,csharp,static_frontend,rust
secrets: |
rh_username=${{ secrets.RH_USERNAME }}
rh_password=${{ secrets.RH_PASSWORD }}
@@ -81,7 +81,7 @@ jobs:
platforms: linux/arm64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core,license,http_trigger,zip,oauth2,kafka,nats,php,mysql,mssql,bigquery,websocket,python,smtp,csharp,static_frontend,rust
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core,license,http_trigger,zip,oauth2,kafka,nats,php,mysql,mssql,bigquery,oracledb,websocket,python,smtp,csharp,static_frontend,rust
secrets: |
rh_username=${{ secrets.RH_USERNAME }}
rh_password=${{ secrets.RH_PASSWORD }}
+1 -1
View File
@@ -45,7 +45,7 @@ jobs:
$env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static"
mkdir frontend/build && cd backend
New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,nats,php,mysql,mssql,bigquery,websocket,python,smtp,csharp,static_frontend,rust
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,nats,php,mysql,mssql,bigquery,oracledb,websocket,python,smtp,csharp,static_frontend,rust
- name: Rename binary with corresponding architecture
run: |
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
build-args: |
features=embedding,parquet,openidconnect,deno_core,license,http_trigger,zip,oauth2,php,mysql,mssql,bigquery,websocket,python,smtp,csharp,static_frontend,rust
features=embedding,parquet,openidconnect,deno_core,license,http_trigger,zip,oauth2,php,mysql,mssql,bigquery,oracledb,websocket,python,smtp,csharp,static_frontend,rust
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
${{ steps.meta-public.outputs.tags }}
+3 -3
View File
@@ -86,7 +86,7 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
build-args: |
features=embedding,parquet,openidconnect,jemalloc,deno_core,license,http_trigger,zip,oauth2,dind,php,mysql,mssql,bigquery,websocket,python,smtp,csharp,static_frontend,rust
features=embedding,parquet,openidconnect,jemalloc,deno_core,license,http_trigger,zip,oauth2,dind,php,mysql,mssql,bigquery,oracledb,websocket,python,smtp,csharp,static_frontend,rust
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
${{ steps.meta-public.outputs.tags }}
@@ -149,7 +149,7 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,nats,otel,dind,php,mysql,mssql,bigquery,websocket,python,smtp,csharp,static_frontend,rust
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,nats,otel,dind,php,mysql,mssql,bigquery,oracledb,websocket,python,smtp,csharp,static_frontend,rust
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
${{ steps.meta-ee-public.outputs.tags }}
@@ -211,7 +211,7 @@ jobs:
platforms: linux/amd64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,nats,otel,dind,php,mysql,mssql,bigquery,websocket,python,smtp,csharp,static_frontend,rust
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,nats,otel,dind,php,mysql,mssql,bigquery,oracledb,websocket,python,smtp,csharp,static_frontend,rust
PYTHON_IMAGE=python:3.12.2-slim-bookworm
tags: |
${{ steps.meta-ee-public-py312.outputs.tags }}
+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
$env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static"
mkdir frontend/build && cd backend
New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,nats,php,mysql,mssql,bigquery,websocket,python,smtp,csharp,static_frontend,rust
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,deno_core,license,http_trigger,zip,oauth2,kafka,nats,php,mysql,mssql,bigquery,oracledb,websocket,python,smtp,csharp,static_frontend,rust
- name: Rename binary with corresponding architecture
run: |
@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT value as \"value: sqlx::types::Json<Box<RawValue>>\" FROM flow_version WHERE id = $1 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "value: sqlx::types::Json<Box<RawValue>>",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false
]
},
"hash": "00a16dbe1729d530f136279471fa3d89ca7f1a88a48fcf0b1ac07144924c1707"
}
@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT raw_flow AS \"raw_flow!: Json<Box<JsonRawValue>>\"\n FROM job WHERE id = $1 AND workspace_id = $2 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "raw_flow!: Json<Box<JsonRawValue>>",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Uuid",
"Text"
]
},
"nullable": [
true
]
},
"hash": "0722f461da9edb169c3c73423440eaa3e1b9f2ca3b7033bd7ed978a3b4ddf3fc"
}
@@ -76,7 +76,8 @@
"bunnative",
"rust",
"ansible",
"csharp"
"csharp",
"oracledb"
]
}
}
@@ -38,7 +38,8 @@
"bunnative",
"rust",
"ansible",
"csharp"
"csharp",
"oracledb"
]
}
}
@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT value AS \"value!: Json<Box<RawValue>>\"\n FROM flow_version WHERE id = $1 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "value!: Json<Box<RawValue>>",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false
]
},
"hash": "10cd655d3d2916721b530c7faa7652fb8fae25383b58f6b9e8dc431b76947315"
}
@@ -1,47 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT * FROM workspace LIMIT $1 OFFSET $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "name",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "owner",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "deleted",
"type_info": "Bool"
},
{
"ordinal": 4,
"name": "premium",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Int8",
"Int8"
]
},
"nullable": [
false,
false,
false,
false,
false
]
},
"hash": "1289e7278d2a289bfaa53f00e0b6dceb195df0fb43a8ac03bc8b35939fc941dd"
}
@@ -1,34 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT workspace.id, workspace.name, usr.username\n FROM workspace, usr WHERE usr.workspace_id = workspace.id AND usr.email = $1 AND deleted = false",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "name",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "username",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false
]
},
"hash": "21cd7cbab7799baf5c381427d9b373c0bb144715eddfe54e3b01f6049d7966a2"
}
@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT flow.versions[array_upper(flow.versions, 1)] AS \"version!: i64\"\n FROM flow WHERE path = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "version!: i64",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "2b2ee874dbd90beec26713d2effdc5d011d9f1091a13761642d064220add7b41"
}
@@ -62,7 +62,8 @@
"bunnative",
"rust",
"ansible",
"csharp"
"csharp",
"oracledb"
]
}
}
@@ -1,28 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT usr.email, usage.executions\n FROM usr\n , LATERAL (\n SELECT COALESCE(SUM(duration_ms + 1000)/1000 , 0)::BIGINT executions\n FROM completed_job\n WHERE workspace_id = $1\n AND job_kind NOT IN ('flow', 'flowpreview')\n AND email = usr.email\n AND now() - '1 week'::interval < created_at \n ) usage\n WHERE workspace_id = $1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "executions",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
null
]
},
"hash": "4ab6d6584029aacdc1e39fcbf39767a49717246179c0652b10aac9078b4e2ae7"
}
@@ -58,7 +58,8 @@
"bunnative",
"rust",
"ansible",
"csharp"
"csharp",
"oracledb"
]
}
}
@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],
@@ -56,7 +56,8 @@
"bunnative",
"rust",
"ansible",
"csharp"
"csharp",
"oracledb"
]
}
}
@@ -58,7 +58,8 @@
"bunnative",
"rust",
"ansible",
"csharp"
"csharp",
"oracledb"
]
}
}
@@ -31,7 +31,8 @@
"bunnative",
"rust",
"ansible",
"csharp"
"csharp",
"oracledb"
]
}
}
@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT value AS \"value!: Json<Box<RawValue>>\"\n FROM flow_version_lite WHERE id = $1 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "value!: Json<Box<RawValue>>",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
true
]
},
"hash": "85085cdc08c262c4750566a0a0b9754017b890ea8c1161a5892b6c29e663ee0e"
}
@@ -58,7 +58,8 @@
"bunnative",
"rust",
"ansible",
"csharp"
"csharp",
"oracledb"
]
}
}
@@ -1,34 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE queue SET running = false, started_at = null\n WHERE last_ping < now() - ($1 || ' seconds')::interval\n AND running = true AND job_kind NOT IN ('flow', 'flowpreview', 'singlescriptflow') AND same_worker = false RETURNING id, workspace_id, last_ping",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Uuid"
},
{
"ordinal": 1,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "last_ping",
"type_info": "Timestamptz"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
true
]
},
"hash": "8c15ea27be82665fe74a4be3182a5049fa937e30b4115caf0ba8d61d34f2089a"
}
@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],
@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT coalesce(flow_version_lite.value, flow_version.value) as \"value!: sqlx::types::Json<Box<RawValue>>\" FROM flow \n LEFT JOIN flow_version\n ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]\n LEFT JOIN flow_version_lite \n ON flow_version_lite.id = flow_version.id\n WHERE flow.path = $1 AND flow.workspace_id = $2 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "value!: sqlx::types::Json<Box<RawValue>>",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
null
]
},
"hash": "94a38b83d59f8c010bd96852bf57163476791448201bdd22a9e463a4116b9582"
}
@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],
@@ -1,71 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT lock AS \"lock: String\", content AS \"code!: String\",\n language AS \"language: Option<ScriptLang>\", envs AS \"envs: Vec<String>\", codebase AS \"codebase: String\" FROM script WHERE hash = $1 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "lock: String",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "code!: String",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "language: Option<ScriptLang>",
"type_info": {
"Custom": {
"name": "script_lang",
"kind": {
"Enum": [
"python3",
"deno",
"go",
"bash",
"postgresql",
"nativets",
"bun",
"mysql",
"bigquery",
"snowflake",
"graphql",
"powershell",
"mssql",
"php",
"bunnative",
"rust",
"ansible"
]
}
}
}
},
{
"ordinal": 3,
"name": "envs: Vec<String>",
"type_info": "VarcharArray"
},
{
"ordinal": 4,
"name": "codebase: String",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
true,
false,
false,
true,
true
]
},
"hash": "9c1a1d0feb79f750c7143fabb0cfa7dab8dd683cd294c27d0549bd8d78ab60a0"
}
@@ -56,7 +56,8 @@
"bunnative",
"rust",
"ansible",
"csharp"
"csharp",
"oracledb"
]
}
}
@@ -1,34 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT lock AS \"lock: String\", code AS \"code: String\", flow AS \"flow: Json<Box<RawValue>>\" FROM flow_node WHERE id = $1 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "lock: String",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "code: String",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "flow: Json<Box<RawValue>>",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
true,
true,
true
]
},
"hash": "9ee2f67042c1bed1e7d13eb7d07e78991e5d7cf01fc7993531dbedf33dac2e0b"
}
@@ -1,25 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n UPDATE queue SET flow_status = JSONB_SET(flow_status, ARRAY['user_states'], JSONB_SET(COALESCE(flow_status->'user_states', '{}'::jsonb), ARRAY[$1], $2))\n WHERE id = $3 AND workspace_id = $4 AND job_kind IN ('flow', 'flowpreview') RETURNING 1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "?column?",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Text",
"Jsonb",
"Uuid",
"Text"
]
},
"nullable": [
null
]
},
"hash": "a21844a8a73420a1f25dac7815e525a27eda3ca6c69d690e8aa437b425c90963"
}
@@ -1,14 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO workspace_settings\n (workspace_id)\n VALUES ($1)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar"
]
},
"nullable": []
},
"hash": "a98b2d68f023f46ab91167d3147416df672c2aed2ba5ab70e98a9da5fa47255a"
}
@@ -1,29 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT\n flow_status AS \"flow_status!: Json<Box<RawValue>>\",\n coalesce(job.raw_flow, queue.raw_flow)->'modules'->(flow_status->'step')::int AS \"module: Json<Box<RawValue>>\"\n FROM queue LEFT JOIN job USING(id, workspace_id) WHERE id = $1 AND workspace_id = $2 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "flow_status!: Json<Box<RawValue>>",
"type_info": "Jsonb"
},
{
"ordinal": 1,
"name": "module: Json<Box<RawValue>>",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Uuid",
"Text"
]
},
"nullable": [
true,
null
]
},
"hash": "b3a9ab85a78edf292ddb0af33f7435bbcc84f63c9079e29d84043f258eba208e"
}
@@ -1,72 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT content AS \"content!: String\",\n lock AS \"lock: String\", language AS \"language: Option<ScriptLang>\", envs AS \"envs: Vec<String>\", codebase AS \"codebase: String\" FROM script WHERE hash = $1 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "content!: String",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "lock: String",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "language: Option<ScriptLang>",
"type_info": {
"Custom": {
"name": "script_lang",
"kind": {
"Enum": [
"python3",
"deno",
"go",
"bash",
"postgresql",
"nativets",
"bun",
"mysql",
"bigquery",
"snowflake",
"graphql",
"powershell",
"mssql",
"php",
"bunnative",
"rust",
"ansible",
"csharp"
]
}
}
}
},
{
"ordinal": 3,
"name": "envs: Vec<String>",
"type_info": "VarcharArray"
},
{
"ordinal": 4,
"name": "codebase: String",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false,
true,
false,
true,
true
]
},
"hash": "b7f2ed32e933b65fa5455928c71f61068ad7dfee8352a76122f5af893ffe6517"
}
@@ -1,67 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT\n queue.job_kind AS \"job_kind: JobKind\",\n queue.script_hash AS \"script_hash: ScriptHash\",\n queue.raw_flow AS \"raw_flow: sqlx::types::Json<Box<RawValue>>\",\n completed_job.parent_job AS \"parent_job: Uuid\"\n FROM queue\n JOIN completed_job ON completed_job.parent_job = queue.id\n WHERE completed_job.id = $1 AND completed_job.workspace_id = $2\n LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "job_kind: JobKind",
"type_info": {
"Custom": {
"name": "job_kind",
"kind": {
"Enum": [
"script",
"preview",
"flow",
"dependencies",
"flowpreview",
"script_hub",
"identity",
"flowdependencies",
"http",
"graphql",
"postgresql",
"noop",
"appdependencies",
"deploymentcallback",
"singlescriptflow",
"flowscript",
"flownode",
"appscript"
]
}
}
}
},
{
"ordinal": 1,
"name": "script_hash: ScriptHash",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "raw_flow: sqlx::types::Json<Box<RawValue>>",
"type_info": "Jsonb"
},
{
"ordinal": 3,
"name": "parent_job: Uuid",
"type_info": "Uuid"
}
],
"parameters": {
"Left": [
"Uuid",
"Text"
]
},
"nullable": [
false,
true,
true,
true
]
},
"hash": "bb6141ad0e93986b38ccdf4d027c486137d4fe79906e22842b711f4a9379b8c8"
}
@@ -39,7 +39,8 @@
"bunnative",
"rust",
"ansible",
"csharp"
"csharp",
"oracledb"
]
}
}
@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],
@@ -1,34 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT lock AS \"lock: String\", code AS \"code: String\", flow::text AS \"flow: Box<str>\" FROM flow_node WHERE id = $1 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "lock: String",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "code: String",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "flow: Box<str>",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
true,
true,
null
]
},
"hash": "c57ed2d91de46d7de88e20b94b7afbafb622528864f2b23c8b7278bd506d967f"
}
@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],
@@ -1,72 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT lock AS \"lock: String\", content AS \"code!: String\",\n language AS \"language: Option<ScriptLang>\", envs AS \"envs: Vec<String>\", codebase AS \"codebase: String\" FROM script WHERE hash = $1 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "lock: String",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "code!: String",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "language: Option<ScriptLang>",
"type_info": {
"Custom": {
"name": "script_lang",
"kind": {
"Enum": [
"python3",
"deno",
"go",
"bash",
"postgresql",
"nativets",
"bun",
"mysql",
"bigquery",
"snowflake",
"graphql",
"powershell",
"mssql",
"php",
"bunnative",
"rust",
"ansible",
"csharp"
]
}
}
}
},
{
"ordinal": 3,
"name": "envs: Vec<String>",
"type_info": "VarcharArray"
},
{
"ordinal": 4,
"name": "codebase: String",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
true,
false,
false,
true,
true
]
},
"hash": "d061e7ca73987036928e17245360fd7f2969aab0c422a00bbdade6a7236aa75d"
}
@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],
@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],
@@ -1,35 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT raw_code, raw_lock, raw_flow AS \"raw_flow: Json<Box<RawValue>>\"\n FROM job WHERE id = $1 AND workspace_id = $2 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "raw_code",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "raw_lock",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "raw_flow: Json<Box<RawValue>>",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Uuid",
"Text"
]
},
"nullable": [
true,
true,
true
]
},
"hash": "da861c06f6448f68069ba3be9b677641c5c6b2c1e4960c74db27dbaffcbd4c40"
}
@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT flow_version.value as \"value!: sqlx::types::Json<Box<RawValue>>\" FROM flow \n LEFT JOIN flow_version\n ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]\n WHERE flow.path = $1 AND flow.workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "value!: sqlx::types::Json<Box<RawValue>>",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false
]
},
"hash": "dc44783a555d41c9df4cd303af686e43bbf89add915bfb09d94e25e8517d65ff"
}
@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT content FROM script WHERE hash = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "content",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Int8",
"Text"
]
},
"nullable": [
false
]
},
"hash": "deb2e4c21d4f9d0b9439780ca6ebee13450609b578c469cb437c5420ab520258"
}
@@ -1,73 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT lock AS \"lock: String\", content AS \"code!: String\",\n language AS \"language: Option<ScriptLang>\", envs AS \"envs: Vec<String>\", codebase AS \"codebase: String\" FROM script WHERE hash = $1 AND workspace_id = $2 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "lock: String",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "code!: String",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "language: Option<ScriptLang>",
"type_info": {
"Custom": {
"name": "script_lang",
"kind": {
"Enum": [
"python3",
"deno",
"go",
"bash",
"postgresql",
"nativets",
"bun",
"mysql",
"bigquery",
"snowflake",
"graphql",
"powershell",
"mssql",
"php",
"bunnative",
"rust",
"ansible",
"csharp"
]
}
}
}
},
{
"ordinal": 3,
"name": "envs: Vec<String>",
"type_info": "VarcharArray"
},
{
"ordinal": 4,
"name": "codebase: String",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Int8",
"Text"
]
},
"nullable": [
true,
false,
false,
true,
true
]
},
"hash": "df52a71d59eb84a2b08133d25f0a8bba7f2b56625fdb7a7c0e10b51377eeb1d4"
}
@@ -53,7 +53,8 @@
"bunnative",
"rust",
"ansible",
"csharp"
"csharp",
"oracledb"
]
}
}
@@ -5,7 +5,7 @@
"columns": [
{
"ordinal": 0,
"name": "?column?",
"name": "bool",
"type_info": "Bool"
}
],
@@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT raw_flow AS \"raw_flow!: Json<Box<sqlx::types::JsonRawValue>>\"\n FROM job WHERE id = $1 AND workspace_id = $2 LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "raw_flow!: Json<Box<sqlx::types::JsonRawValue>>",
"type_info": "Jsonb"
}
],
"parameters": {
"Left": [
"Uuid",
"Text"
]
},
"nullable": [
true
]
},
"hash": "f24303a2386575a750a48a21d1b6cdecd6e64a0415c60ad1495f100c5d85bbe1"
}
@@ -1,46 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT workspace.* FROM workspace, usr WHERE usr.workspace_id = workspace.id AND usr.email = $1 AND deleted = false",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "name",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "owner",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "deleted",
"type_info": "Bool"
},
{
"ordinal": 4,
"name": "premium",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false,
false,
false
]
},
"hash": "f7f2598ac824e9b5719b96b1d1873ad728f7f786c7f447df6cac3e65d5a53bf1"
}
@@ -82,7 +82,8 @@
"bunnative",
"rust",
"ansible",
"csharp"
"csharp",
"oracledb"
]
}
}
@@ -1,20 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT current_setting('session.groups')",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "current_setting",
"type_info": "Text"
}
],
"parameters": {
"Left": []
},
"nullable": [
null
]
},
"hash": "fea19da213230401161cbf2ee1de521040caa6f6cc745afb7de66f4442e76cba"
}
+72
View File
@@ -2103,6 +2103,16 @@ dependencies = [
"syn 2.0.96",
]
[[package]]
name = "darling"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
dependencies = [
"darling_core 0.13.4",
"darling_macro 0.13.4",
]
[[package]]
name = "darling"
version = "0.14.4"
@@ -2123,6 +2133,20 @@ dependencies = [
"darling_macro 0.20.10",
]
[[package]]
name = "darling_core"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn 1.0.109",
]
[[package]]
name = "darling_core"
version = "0.14.4"
@@ -2151,6 +2175,17 @@ dependencies = [
"syn 2.0.96",
]
[[package]]
name = "darling_macro"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
dependencies = [
"darling_core 0.13.4",
"quote",
"syn 1.0.109",
]
[[package]]
name = "darling_macro"
version = "0.14.4"
@@ -5644,6 +5679,15 @@ dependencies = [
"walkdir",
]
[[package]]
name = "odpic-sys"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "920b5474a5128a9f0232df5a0ffc50aaa5b077b29b8b06ab0131985ac82793ed"
dependencies = [
"cc",
]
[[package]]
name = "once_cell"
version = "1.20.2"
@@ -5870,6 +5914,33 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "oracle"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3db40fe6e4df881b683691ade5ef1f7b1afd52aefa115581f7b92855524d7ec0"
dependencies = [
"cc",
"chrono",
"odpic-sys",
"once_cell",
"oracle_procmacro",
"paste",
"rustversion",
]
[[package]]
name = "oracle_procmacro"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad247f3421d57de56a0d0408d3249d4b1048a522be2013656d92f022c3d8af27"
dependencies = [
"darling 0.13.4",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "ordered-float"
version = "2.10.1"
@@ -11211,6 +11282,7 @@ dependencies = [
"object_store",
"once_cell",
"opentelemetry",
"oracle",
"pem 3.0.4",
"postgres-native-tls",
"prometheus",
+2
View File
@@ -69,6 +69,7 @@ dind = ["windmill-worker/dind"]
php = ["windmill-worker/php"]
rust = ["windmill-worker/rust"]
mysql = ["windmill-worker/mysql"]
oracledb = ["windmill-worker/oracledb"]
mssql = ["windmill-worker/mssql"]
bigquery = ["windmill-worker/bigquery"]
websocket = ["windmill-api/websocket"]
@@ -333,3 +334,4 @@ yaml-rust = "0.4.5"
tokio-tungstenite = { version = "0.24.0", features = ["native-tls"] }
tree-sitter = {version = "0.23.0", features = []}
tree-sitter-c-sharp = "0.23.0"
oracle = { version = "0.6.3", features = ["chrono"] }
@@ -0,0 +1 @@
-- Add down migration script here
@@ -0,0 +1,3 @@
-- Add up migration script here
ALTER TYPE SCRIPT_LANG ADD VALUE IF NOT EXISTS 'oracledb';
UPDATE config set config = jsonb_set(config, '{worker_tags}', config->'worker_tags' || '["oracledb"]'::jsonb) where name = 'worker__native' and config @> '{"worker_tags": ["deno", "python3", "go", "bash", "powershell", "dependency", "flow", "hub", "other", "bun", "php", "rust", "ansible", "csharp"]}'::jsonb AND NOT config->'worker_tags' @> '"oracledb"'::jsonb;
+6
View File
@@ -0,0 +1,6 @@
{
"name": "backend",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}
@@ -32,6 +32,22 @@ pub fn parse_mysql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
}
}
pub fn parse_oracledb_sig(code: &str) -> anyhow::Result<MainArgSignature> {
let parsed = parse_oracledb_file(&code)?;
if let Some(x) = parsed {
let args = x;
Ok(MainArgSignature {
star_args: false,
star_kwargs: false,
args,
no_main_func: None,
has_preprocessor: None,
})
} else {
Err(anyhow!("Error parsing sql".to_string()))
}
}
pub fn parse_pgsql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
let parsed = parse_pg_file(&code)?;
if let Some(x) = parsed {
@@ -157,6 +173,61 @@ fn parsed_default(parsed_typ: &Typ, default: String) -> Option<serde_json::Value
_ => Some(json!(default)),
}
}
fn parse_oracledb_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let mut args: Vec<Arg> = vec![];
let mut using_named_args = false;
for cap in RE_ARG_MYSQL_NAMED.captures_iter(code) {
using_named_args = true;
let name = cap.get(1).map(|x| x.as_str().to_string()).unwrap();
let typ = cap
.get(2)
.map(|x| x.as_str().to_string().to_lowercase())
.unwrap();
let default = cap.get(3).map(|x| x.as_str().to_string());
let has_default = default.is_some();
let parsed_typ = parse_oracledb_typ(typ.as_str());
let parsed_default = default.and_then(|x| parsed_default(&parsed_typ, x));
args.push(Arg {
name,
typ: parsed_typ,
default: parsed_default,
otyp: Some(typ),
has_default,
oidx: None,
});
}
if !using_named_args {
// backwards compatibility
for cap in RE_ARG_MYSQL.captures_iter(code) {
let name = cap.get(1).map(|x| x.as_str().to_string()).unwrap();
let typ = cap
.get(2)
.map(|x| x.as_str().to_string().to_lowercase())
.unwrap();
let default = cap.get(3).map(|x| x.as_str().to_string());
let has_default = default.is_some();
let parsed_typ = parse_oracledb_typ(typ.as_str());
let parsed_default = default.and_then(|x| parsed_default(&parsed_typ, x));
args.push(Arg {
name,
typ: parsed_typ,
default: parsed_default,
otyp: Some(typ),
has_default,
oidx: None,
});
}
}
Ok(Some(args))
}
fn parse_mysql_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let mut args: Vec<Arg> = vec![];
@@ -486,6 +557,18 @@ pub fn parse_mysql_typ(typ: &str) -> Typ {
}
}
pub fn parse_oracledb_typ(typ: &str) -> Typ {
match typ {
"varchar" | "nvarchar" | "varchar2" | "char" | "nchar" | "nvarchar2" | "clob" | "blob"
| "nclob" => Typ::Str(None),
"integer" | "int" | "long" | "rowid" | "urowid" => Typ::Int,
"bool" => Typ::Bool,
"number" | "float" | "binary_float" | "binary_double" => Typ::Float,
"date" | "datetime" | "timestamp" | "time" => Typ::Datetime,
_ => Typ::Str(None),
}
}
pub fn parse_pg_typ(typ: &str) -> Typ {
if typ.ends_with("[]") {
let base_typ = parse_pg_typ(typ.strip_suffix("[]").unwrap());
@@ -88,6 +88,12 @@ pub fn parse_mysql(code: &str) -> String {
wrap_sig(windmill_parser_sql::parse_mysql_sig(code))
}
#[cfg(feature = "sql-parser")]
#[wasm_bindgen]
pub fn parse_oracledb(code: &str) -> String {
wrap_sig(windmill_parser_sql::parse_oracledb_sig(code))
}
#[cfg(feature = "sql-parser")]
#[wasm_bindgen]
pub fn parse_bigquery(code: &str) -> String {
+6
View File
@@ -10992,6 +10992,7 @@ components:
bigquery,
snowflake,
mssql,
oracledb,
graphql,
nativets,
bun,
@@ -11097,6 +11098,7 @@ components:
bigquery,
snowflake,
mssql,
oracledb,
graphql,
nativets,
bun,
@@ -11324,6 +11326,7 @@ components:
bigquery,
snowflake,
mssql,
oracledb,
graphql,
nativets,
bun,
@@ -11447,6 +11450,7 @@ components:
bigquery,
snowflake,
mssql,
oracledb,
graphql,
nativets,
bun,
@@ -12003,6 +12007,7 @@ components:
bigquery,
snowflake,
mssql,
oracledb,
graphql,
nativets,
bun,
@@ -13747,6 +13752,7 @@ components:
bigquery,
snowflake,
mssql,
oracledb,
graphql,
nativets,
bun,
@@ -354,6 +354,7 @@ pub(crate) async fn tarball_workspace(
ScriptLang::Rust => "rs",
ScriptLang::Ansible => "playbook.yml",
ScriptLang::CSharp => "cs",
ScriptLang::OracleDB => "oracle.sql",
};
archive
.write_to_archive(&script.content, &format!("{}.{}", script.path, ext))
+2
View File
@@ -42,6 +42,7 @@ pub enum ScriptLang {
Snowflake,
Graphql,
Mssql,
OracleDB,
Php,
Rust,
Ansible,
@@ -65,6 +66,7 @@ impl ScriptLang {
ScriptLang::Snowflake => "snowflake",
ScriptLang::Mssql => "mssql",
ScriptLang::Graphql => "graphql",
ScriptLang::OracleDB => "oracledb",
ScriptLang::Php => "php",
ScriptLang::Rust => "rust",
ScriptLang::Ansible => "ansible",
+2
View File
@@ -25,6 +25,7 @@ otel = ["windmill-common/otel", "dep:opentelemetry"]
dind = ["dep:bollard"]
php = ["dep:windmill-parser-php"]
mysql = ["dep:mysql_async"]
oracledb = ["dep:oracle"]
python = ["dep:windmill-parser-py", "dep:windmill-parser-py-imports"]
csharp = ["dep:windmill-parser-csharp"]
rust = ["dep:windmill-parser-rust"]
@@ -104,6 +105,7 @@ backon.workspace = true
opentelemetry = { workspace = true, optional = true }
bollard = { workspace = true, optional = true }
oracle = { workspace = true, optional = true }
[build-dependencies]
deno_fetch = { workspace = true, optional = true }
+2
View File
@@ -36,6 +36,8 @@ mod rust_executor;
mod worker;
mod worker_flow;
mod worker_lockfiles;
#[cfg(feature = "oracledb")]
mod oracledb_executor;
pub use worker::*;
pub use result_processor::handle_job_error;
@@ -0,0 +1,418 @@
use anyhow::anyhow;
use chrono::Utc;
use std::{collections::HashMap, str::FromStr, sync::Arc};
use windmill_parser::Arg;
use futures::{future::BoxFuture, FutureExt};
use itertools::Itertools;
use oracle::sql_type::{InnerValue, OracleType, ToSql};
use serde::{Deserialize, Serialize};
use serde_json::{json, value::RawValue, Value};
use sqlx::types::Json;
use windmill_common::{
error::{to_anyhow, Error},
jobs::QueuedJob,
worker::to_raw_value,
};
use windmill_parser_sql::{
parse_db_resource, parse_oracledb_sig, parse_sql_blocks, parse_sql_statement_named_params,
};
use windmill_queue::CanceledBy;
use crate::{
common::{check_executor_binary_exists, build_args_map, OccupancyMetrics},
handle_child::run_future_with_polling_update_job_poller,
AuthedClientBackgroundTask,
};
#[derive(Deserialize)]
struct OracleDatabase {
user: String,
password: String,
database: String,
}
lazy_static::lazy_static! {
static ref ORACLE_LIB_DIR: String = std::env::var("ORACLE_LIB_DIR").unwrap_or_else(|_| "/opt/oracle/23/lib".to_string());
}
pub fn do_oracledb_inner<'a>(
query: &str,
params: Vec<(String, Box<dyn ToSql + Send + Sync>)>,
conn: Arc<std::sync::Mutex<oracle::Connection>>,
column_order: Option<&'a mut Option<Vec<String>>>,
skip_collect: bool,
) -> windmill_common::error::Result<BoxFuture<'a, anyhow::Result<Box<RawValue>>>> {
let qw = query.trim_end_matches(';').to_string();
let result_f = async move {
let param_names = parse_sql_statement_named_params(&qw, ':')
.into_iter()
.map(|x| x.into_bytes())
.collect_vec();
if skip_collect {
tokio::task::spawn_blocking(move || {
let c = conn.lock()?;
let params2: Vec<(&str, &dyn ToSql)> = params
.iter()
.filter(|(k, _)| param_names.contains(&k.clone().into_bytes()))
.map(|(key, val)| (key.as_str(), &**val as &dyn ToSql))
.collect();
let mut stmt = c.statement(&qw).build()?;
match stmt.statement_type() {
oracle::StatementType::Select => {
stmt.query_named(&params2)?;
}
_ => {
stmt.execute_named(&params2)?;
c.commit()?;
}
}
oracle::Result::Ok(())
})
.await
.map_err(to_anyhow)?
.map_err(to_anyhow)?;
Ok(to_raw_value(&Value::Array(vec![])))
} else {
let rows = tokio::task::spawn_blocking(move || {
let params2: Vec<(&str, &dyn ToSql)> = params
.iter()
.filter(|(k, _)| param_names.contains(&k.clone().into_bytes()))
.map(|(key, val)| (key.as_str(), &**val as &dyn ToSql))
.collect();
let c = conn.lock()?;
let mut stmt = c.statement(&qw).build()?;
let rows = match stmt.statement_type() {
oracle::StatementType::Select => {
let result_rows = stmt.query_named(&params2)?;
let rows: Vec<oracle::Row> =
result_rows.into_iter().filter_map(Result::ok).collect_vec();
rows
}
_ => {
stmt.execute_named(&params2)?;
c.commit()?;
vec![]
}
};
oracle::Result::Ok(rows)
})
.await
.map_err(to_anyhow)?
.map_err(to_anyhow)?;
if let Some(column_order) = column_order {
*column_order = Some(
rows.first()
.map(|x| {
x.column_info()
.iter()
.map(|x| x.name().to_string())
.collect::<Vec<String>>()
})
.unwrap_or_default(),
);
}
Ok(to_raw_value(
&rows
.into_iter()
.map(|x| convert_row_to_value(x))
.collect::<Vec<serde_json::Value>>(),
))
}
};
Ok(result_f.boxed())
}
fn convert_row_to_value(row: oracle::Row) -> serde_json::Value {
let mut map = serde_json::Map::new();
for (key, value) in row.column_info().iter().zip(row.sql_values()) {
map.insert(
key.name().to_string(),
convert_oracledb_value_to_json(value, key.oracle_type()),
);
}
serde_json::Value::Object(map)
}
fn conversion_error<T: Serialize>(r: Result<T, oracle::Error>) -> serde_json::Value {
match r {
Ok(v) => json!(v),
Err(e) => json!(format!("Error converting value: {:?}", e)),
}
}
fn convert_oracledb_value_to_json(v: &oracle::SqlValue, c: &OracleType) -> serde_json::Value {
match v.as_inner_value() {
Err(_) => serde_json::Value::Null,
Ok(iv) => match iv {
InnerValue::Int64(n) => json!(n),
InnerValue::UInt64(n) => json!(n),
InnerValue::Float(n) => json!(n),
InnerValue::Double(n) => json!(n),
InnerValue::Char(n) => json!(String::from_utf8_lossy(n)),
InnerValue::Number(n) => json!(n),
InnerValue::Boolean(n) => json!(n),
InnerValue::Timestamp(_) => conversion_error(v.get::<String>()),
InnerValue::IntervalDS(_) => conversion_error(v.get::<String>()),
InnerValue::IntervalYM(_) => conversion_error(v.get::<String>()),
InnerValue::Clob(_) => json!("Unsupported type Clob"),
InnerValue::Blob(_) => json!("Unsupported type Blob"),
InnerValue::Rowid(_) => json!("Unsupported type Rowid"),
InnerValue::Object(_) => json!("Unsuppported type Object"),
InnerValue::Stmt(_) => json!("Unsupported type Stmt"),
InnerValue::Raw(b) => {
match c {
OracleType::Varchar2(_)
| OracleType::NVarchar2(_)
| OracleType::Char(_)
| OracleType::NChar(_)
| OracleType::Json
| OracleType::Xml => {
let s = String::from_utf8_lossy(b);
json!(s)
}
// OracleType::Rowid => todo!(),
// OracleType::Raw(_) => todo!(),
//
// OracleType::BinaryFloat => todo!(),
// OracleType::BinaryDouble => todo!(),
// OracleType::Number(_, _) => todo!(),
// OracleType::Float(_) => todo!(),
// OracleType::Int64 => todo!(),
// OracleType::UInt64 => todo!(),
// OracleType::Long => todo!(),
// OracleType::LongRaw => todo!(),
OracleType::Date
| OracleType::Timestamp(_)
| OracleType::TimestampTZ(_)
| OracleType::TimestampLTZ(_)
| OracleType::IntervalDS(_, _)
| OracleType::IntervalYM(_) => conversion_error(v.get::<String>()),
// OracleType::CLOB => todo!(),
// OracleType::NCLOB => todo!(),
// OracleType::BLOB => todo!(),
// OracleType::BFILE => todo!(),
// OracleType::RefCursor => todo!(),
// OracleType::Boolean => todo!(),
// OracleType::Object(_) => todo!(),
_ => json!(format!("Unsupported type: {c:?}")),
}
}
_ => json!(format!("Unsupported type: {c:?}")),
},
}
}
fn get_statement_values(
sig: Vec<Arg>,
job_args: Option<&Json<HashMap<String, Box<RawValue>>>>,
) -> (Vec<(String, Box<dyn ToSql + Send + Sync>)>, Vec<String>) {
let mut statement_values = vec![];
let mut errors = vec![];
for arg in &sig {
let arg_t = arg.otyp.clone().unwrap_or_else(|| "text".to_string());
let arg_n = arg.name.clone();
let oracle_v: Box<dyn ToSql + Send + Sync> = match job_args
.and_then(|x| {
x.get(arg.name.as_str())
.map(|x| serde_json::from_str::<serde_json::Value>(x.get()).ok())
})
.flatten()
.unwrap_or_else(|| json!(null))
{
// Value::Null => todo!(),
Value::Bool(b) => Box::new(b),
Value::String(s)
if arg_t == "timestamp"
|| arg_t == "datetime"
|| arg_t == "date"
|| arg_t == "time" =>
{
if let Ok(d) = chrono::DateTime::<Utc>::from_str(s.as_str()) {
Box::new(d)
} else {
Box::new(s)
}
}
Value::String(s) => Box::new(s),
Value::Number(n)
if n.is_i64()
&& (arg_t == "int"
|| arg_t == "integer"
|| arg_t == "smallint"
|| arg_t == "bigint") =>
{
Box::new(n.as_i64().unwrap())
}
Value::Number(n) if n.is_f64() && arg_t == "float" => {
Box::new(n.as_f64().unwrap() as f32)
}
Value::Number(n) if n.is_i64() && arg_t == "float" => {
Box::new(n.as_i64().unwrap() as f32)
}
Value::Number(n) if n.is_u64() && arg_t == "uint" => Box::new(n.as_u64().unwrap()),
Value::Number(n)
if n.is_f64() && (arg_t == "real" || arg_t == "dec" || arg_t == "fixed") =>
{
Box::new(n.as_f64().unwrap())
}
Value::Number(n)
if n.is_i64() && (arg_t == "real" || arg_t == "dec" || arg_t == "fixed") =>
{
Box::new(n.as_i64().unwrap() as f64)
}
value @ _ => {
errors.push(format!(
"Unsupported type in query: {value:?} and signature {arg_t:?} for {arg_n}"
));
continue;
}
};
statement_values.push((arg_n, oracle_v));
}
(statement_values, errors)
}
pub async fn do_oracledb(
job: &QueuedJob,
client: &AuthedClientBackgroundTask,
query: &str,
db: &sqlx::Pool<sqlx::Postgres>,
mem_peak: &mut i32,
canceled_by: &mut Option<CanceledBy>,
worker_name: &str,
column_order: &mut Option<Vec<String>>,
occupancy_metrics: &mut OccupancyMetrics,
) -> windmill_common::error::Result<Box<RawValue>> {
check_executor_binary_exists("the Oracle client lib", ORACLE_LIB_DIR.as_str(), "Oracle Database")?;
let args = build_args_map(job, client, db).await?.map(Json);
let job_args = if args.is_some() {
args.as_ref()
} else {
job.args.as_ref()
};
let inline_db_res_path = parse_db_resource(&query);
let db_arg = if let Some(inline_db_res_path) = inline_db_res_path {
let val = client
.get_authed()
.await
.get_resource_value_interpolated::<serde_json::Value>(
&inline_db_res_path,
Some(job.id.to_string()),
)
.await?;
let as_raw = serde_json::from_value(val).map_err(|e| {
Error::InternalErr(format!("Error while parsing inline resource: {e:#}"))
})?;
Some(as_raw)
} else {
job_args.and_then(|x| x.get("database").cloned())
};
let database = if let Some(db) = db_arg {
serde_json::from_str::<OracleDatabase>(db.get())
.map_err(|e| Error::ExecutionErr(e.to_string()))?
} else {
return Err(Error::BadRequest("Missing database argument".to_string()));
};
let annotations = windmill_common::worker::SqlAnnotations::parse(query);
let sig = parse_oracledb_sig(query)
.map_err(|x| Error::ExecutionErr(x.to_string()))?
.args;
let (statement_values, errors) = get_statement_values(sig.clone(), job_args);
if !errors.is_empty() {
return Err(Error::ExecutionErr(errors.join("\n")));
}
if !oracle::InitParams::is_initialized() {
let _ = oracle::InitParams::new()
.oracle_client_lib_dir(ORACLE_LIB_DIR.as_str())
.map_err(|e| anyhow!("Failed to initialize oracle client: {e}"))?
.init();
}
let conn = tokio::task::spawn_blocking(|| {
oracle::Connection::connect(database.user, database.password, database.database)
.map_err(|e| Error::ExecutionErr(e.to_string()))
})
.await
.map_err(to_anyhow)??;
let conn_a = Arc::new(std::sync::Mutex::new(conn));
let queries = parse_sql_blocks(query);
let result_f = if queries.len() > 1 {
let f = async {
let mut res: Vec<Box<RawValue>> = vec![];
for (i, q) in queries.iter().enumerate() {
let (vals, _) = get_statement_values(sig.clone(), job_args);
let r = do_oracledb_inner(
q,
vals,
conn_a.clone(),
None,
annotations.return_last_result && i < queries.len() - 1,
)?
.await?;
res.push(r);
}
if annotations.return_last_result && res.len() > 0 {
Ok(res.pop().unwrap())
} else {
Ok(to_raw_value(&res))
}
};
f.boxed()
} else {
do_oracledb_inner(query, statement_values, conn_a, Some(column_order), false)?
};
let result = run_future_with_polling_update_job_poller(
job.id,
job.timeout,
db,
mem_peak,
canceled_by,
result_f,
worker_name,
&job.workspace_id,
&mut Some(occupancy_metrics),
Box::pin(futures::stream::once(async { 0 })),
)
.await?;
let raw_result = windmill_common::worker::to_raw_value(&json!(result));
*mem_peak = (raw_result.get().len() / 1000) as i32;
return Ok(raw_result);
}
+37 -17
View File
@@ -90,26 +90,12 @@ use tokio::{
use rand::Rng;
use crate::{
bash_executor::{handle_bash_job, handle_powershell_job},
bun_executor::handle_bun_job,
common::{
bash_executor::{handle_bash_job, handle_powershell_job}, bun_executor::handle_bun_job, common::{
build_args_map, cached_result_path, get_cached_resource_value_if_valid,
get_reserved_variables, update_worker_ping_for_failed_init_script, OccupancyMetrics,
},
csharp_executor::handle_csharp_job,
deno_executor::handle_deno_job,
go_executor::handle_go_job,
graphql_executor::do_graphql,
handle_child::SLOW_LOGS,
handle_job_error,
job_logger::NO_LOGS_AT_ALL,
js_eval::{eval_fetch_timeout, transpile_ts},
pg_executor::do_postgresql,
result_processor::{process_result, start_background_processor},
worker_flow::{handle_flow, update_flow_status_in_progress},
worker_lockfiles::{
}, csharp_executor::handle_csharp_job, deno_executor::handle_deno_job, go_executor::handle_go_job, graphql_executor::do_graphql, handle_child::SLOW_LOGS, handle_job_error, job_logger::NO_LOGS_AT_ALL, js_eval::{eval_fetch_timeout, transpile_ts}, pg_executor::do_postgresql, result_processor::{process_result, start_background_processor}, worker_flow::{handle_flow, update_flow_status_in_progress}, worker_lockfiles::{
handle_app_dependency_job, handle_dependency_job, handle_flow_dependency_job,
},
}
};
#[cfg(feature = "rust")]
@@ -127,6 +113,9 @@ use crate::ansible_executor::handle_ansible_job;
#[cfg(feature = "mysql")]
use crate::mysql_executor::do_mysql;
#[cfg(feature = "oracledb")]
use crate::oracledb_executor::do_oracledb;
use backon::ConstantBuilder;
use backon::{BackoffBuilder, Retryable};
@@ -2480,6 +2469,37 @@ async fn handle_code_execution_job(
)
.await;
}
} else if language == Some(ScriptLang::OracleDB) {
#[cfg(not(feature = "enterprise"))]
{
return Err(Error::ExecutionErr(
"Oracle DB is only available with an enterprise license".to_string(),
));
}
#[allow(unreachable_code)]
#[cfg(not(feature = "oracledb"))]
{
return Err(Error::InternalErr(
"Oracle DB requires the oracledb feature to be enabled".to_string(),
));
}
#[cfg(all(feature = "enterprise", feature = "oracledb"))]
{
return do_oracledb(
job,
&client,
&code,
db,
mem_peak,
canceled_by,
worker_name,
column_order,
occupancy_metrics,
)
.await;
}
} else if language == Some(ScriptLang::Graphql) {
return do_graphql(
job,
@@ -1946,6 +1946,7 @@ async fn capture_dependency_job(
ScriptLang::Snowflake => Ok("".to_owned()),
ScriptLang::Mssql => Ok("".to_owned()),
ScriptLang::Graphql => Ok("".to_owned()),
ScriptLang::OracleDB => Ok("".to_owned()),
ScriptLang::Bash => Ok("".to_owned()),
ScriptLang::Powershell => Ok("".to_owned()),
ScriptLang::Nativets => Ok("".to_owned()),
+3
View File
@@ -8,3 +8,6 @@ RUN pip3 install ansible
COPY --from=bitnami/dotnet-sdk:9.0.101-debian-12-r0 /opt/bitnami/dotnet-sdk /opt/dotnet-sdk
RUN ln -s /opt/dotnet-sdk/bin/dotnet /usr/bin/dotnet
ENV DOTNET_ROOT="/opt/dotnet-sdk/bin"
# Oracle DB Client
COPY --from=ghcr.io/oracle/oraclelinux9-instantclient:23 /usr/lib/oracle/23/client64/lib /opt/oracle/23/lib
+3
View File
@@ -34,4 +34,7 @@ COPY --from=bitnami/dotnet-sdk:9.0.101-debian-12-r0 /opt/bitnami/dotnet-sdk /opt
RUN ln -s /opt/dotnet-sdk/bin/dotnet /usr/bin/dotnet
ENV DOTNET_ROOT="/opt/dotnet-sdk/bin"
# Oracle DB Client
COPY --from=ghcr.io/oracle/oraclelinux9-instantclient:23 /usr/lib/oracle/23/client64/lib /opt/oracle/23/lib
COPY --from=nsjail /nsjail/nsjail /bin/nsjail
+4 -4
View File
@@ -75,7 +75,7 @@
"windmill-parser-wasm-go": "^1.429.0",
"windmill-parser-wasm-php": "^1.429.0",
"windmill-parser-wasm-py": "^1.429.0",
"windmill-parser-wasm-regex": "^1.435.2",
"windmill-parser-wasm-regex": "^1.439.0",
"windmill-parser-wasm-rust": "^1.429.0",
"windmill-parser-wasm-ts": "^1.429.0",
"windmill-parser-wasm-yaml": "^1.429.0",
@@ -13991,9 +13991,9 @@
"integrity": "sha512-cqc+tblQVHVrc8wNA4esVYD1Dv59XQJ4mHXFFPa8Lx5UjXv7FO/0VQxyQuRyXaP/V6J6DDy0oeN107eFNLxrBg=="
},
"node_modules/windmill-parser-wasm-regex": {
"version": "1.435.2",
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-regex/-/windmill-parser-wasm-regex-1.435.2.tgz",
"integrity": "sha512-kQShKPNjXok8jV7b7llfvKhypHfqePwiKgTHm8MlzBt4BZC87xrQ7TH0eS9h7OSK9Q98H9fIiUr2AbN8zTCraA=="
"version": "1.439.0",
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-regex/-/windmill-parser-wasm-regex-1.439.0.tgz",
"integrity": "sha512-v7vcEOWurGbqvoTdtQ8wauyUYeuQExRCmr7phPtwUwD+1cNbqDrS11kM8p2Na3DXjL9RqGAGjd6uRjNSccZjjQ=="
},
"node_modules/windmill-parser-wasm-rust": {
"version": "1.429.0",
+1 -1
View File
@@ -150,7 +150,7 @@
"windmill-parser-wasm-go": "^1.429.0",
"windmill-parser-wasm-php": "^1.429.0",
"windmill-parser-wasm-py": "^1.429.0",
"windmill-parser-wasm-regex": "^1.435.2",
"windmill-parser-wasm-regex": "^1.439.0",
"windmill-parser-wasm-rust": "^1.429.0",
"windmill-parser-wasm-ts": "^1.429.0",
"windmill-parser-wasm-yaml": "^1.429.0",
@@ -41,7 +41,8 @@
'bigquery',
'snowflake',
'mssql',
'graphql'
'graphql',
'oracledb'
]
let filter = ''
@@ -163,6 +163,7 @@
BIGQUERY_TYPES,
MSSQL_TYPES,
MYSQL_TYPES,
ORACLEDB_TYPES,
POSTGRES_TYPES,
SNOWFLAKE_TYPES
} from '$lib/consts'
@@ -462,6 +463,8 @@
? BIGQUERY_TYPES
: scriptLang === 'mssql'
? MSSQL_TYPES
: scriptLang === 'oracledb'
? ORACLEDB_TYPES
: []
).map((t) => ({
label: t,
@@ -50,6 +50,8 @@
return sql
case 'bigquery':
return sql
case 'oracledb':
return sql
case 'powershell':
return powershell
case 'php':
@@ -158,7 +158,7 @@
showCaptureHint: showCaptureHint
})
const enterpriseLangs = ['bigquery', 'snowflake', 'mssql']
const enterpriseLangs = ['bigquery', 'snowflake', 'mssql', 'oracledb']
export function setCode(code: string): void {
editor?.setCode(code)
@@ -50,6 +50,11 @@
lang: 'mssql',
argName: 'database'
},
oracledb: {
code: `SELECT 1`,
lang: 'oracledb',
argName: 'database'
},
s3: {
code: `
import * as wmill from "windmill-client"
@@ -131,7 +131,8 @@
'graphql',
'snowflake',
'mssql',
'bigquery'
'bigquery',
'oracledb'
]
const aws_env_vars_preset = [
@@ -23,7 +23,8 @@ export const DEFAULT_CODES: Partial<
| 'postgresql'
| 'snowflake'
| 'mssql'
| 'bigquery',
| 'bigquery'
| 'oracledb',
string
>
>
@@ -73,7 +73,7 @@
<IconSelectInput bind:value={componentInput.value} />
{:else if fieldType === 'tab-select'}
<TabSelectInput bind:componentInput />
{:else if fieldType === 'resource' && subFieldType && ['mysql', 'postgres', 'ms_sql_server', 'snowflake', 'snowflake_oauth', 'bigquery'].includes(subFieldType)}
{:else if fieldType === 'resource' && subFieldType && ['mysql', 'postgres', 'ms_sql_server', 'snowflake', 'snowflake_oauth', 'bigquery', 'oracledb'].includes(subFieldType)}
<ResourcePicker
initialValue={componentInput.value?.split('$res:')?.[1] || ''}
on:change={(e) => {
@@ -40,6 +40,7 @@ export type InputType =
| 'snowflake'
| 'snowflake_oauth'
| 'bigquery'
| 'oracledb'
| 'app-path'
// Connection to an output of another component
@@ -227,6 +228,7 @@ export type AppInput =
| AppInputSpec<'resource', string, 'snowflake'>
| AppInputSpec<'resource', string, 'snowflake_oauth'>
| AppInputSpec<'resource', string, 'bigquery'>
| AppInputSpec<'resource', string, 'oracledb'>
| AppInputSpec<'array', object[], 'number-tuple'>
| AppInputSpec<'app-path', string>
@@ -9,6 +9,7 @@
import RestIcon from '$lib/components/icons/RestIcon.svelte'
import PowershellIcon from '$lib/components/icons/PowershellIcon.svelte'
import BigQueryIcon from '$lib/components/icons/BigQueryIcon.svelte'
import OracleDBIcon from '$lib/components/icons/OracleDBIcon.svelte'
import SnowflakeIcon from '$lib/components/icons/SnowflakeIcon.svelte'
import GraphqlIcon from '$lib/components/icons/GraphqlIcon.svelte'
import MSSqlServerIcon from '$lib/components/icons/MSSqlServerIcon.svelte'
@@ -45,6 +46,7 @@
graphql: 'GraphQL',
postgresql: 'Postgresql',
bigquery: 'BigQuery',
oracledb: 'Oracle Database',
snowflake: 'Snowflake',
mysql: 'MySQL',
mssql: 'MS SQL Server',
@@ -69,6 +71,7 @@
pgsql: PostgresIcon,
mysql: MySQLIcon,
bigquery: BigQueryIcon,
oracledb: OracleDBIcon,
snowflake: SnowflakeIcon,
mssql: MSSqlServerIcon,
javascript: JavaScript,
@@ -471,7 +471,7 @@
</div>
{/if}
{#if ['postgresql', 'mysql', 'snowflake', 'bigquery', 'mssql', 'graphql'].includes(lang ?? '') && dbSchema?.lang === lang}
{#if ['postgresql', 'mysql', 'snowflake', 'bigquery', 'mssql', 'graphql, oracledb'].includes(lang ?? '') && dbSchema?.lang === lang}
<div class="flex flex-row items-center justify-between gap-2 w-96">
<div class="flex flex-row items-center gap-1">
<p class="text-xs text-secondary">
+1 -1
View File
@@ -410,7 +410,7 @@ function addDBSChema(scriptOptions: CopilotOptions, prompt: string) {
const { dbSchema, language } = scriptOptions
if (
dbSchema &&
['postgresql', 'mysql', 'snowflake', 'bigquery', 'mssql', 'graphql'].includes(language) && // make sure we are using a SQL/query language
['postgresql', 'mysql', 'snowflake', 'bigquery', 'mssql', 'graphql', 'oracledb'].includes(language) && // make sure we are using a SQL/query language
language === dbSchema.lang // make sure we are using the same language as the schema
) {
let { stringified } = dbSchema
@@ -125,7 +125,7 @@
let inlineScripts: [string, SupportedLanguage | 'docker'][] = []
const enterpriseLangs = ['bigquery', 'snowflake', 'mssql']
const enterpriseLangs = ['bigquery', 'snowflake', 'mssql', 'oracledb']
function computeInlineScriptChoices(
funcDesc: string,
@@ -11,7 +11,7 @@
export let id: string | undefined = undefined
const enterpriseLangs = ['bigquery', 'snowflake', 'mssql']
const enterpriseLangs = ['bigquery', 'snowflake', 'mssql', 'oracledb']
</script>
<Popover disablePopup={!enterpriseLangs.includes(lang || '') || !!$enterpriseLicense}>
@@ -0,0 +1,15 @@
<script>
export let height = '24px'
export let width = '24px'
</script>
<svg
{height}
{width}
id="svg3430"
version="1.1"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path fill="#C74634" fill-rule="evenodd" d="M7.957359,18.9123664 C4.11670252,18.9123664 1,15.803458 1,11.9617373 C1,8.12000773 4.11670252,5 7.957359,5 L16.0437948,5 C19.8855156,5 23,8.12000773 23,11.9617373 C23,15.803458 19.8855156,18.9123664 16.0437948,18.9123664 L7.957359,18.9123664 L7.957359,18.9123664 Z M15.8639176,16.4585488 C18.352201,16.4585488 20.3674397,14.448858 20.3674397,11.9617373 C20.3674397,9.47460595 18.352201,7.45381934 15.8639176,7.45381934 L8.1360824,7.45381934 C5.64895285,7.45381934 3.63255855,9.47460595 3.63255855,11.9617373 C3.63255855,14.448858 5.64895285,16.4585488 8.1360824,16.4585488 L15.8639176,16.4585488 L15.8639176,16.4585488 Z"/>
</svg>
@@ -63,6 +63,7 @@ import RssIcon from './RssIcon.svelte'
import ShopifyIcon from './ShopifyIcon.svelte'
import TypeformIcon from './TypeformIcon.svelte'
import BigQueryIcon from './BigQueryIcon.svelte'
import OracleDBIcon from './OracleDBIcon.svelte'
import GraphqlIcon from './GraphqlIcon.svelte'
import NocoDbIcon from './NocoDbIcon.svelte'
import AzureIcon from './AzureIcon.svelte'
@@ -164,6 +165,7 @@ export const APP_TO_ICON_COMPONENT = {
shopify: ShopifyIcon,
typeform: TypeformIcon,
bigquery: BigQueryIcon,
oracledb: OracleDBIcon,
graphql: GraphqlIcon,
nocodb: NocoDbIcon,
azure: AzureIcon,
@@ -260,6 +262,7 @@ export {
ShopifyIcon,
TypeformIcon,
BigQueryIcon,
OracleDBIcon,
GraphqlIcon,
NocoDbIcon,
AzureIcon,
+18
View File
@@ -115,6 +115,24 @@ export const MYSQL_TYPES = [
'time'
]
export const ORACLEDB_TYPES = [
'varchar2',
'char',
'nchar',
'nvarchar2',
'clob',
'nclob',
'blob',
'number',
'float',
'binary_float',
'binary_double',
'date',
'timestamp',
'long',
'rowid',
'urowid'
]
export const BIGQUERY_TYPES = [
'string',
'string[]',
+10 -1
View File
@@ -8,6 +8,7 @@ import initTsParser, { parse_deno, parse_outputs } from 'windmill-parser-wasm-ts
import initRegexParsers, {
parse_sql,
parse_mysql,
parse_oracledb,
parse_bigquery,
parse_snowflake,
parse_graphql,
@@ -85,7 +86,7 @@ export async function inferArgs(
}
let inlineDBResource: string | undefined = undefined
if (['postgresql', 'mysql', 'bigquery', 'snowflake', 'mssql'].includes(language ?? '')) {
if (['postgresql', 'mysql', 'bigquery', 'snowflake', 'mssql', 'oracledb'].includes(language ?? '')) {
await initWasmRegex()
inlineDBResource = parse_db_resource(code)
}
@@ -128,6 +129,14 @@ export async function inferArgs(
...inferedSchema.args
]
}
} else if (language == 'oracledb') {
inferedSchema = JSON.parse(parse_oracledb(code))
if (inlineDBResource === undefined) {
inferedSchema.args = [
{ name: 'database', typ: { resource: 'oracledb' } },
...inferedSchema.args
]
}
} else if (language == 'snowflake') {
inferedSchema = JSON.parse(parse_snowflake(code))
if (inlineDBResource === undefined) {
+13
View File
@@ -267,6 +267,14 @@ INSERT INTO \`demodb.demo\` VALUES (@name1, @name2, @name3);
UPDATE \`demodb.demo\` SET col2 = @name4 WHERE col2 = @name2;
`
const ORACLEDB_INIT_CODE = `-- to pin the database use '-- database f/your/path'
-- :name1 (text) = default arg
-- :name2 (int)
-- :name3 (int)
INSERT INTO demo VALUES (:name1, :name2);
UPDATE demo SET col2 = :name3 WHERE col2 = :name2;
`
const SNOWFLAKE_INIT_CODE = `-- to pin the database use '-- database f/your/path'
-- ? name1 (varchar) = default arg
-- ? name2 (int)
@@ -838,6 +846,9 @@ export const INITIAL_CODE = {
graphql: {
script: GRAPHQL_INIT_CODE
},
oracledb: {
script: ORACLEDB_INIT_CODE
},
php: {
script: PHP_INIT_CODE
},
@@ -942,6 +953,8 @@ export function initialCode(
return INITIAL_CODE.mysql.script
} else if (language == 'bigquery') {
return INITIAL_CODE.bigquery.script
} else if (language == 'oracledb') {
return INITIAL_CODE.oracledb.script
} else if (language == 'snowflake') {
return INITIAL_CODE.snowflake.script
} else if (language == 'mssql') {
+3
View File
@@ -21,6 +21,8 @@ export function scriptLangToEditorLang(
return 'sql'
} else if (lang == 'bigquery') {
return 'sql'
} else if (lang == 'oracledb') {
return 'sql'
} else if (lang == 'snowflake') {
return 'sql'
} else if (lang == 'mssql') {
@@ -111,6 +113,7 @@ const scriptLanguagesArray: [SupportedLanguage | 'docker' | 'bunnative', string]
['postgresql', 'PostgreSQL'],
['mysql', 'MySQL'],
['bigquery', 'BigQuery'],
['oracledb', 'Oracle Database'],
['snowflake', 'Snowflake'],
['mssql', 'MS SQL Server'],
['graphql', 'GraphQL'],
+1 -1
View File
@@ -110,7 +110,7 @@ type SQLBaseSchema = {
}
export interface SQLSchema {
lang: 'mysql' | 'bigquery' | 'postgresql' | 'snowflake' | 'mssql'
lang: 'mysql' | 'bigquery' | 'postgresql' | 'snowflake' | 'mssql' | 'oracledb'
schema: SQLBaseSchema
publicOnly: boolean | undefined
stringified: string
@@ -732,7 +732,7 @@
<Badge color="blue">priority: {job.priority}</Badge>
</div>
{/if}
{#if job.tag && !['deno', 'python3', 'flow', 'other', 'go', 'postgresql', 'mysql', 'bigquery', 'snowflake', 'mssql', 'graphql', 'nativets', 'bash', 'powershell', 'php', 'rust', 'other', 'ansible', 'csharp', 'dependency'].includes(job.tag)}
{#if job.tag && !['deno', 'python3', 'flow', 'other', 'go', 'postgresql', 'mysql', 'bigquery', 'snowflake', 'mssql', 'graphql', 'oracledb', 'nativets', 'bash', 'powershell', 'php', 'rust', 'other', 'ansible', 'csharp', 'dependency'].includes(job.tag)}
<div>
<Badge color="indigo">Tag: {job.tag}</Badge>
</div>