@@ -123,31 +123,53 @@ pub fn translate_column_name_assignment(assignment: &Assignment) -> Option<Strin
123123///
124124/// some_field = $1
125125/// some_table.some_field = $1
126+ ///
127+ /// Also handles the reversed case where the placeholder is on the left:
128+ /// ? >= some_field
129+ /// $1 >= some_table.some_field
126130pub async fn get_sql_query_param (
127131 left : & Expr ,
128132 right : & Expr ,
129133 single_table_name : & Option < & str > ,
130134 table_with_joins : & Option < Vec < TableWithJoins > > ,
131135 db_conn : & DBConn ,
132136 cte_columns : & std:: collections:: HashMap < String , std:: collections:: HashMap < String , TsFieldType > > ,
137+ ) -> Result < Option < ( TsFieldType , bool , Option < String > ) > , TsGeneratorError > {
138+ // Try the standard order first: left=column, right=placeholder
139+ let result =
140+ get_sql_query_param_directed ( left, right, single_table_name, table_with_joins, db_conn, cte_columns) . await ?;
141+ if result. is_some ( ) {
142+ return Ok ( result) ;
143+ }
144+
145+ // Try the reversed order: left=placeholder, right=column
146+ get_sql_query_param_directed ( right, left, single_table_name, table_with_joins, db_conn, cte_columns) . await
147+ }
148+
149+ /// Internal helper that checks a specific direction: column_expr for column name, placeholder_expr for placeholder
150+ async fn get_sql_query_param_directed (
151+ column_expr : & Expr ,
152+ placeholder_expr : & Expr ,
153+ single_table_name : & Option < & str > ,
154+ table_with_joins : & Option < Vec < TableWithJoins > > ,
155+ db_conn : & DBConn ,
156+ cte_columns : & std:: collections:: HashMap < String , std:: collections:: HashMap < String , TsFieldType > > ,
133157) -> Result < Option < ( TsFieldType , bool , Option < String > ) > , TsGeneratorError > {
134158 let table_name: Option < String > ;
135159
136160 if table_with_joins. is_some ( ) {
137- table_name = translate_table_from_expr ( table_with_joins, & left . clone ( ) ) . ok ( ) ;
161+ table_name = translate_table_from_expr ( table_with_joins, & column_expr . clone ( ) ) . ok ( ) ;
138162 } else if single_table_name. is_some ( ) {
139163 table_name = single_table_name. map ( |x| x. to_string ( ) ) ;
140164 } else {
141- return Err ( TsGeneratorError :: TableNameInferenceFailedInWhere {
142- query : left. to_string ( ) ,
143- } ) ;
165+ return Ok ( None ) ;
144166 }
145167
146- let column_name = translate_column_name_expr ( left ) ;
168+ let column_name = translate_column_name_expr ( column_expr ) ;
147169
148- // If the right side of the expression is a placeholder `?` or `$n`
170+ // If the placeholder side of the expression is a placeholder `?` or `$n`
149171 // they are valid query parameter to process
150- let expr_placeholder = get_expr_placeholder ( right ) ;
172+ let expr_placeholder = get_expr_placeholder ( placeholder_expr ) ;
151173
152174 match ( column_name, expr_placeholder, table_name) {
153175 ( Some ( column_name) , Some ( expr_placeholder) , Some ( table_name) ) => {
@@ -407,30 +429,67 @@ pub async fn translate_expr(
407429 low,
408430 high,
409431 } => {
410- let low = get_sql_query_param (
411- expr,
412- low,
413- single_table_name,
414- table_with_joins,
415- db_conn,
416- & ts_query. table_valued_function_columns ,
417- )
418- . await ?;
419- let high = get_sql_query_param (
420- expr,
421- high,
422- single_table_name,
423- table_with_joins,
424- db_conn,
425- & ts_query. table_valued_function_columns ,
426- )
427- . await ?;
428- if let Some ( ( value, is_nullable, placeholder) ) = low {
429- ts_query. insert_param ( & value, & is_nullable, & placeholder) ?;
430- }
431-
432- if let Some ( ( value, is_nullable, placeholder) ) = high {
433- ts_query. insert_param ( & value, & is_nullable, & placeholder) ?;
432+ // BETWEEN has two forms:
433+ // 1. `column BETWEEN ? AND ?` — expr is column, low/high are placeholders
434+ // 2. `? BETWEEN low_col AND high_col` — expr is placeholder, low/high are columns
435+ let expr_is_placeholder = get_expr_placeholder ( expr) . is_some ( ) ;
436+
437+ if expr_is_placeholder {
438+ // Case 2: `? BETWEEN low_col AND high_col`
439+ // The placeholder is the expr itself, infer its type from low (or high) column
440+ let result = get_sql_query_param_directed (
441+ low,
442+ expr,
443+ single_table_name,
444+ table_with_joins,
445+ db_conn,
446+ & ts_query. table_valued_function_columns ,
447+ )
448+ . await ?;
449+ if let Some ( ( value, is_nullable, placeholder) ) = result {
450+ ts_query. insert_param ( & value, & is_nullable, & placeholder) ?;
451+ } else {
452+ // Fallback: try high column
453+ let result = get_sql_query_param_directed (
454+ high,
455+ expr,
456+ single_table_name,
457+ table_with_joins,
458+ db_conn,
459+ & ts_query. table_valued_function_columns ,
460+ )
461+ . await ?;
462+ if let Some ( ( value, is_nullable, placeholder) ) = result {
463+ ts_query. insert_param ( & value, & is_nullable, & placeholder) ?;
464+ }
465+ }
466+ } else {
467+ // Case 1: `column BETWEEN ? AND ?`
468+ // The expr is a column, low and high may be placeholders
469+ let low_result = get_sql_query_param_directed (
470+ expr,
471+ low,
472+ single_table_name,
473+ table_with_joins,
474+ db_conn,
475+ & ts_query. table_valued_function_columns ,
476+ )
477+ . await ?;
478+ let high_result = get_sql_query_param_directed (
479+ expr,
480+ high,
481+ single_table_name,
482+ table_with_joins,
483+ db_conn,
484+ & ts_query. table_valued_function_columns ,
485+ )
486+ . await ?;
487+ if let Some ( ( value, is_nullable, placeholder) ) = low_result {
488+ ts_query. insert_param ( & value, & is_nullable, & placeholder) ?;
489+ }
490+ if let Some ( ( value, is_nullable, placeholder) ) = high_result {
491+ ts_query. insert_param ( & value, & is_nullable, & placeholder) ?;
492+ }
434493 }
435494 Ok ( ( ) )
436495 }
0 commit comments