diff --git a/backend/windmill-api/src/workspaces_export.rs b/backend/windmill-api/src/workspaces_export.rs index 159a9962a0..578e170c1c 100644 --- a/backend/windmill-api/src/workspaces_export.rs +++ b/backend/windmill-api/src/workspaces_export.rs @@ -354,7 +354,7 @@ pub(crate) async fn tarball_workspace( ScriptLang::Rust => "rs", ScriptLang::Ansible => "playbook.yml", ScriptLang::CSharp => "cs", - ScriptLang::OracleDB => "oracle.sql", + ScriptLang::OracleDB => "odb.sql", }; archive .write_to_archive(&script.content, &format!("{}.{}", script.path, ext)) diff --git a/backend/windmill-worker/src/csharp_executor.rs b/backend/windmill-worker/src/csharp_executor.rs index 7d36e1fef0..61f53ab65a 100644 --- a/backend/windmill-worker/src/csharp_executor.rs +++ b/backend/windmill-worker/src/csharp_executor.rs @@ -235,11 +235,15 @@ fn gen_cs_proj( let script_call = match (sig_meta.is_async, sig_meta.returns_void) { (true, true) => format!( r#" - {class_name}.Main({spread}).Wait();"# + {class_name}.Main({spread}).Wait(); + File.WriteAllText("result.json", "null"); + "# ), (false, true) => format!( r#" - {class_name}.Main({spread});"# + {class_name}.Main({spread}); + File.WriteAllText("result.json", "null"); + "# ), (false, false) => format!( @@ -282,9 +286,15 @@ namespace WindmillScriptCSharpInternal {{ using FileStream fs = File.OpenRead("args.json"); Args parsedArgs = JsonSerializer.Deserialize(fs); - File.WriteAllText("result.json", "null"); - - {script_call} + try + {{ + {script_call} + }} + catch (Exception ex) + {{ + Console.Error.WriteLine("Unhandeled Exception: " + ex.ToString()); + Environment.Exit(1); + }} }} }} }} @@ -330,6 +340,7 @@ async fn build_cs_proj( job_dir, "--no-self-contained", "-p:PublishSingleFile=true", + "-p:IncludeNativeLibrariesForSelfExtract=true", ]) .stdout(Stdio::piped()) .stderr(Stdio::piped()); diff --git a/cli/bootstrap/script_bootstrap.ts b/cli/bootstrap/script_bootstrap.ts index 6871f5ab77..bd609ae9ed 100644 --- a/cli/bootstrap/script_bootstrap.ts +++ b/cli/bootstrap/script_bootstrap.ts @@ -85,6 +85,15 @@ function main() { } `, + csharp: `class Script +{ + public static void Main() + { + Console.WriteLine("Hello World"); + } +} + `, + rust: `fn main() -> Result<(), String> { println!("Hello World"); Ok(()) diff --git a/cli/metadata.ts b/cli/metadata.ts index 92ac7dfc4b..32cd556f76 100644 --- a/cli/metadata.ts +++ b/cli/metadata.ts @@ -26,8 +26,10 @@ import { parse_powershell, parse_python, parse_rust, + parse_csharp, parse_snowflake, parse_sql, + parse_oracledb, } from "./wasm/windmill_parser_wasm.generated.js"; import { Workspace } from "./workspace.ts"; import { SchemaProperty } from "./bootstrap/common.ts"; @@ -468,6 +470,12 @@ export function inferSchema( { name: "database", typ: { resource: "bigquery" } }, ...inferedSchema.args, ]; + } else if (language === "oracledb") { + inferedSchema = JSON.parse(parse_oracledb(content)); + inferedSchema.args = [ + { name: "database", typ: { resource: "oracledb" } }, + ...inferedSchema.args, + ]; } else if (language === "snowflake") { inferedSchema = JSON.parse(parse_snowflake(content)); inferedSchema.args = [ @@ -500,6 +508,8 @@ export function inferSchema( inferedSchema = JSON.parse(parse_php(content)); } else if (language === "rust") { inferedSchema = JSON.parse(parse_rust(content)); + } else if (language === "csharp") { + inferedSchema = JSON.parse(parse_csharp(content)); } else if (language === "ansible") { inferedSchema = JSON.parse(parse_ansible(content)); } else { diff --git a/cli/script.ts b/cli/script.ts index 0ee33553ac..c341489534 100644 --- a/cli/script.ts +++ b/cli/script.ts @@ -510,6 +510,8 @@ export function filePathExtensionFromContentType( return ".my.sql"; } else if (language === "bigquery") { return ".bq.sql"; + } else if (language === "oracledb") { + return ".odb.sql"; } else if (language === "snowflake") { return ".sf.sql"; } else if (language === "mssql") { @@ -528,6 +530,8 @@ export function filePathExtensionFromContentType( return ".rs"; } else if (language === "ansible") { return ".playbook.yml"; + } else if (language === "csharp") { + return ".cs"; } else { throw new Error("Invalid language: " + language); } @@ -544,6 +548,7 @@ export const exts = [ ".pg.sql", ".my.sql", ".bq.sql", + ".odb.sql", ".sf.sql", ".ms.sql", ".sql", @@ -551,6 +556,7 @@ export const exts = [ ".ps1", ".php", ".rs", + ".cs", ".playbook.yml", ]; diff --git a/cli/script_common.ts b/cli/script_common.ts index 220c6c41e9..49fc491803 100644 --- a/cli/script_common.ts +++ b/cli/script_common.ts @@ -9,11 +9,13 @@ export type ScriptLanguage = | "postgresql" | "mysql" | "bigquery" + | "oracledb" | "snowflake" | "mssql" | "graphql" | "php" | "rust" + | "csharp" | "ansible"; export function inferContentTypeFromFilePath( @@ -36,6 +38,8 @@ export function inferContentTypeFromFilePath( return "mysql"; } else if (contentPath.endsWith(".bq.sql")) { return "bigquery"; + } else if (contentPath.endsWith(".odb.sql")) { + return "oracledb"; } else if (contentPath.endsWith(".sf.sql")) { return "snowflake"; } else if (contentPath.endsWith(".ms.sql")) { @@ -52,6 +56,8 @@ export function inferContentTypeFromFilePath( return "php"; } else if (contentPath.endsWith(".rs")) { return "rust"; + } else if (contentPath.endsWith(".cs")) { + return "csharp"; } else if (contentPath.endsWith(".playbook.yml")) { return "ansible"; } else { diff --git a/cli/sync.ts b/cli/sync.ts index f0ec3dce2c..408c01884e 100644 --- a/cli/sync.ts +++ b/cli/sync.ts @@ -320,6 +320,7 @@ export function newPathAssigner(defaultTs: "bun" | "deno"): PathAssigner { else if (language == "postgresql") ext = "pg.sql"; else if (language == "mysql") ext = "my.sql"; else if (language == "bigquery") ext = "bq.sql"; + else if (language == "oracledb") ext = "odb.sql"; else if (language == "snowflake") ext = "sf.sql"; else if (language == "mssql") ext = "ms.sql"; else if (language == "graphql") ext = "gql"; @@ -327,6 +328,7 @@ export function newPathAssigner(defaultTs: "bun" | "deno"): PathAssigner { else if (language == "frontend") ext = "frontend.js"; else if (language == "php") ext = "php"; else if (language == "rust") ext = "rs"; + else if (language == "csharp") ext = "cs"; else if (language == "ansible") ext = "playbook.yml"; else ext = "no_ext"; @@ -647,6 +649,7 @@ export async function elementsToMap( "js", "lock", "rs", + "cs", "yml", ].includes(path.split(".").pop() ?? "") && !isFileResource(path) diff --git a/cli/types.ts b/cli/types.ts index e264521cb7..0d717c296e 100644 --- a/cli/types.ts +++ b/cli/types.ts @@ -202,6 +202,7 @@ export function getTypeStrFromPath( parsed.ext == ".js" || parsed.ext == ".php" || parsed.ext == ".rs" || + parsed.ext == ".cs" || (parsed.ext == ".yml" && parsed.name.split(".").pop() == "playbook") ) { return "script"; diff --git a/cli/wasm/windmill_parser_wasm.generated.d.ts b/cli/wasm/windmill_parser_wasm.generated.d.ts index 547c600062..ac139dee65 100644 --- a/cli/wasm/windmill_parser_wasm.generated.d.ts +++ b/cli/wasm/windmill_parser_wasm.generated.d.ts @@ -13,6 +13,7 @@ export interface InstantiateResult { parse_python: typeof parse_python; parse_sql: typeof parse_sql; parse_mysql: typeof parse_mysql; + parse_oracledb: typeof parse_oracledb; parse_bigquery: typeof parse_bigquery; parse_snowflake: typeof parse_snowflake; parse_mssql: typeof parse_mssql; @@ -20,7 +21,8 @@ export interface InstantiateResult { parse_graphql: typeof parse_graphql; parse_php: typeof parse_php; parse_rust: typeof parse_rust; - parse_ansible: typeof parse_ansible + parse_ansible: typeof parse_ansible; + parse_csharp: typeof parse_csharp }; } @@ -96,6 +98,11 @@ export function parse_mysql(code: string): string; * @param {string} code * @returns {string} */ +export function parse_oracledb(code: string): string; +/** +* @param {string} code +* @returns {string} +*/ export function parse_bigquery(code: string): string; /** * @param {string} code @@ -132,3 +139,8 @@ export function parse_rust(code: string): string; * @returns {string} */ export function parse_ansible(code: string): string; +/** +* @param {string} code +* @returns {string} +*/ +export function parse_csharp(code: string): string; diff --git a/cli/wasm/windmill_parser_wasm.generated.js b/cli/wasm/windmill_parser_wasm.generated.js index c8589935b2..81ae826164 100644 --- a/cli/wasm/windmill_parser_wasm.generated.js +++ b/cli/wasm/windmill_parser_wasm.generated.js @@ -4,7 +4,7 @@ // deno-fmt-ignore-file /// -// source-hash: 6da62ad21f79c5b8fe442f1de939e367943e2665 +// source-hash: f0b1d0be016a3874b6824c3458a87cf04fc171fd let wasm; const heap = new Array(128).fill(undefined); @@ -15,7 +15,38 @@ function getObject(idx) { return heap[idx]; } -let WASM_VECTOR_LEN = 0; +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +const cachedTextDecoder = typeof TextDecoder !== "undefined" + ? new TextDecoder("utf-8", { ignoreBOM: true, fatal: true }) + : { + decode: () => { + throw Error("TextDecoder not available"); + }, + }; + +if (typeof TextDecoder !== "undefined") cachedTextDecoder.decode(); let cachedUint8Memory0 = null; @@ -26,6 +57,35 @@ function getUint8Memory0() { return cachedUint8Memory0; } +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +let cachedFloat64Memory0 = null; + +function getFloat64Memory0() { + if (cachedFloat64Memory0 === null || cachedFloat64Memory0.byteLength === 0) { + cachedFloat64Memory0 = new Float64Array(wasm.memory.buffer); + } + return cachedFloat64Memory0; +} + +let cachedInt32Memory0 = null; + +function getInt32Memory0() { + if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { + cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); + } + return cachedInt32Memory0; +} + +let WASM_VECTOR_LEN = 0; + const cachedTextEncoder = typeof TextEncoder !== "undefined" ? new TextEncoder("utf-8") : { @@ -76,66 +136,6 @@ function passStringToWasm0(arg, malloc, realloc) { return ptr; } -function isLikeNone(x) { - return x === undefined || x === null; -} - -let cachedInt32Memory0 = null; - -function getInt32Memory0() { - if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { - cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); - } - return cachedInt32Memory0; -} - -let heap_next = heap.length; - -function dropObject(idx) { - if (idx < 132) return; - heap[idx] = heap_next; - heap_next = idx; -} - -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} - -let cachedFloat64Memory0 = null; - -function getFloat64Memory0() { - if (cachedFloat64Memory0 === null || cachedFloat64Memory0.byteLength === 0) { - cachedFloat64Memory0 = new Float64Array(wasm.memory.buffer); - } - return cachedFloat64Memory0; -} - -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - -const cachedTextDecoder = typeof TextDecoder !== "undefined" - ? new TextDecoder("utf-8", { ignoreBOM: true, fatal: true }) - : { - decode: () => { - throw Error("TextDecoder not available"); - }, - }; - -if (typeof TextDecoder !== "undefined") cachedTextDecoder.decode(); - -function getStringFromWasm0(ptr, len) { - ptr = ptr >>> 0; - return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); -} - let cachedBigInt64Memory0 = null; function getBigInt64Memory0() { @@ -472,6 +472,33 @@ export function parse_mysql(code) { } } +/** + * @param {string} code + * @returns {string} + */ +export function parse_oracledb(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_oracledb(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + /** * @param {string} code * @returns {string} @@ -688,6 +715,33 @@ export function parse_ansible(code) { } } +/** + * @param {string} code + * @returns {string} + */ +export function parse_csharp(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_csharp(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + function handleError(f, args) { try { return f.apply(this, args); @@ -698,71 +752,6 @@ function handleError(f, args) { const imports = { __wbindgen_placeholder__: { - __wbindgen_string_get: function (arg0, arg1) { - const obj = getObject(arg1); - const ret = typeof obj === "string" ? obj : undefined; - var ptr1 = isLikeNone(ret) - ? 0 - : passStringToWasm0( - ret, - wasm.__wbindgen_malloc, - wasm.__wbindgen_realloc, - ); - var len1 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len1; - getInt32Memory0()[arg0 / 4 + 0] = ptr1; - }, - __wbindgen_object_drop_ref: function (arg0) { - takeObject(arg0); - }, - __wbindgen_boolean_get: function (arg0) { - const v = getObject(arg0); - const ret = typeof v === "boolean" ? (v ? 1 : 0) : 2; - return ret; - }, - __wbindgen_is_bigint: function (arg0) { - const ret = typeof (getObject(arg0)) === "bigint"; - return ret; - }, - __wbindgen_number_get: function (arg0, arg1) { - const obj = getObject(arg1); - const ret = typeof obj === "number" ? obj : undefined; - getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret; - getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); - }, - __wbindgen_is_object: function (arg0) { - const val = getObject(arg0); - const ret = typeof val === "object" && val !== null; - return ret; - }, - __wbindgen_in: function (arg0, arg1) { - const ret = getObject(arg0) in getObject(arg1); - return ret; - }, - __wbindgen_bigint_from_i64: function (arg0) { - const ret = arg0; - return addHeapObject(ret); - }, - __wbindgen_jsval_eq: function (arg0, arg1) { - const ret = getObject(arg0) === getObject(arg1); - return ret; - }, - __wbindgen_bigint_from_u64: function (arg0) { - const ret = BigInt.asUintN(64, arg0); - return addHeapObject(ret); - }, - __wbindgen_error_new: function (arg0, arg1) { - const ret = new Error(getStringFromWasm0(arg0, arg1)); - return addHeapObject(ret); - }, - __wbg_eval_8243d86342a64a9b: function (arg0, arg1) { - const ret = eval(getStringFromWasm0(arg0, arg1)); - return addHeapObject(ret); - }, - __wbindgen_jsval_loose_eq: function (arg0, arg1) { - const ret = getObject(arg0) == getObject(arg1); - return ret; - }, __wbg_get_bd8e338fbd5f5cc8: function (arg0, arg1) { const ret = getObject(arg0)[arg1 >>> 0]; return addHeapObject(ret); @@ -771,14 +760,22 @@ const imports = { const ret = getObject(arg0).length; return ret; }, - __wbindgen_is_function: function (arg0) { - const ret = typeof (getObject(arg0)) === "function"; + __wbindgen_is_object: function (arg0) { + const val = getObject(arg0); + const ret = typeof val === "object" && val !== null; return ret; }, __wbg_next_40fc327bfc8770e6: function (arg0) { const ret = getObject(arg0).next; return addHeapObject(ret); }, + __wbindgen_is_function: function (arg0) { + const ret = typeof (getObject(arg0)) === "function"; + return ret; + }, + __wbindgen_object_drop_ref: function (arg0) { + takeObject(arg0); + }, __wbg_next_196c84450b364254: function () { return handleError(function (arg0) { const ret = getObject(arg0).next(); @@ -841,6 +838,10 @@ const imports = { const ret = Object.entries(getObject(arg0)); return addHeapObject(ret); }, + __wbindgen_memory: function () { + const ret = wasm.memory; + return addHeapObject(ret); + }, __wbg_buffer_12d079cc21e14bdb: function (arg0) { const ret = getObject(arg0).buffer; return addHeapObject(ret); @@ -866,6 +867,39 @@ const imports = { const ret = result; return ret; }, + __wbindgen_error_new: function (arg0, arg1) { + const ret = new Error(getStringFromWasm0(arg0, arg1)); + return addHeapObject(ret); + }, + __wbindgen_jsval_loose_eq: function (arg0, arg1) { + const ret = getObject(arg0) == getObject(arg1); + return ret; + }, + __wbindgen_boolean_get: function (arg0) { + const v = getObject(arg0); + const ret = typeof v === "boolean" ? (v ? 1 : 0) : 2; + return ret; + }, + __wbindgen_number_get: function (arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof obj === "number" ? obj : undefined; + getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret; + getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); + }, + __wbindgen_string_get: function (arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof obj === "string" ? obj : undefined; + var ptr1 = isLikeNone(ret) + ? 0 + : passStringToWasm0( + ret, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + var len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }, __wbindgen_bigint_get_as_i64: function (arg0, arg1) { const v = getObject(arg1); const ret = typeof v === "bigint" ? v : undefined; @@ -886,8 +920,28 @@ const imports = { __wbindgen_throw: function (arg0, arg1) { throw new Error(getStringFromWasm0(arg0, arg1)); }, - __wbindgen_memory: function () { - const ret = wasm.memory; + __wbindgen_is_bigint: function (arg0) { + const ret = typeof (getObject(arg0)) === "bigint"; + return ret; + }, + __wbindgen_in: function (arg0, arg1) { + const ret = getObject(arg0) in getObject(arg1); + return ret; + }, + __wbindgen_bigint_from_i64: function (arg0) { + const ret = arg0; + return addHeapObject(ret); + }, + __wbindgen_jsval_eq: function (arg0, arg1) { + const ret = getObject(arg0) === getObject(arg1); + return ret; + }, + __wbindgen_bigint_from_u64: function (arg0) { + const ret = BigInt.asUintN(64, arg0); + return addHeapObject(ret); + }, + __wbg_eval_73a4cb0af5990ab1: function (arg0, arg1) { + const ret = eval(getStringFromWasm0(arg0, arg1)); return addHeapObject(ret); }, }, @@ -1031,6 +1085,7 @@ function getWasmInstanceExports() { parse_python, parse_sql, parse_mysql, + parse_oracledb, parse_bigquery, parse_snowflake, parse_mssql, @@ -1039,6 +1094,7 @@ function getWasmInstanceExports() { parse_php, parse_rust, parse_ansible, + parse_csharp, }; } diff --git a/cli/wasm/windmill_parser_wasm_bg.wasm b/cli/wasm/windmill_parser_wasm_bg.wasm index 692c1e4fa8..19f605d076 100644 Binary files a/cli/wasm/windmill_parser_wasm_bg.wasm and b/cli/wasm/windmill_parser_wasm_bg.wasm differ diff --git a/frontend/src/lib/components/TestConnection.svelte b/frontend/src/lib/components/TestConnection.svelte index 321b39ceac..930a6a72de 100644 --- a/frontend/src/lib/components/TestConnection.svelte +++ b/frontend/src/lib/components/TestConnection.svelte @@ -51,7 +51,7 @@ argName: 'database' }, oracledb: { - code: `SELECT 1`, + code: `SELECT 1 FROM DUAL`, lang: 'oracledb', argName: 'database' },