149750b05782dc80311278f495fc994ad1b9cf1f
1 /*
2 * SysDB - src/frontend/query.c
3 * Copyright (C) 2013-2014 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 #include "sysdb.h"
30 #include "core/store.h"
31 #include "frontend/connection-private.h"
32 #include "parser/ast.h"
33 #include "parser/parser.h"
34 #include "utils/error.h"
35 #include "utils/proto.h"
36 #include "utils/strbuf.h"
38 #include <errno.h>
39 #include <string.h>
41 /*
42 * private helper functions
43 */
45 static int
46 list_tojson(sdb_store_obj_t *obj,
47 sdb_store_matcher_t __attribute__((unused)) *filter,
48 void *user_data)
49 {
50 sdb_store_json_formatter_t *f = user_data;
51 return sdb_store_json_emit(f, obj);
52 } /* list_tojson */
54 static int
55 lookup_tojson(sdb_store_obj_t *obj, sdb_store_matcher_t *filter,
56 void *user_data)
57 {
58 sdb_store_json_formatter_t *f = user_data;
59 return sdb_store_json_emit_full(f, obj, filter);
60 } /* lookup_tojson */
62 /*
63 * public API
64 */
66 int
67 sdb_fe_query(sdb_conn_t *conn)
68 {
69 sdb_llist_t *parsetree;
70 sdb_ast_node_t *ast = NULL;
72 sdb_store_matcher_t *q;
73 sdb_strbuf_t *buf = NULL;
74 int status = 0;
76 if ((! conn) || (conn->cmd != SDB_CONNECTION_QUERY))
77 return -1;
79 parsetree = sdb_parser_parse(sdb_strbuf_string(conn->buf),
80 (int)conn->cmd_len, conn->errbuf);
81 if (! parsetree) {
82 char query[conn->cmd_len + 1];
83 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
84 query[sizeof(query) - 1] = '\0';
85 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse query '%s': %s",
86 query, sdb_strbuf_string(conn->errbuf));
87 return -1;
88 }
90 switch (sdb_llist_len(parsetree)) {
91 case 0:
92 /* skipping empty command; send back an empty reply */
93 sdb_connection_send(conn, SDB_CONNECTION_DATA, 0, NULL);
94 break;
95 case 1:
96 ast = SDB_AST_NODE(sdb_llist_get(parsetree, 0));
97 break;
99 default:
100 {
101 char query[conn->cmd_len + 1];
102 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
103 query[sizeof(query) - 1] = '\0';
104 sdb_log(SDB_LOG_WARNING, "frontend: Ignoring %zu command%s "
105 "in multi-statement query '%s'",
106 sdb_llist_len(parsetree) - 1,
107 sdb_llist_len(parsetree) == 2 ? "" : "s",
108 query);
109 ast = SDB_AST_NODE(sdb_llist_get(parsetree, 0));
110 }
111 }
113 q = sdb_store_query_prepare(ast);
114 if (! q) {
115 /* this shouldn't happen */
116 sdb_strbuf_sprintf(conn->errbuf, "failed to compile AST");
117 sdb_log(SDB_LOG_ERR, "frontend: failed to compile AST");
118 status = -1;
119 } else {
120 buf = sdb_strbuf_create(1024);
121 if (! buf) {
122 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
123 sdb_object_deref(SDB_OBJ(q));
124 return -1;
125 }
126 status = sdb_store_query_execute(q, buf, conn->errbuf);
127 if (status < 0) {
128 char query[conn->cmd_len + 1];
129 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
130 query[sizeof(query) - 1] = '\0';
131 sdb_log(SDB_LOG_ERR, "frontend: failed to execute query '%s'", query);
132 }
133 }
134 sdb_object_deref(SDB_OBJ(ast));
135 sdb_llist_destroy(parsetree);
137 if (status < 0) {
138 sdb_object_deref(SDB_OBJ(q));
139 sdb_strbuf_destroy(buf);
140 return status;
141 }
143 assert(buf);
144 sdb_connection_send(conn, status,
145 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
147 sdb_strbuf_destroy(buf);
148 sdb_object_deref(SDB_OBJ(q));
149 return 0;
150 } /* sdb_fe_query */
152 int
153 sdb_fe_fetch(sdb_conn_t *conn)
154 {
155 char name[conn->cmd_len + 1];
156 uint32_t type;
158 if ((! conn) || (conn->cmd != SDB_CONNECTION_FETCH))
159 return -1;
161 if (conn->cmd_len < sizeof(uint32_t)) {
162 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
163 "FETCH command", conn->cmd_len);
164 sdb_strbuf_sprintf(conn->errbuf, "FETCH: Invalid command length %d",
165 conn->cmd_len);
166 return -1;
167 }
169 sdb_proto_unmarshal_int32(SDB_STRBUF_STR(conn->buf), &type);
170 strncpy(name, sdb_strbuf_string(conn->buf) + sizeof(uint32_t),
171 conn->cmd_len - sizeof(uint32_t));
172 name[sizeof(name) - 1] = '\0';
173 /* TODO: support other types besides hosts */
174 return sdb_fe_exec_fetch(conn, (int)type, name, NULL, /* filter = */ NULL);
175 } /* sdb_fe_fetch */
177 int
178 sdb_fe_list(sdb_conn_t *conn)
179 {
180 uint32_t type = SDB_HOST;
182 if ((! conn) || (conn->cmd != SDB_CONNECTION_LIST))
183 return -1;
185 if (conn->cmd_len == sizeof(uint32_t))
186 sdb_proto_unmarshal_int32(SDB_STRBUF_STR(conn->buf), &type);
187 else if (conn->cmd_len) {
188 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
189 "LIST command", conn->cmd_len);
190 sdb_strbuf_sprintf(conn->errbuf, "LIST: Invalid command length %d",
191 conn->cmd_len);
192 return -1;
193 }
194 return sdb_fe_exec_list(conn, (int)type, /* filter = */ NULL);
195 } /* sdb_fe_list */
197 int
198 sdb_fe_lookup(sdb_conn_t *conn)
199 {
200 sdb_store_matcher_t *m;
201 const char *matcher;
202 size_t matcher_len;
204 uint32_t type;
205 int status;
207 conn_matcher_t m_node = {
208 { SDB_OBJECT_INIT, SDB_CONNECTION_MATCHER }, NULL
209 };
210 conn_lookup_t node = {
211 { SDB_OBJECT_INIT, SDB_CONNECTION_LOOKUP },
212 -1, &m_node, NULL
213 };
215 if ((! conn) || (conn->cmd != SDB_CONNECTION_LOOKUP))
216 return -1;
218 if (conn->cmd_len < sizeof(uint32_t)) {
219 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
220 "LOOKUP command", conn->cmd_len);
221 sdb_strbuf_sprintf(conn->errbuf, "LOOKUP: Invalid command length %d",
222 conn->cmd_len);
223 return -1;
224 }
225 sdb_proto_unmarshal_int32(SDB_STRBUF_STR(conn->buf), &type);
227 matcher = sdb_strbuf_string(conn->buf) + sizeof(uint32_t);
228 matcher_len = conn->cmd_len - sizeof(uint32_t);
229 m = sdb_fe_parse_matcher(matcher, (int)matcher_len, conn->errbuf);
230 if (! m) {
231 char expr[matcher_len + 1];
232 strncpy(expr, matcher, sizeof(expr));
233 expr[sizeof(expr) - 1] = '\0';
234 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse "
235 "lookup condition '%s': %s", expr,
236 sdb_strbuf_string(conn->errbuf));
237 return -1;
238 }
240 node.type = (int)type;
241 m_node.matcher = m;
243 /* run analyzer separately; parse_matcher is missing
244 * the right context to do so */
245 if (sdb_fe_analyze(SDB_CONN_NODE(&node), conn->errbuf)) {
246 char expr[matcher_len + 1];
247 char err[sdb_strbuf_len(conn->errbuf) + sizeof(expr) + 64];
248 strncpy(expr, matcher, sizeof(expr));
249 expr[sizeof(expr) - 1] = '\0';
250 snprintf(err, sizeof(err), "Failed to parse "
251 "lookup condition '%s': %s", expr,
252 sdb_strbuf_string(conn->errbuf));
253 sdb_strbuf_sprintf(conn->errbuf, "%s", err);
254 status = -1;
255 }
256 else
257 status = sdb_fe_exec_lookup(conn, (int)type, m, /* filter = */ NULL);
258 sdb_object_deref(SDB_OBJ(m));
259 return status;
260 } /* sdb_fe_lookup */
262 /*
263 * TODO: let the functions above build an AST; then drop sdb_fe_exec_*.
264 */
266 int
267 sdb_fe_exec_fetch(sdb_conn_t *conn, int type,
268 const char *hostname, const char *name, sdb_store_matcher_t *filter)
269 {
270 uint32_t res_type = htonl(SDB_CONNECTION_FETCH);
272 sdb_store_obj_t *host;
273 sdb_store_obj_t *obj;
275 sdb_store_json_formatter_t *f;
276 sdb_strbuf_t *buf;
278 if ((! hostname) || ((type == SDB_HOST) && name)
279 || ((type != SDB_HOST) && (! name))) {
280 /* This is a programming error, not something the client did wrong */
281 sdb_strbuf_sprintf(conn->errbuf, "INTERNAL ERROR: invalid "
282 "arguments to sdb_fe_exec_fetch(%s, %s, %s)",
283 SDB_STORE_TYPE_TO_NAME(type), hostname, name);
284 return -1;
285 }
286 if (type == SDB_HOST)
287 name = hostname;
289 host = sdb_store_get_host(hostname);
290 if ((! host) || (filter
291 && (! sdb_store_matcher_matches(filter, host, NULL)))) {
292 sdb_strbuf_sprintf(conn->errbuf, "Failed to fetch %s %s: "
293 "host %s not found", SDB_STORE_TYPE_TO_NAME(type),
294 name, hostname);
295 sdb_object_deref(SDB_OBJ(host));
296 return -1;
297 }
298 if (type == SDB_HOST) {
299 obj = host;
300 }
301 else {
302 obj = sdb_store_get_child(host, type, name);
303 if ((! obj) || (filter
304 && (! sdb_store_matcher_matches(filter, obj, NULL)))) {
305 sdb_strbuf_sprintf(conn->errbuf, "Failed to fetch %s %s.%s: "
306 "%s not found", SDB_STORE_TYPE_TO_NAME(type),
307 hostname, name, name);
308 if (obj)
309 sdb_object_deref(SDB_OBJ(obj));
310 sdb_object_deref(SDB_OBJ(host));
311 return -1;
312 }
313 sdb_object_deref(SDB_OBJ(host));
314 }
315 host = NULL;
317 buf = sdb_strbuf_create(1024);
318 if (! buf) {
319 char errbuf[1024];
320 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
321 "buffer to handle FETCH command: %s",
322 sdb_strerror(errno, errbuf, sizeof(errbuf)));
324 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
325 sdb_strbuf_destroy(buf);
326 sdb_object_deref(SDB_OBJ(obj));
327 return -1;
328 }
329 f = sdb_store_json_formatter(buf, type, /* flags = */ 0);
330 if (! f) {
331 char errbuf[1024];
332 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
333 "JSON formatter to handle FETCH command: %s",
334 sdb_strerror(errno, errbuf, sizeof(errbuf)));
336 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
337 sdb_strbuf_destroy(buf);
338 sdb_object_deref(SDB_OBJ(obj));
339 return -1;
340 }
342 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
343 if (sdb_store_json_emit_full(f, obj, filter)) {
344 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
345 "%s %s.%s to JSON", SDB_STORE_TYPE_TO_NAME(type),
346 hostname, name);
347 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
348 sdb_strbuf_destroy(buf);
349 free(f);
350 sdb_object_deref(SDB_OBJ(obj));
351 return -1;
352 }
353 sdb_store_json_finish(f);
355 sdb_connection_send(conn, SDB_CONNECTION_DATA,
356 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
357 sdb_strbuf_destroy(buf);
358 free(f);
359 sdb_object_deref(SDB_OBJ(obj));
360 return 0;
361 } /* sdb_fe_exec_fetch */
363 int
364 sdb_fe_exec_list(sdb_conn_t *conn, int type, sdb_store_matcher_t *filter)
365 {
366 uint32_t res_type = htonl(SDB_CONNECTION_LIST);
368 sdb_store_json_formatter_t *f;
369 sdb_strbuf_t *buf;
371 buf = sdb_strbuf_create(1024);
372 if (! buf) {
373 char errbuf[1024];
374 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
375 "buffer to handle LIST command: %s",
376 sdb_strerror(errno, errbuf, sizeof(errbuf)));
378 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
379 sdb_strbuf_destroy(buf);
380 return -1;
381 }
382 f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
383 if (! f) {
384 char errbuf[1024];
385 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
386 "JSON formatter to handle LIST command: %s",
387 sdb_strerror(errno, errbuf, sizeof(errbuf)));
389 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
390 sdb_strbuf_destroy(buf);
391 return -1;
392 }
394 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
395 if (sdb_store_scan(type, /* m = */ NULL, filter, list_tojson, f)) {
396 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
397 "store to JSON");
398 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
399 sdb_strbuf_destroy(buf);
400 free(f);
401 return -1;
402 }
403 sdb_store_json_finish(f);
405 sdb_connection_send(conn, SDB_CONNECTION_DATA,
406 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
407 sdb_strbuf_destroy(buf);
408 free(f);
409 return 0;
410 } /* sdb_fe_exec_list */
412 int
413 sdb_fe_exec_lookup(sdb_conn_t *conn, int type,
414 sdb_store_matcher_t *m, sdb_store_matcher_t *filter)
415 {
416 uint32_t res_type = htonl(SDB_CONNECTION_LOOKUP);
418 sdb_store_json_formatter_t *f;
419 sdb_strbuf_t *buf;
421 buf = sdb_strbuf_create(1024);
422 if (! buf) {
423 char errbuf[1024];
424 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
425 "buffer to handle LOOKUP command: %s",
426 sdb_strerror(errno, errbuf, sizeof(errbuf)));
428 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
429 return -1;
430 }
431 f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
432 if (! f) {
433 char errbuf[1024];
434 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
435 "JSON formatter to handle LOOKUP command: %s",
436 sdb_strerror(errno, errbuf, sizeof(errbuf)));
438 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
439 sdb_strbuf_destroy(buf);
440 return -1;
441 }
443 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
445 if (sdb_store_scan(type, m, filter, lookup_tojson, f)) {
446 sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup %ss",
447 SDB_STORE_TYPE_TO_NAME(type));
448 sdb_strbuf_sprintf(conn->errbuf, "Failed to lookup %ss",
449 SDB_STORE_TYPE_TO_NAME(type));
450 sdb_strbuf_destroy(buf);
451 free(f);
452 return -1;
453 }
454 sdb_store_json_finish(f);
456 sdb_connection_send(conn, SDB_CONNECTION_DATA,
457 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
458 sdb_strbuf_destroy(buf);
459 free(f);
460 return 0;
461 } /* sdb_fe_exec_lookup */
463 int
464 sdb_fe_exec_timeseries(sdb_conn_t *conn,
465 const char *hostname, const char *metric,
466 sdb_timeseries_opts_t *opts)
467 {
468 sdb_strbuf_t *buf;
469 uint32_t res_type = htonl(SDB_CONNECTION_TIMESERIES);
471 buf = sdb_strbuf_create(1024);
472 if (! buf) {
473 char errbuf[1024];
474 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
475 "buffer to handle TIMESERIES command: %s",
476 sdb_strerror(errno, errbuf, sizeof(errbuf)));
478 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
479 return -1;
480 }
482 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
483 if (sdb_store_fetch_timeseries(hostname, metric, opts, buf)) {
484 sdb_log(SDB_LOG_ERR, "frontend: Failed to fetch time-series");
485 sdb_strbuf_sprintf(conn->errbuf, "Failed to fetch time-series");
486 sdb_strbuf_destroy(buf);
487 return -1;
488 }
490 sdb_connection_send(conn, SDB_CONNECTION_DATA,
491 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
492 sdb_strbuf_destroy(buf);
493 return 0;
494 } /* sdb_fe_exec_timeseries */
496 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */