Skip to content

Commit a862a21

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

File tree

2 files changed

+59
-65
lines changed

2 files changed

+59
-65
lines changed

c_src/libpg_query_ex.c

Lines changed: 51 additions & 64 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,38 @@ 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 make_error_response(ErlNifEnv *env, char *message) {
29+
ERL_NIF_TERM error_map = enif_make_new_map(env);
30+
if (!enif_make_map_put(env, error_map, enif_make_atom(env, "message"),
31+
make_binary(env, message), &error_map)) {
32+
return enif_raise_exception(env, make_binary(env, "failed to update map"));
33+
}
34+
return error_map;
35+
}
36+
37+
ERL_NIF_TERM query_too_long_error(ErlNifEnv *env, size_t query_size) {
38+
char error_msg[128];
39+
snprintf(error_msg, 128,
40+
"cannot parse query: query size %lu is bigger than maximum size %i",
41+
query_size, MAX_QUERY_SIZE);
42+
ERL_NIF_TERM error_map = make_error_response(env, error_msg);
43+
return enif_make_tuple2(env, enif_make_atom(env, "error"), error_map);
44+
}
45+
46+
47+
static ERL_NIF_TERM max_query_size(ErlNifEnv *env, int argc,
48+
const ERL_NIF_TERM argv[]) {
2949
return enif_make_int64(env, MAX_QUERY_SIZE);
3050
}
3151

32-
static ERL_NIF_TERM parse_query(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
52+
static ERL_NIF_TERM parse_query(ErlNifEnv *env, int argc,
53+
const ERL_NIF_TERM argv[]) {
3354
ErlNifBinary query;
3455
ERL_NIF_TERM term;
3556

3657
if (argc == 1 && enif_inspect_binary(env, argv[0], &query)) {
3758
if (query.size >= MAX_QUERY_SIZE) {
38-
return enif_make_badarg(env);
59+
return query_too_long_error(env, query.size);
3960
}
4061

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

5172
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"));
73+
ERL_NIF_TERM error_map = make_error_response(env, result.error->message);
74+
75+
if (!enif_make_map_put(env, error_map, enif_make_atom(env, "cursorpos"),
76+
// drop the cursorpos by one, so it's zero-indexed
77+
enif_make_int(env, result.error->cursorpos - 1),
78+
&error_map)) {
79+
return enif_raise_exception(env,
80+
make_binary(env, "failed to update map"));
7381
}
7482

7583
term = enif_make_tuple2(env, enif_make_atom(env, "error"), error_map);
@@ -86,7 +94,7 @@ static ERL_NIF_TERM parse_query(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg
8694
}
8795

8896
static ERL_NIF_TERM deparse_query(ErlNifEnv *env, int argc,
89-
const ERL_NIF_TERM argv[]) {
97+
const ERL_NIF_TERM argv[]) {
9098
ErlNifBinary proto;
9199
ERL_NIF_TERM term;
92100

@@ -98,17 +106,8 @@ static ERL_NIF_TERM deparse_query(ErlNifEnv *env, int argc,
98106
PgQueryDeparseResult result = pg_query_deparse_protobuf(parse_tree);
99107

100108
if (result.error) {
101-
ERL_NIF_TERM error_map = enif_make_new_map(env);
102-
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"));
111-
}
109+
ERL_NIF_TERM error_map = make_error_response(env, result.error->message);
110+
112111

113112
term = enif_make_tuple2(env, enif_make_atom(env, "error"), error_map);
114113
} else {
@@ -123,13 +122,13 @@ static ERL_NIF_TERM deparse_query(ErlNifEnv *env, int argc,
123122
}
124123

125124
static ERL_NIF_TERM scan_query(ErlNifEnv *env, int argc,
126-
const ERL_NIF_TERM argv[]) {
125+
const ERL_NIF_TERM argv[]) {
127126
ErlNifBinary query;
128127
ERL_NIF_TERM term;
129128

130129
if (argc == 1 && enif_inspect_binary(env, argv[0], &query)) {
131130
if (query.size >= MAX_QUERY_SIZE) {
132-
return enif_make_badarg(env);
131+
return query_too_long_error(env, query.size);
133132
}
134133

135134
// add one more byte for the null termination
@@ -143,27 +142,15 @@ static ERL_NIF_TERM scan_query(ErlNifEnv *env, int argc,
143142
PgQueryScanResult result = pg_query_scan(statement);
144143

145144
if (result.error) {
146-
ERL_NIF_TERM error_map = enif_make_new_map(env);
147-
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"));
156-
}
157-
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"));
145+
ERL_NIF_TERM error_map = make_error_response(env, result.error->message);
146+
147+
if (result.error->cursorpos > 0 &&
148+
!enif_make_map_put(env, error_map, enif_make_atom(env, "cursorpos"),
149+
// drop the cursorpos by one, so it's zero-indexed
150+
enif_make_int(env, result.error->cursorpos - 1),
151+
&error_map)) {
152+
return enif_raise_exception(env,
153+
make_binary(env, "failed to update map"));
167154
}
168155

169156
term = enif_make_tuple2(env, enif_make_atom(env, "error"), error_map);
@@ -179,10 +166,10 @@ static ERL_NIF_TERM scan_query(ErlNifEnv *env, int argc,
179166
}
180167

181168
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},
169+
{"parse_query", 1, parse_query},
170+
{"deparse_query", 1, deparse_query},
171+
{"scan_query", 1, scan_query},
172+
{"max_query_size", 0, max_query_size},
186173
};
187174

188175
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)