mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-07 08:02:40 +00:00
fix: track dollar-quoted strings in SQL block splitter (#8891)
* fix: track dollar-quoted strings in SQL block splitter Queries like `CREATE FUNCTION ... AS $$ ... ; ... $$ LANGUAGE plpgsql;` were being shredded on every `;` inside the function body because the SQL splitter's state machine didn't recognize PostgreSQL dollar-quoted strings. Add an `InDollarQuote(tag)` state so `$$ ... $$` and `$tag$ ... $tag$` regions are treated as a single quoted span. Opt-in via a new `track_dollar_quotes` flag on `parse_sql_blocks`; enabled for PostgreSQL and DuckDB, disabled for MySQL/Oracle/BigQuery/ Snowflake which don't support the syntax. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: make windmill-parser-wasm a self-contained workspace The wasm parser crate is excluded from the backend workspace (its nightly-only `cargo-features = ["panic-immediate-abort"]` would break stable cargo on the whole workspace), but its manifest still used `.workspace = true` inheritance — which fails with "failed to find a workspace root" once the parent no longer considers it a member. Declare the crate as its own workspace by adding `[workspace]`, `[workspace.package]`, and `[workspace.dependencies]` tables. Mirror the relevant entries from the parent `backend/Cargo.toml` (same version specs, same path targets) so resolution stays byte-identical to what the parent would have produced. Also: - Teach `.github/change-versions.sh` (+ mac variant) to update this crate's own `Cargo.toml` version and bulk-bump the `windmill-*` entries in its `Cargo.lock` on each release. - Bump the frontend's pinned `windmill-parser-wasm-regex` to 1.688.0 to match the freshly-built package, and refresh `package-lock.json`. - Regenerate the wasm crate's `Cargo.lock` from scratch (first build under the new workspace re-resolves the full graph; target-gated deps from sibling crates like `windmill-parser-py-imports` are now recorded in the lockfile but not compiled when targeting wasm32). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
2d4fadb590
commit
53badf1a8c
@@ -20,4 +20,10 @@ sed -i '' -e "/^wmill =/s/= .*/= \">=$VERSION\"/" ${root_dirpath}/lsp/Pipfile
|
||||
|
||||
sed -i '' -E "s/name = \"windmill\"\nversion = \"[^\"]*\"\\n(.*)/name = \"windmill\"\nversion = \"$VERSION\"\\n\\1/" ${root_dirpath}/backend/Cargo.lock
|
||||
|
||||
# windmill-parser-wasm is its own workspace (excluded from the backend workspace
|
||||
# because of nightly-only cargo-features), so its version lives in
|
||||
# [workspace.package] and its Cargo.lock is not regenerated by the backend step.
|
||||
sed -i '' -e "/^version =/s/= .*/= \"$VERSION\"/" ${root_dirpath}/backend/parsers/windmill-parser-wasm/Cargo.toml
|
||||
sed -i '' -E "s/(name = \"windmill[^\"]*\"\nversion = )\"[^\"]*\"/\\1\"$VERSION\"/g" ${root_dirpath}/backend/parsers/windmill-parser-wasm/Cargo.lock
|
||||
|
||||
cd ${root_dirpath}/frontend && npm i --package-lock-only
|
||||
|
||||
@@ -21,4 +21,10 @@ sed -i -e "/^wmill =/s/= .*/= \">=$VERSION\"/" ${root_dirpath}/lsp/Pipfile
|
||||
|
||||
sed -i -zE "s/name = \"windmill\"\nversion = \"[^\"]*\"\\n(.*)/name = \"windmill\"\nversion = \"$VERSION\"\\n\\1/" ${root_dirpath}/backend/Cargo.lock
|
||||
|
||||
# windmill-parser-wasm is its own workspace (excluded from the backend workspace
|
||||
# because of nightly-only cargo-features), so its version lives in
|
||||
# [workspace.package] and its Cargo.lock is not regenerated by the backend step.
|
||||
sed -i -e "/^version =/s/= .*/= \"$VERSION\"/" ${root_dirpath}/backend/parsers/windmill-parser-wasm/Cargo.toml
|
||||
sed -i -zE "s/(name = \"windmill[^\"]*\"\nversion = )\"[^\"]*\"/\\1\"$VERSION\"/g" ${root_dirpath}/backend/parsers/windmill-parser-wasm/Cargo.lock
|
||||
|
||||
cd ${root_dirpath}/frontend && npm i --package-lock-only --ignore-scripts
|
||||
|
||||
@@ -15,7 +15,9 @@ use std::{
|
||||
iter::Peekable,
|
||||
str::CharIndices,
|
||||
};
|
||||
pub use windmill_parser::{s3_mode_extension, Arg, MainArgSignature, ObjectType, S3ModeFormat, Typ};
|
||||
pub use windmill_parser::{
|
||||
s3_mode_extension, Arg, MainArgSignature, ObjectType, S3ModeFormat, Typ,
|
||||
};
|
||||
|
||||
pub const SANITIZED_ENUM_STR: &str = "__sanitized_enum__";
|
||||
pub const SANITIZED_RAW_STRING_STR: &str = "__sanitized_raw_string__";
|
||||
@@ -191,12 +193,13 @@ pub fn parse_s3_mode(code: &str) -> anyhow::Result<Option<S3ModeArgs>> {
|
||||
Ok(Some(S3ModeArgs { prefix, storage, format }))
|
||||
}
|
||||
|
||||
pub fn parse_sql_blocks(code: &str) -> Vec<&str> {
|
||||
pub fn parse_sql_blocks(code: &str, track_dollar_quotes: bool) -> Vec<&str> {
|
||||
let mut blocks = vec![];
|
||||
let mut last_idx = 0;
|
||||
|
||||
run_on_sql_statement_matches(
|
||||
code,
|
||||
track_dollar_quotes,
|
||||
|char, _| char == ';',
|
||||
|idx, _| {
|
||||
blocks.push(&code[last_idx..=idx]);
|
||||
@@ -395,6 +398,44 @@ enum ParserState {
|
||||
InDoubleQuote,
|
||||
InSingleLineComment,
|
||||
InMultiLineComment,
|
||||
// Stores the full `$tag$` delimiter (including both `$`s). On re-encountering
|
||||
// the same delimiter we return to `Normal`. An empty tag yields `$$`.
|
||||
InDollarQuote(String),
|
||||
}
|
||||
|
||||
// If `code[idx..]` starts with a PostgreSQL dollar-quote delimiter (`$$` or
|
||||
// `$tag$`), returns the delimiter's byte length. The tag follows identifier
|
||||
// rules (first char letter/underscore, subsequent letters/digits/underscores),
|
||||
// which naturally rejects placeholder syntax like `$1` or `$2::int`.
|
||||
fn parse_dollar_quote_delimiter(code: &str, idx: usize) -> Option<usize> {
|
||||
let bytes = code.as_bytes();
|
||||
if bytes.get(idx) != Some(&b'$') {
|
||||
return None;
|
||||
}
|
||||
let mut i = idx + 1;
|
||||
while i < bytes.len() {
|
||||
let b = bytes[i];
|
||||
if b == b'$' {
|
||||
return Some(i + 1 - idx);
|
||||
}
|
||||
let is_first = i == idx + 1;
|
||||
let ok = if is_first {
|
||||
b.is_ascii_alphabetic() || b == b'_'
|
||||
} else {
|
||||
b.is_ascii_alphanumeric() || b == b'_'
|
||||
};
|
||||
if !ok {
|
||||
return None;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn advance_past<I: Iterator<Item = (usize, char)>>(chars: &mut Peekable<I>, target: usize) {
|
||||
while chars.peek().map_or(false, |&(i, _)| i < target) {
|
||||
chars.next();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_on_sql_statement_matches<
|
||||
@@ -402,6 +443,7 @@ fn run_on_sql_statement_matches<
|
||||
F2: FnMut(usize, &mut Peekable<CharIndices>) -> (),
|
||||
>(
|
||||
code: &str,
|
||||
track_dollar_quotes: bool,
|
||||
mut cond: F1,
|
||||
mut case: F2,
|
||||
) {
|
||||
@@ -425,6 +467,16 @@ fn run_on_sql_statement_matches<
|
||||
{
|
||||
state = ParserState::InMultiLineComment;
|
||||
}
|
||||
(ParserState::Normal, '$') if track_dollar_quotes => {
|
||||
if let Some(delim_len) = parse_dollar_quote_delimiter(code, idx) {
|
||||
let delim_end = idx + delim_len;
|
||||
let delim = code[idx..delim_end].to_string();
|
||||
advance_past(&mut chars, delim_end);
|
||||
state = ParserState::InDollarQuote(delim);
|
||||
} else if cond(char, &mut chars) {
|
||||
case(idx, &mut chars);
|
||||
}
|
||||
}
|
||||
(ParserState::Normal, _) if cond(char, &mut chars) => {
|
||||
case(idx, &mut chars);
|
||||
}
|
||||
@@ -453,6 +505,13 @@ fn run_on_sql_statement_matches<
|
||||
{
|
||||
state = ParserState::Normal;
|
||||
}
|
||||
(ParserState::InDollarQuote(delim), '$') => {
|
||||
if code[idx..].starts_with(delim.as_str()) {
|
||||
let target = idx + delim.len();
|
||||
advance_past(&mut chars, target);
|
||||
state = ParserState::Normal;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -462,6 +521,7 @@ pub fn parse_pg_statement_arg_indices(code: &str) -> HashSet<i32> {
|
||||
let mut arg_indices = HashSet::new();
|
||||
run_on_sql_statement_matches(
|
||||
code,
|
||||
true,
|
||||
|char, chars| {
|
||||
char == '$'
|
||||
&& chars
|
||||
@@ -631,6 +691,7 @@ pub fn parse_sql_statement_named_params(code: &str, prefix: char) -> HashSet<Str
|
||||
let mut arg_names = HashSet::new();
|
||||
run_on_sql_statement_matches(
|
||||
code,
|
||||
false,
|
||||
|char, chars| {
|
||||
char == prefix
|
||||
&& chars
|
||||
@@ -952,7 +1013,7 @@ SELECT * FROM table WHERE token=$1::TEXT AND image=$2::BIGINT
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1002,7 +1063,7 @@ SELECT $2::TEXT;
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1019,7 +1080,7 @@ SELECT '--', ';' $3::TEXT, $1::BIGINT;
|
||||
-- ;
|
||||
SELECT $2::TEXT;
|
||||
"#;
|
||||
assert_eq!(parse_sql_blocks(code).len(), 2);
|
||||
assert_eq!(parse_sql_blocks(code, true).len(), 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1035,7 +1096,7 @@ SELECT '--', ';' $3::TEXT, $1::BIGINT;
|
||||
SELECT $2::TEXT
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse_sql_blocks(code),
|
||||
parse_sql_blocks(code, true),
|
||||
vec![
|
||||
r#"
|
||||
-- $1 param1
|
||||
@@ -1062,7 +1123,7 @@ SELECT '--', ';' $3::TEXT, $1::BIGINT;
|
||||
-- hey
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse_sql_blocks(code),
|
||||
parse_sql_blocks(code, true),
|
||||
vec![
|
||||
r#"
|
||||
-- $1 param1
|
||||
@@ -1084,7 +1145,7 @@ SELECT '--', ';' $3::TEXT, $1::BIGINT;"#,
|
||||
SELECT '--', ';' $3::TEXT, $1::BIGINT
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse_sql_blocks(code),
|
||||
parse_sql_blocks(code, true),
|
||||
vec![
|
||||
r#"
|
||||
-- $1 param1
|
||||
@@ -1098,6 +1159,81 @@ SELECT '--', ';' $3::TEXT, $1::BIGINT
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_sql_blocks_dollar_quoted_function() -> anyhow::Result<()> {
|
||||
let code = r#"CREATE OR REPLACE FUNCTION track_status_change()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF OLD.status IS DISTINCT FROM NEW.status THEN
|
||||
INSERT INTO use_case_card_history (use_case_id, old_status, new_status)
|
||||
VALUES (NEW.id, OLD.status, NEW.status);
|
||||
END IF;
|
||||
NEW.updated_at := NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;"#;
|
||||
assert_eq!(parse_sql_blocks(code, true), vec![code]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_sql_blocks_dollar_quoted_tagged() -> anyhow::Result<()> {
|
||||
let code = r#"CREATE FUNCTION f() RETURNS int AS $body$ BEGIN RETURN 1; END; $body$ LANGUAGE plpgsql;
|
||||
SELECT 1;"#;
|
||||
let blocks = parse_sql_blocks(code, true);
|
||||
assert_eq!(blocks.len(), 2);
|
||||
assert!(blocks[0].contains("$body$"));
|
||||
assert!(blocks[0].contains("END;"));
|
||||
assert_eq!(blocks[1], "\nSELECT 1;");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_sql_blocks_dollar_quote_does_not_match_placeholder() -> anyhow::Result<()> {
|
||||
// `$1`, `$2` must still be treated as placeholders, not dollar-quote openers.
|
||||
let code = r#"SELECT $1::TEXT, $2::BIGINT;
|
||||
SELECT $1 FROM t;"#;
|
||||
assert_eq!(parse_sql_blocks(code, true).len(), 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pg_statement_arg_indices_inside_dollar_quote() -> anyhow::Result<()> {
|
||||
// `$1` inside a dollar-quoted body is part of the string literal, not a parameter.
|
||||
let code = r#"CREATE FUNCTION f() RETURNS int AS $$ SELECT $1 $$ LANGUAGE sql;
|
||||
SELECT $2;"#;
|
||||
let indices = parse_pg_statement_arg_indices(code);
|
||||
assert!(!indices.contains(&1), "$1 inside $$...$$ should be ignored");
|
||||
assert!(
|
||||
indices.contains(&2),
|
||||
"$2 outside dollar-quote should be collected"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_sql_blocks_non_pg_ignores_dollar_quotes() -> anyhow::Result<()> {
|
||||
// Non-Postgres dialects (MySQL/Oracle/BigQuery/Snowflake) pass `false`,
|
||||
// so a bare `$tag$...;...$tag$` sequence must still split on `;`.
|
||||
let code = "SELECT $foo$; SELECT 1;";
|
||||
assert_eq!(parse_sql_blocks(code, false).len(), 2);
|
||||
// With tracking on (PG), `$foo$` opens a dollar-quote that is never closed,
|
||||
// so the whole thing becomes a single block.
|
||||
assert_eq!(parse_sql_blocks(code, true).len(), 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_sql_blocks_nested_tag_mismatch() -> anyhow::Result<()> {
|
||||
// An inner tag that doesn't match the outer one does not terminate the outer quote.
|
||||
let code = r#"CREATE FUNCTION f() RETURNS int AS $outer$ SELECT $inner$ x; y $inner$ ; $outer$ LANGUAGE sql;
|
||||
SELECT 1;"#;
|
||||
let blocks = parse_sql_blocks(code, true);
|
||||
assert_eq!(blocks.len(), 2);
|
||||
assert!(blocks[0].contains("$outer$ LANGUAGE sql;"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_mysql_positional_sig() -> anyhow::Result<()> {
|
||||
let code = r#"
|
||||
@@ -1130,7 +1266,7 @@ SELECT ?, ?;
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1179,7 +1315,7 @@ SELECT :param2;
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1220,7 +1356,7 @@ SELECT @token;
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1269,7 +1405,7 @@ SELECT ?;
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1318,7 +1454,7 @@ SELECT @P2;
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1368,7 +1504,7 @@ SELECT * FROM table_name WHERE thing = :name4;
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1407,7 +1543,7 @@ SELECT * FROM users WHERE id = $1 AND email = $2::text;
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1446,7 +1582,7 @@ SELECT * FROM users LIMIT $1 OFFSET $2;
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1497,7 +1633,7 @@ WHERE id = $1
|
||||
],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1525,7 +1661,7 @@ SELECT * FROM users WHERE id = ANY($1);
|
||||
},],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1555,7 +1691,7 @@ SELECT $1::integer;
|
||||
},],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1588,7 +1724,7 @@ SELECT x
|
||||
},],
|
||||
auto_kind: None,
|
||||
has_preprocessor: None,
|
||||
..Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
+2508
-73
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,48 @@
|
||||
cargo-features = ["panic-immediate-abort"]
|
||||
|
||||
# This crate is intentionally excluded from the parent backend workspace
|
||||
# (see `backend/Cargo.toml` `exclude = [...]`) because it uses nightly-only
|
||||
# features (`cargo-features`, `-Z build-std`) that would break stable builds.
|
||||
# It declares its own workspace here so that `.workspace = true` inheritance
|
||||
# resolves against its own `[workspace.package]` / `[workspace.dependencies]`
|
||||
# tables below. Sibling windmill-parser-* crates are referenced by path and
|
||||
# keep using the parent workspace for their own dependencies.
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["."]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.688.0"
|
||||
edition = "2021"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = "^1"
|
||||
serde_json = { version = "^1", features = ["preserve_order", "raw_value"] }
|
||||
wasm-bindgen = "=0.2.103"
|
||||
wasm-bindgen-test = "^0"
|
||||
getrandom = "0.2"
|
||||
windmill-parser = { path = "../windmill-parser" }
|
||||
windmill-parser-ts = { path = "../windmill-parser-ts" }
|
||||
windmill-parser-ts-asset = { path = "../windmill-parser-ts-asset" }
|
||||
windmill-parser-py = { path = "../windmill-parser-py" }
|
||||
windmill-parser-py-asset = { path = "../windmill-parser-py-asset" }
|
||||
windmill-parser-py-imports = { path = "../windmill-parser-py-imports" }
|
||||
windmill-parser-go = { path = "../windmill-parser-go" }
|
||||
windmill-parser-rust = { path = "../windmill-parser-rust" }
|
||||
windmill-parser-yaml = { path = "../windmill-parser-yaml" }
|
||||
windmill-parser-csharp = { path = "../windmill-parser-csharp" }
|
||||
windmill-parser-java = { path = "../windmill-parser-java" }
|
||||
windmill-parser-ruby = { path = "../windmill-parser-ruby" }
|
||||
windmill-parser-r = { path = "../windmill-parser-r" }
|
||||
windmill-parser-nu = { path = "../windmill-parser-nu" }
|
||||
windmill-parser-bash = { path = "../windmill-parser-bash" }
|
||||
windmill-parser-sql = { path = "../windmill-parser-sql" }
|
||||
windmill-parser-sql-asset = { path = "../windmill-parser-sql-asset" }
|
||||
windmill-parser-graphql = { path = "../windmill-parser-graphql" }
|
||||
windmill-parser-php = { path = "../windmill-parser-php" }
|
||||
windmill-parser-wac = { path = "../windmill-parser-wac" }
|
||||
|
||||
[package]
|
||||
name = "windmill-parser-wasm"
|
||||
version.workspace = true
|
||||
|
||||
@@ -392,7 +392,7 @@ pub async fn do_bigquery(
|
||||
&reserved_variables,
|
||||
)?;
|
||||
|
||||
let queries = parse_sql_blocks(query);
|
||||
let queries = parse_sql_blocks(query, false);
|
||||
|
||||
let mut statement_values: HashMap<String, Value> = HashMap::new();
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ pub async fn do_duckdb(
|
||||
m
|
||||
};
|
||||
|
||||
let query_block_list = parse_sql_blocks(&query);
|
||||
let query_block_list = parse_sql_blocks(&query, true);
|
||||
|
||||
// Replace custom ATTACH statements with the real instructions
|
||||
let query_block_list = {
|
||||
@@ -649,7 +649,6 @@ async fn transform_attach_ducklake(
|
||||
.unwrap_or(DEFAULT_STORAGE);
|
||||
let data_path = ducklake.storage.path;
|
||||
|
||||
|
||||
let extra_args = if let Some(default_extra_args) = ducklake.extra_args {
|
||||
format!("{},{}", extra_args, default_extra_args)
|
||||
} else {
|
||||
@@ -669,7 +668,6 @@ async fn transform_attach_ducklake(
|
||||
} else {
|
||||
format!(", AUTOMATIC_MIGRATION TRUE{extra_args}")
|
||||
};
|
||||
|
||||
|
||||
let attach_str = format!(
|
||||
"ATTACH 'ducklake:{db_type}:{db_conn_str}' AS {alias_name} (DATA_PATH 's3://{storage}/{data_path}'{extra_args});",
|
||||
|
||||
@@ -307,7 +307,7 @@ pub async fn do_mysql(
|
||||
let mysql_conn = pool.get_conn().await.map_err(to_anyhow)?;
|
||||
let conn_a = Arc::new(Mutex::new(mysql_conn));
|
||||
|
||||
let queries = parse_sql_blocks(query);
|
||||
let queries = parse_sql_blocks(query, false);
|
||||
|
||||
let conn_a_ref = &conn_a;
|
||||
let result_f = async move {
|
||||
|
||||
@@ -430,7 +430,7 @@ pub async fn do_oracledb(
|
||||
|
||||
let conn_a = Arc::new(std::sync::Mutex::new(oracle_conn));
|
||||
|
||||
let queries = parse_sql_blocks(&query);
|
||||
let queries = parse_sql_blocks(&query, false);
|
||||
|
||||
let result_f = async move {
|
||||
let mut results = vec![];
|
||||
|
||||
@@ -394,7 +394,7 @@ pub async fn do_postgresql(
|
||||
let (query, _) =
|
||||
&sanitize_and_interpolate_unsafe_sql_args(query, &sig.args, &pg_args, &reserved_variables)?;
|
||||
|
||||
let queries = parse_sql_blocks(query);
|
||||
let queries = parse_sql_blocks(query, true);
|
||||
|
||||
let (client, handle) = if let Some((client, handle)) = new_client.as_ref() {
|
||||
(client, Some(handle))
|
||||
|
||||
@@ -659,7 +659,7 @@ pub async fn do_snowflake(
|
||||
.as_secs();
|
||||
body.insert("timeout".to_string(), json!(timeout));
|
||||
|
||||
let queries = parse_sql_blocks(query);
|
||||
let queries = parse_sql_blocks(query, false);
|
||||
|
||||
let (timeout_duration, _, _) =
|
||||
resolve_job_timeout(&conn, &job.workspace_id, job.id, job.timeout).await;
|
||||
|
||||
Generated
+6
-52
@@ -83,7 +83,7 @@
|
||||
"windmill-parser-wasm-php": "1.647.1",
|
||||
"windmill-parser-wasm-py": "1.657.2",
|
||||
"windmill-parser-wasm-r": "1.668.1",
|
||||
"windmill-parser-wasm-regex": "^1.670.0",
|
||||
"windmill-parser-wasm-regex": "1.688.0",
|
||||
"windmill-parser-wasm-ruby": "1.526.1",
|
||||
"windmill-parser-wasm-rust": "1.647.1",
|
||||
"windmill-parser-wasm-ts": "1.657.2",
|
||||
@@ -844,7 +844,6 @@
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz",
|
||||
"integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@@ -856,7 +855,6 @@
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz",
|
||||
"integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@@ -867,7 +865,6 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz",
|
||||
"integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@@ -1357,7 +1354,6 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
|
||||
"integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@@ -1514,7 +1510,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1531,7 +1526,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1548,7 +1542,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1565,7 +1558,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1582,7 +1574,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1599,7 +1590,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1616,7 +1606,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1633,7 +1622,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1650,7 +1638,6 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1667,7 +1654,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1684,7 +1670,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1701,7 +1686,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1718,7 +1702,6 @@
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@@ -1735,7 +1718,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1752,7 +1734,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2058,7 +2039,6 @@
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@@ -6834,7 +6814,7 @@
|
||||
"version": "1.21.7",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
|
||||
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jiti": "bin/jiti.js"
|
||||
@@ -7333,7 +7313,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7354,7 +7333,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7375,7 +7353,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7396,7 +7373,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7417,7 +7393,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7438,7 +7413,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7459,7 +7433,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7480,7 +7453,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7501,7 +7473,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7522,7 +7493,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7543,7 +7513,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -12112,21 +12081,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-check/node_modules/picomatch": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-eslint-parser": {
|
||||
"version": "0.43.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.43.0.tgz",
|
||||
@@ -12857,7 +12811,7 @@
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@@ -13671,9 +13625,9 @@
|
||||
"integrity": "sha512-5YNeUibxpNBvYrxCgQcz1PxGhTFx2CyEpg2udtIhq7bx0d4gF/KDZVupMeQmAObmrEtTSFGUWNRJ4zXSWNrSpQ=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-regex": {
|
||||
"version": "1.670.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-regex/-/windmill-parser-wasm-regex-1.670.0.tgz",
|
||||
"integrity": "sha512-foQBwLn7L3JpmPSzMYmFhpVvUnXB1KX4v1rGBsm5W6KircwrjW3VXl/Ih9Izjyju2RRK+tYzjN8vvd2Zo7ulKQ=="
|
||||
"version": "1.688.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-regex/-/windmill-parser-wasm-regex-1.688.0.tgz",
|
||||
"integrity": "sha512-TB6ysy8nRcWDPRR79ujDihJc3S4oJTqawWSjUF837JDxaG595P9osZErxR4MOO5Ye5MecKESeIgP3UDK6O6bhg=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-ruby": {
|
||||
"version": "1.526.1",
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
"windmill-parser-wasm-php": "1.647.1",
|
||||
"windmill-parser-wasm-py": "1.657.2",
|
||||
"windmill-parser-wasm-r": "1.668.1",
|
||||
"windmill-parser-wasm-regex": "^1.670.0",
|
||||
"windmill-parser-wasm-regex": "1.688.0",
|
||||
"windmill-parser-wasm-ruby": "1.526.1",
|
||||
"windmill-parser-wasm-rust": "1.647.1",
|
||||
"windmill-parser-wasm-ts": "1.657.2",
|
||||
|
||||
Reference in New Issue
Block a user