@@ -375,6 +375,33 @@ mod tests {
375375 assert ! ( !query. sql. contains( "sqlpage." ) ) ;
376376 }
377377
378+ #[ test]
379+ fn emulated_concat_keeps_nested_call_per_row ( ) {
380+ let FileStatement :: Query ( Query {
381+ body : QueryBody :: Database ( query) ,
382+ ..
383+ } ) = one ( "select concat(sqlpage.url_encode(value), '!') as encoded from t" )
384+ else {
385+ panic ! ( "expected database query" ) ;
386+ } ;
387+ assert ! ( query. bindings. is_empty( ) ) ;
388+ assert_eq ! ( query. row_input_json. len( ) , 1 ) ;
389+ assert ! ( !query. sql. contains( "sqlpage." ) ) ;
390+ assert_eq ! (
391+ query. computed_columns. as_ref( ) ,
392+ [ OutputColumn {
393+ name: "encoded" . into( ) ,
394+ value: SqlPageExpr :: Concat {
395+ arguments: Box :: new( [
396+ call( SqlPageFunctionName :: url_encode, [ row( 0 ) ] ) ,
397+ text( "!" ) ,
398+ ] ) ,
399+ null_behavior: ConcatNullBehavior :: IgnoreNull ,
400+ } ,
401+ } ]
402+ ) ;
403+ }
404+
378405 #[ test]
379406 fn parentheses_keep_nested_call_per_row ( ) {
380407 let FileStatement :: Query ( Query {
@@ -621,6 +648,31 @@ mod tests {
621648 assert ! ( query. must_buffer_rows( ) ) ;
622649 }
623650
651+ #[ test]
652+ fn only_nested_run_sql_requires_buffering ( ) {
653+ let FileStatement :: Query ( Query {
654+ body : QueryBody :: Database ( query) ,
655+ ..
656+ } ) = one ( "select coalesce(sqlpage.url_encode(path), '') from files" )
657+ else {
658+ panic ! ( "expected database query" ) ;
659+ } ;
660+ assert ! ( !query. must_buffer_rows( ) ) ;
661+ }
662+
663+ #[ test]
664+ fn distinct_is_rejected_only_when_a_projection_is_computed_by_sqlpage ( ) {
665+ assert_eq ! (
666+ sql_for( SupportedDatabase :: Postgres , "select distinct a from t" ) ,
667+ "SELECT DISTINCT a FROM t"
668+ ) ;
669+ let FileStatement :: Error ( err) = one ( "select distinct sqlpage.url_encode(a) as x from t" )
670+ else {
671+ panic ! ( "expected an error" ) ;
672+ } ;
673+ assert ! ( err. to_string( ) . contains( "DISTINCT" ) , "{err}" ) ;
674+ }
675+
624676 #[ test]
625677 fn documented_facet_query_is_accepted ( ) {
626678 let stmt = one ( "select category as title, \
@@ -644,6 +696,30 @@ mod tests {
644696 assert_eq ! ( query. columns. len( ) , 1 ) ;
645697 }
646698
699+ #[ test]
700+ fn boolean_literal_stays_a_literal_in_the_static_simple_select ( ) {
701+ let FileStatement :: Query ( Query {
702+ body : QueryBody :: StaticSimpleSelect ( query) ,
703+ ..
704+ } ) = one ( "select 'shell' as component, true as fixed_top_menu" )
705+ else {
706+ panic ! ( "expected a single SQLPage-owned row" ) ;
707+ } ;
708+ assert_eq ! (
709+ query. columns. as_ref( ) ,
710+ [
711+ OutputColumn {
712+ name: "component" . into( ) ,
713+ value: text( "shell" ) ,
714+ } ,
715+ OutputColumn {
716+ name: "fixed_top_menu" . into( ) ,
717+ value: SqlPageExpr :: Literal ( serde_json:: Value :: Bool ( true ) ) ,
718+ } ,
719+ ]
720+ ) ;
721+ }
722+
647723 #[ test]
648724 fn concat_operator_uses_backend_null_behavior_in_sqlpage_expressions ( ) {
649725 for database_type in [ SupportedDatabase :: Oracle , SupportedDatabase :: Mssql ] {
@@ -862,4 +938,57 @@ mod tests {
862938 "SELECT value FROM t LIMIT $1"
863939 ) ;
864940 }
941+
942+ #[ test]
943+ fn with_and_limit_clauses_are_never_folded_into_a_constant_row ( ) {
944+ let query = rewrite_database (
945+ "with d as (insert into t(a) values ($v) returning a) \
946+ select 'redirect' as component, '/index.sql' as link",
947+ ) ;
948+ assert ! ( query. sql. contains( "INSERT INTO" ) , "{}" , query. sql) ;
949+ assert_eq ! ( query. bindings. as_ref( ) , [ variable( "v" ) ] ) ;
950+
951+ assert_eq ! (
952+ sql_for( SupportedDatabase :: Postgres , "select 'a' as x limit 0" ) ,
953+ "SELECT 'a' AS x LIMIT 0"
954+ ) ;
955+ }
956+
957+ #[ test]
958+ fn a_database_operand_keeps_the_whole_projection_in_the_database ( ) {
959+ assert_eq ! (
960+ sql_for( SupportedDatabase :: Postgres , "select 'x' || now() as v" ) ,
961+ "SELECT 'x' || now() AS v"
962+ ) ;
963+ }
964+
965+ #[ test]
966+ fn concat_operator_is_rewritten_to_a_function_only_on_sql_server ( ) {
967+ assert_eq ! (
968+ sql_for( SupportedDatabase :: Mssql , "select a || b from t" ) ,
969+ "SELECT CONCAT(a, b) FROM t"
970+ ) ;
971+ assert_eq ! (
972+ sql_for( SupportedDatabase :: Postgres , "select a || b from t" ) ,
973+ "SELECT a || b FROM t"
974+ ) ;
975+ }
976+
977+ #[ test]
978+ fn modifiers_on_a_sqlpage_function_are_rejected ( ) {
979+ for src in [
980+ "select sqlpage.url_encode(a) over () from t" ,
981+ "select sqlpage.url_encode(a) filter (where a > 1) from t" ,
982+ "select sqlpage.url_encode(a) ignore nulls from t" ,
983+ "select sqlpage.url_encode(a) within group (order by a) from t" ,
984+ ] {
985+ let FileStatement :: Error ( error) = one ( src) else {
986+ panic ! ( "expected a rewrite error for `{src}`" ) ;
987+ } ;
988+ assert ! (
989+ error. to_string( ) . contains( "Modifiers are not supported" ) ,
990+ "`{src}` produced: {error}"
991+ ) ;
992+ }
993+ }
865994}
0 commit comments