feat: add sql server (#2604)

* feat: add sql server

* feat: mssql test, db schema, AI

* chore: update to latest parser

* fix: clean mssql executor
This commit is contained in:
HugoCasa
2023-11-10 11:16:13 +01:00
committed by GitHub
parent 7946d58a6f
commit 2fe623cf6b
49 changed files with 792 additions and 81 deletions
+140
View File
@@ -434,6 +434,19 @@ dependencies = [
"tokio",
]
[[package]]
name = "asynchronous-codec"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568"
dependencies = [
"bytes",
"futures-sink",
"futures-util",
"memchr",
"pin-project-lite",
]
[[package]]
name = "atoi"
version = "1.0.0"
@@ -1260,6 +1273,12 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "connection-string"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "510ca239cf13b7f8d16a2b48f263de7b4f8c566f0af58d901031473c76afb1e3"
[[package]]
name = "console"
version = "0.15.7"
@@ -2055,6 +2074,70 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "encoding"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
dependencies = [
"encoding-index-japanese",
"encoding-index-korean",
"encoding-index-simpchinese",
"encoding-index-singlebyte",
"encoding-index-tradchinese",
]
[[package]]
name = "encoding-index-japanese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-korean"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-simpchinese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-singlebyte"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-tradchinese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding_index_tests"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
[[package]]
name = "encoding_rs"
version = "0.8.31"
@@ -2064,6 +2147,26 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "enumflags2"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939"
dependencies = [
"enumflags2_derive",
]
[[package]]
name = "enumflags2_derive"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@@ -4334,6 +4437,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "pretty-hex"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5"
[[package]]
name = "prettyplease"
version = "0.1.25"
@@ -6792,6 +6901,34 @@ dependencies = [
"once_cell",
]
[[package]]
name = "tiberius"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6e2bf3e4b5be181a2a2ceff4b9b12e2684010d436a6958bd564fbc8094d44d"
dependencies = [
"async-trait",
"asynchronous-codec",
"byteorder",
"bytes",
"chrono",
"connection-string",
"encoding",
"enumflags2",
"futures-util",
"num-traits",
"once_cell",
"pin-project-lite",
"pretty-hex",
"rustls-native-certs",
"rustls-pemfile",
"thiserror",
"tokio-rustls 0.23.4",
"tokio-util",
"tracing",
"uuid 1.5.0",
]
[[package]]
name = "tiger"
version = "0.1.0"
@@ -7049,6 +7186,7 @@ checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
dependencies = [
"bytes",
"futures-core",
"futures-io",
"futures-sink",
"pin-project-lite",
"tokio",
@@ -8284,8 +8422,10 @@ dependencies = [
"serde_json",
"sha2 0.10.8",
"sqlx 0.7.2",
"tiberius",
"tokio",
"tokio-postgres",
"tokio-util",
"tracing",
"urlencoding",
"uuid 1.5.0",
+1
View File
@@ -203,3 +203,4 @@ tokenizers = "0.14.1"
candle-core = "0.3.0"
candle-transformers = "0.3.0"
candle-nn = "0.3.0"
tiberius = { version = "0.12.2", default-features = false, features = ["rustls", "tds73", "chrono"] }
@@ -0,0 +1 @@
-- Add down migration script here
@@ -0,0 +1,4 @@
-- Add up migration script here
ALTER TYPE SCRIPT_LANG ADD VALUE IF NOT EXISTS 'mssql';
-- Add up migration script here
UPDATE config set config = '{"worker_tags": ["nativets", "postgresql", "mysql", "graphql", "snowflake", "bigquery", "mssql"]}'::jsonb where name = 'worker__native' and config = '{"worker_tags": ["nativets", "postgresql", "mysql", "graphql", "snowflake", "bigquery"]}'::jsonb ;
@@ -47,6 +47,16 @@ pub fn parse_snowflake_sig(code: &str) -> anyhow::Result<MainArgSignature> {
}
}
pub fn parse_mssql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
let parsed = parse_mssql_file(&code)?;
if let Some(x) = parsed {
let args = x;
Ok(MainArgSignature { star_args: false, star_kwargs: false, args })
} else {
Err(anyhow!("Error parsing sql".to_string()))
}
}
lazy_static::lazy_static! {
static ref RE_CODE_PGSQL: Regex = Regex::new(r#"(?m)\$(\d+)(?:::(\w+(?:\[\])?))?"#).unwrap();
@@ -60,6 +70,9 @@ lazy_static::lazy_static! {
static ref RE_ARG_SNOWFLAKE: Regex = Regex::new(r#"(?m)^-- \? (\w+) \((\w+)\)(?: ?\= ?(.+))? *[\r\n$]"#).unwrap();
static ref RE_ARG_MSSQL: Regex = Regex::new(r#"(?m)^-- @(?:P|p)\d+ (\w+) \((\w+)\)(?: ?\= ?(.+))? *[\r\n$]"#).unwrap();
}
fn parse_mysql_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
@@ -200,6 +213,36 @@ fn parse_snowflake_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
Ok(Some(args))
}
fn parse_mssql_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let mut args: Vec<Arg> = vec![];
for cap in RE_ARG_MSSQL.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_mssql_typ(typ.as_str());
let parsed_default = default.and_then(|x| match parsed_typ {
Typ::Int => x.parse::<i64>().ok().map(|x| json!(x)),
Typ::Float => x.parse::<f64>().ok().map(|x| json!(x)),
_ => Some(json!(x)),
});
args.push(Arg {
name,
typ: parsed_typ,
default: parsed_default,
otyp: Some(typ),
has_default,
});
}
Ok(Some(args))
}
pub fn parse_mysql_typ(typ: &str) -> Typ {
match typ {
"varchar" | "char" | "binary" | "varbinary" | "blob" | "text" | "enum" | "set" => {
@@ -269,6 +312,20 @@ pub fn parse_snowflake_typ(typ: &str) -> Typ {
}
}
pub fn parse_mssql_typ(typ: &str) -> Typ {
match typ {
"char" | "text" | "varchar" | "nchar" | "nvarchar" | "ntext" => Typ::Str(None),
"binary" | "varbinary" | "image" => Typ::Bytes,
"date" | "datetime2" | "datetime" | "datetimeoffset" | "smalldatetime" | "time" => {
Typ::Datetime
}
"bigint" | "int" | "tinyint" | "smallint" => Typ::Int,
"float" | "real" | "numeric" | "decimal" => Typ::Float,
"bit" => Typ::Bool,
_ => Typ::Str(None),
}
}
#[cfg(test)]
mod tests {
@@ -3,7 +3,7 @@
"collaborators": [
"Ruben Fiszel <ruben@windmill.dev>"
],
"version": "1.190.0",
"version": "1.202.1",
"files": [
"windmill_parser_wasm_bg.wasm",
"windmill_parser_wasm.js",
@@ -54,6 +54,11 @@ export function parse_snowflake(code: string): string;
* @param {string} code
* @returns {string}
*/
export function parse_mssql(code: string): string;
/**
* @param {string} code
* @returns {string}
*/
export function parse_graphql(code: string): string;
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
@@ -70,6 +75,7 @@ export interface InitOutput {
readonly parse_mysql: (a: number, b: number, c: number) => void;
readonly parse_bigquery: (a: number, b: number, c: number) => void;
readonly parse_snowflake: (a: number, b: number, c: number) => void;
readonly parse_mssql: (a: number, b: number, c: number) => void;
readonly parse_graphql: (a: number, b: number, c: number) => void;
readonly __wbindgen_malloc: (a: number, b: number) => number;
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
@@ -97,15 +97,6 @@ function getInt32Memory0() {
return cachedInt32Memory0;
}
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(); };
@@ -115,6 +106,15 @@ function getStringFromWasm0(ptr, len) {
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}
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;
}
let cachedFloat64Memory0 = null;
function getFloat64Memory0() {
@@ -427,6 +427,29 @@ export function parse_snowflake(code) {
}
}
/**
* @param {string} code
* @returns {string}
*/
export function parse_mssql(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_mssql(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}
@@ -503,6 +526,14 @@ function __wbg_get_imports() {
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
};
imports.wbg.__wbg_eval_f742bccbdcf34b02 = function(arg0, arg1) {
const ret = eval(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
};
imports.wbg.__wbindgen_error_new = function(arg0, arg1) {
const ret = new Error(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
};
imports.wbg.__wbindgen_boolean_get = function(arg0) {
const v = getObject(arg0);
const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
@@ -520,14 +551,6 @@ function __wbg_get_imports() {
const ret = getObject(arg0) === getObject(arg1);
return ret;
};
imports.wbg.__wbindgen_bigint_from_u64 = function(arg0) {
const ret = BigInt.asUintN(64, arg0);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_error_new = function(arg0, arg1) {
const ret = new Error(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
};
imports.wbg.__wbindgen_number_get = function(arg0, arg1) {
const obj = getObject(arg1);
const ret = typeof(obj) === 'number' ? obj : undefined;
@@ -543,19 +566,19 @@ function __wbg_get_imports() {
const ret = getObject(arg0) in getObject(arg1);
return ret;
};
imports.wbg.__wbg_eval_d090ffdbb4b6658b = function(arg0, arg1) {
const ret = eval(getStringFromWasm0(arg0, arg1));
imports.wbg.__wbindgen_bigint_from_u64 = function(arg0) {
const ret = BigInt.asUintN(64, arg0);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_jsval_loose_eq = function(arg0, arg1) {
const ret = getObject(arg0) == getObject(arg1);
return ret;
};
imports.wbg.__wbg_get_44be0491f933a435 = function(arg0, arg1) {
imports.wbg.__wbg_get_4a9aa5157afeb382 = function(arg0, arg1) {
const ret = getObject(arg0)[arg1 >>> 0];
return addHeapObject(ret);
};
imports.wbg.__wbg_length_fff51ee6522a1a18 = function(arg0) {
imports.wbg.__wbg_length_cace2e0b3ddc0502 = function(arg0) {
const ret = getObject(arg0).length;
return ret;
};
@@ -563,76 +586,76 @@ function __wbg_get_imports() {
const ret = typeof(getObject(arg0)) === 'function';
return ret;
};
imports.wbg.__wbg_next_526fc47e980da008 = function(arg0) {
imports.wbg.__wbg_next_15da6a3df9290720 = function(arg0) {
const ret = getObject(arg0).next;
return addHeapObject(ret);
};
imports.wbg.__wbg_next_ddb3312ca1c4e32a = function() { return handleError(function (arg0) {
imports.wbg.__wbg_next_1989a20442400aaa = function() { return handleError(function (arg0) {
const ret = getObject(arg0).next();
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_done_5c1f01fb660d73b5 = function(arg0) {
imports.wbg.__wbg_done_bc26bf4ada718266 = function(arg0) {
const ret = getObject(arg0).done;
return ret;
};
imports.wbg.__wbg_value_1695675138684bd5 = function(arg0) {
imports.wbg.__wbg_value_0570714ff7d75f35 = function(arg0) {
const ret = getObject(arg0).value;
return addHeapObject(ret);
};
imports.wbg.__wbg_iterator_97f0c81209c6c35a = function() {
imports.wbg.__wbg_iterator_7ee1a391d310f8e4 = function() {
const ret = Symbol.iterator;
return addHeapObject(ret);
};
imports.wbg.__wbg_get_97b561fb56f034b5 = function() { return handleError(function (arg0, arg1) {
imports.wbg.__wbg_get_2aff440840bb6202 = function() { return handleError(function (arg0, arg1) {
const ret = Reflect.get(getObject(arg0), getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_call_cb65541d95d71282 = function() { return handleError(function (arg0, arg1) {
imports.wbg.__wbg_call_669127b9d730c650 = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg0).call(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_isArray_4c24b343cb13cfb1 = function(arg0) {
imports.wbg.__wbg_isArray_38525be7442aa21e = function(arg0) {
const ret = Array.isArray(getObject(arg0));
return ret;
};
imports.wbg.__wbg_instanceof_ArrayBuffer_39ac22089b74fddb = function(arg0) {
imports.wbg.__wbg_instanceof_ArrayBuffer_c7cc317e5c29cc0d = function(arg0) {
let result;
try {
result = getObject(arg0) instanceof ArrayBuffer;
} catch {
} catch (_) {
result = false;
}
const ret = result;
return ret;
};
imports.wbg.__wbg_isSafeInteger_bb8e18dd21c97288 = function(arg0) {
imports.wbg.__wbg_isSafeInteger_c38b0a16d0c7cef7 = function(arg0) {
const ret = Number.isSafeInteger(getObject(arg0));
return ret;
};
imports.wbg.__wbg_entries_e51f29c7bba0c054 = function(arg0) {
imports.wbg.__wbg_entries_6d727b73ee02b7ce = function(arg0) {
const ret = Object.entries(getObject(arg0));
return addHeapObject(ret);
};
imports.wbg.__wbg_buffer_085ec1f694018c4f = function(arg0) {
imports.wbg.__wbg_buffer_344d9b41efe96da7 = function(arg0) {
const ret = getObject(arg0).buffer;
return addHeapObject(ret);
};
imports.wbg.__wbg_new_8125e318e6245eed = function(arg0) {
imports.wbg.__wbg_new_d8a000788389a31e = function(arg0) {
const ret = new Uint8Array(getObject(arg0));
return addHeapObject(ret);
};
imports.wbg.__wbg_set_5cf90238115182c3 = function(arg0, arg1, arg2) {
imports.wbg.__wbg_set_dcfd613a3420f908 = function(arg0, arg1, arg2) {
getObject(arg0).set(getObject(arg1), arg2 >>> 0);
};
imports.wbg.__wbg_length_72e2208bbc0efc61 = function(arg0) {
imports.wbg.__wbg_length_a5587d6cd79ab197 = function(arg0) {
const ret = getObject(arg0).length;
return ret;
};
imports.wbg.__wbg_instanceof_Uint8Array_d8d9cb2b8e8ac1d4 = function(arg0) {
imports.wbg.__wbg_instanceof_Uint8Array_19e6f142a5e7e1e1 = function(arg0) {
let result;
try {
result = getObject(arg0) instanceof Uint8Array;
} catch {
} catch (_) {
result = false;
}
const ret = result;
@@ -11,6 +11,7 @@ export function parse_sql(a: number, b: number, c: number): void;
export function parse_mysql(a: number, b: number, c: number): void;
export function parse_bigquery(a: number, b: number, c: number): void;
export function parse_snowflake(a: number, b: number, c: number): void;
export function parse_mssql(a: number, b: number, c: number): void;
export function parse_graphql(a: number, b: number, c: number): void;
export function __wbindgen_malloc(a: number, b: number): number;
export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number;
@@ -67,6 +67,11 @@ pub fn parse_snowflake(code: &str) -> String {
wrap_sig(windmill_parser_sql::parse_snowflake_sig(code))
}
#[wasm_bindgen]
pub fn parse_mssql(code: &str) -> String {
wrap_sig(windmill_parser_sql::parse_mssql_sig(code))
}
#[wasm_bindgen]
pub fn parse_graphql(code: &str) -> String {
wrap_sig(windmill_parser_graphql::parse_graphql_sig(code))
+5
View File
@@ -6587,6 +6587,7 @@ components:
mysql,
bigquery,
snowflake,
mssql,
graphql,
nativets,
bun,
@@ -6668,6 +6669,7 @@ components:
mysql,
bigquery,
snowflake,
mssql,
graphql,
nativets,
bun,
@@ -6857,6 +6859,7 @@ components:
mysql,
bigquery,
snowflake,
mssql,
graphql,
nativets,
bun,
@@ -6964,6 +6967,7 @@ components:
mysql,
bigquery,
snowflake,
mssql,
graphql,
nativets,
bun,
@@ -7478,6 +7482,7 @@ components:
mysql,
bigquery,
snowflake,
mssql,
graphql,
nativets,
bun,
+1
View File
@@ -1633,6 +1633,7 @@ async fn tarball_workspace(
ScriptLang::Mysql => "my.sql",
ScriptLang::Bigquery => "bq.sql",
ScriptLang::Snowflake => "sf.sql",
ScriptLang::Mssql => "ms.sql",
ScriptLang::Graphql => "gql",
ScriptLang::Nativets => "fetch.ts",
ScriptLang::Bun => "bun.ts",
+2
View File
@@ -42,6 +42,7 @@ pub enum ScriptLang {
Bigquery,
Snowflake,
Graphql,
Mssql,
}
impl ScriptLang {
@@ -58,6 +59,7 @@ impl ScriptLang {
ScriptLang::Mysql => "mysql",
ScriptLang::Bigquery => "bigquery",
ScriptLang::Snowflake => "snowflake",
ScriptLang::Mssql => "mssql",
ScriptLang::Graphql => "graphql",
}
}
+1
View File
@@ -28,6 +28,7 @@ lazy_static::lazy_static! {
"postgresql".to_string(),
"bigquery".to_string(),
"snowflake".to_string(),
"mssql".to_string(),
"graphql".to_string(),
"dependency".to_string(),
"flow".to_string(),
+3 -1
View File
@@ -10,7 +10,7 @@ path = "src/lib.rs"
[features]
default = []
enterprise = ["windmill-queue/enterprise", "dep:gcp_auth", "dep:jsonwebtoken", "dep:pem", "dep:sha2"]
enterprise = ["windmill-queue/enterprise", "dep:gcp_auth", "dep:jsonwebtoken", "dep:pem", "dep:sha2", "dep:tiberius", "dep:tokio-util"]
benchmark = ["windmill-queue/benchmark"]
flamegraph = []
@@ -74,6 +74,8 @@ nix.workspace = true
bytes.workspace = true
reqwest.workspace = true
hex.workspace = true
tiberius = { workspace = true, optional = true }
tokio-util = { workspace = true, optional = true }
[build-dependencies]
deno_fetch.workspace = true
+2
View File
@@ -1,6 +1,8 @@
#[cfg(feature = "enterprise")]
mod bigquery_executor;
#[cfg(feature = "enterprise")]
mod mssql_executor;
#[cfg(feature = "enterprise")]
mod snowflake_executor;
mod bash_executor;
@@ -0,0 +1,236 @@
use base64::{engine::general_purpose, Engine as _};
use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc};
use serde::Deserialize;
use serde_json::value::RawValue;
use serde_json::{Map, Value};
use tiberius::{AuthMethod, Client, ColumnData, Config, FromSqlOwned, Query, Row};
use tokio::net::TcpStream;
use tokio_util::compat::TokioAsyncWriteCompatExt;
use uuid::Uuid;
use windmill_common::error::{self, Error};
use windmill_common::worker::to_raw_value;
use windmill_common::{error::to_anyhow, jobs::QueuedJob};
use windmill_parser_sql::parse_mssql_sig;
use crate::common::build_args_values;
use crate::AuthedClientBackgroundTask;
#[derive(Deserialize)]
struct MssqlDatabase {
host: String,
user: String,
password: String,
port: Option<u16>,
dbname: String,
}
pub async fn do_mssql(
job: &QueuedJob,
client: &AuthedClientBackgroundTask,
query: &str,
db: &sqlx::Pool<sqlx::Postgres>,
) -> error::Result<Box<RawValue>> {
let mssql_args = build_args_values(job, client, db).await?;
let database = if let Some(db) = mssql_args.get("database") {
serde_json::from_value::<MssqlDatabase>(db.clone())
.map_err(|e| Error::ExecutionErr(e.to_string()))?
} else {
return Err(Error::BadRequest("Missing database argument".to_string()));
};
let mut config = Config::new();
config.host(database.host);
config.port(database.port.unwrap_or(1433));
config.database(database.dbname);
// Using SQL Server authentication.
config.authentication(AuthMethod::sql_server(database.user, database.password));
config.trust_cert(); // on production, it is not a good idea to do this
let tcp = TcpStream::connect(config.get_addr()).await?;
tcp.set_nodelay(true)?;
// To be able to use Tokio's tcp, we're using the `compat_write` from
// the `TokioAsyncWriteCompatExt` to get a stream compatible with the
// traits from the `futures` crate.
let mut client = Client::connect(config, tcp.compat_write())
.await
.map_err(to_anyhow)?;
let sig = parse_mssql_sig(&query)
.map_err(|x| Error::ExecutionErr(x.to_string()))?
.args;
let mut prepared_query = Query::new(query.to_owned());
for arg in &sig {
let arg_t = arg.otyp.clone().unwrap_or_else(|| "string".to_string());
let arg_v = mssql_args
.get(&arg.name)
.cloned()
.unwrap_or(serde_json::json!(""));
json_value_to_sql(&mut prepared_query, &arg_v, &arg_t)?;
}
// A response to a query is a stream of data, that must be
// polled to the end before querying again. Using streams allows
// fetching data in an asynchronous manner, if needed.
let stream = prepared_query.query(&mut client).await.map_err(to_anyhow)?;
let rows = stream
.into_results()
.await
.map_err(to_anyhow)?
.into_iter()
.map(|rows| {
let result = rows
.into_iter()
.map(|row| row_to_json(row))
.collect::<Result<Vec<Map<String, Value>>, Error>>();
result
})
.collect::<Result<Vec<Vec<Map<String, Value>>>, Error>>()?;
return Ok(to_raw_value(&rows));
}
fn json_value_to_sql<'a>(
query: &mut Query,
value: &Value,
arg_t: &String,
) -> windmill_common::error::Result<()> {
match value {
Value::Null => {
query.bind(None::<String>);
}
Value::Bool(b) => {
query.bind(b.to_owned());
}
Value::Number(n) if n.is_u64() && arg_t == "tinyint" => {
query.bind(n.as_u64().unwrap().to_owned() as u8)
}
Value::Number(n) if n.is_i64() && arg_t == "smallint" => {
query.bind(n.as_i64().unwrap().to_owned() as i16)
}
Value::Number(n) if n.is_i64() && arg_t == "int" => {
query.bind(n.as_i64().unwrap().to_owned() as i32)
}
Value::Number(n) if n.is_i64() && arg_t == "bigint" => {
query.bind(n.as_i64().unwrap().to_owned() as i64)
}
Value::Number(n) if n.is_f64() && arg_t == "real" => {
query.bind(n.as_f64().unwrap().to_owned() as f32)
}
Value::Number(n) => query.bind(n.as_f64().unwrap().to_owned()),
Value::String(s) if arg_t == "uuid" => query.bind(Uuid::parse_str(s).map_err(to_anyhow)?),
Value::String(s) if arg_t == "binary" || arg_t == "varbinary" || arg_t == "image" => {
query.bind(general_purpose::STANDARD.decode(s).map_err(to_anyhow)?)
}
Value::String(s) if arg_t == "date" => {
let date = NaiveDate::parse_from_str(s, "%Y-%m-%dT%H:%M:%S.%3fZ").unwrap_or_default();
query.bind(date)
}
Value::String(s) if arg_t == "time" => {
let time = NaiveTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S.%3fZ").unwrap_or_default();
query.bind(time)
}
Value::String(s)
if arg_t == "datetime" || arg_t == "datetime2" || arg_t == "smalldatetime" =>
{
let datetime =
NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S.%3fZ").unwrap_or_default();
query.bind(datetime)
}
Value::String(s) if arg_t == "datetimeoffset" => {
let datetime = NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S.%3fZ")
.unwrap_or_default()
.and_utc();
query.bind(datetime)
}
Value::String(s) => query.bind(s.to_owned()),
_ => {
return Err(Error::ExecutionErr(format!(
"Unsupported type in query: {:?} and signature {arg_t:?}",
value
)))
}
};
Ok(())
}
fn row_to_json(row: Row) -> Result<Map<String, Value>, Error> {
let cols = row
.columns()
.iter()
.map(|x| x.to_owned())
.collect::<Vec<_>>();
let mut map = Map::new();
for (col, val) in cols.iter().zip(row.into_iter()) {
map.insert(col.name().to_string(), sql_to_json_value(val)?);
}
Ok(map)
}
fn value_or_null<T>(
val: Option<T>,
convert: impl Fn(T) -> Result<Value, Error>,
) -> Result<Value, Error> {
val.map_or(Ok(Value::Null), convert)
}
fn sql_to_json_value(val: ColumnData) -> Result<Value, Error> {
match val {
ColumnData::Bit(x) => value_or_null(x, |x| Ok(Value::Bool(x))),
ColumnData::U8(x) => value_or_null(x, |x| Ok(Value::Number(x.into()))),
ColumnData::I16(x) => value_or_null(x, |x| Ok(Value::Number(x.into()))),
ColumnData::I32(x) => value_or_null(x, |x| Ok(Value::Number(x.into()))),
ColumnData::I64(x) => value_or_null(x, |x| Ok(Value::Number(x.into()))),
ColumnData::String(x) => value_or_null(x, |x| Ok(Value::String(x.to_string()))),
ColumnData::Binary(x) => value_or_null(x, |x| {
Ok(Value::String(general_purpose::STANDARD.encode(x.as_ref())))
}),
ColumnData::F32(x) => value_or_null(x, |x| {
Ok(Value::Number(
serde_json::Number::from_f64(x.into())
.ok_or(anyhow::anyhow!("invalid json-float"))?,
))
}),
ColumnData::F64(x) => value_or_null(x, |x| {
Ok(Value::Number(
serde_json::Number::from_f64(x).ok_or(anyhow::anyhow!("invalid json-float"))?,
))
}),
ColumnData::Guid(x) => value_or_null(x, |x| Ok(Value::String(x.to_string()))),
ColumnData::Xml(x) => value_or_null(x, |x| Ok(Value::String(x.to_string()))),
ColumnData::Numeric(x) => value_or_null(x, |x| {
Ok(Value::Number(
serde_json::Number::from_f64(x.into())
.ok_or(anyhow::anyhow!("invalid json-float"))?,
))
}),
ColumnData::DateTime(x) => value_or_null(
NaiveDateTime::from_sql_owned(ColumnData::DateTime(x)).map_err(to_anyhow)?,
|x| Ok(Value::String(x.to_string())),
),
ColumnData::DateTime2(x) => value_or_null(
NaiveDateTime::from_sql_owned(ColumnData::DateTime2(x)).map_err(to_anyhow)?,
|x| Ok(Value::String(x.to_string())),
),
ColumnData::SmallDateTime(x) => value_or_null(
NaiveDateTime::from_sql_owned(ColumnData::SmallDateTime(x)).map_err(to_anyhow)?,
|x| Ok(Value::String(x.to_string())),
),
ColumnData::Time(x) => value_or_null(
NaiveTime::from_sql_owned(ColumnData::Time(x)).map_err(to_anyhow)?,
|x| Ok(Value::String(x.to_string())),
),
ColumnData::Date(x) => value_or_null(
NaiveDate::from_sql_owned(ColumnData::Date(x)).map_err(to_anyhow)?,
|x| Ok(Value::String(x.to_string())),
),
ColumnData::DateTimeOffset(x) => value_or_null(
DateTime::<Utc>::from_sql_owned(ColumnData::DateTimeOffset(x)).map_err(to_anyhow)?,
|x| Ok(Value::String(x.to_string())),
),
}
}
+19 -4
View File
@@ -90,7 +90,9 @@ use crate::{
};
#[cfg(feature = "enterprise")]
use crate::{bigquery_executor::do_bigquery, snowflake_executor::do_snowflake};
use crate::{
bigquery_executor::do_bigquery, mssql_executor::do_mssql, snowflake_executor::do_snowflake,
};
pub async fn create_token_for_owner_in_bg(
db: &Pool<Postgres>,
@@ -1863,9 +1865,9 @@ pub async fn process_completed_job<R: rsmq_async::RsmqConnection + Send + Sync +
logs.to_string(),
mem_peak.to_owned(),
canceled_by,
serde_json::from_str(result.get()).unwrap_or_else(
|_| json!({ "message": format!("Non serializable error: {}", result.get()) }),
),
serde_json::from_str(result.get()).unwrap_or_else(|_| {
json!({ "message": format!("Non serializable error: {}", result.get()) })
}),
rsmq.clone(),
worker_name,
)
@@ -2531,6 +2533,18 @@ async fn handle_code_execution_job(
{
return do_snowflake(job, &client, &inner_content, db).await;
}
} else if language == Some(ScriptLang::Mssql) {
#[cfg(not(feature = "enterprise"))]
{
return Err(Error::ExecutionErr(
"Microsoft SQL server is only available with an enterprise license".to_string(),
));
}
#[cfg(feature = "enterprise")]
{
return do_mssql(job, &client, &inner_content, db).await;
}
} else if language == Some(ScriptLang::Graphql) {
return do_graphql(job, &client, &inner_content, db).await;
} else if language == Some(ScriptLang::Nativets) {
@@ -3343,6 +3357,7 @@ async fn capture_dependency_job(
ScriptLang::Mysql => Ok("".to_owned()),
ScriptLang::Bigquery => Ok("".to_owned()),
ScriptLang::Snowflake => Ok("".to_owned()),
ScriptLang::Mssql => Ok("".to_owned()),
ScriptLang::Graphql => Ok("".to_owned()),
ScriptLang::Bash => Ok("".to_owned()),
ScriptLang::Powershell => Ok("".to_owned()),
+2
View File
@@ -68,6 +68,8 @@ async function dev(opts: GlobalOptions & { filter?: string }) {
? "bigquery"
: len > 2 && splitted[len - 2] == "sf"
? "snowflake"
: len > 2 && splitted[len - 2] == "ms"
? "mssql"
: "postgresql"
: ext == "gql"
? "graphql"
+3
View File
@@ -260,6 +260,7 @@ export function inferContentTypeFromFilePath(
| "mysql"
| "bigquery"
| "snowflake"
| "mssql"
| "graphql" {
if (contentPath.endsWith(".py")) {
return "python3";
@@ -277,6 +278,8 @@ export function inferContentTypeFromFilePath(
return "bigquery";
} else if (contentPath.endsWith(".sf.sql")) {
return "snowflake";
} else if (contentPath.endsWith(".ms.sql")) {
return "mssql";
} else if (contentPath.endsWith(".pg.sql")) {
return "postgresql";
} else if (contentPath.endsWith(".gql")) {
+1
View File
@@ -139,6 +139,7 @@ function ZipFSElement(zip: JSZip, useYaml: boolean): DynFSElement {
else if (language == "mysql") ext = "my.sql";
else if (language == "bigquery") ext = "bq.sql";
else if (language == "snowflake") ext = "sf.sql";
else if (language == "mssql") ext = "ms.sql";
else if (language == "graphql") ext = "gql";
else if (language == "bun") ext = "bun.ts";
else if (language == "nativets") ext = "native.ts";
+7 -7
View File
@@ -45,7 +45,7 @@
"svelte-tiny-virtual-list": "^2.0.5",
"tailwind-merge": "^1.13.2",
"vscode-ws-jsonrpc": "3.0.0",
"windmill-parser-wasm": "^1.190.0",
"windmill-parser-wasm": "^1.202.1",
"y-monaco": "^0.1.4",
"y-websocket": "^1.5.0",
"yjs": "^13.6.7"
@@ -10056,9 +10056,9 @@
}
},
"node_modules/windmill-parser-wasm": {
"version": "1.190.0",
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.190.0.tgz",
"integrity": "sha512-E2VUFVfsL2pHyaElUM8pK4aoV3KKTY50e1UZY14oKnQRmtYYrjxYfFFlOoFVbPyT29AyWws7ZXEt6M1qb8MwUw=="
"version": "1.202.1",
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.202.1.tgz",
"integrity": "sha512-And4nVsHkhDKJL5mtE5YL1csEwtkegBJnxFtdar9g42Oui/F5L4lU2v5RF4rIcLt8fdkN8/Y1Fn/4oN0TYPJtw=="
},
"node_modules/wordwrap": {
"version": "1.0.0",
@@ -17215,9 +17215,9 @@
}
},
"windmill-parser-wasm": {
"version": "1.190.0",
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.190.0.tgz",
"integrity": "sha512-E2VUFVfsL2pHyaElUM8pK4aoV3KKTY50e1UZY14oKnQRmtYYrjxYfFFlOoFVbPyT29AyWws7ZXEt6M1qb8MwUw=="
"version": "1.202.1",
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.202.1.tgz",
"integrity": "sha512-And4nVsHkhDKJL5mtE5YL1csEwtkegBJnxFtdar9g42Oui/F5L4lU2v5RF4rIcLt8fdkN8/Y1Fn/4oN0TYPJtw=="
},
"wordwrap": {
"version": "1.0.0",
+1 -1
View File
@@ -123,7 +123,7 @@
"svelte-tiny-virtual-list": "^2.0.5",
"tailwind-merge": "^1.13.2",
"vscode-ws-jsonrpc": "3.0.0",
"windmill-parser-wasm": "^1.190.0",
"windmill-parser-wasm": "^1.202.1",
"y-monaco": "^0.1.4",
"y-websocket": "^1.5.0",
"yjs": "^13.6.7"
@@ -73,7 +73,14 @@
export let newPageOAuth = false
const nativeLanguagesCategory = ['postgresql', 'mysql', 'bigquery', 'snowflake', 'graphql']
const nativeLanguagesCategory = [
'postgresql',
'mysql',
'bigquery',
'snowflake',
'mssql',
'graphql'
]
let filter = ''
let manual = false
@@ -166,6 +166,50 @@ GROUP BY table_name".replace('{dataset.id}', dataset.id)
return schema
},
argName: 'database'
},
ms_sql_server: {
argName: 'database',
code: `select TABLE_SCHEMA, TABLE_NAME, DATA_TYPE, COLUMN_NAME, COLUMN_DEFAULT from information_schema.columns where table_schema != 'sys'`,
lang: 'mssql',
processingFn: (rows) => {
const schemas = rows[0].reduce((acc, a) => {
const table_schema = a.TABLE_SCHEMA
delete a.TABLE_SCHEMA
acc[table_schema] = acc[table_schema] || []
acc[table_schema].push(a)
return acc
}, {})
const data = {}
for (const key in schemas) {
data[key] = schemas[key].reduce((acc, a) => {
const table_name = a.TABLE_NAME
delete a.TABLE_NAME
acc[table_name] = acc[table_name] || {}
const p: {
type: string
required: boolean
default?: string
} = {
type: a.DATA_TYPE,
required: a.is_nullable === 'NO'
}
if (a.column_default) {
p.default = a.COLUMN_DEFAULT
}
acc[table_name][a.COLUMN_NAME] = p
return acc
}, {})
}
return data
}
}
}
function resourceTypeToLang(rt: string) {
if (rt === 'ms_sql_server') {
return 'mssql'
} else {
return rt
}
}
@@ -200,9 +244,9 @@ GROUP BY table_name".replace('{dataset.id}', dataset.id)
const schema =
processingFn !== undefined ? processingFn(testResult.result) : testResult.result
$dbSchemas[resourcePath] = {
lang: resourceType as SQLSchema['lang'],
lang: resourceTypeToLang(resourceType) as SQLSchema['lang'],
schema,
publicOnly: !!schema.public || !!schema.PUBLIC
publicOnly: !!schema.public || !!schema.PUBLIC || !!schema.dbo
}
} else {
if (typeof testResult.result !== 'object' || !('__schema' in testResult.result)) {
@@ -247,7 +291,7 @@ GROUP BY table_name".replace('{dataset.id}', dataset.id)
function formatSchema(dbSchema: DBSchema) {
if (dbSchema.lang !== 'graphql' && dbSchema.publicOnly) {
return dbSchema.schema.public || dbSchema.schema.PUBLIC || dbSchema
return dbSchema.schema.public || dbSchema.schema.PUBLIC || dbSchema.schema.dbo || dbSchema
} else if (dbSchema.lang === 'mysql' && Object.keys(dbSchema.schema).length === 1) {
return dbSchema.schema[Object.keys(dbSchema.schema)[0]]
} else {
@@ -293,9 +337,9 @@ GROUP BY table_name".replace('{dataset.id}', dataset.id)
Refresh
</Button>
</svelte:fragment>
{#if dbSchema.lang !== 'graphql' && (dbSchema.schema?.public || dbSchema.schema?.PUBLIC)}
{#if dbSchema.lang !== 'graphql' && (dbSchema.schema?.public || dbSchema.schema?.PUBLIC || dbSchema.schema?.dbo)}
<ToggleButtonGroup class="mb-4" bind:selected={dbSchema.publicOnly}>
<ToggleButton value={true} label="Public" />
<ToggleButton value={true} label={dbSchema.schema.dbo ? 'Dbo' : 'Public'} />
<ToggleButton value={false} label="All" />
</ToggleButtonGroup>
{/if}
@@ -77,7 +77,7 @@
let editor: Editor | undefined = undefined
let scriptEditor: ScriptEditor | undefined = undefined
const enterpriseLangs = ['bigquery', 'snowflake']
const enterpriseLangs = ['bigquery', 'snowflake', 'mssql']
loadWorkerGroups()
@@ -107,6 +107,7 @@
langs.push(['MySQL', Script.language.MYSQL])
langs.push(['BigQuery', Script.language.BIGQUERY])
langs.push(['Snowflake', Script.language.SNOWFLAKE])
langs.push(['MS SQL Server', Script.language.MSSQL])
langs.push(['GraphQL', Script.language.GRAPHQL])
langs.push(['PowerShell', Script.language.POWERSHELL])
@@ -38,6 +38,11 @@
lang: 'snowflake',
argName: 'database'
},
ms_sql_server: {
code: `SELECT 1`,
lang: 'mssql',
argName: 'database'
},
graphql: {
code: '{ __typename }',
lang: 'graphql',
@@ -61,7 +61,15 @@
'other',
'bun'
]
const nativeTags = ['nativets', 'postgresql', 'mysql', 'graphql', 'snowflake', 'bigquery']
const nativeTags = [
'nativets',
'postgresql',
'mysql',
'graphql',
'snowflake',
'mssql',
'bigquery'
]
let newTag: string = ''
$: selected = nconfig?.dedicated_worker != undefined ? 'dedicated' : 'normal'
@@ -103,6 +103,7 @@
['mysql', 'MySQL'],
['bigquery', 'BigQuery'],
['snowflake', 'Snowflake'],
['mssql', 'MS SQL Server'],
['graphql', 'GraphQL'],
['bun', 'TypeScript (Bun)']
] as [Script.language, string][]
@@ -12,6 +12,7 @@
import BigQueryIcon from '$lib/components/icons/BigQueryIcon.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'
export let lang:
| SupportedLanguage
@@ -36,7 +37,8 @@
[Script.language.GRAPHQL]: 'GraphQL',
[Script.language.POSTGRESQL]: 'Postgresql',
[Script.language.BIGQUERY]: 'BigQuery',
[Script.language.SNOWFLAKE]: 'Snowflake'
[Script.language.SNOWFLAKE]: 'Snowflake',
[Script.language.MSSQL]: 'MS SQL Server'
}
const langToComponent: Record<
@@ -53,6 +55,7 @@
mysql: MySQLIcon,
bigquery: BigQueryIcon,
snowflake: SnowflakeIcon,
mssql: MSSqlServerIcon,
javascript: JavaScript,
fetch: FetchIcon,
docker: DockerIcon,
@@ -308,7 +308,7 @@
/>
</div>
{#if ['postgresql', 'mysql', 'snowflake', 'bigquery', 'graphql'].includes(lang) && dbSchema?.lang === lang}
{#if ['postgresql', 'mysql', 'snowflake', 'bigquery', 'mssql', 'graphql'].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">
<p class="text-xs text-secondary">
@@ -318,9 +318,14 @@
In order to better generate the script, we pass the selected schema to GPT-4.
</Tooltip>
</div>
{#if dbSchema.lang !== 'graphql' && (dbSchema.schema?.public || dbSchema.schema?.PUBLIC)}
{#if dbSchema.lang !== 'graphql' && (dbSchema.schema?.public || dbSchema.schema?.PUBLIC || dbSchema.schema?.dbo)}
<ToggleButtonGroup class="w-auto shrink-0" bind:selected={dbSchema.publicOnly}>
<ToggleButton value={true} label="Public schema" small light />
<ToggleButton
value={true}
label={(dbSchema.schema?.dbo ? 'Dbo' : 'Public') + ' schema'}
small
light
/>
<ToggleButton value={false} label="All schemas" small light />
</ToggleButtonGroup>
{/if}
+3 -2
View File
@@ -154,7 +154,7 @@ function addDBSChema(scriptOptions: CopilotOptions, prompt: string) {
const { dbSchema, language } = scriptOptions
if (
dbSchema &&
['postgresql', 'mysql', 'snowflake', 'bigquery', 'graphql'].includes(language) && // make sure we are using a SQL/query language
['postgresql', 'mysql', 'snowflake', 'bigquery', 'mssql', 'graphql'].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
) {
const { schema, lang } = dbSchema
@@ -188,7 +188,8 @@ function addDBSChema(scriptOptions: CopilotOptions, prompt: string) {
let finalSchema: typeof smallerSchema | (typeof smallerSchema)['schemaKey'] = smallerSchema
if (dbSchema.publicOnly) {
finalSchema = smallerSchema.public || smallerSchema.PUBLIC || smallerSchema
finalSchema =
smallerSchema.public || smallerSchema.PUBLIC || smallerSchema.dbo || smallerSchema
} else if (lang === 'mysql' && Object.keys(smallerSchema).length === 1) {
finalSchema = smallerSchema[Object.keys(smallerSchema)[0]]
}
@@ -89,7 +89,6 @@ prompts:
```
<contextual_information>
You can define arguments by adding comments before the statement like that: `-- @name1 ({type})` or `-- @name2 ({type}) = default` (one per row). They can then be obtained directly in the statement with `@name1`, `@name2`, etc....
I get the following error: {error}
</contextual_information>
My instructions: {description}
snowflake:
@@ -100,7 +99,16 @@ prompts:
```
<contextual_information>
Arguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)
I get the following error: {error}
</contextual_information>
My instructions: {description}
mssql:
prompt: |-
Here's my Microsoft SQL Server code:
```sql
{code}
```
<contextual_information>
Arguments can be obtained directly in the statement with @p1, @p2, etc.. Name the parameters by adding comments before the statement like that: `-- @p1 name1 ({type})` or `-- @p2 name2 ({type}) = default` (one per row)
</contextual_information>
My instructions: {description}
graphql:
@@ -111,7 +119,6 @@ prompts:
```
<contextual_information>
Add the needed arguments as query parameters.
I get the following error: {error}
</contextual_information>
My instructions: {description}
powershell:
@@ -122,7 +129,6 @@ prompts:
```
<contextual_information>
Arguments can be obtained by calling the param function on the first line like that: `param($ParamName1, $ParamName2 = "default value", [{type}]$ParamName3, ...)`
I get the following error: {error}
</contextual_information>
My instructions: {description}
nativets:
@@ -20,16 +20,19 @@ export const EDIT_PROMPT = {
"prompt": "Here's my MySQL code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)\n</contextual_information>\nMy instructions: {description}"
},
"bigquery": {
"prompt": "Here's my BigQuery code: \n```sql\n{code}\n```\n<contextual_information>\nYou can define arguments by adding comments before the statement like that: `-- @name1 ({type})` or `-- @name2 ({type}) = default` (one per row). They can then be obtained directly in the statement with `@name1`, `@name2`, etc....\nI get the following error: {error}\n</contextual_information>\nMy instructions: {description}"
"prompt": "Here's my BigQuery code: \n```sql\n{code}\n```\n<contextual_information>\nYou can define arguments by adding comments before the statement like that: `-- @name1 ({type})` or `-- @name2 ({type}) = default` (one per row). They can then be obtained directly in the statement with `@name1`, `@name2`, etc....\n</contextual_information>\nMy instructions: {description}"
},
"snowflake": {
"prompt": "Here's my snowflake code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)\nI get the following error: {error}\n</contextual_information>\nMy instructions: {description}"
"prompt": "Here's my snowflake code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)\n</contextual_information>\nMy instructions: {description}"
},
"mssql": {
"prompt": "Here's my Microsoft SQL Server code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with @p1, @p2, etc.. Name the parameters by adding comments before the statement like that: `-- @p1 name1 ({type})` or `-- @p2 name2 ({type}) = default` (one per row)\n</contextual_information>\nMy instructions: {description}"
},
"graphql": {
"prompt": "Here's my graphql code: \n```graphql\n{code}\n```\n<contextual_information>\nAdd the needed arguments as query parameters.\nI get the following error: {error}\n</contextual_information>\nMy instructions: {description}"
"prompt": "Here's my graphql code: \n```graphql\n{code}\n```\n<contextual_information>\nAdd the needed arguments as query parameters.\n</contextual_information>\nMy instructions: {description}"
},
"powershell": {
"prompt": "Here's my powershell code: \n```powershell\n{code}\n```\n<contextual_information>\nArguments can be obtained by calling the param function on the first line like that: `param($ParamName1, $ParamName2 = \"default value\", [{type}]$ParamName3, ...)`\nI get the following error: {error}\n</contextual_information>\nMy instructions: {description}"
"prompt": "Here's my powershell code: \n```powershell\n{code}\n```\n<contextual_information>\nArguments can be obtained by calling the param function on the first line like that: `param($ParamName1, $ParamName2 = \"default value\", [{type}]$ParamName3, ...)`\n</contextual_information>\nMy instructions: {description}"
},
"nativets": {
"prompt": "Here's my TypeScript code: \n```typescript\n{code}\n```\n<contextual_information>\nWe have to export a \"main\" function like this: \"export async function main(...)\" and specify the parameter types but do not call it.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. Name the resource parameters like this: \"{resource_type}Resource\".\nThe following resource types are available:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nOnly define the type for resources that are actually needed to achieve the function purpose. The resource type name has to be exactly as specified. If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.\n</contextual_information>\nMy instructions: {description}"
@@ -111,6 +111,17 @@ prompts:
</contextual_information>
I get the following error: {error}
Fix my code.
mssql:
prompt: |-
Here's my Microsoft SQL Server code:
```sql
{code}
```
<contextual_information>
Arguments can be obtained directly in the statement with @p1, @p2, etc.. Name the parameters by adding comments before the statement like that: `-- @p1 name1 ({type})` or `-- @p2 name2 ({type}) = default` (one per row)
</contextual_information>
I get the following error: {error}
Fix my code.
graphql:
prompt: |-
Here's my graphql code:
@@ -25,6 +25,9 @@ export const FIX_PROMPT = {
"snowflake": {
"prompt": "Here's my snowflake code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"mssql": {
"prompt": "Here's my Microsoft SQL Server code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with @p1, @p2, etc.. Name the parameters by adding comments before the statement like that: `-- @p1 name1 ({type})` or `-- @p2 name2 ({type}) = default` (one per row)\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"graphql": {
"prompt": "Here's my graphql code: \n```graphql\n{code}\n```\n<contextual_information>\nAdd the needed arguments as query parameters.\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
@@ -45,6 +45,9 @@ prompts:
snowflake:
prompt: |-
Write SQL code for snowflake that should {description}. Arguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)
mssql:
prompt: |-
Write SQL code for Microsoft SQL Server that should {description}. Arguments can be obtained directly in the statement with @p1, @p2, etc.. Name the parameters by adding comments before the statement like that: `-- @p1 name1 ({type})` or `-- @p2 name2 ({type}) = default` (one per row)
graphql:
prompt: |-
Write a GraphQL query that should {description}. Add the needed arguments as query parameters.
@@ -25,6 +25,9 @@ export const GEN_PROMPT = {
"snowflake": {
"prompt": "Write SQL code for snowflake that should {description}. Arguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)"
},
"mssql": {
"prompt": "Write SQL code for Microsoft SQL Server that should {description}. Arguments can be obtained directly in the statement with @p1, @p2, etc.. Name the parameters by adding comments before the statement like that: `-- @p1 name1 ({type})` or `-- @p2 name2 ({type}) = default` (one per row)"
},
"graphql": {
"prompt": "Write a GraphQL query that should {description}. Add the needed arguments as query parameters."
},
@@ -219,6 +219,20 @@
}}
/>
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="MS SQL Server"
lang={Script.language.MSSQL}
on:click={() => {
dispatch('new', {
language: RawScript.language.MSSQL,
kind,
subkind: 'flow',
summary
})
}}
/>
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="GraphQL"
@@ -19,7 +19,7 @@
export let id: string | undefined = undefined
const enterpriseLangs = ['bigquery', 'snowflake']
const enterpriseLangs = ['bigquery', 'snowflake', 'mssql']
</script>
<Popover disablePopup={!enterpriseLangs.includes(lang || '') || !!$enterpriseLicense}>
File diff suppressed because one or more lines are too long
@@ -65,6 +65,7 @@ import GraphqlIcon from './GraphqlIcon.svelte'
import NocoDbIcon from './NocoDbIcon.svelte'
import AzureIcon from './AzureIcon.svelte'
import OktaIcon from './OktaIcon.svelte'
import MsSqlServerIcon from './MSSqlServerIcon.svelte'
export const APP_TO_ICON_COMPONENT = {
postgresql: PostgresIcon,
@@ -90,6 +91,7 @@ export const APP_TO_ICON_COMPONENT = {
mailchimp: MailchimpIcon,
sendgrid: SendgridIcon,
snowflake: SendflakeIcon,
ms_sql_server: MsSqlServerIcon,
qrcode: QRCodeIcon,
linkedin: LinkedinIcon,
hubspot: HubspotIcon,
+8 -1
View File
@@ -14,7 +14,8 @@ import init, {
parse_snowflake,
parse_graphql,
parse_powershell,
parse_outputs
parse_outputs,
parse_mssql
} from 'windmill-parser-wasm'
import wasmUrl from 'windmill-parser-wasm/windmill_parser_wasm_bg.wasm?url'
import { workspaceStore } from './stores.js'
@@ -66,6 +67,12 @@ export async function inferArgs(
{ name: 'database', typ: { resource: 'snowflake' } },
...inferedSchema.args
]
} else if (language == 'mssql') {
inferedSchema = JSON.parse(parse_mssql(code))
inferedSchema.args = [
{ name: 'database', typ: { resource: 'ms_sql_server' } },
...inferedSchema.args
]
} else if (language == 'graphql') {
inferedSchema = JSON.parse(parse_graphql(code))
inferedSchema.args = [{ name: 'api', typ: { resource: 'graphql' } }, ...inferedSchema.args]
+10 -1
View File
@@ -139,7 +139,8 @@ export async function main(message: string, name: string) {
export const POSTGRES_INIT_CODE = `-- $1 name1 = default arg
-- $2 name2
INSERT INTO demo VALUES (\$1::TEXT, \$2::INT) RETURNING *
-- $3 name3
INSERT INTO demo VALUES (\$1::TEXT, \$2::INT, \$3::TEXT[]) RETURNING *
`
export const MYSQL_INIT_CODE = `-- ? name1 (text) = default arg
@@ -158,6 +159,11 @@ export const SNOWFLAKE_INIT_CODE = `-- ? name1 (varchar) = default arg
INSERT INTO demo VALUES (?, ?)
`
export const MSSQL_INIT_CODE = `-- @p1 name1 (varchar) = default arg
-- @p2 name2 (int)
INSERT INTO demo VALUES (@p1, @p2)
`
export const GRAPHQL_INIT_CODE = `query($name4: String, $name2: Int, $name3: [String]) {
demo(name1: $name1, name2: $name2, name3: $name3) {
name1,
@@ -291,6 +297,7 @@ const ALL_INITIAL_CODE = [
MYSQL_INIT_CODE,
BIGQUERY_INIT_CODE,
SNOWFLAKE_INIT_CODE,
MSSQL_INIT_CODE,
GRAPHQL_INIT_CODE,
DENO_INIT_CODE_TRIGGER,
DENO_INIT_CODE_CLEAR,
@@ -368,6 +375,8 @@ export function initialCode(
return BIGQUERY_INIT_CODE
} else if (language == 'snowflake') {
return SNOWFLAKE_INIT_CODE
} else if (language == 'mssql') {
return MSSQL_INIT_CODE
} else if (language == 'graphql') {
return GRAPHQL_INIT_CODE
} else if (language == 'bun') {
+2
View File
@@ -20,6 +20,8 @@ export function scriptLangToEditorLang(lang: Script.language) {
return 'sql'
} else if (lang == 'snowflake') {
return 'sql'
} else if (lang == 'mssql') {
return 'sql'
} else if (lang == 'python3') {
return 'python'
} else if (lang == 'bash') {
+1 -2
View File
@@ -78,7 +78,7 @@ type SQLBaseSchema = {
}
export interface SQLSchema {
lang: 'mysql' | 'bigquery' | 'postgresql' | 'snowflake'
lang: 'mysql' | 'bigquery' | 'postgresql' | 'snowflake' | 'mssql'
schema: SQLBaseSchema
publicOnly: boolean | undefined
}
@@ -93,4 +93,3 @@ export type DBSchema = SQLSchema | GraphqlSchema
type DBSchemas = Partial<Record<string, DBSchema>>
export const dbSchemas = writable<DBSchemas>({})
@@ -396,7 +396,7 @@
<Badge color="red">priority: {job.priority}</Badge>
</div>
{/if}
{#if job.tag && !['deno', 'python3', 'flow', 'other', 'go', 'postgresql', 'mysql', 'bigquery', 'snowflake', 'graphql', 'nativets', 'bash', 'powershell', 'other', 'dependency'].includes(job.tag)}
{#if job.tag && !['deno', 'python3', 'flow', 'other', 'go', 'postgresql', 'mysql', 'bigquery', 'snowflake', 'mssql', 'graphql', 'nativets', 'bash', 'powershell', 'other', 'dependency'].includes(job.tag)}
<div>
<Badge color="indigo">Tag: {job.tag}</Badge>
</div>
+1
View File
@@ -214,6 +214,7 @@ components:
- mysql
- bigquery
- snowflake
- mssql
- graphql
- nativets
path: