Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/gen.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 ->
Expand All @@ -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
Expand All @@ -217,25 +217,25 @@ 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
{ ctor; sql; args; is_poly }
| 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)
Expand Down
176 changes: 176 additions & 0 deletions test/cram/reusable_queries_postgresql_params.t
Original file line number Diff line number Diff line change
@@ -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