@@ -477,7 +477,15 @@ pub fn signal(instance_id: &str, signal_name: &str, signal_data: default!(&str,
477477/// Variables from df.vars are captured and passed to the orchestration.
478478#[ pg_extern( schema = "df" ) ]
479479pub fn start ( fut : & str , label : default ! ( Option <& str >, "NULL" ) ) -> String {
480- let durofut = Durofut :: ensure ( fut) ;
480+ let durofut = match Durofut :: ensure_strict ( fut) {
481+ Ok ( d) => d,
482+ Err ( e) => pgrx:: error!( "Invalid durable function: {}" , e) ,
483+ } ;
484+
485+ // Validate the entire graph recursively before inserting
486+ if let Err ( e) = durofut. validate_recursive ( ) {
487+ pgrx:: error!( "Invalid durable function graph: {}" , e) ;
488+ }
481489 let instance_id = short_id ( ) ;
482490
483491 let label_sql = label
@@ -504,24 +512,40 @@ pub fn start(fut: &str, label: default!(Option<&str>, "NULL")) -> String {
504512 // For IF/LOOP nodes: replace condition_node Durofut with ID
505513 if node. node_type == "IF" || node. node_type == "LOOP" {
506514 if let Some ( cond_json) = config. get ( "condition_node" ) {
507- if let Ok ( cond_node) = serde_json:: from_value :: < Durofut > ( cond_json. clone ( ) )
508- {
509- let cond_id = insert_nodes ( & cond_node, instance_id) ;
510- config[ "condition_node" ] = serde_json:: json!( cond_id) ;
515+ match serde_json:: from_value :: < Durofut > ( cond_json. clone ( ) ) {
516+ Ok ( cond_node) => {
517+ let cond_id = insert_nodes ( & cond_node, instance_id) ;
518+ config[ "condition_node" ] = serde_json:: json!( cond_id) ;
519+ }
520+ Err ( _) => {
521+ if cond_json. is_string ( ) {
522+ pgrx:: error!(
523+ "condition_node in {} must be a Durofut object, not a string ID" ,
524+ node. node_type
525+ ) ;
526+ }
527+ // For other types, this will be caught by validate_recursive
528+ }
511529 }
512530 }
513531 }
514532 // For JOIN3 nodes: replace extra_nodes Durofuts with IDs
515533 if node. node_type == "JOIN" {
516534 if let Some ( extras) = config. get ( "extra_nodes" ) . and_then ( |e| e. as_array ( ) ) {
517- let extra_ids: Vec < String > = extras
518- . iter ( )
519- . filter_map ( |extra_json| {
520- serde_json:: from_value :: < Durofut > ( extra_json. clone ( ) )
521- . ok ( )
522- . map ( |n| insert_nodes ( & n, instance_id) )
523- } )
524- . collect ( ) ;
535+ let mut extra_ids: Vec < String > = Vec :: new ( ) ;
536+ for ( i, extra_json) in extras. iter ( ) . enumerate ( ) {
537+ match serde_json:: from_value :: < Durofut > ( extra_json. clone ( ) ) {
538+ Ok ( n) => {
539+ extra_ids. push ( insert_nodes ( & n, instance_id) ) ;
540+ }
541+ Err ( _) => {
542+ pgrx:: error!(
543+ "extra_nodes[{}] in JOIN must be a Durofut object" ,
544+ i
545+ ) ;
546+ }
547+ }
548+ }
525549 if !extra_ids. is_empty ( ) {
526550 config[ "extra_nodes" ] = serde_json:: json!( extra_ids) ;
527551 }
0 commit comments