Skip to content

Commit 6d3e629

Browse files
committed
return helpful error tuple if query is too big
1 parent fdf6c8a commit 6d3e629

2 files changed

Lines changed: 64 additions & 58 deletions

File tree

c_src/libpg_query_ex.c

Lines changed: 56 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#include <stdio.h>
33
#include <string.h>
44

5-
#define MAX_QUERY_SIZE 65536 /* queries larger than this are rejected */
5+
#define MAX_QUERY_SIZE 65536 /* queries larger than this are rejected */
66

77
#include "libpg_query/pg_query.h"
88

@@ -25,17 +25,37 @@ ERL_NIF_TERM make_binary(ErlNifEnv *env, char *source) {
2525
return binary;
2626
}
2727

28-
static ERL_NIF_TERM max_query_size(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
28+
ERL_NIF_TERM query_too_long_error(ErlNifEnv *env, size_t query_size) {
29+
char error_msg[128];
30+
snprintf(error_msg, 128,
31+
"cannot parse query: query size %lu is bigger than maximum size %i",
32+
query_size, MAX_QUERY_SIZE);
33+
ERL_NIF_TERM error_map = make_error_response(env, error_msg);
34+
return enif_make_tuple2(env, enif_make_atom(env, "error"), error_map);
35+
}
36+
37+
ERL_NIF_TERM make_error_response(ErlNifEnv *env, char *message) {
38+
ERL_NIF_TERM error_map = enif_make_new_map(env);
39+
if (!enif_make_map_put(env, error_map, enif_make_atom(env, "message"),
40+
make_binary(env, message), &error_map)) {
41+
return enif_raise_exception(env, make_binary(env, "failed to update map"));
42+
}
43+
return error_map;
44+
}
45+
46+
static ERL_NIF_TERM max_query_size(ErlNifEnv *env, int argc,
47+
const ERL_NIF_TERM argv[]) {
2948
return enif_make_int64(env, MAX_QUERY_SIZE);
3049
}
3150

32-
static ERL_NIF_TERM parse_query(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
51+
static ERL_NIF_TERM parse_query(ErlNifEnv *env, int argc,
52+
const ERL_NIF_TERM argv[]) {
3353
ErlNifBinary query;
3454
ERL_NIF_TERM term;
3555

3656
if (argc == 1 && enif_inspect_binary(env, argv[0], &query)) {
3757
if (query.size >= MAX_QUERY_SIZE) {
38-
return enif_make_badarg(env);
58+
return query_too_long_error(env, query.size);
3959
}
4060

4161
// add one more byte for the null termination
@@ -49,27 +69,14 @@ static ERL_NIF_TERM parse_query(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg
4969
PgQueryProtobufParseResult result = pg_query_parse_protobuf(statement);
5070

5171
if (result.error) {
52-
ERL_NIF_TERM error_map = enif_make_new_map(env);
53-
54-
if (!enif_make_map_put(
55-
env,
56-
error_map,
57-
enif_make_atom(env, "message"),
58-
make_binary(env, result.error->message),
59-
&error_map
60-
)) {
61-
return enif_raise_exception(env, make_binary(env, "failed to update map"));
62-
}
63-
64-
if (!enif_make_map_put(
65-
env,
66-
error_map,
67-
enif_make_atom(env, "cursorpos"),
68-
// drop the cursorpos by one, so it's zero-indexed
69-
enif_make_int(env, result.error->cursorpos - 1),
70-
&error_map
71-
)) {
72-
return enif_raise_exception(env, make_binary(env, "failed to update map"));
72+
ERL_NIF_TERM error_map = make_error_response(env, result.error->message);
73+
74+
if (!enif_make_map_put(env, error_map, enif_make_atom(env, "cursorpos"),
75+
// drop the cursorpos by one, so it's zero-indexed
76+
enif_make_int(env, result.error->cursorpos - 1),
77+
&error_map)) {
78+
return enif_raise_exception(env,
79+
make_binary(env, "failed to update map"));
7380
}
7481

7582
term = enif_make_tuple2(env, enif_make_atom(env, "error"), error_map);
@@ -86,7 +93,7 @@ static ERL_NIF_TERM parse_query(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg
8693
}
8794

8895
static ERL_NIF_TERM deparse_query(ErlNifEnv *env, int argc,
89-
const ERL_NIF_TERM argv[]) {
96+
const ERL_NIF_TERM argv[]) {
9097
ErlNifBinary proto;
9198
ERL_NIF_TERM term;
9299

@@ -100,14 +107,11 @@ static ERL_NIF_TERM deparse_query(ErlNifEnv *env, int argc,
100107
if (result.error) {
101108
ERL_NIF_TERM error_map = enif_make_new_map(env);
102109

103-
if (!enif_make_map_put(
104-
env,
105-
error_map,
106-
enif_make_atom(env, "message"),
107-
make_binary(env, result.error->message),
108-
&error_map
109-
)) {
110-
return enif_raise_exception(env, make_binary(env, "failed to update map"));
110+
if (!enif_make_map_put(env, error_map, enif_make_atom(env, "message"),
111+
make_binary(env, result.error->message),
112+
&error_map)) {
113+
return enif_raise_exception(env,
114+
make_binary(env, "failed to update map"));
111115
}
112116

113117
term = enif_make_tuple2(env, enif_make_atom(env, "error"), error_map);
@@ -123,13 +127,13 @@ static ERL_NIF_TERM deparse_query(ErlNifEnv *env, int argc,
123127
}
124128

125129
static ERL_NIF_TERM scan_query(ErlNifEnv *env, int argc,
126-
const ERL_NIF_TERM argv[]) {
130+
const ERL_NIF_TERM argv[]) {
127131
ErlNifBinary query;
128132
ERL_NIF_TERM term;
129133

130134
if (argc == 1 && enif_inspect_binary(env, argv[0], &query)) {
131135
if (query.size >= MAX_QUERY_SIZE) {
132-
return enif_make_badarg(env);
136+
return query_too_long_error(env, query.size);
133137
}
134138

135139
// add one more byte for the null termination
@@ -145,25 +149,20 @@ static ERL_NIF_TERM scan_query(ErlNifEnv *env, int argc,
145149
if (result.error) {
146150
ERL_NIF_TERM error_map = enif_make_new_map(env);
147151

148-
if (!enif_make_map_put(
149-
env,
150-
error_map,
151-
enif_make_atom(env, "message"),
152-
make_binary(env, result.error->message),
153-
&error_map
154-
)) {
155-
return enif_raise_exception(env, make_binary(env, "failed to update map"));
152+
if (!enif_make_map_put(env, error_map, enif_make_atom(env, "message"),
153+
make_binary(env, result.error->message),
154+
&error_map)) {
155+
return enif_raise_exception(env,
156+
make_binary(env, "failed to update map"));
156157
}
157158

158-
if (result.error->cursorpos > 0 && !enif_make_map_put(
159-
env,
160-
error_map,
161-
enif_make_atom(env, "cursorpos"),
162-
// drop the cursorpos by one, so it's zero-indexed
163-
enif_make_int(env, result.error->cursorpos - 1),
164-
&error_map
165-
)) {
166-
return enif_raise_exception(env, make_binary(env, "failed to update map"));
159+
if (result.error->cursorpos > 0 &&
160+
!enif_make_map_put(env, error_map, enif_make_atom(env, "cursorpos"),
161+
// drop the cursorpos by one, so it's zero-indexed
162+
enif_make_int(env, result.error->cursorpos - 1),
163+
&error_map)) {
164+
return enif_raise_exception(env,
165+
make_binary(env, "failed to update map"));
167166
}
168167

169168
term = enif_make_tuple2(env, enif_make_atom(env, "error"), error_map);
@@ -179,10 +178,10 @@ static ERL_NIF_TERM scan_query(ErlNifEnv *env, int argc,
179178
}
180179

181180
static ErlNifFunc funcs[] = {
182-
{"parse_query", 1, parse_query},
183-
{"deparse_query", 1, deparse_query},
184-
{"scan_query", 1, scan_query},
185-
{"max_query_size", 0, max_query_size},
181+
{"parse_query", 1, parse_query},
182+
{"deparse_query", 1, deparse_query},
183+
{"scan_query", 1, scan_query},
184+
{"max_query_size", 0, max_query_size},
186185
};
187186

188187
ERL_NIF_INIT(Elixir.PgQuery.Parser, funcs, NULL, NULL, NULL, NULL)

test/pg_query_test.exs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ defmodule PgQueryTest do
5454
assert {:ok, _} = PgQuery.parse(small_query)
5555

5656
oversized = "SELECT '" <> String.duplicate("a", 65_536) <> "'"
57-
assert_raise ArgumentError, fn -> PgQuery.parse(oversized) end
57+
assert {:error, %{message: m}} = PgQuery.parse(oversized)
58+
assert m == "cannot parse query: query size 65545 is bigger than maximum size 65536"
5859
end
5960

6061
test "scans a query" do
@@ -76,6 +77,12 @@ defmodule PgQueryTest do
7677
end)
7778
end
7879

80+
test "errors when scanning an overlarge query" do
81+
oversized = "SELECT '" <> String.duplicate("a", 65_536) <> "'"
82+
assert {:error, %{message: m}} = PgQuery.scan(oversized)
83+
assert m == "cannot parse query: query size 65545 is bigger than maximum size 65536"
84+
end
85+
7986
test "max_query_size/0" do
8087
assert 65536 = PgQuery.max_query_size()
8188
end

0 commit comments

Comments
 (0)