Skip to content

Commit 61af6a7

Browse files
committed
Replace "postgres_output row <row>" with "postgres_output text".
This is more general approach, which forces writing proper SQL queries instead of filtering results on the nginx side and allows for sending output from multiple rows to end-users. Discussed with Silly Sad.
1 parent 2661184 commit 61af6a7

File tree

5 files changed

+87
-51
lines changed

5 files changed

+87
-51
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,16 @@ This directive can be used more than once within same context.
8989

9090
postgres_output
9191
---------------
92-
* **syntax**: `postgres_output rds|row|value|binary_value|none [row] [column]`
92+
* **syntax**: `postgres_output rds|text|value|binary_value|none [row] [column]`
9393
* **default**: `rds`
9494
* **context**: `http`, `server`, `location`, `if location`
9595

9696
Set output format:
9797

98-
- `rds` - return output in `rds` format (with appropriate
99-
`Content-Type`),
100-
- `row` - return all values from a single row from the result-set
101-
in text format, values are separated by new line (with default `Content-Type`),
98+
- `rds` - return all values from the result-set in `rds` format
99+
(with appropriate `Content-Type`),
100+
- `text` - return all values from the result-set in text format
101+
(with default `Content-Type`), values are separated by new line,
102102
- `value` - return single value from the result-set in text format
103103
(with default `Content-Type`),
104104
- `binary_value` - return single value from the result-set in binary format

src/ngx_postgres_module.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ ngx_postgres_rewrite_enum_t ngx_postgres_rewrite_handlers[] = {
219219
ngx_postgres_output_enum_t ngx_postgres_output_handlers[] = {
220220
{ ngx_string("none"), 0, 0, NULL },
221221
{ ngx_string("rds"), 0, 0, ngx_postgres_output_rds },
222-
{ ngx_string("row"), 1, 0, ngx_postgres_output_row },
222+
{ ngx_string("text") , 0, 0, ngx_postgres_output_text },
223223
{ ngx_string("value"), 2, 0, ngx_postgres_output_value },
224224
{ ngx_string("binary_value"), 2, 1, ngx_postgres_output_value },
225225
{ ngx_null_string, 0, 0, NULL }

src/ngx_postgres_output.c

Lines changed: 29 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,14 @@ ngx_postgres_output_value(ngx_http_request_t *r, PGresult *res,
142142
}
143143

144144
ngx_int_t
145-
ngx_postgres_output_row(ngx_http_request_t *r, PGresult *res,
145+
ngx_postgres_output_text(ngx_http_request_t *r, PGresult *res,
146146
ngx_postgres_value_t *pgv)
147147
{
148148
ngx_postgres_ctx_t *pgctx;
149-
ngx_http_core_loc_conf_t *clcf;
150149
ngx_chain_t *cl;
151150
ngx_buf_t *b;
152151
size_t size;
153-
ngx_int_t col_count, row_count, col;
152+
ngx_int_t col_count, row_count, col, row;
154153

155154
dd("entering");
156155

@@ -159,39 +158,25 @@ ngx_postgres_output_row(ngx_http_request_t *r, PGresult *res,
159158
col_count = pgctx->var_cols;
160159
row_count = pgctx->var_rows;
161160

162-
if (pgv->row >= row_count) {
163-
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
164-
165-
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
166-
"postgres: \"postgres_output row\" requires row out"
167-
" of range of the received result-set (rows:%d cols:%d)"
168-
" in location \"%V\"", row_count, col_count, &clcf->name);
169-
170-
dd("returning NGX_DONE, status NGX_HTTP_INTERNAL_SERVER_ERROR");
171-
pgctx->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
172-
return NGX_DONE;
173-
}
174-
175161
/* pre-calculate total length up-front for single buffer allocation */
176162
size = 0;
177163

178-
for (col = 0; col < col_count; col++) {
179-
if (PQgetisnull(res, pgv->row, col)) {
180-
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
181-
182-
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
183-
"postgres: \"postgres_output row\" received NULL"
184-
" value in location \"%V\"", &clcf->name);
185-
186-
dd("returning NGX_DONE, status NGX_HTTP_INTERNAL_SERVER_ERROR");
187-
pgctx->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
188-
return NGX_DONE;
164+
for (row = 0; row < row_count; row++) {
165+
for (col = 0; col < col_count; col++) {
166+
if (PQgetisnull(res, row, col)) {
167+
size += sizeof("(null)") - 1;
168+
} else {
169+
size += PQgetlength(res, row, col); /* field string data */
170+
}
189171
}
190-
191-
size += PQgetlength(res, pgv->row, col); /* field string data */
192172
}
193173

194-
size += col_count - 1; /* delimiters */
174+
size += row_count * col_count - 1; /* delimiters */
175+
176+
if ((row_count == 0) || (size == 0)) {
177+
dd("returning NGX_DONE (empty result)");
178+
return NGX_DONE;
179+
}
195180

196181
b = ngx_create_temp_buf(r->pool, size);
197182
if (b == NULL) {
@@ -210,18 +195,21 @@ ngx_postgres_output_row(ngx_http_request_t *r, PGresult *res,
210195
b->tag = r->upstream->output.tag;
211196

212197
/* fill data */
213-
for (col = 0; col < col_count - 1; col++) {
214-
size = PQgetlength(res, pgv->row, col);
215-
if (size) {
216-
b->last = ngx_copy(b->last, PQgetvalue(res, pgv->row, col), size);
217-
}
218-
b->last = ngx_copy(b->last, "\n", 1);
219-
}
198+
for (row = 0; row < row_count; row++) {
199+
for (col = 0; col < col_count; col++) {
200+
if (PQgetisnull(res, row, col)) {
201+
b->last = ngx_copy(b->last, "(null)", sizeof("(null)") - 1);
202+
} else {
203+
size = PQgetlength(res, row, col);
204+
if (size) {
205+
b->last = ngx_copy(b->last, PQgetvalue(res, row, col), size);
206+
}
207+
}
220208

221-
/* last column without delimiter */
222-
size = PQgetlength(res, pgv->row, col);
223-
if (size) {
224-
b->last = ngx_copy(b->last, PQgetvalue(res, pgv->row, col), size);
209+
if ((row != row_count - 1) || (col != col_count - 1)) {
210+
b->last = ngx_copy(b->last, "\n", 1);
211+
}
212+
}
225213
}
226214

227215
if (b->last != b->end) {

src/ngx_postgres_output.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
ngx_int_t ngx_postgres_output_value(ngx_http_request_t *, PGresult *,
4141
ngx_postgres_value_t *);
42-
ngx_int_t ngx_postgres_output_row(ngx_http_request_t *, PGresult *,
42+
ngx_int_t ngx_postgres_output_text(ngx_http_request_t *, PGresult *,
4343
ngx_postgres_value_t *);
4444
ngx_int_t ngx_postgres_output_rds(ngx_http_request_t *, PGresult *,
4545
ngx_postgres_value_t *);

t/output.t

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,15 @@ GET /postgres
132132
133133
134134
135-
=== TEST 7: row - sanity
135+
=== TEST 7: text - sanity
136136
--- http_config eval: $::http_config
137137
--- config
138138
default_type text/plain;
139139
140140
location /postgres {
141141
postgres_pass database;
142142
postgres_query "select 'a', 'b', 'c', 'd'";
143-
postgres_output row 0;
143+
postgres_output text;
144144
}
145145
--- request
146146
GET /postgres
@@ -254,7 +254,7 @@ test
254254
=== TEST 11: inheritance (mixed, don't inherit)
255255
--- http_config eval: $::http_config
256256
--- config
257-
postgres_output row 0;
257+
postgres_output text;
258258
259259
location /postgres {
260260
postgres_pass database;
@@ -417,3 +417,51 @@ Content-Type: text/plain
417417
--- response_body chomp
418418
2
419419
--- timeout: 10
420+
421+
422+
423+
=== TEST 19: text - NULL value
424+
--- http_config eval: $::http_config
425+
--- config
426+
default_type text/plain;
427+
428+
location /postgres {
429+
postgres_pass database;
430+
postgres_query "select * from cats order by id";
431+
postgres_output text;
432+
}
433+
--- request
434+
GET /postgres
435+
--- error_code: 200
436+
--- response_headers
437+
Content-Type: text/plain
438+
--- response_body eval
439+
"2".
440+
"\x{0a}". # new line - delimiter
441+
"(null)".
442+
"\x{0a}". # new line - delimiter
443+
"3".
444+
"\x{0a}". # new line - delimiter
445+
"bob"
446+
--- timeout: 10
447+
448+
449+
450+
=== TEST 20: text - empty result
451+
--- http_config eval: $::http_config
452+
--- config
453+
default_type text/plain;
454+
455+
location /postgres {
456+
postgres_pass database;
457+
postgres_query "select * from cats where id=1";
458+
postgres_output text;
459+
}
460+
--- request
461+
GET /postgres
462+
--- error_code: 200
463+
--- response_headers
464+
Content-Type: text/plain
465+
--- response_body eval
466+
""
467+
--- timeout: 10

0 commit comments

Comments
 (0)