diff --git a/src/gen.ml b/src/gen.ml index 56c3e75d..42a90aad 100644 --- a/src/gen.ml +++ b/src/gen.ml @@ -90,7 +90,7 @@ and sql_dynamic_ctor = { let substitute_vars s vars subst_param = let rec loop s acc i parami vars = match vars with - | [] -> acc, i + | [] -> acc, i, parami | Sql.Single (param, _) :: tl -> let (i1,i2) = param.id.pos in assert (i2 > i1); @@ -116,7 +116,7 @@ let substitute_vars s vars subst_param = assert (i2 > i1); assert (i1 > i); let acc = - DynamicIn (name, kind, List.rev @@ fst @@ loop s [] i1 0 vars) :: + DynamicIn (name, kind, (let acc, _, _ = loop s [] i1 0 vars in List.rev acc)) :: Static (String.slice ~first:i ~last:i1 s) :: acc in @@ -161,7 +161,7 @@ let substitute_vars s vars subst_param = assert ((c2 = 0 && c1 = 1) || c2 > c1); assert (c1 > i); let pieces = - let (acc, last) = loop s [] c1 0 vars in + let (acc, last, _) = loop s [] c1 0 vars in let s_choice = let sql = [Static " ( "] @ List.rev(Static (String.slice ~first:last ~last:c2 s) :: acc) @ [Static " ) "]in let ctor = Sql.{ value=Some("Some"); pos=(0, 0); } in @@ -181,7 +181,7 @@ let substitute_vars s vars subst_param = assert (i2 > i1); assert (i1 > i); let shared_sql, (_: Sql.select_full) = Shared_queries.get id.value in - let raw_processed = loop_and_squash shared_sql shared_vars in + let raw_processed, parami = loop_and_squash ~parami shared_sql shared_vars in let processed_shared = [Static "("] @ raw_processed @ [Static ")"] in loop s (List.rev processed_shared @ Static (String.slice ~first:i ~last:i1 s) :: acc) i2 parami tl | DynamicSelect (name,ctors) :: tl -> @@ -191,7 +191,7 @@ let substitute_vars s vars subst_param = let sql = match args with | None | Some [] -> [Static (String.slice ~first:c1 ~last:c2 s)] | Some l -> - let (acc, last) = loop s [] (c1 - 1) 0 l in + let (acc, last, _) = loop s [] (c1 - 1) 0 l in let pieces = List.rev (Static (String.slice ~first:last ~last:c2 s) :: acc) in begin match pieces with | Static hd :: rest -> Static (String.slice ~first:1 hd) :: rest @@ -217,7 +217,7 @@ let substitute_vars s vars subst_param = match args with | None -> [Static ""] | Some l -> - let (acc, last) = loop s [] c1 0 l in + let (acc, last, _) = loop s [] c1 0 l in let body = List.rev (Static (String.slice ~first:last ~last:c2 s) :: acc) in squash [] ((Static " (" :: body) @ [Static ") "]) in @@ -225,17 +225,17 @@ let substitute_vars s vars subst_param = | Verbatim (n, v) -> { ctor = { value = Some n; pos = (0,0) }; args = Some []; sql = [Static v]; is_poly } end - and loop_and_squash sql vars = - let acc, last = loop sql [] 0 0 vars in + and loop_and_squash ?(parami=0) sql vars = + let acc, last, parami = loop sql [] 0 parami vars in let acc = List.rev (Static (String.slice ~first:last sql) :: acc) in - squash [] acc + squash [] acc, parami and squash acc = function | [] -> List.rev acc | Static "" :: tl -> squash acc tl | Static s1 :: Static s2 :: tl -> squash acc (Static (s1 ^ s2) :: tl) | x::xs -> squash (x::acc) xs in - loop_and_squash s vars + fst (loop_and_squash s vars) let subst_named index p = "@" ^ (show_param_name p index) let subst_oracle index p = ":" ^ (show_param_name p index) diff --git a/test/cram/reusable_queries_postgresql_params.t b/test/cram/reusable_queries_postgresql_params.t new file mode 100644 index 00000000..d71315fb --- /dev/null +++ b/test/cram/reusable_queries_postgresql_params.t @@ -0,0 +1,176 @@ +PostgreSQL parameter numbering in reusable queries, distinct parameters: + + $ sqlgg -no-header -gen caml -params postgresql -dialect postgresql - <<'EOF' 2>/dev/null | awk '/^ let get_users/{p=1} p&&/^$/{exit} p{print}' + > CREATE TABLE person ( + > id INT PRIMARY KEY, + > user_id INT NOT NULL, + > name TEXT NOT NULL + > ); + > CREATE TABLE "user" ( + > id INT PRIMARY KEY, + > username TEXT NOT NULL + > ); + > -- @get_persons | include: reuse + > SELECT * + > FROM person + > WHERE name LIKE @name; + > -- @get_users + > WITH person AS &get_persons + > SELECT * + > FROM "user" + > JOIN person ON person.user_id = "user".id + > WHERE "user".id > @min_id; + > EOF + let get_users db ~name ~min_id callback = + let invoke_callback stmt = + callback + ~id:(T.get_column_Int stmt 0) + ~username:(T.get_column_Text stmt 1) + ~id0:(T.get_column_Int stmt 2) + ~user_id:(T.get_column_Int stmt 3) + ~name:(T.get_column_Text stmt 4) + in + let set_params stmt = + let p = T.start_params stmt (2) in + T.set_param_Text p name; + T.set_param_Int p min_id; + T.finish_params p + in + T.select db ("WITH person AS (SELECT *\n\ + FROM person\n\ + WHERE name LIKE $1)\n\ + SELECT *\n\ + FROM \"user\"\n\ + JOIN person ON person.user_id = \"user\".id\n\ + WHERE \"user\".id > $2") set_params invoke_callback + +PostgreSQL parameter numbering in reusable queries, shared parameter: + + $ sqlgg -no-header -gen caml -params postgresql -dialect postgresql - <<'EOF' 2>/dev/null | awk '/^ let get_users/{p=1} p&&/^$/{exit} p{print}' + > CREATE TABLE person ( + > id INT PRIMARY KEY, + > user_id INT NOT NULL, + > name TEXT NOT NULL + > ); + > CREATE TABLE "user" ( + > id INT PRIMARY KEY, + > username TEXT NOT NULL + > ); + > -- @get_persons | include: reuse + > SELECT * + > FROM person + > WHERE + > name LIKE @name + > AND person.id > @min_id; + > -- @get_users + > WITH person AS &get_persons + > SELECT * + > FROM "user" + > JOIN person ON person.user_id = "user".id + > WHERE + > username LIKE @username + > AND "user".id > @min_id; + > EOF + let get_users db ~name ~username ~min_id callback = + let invoke_callback stmt = + callback + ~id:(T.get_column_Int stmt 0) + ~username:(T.get_column_Text stmt 1) + ~id0:(T.get_column_Int stmt 2) + ~user_id:(T.get_column_Int stmt 3) + ~name:(T.get_column_Text stmt 4) + in + let set_params stmt = + let p = T.start_params stmt (4) in + T.set_param_Text p name; + T.set_param_Int p min_id; + T.set_param_Text p username; + T.set_param_Int p min_id; + T.finish_params p + in + T.select db ("WITH person AS (SELECT *\n\ + FROM person\n\ + WHERE\n\ + name LIKE $1\n\ + AND person.id > $2)\n\ + SELECT *\n\ + FROM \"user\"\n\ + JOIN person ON person.user_id = \"user\".id\n\ + WHERE\n\ + username LIKE $3\n\ + AND \"user\".id > $4") set_params invoke_callback + +PostgreSQL parameter numbering with two reusable queries in one outer query: + + $ sqlgg -no-header -gen caml -params postgresql -dialect postgresql - <<'EOF' 2>/dev/null | awk '/^ let two_ctes/{p=1} p&&/^$/{exit} p{print}' + > CREATE TABLE person ( + > id INT PRIMARY KEY, + > user_id INT NOT NULL, + > name TEXT NOT NULL + > ); + > CREATE TABLE "user" ( + > id INT PRIMARY KEY, + > username TEXT NOT NULL + > ); + > -- @get_persons | include: reuse + > SELECT * FROM person WHERE name LIKE @name; + > -- @get_admins | include: reuse + > SELECT * FROM "user" WHERE username LIKE @admin; + > -- @two_ctes + > WITH p AS &get_persons, a AS &get_admins + > SELECT p.id, a.username + > FROM p JOIN a ON a.id = p.user_id + > WHERE p.id > @min_id; + > EOF + let two_ctes db ~name ~admin ~min_id callback = + let invoke_callback stmt = + callback + ~id:(T.get_column_Int stmt 0) + ~username:(T.get_column_Text stmt 1) + in + let set_params stmt = + let p = T.start_params stmt (3) in + T.set_param_Text p name; + T.set_param_Text p admin; + T.set_param_Int p min_id; + T.finish_params p + in + T.select db ("WITH p AS (SELECT * FROM person WHERE name LIKE $1), a AS (SELECT * FROM \"user\" WHERE username LIKE $2)\n\ + SELECT p.id, a.username\n\ + FROM p JOIN a ON a.id = p.user_id\n\ + WHERE p.id > $3") set_params invoke_callback + +PostgreSQL parameter numbering with reusable queries threaded into each other: + + $ sqlgg -no-header -gen caml -params postgresql -dialect postgresql - <<'EOF' 2>/dev/null | awk '/^ let outer_q/{p=1} p&&/^$/{exit} p{print}' + > CREATE TABLE person ( + > id INT PRIMARY KEY, + > user_id INT NOT NULL, + > name TEXT NOT NULL + > ); + > -- @get_persons | include: reuse + > SELECT * FROM person WHERE name LIKE @name; + > -- @wrap_persons | include: reuse + > WITH inner_p AS &get_persons + > SELECT * FROM inner_p WHERE inner_p.id > @inner_min; + > -- @outer_q + > WITH p AS &wrap_persons + > SELECT * FROM p WHERE p.user_id > @outer_min; + > EOF + let outer_q db ~name ~inner_min ~outer_min callback = + let invoke_callback stmt = + callback + ~id:(T.get_column_Int stmt 0) + ~user_id:(T.get_column_Int stmt 1) + ~name:(T.get_column_Text stmt 2) + in + let set_params stmt = + let p = T.start_params stmt (3) in + T.set_param_Text p name; + T.set_param_Int p inner_min; + T.set_param_Int p outer_min; + T.finish_params p + in + T.select db ("WITH p AS (WITH inner_p AS (SELECT * FROM person WHERE name LIKE $1)\n\ + SELECT * FROM inner_p WHERE inner_p.id > $2)\n\ + SELECT * FROM p WHERE p.user_id > $3") set_params invoke_callback