kore/examples/pgsql/src/pgsql.c

233 lines
6.4 KiB
C

/*
* Copyright (c) 2014 Joris Vink <joris@coders.se>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This example demonstrates on how to use state machines and
* asynchronous pgsql queries. For a synchronous query example
* see the pgsql-sync/ example under the examples/ directory.
*
* While this example might seem overly complex for a simple pgsql
* query, there is a reason behind its complexity:
* Asynchronous pgsql queries mean that Kore will not block while
* executing the queries, giving a worker time to continue handling
* other events such as I/O or other http requests.
*
* The state machine framework present in Kore makes it trivial
* to get going into dropping from your page handler into the right
* state that you are currently in.
*
* The example connects to a local pgsql database (test) using a table
* called "coders" (which has 2 columns): name, surname.
*/
#include <kore/kore.h>
#include <kore/http.h>
#include <kore/pgsql.h>
#define REQ_STATE_INIT 0
#define REQ_STATE_QUERY 1
#define REQ_STATE_DB_WAIT 2
#define REQ_STATE_DB_READ 3
#define REQ_STATE_ERROR 4
#define REQ_STATE_DONE 5
int init(int);
int page(struct http_request *);
static int request_perform_init(struct http_request *);
static int request_perform_query(struct http_request *);
static int request_db_wait(struct http_request *);
static int request_db_read(struct http_request *);
static int request_error(struct http_request *);
static int request_done(struct http_request *);
struct http_state mystates[] = {
{ "REQ_STATE_INIT", request_perform_init },
{ "REQ_STATE_QUERY", request_perform_query },
{ "REQ_STATE_DB_WAIT", request_db_wait },
{ "REQ_STATE_DB_READ", request_db_read },
{ "REQ_STATE_ERROR", request_error },
{ "REQ_STATE_DONE", request_done },
};
#define mystates_size (sizeof(mystates) / sizeof(mystates[0]))
struct rstate {
int cnt;
struct kore_pgsql sql;
};
/* Called when our module is loaded (see config) */
int
init(int state)
{
/* Register our database. */
kore_pgsql_register("db", "host=/tmp dbname=test");
return (KORE_RESULT_OK);
}
/* Page handler entry point (see config) */
int
page(struct http_request *req)
{
/* Drop into our state machine. */
kore_log(LOG_NOTICE, "page start");
return (http_state_run(mystates, mystates_size, req));
}
/* Initialize our PGSQL data structure and prepare for an async query. */
int
request_perform_init(struct http_request *req)
{
struct rstate *state;
/* Setup our state context (if not yet set). */
if (req->hdlr_extra == NULL) {
state = kore_malloc(sizeof(*state));
req->hdlr_extra = state;
} else {
state = req->hdlr_extra;
}
/* Initialize our kore_pgsql data structure. */
if (!kore_pgsql_query_init(&state->sql, req, "db", KORE_PGSQL_ASYNC)) {
/* If the state was still INIT, we'll try again later. */
if (state->sql.state == KORE_PGSQL_STATE_INIT) {
req->fsm_state = REQ_STATE_INIT;
return (HTTP_STATE_RETRY);
}
kore_pgsql_logerror(&state->sql);
req->fsm_state = REQ_STATE_ERROR;
} else {
req->fsm_state = REQ_STATE_QUERY;
}
return (HTTP_STATE_CONTINUE);
}
/* After setting everything up we will execute our async query. */
int
request_perform_query(struct http_request *req)
{
struct rstate *state = req->hdlr_extra;
/* We want to move to read result after this. */
req->fsm_state = REQ_STATE_DB_WAIT;
/* Fire off the query. */
if (!kore_pgsql_query(&state->sql, "SELECT * FROM coders")) {
/*
* Let the state machine continue immediately since we
* have an error anyway.
*/
return (HTTP_STATE_CONTINUE);
}
/* Resume state machine later when the query results start coming in. */
return (HTTP_STATE_RETRY);
}
/*
* After firing off the query, we returned HTTP_STATE_RETRY (see above).
* When request_db_wait() finally is called by Kore we will have results
* from pgsql so we'll process them.
*/
int
request_db_wait(struct http_request *req)
{
struct rstate *state = req->hdlr_extra;
kore_log(LOG_NOTICE, "request_db_wait: %d", state->sql.state);
/*
* When we get here, our asynchronous pgsql query has
* given us something, check the state to figure out what.
*/
switch (state->sql.state) {
case KORE_PGSQL_STATE_WAIT:
return (HTTP_STATE_RETRY);
case KORE_PGSQL_STATE_COMPLETE:
req->fsm_state = REQ_STATE_DONE;
break;
case KORE_PGSQL_STATE_ERROR:
req->fsm_state = REQ_STATE_ERROR;
kore_pgsql_logerror(&state->sql);
break;
case KORE_PGSQL_STATE_RESULT:
req->fsm_state = REQ_STATE_DB_READ;
break;
default:
/* This MUST be present in order to advance the pgsql state */
kore_pgsql_continue(req, &state->sql);
break;
}
return (HTTP_STATE_CONTINUE);
}
/*
* Called when there's an actual result to be gotten. After we handle the
* entire result, we'll drop back into REQ_STATE_DB_WAIT (above) in order
* to continue until the pgsql API returns KORE_PGSQL_STATE_COMPLETE.
*/
int
request_db_read(struct http_request *req)
{
char *name;
int i, rows;
struct rstate *state = req->hdlr_extra;
/* We have sql data to read! */
rows = kore_pgsql_ntuples(&state->sql);
for (i = 0; i < rows; i++) {
name = kore_pgsql_getvalue(&state->sql, i, 0);
kore_log(LOG_NOTICE, "name: '%s'", name);
}
/* Continue processing our query results. */
kore_pgsql_continue(req, &state->sql);
/* Back to our DB waiting state. */
req->fsm_state = REQ_STATE_DB_WAIT;
return (HTTP_STATE_CONTINUE);
}
/* An error occurred. */
int
request_error(struct http_request *req)
{
struct rstate *state = req->hdlr_extra;
kore_pgsql_cleanup(&state->sql);
http_response(req, 500, NULL, 0);
return (HTTP_STATE_COMPLETE);
}
/* Request was completed successfully. */
int
request_done(struct http_request *req)
{
struct rstate *state = req->hdlr_extra;
kore_pgsql_cleanup(&state->sql);
http_response(req, 200, NULL, 0);
return (HTTP_STATE_COMPLETE);
}