Skip to content
This repository
Browse code

Add "postgres_set" directive and predefined variables ($postgres_colu…

…mn_count, $postgres_row_count and $postgres_value).
  • Loading branch information...
commit 730aced1cefacf7546a60a4d8025b6a23edc104f 1 parent 9423c41
Piotr Sikora PiotrSikora authored
30 README
@@ -55,6 +55,17 @@ CONFIGURATION DIRECTIVES:
55 55 default: none
56 56
57 57
  58 + postgres_set $variable row column [optional|required] (context: http, server, location)
  59 + ---------------------------------------------------------------------------------------
  60 + Get single value from the result-set and keep it in $variable. When requirement level
  61 + is set to "required" and value is either out-of-range, NULL or zero-length, then nginx
  62 + returns 500 Internal Server Error. Such condition is silently ignored when requirement
  63 + level is set to "optional".
  64 + Row and column numbers start at 0.
  65 +
  66 + default: none
  67 +
  68 +
58 69 postgres_connect_timeout timeout (context: http, server, location)
59 70 ------------------------------------------------------------------
60 71 Set timeout for connecting to the database (in milliseconds).
@@ -69,6 +80,25 @@ CONFIGURATION DIRECTIVES:
69 80 default: 30000ms (30s)
70 81
71 82
  83 +CONFIGURATION VARIABLES:
  84 +------------------------
  85 +
  86 + $postgres_column_count
  87 + ----------------------
  88 + Number of columns in received result-set.
  89 +
  90 +
  91 + $postgres_row_count
  92 + -------------------
  93 + Number of rows in received result-set.
  94 +
  95 +
  96 + $postgres_value
  97 + ---------------
  98 + Single value from the received result-set (available only when
  99 + "postgres_get_value" directive is being used).
  100 +
  101 +
72 102 EXAMPLE CONFIGURATION #1:
73 103 -------------------------
74 104 http {
4 config
@@ -89,7 +89,7 @@ fi
89 89 ngx_addon_name=ngx_postgres_module
90 90
91 91 HTTP_MODULES="$HTTP_MODULES ngx_postgres_module"
92   -NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_postgres_module.c $ngx_addon_dir/src/ngx_postgres_handler.c $ngx_addon_dir/src/ngx_postgres_processor.c $ngx_addon_dir/src/ngx_postgres_upstream.c $ngx_addon_dir/src/ngx_postgres_util.c $ngx_addon_dir/src/ngx_postgres_output.c $ngx_addon_dir/src/ngx_postgres_keepalive.c"
93   -NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ngx_postgres_ddebug.h $ngx_addon_dir/src/ngx_postgres_module.h $ngx_addon_dir/src/ngx_postgres_handler.h $ngx_addon_dir/src/ngx_postgres_processor.h $ngx_addon_dir/src/ngx_postgres_upstream.h $ngx_addon_dir/src/ngx_postgres_util.h $ngx_addon_dir/src/ngx_postgres_output.h $ngx_addon_dir/src/resty_dbd_stream.h $ngx_addon_dir/src/ngx_postgres_keepalive.h"
  92 +NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_postgres_handler.c $ngx_addon_dir/src/ngx_postgres_keepalive.c $ngx_addon_dir/src/ngx_postgres_module.c $ngx_addon_dir/src/ngx_postgres_output.c $ngx_addon_dir/src/ngx_postgres_processor.c $ngx_addon_dir/src/ngx_postgres_upstream.c $ngx_addon_dir/src/ngx_postgres_util.c $ngx_addon_dir/src/ngx_postgres_variable.c"
  93 +NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ngx_postgres_handler.h $ngx_addon_dir/src/ngx_postgres_keepalive.h $ngx_addon_dir/src/ngx_postgres_module.h $ngx_addon_dir/src/ngx_postgres_output.h $ngx_addon_dir/src/ngx_postgres_processor.h $ngx_addon_dir/src/ngx_postgres_upstream.h $ngx_addon_dir/src/ngx_postgres_util.h $ngx_addon_dir/src/ngx_postgres_variable.h $ngx_addon_dir/src/ngx_postgres_ddebug.h $ngx_addon_dir/src/resty_dbd_stream.h"
94 94
95 95 have=NGX_POSTGRES_MODULE . auto/have
12 src/ngx_postgres_handler.c
@@ -34,8 +34,6 @@
34 34 #include "ngx_postgres_processor.h"
35 35 #include "ngx_postgres_util.h"
36 36
37   -#include <nginx.h>
38   -
39 37
40 38 ngx_int_t
41 39 ngx_postgres_handler(ngx_http_request_t *r)
@@ -295,13 +293,17 @@ ngx_postgres_abort_request(ngx_http_request_t *r)
295 293 void
296 294 ngx_postgres_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
297 295 {
298   - ngx_chain_t *response;
  296 + ngx_postgres_ctx_t *pgctx;
299 297
300 298 dd("entering");
301 299
302 300 if (rc == NGX_OK) {
303   - response = ngx_http_get_module_ctx(r, ngx_postgres_module);
304   - ngx_postgres_output_chain(r, response);
  301 + pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
  302 + if (pgctx != NULL) {
  303 + ngx_postgres_output_chain(r, pgctx->response);
  304 + } else {
  305 + ngx_postgres_output_chain(r, NULL);
  306 + }
305 307 }
306 308
307 309 dd("returning");
197 src/ngx_postgres_module.c
@@ -33,6 +33,7 @@
33 33 #include "ngx_postgres_module.h"
34 34 #include "ngx_postgres_upstream.h"
35 35 #include "ngx_postgres_util.h"
  36 +#include "ngx_postgres_variable.h"
36 37
37 38
38 39 static ngx_command_t ngx_postgres_module_commands[] = {
@@ -72,6 +73,14 @@ static ngx_command_t ngx_postgres_module_commands[] = {
72 73 0,
73 74 NULL },
74 75
  76 + { ngx_string("postgres_set"),
  77 + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
  78 + |NGX_CONF_TAKE3|NGX_CONF_TAKE4,
  79 + ngx_postgres_conf_set,
  80 + NGX_HTTP_LOC_CONF_OFFSET,
  81 + 0,
  82 + NULL },
  83 +
75 84 { ngx_string("postgres_connect_timeout"),
76 85 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
77 86 ngx_conf_set_msec_slot,
@@ -89,14 +98,31 @@ static ngx_command_t ngx_postgres_module_commands[] = {
89 98 ngx_null_command
90 99 };
91 100
  101 +static ngx_http_variable_t ngx_postgres_module_variables[] = {
  102 +
  103 + { ngx_string("postgres_column_count"), NULL,
  104 + ngx_postgres_variable_column_count, 0,
  105 + NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
  106 +
  107 + { ngx_string("postgres_row_count"), NULL,
  108 + ngx_postgres_variable_row_count, 0,
  109 + NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
  110 +
  111 + { ngx_string("postgres_value"), NULL,
  112 + ngx_postgres_variable_value, 0,
  113 + NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
  114 +
  115 + { ngx_null_string, NULL, NULL, 0, 0, 0 }
  116 +};
  117 +
92 118 static ngx_http_module_t ngx_postgres_module_ctx = {
93   - NULL, /* preconfiguration */
  119 + ngx_postgres_add_variables, /* preconfiguration */
94 120 NULL, /* postconfiguration */
95 121
96 122 NULL, /* create main configuration */
97 123 NULL, /* init main configuration */
98 124
99   - ngx_postgres_upstream_create_srv_conf, /* create server configuration */
  125 + ngx_postgres_create_upstream_srv_conf, /* create server configuration */
100 126 NULL, /* merge server configuration */
101 127
102 128 ngx_postgres_create_loc_conf, /* create location configuration */
@@ -138,9 +164,37 @@ ngx_postgres_http_method_t ngx_postgres_http_methods[] = {
138 164 { NULL, 0 }
139 165 };
140 166
  167 +ngx_conf_enum_t ngx_postgres_requirement_options[] = {
  168 + { ngx_string("optional"), NGX_POSTGRES_OPTIONAL },
  169 + { ngx_string("required"), NGX_POSTGRES_REQUIRED },
  170 + { ngx_null_string, 0 }
  171 +};
  172 +
  173 +
  174 +ngx_int_t
  175 +ngx_postgres_add_variables(ngx_conf_t *cf)
  176 +{
  177 + ngx_http_variable_t *var, *v;
  178 +
  179 + dd("entering");
  180 +
  181 + for (v = ngx_postgres_module_variables; v->name.len; v++) {
  182 + var = ngx_http_add_variable(cf, &v->name, v->flags);
  183 + if (var == NULL) {
  184 + dd("returning NGX_ERROR");
  185 + return NGX_ERROR;
  186 + }
  187 +
  188 + var->get_handler = v->get_handler;
  189 + var->data = v->data;
  190 + }
  191 +
  192 + dd("returning NGX_OK");
  193 + return NGX_OK;
  194 +}
141 195
142 196 void *
143   -ngx_postgres_upstream_create_srv_conf(ngx_conf_t *cf)
  197 +ngx_postgres_create_upstream_srv_conf(ngx_conf_t *cf)
144 198 {
145 199 ngx_postgres_upstream_srv_conf_t *conf;
146 200 ngx_pool_cleanup_t *cln;
@@ -153,7 +207,9 @@ ngx_postgres_upstream_create_srv_conf(ngx_conf_t *cf)
153 207 return NULL;
154 208 }
155 209
156   - /* set by ngx_pcalloc:
  210 + /*
  211 + * set by ngx_pcalloc():
  212 + *
157 213 * conf->peers = NULL
158 214 * conf->current = 0
159 215 * conf->servers = NULL
@@ -190,12 +246,15 @@ ngx_postgres_create_loc_conf(ngx_conf_t *cf)
190 246 return NULL;
191 247 }
192 248
193   - /* set by ngx_pcalloc:
  249 + /*
  250 + * set by ngx_pcalloc():
  251 + *
194 252 * conf->upstream.* = 0 / NULL
195 253 * conf->upstream_cv = NULL
196 254 * conf->default_query = NULL
197 255 * conf->methods_set = 0
198 256 * conf->queries = NULL
  257 + * conf->variables = NULL
199 258 */
200 259
201 260 conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
@@ -255,6 +314,10 @@ ngx_postgres_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
255 314 conf->get_value[1] = prev->get_value[1];
256 315 }
257 316
  317 + if (conf->variables == NULL) {
  318 + conf->variables = prev->variables;
  319 + }
  320 +
258 321 dd("returning NGX_CONF_OK");
259 322 return NGX_CONF_OK;
260 323 }
@@ -688,8 +751,8 @@ ngx_postgres_conf_query(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
688 751 char *
689 752 ngx_postgres_conf_get_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
690 753 {
691   - ngx_str_t *value = cf->args->elts;
692   - ngx_postgres_loc_conf_t *pglcf = conf;
  754 + ngx_str_t *value = cf->args->elts;
  755 + ngx_postgres_loc_conf_t *pglcf = conf;
693 756
694 757 dd("entering");
695 758
@@ -722,6 +785,125 @@ ngx_postgres_conf_get_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
722 785 return NGX_CONF_OK;
723 786 }
724 787
  788 +char *
  789 +ngx_postgres_conf_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  790 +{
  791 + ngx_str_t *value = cf->args->elts;
  792 + ngx_postgres_loc_conf_t *pglcf = conf;
  793 + ngx_postgres_variable_t *pgvar;
  794 + ngx_conf_enum_t *e;
  795 + ngx_int_t idx;
  796 + ngx_uint_t i;
  797 +
  798 + dd("entering");
  799 +
  800 + if (value[1].data[0] != '$') {
  801 + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  802 + "postgres: invalid variable name \"%V\""
  803 + " in \"%V\" directive", &value[1], &cmd->name);
  804 +
  805 + dd("returning NGX_CONF_ERROR");
  806 + return NGX_CONF_ERROR;
  807 + }
  808 +
  809 + value[1].len--;
  810 + value[1].data++;
  811 +
  812 + if (pglcf->variables == NULL) {
  813 + pglcf->variables = ngx_array_create(cf->pool, 4,
  814 + sizeof(ngx_postgres_variable_t));
  815 + if (pglcf->variables == NULL) {
  816 + dd("returning NGX_CONF_ERROR");
  817 + return NGX_CONF_ERROR;
  818 + }
  819 + }
  820 +
  821 + pgvar = ngx_array_push(pglcf->variables);
  822 + if (pgvar == NULL) {
  823 + dd("returning NGX_CONF_ERROR");
  824 + return NGX_CONF_ERROR;
  825 + }
  826 +
  827 + pgvar->idx = pglcf->variables->nelts - 1;
  828 +
  829 + pgvar->var = ngx_http_add_variable(cf, &value[1], 0);
  830 + if (pgvar->var == NULL) {
  831 + dd("returning NGX_CONF_ERROR");
  832 + return NGX_CONF_ERROR;
  833 + }
  834 +
  835 + idx = ngx_http_get_variable_index(cf, &value[1]);
  836 + if (idx == NGX_ERROR) {
  837 + dd("returning NGX_CONF_ERROR");
  838 + return NGX_CONF_ERROR;
  839 + }
  840 +
  841 + /*
  842 + * Check if "$variable" was previously defined,
  843 + * back-off even if it was marked as "CHANGEABLE".
  844 + */
  845 + if (pgvar->var->get_handler != NULL) {
  846 + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  847 + "postgres: variable \"$%V\" is duplicate"
  848 + " in \"%V\" directive", &value[1], &cmd->name);
  849 +
  850 + dd("returning NGX_CONF_ERROR");
  851 + return NGX_CONF_ERROR;
  852 + }
  853 +
  854 + pgvar->var->get_handler = ngx_postgres_variable_get_custom;
  855 + pgvar->var->data = (uintptr_t) pgvar;
  856 +
  857 + pgvar->value.row = ngx_atoi(value[2].data, value[2].len);
  858 + if (pgvar->value.row == NGX_ERROR) {
  859 + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  860 + "postgres: invalid row number \"%V\""
  861 + " in \"%V\" directive", &value[2], &cmd->name);
  862 +
  863 + dd("returning NGX_CONF_ERROR");
  864 + return NGX_CONF_ERROR;
  865 + }
  866 +
  867 + pgvar->value.column = ngx_atoi(value[3].data, value[3].len);
  868 + if (pgvar->value.column == NGX_ERROR) {
  869 + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  870 + "postgres: invalid column number \"%V\""
  871 + " in \"%V\" directive", &value[3], &cmd->name);
  872 +
  873 + dd("returning NGX_CONF_ERROR");
  874 + return NGX_CONF_ERROR;
  875 + }
  876 +
  877 + if (cf->args->nelts == 4) {
  878 + /* default value */
  879 + pgvar->value.required = NGX_POSTGRES_OPTIONAL;
  880 + } else {
  881 + /* user-specified value */
  882 + e = ngx_postgres_requirement_options;
  883 + for (i = 0; e[i].name.len != 0; i++) {
  884 + if ((e[i].name.len == value[4].len)
  885 + && (ngx_strcasecmp(e[i].name.data, value[4].data) == 0))
  886 + {
  887 + /* correct requirement option */
  888 + pgvar->value.required = e[i].value;
  889 + break;
  890 + }
  891 + }
  892 +
  893 + if (e[i].name.len == 0) {
  894 + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  895 + "postgres: invalid requirement option \"%V\""
  896 + " in \"%V\" directive", &value[4], &cmd->name);
  897 +
  898 + dd("returning NGX_CONF_ERROR");
  899 + return NGX_CONF_ERROR;
  900 + }
  901 + }
  902 +
  903 + dd("returning NGX_CONF_OK");
  904 + return NGX_CONF_OK;
  905 +}
  906 +
725 907 ngx_http_upstream_srv_conf_t *
726 908 ngx_postgres_find_upstream(ngx_http_request_t *r, ngx_url_t *url)
727 909 {
@@ -765,3 +947,4 @@ ngx_postgres_find_upstream(ngx_http_request_t *r, ngx_url_t *url)
765 947 dd("returning NULL");
766 948 return NULL;
767 949 }
  950 +
65 src/ngx_postgres_module.h
@@ -43,10 +43,28 @@ typedef enum {
43 43 } ngx_postgres_keepalive_overflow_t;
44 44
45 45 typedef struct {
  46 + ngx_uint_t key;
  47 + ngx_str_t sv;
  48 + ngx_http_complex_value_t *cv;
  49 +} ngx_postgres_mixed_t;
  50 +
  51 +typedef struct {
  52 + ngx_int_t row;
  53 + ngx_int_t column;
  54 + ngx_uint_t required;
  55 +} ngx_postgres_value_t;
  56 +
  57 +typedef struct {
  58 + ngx_uint_t idx;
  59 + ngx_http_variable_t *var;
  60 + ngx_postgres_value_t value;
  61 +} ngx_postgres_variable_t;
  62 +
  63 +typedef struct {
46 64 #if defined(nginx_version) && (nginx_version >= 8022)
47 65 ngx_addr_t *addrs;
48 66 #else
49   - ngx_peer_addr_t *addrs;
  67 + ngx_peer_addr_t *addrs;
50 68 #endif
51 69 ngx_uint_t naddrs;
52 70 in_port_t port;
@@ -88,12 +106,6 @@ typedef struct {
88 106 } ngx_postgres_upstream_srv_conf_t;
89 107
90 108 typedef struct {
91   - ngx_uint_t key;
92   - ngx_str_t sv;
93   - ngx_http_complex_value_t *cv;
94   -} ngx_postgres_mixed_t;
95   -
96   -typedef struct {
97 109 /* upstream */
98 110 ngx_http_upstream_conf_t upstream;
99 111 ngx_http_complex_value_t *upstream_cv;
@@ -103,6 +115,8 @@ typedef struct {
103 115 ngx_array_t *queries;
104 116 /* get_value */
105 117 ngx_int_t get_value[2];
  118 + /* custom variables */
  119 + ngx_array_t *variables;
106 120 } ngx_postgres_loc_conf_t;
107 121
108 122 typedef struct {
@@ -110,15 +124,34 @@ typedef struct {
110 124 uint32_t key;
111 125 } ngx_postgres_http_method_t;
112 126
113   -
114   -void *ngx_postgres_upstream_create_srv_conf(ngx_conf_t *);
115   -void *ngx_postgres_create_loc_conf(ngx_conf_t *);
116   -char *ngx_postgres_merge_loc_conf(ngx_conf_t *, void *, void *);
117   -char *ngx_postgres_conf_server(ngx_conf_t *, ngx_command_t *, void *);
118   -char *ngx_postgres_conf_keepalive(ngx_conf_t *, ngx_command_t *, void *);
119   -char *ngx_postgres_conf_pass(ngx_conf_t *, ngx_command_t *, void *);
120   -char *ngx_postgres_conf_query(ngx_conf_t *, ngx_command_t *, void *);
121   -char *ngx_postgres_conf_get_value(ngx_conf_t *, ngx_command_t *, void *);
  127 +typedef struct {
  128 + ngx_chain_t *response;
  129 + ngx_int_t var_cols;
  130 + ngx_int_t var_rows;
  131 + ngx_str_t var_value;
  132 + ngx_array_t *variables;
  133 +} ngx_postgres_ctx_t;
  134 +
  135 +
  136 +#define NGX_POSTGRES_OUTPUT_NONE 0
  137 +#define NGX_POSTGRES_OUTPUT_VALUE 1
  138 +#define NGX_POSTGRES_OUTPUT_ROW 2
  139 +#define NGX_POSTGRES_OUTPUT_RDS 3
  140 +
  141 +#define NGX_POSTGRES_OPTIONAL 0
  142 +#define NGX_POSTGRES_REQUIRED 1
  143 +
  144 +
  145 +ngx_int_t ngx_postgres_add_variables(ngx_conf_t *);
  146 +void *ngx_postgres_create_upstream_srv_conf(ngx_conf_t *);
  147 +void *ngx_postgres_create_loc_conf(ngx_conf_t *);
  148 +char *ngx_postgres_merge_loc_conf(ngx_conf_t *, void *, void *);
  149 +char *ngx_postgres_conf_server(ngx_conf_t *, ngx_command_t *, void *);
  150 +char *ngx_postgres_conf_keepalive(ngx_conf_t *, ngx_command_t *, void *);
  151 +char *ngx_postgres_conf_pass(ngx_conf_t *, ngx_command_t *, void *);
  152 +char *ngx_postgres_conf_query(ngx_conf_t *, ngx_command_t *, void *);
  153 +char *ngx_postgres_conf_get_value(ngx_conf_t *, ngx_command_t *, void *);
  154 +char *ngx_postgres_conf_set(ngx_conf_t *, ngx_command_t *, void *);
122 155
123 156 ngx_http_upstream_srv_conf_t *ngx_postgres_find_upstream(ngx_http_request_t *,
124 157 ngx_url_t *);
45 src/ngx_postgres_output.c
@@ -33,28 +33,10 @@
33 33
34 34
35 35 ngx_int_t
36   -ngx_postgres_output(ngx_http_request_t *r, PGresult *res)
37   -{
38   - ngx_postgres_loc_conf_t *pglcf;
39   -
40   - dd("entering");
41   -
42   - pglcf = ngx_http_get_module_loc_conf(r, ngx_postgres_module);
43   -
44   - if (pglcf->get_value[0] != NGX_CONF_UNSET) {
45   - dd("returning (single value)");
46   - return ngx_postgres_output_value(r, res, pglcf->get_value[0],
47   - pglcf->get_value[1]);
48   - } else {
49   - dd("returning (RDS)");
50   - return ngx_postgres_output_rds(r, res);
51   - }
52   -}
53   -
54   -ngx_int_t
55 36 ngx_postgres_output_value(ngx_http_request_t *r, PGresult *res, ngx_int_t row,
56 37 ngx_int_t col)
57 38 {
  39 + ngx_postgres_ctx_t *pgctx;
58 40 ngx_http_core_loc_conf_t *clcf;
59 41 ngx_chain_t *cl;
60 42 ngx_buf_t *b;
@@ -126,7 +108,17 @@ ngx_postgres_output_value(ngx_http_request_t *r, PGresult *res, ngx_int_t row,
126 108
127 109 cl->next = NULL;
128 110
129   - ngx_http_set_ctx(r, cl, ngx_postgres_module);
  111 + pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
  112 + pgctx->response = cl;
  113 +
  114 + pgctx->var_value.len = len;
  115 + pgctx->var_value.data = ngx_pnalloc(r->pool, len);
  116 + if (pgctx->var_value.data == NULL) {
  117 + dd("returning NGX_ERROR");
  118 + return NGX_ERROR;
  119 + }
  120 +
  121 + ngx_copy(pgctx->var_value.data, PQgetvalue(res, row, col), len);
130 122
131 123 dd("returning NGX_DONE");
132 124 return NGX_DONE;
@@ -135,8 +127,9 @@ ngx_postgres_output_value(ngx_http_request_t *r, PGresult *res, ngx_int_t row,
135 127 ngx_int_t
136 128 ngx_postgres_output_rds(ngx_http_request_t *r, PGresult *res)
137 129 {
138   - ngx_chain_t *first, *last;
139   - ngx_int_t col_count, row_count, row;
  130 + ngx_postgres_ctx_t *pgctx;
  131 + ngx_chain_t *first, *last;
  132 + ngx_int_t col_count, row_count, row;
140 133
141 134 dd("entering");
142 135
@@ -184,7 +177,8 @@ ngx_postgres_output_rds(ngx_http_request_t *r, PGresult *res)
184 177 done:
185 178 last->next = NULL;
186 179
187   - ngx_http_set_ctx(r, first, ngx_postgres_module);
  180 + pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
  181 + pgctx->response = first;
188 182
189 183 dd("returning NGX_DONE");
190 184 return NGX_DONE;
@@ -489,6 +483,11 @@ ngx_postgres_output_chain(ngx_http_request_t *r, ngx_chain_t *cl)
489 483 }
490 484 }
491 485
  486 + if (cl == NULL) {
  487 + dd("returning NGX_DONE");
  488 + return NGX_DONE;
  489 + }
  490 +
492 491 rc = ngx_http_output_filter(r, cl);
493 492 if (rc == NGX_ERROR || rc > NGX_OK) {
494 493 dd("returning rc:%d", (int) rc);
1  src/ngx_postgres_output.h
@@ -36,7 +36,6 @@
36 36 #include "resty_dbd_stream.h"
37 37
38 38
39   -ngx_int_t ngx_postgres_output(ngx_http_request_t *, PGresult *);
40 39 ngx_int_t ngx_postgres_output_value(ngx_http_request_t *, PGresult *,
41 40 ngx_int_t, ngx_int_t);
42 41 ngx_int_t ngx_postgres_output_rds(ngx_http_request_t *, PGresult *);
70 src/ngx_postgres_processor.c
@@ -31,6 +31,7 @@
31 31 #include "ngx_postgres_output.h"
32 32 #include "ngx_postgres_processor.h"
33 33 #include "ngx_postgres_util.h"
  34 +#include "ngx_postgres_variable.h"
34 35
35 36
36 37 void
@@ -297,7 +298,8 @@ ngx_postgres_upstream_get_result(ngx_http_request_t *r, ngx_connection_t *pgxc,
297 298 dd("result received successfully, cols:%d rows:%d",
298 299 PQnfields(res), PQntuples(res));
299 300
300   - rc = ngx_postgres_output(r, res);
  301 + pgxc->log->action = "processing result from PostgreSQL database";
  302 + rc = ngx_postgres_process_response(r, res);
301 303
302 304 PQclear(res);
303 305
@@ -316,6 +318,72 @@ ngx_postgres_upstream_get_result(ngx_http_request_t *r, ngx_connection_t *pgxc,
316 318 }
317 319
318 320 ngx_int_t
  321 +ngx_postgres_process_response(ngx_http_request_t *r, PGresult *res)
  322 +{
  323 + ngx_postgres_loc_conf_t *pglcf;
  324 + ngx_postgres_ctx_t *pgctx;
  325 + ngx_postgres_variable_t *pgvar;
  326 + ngx_str_t *store;
  327 + ngx_uint_t i;
  328 +
  329 + dd("entering");
  330 +
  331 + pglcf = ngx_http_get_module_loc_conf(r, ngx_postgres_module);
  332 +
  333 + pgctx = ngx_pcalloc(r->pool, sizeof(ngx_postgres_ctx_t));
  334 + if (pgctx == NULL) {
  335 + dd("returning NGX_ERROR");
  336 + return NGX_ERROR;
  337 + }
  338 +
  339 + /*
  340 + * set by ngx_pcalloc():
  341 + *
  342 + * pgctx->response = NULL
  343 + * pgctx->var_value = { 0, NULL }
  344 + * pgctx->variables = NULL
  345 + */
  346 +
  347 + pgctx->var_cols = PQnfields(res);
  348 + pgctx->var_rows = PQntuples(res);
  349 +
  350 + ngx_http_set_ctx(r, pgctx, ngx_postgres_module);
  351 +
  352 + if (pglcf->variables != NULL) {
  353 + /* set custom variables */
  354 + pgctx->variables = ngx_array_create(r->pool, pglcf->variables->nelts,
  355 + sizeof(ngx_str_t));
  356 + if (pgctx->variables == NULL) {
  357 + dd("returning NGX_ERROR");
  358 + return NGX_ERROR;
  359 + }
  360 +
  361 + /* skip ngx_array_push'ing */
  362 + pgctx->variables->nelts = pglcf->variables->nelts;
  363 +
  364 + pgvar = pglcf->variables->elts;
  365 + store = pgctx->variables->elts;
  366 +
  367 + for (i = 0; i < pglcf->variables->nelts; i++) {
  368 + store[i] = ngx_postgres_variable_set_custom(r, res, &pgvar[i]);
  369 + if ((store[i].len == 0) && (pgvar[i].value.required)) {
  370 + dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR");
  371 + return NGX_HTTP_INTERNAL_SERVER_ERROR;
  372 + }
  373 + }
  374 + }
  375 +
  376 + if (pglcf->get_value[0] != NGX_CONF_UNSET) {
  377 + dd("returning (single value)");
  378 + return ngx_postgres_output_value(r, res, pglcf->get_value[0],
  379 + pglcf->get_value[1]);
  380 + } else {
  381 + dd("returning (RDS)");
  382 + return ngx_postgres_output_rds(r, res);
  383 + }
  384 +}
  385 +
  386 +ngx_int_t
319 387 ngx_postgres_upstream_get_ack(ngx_http_request_t *r, ngx_connection_t *pgxc,
320 388 ngx_postgres_upstream_peer_data_t *pgdt)
321 389 {
2  src/ngx_postgres_processor.h
@@ -31,6 +31,7 @@
31 31
32 32 #include <ngx_core.h>
33 33 #include <ngx_http.h>
  34 +#include <libpq-fe.h>
34 35
35 36 #include "ngx_postgres_upstream.h"
36 37
@@ -42,6 +43,7 @@ ngx_int_t ngx_postgres_upstream_send_query(ngx_http_request_t *,
42 43 ngx_connection_t *, ngx_postgres_upstream_peer_data_t *);
43 44 ngx_int_t ngx_postgres_upstream_get_result(ngx_http_request_t *,
44 45 ngx_connection_t *, ngx_postgres_upstream_peer_data_t *);
  46 +ngx_int_t ngx_postgres_process_response(ngx_http_request_t *, PGresult *);
45 47 ngx_int_t ngx_postgres_upstream_get_ack(ngx_http_request_t *,
46 48 ngx_connection_t *, ngx_postgres_upstream_peer_data_t *);
47 49 ngx_int_t ngx_postgres_upstream_done(ngx_http_request_t *,
231 src/ngx_postgres_variable.c
... ... @@ -0,0 +1,231 @@
  1 +/*
  2 + * Copyright (c) 2010, FRiCKLE Piotr Sikora <info@frickle.com>
  3 + * All rights reserved.
  4 + *
  5 + * Redistribution and use in source and binary forms, with or without
  6 + * modification, are permitted provided that the following conditions
  7 + * are met:
  8 + * 1. Redistributions of source code must retain the above copyright
  9 + * notice, this list of conditions and the following disclaimer.
  10 + * 2. Redistributions in binary form must reproduce the above copyright
  11 + * notice, this list of conditions and the following disclaimer in the
  12 + * documentation and/or other materials provided with the distribution.
  13 + *
  14 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  15 + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  16 + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  17 + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  18 + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  19 + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  20 + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  21 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  22 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25 + */
  26 +
  27 +#define DDEBUG 0
  28 +#include "ngx_postgres_ddebug.h"
  29 +#include "ngx_postgres_module.h"
  30 +#include "ngx_postgres_variable.h"
  31 +
  32 +
  33 +ngx_int_t
  34 +ngx_postgres_variable_column_count(ngx_http_request_t *r,
  35 + ngx_http_variable_value_t *v, uintptr_t data)
  36 +{
  37 + ngx_postgres_ctx_t *pgctx;
  38 +
  39 + dd("entering: \"$postgres_column_count\"");
  40 +
  41 + pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
  42 +
  43 + if (pgctx == NULL) {
  44 + v->not_found = 1;
  45 + dd("returning NGX_OK");
  46 + return NGX_OK;
  47 + }
  48 +
  49 + v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN);
  50 + if (v->data == NULL) {
  51 + dd("returning NGX_ERROR");
  52 + return NGX_ERROR;
  53 + }
  54 +
  55 + v->len = ngx_sprintf(v->data, "%i", pgctx->var_cols) - v->data;
  56 + v->valid = 1;
  57 + v->no_cacheable = 0;
  58 + v->not_found = 0;
  59 +
  60 + dd("returning NGX_OK");
  61 + return NGX_OK;
  62 +}
  63 +
  64 +ngx_int_t
  65 +ngx_postgres_variable_row_count(ngx_http_request_t *r,
  66 + ngx_http_variable_value_t *v, uintptr_t data)
  67 +{
  68 + ngx_postgres_ctx_t *pgctx;
  69 +
  70 + dd("entering: \"$postgres_row_count\"");
  71 +
  72 + pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
  73 +
  74 + if (pgctx == NULL) {
  75 + v->not_found = 1;
  76 + dd("returning NGX_OK");
  77 + return NGX_OK;
  78 + }
  79 +
  80 + v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN);
  81 + if (v->data == NULL) {
  82 + dd("returning NGX_ERROR");
  83 + return NGX_ERROR;
  84 + }
  85 +
  86 + v->len = ngx_sprintf(v->data, "%i", pgctx->var_rows) - v->data;
  87 + v->valid = 1;
  88 + v->no_cacheable = 0;
  89 + v->not_found = 0;
  90 +
  91 + dd("returning NGX_OK");
  92 + return NGX_OK;
  93 +}
  94 +
  95 +ngx_int_t
  96 +ngx_postgres_variable_value(ngx_http_request_t *r,
  97 + ngx_http_variable_value_t *v, uintptr_t data)
  98 +{
  99 + ngx_postgres_ctx_t *pgctx;
  100 +
  101 + dd("entering: \"$postgres_value\"");
  102 +
  103 + pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
  104 +
  105 + if ((pgctx == NULL) || (pgctx->var_value.len == 0)) {
  106 + v->not_found = 1;
  107 + dd("returning NGX_OK");
  108 + return NGX_OK;
  109 + }
  110 +
  111 + v->valid = 1;
  112 + v->no_cacheable = 0;
  113 + v->not_found = 0;
  114 + v->len = pgctx->var_value.len;
  115 + v->data = pgctx->var_value.data;
  116 +
  117 + dd("returning NGX_OK");
  118 + return NGX_OK;
  119 +}
  120 +
  121 +ngx_int_t
  122 +ngx_postgres_variable_get_custom(ngx_http_request_t *r,
  123 + ngx_http_variable_value_t *v, uintptr_t data)
  124 +{
  125 + ngx_postgres_variable_t *pgvar = (ngx_postgres_variable_t *) data;
  126 + ngx_postgres_ctx_t *pgctx;
  127 + ngx_str_t *store;
  128 +
  129 + dd("entering: \"$%.*s\"", (int) pgvar->var->name.len,
  130 + pgvar->var->name.data);
  131 +
  132 + pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
  133 +
  134 + if ((pgctx == NULL) || (pgctx->variables == NULL)) {
  135 + v->not_found = 1;
  136 + dd("returning NGX_OK");
  137 + return NGX_OK;
  138 + }
  139 +
  140 + store = pgctx->variables->elts;
  141 +
  142 + /* idx is always valid */
  143 + if (store[pgvar->idx].len == 0) {
  144 + v->not_found = 1;
  145 + dd("returning NGX_OK");
  146 + return NGX_OK;
  147 + }
  148 +
  149 + v->valid = 1;
  150 + v->no_cacheable = 0;
  151 + v->not_found = 0;
  152 + v->len = store[pgvar->idx].len;
  153 + v->data = store[pgvar->idx].data;
  154 +
  155 + dd("returning NGX_OK");
  156 + return NGX_OK;
  157 +}
  158 +
  159 +ngx_str_t
  160 +ngx_postgres_variable_set_custom(ngx_http_request_t *r, PGresult *res,
  161 + ngx_postgres_variable_t *pgvar)
  162 +{
  163 + ngx_http_core_loc_conf_t *clcf;
  164 + ngx_postgres_value_t *pgv;
  165 + ngx_int_t col_count, row_count, len;
  166 + ngx_str_t value = ngx_null_string;
  167 +
  168 + dd("entering: \"$%.*s\"", (int) pgvar->var->name.len,
  169 + pgvar->var->name.data);
  170 +
  171 + col_count = PQnfields(res);
  172 + row_count = PQntuples(res);
  173 +
  174 + pgv = &pgvar->value;
  175 +
  176 + if ((pgv->row >= row_count) || (pgv->column >= col_count)) {
  177 + if (pgv->required) {
  178 + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
  179 +
  180 + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  181 + "postgres: \"postgres_set\" for variable \"$%V\""
  182 + " requires value out of range of the received"
  183 + " result-set (rows:%d cols:%d) in location \"%V\"",
  184 + &pgvar->var->name, row_count, col_count, &clcf->name);
  185 + }
  186 +
  187 + dd("returning empty value");
  188 + return value;
  189 + }
  190 +
  191 + if (PQgetisnull(res, pgv->row, pgv->column)) {
  192 + if (pgv->required) {
  193 + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
  194 +
  195 + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  196 + "postgres: \"postgres_set\" for variable \"$%V\""
  197 + " requires non-NULL value in location \"%V\"",
  198 + &pgvar->var->name, &clcf->name);
  199 + }
  200 +
  201 + dd("returning empty value");
  202 + return value;
  203 + }
  204 +
  205 + len = PQgetlength(res, pgv->row, pgv->column);
  206 + if (len == 0) {
  207 + if (pgv->required) {
  208 + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
  209 +
  210 + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  211 + "postgres: \"postgres_set\" for variable \"$%V\""
  212 + " requires non-zero length value in location \"%V\"",
  213 + &pgvar->var->name, &clcf->name);
  214 + }
  215 +
  216 + dd("returning empty value");
  217 + return value;
  218 + }
  219 +
  220 + value.data = ngx_pnalloc(r->pool, len);
  221 + if (value.data == NULL) {
  222 + dd("returning empty value");
  223 + return value;
  224 + }
  225 +
  226 + ngx_copy(value.data, PQgetvalue(res, pgv->row, pgv->column), len);
  227 + value.len = len;
  228 +
  229 + dd("returning non-empty value");
  230 + return value;
  231 +}
48 src/ngx_postgres_variable.h
... ... @@ -0,0 +1,48 @@
  1 +/*
  2 + * Copyright (c) 2010, FRiCKLE Piotr Sikora <info@frickle.com>
  3 + * All rights reserved.
  4 + *
  5 + * Redistribution and use in source and binary forms, with or without
  6 + * modification, are permitted provided that the following conditions
  7 + * are met:
  8 + * 1. Redistributions of source code must retain the above copyright
  9 + * notice, this list of conditions and the following disclaimer.
  10 + * 2. Redistributions in binary form must reproduce the above copyright
  11 + * notice, this list of conditions and the following disclaimer in the
  12 + * documentation and/or other materials provided with the distribution.
  13 + *
  14 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  15 + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  16 + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  17 + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  18 + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  19 + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  20 + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  21 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  22 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25 + */
  26 +
  27 +#ifndef _NGX_POSTGRES_VARIABLE_H_
  28 +#define _NGX_POSTGRES_VARIABLE_H_
  29 +
  30 +#include <ngx_core.h>
  31 +#include <ngx_http.h>
  32 +#include <libpq-fe.h>
  33 +
  34 +#include "ngx_postgres_module.h"
  35 +
  36 +
  37 +ngx_int_t ngx_postgres_variable_column_count(ngx_http_request_t *,
  38 + ngx_http_variable_value_t *, uintptr_t);
  39 +ngx_int_t ngx_postgres_variable_row_count(ngx_http_request_t *,
  40 + ngx_http_variable_value_t *, uintptr_t);
  41 +ngx_int_t ngx_postgres_variable_value(ngx_http_request_t *,
  42 + ngx_http_variable_value_t *, uintptr_t);
  43 +ngx_int_t ngx_postgres_variable_get_custom(ngx_http_request_t *,
  44 + ngx_http_variable_value_t *, uintptr_t);
  45 +ngx_str_t ngx_postgres_variable_set_custom(ngx_http_request_t *r,
  46 + PGresult *, ngx_postgres_variable_t *);
  47 +
  48 +#endif /* _NGX_POSTGRES_VARIABLE_H_ */
286 test/t/variables.t
... ... @@ -0,0 +1,286 @@
  1 +# vi:filetype=perl
  2 +
  3 +use lib 'lib';
  4 +use Test::Nginx::Socket;
  5 +
  6 +repeat_each(2);
  7 +
  8 +plan tests => repeat_each() * (blocks() * 3 + 2 * 1 - 3 * 2);
  9 +
  10 +worker_connections(128);
  11 +run_tests();
  12 +
  13 +no_diff();
  14 +
  15 +__DATA__
  16 +
  17 +=== TEST 1: sanity
  18 +little-endian systems only
  19 +
  20 +--- http_config
  21 + upstream database {
  22 + postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
  23 + }
  24 +--- config
  25 + location /postgres {
  26 + postgres_pass database;
  27 + postgres_query "select 'test' as echo";
  28 + postgres_set $test 0 0;
  29 + add_header "X-Test" $test;
  30 + }
  31 +--- request
  32 +GET /postgres
  33 +--- error_code: 200
  34 +--- response_headers
  35 +Content-Type: application/x-resty-dbd-stream
  36 +X-Test: test
  37 +--- response_body eval
  38 +"\x{00}". # endian
  39 +"\x{03}\x{00}\x{00}\x{00}". # format version 0.0.3
  40 +"\x{00}". # result type
  41 +"\x{00}\x{00}". # std errcode
  42 +"\x{02}\x{00}". # driver errcode
  43 +"\x{00}\x{00}". # driver errstr len
  44 +"". # driver errstr data
  45 +"\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}". # rows affected
  46 +"\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}". # insert id
  47 +"\x{01}\x{00}". # col count
  48 +"\x{00}\x{80}". # std col type (unknown/str)
  49 +"\x{c1}\x{02}". # driver col type
  50 +"\x{04}\x{00}". # col name len
  51 +"echo". # col name data
  52 +"\x{01}". # valid row flag
  53 +"\x{04}\x{00}\x{00}\x{00}". # field len
  54 +"test". # field data
  55 +"\x{00}" # row list terminator
  56 +--- timeout: 10
  57 +
  58 +
  59 +
  60 +=== TEST 2: out-of-range value (optional)
  61 +little-endian systems only
  62 +
  63 +--- http_config
  64 + upstream database {
  65 + postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
  66 + }
  67 +--- config
  68 + location /postgres {
  69 + postgres_pass database;
  70 + postgres_query "select 'test' as echo";
  71 + postgres_set $test 0 1;
  72 + add_header "X-Test" $test;
  73 + }
  74 +--- request
  75 +GET /postgres
  76 +--- error_code: 200
  77 +--- response_headers
  78 +Content-Type: application/x-resty-dbd-stream
  79 +X-Test:
  80 +--- timeout: 10
  81 +
  82 +
  83 +
  84 +=== TEST 3: NULL value (optional)
  85 +little-endian systems only
  86 +
  87 +--- http_config
  88 + upstream database {
  89 + postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
  90 + }
  91 +--- config
  92 + location /postgres {
  93 + postgres_pass database;
  94 + postgres_query "select NULL as echo";
  95 + postgres_set $test 0 0;
  96 + add_header "X-Test" $test;
  97 + }
  98 +--- request
  99 +GET /postgres
  100 +--- error_code: 200
  101 +--- response_headers
  102 +Content-Type: application/x-resty-dbd-stream
  103 +X-Test:
  104 +--- timeout: 10
  105 +
  106 +
  107 +
  108 +=== TEST 4: zero-length value (optional)
  109 +little-endian systems only
  110 +
  111 +--- http_config
  112 + upstream database {
  113 + postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
  114 + }
  115 +--- config
  116 + location /postgres {
  117 + postgres_pass database;
  118 + postgres_query "select '' as echo";
  119 + postgres_set $test 0 0;
  120 + add_header "X-Test" $test;
  121 + }
  122 +--- request
  123 +GET /postgres
  124 +--- error_code: 200
  125 +--- response_headers
  126 +Content-Type: application/x-resty-dbd-stream
  127 +X-Test:
  128 +--- timeout: 10
  129 +
  130 +
  131 +
  132 +=== TEST 5: out-of-range value (required)
  133 +little-endian systems only
  134 +
  135 +--- http_config
  136 + upstream database {
  137 + postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
  138 + }
  139 +--- config
  140 + location /postgres {
  141 + postgres_pass database;
  142 + postgres_query "select 'test' as echo";
  143 + postgres_set $test 0 1 required;
  144 + add_header "X-Test" $test;
  145 + }
  146 +--- request
  147 +GET /postgres
  148 +--- error_code: 500
  149 +--- timeout: 10
  150 +
  151 +
  152 +
  153 +=== TEST 6: NULL value (required)
  154 +little-endian systems only
  155 +
  156 +--- http_config
  157 + upstream database {
  158 + postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
  159 + }
  160 +--- config
  161 + location /postgres {
  162 + postgres_pass database;
  163 + postgres_query "select NULL as echo";
  164 + postgres_set $test 0 0 required;
  165 + add_header "X-Test" $test;
  166 + }
  167 +--- request
  168 +GET /postgres
  169 +--- error_code: 500
  170 +--- timeout: 10
  171 +
  172 +
  173 +
  174 +=== TEST 7: zero-length value (required)
  175 +little-endian systems only
  176 +
  177 +--- http_config
  178 + upstream database {
  179 + postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
  180 + }
  181 +--- config
  182 + location /postgres {
  183 + postgres_pass database;
  184 + postgres_query "select '' as echo";
  185 + postgres_set $test 0 0 required;
  186 + add_header "X-Test" $test;
  187 + }
  188 +--- request
  189 +GET /postgres
  190 +--- error_code: 500
  191 +--- timeout: 10
  192 +
  193 +
  194 +
  195 +=== TEST 8: $postgres_column_count
  196 +little-endian systems only
  197 +
  198 +--- http_config
  199 + upstream database {
  200 + postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
  201 + }
  202 +--- config
  203 + location /postgres {
  204 + postgres_pass database;
  205 + postgres_query "select 'a', 'b', 'c'";
  206 + add_header "X-Columns" $postgres_column_count;
  207 + }
  208 +--- request
  209 +GET /postgres
  210 +--- error_code: 200
  211 +--- response_headers
  212 +Content-Type: application/x-resty-dbd-stream
  213 +X-Columns: 3
  214 +--- timeout: 10
  215 +
  216 +
  217 +
  218 +=== TEST 9: $postgres_row_count
  219 +little-endian systems only
  220 +
  221 +--- http_config
  222 + upstream database {
  223 + postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
  224 + }
  225 +--- config
  226 + location /postgres {
  227 + postgres_pass database;
  228 + postgres_query "select 'a', 'b', 'c'";
  229 + add_header "X-Rows" $postgres_row_count;
  230 + }
  231 +--- request
  232 +GET /postgres
  233 +--- error_code: 200
  234 +--- response_headers
  235 +Content-Type: application/x-resty-dbd-stream
  236 +X-Rows: 1
  237 +--- timeout: 10
  238 +
  239 +