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 */