From 59cdb141c339a8fac49462d0ce7aa27c61ce89be Mon Sep 17 00:00:00 2001 From: Diego Imbert <70353967+diegoimbert@users.noreply.github.com> Date: Thu, 2 Oct 2025 12:10:53 +0200 Subject: [PATCH] NULL Toggle in InsertRow drawer (#6729) * NULL toggle in InsertRow * fix long type parsing in postgres * nits * graphite catch * lazy_static * support for time/timestamp/tz long forms in pg parser * graphite suggestion --- .../parsers/windmill-parser-sql/src/lib.rs | 40 +++++++++++++++++-- backend/windmill-worker/src/pg_executor.rs | 2 +- frontend/src/lib/common.ts | 1 + frontend/src/lib/components/SchemaForm.svelte | 4 +- .../display/dbtable/InsertRow.svelte | 30 +++++++++++++- .../display/dbtable/queries/insert.ts | 1 - 6 files changed, 70 insertions(+), 8 deletions(-) diff --git a/backend/parsers/windmill-parser-sql/src/lib.rs b/backend/parsers/windmill-parser-sql/src/lib.rs index 5856fbc4fd..7038ddab3c 100644 --- a/backend/parsers/windmill-parser-sql/src/lib.rs +++ b/backend/parsers/windmill-parser-sql/src/lib.rs @@ -2,6 +2,7 @@ use anyhow::anyhow; +use lazy_static::lazy_static; #[cfg(not(target_arch = "wasm32"))] use regex::Regex; #[cfg(target_arch = "wasm32")] @@ -491,13 +492,15 @@ fn parse_pg_file(code: &str) -> anyhow::Result>> { let mut args = vec![]; let mut hm: HashMap = HashMap::new(); for cap in RE_CODE_PGSQL.captures_iter(code) { + let typ = cap + .get(2) + .map(|cap| transform_types_with_spaces(&cap, &code)) + .unwrap_or("text"); hm.insert( cap.get(1) .and_then(|x| x.as_str().parse::().ok()) .ok_or_else(|| anyhow!("Impossible to parse arg digit"))?, - cap.get(2) - .map(|x| x.as_str().to_string()) - .unwrap_or_else(|| "text".to_string()), + typ.to_string(), ); } for (i, v) in hm.iter() { @@ -543,6 +546,37 @@ fn parse_pg_file(code: &str) -> anyhow::Result>> { Ok(Some(args)) } +// The regex doesn't parse types with space such as "character varying" +// So we look for them manually and replace them with their shorter counterpart +fn transform_types_with_spaces<'a>(cap: ®ex::Match<'a>, code: &str) -> &'a str { + lazy_static! { + static ref TYPES: [(&'static str, &'static str); 6] = [ + ("character varying", "varchar"), + ("double precision", "double"), + ("time with time zone", "timetz"), + ("time without time zone", "time"), + ("timestamp with time zone", "timestamptz"), + ("timestamp without time zone", "timestamp"), + ]; + } + let typ = &code[cap.start()..]; + for (long_type, alias) in TYPES.iter() { + let mut typ = typ; + let mut found_mismatch = false; + for token in long_type.split(' ') { + if typ.len() < token.len() || !typ[..token.len()].eq_ignore_ascii_case(token) { + found_mismatch = true; + break; + } + typ = typ[token.len()..].trim_start(); + } + if !found_mismatch { + return alias; + } + } + cap.as_str() +} + pub fn parse_sql_statement_named_params(code: &str, prefix: char) -> HashSet { let mut arg_names = HashSet::new(); run_on_sql_statement_matches( diff --git a/backend/windmill-worker/src/pg_executor.rs b/backend/windmill-worker/src/pg_executor.rs index 2c7fe123dc..49513d1465 100644 --- a/backend/windmill-worker/src/pg_executor.rs +++ b/backend/windmill-worker/src/pg_executor.rs @@ -88,7 +88,7 @@ fn do_postgresql_inner<'a>( let arg_t = arg .otyp .as_ref() - .ok_or_else(|| anyhow::anyhow!("Missing otzyp for pg arg"))?; + .ok_or_else(|| anyhow::anyhow!("Missing otyp for pg arg"))?; let typ = &arg.typ; let param = convert_val(value, arg_t, typ)?; query_params.push(param); diff --git a/frontend/src/lib/common.ts b/frontend/src/lib/common.ts index 7ea1de146e..f3dc46db7a 100644 --- a/frontend/src/lib/common.ts +++ b/frontend/src/lib/common.ts @@ -49,6 +49,7 @@ export interface SchemaProperty { placeholder?: string oneOf?: SchemaProperty[] originalType?: string + disabled?: boolean } export interface ModalSchemaProperty { diff --git a/frontend/src/lib/components/SchemaForm.svelte b/frontend/src/lib/components/SchemaForm.svelte index 932ec78745..95e436bd27 100644 --- a/frontend/src/lib/components/SchemaForm.svelte +++ b/frontend/src/lib/components/SchemaForm.svelte @@ -70,7 +70,7 @@ | undefined) | undefined workspace?: string | undefined - actions?: import('svelte').Snippet + actions?: import('svelte').Snippet<[{ item: { id: string; value: string } }]> | undefined } let { @@ -414,7 +414,7 @@ {displayType} > {#snippet actions()} - {@render actions_render?.()} + {@render actions_render?.({ item })} {#if linkedSecretCandidates?.includes(argName)}
{#if schema} - + + {#snippet actions({ item })} + {@const disabled = fields?.[fields?.findIndex((f) => f.name === item.id)]?.nullable != 'YES'} + {#if !disabled} + args[item.id] === null, + (v) => { + if (!schema?.properties[item.id]) return + if (v) { + schema.properties[item.id].nullable = true + schema.properties[item.id].disabled = true + args[item.id] = null + } else { + delete schema.properties[item.id].disabled + delete schema.properties[item.id].nullable + args[item.id] = schema.properties[item.id].default ?? '' + } + } + } + /> + {/if} + {/snippet} + {/if} diff --git a/frontend/src/lib/components/apps/components/display/dbtable/queries/insert.ts b/frontend/src/lib/components/apps/components/display/dbtable/queries/insert.ts index c13b6c1bc2..ca83586fee 100644 --- a/frontend/src/lib/components/apps/components/display/dbtable/queries/insert.ts +++ b/frontend/src/lib/components/apps/components/display/dbtable/queries/insert.ts @@ -97,7 +97,6 @@ export function makeInsertQuery(table: string, columns: ColumnDef[], dbType: DbT const commaOrEmpty = shouldInsertComma ? ', ' : '' query += `INSERT INTO ${table} (${columnNames}) VALUES (${insertValues}${commaOrEmpty}${defaultValues})` - return query }