kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

pgsql.c (6853B)



      1 /*
      2  * Copyright (c) 2014-2018 Joris Vink <joris@coders.se>
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 /*
     18  * This example demonstrates on how to use state machines and
     19  * asynchronous pgsql queries. For a synchronous query example
     20  * see the pgsql-sync/ example under the examples/ directory.
     21  *
     22  * While this example might seem overly complex for a simple pgsql
     23  * query, there is a reason behind its complexity:
     24  *	Asynchronous pgsql queries mean that Kore will not block while
     25  *	executing the queries, giving a worker time to continue handling
     26  *	other events such as I/O or other http requests.
     27  *
     28  * The state machine framework present in Kore makes it trivial
     29  * to get going into dropping from your page handler into the right
     30  * state that you are currently in.
     31  */
     32 
     33 #if !defined(KORE_NO_HTTP)
     34 
     35 #include <kore/kore.h>
     36 #include <kore/http.h>
     37 #include <kore/pgsql.h>
     38 
     39 #define REQ_STATE_INIT			0
     40 #define REQ_STATE_QUERY			1
     41 #define REQ_STATE_DB_WAIT		2
     42 #define REQ_STATE_DB_READ		3
     43 #define REQ_STATE_ERROR			4
     44 #define REQ_STATE_DONE			5
     45 
     46 int		page(struct http_request *);
     47 
     48 static int	request_perform_init(struct http_request *);
     49 static int	request_perform_query(struct http_request *);
     50 static int	request_db_wait(struct http_request *);
     51 static int	request_db_read(struct http_request *);
     52 static int	request_error(struct http_request *);
     53 static int	request_done(struct http_request *);
     54 
     55 struct http_state	mystates[] = {
     56 	{ "REQ_STATE_INIT",		request_perform_init },
     57 	{ "REQ_STATE_QUERY",		request_perform_query },
     58 	{ "REQ_STATE_DB_WAIT",		request_db_wait },
     59 	{ "REQ_STATE_DB_READ",		request_db_read },
     60 	{ "REQ_STATE_ERROR",		request_error },
     61 	{ "REQ_STATE_DONE",		request_done },
     62 };
     63 
     64 #define mystates_size		(sizeof(mystates) / sizeof(mystates[0]))
     65 
     66 struct rstate {
     67 	int			cnt;
     68 	struct kore_pgsql	sql;
     69 };
     70 
     71 /* Page handler entry point (see config) */
     72 int
     73 page(struct http_request *req)
     74 {
     75 	/* Drop into our state machine. */
     76 	kore_log(LOG_NOTICE, "%p: page start", (void *)req);
     77 	return (http_state_run(mystates, mystates_size, req));
     78 }
     79 
     80 /* Initialize our PGSQL data structure and prepare for an async query. */
     81 int
     82 request_perform_init(struct http_request *req)
     83 {
     84 	struct rstate	*state;
     85 
     86 	/* Setup our state context (if not yet set). */
     87 	if (!http_state_exists(req)) {
     88 		state = http_state_create(req, sizeof(*state));
     89 
     90 		/*
     91 		 * Initialize the kore_pgsql data structure and bind it
     92 		 * to this request so we can be put to sleep / woken up
     93 		 * by the pgsql layer when required.
     94 		 */
     95 		kore_pgsql_init(&state->sql);
     96 		kore_pgsql_bind_request(&state->sql, req);
     97 	} else {
     98 		state = http_state_get(req);
     99 	}
    100 
    101 	/*
    102 	 * Setup the query to be asynchronous in nature, aka just fire it
    103 	 * off and return back to us.
    104 	 */
    105 	if (!kore_pgsql_setup(&state->sql, "db", KORE_PGSQL_ASYNC)) {
    106 		/*
    107 		 * If the state was still in INIT we need to go to sleep and
    108 		 * wait until the pgsql layer wakes us up again when there
    109 		 * an available connection to the database.
    110 		 */
    111 		if (state->sql.state == KORE_PGSQL_STATE_INIT) {
    112 			req->fsm_state = REQ_STATE_INIT;
    113 			return (HTTP_STATE_RETRY);
    114 		}
    115 
    116 		kore_pgsql_logerror(&state->sql);
    117 		req->fsm_state = REQ_STATE_ERROR;
    118 	} else {
    119 		/*
    120 		 * The initial setup was complete, go for query.
    121 		 */
    122 		req->fsm_state = REQ_STATE_QUERY;
    123 	}
    124 
    125 	return (HTTP_STATE_CONTINUE);
    126 }
    127 
    128 /* After setting everything up we will execute our async query. */
    129 int
    130 request_perform_query(struct http_request *req)
    131 {
    132 	struct rstate	*state = http_state_get(req);
    133 
    134 	/* We want to move to read result after this. */
    135 	req->fsm_state = REQ_STATE_DB_WAIT;
    136 
    137 	/* Fire off the query. */
    138 	if (!kore_pgsql_query(&state->sql,
    139 	    "SELECT * FROM coders, pg_sleep(5)")) {
    140 		/*
    141 		 * Let the state machine continue immediately since we
    142 		 * have an error anyway.
    143 		 */
    144 		return (HTTP_STATE_CONTINUE);
    145 	}
    146 
    147 	/* Resume state machine later when the query results start coming in. */
    148 	return (HTTP_STATE_RETRY);
    149 }
    150 
    151 /*
    152  * After firing off the query, we returned HTTP_STATE_RETRY (see above).
    153  * When request_db_wait() finally is called by Kore we will have results
    154  * from pgsql so we'll process them.
    155  */
    156 int
    157 request_db_wait(struct http_request *req)
    158 {
    159 	struct rstate	*state = http_state_get(req);
    160 
    161 	kore_log(LOG_NOTICE, "request_db_wait: %d", state->sql.state);
    162 
    163 	/*
    164 	 * When we get here, our asynchronous pgsql query has
    165 	 * given us something, check the state to figure out what.
    166 	 */
    167 	switch (state->sql.state) {
    168 	case KORE_PGSQL_STATE_WAIT:
    169 		return (HTTP_STATE_RETRY);
    170 	case KORE_PGSQL_STATE_COMPLETE:
    171 		req->fsm_state = REQ_STATE_DONE;
    172 		break;
    173 	case KORE_PGSQL_STATE_ERROR:
    174 		req->fsm_state = REQ_STATE_ERROR;
    175 		kore_pgsql_logerror(&state->sql);
    176 		break;
    177 	case KORE_PGSQL_STATE_RESULT:
    178 		req->fsm_state = REQ_STATE_DB_READ;
    179 		break;
    180 	default:
    181 		/* This MUST be present in order to advance the pgsql state */
    182 		kore_pgsql_continue(&state->sql);
    183 		break;
    184 	}
    185 
    186 	return (HTTP_STATE_CONTINUE);
    187 }
    188 
    189 /*
    190  * Called when there's an actual result to be gotten. After we handle the
    191  * entire result, we'll drop back into REQ_STATE_DB_WAIT (above) in order
    192  * to continue until the pgsql API returns KORE_PGSQL_STATE_COMPLETE.
    193  */
    194 int
    195 request_db_read(struct http_request *req)
    196 {
    197 	char		*name;
    198 	int		i, rows;
    199 	struct rstate	*state = http_state_get(req);
    200 
    201 	/* We have sql data to read! */
    202 	rows = kore_pgsql_ntuples(&state->sql);
    203 	for (i = 0; i < rows; i++) {
    204 		name = kore_pgsql_getvalue(&state->sql, i, 0);
    205 		kore_log(LOG_NOTICE, "name: '%s'", name);
    206 	}
    207 
    208 	/* Continue processing our query results. */
    209 	kore_pgsql_continue(&state->sql);
    210 
    211 	/* Back to our DB waiting state. */
    212 	req->fsm_state = REQ_STATE_DB_WAIT;
    213 	return (HTTP_STATE_CONTINUE);
    214 }
    215 
    216 /* An error occurred. */
    217 int
    218 request_error(struct http_request *req)
    219 {
    220 	struct rstate	*state = http_state_get(req);
    221 
    222 	kore_pgsql_cleanup(&state->sql);
    223 	http_state_cleanup(req);
    224 
    225 	http_response(req, 500, NULL, 0);
    226 
    227 	return (HTTP_STATE_COMPLETE);
    228 }
    229 
    230 /* Request was completed successfully. */
    231 int
    232 request_done(struct http_request *req)
    233 {
    234 	struct rstate	*state = http_state_get(req);
    235 
    236 	kore_pgsql_cleanup(&state->sql);
    237 	http_state_cleanup(req);
    238 
    239 	http_response(req, 200, NULL, 0);
    240 
    241 	return (HTTP_STATE_COMPLETE);
    242 }
    243 
    244 #endif /* !KORE_NO_HTTP */