Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'compact'

  • Loading branch information...
commit 3a935bbaf5fea5ea628fa8656153125f3c510b78 2 parents df2370c + e41118e
@agentzh agentzh authored
View
50 README
@@ -36,6 +36,56 @@ Description
This module provides an output filter that can format the RDS outputs
generated by ngx_drizzle and ngx_postgres modules.
+Directives
+ rds_json
+ syntax: rds_json on|off
+ default: rds_json off
+
+ Enables or disables the output filter of this module.
+
+ rds_json_buffer_size
+ syntax: rds_json_buffer_size <bytes>
+ default: rds_json_buffer_size <page-size>
+
+ Controls the buffer size used by this module. default to the page size (4k/8k).
+ The bigger the buffer size, the less streammy the conversion
+ will be. But usually increasing the buffer size
+ does help reduce CPU time.
+
+ rds_json_format
+ syntax: rds_json_format normal|compact
+ default: rds_json_format normal
+
+ Controls the output JSON format. A sample of the default "normal" format
+ looks like this
+
+ [{"id":1,"name":"marry"},{"id":2,"name":"bob"}]
+
+ while it looks like below when in the "compact" format
+
+ [["id","name"],[1,"marry"],[2,"bob"]]
+
+ that is, the first row holds the column name list.
+
+ rds_json_ret
+ syntax: rds_json_ret <error-code> <descrption>
+ default: none
+
+ This directive enables a content handler that simply emits
+ an response body like this:
+
+ {"errcode":<error-code>,"errstr":"<description>"}
+
+ while the <description> string will be properly quoted as
+ a JSON string.
+
+ rds_json_content_type
+ syntax: rds_json_content_type <mime-type>
+ default: rds_json_content_type application/json
+
+ Controls the Content-Type header of the response generated by
+ this module's output filter.
+
Installation
Grab the nginx source code from nginx.net (<http://nginx.net/>), for
example, the version 0.8.36 (see nginx compatibility), and then build
View
5 src/ngx_http_rds_json_filter_module.c
@@ -19,8 +19,9 @@
#define ngx_http_rds_json_content_type "application/json"
static ngx_conf_enum_t ngx_http_rds_json_formats[] = {
+ { ngx_string("normal"), json_format_normal },
{ ngx_string("compact"), json_format_compact },
- { ngx_string("pretty"), json_format_pretty },
+ /* { ngx_string("pretty"), json_format_pretty }, */
{ ngx_null_string, 0 }
};
@@ -332,7 +333,7 @@ ngx_http_rds_json_merge_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->enabled, prev->enabled, 0);
- ngx_conf_merge_uint_value(conf->format, prev->format, json_format_compact);
+ ngx_conf_merge_uint_value(conf->format, prev->format, json_format_normal);
ngx_conf_merge_str_value(conf->content_type, prev->content_type,
ngx_http_rds_json_content_type);
View
7 src/ngx_http_rds_json_filter_module.h
@@ -26,6 +26,7 @@ extern ngx_http_output_body_filter_pt ngx_http_rds_json_next_body_filter;
typedef enum {
+ json_format_normal,
json_format_compact,
json_format_pretty /* TODO */
@@ -83,9 +84,9 @@ typedef struct {
uint32_t field_data_rest;
- ngx_flag_t header_sent;
-
- ngx_flag_t seen_stream_end;
+ ngx_flag_t header_sent:1;
+ ngx_flag_t seen_stream_end:1;
+ ngx_flag_t generated_col_names:1;
} ngx_http_rds_json_ctx_t;
View
199 src/ngx_http_rds_json_output.c
@@ -211,6 +211,73 @@ ngx_http_rds_json_output_header(ngx_http_request_t *r,
ngx_int_t
+ngx_http_rds_json_output_cols(ngx_http_request_t *r,
+ ngx_http_rds_json_ctx_t *ctx)
+{
+ ngx_uint_t i;
+ ngx_http_rds_column_t *col;
+ size_t size;
+ u_char *pos, *last;
+ uintptr_t key_escape = 0;
+
+ size = sizeof("[]") - 1;
+
+ for (i = 0; i < ctx->col_count; i++) {
+ col = &ctx->cols[i];
+ key_escape = ngx_http_rds_json_escape_json_str(NULL,
+ col->name.data, col->name.len);
+
+ dd("key_escape: %d", (int) key_escape);
+
+ size += col->name.len + key_escape
+ + sizeof("\"\"") - 1
+ ;
+
+ if (i != ctx->col_count - 1) {
+ /* not the last column */
+ size += sizeof(",") - 1;
+ }
+ }
+
+ ctx->generated_col_names = 1;
+
+ pos = ngx_http_rds_json_request_mem(r, ctx, size);
+ if (pos == NULL) {
+ return NGX_ERROR;
+ }
+
+ last = pos;
+
+
+ *last++ = '[';
+
+ for (i = 0; i < ctx->col_count; i++) {
+ col = &ctx->cols[i];
+
+ *last++ = '"';
+
+ if (key_escape == 0) {
+ last = ngx_copy(last, col->name.data, col->name.len);
+
+ } else {
+ last = (u_char *) ngx_http_rds_json_escape_json_str(last,
+ col->name.data, col->name.len);
+ }
+
+ *last++ = '"';
+
+ if (i != ctx->col_count - 1) {
+ *last++ = ',';
+ }
+ }
+
+ *last++ = ']';
+
+ return ngx_http_rds_json_submit_mem(r, ctx, size, 0);
+}
+
+
+ngx_int_t
ngx_http_rds_json_output_field(ngx_http_request_t *r,
ngx_http_rds_json_ctx_t *ctx, u_char *data, size_t len,
ngx_flag_t is_null)
@@ -223,6 +290,12 @@ ngx_http_rds_json_output_field(ngx_http_request_t *r,
uintptr_t val_escape = 0;
u_char *p;
ngx_uint_t i;
+ ngx_http_rds_json_conf_t *conf;
+ ngx_uint_t format;
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_rds_json_filter_module);
+
+ format = conf->format;
dd("reading row %llu, col %d, len %d",
(unsigned long long) ctx->row,
@@ -230,28 +303,47 @@ ngx_http_rds_json_output_field(ngx_http_request_t *r,
/* calculate the buffer size */
- if (ctx->cur_col == 0) {
- dd("first column");
- if (ctx->row == 1) {
- dd("first column, first row");
- size = sizeof("{\"") - 1;
+ if (format == json_format_normal) {
+ if (ctx->cur_col == 0) {
+ dd("first column");
+ if (ctx->row == 1) {
+ dd("first column, first row");
+ size = sizeof("{\"") - 1;
+ } else {
+ size = sizeof(",{\"") - 1;
+ }
+ } else {
+ size = sizeof(",\"") - 1;
+ }
+
+ } else if (format == json_format_compact) {
+ if (ctx->cur_col == 0) {
+ dd("first column");
+ size = sizeof(",[") - 1;
} else {
- size = sizeof(",{\"") - 1;
+ size = sizeof(",") - 1;
}
} else {
- size = sizeof(",\"") - 1;
+ return NGX_ERROR;
}
col = &ctx->cols[ctx->cur_col];
- key_escape = ngx_http_rds_json_escape_json_str(NULL, col->name.data,
- col->name.len);
+ if (format == json_format_normal) {
+ key_escape = ngx_http_rds_json_escape_json_str(NULL, col->name.data,
+ col->name.len);
- dd("key_escape: %d", (int) key_escape);
+ dd("key_escape: %d", (int) key_escape);
- size += col->name.len + key_escape
- + sizeof("\":") - 1
- ;
+ size += col->name.len + key_escape
+ + sizeof("\":") - 1
+ ;
+
+ } else if (format == json_format_compact) {
+ /* do nothing */
+ } else {
+ return NGX_ERROR;
+ }
if (len == 0 && ctx->field_data_rest > 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
@@ -344,7 +436,14 @@ ngx_http_rds_json_output_field(ngx_http_request_t *r,
&& ctx->cur_col == ctx->col_count - 1)
{
/* last column in the row */
- size += sizeof("}") - 1;
+ if (format == json_format_normal) {
+ size += sizeof("}") - 1;
+
+ } else if (format == json_format_compact) {
+ size += sizeof("]") - 1;
+ } else {
+ return NGX_ERROR;
+ }
}
/* allocate the buffer */
@@ -358,30 +457,43 @@ ngx_http_rds_json_output_field(ngx_http_request_t *r,
/* fill up the buffer */
- if (ctx->cur_col == 0) {
- dd("first column");
- if (ctx->row == 1) {
- dd("first column, first row");
- *last++ = '{';
+ if (format == json_format_normal) {
+ if (ctx->cur_col == 0) {
+ dd("first column");
+ if (ctx->row == 1) {
+ dd("first column, first row");
+ *last++ = '{';
+ } else {
+ *last++ = ','; *last++ = '{';
+ }
} else {
- *last++ = ','; *last++ = '{';
+ *last++ = ',';
}
- } else {
- *last++ = ',';
- }
- *last++ = '"';
+ *last++ = '"';
+
+ if (key_escape == 0) {
+ last = ngx_copy(last, col->name.data, col->name.len);
+
+ } else {
+ last = (u_char *) ngx_http_rds_json_escape_json_str(last,
+ col->name.data, col->name.len);
+ }
- if (key_escape == 0) {
- last = ngx_copy(last, col->name.data, col->name.len);
+ *last++ = '"'; *last++ = ':';
+
+ } else if (format == json_format_compact) {
+ if (ctx->cur_col == 0) {
+ dd("first column");
+ *last++ = ','; *last++ = '[';
+ } else {
+ *last++ = ',';
+ }
} else {
- last = (u_char *) ngx_http_rds_json_escape_json_str(last,
- col->name.data, col->name.len);
+ return NGX_ERROR;
}
- *last++ = '"'; *last++ = ':';
-
if (is_null) {
dd("copy null value over");
last = ngx_copy_const_str(last, "null");
@@ -443,7 +555,13 @@ ngx_http_rds_json_output_field(ngx_http_request_t *r,
&& ctx->cur_col == ctx->col_count - 1)
{
dd("last column in the row");
- *last++ = '}';
+ if (format == json_format_normal) {
+ *last++ = '}';
+ } else if (format == json_format_compact) {
+ *last++ = ']';
+ } else {
+ return NGX_ERROR;
+ }
}
if ((size_t) (last - pos) != size) {
@@ -468,6 +586,9 @@ ngx_http_rds_json_output_more_field_data(ngx_http_request_t *r,
uintptr_t escape = 0;
u_char *p;
ngx_uint_t i;
+ ngx_http_rds_json_conf_t *conf;
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_rds_json_filter_module);
/* calculate the buffer size */
@@ -511,7 +632,13 @@ ngx_http_rds_json_output_more_field_data(ngx_http_request_t *r,
&& ctx->cur_col == ctx->col_count - 1)
{
/* last column in the row */
- size += sizeof("}") - 1;
+ if (conf->format == json_format_normal) {
+ size += sizeof("}") - 1;
+ } else if (conf->format == json_format_compact) {
+ size += sizeof("]") - 1;
+ } else {
+ return NGX_ERROR;
+ }
}
/* allocate the buffer */
@@ -569,7 +696,13 @@ ngx_http_rds_json_output_more_field_data(ngx_http_request_t *r,
ctx->cur_col == ctx->col_count - 1)
{
/* last column in the row */
- *last++ = '}';
+ if (conf->format == json_format_normal) {
+ *last++ = '}';
+ } else if (conf->format == json_format_compact) {
+ *last++ = ']';
+ } else {
+ return NGX_ERROR;
+ }
}
if ((size_t) (last - pos) != size) {
View
3  src/ngx_http_rds_json_output.h
@@ -18,6 +18,9 @@
ngx_int_t ngx_http_rds_json_output_header(ngx_http_request_t *r,
ngx_http_rds_json_ctx_t *ctx, ngx_http_rds_header_t *header);
+ngx_int_t ngx_http_rds_json_output_cols(ngx_http_request_t *r,
+ ngx_http_rds_json_ctx_t *ctx);
+
ngx_int_t ngx_http_rds_json_output_literal(ngx_http_request_t *r,
ngx_http_rds_json_ctx_t *ctx, u_char *data, size_t len,
ngx_flag_t last_buf);
View
22 src/ngx_http_rds_json_processor.c
@@ -52,10 +52,14 @@ ngx_http_rds_json_process_header(ngx_http_request_t *r,
goto invalid;
}
+ dd("col count: %d", (int) header.col_count);
+
if (header.col_count == 0) {
/* for empty result set, just return the JSON
* representation of the RDS header */
+ dd("col count == 0");
+
if (b->pos != b->last) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"rds_json: header: there's unexpected remaining data in the buf");
@@ -133,6 +137,7 @@ ngx_http_rds_json_process_col(ngx_http_request_t *r,
{
ngx_buf_t *b;
ngx_int_t rc;
+ ngx_http_rds_json_conf_t *conf;
if (in == NULL) {
return NGX_OK;
@@ -193,6 +198,23 @@ ngx_http_rds_json_process_col(ngx_http_request_t *r,
return rc;
}
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_rds_json_filter_module);
+
+ if (conf->format == json_format_compact) {
+ rc = ngx_http_rds_json_output_cols(r, ctx);
+
+ if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ return rc;
+ }
+ }
+
+ dd("after output literal");
+
+ if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ return rc;
+ }
+
+
dd("process col is entering process row...");
return ngx_http_rds_json_process_row(r, in, ctx);
}
View
235 test/t/compact/buf.t
@@ -0,0 +1,235 @@
+# vi:filetype=perl
+
+use lib 'lib';
+use Test::Nginx::Socket;
+
+repeat_each(1);
+
+plan tests => repeat_each() * 2 * blocks();
+
+no_long_string();
+
+run_tests();
+
+#no_diff();
+
+__DATA__
+
+=== TEST 1: rds in a single buf (empty result set)
+--- config
+location = /single {
+ default_type 'application/x-resty-dbd-stream';
+ set_unescape_uri $rds $arg_rds;
+ echo_duplicate 1 $rds;
+ rds_json on;
+ rds_json_format compact;
+}
+--- request eval
+my $rds = "\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{00}\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{02}\x{00}". # col count
+"\x{01}\x{00}". # std col type (bigint/int)
+"\x{03}\x{00}". # drizzle col type
+"\x{02}\x{00}". # col name len
+"id". # col name data
+"\x{13}\x{80}". # std col type (blob/str)
+"\x{fc}\x{00}". # drizzle col type
+"\x{04}\x{00}". # col name len
+"name". # col name data
+"\x{00}"; # row list terminator
+
+use URI::Escape;
+$rds = uri_escape($rds);
+"GET /single?rds=$rds"
+--- response_body chop
+[["id","name"]]
+
+
+
+=== TEST 2: rds in a single buf (non-empty result set)
+--- config
+location = /single {
+ default_type 'application/x-resty-dbd-stream';
+ set_unescape_uri $rds $arg_rds;
+ echo_duplicate 1 $rds;
+ rds_json on;
+ rds_json_format compact;
+}
+--- request eval
+my $rds =
+"\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{00}\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{02}\x{00}". # col count
+"\x{01}\x{00}". # std col type (bigint/int)
+"\x{03}\x{00}". # drizzle col type
+"\x{02}\x{00}". # col name len
+"id". # col name data
+"\x{13}\x{80}". # std col type (blob/str)
+"\x{fc}\x{00}". # drizzle col type
+"\x{04}\x{00}". # col name len
+"name". # col name data
+"\x{01}". # valid row flag
+"\x{01}\x{00}\x{00}\x{00}". # field len
+"2". # field data
+"\x{ff}\x{ff}\x{ff}\x{ff}". # field len
+"". # field data
+"\x{01}". # valid row flag
+"\x{01}\x{00}\x{00}\x{00}". # field len
+"3". # field data
+"\x{03}\x{00}\x{00}\x{00}". # field len
+"bob". # field data
+"\x{00}"; # row list terminator
+
+use URI::Escape;
+$rds = uri_escape($rds);
+"GET /single?rds=$rds"
+--- response_body chop
+[["id","name"],[2,null],[3,"bob"]]
+
+
+
+=== TEST 3: rds in a single buf (non-empty result set, and each row in a single buf)
+--- config
+location = /single {
+ default_type 'application/x-resty-dbd-stream';
+
+ set_unescape_uri $a $arg_a;
+ set_unescape_uri $b $arg_b;
+ set_unescape_uri $c $arg_c;
+ set_unescape_uri $d $arg_d;
+
+ echo_duplicate 1 $a;
+ echo_duplicate 1 $b;
+ echo_duplicate 1 $c;
+ echo_duplicate 1 $d;
+
+ rds_json on;
+ rds_json_format compact;
+}
+--- request eval
+my $a =
+"\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{00}\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{02}\x{00}". # col count
+"\x{01}\x{00}". # std col type (bigint/int)
+"\x{03}\x{00}". # drizzle col type
+"\x{02}\x{00}". # col name len
+"id". # col name data
+"\x{13}\x{80}". # std col type (blob/str)
+"\x{fc}\x{00}". # drizzle col type
+"\x{04}\x{00}". # col name len
+"name"; # col name data
+
+my $b =
+"\x{01}". # valid row flag
+"\x{01}\x{00}\x{00}\x{00}". # field len
+"2". # field data
+"\x{ff}\x{ff}\x{ff}\x{ff}". # field len
+""; # field data
+
+my $c =
+"\x{01}". # valid row flag
+"\x{01}\x{00}\x{00}\x{00}". # field len
+"3". # field data
+"\x{03}\x{00}\x{00}\x{00}". # field len
+"bob"; # field data
+
+my $d =
+"\x{00}"; # row list terminator
+
+use URI::Escape;
+
+$a = uri_escape($a);
+$b = uri_escape($b);
+$c = uri_escape($c);
+$d = uri_escape($d);
+
+"GET /single?a=$a&b=$b&c=$c&d=$d"
+--- response_body chop
+[["id","name"],[2,null],[3,"bob"]]
+
+
+
+=== TEST 4: rds in a single buf (non-empty result set, and each row in a single buf)
+--- config
+location = /single {
+ default_type 'application/x-resty-dbd-stream';
+
+ set_unescape_uri $a $arg_a;
+ set_unescape_uri $b $arg_b;
+ set_unescape_uri $c $arg_c;
+
+ echo -n $a;
+ echo -n $b;
+ echo -n $c;
+
+ rds_json on;
+ rds_json_format compact;
+}
+--- request eval
+my $a =
+"\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{00}\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{02}\x{00}". # col count
+"\x{01}\x{00}". # std col type (bigint/int)
+"\x{03}\x{00}". # drizzle col type
+"\x{02}\x{00}". # col name len
+"id". # col name data
+"\x{13}\x{80}". # std col type (blob/str)
+"\x{fc}\x{00}". # drizzle col type
+"\x{04}\x{00}". # col name len
+"name"; # col name data
+
+my $b =
+"\x{01}". # valid row flag
+"\x{01}\x{00}\x{00}\x{00}". # field len
+"2". # field data
+"\x{ff}\x{ff}\x{ff}\x{ff}". # field len
+""; # field data
+
+my $c =
+"\x{01}". # valid row flag
+"\x{01}\x{00}\x{00}\x{00}". # field len
+"3". # field data
+"\x{03}\x{00}\x{00}\x{00}". # field len
+"bob". # field data
+"\x{00}"; # row list terminator
+
+use URI::Escape;
+
+$a = uri_escape($a);
+$b = uri_escape($b);
+$c = uri_escape($c);
+
+"GET /single?a=$a&b=$b&c=$c"
+--- response_body chop
+[["id","name"],[2,null],[3,"bob"]]
+
View
647 test/t/compact/openresty.t
@@ -0,0 +1,647 @@
+# vi:filetype=perl
+
+use lib 'lib';
+use Test::Nginx::Socket skip_all => 'not working at all (missing data)';
+
+#repeat_each(100);
+repeat_each(2);
+
+worker_connections(128);
+workers(1);
+master_on;
+log_level('warn');
+
+plan tests => repeat_each() * 3 * blocks();
+
+$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;
+
+our $http_config = <<'_EOC_';
+ upstream backend {
+ drizzle_server 127.0.0.1:$TEST_NGINX_MYSQL_PORT protocol=mysql
+ dbname=ngx_test user=ngx_test password=ngx_test;
+ }
+_EOC_
+
+our $config = <<'_EOC_';
+ xss_get on;
+ xss_callback_arg _callback;
+ rds_json on;
+ rds_json_format compact;
+
+ location = /auth {
+ #internal;
+ default_type 'application/json';
+
+ #internal;
+ eval_subrequest_in_memory off;
+ eval $res {
+ set_quote_sql_str $user $arg_user;
+ set $sql 'select count(*) res from users where name=$user';
+ drizzle_query $sql;
+ drizzle_pass backend;
+ #echo $sql;
+ #echo hi;
+ rds_json on;
+ rds_json_content_type application/octet-stream;
+ }
+ #echo $res;
+ if ($res ~ '"res":1') {
+ echo pass;
+ break;
+ }
+ if ($res !~ '"res":1') {
+ return 403;
+ break;
+ }
+ }
+
+ # XXX we should implement these in the ngx_xss module
+ location @err500 { rds_json_ret 500 "Internal Server Error"; }
+ location @err404 { rds_json_ret 404 "Not Found"; }
+ location @err400 { rds_json_ret 400 "Bad Request"; }
+ location @err403 { rds_json_ret 403 "Forbidden"; }
+ location @err502 { rds_json_ret 502 "Bad Gateway"; }
+ location @err503 { rds_json_ret 503 "Service Unavailable"; }
+
+ error_page 500 = @err500;
+ error_page 404 = @err404;
+ error_page 403 = @err403;
+ error_page 400 = @err400;
+ error_page 502 = @err502;
+ error_page 503 = @err503;
+ error_page 504 507 = @err500;
+
+ location = '/=/view/PostsByMonth/~/~' {
+ if ($arg_year !~ '^\d{4}$') {
+ rds_json_ret 400 'Bad "year" argument';
+ }
+ if ($arg_month !~ '^\d{1,2}$') {
+ rds_json_ret 400 'Bad "month" argument';
+ }
+
+ drizzle_query
+"select id, title, day(created) as day
+from posts
+where year(created) = $arg_year and month(created) = $arg_month
+order by created asc";
+
+ drizzle_pass backend;
+ }
+
+ location = '/=/view/RecentComments/~/~' {
+ set $offset $arg_offset;
+ set_if_empty $offset 0;
+
+ set $limit $arg_limit;
+ set_if_empty $limit 10;
+
+ if ($offset !~ '^\d+$') {
+ rds_json_ret 400 'Bad "offset" argument';
+ }
+ if ($limit !~ '^\d{1,2}$') {
+ rds_json_ret 400 'Bad "limit" argument';
+ }
+
+ drizzle_query
+"select comments.id as id, post, sender, title
+from posts, comments
+where post = posts.id
+order by comments.id desc
+limit $offset, $limit";
+
+ drizzle_pass backend;
+ }
+
+ location = '/=/view/RecentPosts/~/~' {
+ set $offset $arg_offset;
+ set_if_empty $offset 0;
+
+ set $limit $arg_limit;
+ set_if_empty $limit 10;
+
+ if ($offset !~ '^\d+$') {
+ rds_json_ret 400 'Bad "offset" argument';
+ }
+ if ($limit !~ '^\d{1,2}$') {
+ rds_json_ret 400 'Bad "limit" argument';
+ }
+
+ drizzle_query
+"select id, title
+from posts
+order by id desc
+limit $offset, $limit";
+
+ drizzle_pass backend;
+ }
+
+ location = '/=/view/PrevNextPost/~/~' {
+ if ($arg_current !~ '^\d+$') {
+ rds_json_ret 400 'Bad "current" argument';
+ }
+
+ drizzle_query
+"(select id, title
+ from posts
+ where id < $arg_current
+ order by id desc
+ limit 1)
+ union
+ (select id, title
+ from posts
+ where id > $arg_current
+ order by id asc
+ limit 1)";
+
+ drizzle_pass backend;
+ }
+
+ location = '/=/view/RowCount/~/~' {
+ if ($arg_model = 'Post') {
+ drizzle_query "select count(*) as count from posts";
+ drizzle_pass backend;
+ }
+ if ($arg_model = 'Comment') {
+ drizzle_query "select count(*) as count from comments";
+ drizzle_pass backend;
+ }
+
+ rds_json_ret 400 'Bad "model" argument';
+ }
+
+ location = '/=/view/PostCountByMonths/~/~' {
+ set $offset $arg_offset;
+ set_if_empty $offset 0;
+
+ set $limit $arg_limit;
+ set_if_empty $limit 10;
+
+ if ($offset !~ '^\d+$') {
+ rds_json_ret 400 'Bad "offset" argument';
+ }
+ if ($limit !~ '^\d{1,2}$') {
+ rds_json_ret 400 'Bad "limit" argument';
+ }
+
+ drizzle_query
+"select date_format(created, '%Y-%m-01') `year_month`, count(*) count
+from posts
+group by `year_month`
+order by `year_month` desc
+limit $offset, $limit";
+ drizzle_pass backend;
+ }
+
+ location = '/=/view/FullPostsByMonth/~/~' {
+ set $count $arg_count;
+ set_if_empty $count 40;
+
+ if ($arg_year !~ '^(?:19|20)\d{2}$') {
+ rds_json_ret 400 'Bad "year" argument';
+ }
+ if ($arg_month !~ '^\d{1,2}$') {
+ rds_json_ret 400 'Bad "month" argument';
+ }
+ if ($arg_count !~ '^\d+$') {
+ rds_json_ret 400 'Bad "count" argument';
+ }
+
+ drizzle_query
+"select * from posts
+where year(created) = $arg_year and month(created) = $arg_month
+order by id desc
+limit $count";
+
+ drizzle_pass backend;
+ }
+
+ location = '/=/view/PrevNextArchive/~/~' {
+ if ($arg_now !~ '^\d{4}-\d{1,2}(?:-\d{1,2})?$') {
+ rds_json_ret 400 'Bad "now" argument';
+ }
+ if ($arg_month !~ '^\d{1,2}$') {
+ rds_json_ret 400 'Bad "month" argument';
+ }
+
+ drizzle_query
+"(select 'next' as id, month(created) as month, year(created) as year
+ from posts
+ where created > $arg_now and month(created) <> $arg_month
+ order by created asc
+ limit 1)
+ union
+ (select 'prev' as id, month(created) as month, year(created) as year
+ from posts
+ where created < $arg_now and month(created) <> $arg_month
+ order by created desc
+ limit 1)";
+
+ drizzle_pass backend;
+ }
+
+ location = '/=/batch/GetSidebar/~/~' {
+ if ($arg_year !~ '^(?:19|20)\d{2}$') {
+ rds_json_ret 400 'Bad "year" argument';
+ }
+ if ($arg_month !~ '^\d{1,2}$') {
+ rds_json_ret 400 'Bad "month" argument';
+ }
+
+ default_type 'application/json';
+ echo '[';
+ echo_location_async '/=/view/PostsByMonth/~/~' "year=$arg_year&month=$arg_month";
+ echo ',';
+ echo_location_async '/=/view/RecentPosts/~/~' "offset=0&limit=6";
+ echo ',';
+ echo_location_async '/=/view/RecentComments/~/~' "offset=0&limit=6";
+ echo ',';
+ echo_location_async '/=/view/PostCountByMonths/~/~' "offset=0&limit=12";
+ echo ']';
+ }
+
+ location = '/=/batch/GetFullPost/~/~' {
+ if ($arg_id !~ '^\d+$') {
+ rds_json_ret 400 'Bad "id" argument';
+ }
+
+ default_type 'application/json';
+ echo '[';
+ echo_location_async "/=/model/Post/id/$arg_id";
+ echo ',';
+ echo_location_async "/=/view/PrevNextPost/~/~" "current=$arg_id";
+ echo ',';
+ echo_location_async "/=/model/Comment/post/$arg_id" "_order_by=id:desc";
+ echo ']';
+ }
+
+ location ~* '^/=/model/Post/id/(.*)$' {
+ set $id $1;
+ if ($id !~ '^\d+$') {
+ rds_json_ret 400 'Bad "id" value';
+ }
+
+ drizzle_query "select * from posts where id = $id";
+ drizzle_pass backend;
+ }
+
+ location ~* '^/=/model/Comment/post/(.*)$' {
+ set $post $1;
+ if ($post !~ '^\d+$') {
+ rds_json_ret 400 'Bad "post" value';
+ }
+
+ drizzle_query "select * from comments where post = $post";
+ drizzle_pass backend;
+ }
+
+ location = '/=/model/Post/~/~' {
+ if ($arg__offset !~ '^\d+$') {
+ rds_json_ret 400 'Bad "_offset" argument';
+ }
+ if ($arg__limit !~ '^\d{1,2}$') {
+ rds_json_ret 400 'Bad "_limit" argument';
+ }
+ if ($arg__order_by !~ '^([A-Za-z]\w*)%3A(desc|asc)$') {
+ rds_json_ret 400 'Bad "_order_by" argument';
+ }
+ set $col $1;
+ set $order $2;
+ drizzle_query
+"select *
+from posts
+order by `$col` $order
+limit $arg__offset, $arg__limit";
+
+ drizzle_pass backend;
+ }
+
+ location = '/=/batch/NewComment/~/~' {
+ default_type 'application/json';
+
+ set_unescape_uri $sender $arg_sender;
+
+ set_unescape_uri $email $arg_email;
+
+ set_unescape_uri $url $arg_url;
+
+ set_unescape_uri $body $arg_body;
+
+ set_unescape_uri $post_id $arg_post_id;
+
+ if ($sender !~ '\S') {
+ rds_json_ret 400 "Bad \"sender\" argument";
+ }
+ if ($email !~ '^[-A-Za-z0-9_.]+@[-A-Za-z0-9_.]+$') {
+ rds_json_ret 400 "Bad \"email\" argument";
+ }
+ if ($url !~ '^(?:\s*|https?://\S+)$') {
+ rds_json_ret 400 "Bad \"url\" argument: $url";
+ }
+ if ($body ~ '^\s*$') {
+ rds_json_ret 400 "Bad \"body\" argument";
+ }
+ if ($post_id !~ '^[1-9]\d*$') {
+ rds_json_ret 400 "Bad \"post_id\" argument";
+ }
+
+ set_quote_sql_str $sender;
+ set_quote_sql_str $email;
+ set_quote_sql_str $url;
+ set_quote_sql_str $body;
+
+ # XXX these operations should be put into a
+ # single transaction
+ echo '[';
+
+ echo_location '/=/action/RunSQL/~/~'
+"insert into comments (sender, email, url, body, post)
+values($sender, $email, $url, $body, $post_id)";
+
+ echo ',';
+
+ echo_location '/=/action/RunSQL/~/~'
+"update posts
+set comments = comments + 1
+where id = $post_id";
+
+ echo ']';
+ }
+
+ location = '/=/action/RunSQL/~/~' {
+ internal;
+ drizzle_query $query_string;
+ drizzle_pass backend;
+ }
+_EOC_
+
+no_long_string();
+
+run_tests();
+
+#no_diff();
+
+__DATA__
+
+=== TEST 1: PostsByMonth view (no month arg)
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/view/PostsByMonth/~/~?_callback=foo
+--- response_headers
+Content-Type: application/x-javascript
+--- response_body chop
+foo({"errcode":400,"errstr":"Bad \"month\" argument"});
+
+
+
+=== TEST 2: PostsByMonth view (bad month)
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/view/PostsByMonth/~/~?month=1234&_callback=foo
+--- response_headers
+Content-Type: application/x-javascript
+--- response_body chop
+foo({"errcode":400,"errstr":"Bad \"month\" argument"});
+
+
+
+=== TEST 3: PostsByMonth view (emtpy result)
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/view/PostsByMonth/~/~?year=1984&month=2&_callback=bar
+--- response_headers
+Content-Type: application/x-javascript
+--- response_body chop
+bar([]);
+
+
+
+=== TEST 4: PostsByMonth view (non-emtpy result)
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/view/PostsByMonth/~/~?year=2009&month=10&_callback=foo
+--- response_headers
+Content-Type: application/x-javascript
+--- response_body chop
+foo([{"id":114,"title":"Hacking on the Nginx echo module","day":15}]);
+
+
+
+=== TEST 5: PostsByMonth view (non-emtpy result)
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/view/PostsByMonth/~/~?year=2009&month=12&_callback=foo
+--- response_headers
+Content-Type: application/x-javascript
+--- response_body chop
+foo([{"id":117,"title":"Major updates to ngx_chunkin: lots of bug fixes and beginning of keep-alive support","day":4},{"id":118,"title":"ngx_memc: an extended version of ngx_memcached that supports set, add, delete, and many more commands","day":6},{"id":119,"title":"Test::Nginx::LWP and Test::Nginx::Socket are now on CPAN","day":8}]);
+--- timeout: 90
+
+
+
+=== TEST 6: GetSideBar
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/batch/GetSidebar/~/~?year=2009&month=10&_callback=foo
+--- response_headers
+Content-Type: application/x-javascript
+--- response_body chop
+foo([
+[{"id":114,"title":"Hacking on the Nginx echo module","day":15}],
+[{"id":119,"title":"Test::Nginx::LWP and Test::Nginx::Socket are now on CPAN"},{"id":118,"title":"ngx_memc: an extended version of ngx_memcached that supports set, add, delete, and many more commands"},{"id":117,"title":"Major updates to ngx_chunkin: lots of bug fixes and beginning of keep-alive support"},{"id":116,"title":"The \"headers more\" module: scripting input and output filters in your Nginx config file"},{"id":115,"title":"The \"chunkin\" module: Experimental chunked input support for Nginx"},{"id":114,"title":"Hacking on the Nginx echo module"}],
+[{"id":179,"post":101,"sender":"agentzh","title":"生活搜基于 Firefox 3.1 的 List Hunter 集群"},{"id":178,"post":101,"sender":"Winter","title":"生活搜基于 Firefox 3.1 的 List Hunter 集群"},{"id":177,"post":100,"sender":"Mountain","title":"漂在北京"},{"id":176,"post":106,"sender":"agentzh","title":"Text::SmartLinks: The Perl 6 love for Perl 5"},{"id":175,"post":106,"sender":"gosber","title":"Text::SmartLinks: The Perl 6 love for Perl 5"},{"id":174,"post":105,"sender":"cnangel","title":"SSH::Batch: Treating clusters as maths sets and intervals"}],
+[{"year_month":"2009-12-01","count":3},{"year_month":"2009-11-01","count":2},{"year_month":"2009-10-01","count":1},{"year_month":"2009-09-01","count":5},{"year_month":"2009-05-01","count":2},{"year_month":"2009-04-01","count":3},{"year_month":"2009-02-01","count":2},{"year_month":"2008-12-01","count":3},{"year_month":"2008-11-01","count":2},{"year_month":"2008-10-01","count":2},{"year_month":"2008-09-01","count":4},{"year_month":"2008-08-01","count":2}]]
+);
+
+
+
+=== TEST 7: GetFullPost
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/batch/GetFullPost/~/~?id=116&_user=agentzh.Public&_callback=OpenResty.callbackMap%5B1264354204389%5D
+--- response_headers
+Content-Type: application/x-javascript
+--- response_body chop
+--- SKIP
+
+
+
+=== TEST 8: RowCount
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/view/RowCount/~/~?model=Post&_callback=foo
+--- response_headers
+Content-Type: application/x-javascript
+--- response_body chop
+foo([{"count":118}]);
+
+
+
+=== TEST 9: GetFullPost bug
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/model/Comment/post/67
+--- response_headers
+Content-Type: application/json
+--- response_body_like: laser
+--- error_code: 200
+
+
+
+=== TEST 10: more field data error
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/model/Post/~/~?_limit=5&_order_by=id%3Adesc&_offset=100
+--- response_headers
+Content-Type: application/json
+--- response_body_like: 测试
+--- error_code: 200
+--- SKIP
+
+
+
+=== TEST 11: default arguments
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/view/RecentComments/~/~
+--- response_headers
+Content-Type: application/json
+--- response_body chop
+[{"id":179,"post":101,"sender":"agentzh","title":"生活搜基于 Firefox 3.1 的 List Hunter 集群"},{"id":178,"post":101,"sender":"Winter","title":"生活搜基于 Firefox 3.1 的 List Hunter 集群"},{"id":177,"post":100,"sender":"Mountain","title":"漂在北京"},{"id":176,"post":106,"sender":"agentzh","title":"Text::SmartLinks: The Perl 6 love for Perl 5"},{"id":175,"post":106,"sender":"gosber","title":"Text::SmartLinks: The Perl 6 love for Perl 5"},{"id":174,"post":105,"sender":"cnangel","title":"SSH::Batch: Treating clusters as maths sets and intervals"},{"id":173,"post":106,"sender":"cnangel","title":"Text::SmartLinks: The Perl 6 love for Perl 5"},{"id":172,"post":104,"sender":"agentzh","title":"My VDOM.pm & WebKit Cluster Talk at the April Meeting of Beijing Perl Workshop"},{"id":171,"post":104,"sender":"kindy","title":"My VDOM.pm & WebKit Cluster Talk at the April Meeting of Beijing Perl Workshop"},{"id":170,"post":104,"sender":"cnangel","title":"My VDOM.pm & WebKit Cluster Talk at the April Meeting of Beijing Perl Workshop"}]
+--- error_code: 200
+--- timeout: 3
+
+
+
+=== TEST 12: post a comment with empty body
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/batch/NewComment/~/~?sender=agentzh&email=agentzh@gmail.com&body=&post_id=3
+--- response_headers
+Content-Type: application/json
+--- response_body chop
+{"errcode":400,"errstr":"Bad \"body\" argument"}
+--- error_code: 200
+
+
+
+=== TEST 13: post a comment with valid gmail
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/batch/NewComment/~/~?sender=agentzh&email=agentzh%40gmail.com&body=hi&post_id=
+--- response_headers
+Content-Type: application/json
+--- response_body chop
+{"errcode":400,"errstr":"Bad \"post_id\" argument"}
+--- error_code: 200
+
+
+
+=== TEST 14: try to run the internal location
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /=/action/RunSQL/~/~?select
+--- response_headers
+Content-Type: application/json
+--- response_body chop
+{"errcode":404,"errstr":"Not Found"}
+--- error_code: 200
+
+
+
+=== TEST 15: auth
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /auth?user=agentzh
+--- response_headers
+Content-Type: application/json
+--- response_body
+pass
+--- error_code: 200
+--- SKIP
+
+
+
+=== TEST 16: auth
+
+init db:
+
+create table users (
+ id serial primary key,
+ name text not null,
+ password text not null
+);
+
+insert into users
+ (name, password)
+ values ('agentzh', 'some_pass');
+
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /auth?user=john
+--- response_headers
+Content-Type: application/json
+--- response_body chop
+{"errcode":403,"errstr":"Forbidden"}
+--- error_code: 200
+--- SKIP
+
+
+
+=== TEST 17: auth
+
+db init:
+
+create table users (
+ id serial primary key,
+ name text not null,
+ password text not null
+);
+
+insert into users
+ (name, password)
+ values ('agentzh', 'some_pass');
+
+
+--- http_config eval: $::http_config
+--- config eval: $::config
+--- request
+GET /test
+--- response_headers
+Content-Type: application/json
+--- response_body
+pass
+--- error_code: 200
+--- SKIP
+
+
+
+=== TEST 18: default arguments (small pagesize)
+--- http_config eval: $::http_config
+--- config eval
+"rds_json_buffer_size 1;
+$::config"
+--- request
+GET /=/view/RecentComments/~/~
+--- response_headers
+Content-Type: application/json
+--- response_body chop
+[{"id":179,"post":101,"sender":"agentzh","title":"生活搜基于 Firefox 3.1 的 List Hunter 集群"},{"id":178,"post":101,"sender":"Winter","title":"生活搜基于 Firefox 3.1 的 List Hunter 集群"},{"id":177,"post":100,"sender":"Mountain","title":"漂在北京"},{"id":176,"post":106,"sender":"agentzh","title":"Text::SmartLinks: The Perl 6 love for Perl 5"},{"id":175,"post":106,"sender":"gosber","title":"Text::SmartLinks: The Perl 6 love for Perl 5"},{"id":174,"post":105,"sender":"cnangel","title":"SSH::Batch: Treating clusters as maths sets and intervals"},{"id":173,"post":106,"sender":"cnangel","title":"Text::SmartLinks: The Perl 6 love for Perl 5"},{"id":172,"post":104,"sender":"agentzh","title":"My VDOM.pm & WebKit Cluster Talk at the April Meeting of Beijing Perl Workshop"},{"id":171,"post":104,"sender":"kindy","title":"My VDOM.pm & WebKit Cluster Talk at the April Meeting of Beijing Perl Workshop"},{"id":170,"post":104,"sender":"cnangel","title":"My VDOM.pm & WebKit Cluster Talk at the April Meeting of Beijing Perl Workshop"}]
+--- error_code: 200
+--- timeout: 3
+
View
401 test/t/compact/sanity.t
@@ -0,0 +1,401 @@
+# vi:filetype=perl
+
+use lib 'lib';
+use Test::Nginx::Socket;
+
+#repeat_each(10);
+no_shuffle();
+
+repeat_each(1);
+
+plan tests => repeat_each() * 2 * blocks() + 2 * repeat_each() * 3;
+
+$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;
+
+our $http_config = <<'_EOC_';
+ upstream backend {
+ drizzle_server 127.0.0.1:$TEST_NGINX_MYSQL_PORT protocol=mysql
+ dbname=ngx_test user=ngx_test password=ngx_test;
+ }
+_EOC_
+
+#no_long_string();
+
+run_tests();
+
+#no_diff();
+
+__DATA__
+
+=== TEST 1: sanity
+--- http_config eval: $::http_config
+--- config
+ location /mysql {
+ drizzle_pass backend;
+ #drizzle_dbname $dbname;
+ drizzle_query 'select * from cats';
+ rds_json on;
+ rds_json_format compact;
+ }
+--- request
+GET /mysql
+--- response_headers_like
+X-Resty-DBD-Module: ngx_drizzle \d+\.\d+\.\d+
+Content-Type: application/json
+--- response_body chomp
+[["id","name"],[2,null],[3,"bob"]]
+--- timeout: 15
+
+
+
+=== TEST 2: keep-alive
+--- http_config eval: $::http_config
+--- config
+ location /mysql {
+ drizzle_pass backend;
+ #drizzle_dbname $dbname;
+ drizzle_query 'select * from cats';
+ rds_json on;
+ rds_json_format compact;
+ }
+--- request
+GET /mysql
+--- response_body chop
+[["id","name"],[2,null],[3,"bob"]]
+
+
+
+=== TEST 3: update
+--- http_config eval: $::http_config
+--- config
+ location /mysql {
+ drizzle_pass backend;
+ #drizzle_dbname $dbname;
+ drizzle_query "update cats set name='bob' where name='bob'";
+ rds_json on;
+ rds_json_format compact;
+ }
+--- request
+GET /mysql
+--- response_body chop
+{"errcode":0,"errstr":"Rows matched: 1 Changed: 0 Warnings: 0"}
+
+
+
+=== TEST 4: select empty result
+--- http_config eval: $::http_config
+--- config
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_query "select * from cats where name='tom'";
+ rds_json on;
+ rds_json_format compact;
+ }
+--- request
+GET /mysql
+--- response_body chop
+[["id","name"]]
+
+
+
+=== TEST 5: update & no module header
+--- http_config eval: $::http_config
+--- config
+ location /mysql {
+ if ($arg_name ~ '[^A-Za-z0-9]') {
+ return 400;
+ }
+
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query "update cats set name='$arg_name' where name='$arg_name'";
+
+ rds_json on;
+ rds_json_format compact;
+ }
+--- request
+GET /mysql?name=bob
+--- response_headers
+X-Resty-DBD-Module:
+Content-Type: application/json
+--- response_body chop
+{"errcode":0,"errstr":"Rows matched: 1 Changed: 0 Warnings: 0"}
+
+
+
+=== TEST 6: invalid SQL
+--- http_config eval: $::http_config
+--- config
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query "select '32";
+ rds_json on;
+ rds_json_format compact;
+ }
+--- response_headers
+X-Resty-DBD-Module:
+Content-Type: text/html
+--- request
+GET /mysql
+--- error_code: 500
+--- response_body_like: 500 Internal Server Error
+
+
+
+=== TEST 7: single row, single col
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists singles";
+ echo;
+ echo_location /mysql "create table singles (name varchar(15));";
+ echo;
+ echo_location /mysql "insert into singles values ('marry');";
+ echo;
+ echo_location /mysql "select * from singles;";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"affected_rows":1}
+[["name"],["marry"]]
+--- skip_nginx: 2: < 0.7.46
+--- timeout: 5
+
+
+
+=== TEST 8: floating number and insert id
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial not null, primary key (id), val real);";
+ echo;
+ echo_location /mysql "insert into foo (val) values (3.1415926);";
+ echo;
+ echo_location /mysql "select * from foo;";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+[["id","val"],[1,3.1415926]]
+--- skip_nginx: 2: < 0.7.46
+
+
+
+=== TEST 9: text blob field
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial, body text);";
+ echo;
+ echo_location /mysql "insert into foo (body) values ('hello');";
+ echo;
+ echo_location /mysql "select * from foo;";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+[["id","body"],[1,"hello"]]
+--- skip_nginx: 2: < 0.7.46
+
+
+
+=== TEST 10: bool blob field
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial, flag bool);";
+ echo;
+ echo_location /mysql "insert into foo (flag) values (true);";
+ echo;
+ echo_location /mysql "insert into foo (flag) values (false);";
+ echo;
+ echo_location /mysql "select * from foo order by id;";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+{"errcode":0,"insert_id":2,"affected_rows":1}
+[["id","flag"],[1,1],[2,0]]
+--- skip_nginx: 2: < 0.7.46
+--- timeout: 10
+
+
+
+=== TEST 11: bit field
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial, flag bit);";
+ echo;
+ echo_location /mysql "insert into foo (flag) values (1);";
+ echo;
+ echo_location /mysql "insert into foo (flag) values (0);";
+ echo;
+ echo_location /mysql "select * from foo order by id;";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+{"errcode":0,"insert_id":2,"affected_rows":1}
+[["id","flag"],[1,"\u0001"],[2,"\u0000"]]
+--- skip_nginx: 2: < 0.7.46
+--- timeout: 10
+
+
+
+=== TEST 12: date type
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial, created date);";
+ echo;
+ echo_location /mysql "insert into foo (created) values ('2007-05-24');";
+ echo;
+ echo_location /mysql "select * from foo";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+[["id","created"],[1,"2007-05-24"]]
+
+
+
+=== TEST 13: strings need to be escaped
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial, body char(25));";
+ echo;
+ echo_location /mysql "insert into foo (body) values ('a\\r\\nb\\bhello\Z');";
+ echo;
+ echo_location /mysql "select * from foo";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+[["id","body"],[1,"a\r\nb\bhello\u001a"]]
+
+
+
+=== TEST 14: null values
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial, name char(10), age integer);";
+ echo;
+ echo_location /mysql "insert into foo (name, age) values ('', null);";
+ echo;
+ echo_location /mysql "insert into foo (name, age) values (null, 0);";
+ echo;
+ echo_location /mysql "select * from foo order by id";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+{"errcode":0,"insert_id":2,"affected_rows":1}
+[["id","name","age"],[1,"",null],[2,null,0]]
+--- timeout: 10
+
View
415 test/t/sanity-stream.t
@@ -0,0 +1,415 @@
+# vi:filetype=perl
+
+use lib 'lib';
+use Test::Nginx::Socket;
+
+#repeat_each(10);
+no_shuffle();
+
+repeat_each(1);
+
+plan tests => repeat_each() * 2 * blocks() + 2 * repeat_each() * 3;
+
+$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;
+
+our $http_config = <<'_EOC_';
+ upstream backend {
+ drizzle_server 127.0.0.1:$TEST_NGINX_MYSQL_PORT protocol=mysql
+ dbname=ngx_test user=ngx_test password=ngx_test;
+ }
+_EOC_
+
+#no_long_string();
+
+run_tests();
+
+#no_diff();
+
+__DATA__
+
+=== TEST 1: sanity
+--- http_config eval: $::http_config
+--- config
+ location /mysql {
+ drizzle_pass backend;
+ #drizzle_dbname $dbname;
+ drizzle_query 'select * from cats';
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- request
+GET /mysql
+--- response_headers_like
+X-Resty-DBD-Module: ngx_drizzle \d+\.\d+\.\d+
+Content-Type: application/json
+--- response_body chomp
+[["id","name"],[2,null],[3,"bob"]]
+--- timeout: 15
+
+
+
+=== TEST 2: keep-alive
+--- http_config eval: $::http_config
+--- config
+ location /mysql {
+ drizzle_pass backend;
+ #drizzle_dbname $dbname;
+ drizzle_query 'select * from cats';
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- request
+GET /mysql
+--- response_body chop
+[["id","name"],[2,null],[3,"bob"]]
+
+
+
+=== TEST 3: update
+--- http_config eval: $::http_config
+--- config
+ location /mysql {
+ drizzle_pass backend;
+ #drizzle_dbname $dbname;
+ drizzle_query "update cats set name='bob' where name='bob'";
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- request
+GET /mysql
+--- response_body chop
+{"errcode":0,"errstr":"Rows matched: 1 Changed: 0 Warnings: 0"}
+
+
+
+=== TEST 4: select empty result
+--- http_config eval: $::http_config
+--- config
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_query "select * from cats where name='tom'";
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- request
+GET /mysql
+--- response_body chop
+[["id","name"]]
+
+
+
+=== TEST 5: update & no module header
+--- http_config eval: $::http_config
+--- config
+ location /mysql {
+ if ($arg_name ~ '[^A-Za-z0-9]') {
+ return 400;
+ }
+
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query "update cats set name='$arg_name' where name='$arg_name'";
+
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- request
+GET /mysql?name=bob
+--- response_headers
+X-Resty-DBD-Module:
+Content-Type: application/json
+--- response_body chop
+{"errcode":0,"errstr":"Rows matched: 1 Changed: 0 Warnings: 0"}
+
+
+
+=== TEST 6: invalid SQL
+--- http_config eval: $::http_config
+--- config
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query "select '32";
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- response_headers
+X-Resty-DBD-Module:
+Content-Type: text/html
+--- request
+GET /mysql
+--- error_code: 500
+--- response_body_like: 500 Internal Server Error
+
+
+
+=== TEST 7: single row, single col
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists singles";
+ echo;
+ echo_location /mysql "create table singles (name varchar(15));";
+ echo;
+ echo_location /mysql "insert into singles values ('marry');";
+ echo;
+ echo_location /mysql "select * from singles;";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"affected_rows":1}
+[["name"],["marry"]]
+--- skip_nginx: 2: < 0.7.46
+--- timeout: 5
+
+
+
+=== TEST 8: floating number and insert id
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial not null, primary key (id), val real);";
+ echo;
+ echo_location /mysql "insert into foo (val) values (3.1415926);";
+ echo;
+ echo_location /mysql "select * from foo;";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+[["id","val"],[1,3.1415926]]
+--- skip_nginx: 2: < 0.7.46
+
+
+
+=== TEST 9: text blob field
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial, body text);";
+ echo;
+ echo_location /mysql "insert into foo (body) values ('hello');";
+ echo;
+ echo_location /mysql "select * from foo;";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+[["id","body"],[1,"hello"]]
+--- skip_nginx: 2: < 0.7.46
+
+
+
+=== TEST 10: bool blob field
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial, flag bool);";
+ echo;
+ echo_location /mysql "insert into foo (flag) values (true);";
+ echo;
+ echo_location /mysql "insert into foo (flag) values (false);";
+ echo;
+ echo_location /mysql "select * from foo order by id;";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+{"errcode":0,"insert_id":2,"affected_rows":1}
+[["id","flag"],[1,1],[2,0]]
+--- skip_nginx: 2: < 0.7.46
+--- timeout: 10
+
+
+
+=== TEST 11: bit field
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial, flag bit);";
+ echo;
+ echo_location /mysql "insert into foo (flag) values (1);";
+ echo;
+ echo_location /mysql "insert into foo (flag) values (0);";
+ echo;
+ echo_location /mysql "select * from foo order by id;";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+{"errcode":0,"insert_id":2,"affected_rows":1}
+[["id","flag"],[1,"\u0001"],[2,"\u0000"]]
+--- skip_nginx: 2: < 0.7.46
+--- timeout: 10
+
+
+
+=== TEST 12: date type
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial, created date);";
+ echo;
+ echo_location /mysql "insert into foo (created) values ('2007-05-24');";
+ echo;
+ echo_location /mysql "select * from foo";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+[["id","created"],[1,"2007-05-24"]]
+
+
+
+=== TEST 13: strings need to be escaped
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial, body char(25));";
+ echo;
+ echo_location /mysql "insert into foo (body) values ('a\\r\\nb\\bhello\Z');";
+ echo;
+ echo_location /mysql "select * from foo";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+[["id","body"],[1,"a\r\nb\bhello\u001a"]]
+
+
+
+=== TEST 14: null values
+--- http_config eval: $::http_config
+--- config
+ location /test {
+ echo_location /mysql "drop table if exists foo";
+ echo;
+ echo_location /mysql "create table foo (id serial, name char(10), age integer);";
+ echo;
+ echo_location /mysql "insert into foo (name, age) values ('', null);";
+ echo;
+ echo_location /mysql "insert into foo (name, age) values (null, 0);";
+ echo;
+ echo_location /mysql "select * from foo order by id";
+ echo;
+ }
+ location /mysql {
+ drizzle_pass backend;
+ drizzle_module_header off;
+ drizzle_query $query_string;
+ rds_json on;
+ rds_json_format compact;
+ drizzle_buffer_size 1;
+ }
+--- request
+GET /test
+--- response_body
+{"errcode":0}
+{"errcode":0}
+{"errcode":0,"insert_id":1,"affected_rows":1}
+{"errcode":0,"insert_id":2,"affected_rows":1}
+[["id","name","age"],[1,"",null],[2,null,0]]
+--- timeout: 10
+
Please sign in to comment.
Something went wrong with that request. Please try again.