Permalink
Fetching contributors…
Cannot retrieve contributors at this time
553 lines (399 sloc) 14.9 KB
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_drizzle_processor.h"
#include "ngx_http_drizzle_module.h"
#include "ngx_http_drizzle_util.h"
#include "ngx_http_drizzle_output.h"
#include "ngx_http_drizzle_upstream.h"
#define MYSQL_ER_NO_SUCH_TABLE 1146
static ngx_int_t ngx_http_upstream_drizzle_connect(ngx_http_request_t *r,
ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,
drizzle_con_st *dc);
static ngx_int_t ngx_http_upstream_drizzle_send_query(ngx_http_request_t *r,
ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,
drizzle_con_st *dc);
static ngx_int_t ngx_http_upstream_drizzle_recv_cols(ngx_http_request_t *r,
ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,
drizzle_con_st *dc);
static ngx_int_t ngx_http_upstream_drizzle_recv_rows(ngx_http_request_t *r,
ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,
drizzle_con_st *dc);
ngx_int_t
ngx_http_drizzle_process_events(ngx_http_request_t *r)
{
ngx_http_upstream_t *u;
ngx_connection_t *c;
ngx_http_upstream_drizzle_peer_data_t *dp;
drizzle_con_st *dc;
ngx_int_t rc;
#if 0
drizzle_return_t ret;
#endif
u = r->upstream;
c = u->peer.connection;
dp = u->peer.data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"drizzle process events, state: %d", dp->state);
if (!ngx_http_upstream_drizzle_is_my_peer(&u->peer)) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"process events: it seems you "
"are using a non-drizzle upstream backend"
);
return NGX_ERROR;
}
dc = dp->drizzle_con;
switch (dp->state) {
case state_db_connect:
rc = ngx_http_upstream_drizzle_connect(r, c, dp, dc);
break;
case state_db_idle: /* from connection pool */
c->log->action = "sending query to drizzle upstream";
case state_db_send_query:
rc = ngx_http_upstream_drizzle_send_query(r, c, dp, dc);
break;
case state_db_recv_cols:
rc = ngx_http_upstream_drizzle_recv_cols(r, c, dp, dc);
break;
case state_db_recv_rows:
rc = ngx_http_upstream_drizzle_recv_rows(r, c, dp, dc);
break;
default:
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"unknown state: %d", (int) dp->state);
return NGX_ERROR;
}
dd("rc == %d", (int) rc);
if (rc == NGX_ERROR) {
ngx_http_upstream_drizzle_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
return NGX_ERROR;
}
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
ngx_http_upstream_drizzle_finalize_request(r, u, rc);
return NGX_ERROR;
}
if (rc == NGX_AGAIN) {
#if 0
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
return NGX_ERROR;
}
#endif
rc = ngx_http_drizzle_output_bufs(r, dp);
if (rc == NGX_ERROR || rc > NGX_OK) {
ngx_http_upstream_drizzle_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
}
return rc;
}
static ngx_int_t
ngx_http_upstream_drizzle_connect(ngx_http_request_t *r,
ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,
drizzle_con_st *dc)
{
drizzle_return_t ret;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"drizzle connect: user %s, password %s", dc->user,
dc->password);
ret = drizzle_con_connect(dc);
if (ret == DRIZZLE_RETURN_IO_WAIT) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"drizzle libdrizzle returned IO_WAIT while "
"connecting");
#if 0
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
return NGX_ERROR;
}
#endif
return NGX_AGAIN;
}
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
if (ret != DRIZZLE_RETURN_OK) {
ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to connect: %d: %s",
(int) ret, drizzle_error(dc->drizzle));
return NGX_ERROR;
}
/* ret == DRIZZLE_RETURN_OK */
c->log->action = "sending query to drizzle upstream";
return ngx_http_upstream_drizzle_send_query(r, c, dp, dc);
}
static ngx_int_t
ngx_http_upstream_drizzle_send_query(ngx_http_request_t *r,
ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,
drizzle_con_st *dc)
{
ngx_http_upstream_t *u = r->upstream;
drizzle_return_t ret;
ngx_int_t rc;
ngx_str_t query;
ngx_flag_t has_set_names = 0;
ngx_flag_t enable_charset = 0;
dd("enable charset: %d", (int) dp->enable_charset);
if (dp->enable_charset && ! dp->has_set_names) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"drizzle enables connection charset setting");
query.len = dp->set_names_query->len;
query.data = dp->set_names_query->data;
} else {
query.data = dp->query.data;
query.len = dp->query.len;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"drizzle sending query \"%V\"", &query);
(void) drizzle_query(dc, &dp->drizzle_res, (const char *) query.data,
query.len, &ret);
if (ret == DRIZZLE_RETURN_IO_WAIT) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"drizzle libdrizzle returned IO_WAIT while sending "
"query");
if (dp->state != state_db_send_query) {
dp->state = state_db_send_query;
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
ngx_add_timer(c->write, u->conf->send_timeout);
}
return NGX_AGAIN;
}
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
if (ret != DRIZZLE_RETURN_OK) {
#if 1
if (ret == DRIZZLE_RETURN_ERROR_CODE) {
if (drizzle_error_code(dc->drizzle) == MYSQL_ER_NO_SUCH_TABLE) {
ngx_log_error(NGX_LOG_NOTICE, c->log, 0,
"failed to send query: %i (%d): %s",
ret, drizzle_error_code(dc->drizzle),
drizzle_error(dc->drizzle));
if (dp->enable_charset && ! dp->has_set_names) {
c->log->action = "sending query to drizzle upstream";
dp->has_set_names = 1;
return ngx_http_upstream_drizzle_send_query(r, c, dp, dc);
}
ngx_http_upstream_drizzle_done(r, u, dp, NGX_HTTP_GONE);
return NGX_DONE;
}
}
#endif
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"failed to send query: %d (%d): %s",
(int) ret, drizzle_error_code(dc->drizzle),
drizzle_error(dc->drizzle));
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/* ret == DRIZZLE_RETURN_OK */
dd_drizzle_result(&dp->drizzle_res);
dd("after drizzle restult");
if (dp->enable_charset) {
enable_charset = 1;
}
has_set_names = dp->has_set_names;
rc = ngx_http_drizzle_output_result_header(r, &dp->drizzle_res);
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
if (rc == NGX_DONE) {
if (enable_charset && ! has_set_names) {
c->log->action = "sending query to drizzle upstream";
dp->has_set_names = 1;
dp->state = state_db_idle;
return ngx_http_upstream_drizzle_send_query(r, c, dp, dc);
}
/* no data set following the header */
return rc;
}
c->log->action = "receiving result set columns from drizzle upstream";
return ngx_http_upstream_drizzle_recv_cols(r, c, dp, dc);
}
static ngx_int_t
ngx_http_upstream_drizzle_recv_cols(ngx_http_request_t *r,
ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,
drizzle_con_st *dc)
{
drizzle_column_st *col;
ngx_int_t rc;
drizzle_return_t ret;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"drizzle receive resultset columns");
for (;;) {
col = drizzle_column_read(&dp->drizzle_res, &dp->drizzle_col, &ret);
if (ret == DRIZZLE_RETURN_IO_WAIT) {
if (dp->state != state_db_recv_cols) {
dp->state = state_db_recv_cols;
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
ngx_add_timer(c->read, dp->loc_conf->recv_cols_timeout);
}
#if 0
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_ERROR;
}
#endif
return NGX_AGAIN;
}
if (ret != DRIZZLE_RETURN_OK) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"failed to recv cols: %d: %s",
(int) ret,
drizzle_error(dc->drizzle));
return NGX_ERROR;
}
/* ret == DRIZZLE_RETURN_OK */
if (col) {
rc = ngx_http_drizzle_output_col(r, col);
drizzle_column_free(col);
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
} else { /* after the last column */
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
c->log->action = "receiving result set rows from drizzle "
"upstream";
return ngx_http_upstream_drizzle_recv_rows(r, c, dp, dc);
}
dd_drizzle_column(col);
}
/* impossible to reach here */
}
static ngx_int_t
ngx_http_upstream_drizzle_recv_rows(ngx_http_request_t *r,
ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,
drizzle_con_st *dc)
{
ngx_http_upstream_t *u = r->upstream;
ngx_int_t rc;
drizzle_return_t ret;
size_t offset;
size_t len;
size_t total;
drizzle_field_t field;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"drizzle receive resultset rows");
for (;;) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"drizzle receive resultset row %uL", dp->drizzle_row);
if (dp->drizzle_row == 0) {
dp->drizzle_row = drizzle_row_read(&dp->drizzle_res, &ret);
if (ret == DRIZZLE_RETURN_IO_WAIT) {
dp->drizzle_row = 0;
goto io_wait;
}
if (ret != DRIZZLE_RETURN_OK) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"failed to read row: %d: %s",
(int) ret,
drizzle_error(dc->drizzle));
return NGX_ERROR;
}
/* ret == DRIZZLE_RETURN_OK */
rc = ngx_http_drizzle_output_row(r, dp->drizzle_row);
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
drizzle_result_free(&dp->drizzle_res);
return rc;
}
if (dp->drizzle_row == 0) {
/* after last row */
drizzle_result_free(&dp->drizzle_res);
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (dp->enable_charset && ! dp->has_set_names) {
c->log->action = "sending query to drizzle upstream";
dp->has_set_names = 1;
return ngx_http_upstream_drizzle_send_query(r, c, dp, dc);
}
ngx_http_upstream_drizzle_done(r, u, dp, NGX_DONE);
return NGX_DONE;
}
}
/* dp->drizzle_row != 0 */
for (;;) {
field = drizzle_field_read(&dp->drizzle_res, &offset, &len,
&total, &ret);
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
"drizzle field read: %p (offset %z, len %z)",
field, offset, len);
if (ret == DRIZZLE_RETURN_IO_WAIT) {
goto io_wait;
}
if (ret == DRIZZLE_RETURN_ROW_END) {
/* reached the end of the current row */
break;
}
if (ret != DRIZZLE_RETURN_OK) {
drizzle_result_free(&dp->drizzle_res);
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"failed to read row field: %d: %s",
(int) ret,
drizzle_error(dc->drizzle));
return NGX_ERROR;
}
/* ret == DRIZZLE_RETURN_OK */
rc = ngx_http_drizzle_output_field(r, offset, len, total, field);
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
drizzle_result_free(&dp->drizzle_res);
return rc;
}
if (field) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"drizzle field value read: %*s", len, field);
}
}
dp->drizzle_row = 0;
}
/* impossible to reach here */
io_wait:
if (dp->state != state_db_recv_rows) {
dp->state = state_db_recv_rows;
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
ngx_add_timer(c->read, dp->loc_conf->recv_rows_timeout);
}
#if 0
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_ERROR;
}
#endif
return NGX_AGAIN;
}
void
ngx_http_upstream_drizzle_done(ngx_http_request_t *r,
ngx_http_upstream_t *u, ngx_http_upstream_drizzle_peer_data_t *dp,
ngx_int_t rc)
{
ngx_connection_t *c;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"drizzle upstream done");
(void) ngx_http_drizzle_output_bufs(r, dp);
/* to persuade Maxim Dounin's ngx_http_upstream_keepalive
* module to cache the current connection */
u->length = 0;
if (rc == NGX_DONE) {
u->header_sent = 1;
u->headers_in.status_n = NGX_HTTP_OK;
rc = NGX_OK;
} else {
r->headers_out.status = rc;
u->headers_in.status_n = rc;
}
c = u->peer.connection;
c->log->action = "being idle";
/* reset the state machine */
dp->state = state_db_idle;
dd("about to finalize request...");
ngx_http_upstream_drizzle_finalize_request(r, u, rc);
dd("after finalize request...");
}