Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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
@PiotrSikora PiotrSikora authored
View
30 README
@@ -55,6 +55,17 @@ CONFIGURATION DIRECTIVES:
default: none
+ postgres_set $variable row column [optional|required] (context: http, server, location)
+ ---------------------------------------------------------------------------------------
+ Get single value from the result-set and keep it in $variable. When requirement level
+ is set to "required" and value is either out-of-range, NULL or zero-length, then nginx
+ returns 500 Internal Server Error. Such condition is silently ignored when requirement
+ level is set to "optional".
+ Row and column numbers start at 0.
+
+ default: none
+
+
postgres_connect_timeout timeout (context: http, server, location)
------------------------------------------------------------------
Set timeout for connecting to the database (in milliseconds).
@@ -69,6 +80,25 @@ CONFIGURATION DIRECTIVES:
default: 30000ms (30s)
+CONFIGURATION VARIABLES:
+------------------------
+
+ $postgres_column_count
+ ----------------------
+ Number of columns in received result-set.
+
+
+ $postgres_row_count
+ -------------------
+ Number of rows in received result-set.
+
+
+ $postgres_value
+ ---------------
+ Single value from the received result-set (available only when
+ "postgres_get_value" directive is being used).
+
+
EXAMPLE CONFIGURATION #1:
-------------------------
http {
View
4 config
@@ -89,7 +89,7 @@ fi
ngx_addon_name=ngx_postgres_module
HTTP_MODULES="$HTTP_MODULES ngx_postgres_module"
-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"
-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"
+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"
+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"
have=NGX_POSTGRES_MODULE . auto/have
View
12 src/ngx_postgres_handler.c
@@ -34,8 +34,6 @@
#include "ngx_postgres_processor.h"
#include "ngx_postgres_util.h"
-#include <nginx.h>
-
ngx_int_t
ngx_postgres_handler(ngx_http_request_t *r)
@@ -295,13 +293,17 @@ ngx_postgres_abort_request(ngx_http_request_t *r)
void
ngx_postgres_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{
- ngx_chain_t *response;
+ ngx_postgres_ctx_t *pgctx;
dd("entering");
if (rc == NGX_OK) {
- response = ngx_http_get_module_ctx(r, ngx_postgres_module);
- ngx_postgres_output_chain(r, response);
+ pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
+ if (pgctx != NULL) {
+ ngx_postgres_output_chain(r, pgctx->response);
+ } else {
+ ngx_postgres_output_chain(r, NULL);
+ }
}
dd("returning");
View
197 src/ngx_postgres_module.c
@@ -33,6 +33,7 @@
#include "ngx_postgres_module.h"
#include "ngx_postgres_upstream.h"
#include "ngx_postgres_util.h"
+#include "ngx_postgres_variable.h"
static ngx_command_t ngx_postgres_module_commands[] = {
@@ -72,6 +73,14 @@ static ngx_command_t ngx_postgres_module_commands[] = {
0,
NULL },
+ { ngx_string("postgres_set"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
+ |NGX_CONF_TAKE3|NGX_CONF_TAKE4,
+ ngx_postgres_conf_set,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
{ ngx_string("postgres_connect_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
@@ -89,14 +98,31 @@ static ngx_command_t ngx_postgres_module_commands[] = {
ngx_null_command
};
+static ngx_http_variable_t ngx_postgres_module_variables[] = {
+
+ { ngx_string("postgres_column_count"), NULL,
+ ngx_postgres_variable_column_count, 0,
+ NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+ { ngx_string("postgres_row_count"), NULL,
+ ngx_postgres_variable_row_count, 0,
+ NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+ { ngx_string("postgres_value"), NULL,
+ ngx_postgres_variable_value, 0,
+ NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+ { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
+
static ngx_http_module_t ngx_postgres_module_ctx = {
- NULL, /* preconfiguration */
+ ngx_postgres_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
- ngx_postgres_upstream_create_srv_conf, /* create server configuration */
+ ngx_postgres_create_upstream_srv_conf, /* create server configuration */
NULL, /* merge server configuration */
ngx_postgres_create_loc_conf, /* create location configuration */
@@ -138,9 +164,37 @@ ngx_postgres_http_method_t ngx_postgres_http_methods[] = {
{ NULL, 0 }
};
+ngx_conf_enum_t ngx_postgres_requirement_options[] = {
+ { ngx_string("optional"), NGX_POSTGRES_OPTIONAL },
+ { ngx_string("required"), NGX_POSTGRES_REQUIRED },
+ { ngx_null_string, 0 }
+};
+
+
+ngx_int_t
+ngx_postgres_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var, *v;
+
+ dd("entering");
+
+ for (v = ngx_postgres_module_variables; v->name.len; v++) {
+ var = ngx_http_add_variable(cf, &v->name, v->flags);
+ if (var == NULL) {
+ dd("returning NGX_ERROR");
+ return NGX_ERROR;
+ }
+
+ var->get_handler = v->get_handler;
+ var->data = v->data;
+ }
+
+ dd("returning NGX_OK");
+ return NGX_OK;
+}
void *
-ngx_postgres_upstream_create_srv_conf(ngx_conf_t *cf)
+ngx_postgres_create_upstream_srv_conf(ngx_conf_t *cf)
{
ngx_postgres_upstream_srv_conf_t *conf;
ngx_pool_cleanup_t *cln;
@@ -153,7 +207,9 @@ ngx_postgres_upstream_create_srv_conf(ngx_conf_t *cf)
return NULL;
}
- /* set by ngx_pcalloc:
+ /*
+ * set by ngx_pcalloc():
+ *
* conf->peers = NULL
* conf->current = 0
* conf->servers = NULL
@@ -190,12 +246,15 @@ ngx_postgres_create_loc_conf(ngx_conf_t *cf)
return NULL;
}
- /* set by ngx_pcalloc:
+ /*
+ * set by ngx_pcalloc():
+ *
* conf->upstream.* = 0 / NULL
* conf->upstream_cv = NULL
* conf->default_query = NULL
* conf->methods_set = 0
* conf->queries = NULL
+ * conf->variables = NULL
*/
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)
conf->get_value[1] = prev->get_value[1];
}
+ if (conf->variables == NULL) {
+ conf->variables = prev->variables;
+ }
+
dd("returning NGX_CONF_OK");
return NGX_CONF_OK;
}
@@ -688,8 +751,8 @@ ngx_postgres_conf_query(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
char *
ngx_postgres_conf_get_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
- ngx_str_t *value = cf->args->elts;
- ngx_postgres_loc_conf_t *pglcf = conf;
+ ngx_str_t *value = cf->args->elts;
+ ngx_postgres_loc_conf_t *pglcf = conf;
dd("entering");
@@ -722,6 +785,125 @@ ngx_postgres_conf_get_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_OK;
}
+char *
+ngx_postgres_conf_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_str_t *value = cf->args->elts;
+ ngx_postgres_loc_conf_t *pglcf = conf;
+ ngx_postgres_variable_t *pgvar;
+ ngx_conf_enum_t *e;
+ ngx_int_t idx;
+ ngx_uint_t i;
+
+ dd("entering");
+
+ if (value[1].data[0] != '$') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "postgres: invalid variable name \"%V\""
+ " in \"%V\" directive", &value[1], &cmd->name);
+
+ dd("returning NGX_CONF_ERROR");
+ return NGX_CONF_ERROR;
+ }
+
+ value[1].len--;
+ value[1].data++;
+
+ if (pglcf->variables == NULL) {
+ pglcf->variables = ngx_array_create(cf->pool, 4,
+ sizeof(ngx_postgres_variable_t));
+ if (pglcf->variables == NULL) {
+ dd("returning NGX_CONF_ERROR");
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ pgvar = ngx_array_push(pglcf->variables);
+ if (pgvar == NULL) {
+ dd("returning NGX_CONF_ERROR");
+ return NGX_CONF_ERROR;
+ }
+
+ pgvar->idx = pglcf->variables->nelts - 1;
+
+ pgvar->var = ngx_http_add_variable(cf, &value[1], 0);
+ if (pgvar->var == NULL) {
+ dd("returning NGX_CONF_ERROR");
+ return NGX_CONF_ERROR;
+ }
+
+ idx = ngx_http_get_variable_index(cf, &value[1]);
+ if (idx == NGX_ERROR) {
+ dd("returning NGX_CONF_ERROR");
+ return NGX_CONF_ERROR;
+ }
+
+ /*
+ * Check if "$variable" was previously defined,
+ * back-off even if it was marked as "CHANGEABLE".
+ */
+ if (pgvar->var->get_handler != NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "postgres: variable \"$%V\" is duplicate"
+ " in \"%V\" directive", &value[1], &cmd->name);
+
+ dd("returning NGX_CONF_ERROR");
+ return NGX_CONF_ERROR;
+ }
+
+ pgvar->var->get_handler = ngx_postgres_variable_get_custom;
+ pgvar->var->data = (uintptr_t) pgvar;
+
+ pgvar->value.row = ngx_atoi(value[2].data, value[2].len);
+ if (pgvar->value.row == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "postgres: invalid row number \"%V\""
+ " in \"%V\" directive", &value[2], &cmd->name);
+
+ dd("returning NGX_CONF_ERROR");
+ return NGX_CONF_ERROR;
+ }
+
+ pgvar->value.column = ngx_atoi(value[3].data, value[3].len);
+ if (pgvar->value.column == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "postgres: invalid column number \"%V\""
+ " in \"%V\" directive", &value[3], &cmd->name);
+
+ dd("returning NGX_CONF_ERROR");
+ return NGX_CONF_ERROR;
+ }
+
+ if (cf->args->nelts == 4) {
+ /* default value */
+ pgvar->value.required = NGX_POSTGRES_OPTIONAL;
+ } else {
+ /* user-specified value */
+ e = ngx_postgres_requirement_options;
+ for (i = 0; e[i].name.len != 0; i++) {
+ if ((e[i].name.len == value[4].len)
+ && (ngx_strcasecmp(e[i].name.data, value[4].data) == 0))
+ {
+ /* correct requirement option */
+ pgvar->value.required = e[i].value;
+ break;
+ }
+ }
+
+ if (e[i].name.len == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "postgres: invalid requirement option \"%V\""
+ " in \"%V\" directive", &value[4], &cmd->name);
+
+ dd("returning NGX_CONF_ERROR");
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ dd("returning NGX_CONF_OK");
+ return NGX_CONF_OK;
+}
+
ngx_http_upstream_srv_conf_t *
ngx_postgres_find_upstream(ngx_http_request_t *r, ngx_url_t *url)
{
@@ -765,3 +947,4 @@ ngx_postgres_find_upstream(ngx_http_request_t *r, ngx_url_t *url)
dd("returning NULL");
return NULL;
}
+
View
65 src/ngx_postgres_module.h
@@ -43,10 +43,28 @@ typedef enum {
} ngx_postgres_keepalive_overflow_t;
typedef struct {
+ ngx_uint_t key;
+ ngx_str_t sv;
+ ngx_http_complex_value_t *cv;
+} ngx_postgres_mixed_t;
+
+typedef struct {
+ ngx_int_t row;
+ ngx_int_t column;
+ ngx_uint_t required;
+} ngx_postgres_value_t;
+
+typedef struct {
+ ngx_uint_t idx;
+ ngx_http_variable_t *var;
+ ngx_postgres_value_t value;
+} ngx_postgres_variable_t;
+
+typedef struct {
#if defined(nginx_version) && (nginx_version >= 8022)
ngx_addr_t *addrs;
#else
- ngx_peer_addr_t *addrs;
+ ngx_peer_addr_t *addrs;
#endif
ngx_uint_t naddrs;
in_port_t port;
@@ -88,12 +106,6 @@ typedef struct {
} ngx_postgres_upstream_srv_conf_t;
typedef struct {
- ngx_uint_t key;
- ngx_str_t sv;
- ngx_http_complex_value_t *cv;
-} ngx_postgres_mixed_t;
-
-typedef struct {
/* upstream */
ngx_http_upstream_conf_t upstream;
ngx_http_complex_value_t *upstream_cv;
@@ -103,6 +115,8 @@ typedef struct {
ngx_array_t *queries;
/* get_value */
ngx_int_t get_value[2];
+ /* custom variables */
+ ngx_array_t *variables;
} ngx_postgres_loc_conf_t;
typedef struct {
@@ -110,15 +124,34 @@ typedef struct {
uint32_t key;
} ngx_postgres_http_method_t;
-
-void *ngx_postgres_upstream_create_srv_conf(ngx_conf_t *);
-void *ngx_postgres_create_loc_conf(ngx_conf_t *);
-char *ngx_postgres_merge_loc_conf(ngx_conf_t *, void *, void *);
-char *ngx_postgres_conf_server(ngx_conf_t *, ngx_command_t *, void *);
-char *ngx_postgres_conf_keepalive(ngx_conf_t *, ngx_command_t *, void *);
-char *ngx_postgres_conf_pass(ngx_conf_t *, ngx_command_t *, void *);
-char *ngx_postgres_conf_query(ngx_conf_t *, ngx_command_t *, void *);
-char *ngx_postgres_conf_get_value(ngx_conf_t *, ngx_command_t *, void *);
+typedef struct {
+ ngx_chain_t *response;
+ ngx_int_t var_cols;
+ ngx_int_t var_rows;
+ ngx_str_t var_value;
+ ngx_array_t *variables;
+} ngx_postgres_ctx_t;
+
+
+#define NGX_POSTGRES_OUTPUT_NONE 0
+#define NGX_POSTGRES_OUTPUT_VALUE 1
+#define NGX_POSTGRES_OUTPUT_ROW 2
+#define NGX_POSTGRES_OUTPUT_RDS 3
+
+#define NGX_POSTGRES_OPTIONAL 0
+#define NGX_POSTGRES_REQUIRED 1
+
+
+ngx_int_t ngx_postgres_add_variables(ngx_conf_t *);
+void *ngx_postgres_create_upstream_srv_conf(ngx_conf_t *);
+void *ngx_postgres_create_loc_conf(ngx_conf_t *);
+char *ngx_postgres_merge_loc_conf(ngx_conf_t *, void *, void *);
+char *ngx_postgres_conf_server(ngx_conf_t *, ngx_command_t *, void *);
+char *ngx_postgres_conf_keepalive(ngx_conf_t *, ngx_command_t *, void *);
+char *ngx_postgres_conf_pass(ngx_conf_t *, ngx_command_t *, void *);
+char *ngx_postgres_conf_query(ngx_conf_t *, ngx_command_t *, void *);
+char *ngx_postgres_conf_get_value(ngx_conf_t *, ngx_command_t *, void *);
+char *ngx_postgres_conf_set(ngx_conf_t *, ngx_command_t *, void *);
ngx_http_upstream_srv_conf_t *ngx_postgres_find_upstream(ngx_http_request_t *,
ngx_url_t *);
View
45 src/ngx_postgres_output.c
@@ -33,28 +33,10 @@
ngx_int_t
-ngx_postgres_output(ngx_http_request_t *r, PGresult *res)
-{
- ngx_postgres_loc_conf_t *pglcf;
-
- dd("entering");
-
- pglcf = ngx_http_get_module_loc_conf(r, ngx_postgres_module);
-
- if (pglcf->get_value[0] != NGX_CONF_UNSET) {
- dd("returning (single value)");
- return ngx_postgres_output_value(r, res, pglcf->get_value[0],
- pglcf->get_value[1]);
- } else {
- dd("returning (RDS)");
- return ngx_postgres_output_rds(r, res);
- }
-}
-
-ngx_int_t
ngx_postgres_output_value(ngx_http_request_t *r, PGresult *res, ngx_int_t row,
ngx_int_t col)
{
+ ngx_postgres_ctx_t *pgctx;
ngx_http_core_loc_conf_t *clcf;
ngx_chain_t *cl;
ngx_buf_t *b;
@@ -126,7 +108,17 @@ ngx_postgres_output_value(ngx_http_request_t *r, PGresult *res, ngx_int_t row,
cl->next = NULL;
- ngx_http_set_ctx(r, cl, ngx_postgres_module);
+ pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
+ pgctx->response = cl;
+
+ pgctx->var_value.len = len;
+ pgctx->var_value.data = ngx_pnalloc(r->pool, len);
+ if (pgctx->var_value.data == NULL) {
+ dd("returning NGX_ERROR");
+ return NGX_ERROR;
+ }
+
+ ngx_copy(pgctx->var_value.data, PQgetvalue(res, row, col), len);
dd("returning NGX_DONE");
return NGX_DONE;
@@ -135,8 +127,9 @@ ngx_postgres_output_value(ngx_http_request_t *r, PGresult *res, ngx_int_t row,
ngx_int_t
ngx_postgres_output_rds(ngx_http_request_t *r, PGresult *res)
{
- ngx_chain_t *first, *last;
- ngx_int_t col_count, row_count, row;
+ ngx_postgres_ctx_t *pgctx;
+ ngx_chain_t *first, *last;
+ ngx_int_t col_count, row_count, row;
dd("entering");
@@ -184,7 +177,8 @@ ngx_postgres_output_rds(ngx_http_request_t *r, PGresult *res)
done:
last->next = NULL;
- ngx_http_set_ctx(r, first, ngx_postgres_module);
+ pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
+ pgctx->response = first;
dd("returning NGX_DONE");
return NGX_DONE;
@@ -489,6 +483,11 @@ ngx_postgres_output_chain(ngx_http_request_t *r, ngx_chain_t *cl)
}
}
+ if (cl == NULL) {
+ dd("returning NGX_DONE");
+ return NGX_DONE;
+ }
+
rc = ngx_http_output_filter(r, cl);
if (rc == NGX_ERROR || rc > NGX_OK) {
dd("returning rc:%d", (int) rc);
View
1  src/ngx_postgres_output.h
@@ -36,7 +36,6 @@
#include "resty_dbd_stream.h"
-ngx_int_t ngx_postgres_output(ngx_http_request_t *, PGresult *);
ngx_int_t ngx_postgres_output_value(ngx_http_request_t *, PGresult *,
ngx_int_t, ngx_int_t);
ngx_int_t ngx_postgres_output_rds(ngx_http_request_t *, PGresult *);
View
70 src/ngx_postgres_processor.c
@@ -31,6 +31,7 @@
#include "ngx_postgres_output.h"
#include "ngx_postgres_processor.h"
#include "ngx_postgres_util.h"
+#include "ngx_postgres_variable.h"
void
@@ -297,7 +298,8 @@ ngx_postgres_upstream_get_result(ngx_http_request_t *r, ngx_connection_t *pgxc,
dd("result received successfully, cols:%d rows:%d",
PQnfields(res), PQntuples(res));
- rc = ngx_postgres_output(r, res);
+ pgxc->log->action = "processing result from PostgreSQL database";
+ rc = ngx_postgres_process_response(r, res);
PQclear(res);
@@ -316,6 +318,72 @@ ngx_postgres_upstream_get_result(ngx_http_request_t *r, ngx_connection_t *pgxc,
}
ngx_int_t
+ngx_postgres_process_response(ngx_http_request_t *r, PGresult *res)
+{
+ ngx_postgres_loc_conf_t *pglcf;
+ ngx_postgres_ctx_t *pgctx;
+ ngx_postgres_variable_t *pgvar;
+ ngx_str_t *store;
+ ngx_uint_t i;
+
+ dd("entering");
+
+ pglcf = ngx_http_get_module_loc_conf(r, ngx_postgres_module);
+
+ pgctx = ngx_pcalloc(r->pool, sizeof(ngx_postgres_ctx_t));
+ if (pgctx == NULL) {
+ dd("returning NGX_ERROR");
+ return NGX_ERROR;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * pgctx->response = NULL
+ * pgctx->var_value = { 0, NULL }
+ * pgctx->variables = NULL
+ */
+
+ pgctx->var_cols = PQnfields(res);
+ pgctx->var_rows = PQntuples(res);
+
+ ngx_http_set_ctx(r, pgctx, ngx_postgres_module);
+
+ if (pglcf->variables != NULL) {
+ /* set custom variables */
+ pgctx->variables = ngx_array_create(r->pool, pglcf->variables->nelts,
+ sizeof(ngx_str_t));
+ if (pgctx->variables == NULL) {
+ dd("returning NGX_ERROR");
+ return NGX_ERROR;
+ }
+
+ /* skip ngx_array_push'ing */
+ pgctx->variables->nelts = pglcf->variables->nelts;
+
+ pgvar = pglcf->variables->elts;
+ store = pgctx->variables->elts;
+
+ for (i = 0; i < pglcf->variables->nelts; i++) {
+ store[i] = ngx_postgres_variable_set_custom(r, res, &pgvar[i]);
+ if ((store[i].len == 0) && (pgvar[i].value.required)) {
+ dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR");
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+ }
+
+ if (pglcf->get_value[0] != NGX_CONF_UNSET) {
+ dd("returning (single value)");
+ return ngx_postgres_output_value(r, res, pglcf->get_value[0],
+ pglcf->get_value[1]);
+ } else {
+ dd("returning (RDS)");
+ return ngx_postgres_output_rds(r, res);
+ }
+}
+
+ngx_int_t
ngx_postgres_upstream_get_ack(ngx_http_request_t *r, ngx_connection_t *pgxc,
ngx_postgres_upstream_peer_data_t *pgdt)
{
View
2  src/ngx_postgres_processor.h
@@ -31,6 +31,7 @@
#include <ngx_core.h>
#include <ngx_http.h>
+#include <libpq-fe.h>
#include "ngx_postgres_upstream.h"
@@ -42,6 +43,7 @@ ngx_int_t ngx_postgres_upstream_send_query(ngx_http_request_t *,
ngx_connection_t *, ngx_postgres_upstream_peer_data_t *);
ngx_int_t ngx_postgres_upstream_get_result(ngx_http_request_t *,
ngx_connection_t *, ngx_postgres_upstream_peer_data_t *);
+ngx_int_t ngx_postgres_process_response(ngx_http_request_t *, PGresult *);
ngx_int_t ngx_postgres_upstream_get_ack(ngx_http_request_t *,
ngx_connection_t *, ngx_postgres_upstream_peer_data_t *);
ngx_int_t ngx_postgres_upstream_done(ngx_http_request_t *,
View
231 src/ngx_postgres_variable.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2010, FRiCKLE Piotr Sikora <info@frickle.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define DDEBUG 0
+#include "ngx_postgres_ddebug.h"
+#include "ngx_postgres_module.h"
+#include "ngx_postgres_variable.h"
+
+
+ngx_int_t
+ngx_postgres_variable_column_count(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_postgres_ctx_t *pgctx;
+
+ dd("entering: \"$postgres_column_count\"");
+
+ pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
+
+ if (pgctx == NULL) {
+ v->not_found = 1;
+ dd("returning NGX_OK");
+ return NGX_OK;
+ }
+
+ v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN);
+ if (v->data == NULL) {
+ dd("returning NGX_ERROR");
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(v->data, "%i", pgctx->var_cols) - v->data;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ dd("returning NGX_OK");
+ return NGX_OK;
+}
+
+ngx_int_t
+ngx_postgres_variable_row_count(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_postgres_ctx_t *pgctx;
+
+ dd("entering: \"$postgres_row_count\"");
+
+ pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
+
+ if (pgctx == NULL) {
+ v->not_found = 1;
+ dd("returning NGX_OK");
+ return NGX_OK;
+ }
+
+ v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN);
+ if (v->data == NULL) {
+ dd("returning NGX_ERROR");
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(v->data, "%i", pgctx->var_rows) - v->data;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ dd("returning NGX_OK");
+ return NGX_OK;
+}
+
+ngx_int_t
+ngx_postgres_variable_value(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_postgres_ctx_t *pgctx;
+
+ dd("entering: \"$postgres_value\"");
+
+ pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
+
+ if ((pgctx == NULL) || (pgctx->var_value.len == 0)) {
+ v->not_found = 1;
+ dd("returning NGX_OK");
+ return NGX_OK;
+ }
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->len = pgctx->var_value.len;
+ v->data = pgctx->var_value.data;
+
+ dd("returning NGX_OK");
+ return NGX_OK;
+}
+
+ngx_int_t
+ngx_postgres_variable_get_custom(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_postgres_variable_t *pgvar = (ngx_postgres_variable_t *) data;
+ ngx_postgres_ctx_t *pgctx;
+ ngx_str_t *store;
+
+ dd("entering: \"$%.*s\"", (int) pgvar->var->name.len,
+ pgvar->var->name.data);
+
+ pgctx = ngx_http_get_module_ctx(r, ngx_postgres_module);
+
+ if ((pgctx == NULL) || (pgctx->variables == NULL)) {
+ v->not_found = 1;
+ dd("returning NGX_OK");
+ return NGX_OK;
+ }
+
+ store = pgctx->variables->elts;
+
+ /* idx is always valid */
+ if (store[pgvar->idx].len == 0) {
+ v->not_found = 1;
+ dd("returning NGX_OK");
+ return NGX_OK;
+ }
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->len = store[pgvar->idx].len;
+ v->data = store[pgvar->idx].data;
+
+ dd("returning NGX_OK");
+ return NGX_OK;
+}
+
+ngx_str_t
+ngx_postgres_variable_set_custom(ngx_http_request_t *r, PGresult *res,
+ ngx_postgres_variable_t *pgvar)
+{
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_postgres_value_t *pgv;
+ ngx_int_t col_count, row_count, len;
+ ngx_str_t value = ngx_null_string;
+
+ dd("entering: \"$%.*s\"", (int) pgvar->var->name.len,
+ pgvar->var->name.data);
+
+ col_count = PQnfields(res);
+ row_count = PQntuples(res);
+
+ pgv = &pgvar->value;
+
+ if ((pgv->row >= row_count) || (pgv->column >= col_count)) {
+ if (pgv->required) {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "postgres: \"postgres_set\" for variable \"$%V\""
+ " requires value out of range of the received"
+ " result-set (rows:%d cols:%d) in location \"%V\"",
+ &pgvar->var->name, row_count, col_count, &clcf->name);
+ }
+
+ dd("returning empty value");
+ return value;
+ }
+
+ if (PQgetisnull(res, pgv->row, pgv->column)) {
+ if (pgv->required) {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "postgres: \"postgres_set\" for variable \"$%V\""
+ " requires non-NULL value in location \"%V\"",
+ &pgvar->var->name, &clcf->name);
+ }
+
+ dd("returning empty value");
+ return value;
+ }
+
+ len = PQgetlength(res, pgv->row, pgv->column);
+ if (len == 0) {
+ if (pgv->required) {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "postgres: \"postgres_set\" for variable \"$%V\""
+ " requires non-zero length value in location \"%V\"",
+ &pgvar->var->name, &clcf->name);
+ }
+
+ dd("returning empty value");
+ return value;
+ }
+
+ value.data = ngx_pnalloc(r->pool, len);
+ if (value.data == NULL) {
+ dd("returning empty value");
+ return value;
+ }
+
+ ngx_copy(value.data, PQgetvalue(res, pgv->row, pgv->column), len);
+ value.len = len;
+
+ dd("returning non-empty value");
+ return value;
+}
View
48 src/ngx_postgres_variable.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010, FRiCKLE Piotr Sikora <info@frickle.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _NGX_POSTGRES_VARIABLE_H_
+#define _NGX_POSTGRES_VARIABLE_H_
+
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <libpq-fe.h>
+
+#include "ngx_postgres_module.h"
+
+
+ngx_int_t ngx_postgres_variable_column_count(ngx_http_request_t *,
+ ngx_http_variable_value_t *, uintptr_t);
+ngx_int_t ngx_postgres_variable_row_count(ngx_http_request_t *,
+ ngx_http_variable_value_t *, uintptr_t);
+ngx_int_t ngx_postgres_variable_value(ngx_http_request_t *,
+ ngx_http_variable_value_t *, uintptr_t);
+ngx_int_t ngx_postgres_variable_get_custom(ngx_http_request_t *,
+ ngx_http_variable_value_t *, uintptr_t);
+ngx_str_t ngx_postgres_variable_set_custom(ngx_http_request_t *r,
+ PGresult *, ngx_postgres_variable_t *);
+
+#endif /* _NGX_POSTGRES_VARIABLE_H_ */
View
286 test/t/variables.t
@@ -0,0 +1,286 @@
+# vi:filetype=perl
+
+use lib 'lib';
+use Test::Nginx::Socket;
+
+repeat_each(2);
+
+plan tests => repeat_each() * (blocks() * 3 + 2 * 1 - 3 * 2);
+
+worker_connections(128);
+run_tests();
+
+no_diff();
+
+__DATA__
+
+=== TEST 1: sanity
+little-endian systems only
+
+--- http_config
+ upstream database {
+ postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
+ }
+--- config
+ location /postgres {
+ postgres_pass database;
+ postgres_query "select 'test' as echo";
+ postgres_set $test 0 0;
+ add_header "X-Test" $test;
+ }
+--- request
+GET /postgres
+--- error_code: 200
+--- response_headers
+Content-Type: application/x-resty-dbd-stream
+X-Test: test
+--- response_body eval
+"\x{00}". # endian
+"\x{03}\x{00}\x{00}\x{00}". # format version 0.0.3
+"\x{00}". # result type
+"\x{00}\x{00}". # std errcode
+"\x{02}\x{00}". # driver errcode
+"\x{00}\x{00}". # driver errstr len
+"". # driver errstr data
+"\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}". # rows affected
+"\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}". # insert id
+"\x{01}\x{00}". # col count
+"\x{00}\x{80}". # std col type (unknown/str)
+"\x{c1}\x{02}". # driver col type
+"\x{04}\x{00}". # col name len
+"echo". # col name data
+"\x{01}". # valid row flag
+"\x{04}\x{00}\x{00}\x{00}". # field len
+"test". # field data
+"\x{00}" # row list terminator
+--- timeout: 10
+
+
+
+=== TEST 2: out-of-range value (optional)
+little-endian systems only
+
+--- http_config
+ upstream database {
+ postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
+ }
+--- config
+ location /postgres {
+ postgres_pass database;
+ postgres_query "select 'test' as echo";
+ postgres_set $test 0 1;
+ add_header "X-Test" $test;
+ }
+--- request
+GET /postgres
+--- error_code: 200
+--- response_headers
+Content-Type: application/x-resty-dbd-stream
+X-Test:
+--- timeout: 10
+
+
+
+=== TEST 3: NULL value (optional)
+little-endian systems only
+
+--- http_config
+ upstream database {
+ postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
+ }
+--- config
+ location /postgres {
+ postgres_pass database;
+ postgres_query "select NULL as echo";
+ postgres_set $test 0 0;
+ add_header "X-Test" $test;
+ }
+--- request
+GET /postgres
+--- error_code: 200
+--- response_headers
+Content-Type: application/x-resty-dbd-stream
+X-Test:
+--- timeout: 10
+
+
+
+=== TEST 4: zero-length value (optional)
+little-endian systems only
+
+--- http_config
+ upstream database {
+ postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
+ }
+--- config
+ location /postgres {
+ postgres_pass database;
+ postgres_query "select '' as echo";
+ postgres_set $test 0 0;
+ add_header "X-Test" $test;
+ }
+--- request
+GET /postgres
+--- error_code: 200
+--- response_headers
+Content-Type: application/x-resty-dbd-stream
+X-Test:
+--- timeout: 10
+
+
+
+=== TEST 5: out-of-range value (required)
+little-endian systems only
+
+--- http_config
+ upstream database {
+ postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
+ }
+--- config
+ location /postgres {
+ postgres_pass database;
+ postgres_query "select 'test' as echo";
+ postgres_set $test 0 1 required;
+ add_header "X-Test" $test;
+ }
+--- request
+GET /postgres
+--- error_code: 500
+--- timeout: 10
+
+
+
+=== TEST 6: NULL value (required)
+little-endian systems only
+
+--- http_config
+ upstream database {
+ postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
+ }
+--- config
+ location /postgres {
+ postgres_pass database;
+ postgres_query "select NULL as echo";
+ postgres_set $test 0 0 required;
+ add_header "X-Test" $test;
+ }
+--- request
+GET /postgres
+--- error_code: 500
+--- timeout: 10
+
+
+
+=== TEST 7: zero-length value (required)
+little-endian systems only
+
+--- http_config
+ upstream database {
+ postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
+ }
+--- config
+ location /postgres {
+ postgres_pass database;
+ postgres_query "select '' as echo";
+ postgres_set $test 0 0 required;
+ add_header "X-Test" $test;
+ }
+--- request
+GET /postgres
+--- error_code: 500
+--- timeout: 10
+
+
+
+=== TEST 8: $postgres_column_count
+little-endian systems only
+
+--- http_config
+ upstream database {
+ postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
+ }
+--- config
+ location /postgres {
+ postgres_pass database;
+ postgres_query "select 'a', 'b', 'c'";
+ add_header "X-Columns" $postgres_column_count;
+ }
+--- request
+GET /postgres
+--- error_code: 200
+--- response_headers
+Content-Type: application/x-resty-dbd-stream
+X-Columns: 3
+--- timeout: 10
+
+
+
+=== TEST 9: $postgres_row_count
+little-endian systems only
+
+--- http_config
+ upstream database {
+ postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
+ }
+--- config
+ location /postgres {
+ postgres_pass database;
+ postgres_query "select 'a', 'b', 'c'";
+ add_header "X-Rows" $postgres_row_count;
+ }
+--- request
+GET /postgres
+--- error_code: 200
+--- response_headers
+Content-Type: application/x-resty-dbd-stream
+X-Rows: 1
+--- timeout: 10
+
+
+
+=== TEST 10: $postgres_value (used with get_value)
+little-endian systems only
+
+--- http_config
+ upstream database {
+ postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
+ }
+--- config
+ location /postgres {
+ postgres_pass database;
+ postgres_query "select 'test' as echo";
+ postgres_get_value 0 0;
+ add_header "X-Value" $postgres_value;
+ }
+--- request
+GET /postgres
+--- error_code: 200
+--- response_headers
+Content-Type: text/plain
+X-Value: test
+--- response_body eval
+"test"
+--- timeout: 10
+
+
+
+=== TEST 11: $postgres_value (used without get_value)
+little-endian systems only
+
+--- http_config
+ upstream database {
+ postgres_server 127.0.0.1 dbname=test user=monty password=some_pass;
+ }
+--- config
+ location /postgres {
+ postgres_pass database;
+ postgres_query "select 'test' as echo";
+ add_header "X-Value" $postgres_value;
+ }
+--- request
+GET /postgres
+--- error_code: 200
+--- response_headers
+Content-Type: application/x-resty-dbd-stream
+X-Value:
+--- timeout: 10

0 comments on commit 730aced

Please sign in to comment.
Something went wrong with that request. Please try again.