Code

store: Let JSON formatter know about the to-be-formatted object type.
[sysdb.git] / src / frontend / query.c
1 /*
2  * SysDB - src/frontend/query.c
3  * Copyright (C) 2013 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 "frontend/parser.h"
33 #include "utils/error.h"
34 #include "utils/proto.h"
35 #include "utils/strbuf.h"
37 #include <errno.h>
38 #include <string.h>
40 /*
41  * private helper functions
42  */
44 static int
45 lookup_tojson(sdb_store_obj_t *obj, sdb_store_matcher_t *filter,
46                 void *user_data)
47 {
48         sdb_store_json_formatter_t *f = user_data;
49         return sdb_store_json_emit_full(f, obj, filter);
50 } /* lookup_tojson */
52 /*
53  * public API
54  */
56 int
57 sdb_fe_query(sdb_conn_t *conn)
58 {
59         sdb_llist_t *parsetree;
60         sdb_conn_node_t *node = NULL;
61         int status = 0;
63         if ((! conn) || (conn->cmd != CONNECTION_QUERY))
64                 return -1;
66         parsetree = sdb_fe_parse(sdb_strbuf_string(conn->buf),
67                         (int)conn->cmd_len);
68         if (! parsetree) {
69                 char query[conn->cmd_len + 1];
70                 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
71                 query[sizeof(query) - 1] = '\0';
72                 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse query '%s'",
73                                 query);
74                 return -1;
75         }
77         switch (sdb_llist_len(parsetree)) {
78                 case 0:
79                         /* skipping empty command; send back an empty reply */
80                         sdb_connection_send(conn, CONNECTION_DATA, 0, NULL);
81                         break;
82                 case 1:
83                         node = SDB_CONN_NODE(sdb_llist_get(parsetree, 0));
84                         break;
86                 default:
87                         {
88                                 char query[conn->cmd_len + 1];
89                                 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
90                                 query[sizeof(query) - 1] = '\0';
91                                 sdb_log(SDB_LOG_WARNING, "frontend: Ignoring %zu command%s "
92                                                 "in multi-statement query '%s'",
93                                                 sdb_llist_len(parsetree) - 1,
94                                                 sdb_llist_len(parsetree) == 2 ? "" : "s",
95                                                 query);
96                                 node = SDB_CONN_NODE(sdb_llist_get(parsetree, 0));
97                         }
98         }
100         if (node) {
101                 if (sdb_fe_analyze(node)) {
102                         char query[conn->cmd_len + 1];
103                         strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
104                         query[sizeof(query) - 1] = '\0';
105                         sdb_log(SDB_LOG_ERR, "frontend: Failed to verify query '%s'",
106                                         query);
107                         status = -1;
108                 }
109                 else
110                         status = sdb_fe_exec(conn, node);
111                 sdb_object_deref(SDB_OBJ(node));
112         }
114         sdb_llist_destroy(parsetree);
115         return status;
116 } /* sdb_fe_query */
118 int
119 sdb_fe_fetch(sdb_conn_t *conn)
121         char name[conn->cmd_len + 1];
122         int type;
124         if ((! conn) || (conn->cmd != CONNECTION_FETCH))
125                 return -1;
127         if (conn->cmd_len < sizeof(type)) {
128                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
129                                 "FETCH command", conn->cmd_len);
130                 sdb_strbuf_sprintf(conn->errbuf, "FETCH: Invalid command length %d",
131                                 conn->cmd_len);
132                 return -1;
133         }
135         type = sdb_proto_get_int(conn->buf, 0);
136         strncpy(name, sdb_strbuf_string(conn->buf) + sizeof(type),
137                         conn->cmd_len - sizeof(type));
138         name[sizeof(name) - 1] = '\0';
139         return sdb_fe_exec_fetch(conn, type, name, /* filter = */ NULL);
140 } /* sdb_fe_fetch */
142 int
143 sdb_fe_list(sdb_conn_t *conn)
145         int type = SDB_HOST;
147         if ((! conn) || (conn->cmd != CONNECTION_LIST))
148                 return -1;
150         if (conn->cmd_len == sizeof(uint32_t))
151                 type = sdb_proto_get_int(conn->buf, 0);
152         else if (conn->cmd_len) {
153                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
154                                 "LIST command", conn->cmd_len);
155                 sdb_strbuf_sprintf(conn->errbuf, "LIST: Invalid command length %d",
156                                 conn->cmd_len);
157                 return -1;
158         }
159         return sdb_fe_exec_list(conn, type, /* filter = */ NULL);
160 } /* sdb_fe_list */
162 int
163 sdb_fe_lookup(sdb_conn_t *conn)
165         sdb_store_matcher_t *m;
166         const char *matcher;
167         size_t matcher_len;
169         uint32_t type;
170         int status;
172         conn_matcher_t m_node = {
173                 { SDB_OBJECT_INIT, CONNECTION_MATCHER }, NULL
174         };
175         conn_lookup_t node = {
176                 { SDB_OBJECT_INIT, CONNECTION_LOOKUP },
177                 -1, &m_node, NULL
178         };
180         if ((! conn) || (conn->cmd != CONNECTION_LOOKUP))
181                 return -1;
183         if (conn->cmd_len < sizeof(type)) {
184                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
185                                 "LOOKUP command", conn->cmd_len);
186                 sdb_strbuf_sprintf(conn->errbuf, "LOOKUP: Invalid command length %d",
187                                 conn->cmd_len);
188                 return -1;
189         }
190         type = sdb_proto_get_int(conn->buf, 0);
192         matcher = sdb_strbuf_string(conn->buf) + sizeof(type);
193         matcher_len = conn->cmd_len - sizeof(type);
194         m = sdb_fe_parse_matcher(matcher, (int)matcher_len);
195         if (! m) {
196                 char expr[matcher_len + 1];
197                 strncpy(expr, matcher, sizeof(expr));
198                 expr[sizeof(expr) - 1] = '\0';
199                 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse "
200                                 "lookup condition '%s'", expr);
201                 return -1;
202         }
204         node.type = type;
205         m_node.matcher = m;
207         if (sdb_fe_analyze(SDB_CONN_NODE(&node))) {
208                 char expr[matcher_len + 1];
209                 strncpy(expr, matcher, sizeof(expr));
210                 expr[sizeof(expr) - 1] = '\0';
211                 sdb_log(SDB_LOG_ERR, "frontend: Failed to verify "
212                                 "lookup condition '%s'", expr);
213                 status = -1;
214         }
215         else
216                 status = sdb_fe_exec_lookup(conn, type, m, /* filter = */ NULL);
217         sdb_object_deref(SDB_OBJ(m));
218         return status;
219 } /* sdb_fe_lookup */
221 int
222 sdb_fe_exec(sdb_conn_t *conn, sdb_conn_node_t *node)
224         sdb_store_matcher_t *m = NULL, *filter = NULL;
226         if (! node)
227                 return -1;
229         switch (node->cmd) {
230                 case CONNECTION_FETCH:
231                         if (CONN_FETCH(node)->filter)
232                                 filter = CONN_FETCH(node)->filter->matcher;
233                         return sdb_fe_exec_fetch(conn, CONN_FETCH(node)->type,
234                                         CONN_FETCH(node)->name, filter);
235                 case CONNECTION_LIST:
236                         if (CONN_LIST(node)->filter)
237                                 filter = CONN_LIST(node)->filter->matcher;
238                         return sdb_fe_exec_list(conn, CONN_LIST(node)->type, filter);
239                 case CONNECTION_LOOKUP:
240                         if (CONN_LOOKUP(node)->matcher)
241                                 m = CONN_LOOKUP(node)->matcher->matcher;
242                         if (CONN_LOOKUP(node)->filter)
243                                 filter = CONN_LOOKUP(node)->filter->matcher;
244                         return sdb_fe_exec_lookup(conn,
245                                         CONN_LOOKUP(node)->type, m, filter);
246                 case CONNECTION_TIMESERIES:
247                         return sdb_fe_exec_timeseries(conn,
248                                         CONN_TS(node)->hostname, CONN_TS(node)->metric,
249                                         &CONN_TS(node)->opts);
251                 default:
252                         sdb_log(SDB_LOG_ERR, "frontend: Unknown command %i", node->cmd);
253                         return -1;
254         }
255         return -1;
256 } /* sdb_fe_exec */
258 int
259 sdb_fe_exec_fetch(sdb_conn_t *conn, int type, const char *name,
260                 sdb_store_matcher_t *filter)
262         sdb_strbuf_t *buf;
263         sdb_store_obj_t *host;
264         uint32_t res_type = htonl(CONNECTION_FETCH);
266         /* XXX: support other types */
267         if (type != SDB_HOST) {
268                 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
269                                 "in FETCH command", type);
270                 sdb_strbuf_sprintf(conn->errbuf,
271                                 "FETCH: Invalid object type %d", type);
272                 return -1;
273         }
275         host = sdb_store_get_host(name);
276         if (! host) {
277                 sdb_log(SDB_LOG_DEBUG, "frontend: Failed to fetch host '%s': "
278                                 "not found", name);
280                 sdb_strbuf_sprintf(conn->errbuf, "Host %s not found", name);
281                 return -1;
282         }
284         buf = sdb_strbuf_create(1024);
285         if (! buf) {
286                 char errbuf[1024];
287                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
288                                 "buffer to handle FETCH command: %s",
289                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
291                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
292                 sdb_strbuf_destroy(buf);
293                 sdb_object_deref(SDB_OBJ(host));
294                 return -1;
295         }
297         sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
298         if (sdb_store_host_tojson(host, buf, filter, /* flags = */ 0)) {
299                 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
300                                 "host '%s' to JSON", name);
301                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
302                 sdb_strbuf_destroy(buf);
303                 sdb_object_deref(SDB_OBJ(host));
304                 return -1;
305         }
307         sdb_connection_send(conn, CONNECTION_DATA,
308                         (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
309         sdb_strbuf_destroy(buf);
310         sdb_object_deref(SDB_OBJ(host));
311         return 0;
312 } /* sdb_fe_exec_fetch */
314 int
315 sdb_fe_exec_list(sdb_conn_t *conn, int type, sdb_store_matcher_t *filter)
317         sdb_strbuf_t *buf;
318         uint32_t res_type = htonl(CONNECTION_LIST);
320         int flags;
322         if (type == SDB_HOST)
323                 flags = SDB_SKIP_ALL;
324         else if (type == SDB_SERVICE)
325                 flags = (SDB_SKIP_ALL & (~SDB_SKIP_SERVICES))
326                         | SDB_SKIP_EMPTY_SERVICES;
327         else if (type == SDB_METRIC)
328                 flags = (SDB_SKIP_ALL & (~SDB_SKIP_METRICS))
329                         | SDB_SKIP_EMPTY_METRICS;
330         else {
331                 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
332                                 "for LIST command", type);
333                 sdb_strbuf_sprintf(conn->errbuf,
334                                 "LIST: Invalid object type %d", type);
335                 return -1;
336         }
338         buf = sdb_strbuf_create(1024);
339         if (! buf) {
340                 char errbuf[1024];
341                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
342                                 "buffer to handle LIST command: %s",
343                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
345                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
346                 sdb_strbuf_destroy(buf);
347                 return -1;
348         }
350         sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
351         if (sdb_store_tojson(buf, filter, flags)) {
352                 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
353                                 "store to JSON");
354                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
355                 sdb_strbuf_destroy(buf);
356                 return -1;
357         }
359         sdb_connection_send(conn, CONNECTION_DATA,
360                         (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
361         sdb_strbuf_destroy(buf);
362         return 0;
363 } /* sdb_fe_exec_list */
365 int
366 sdb_fe_exec_lookup(sdb_conn_t *conn, int type,
367                 sdb_store_matcher_t *m, sdb_store_matcher_t *filter)
369         uint32_t res_type = htonl(CONNECTION_LOOKUP);
371         sdb_store_json_formatter_t *f;
372         sdb_strbuf_t *buf;
374         /* XXX: support other types */
375         if (type != SDB_HOST) {
376                 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
377                                 "in LOOKUP command", type);
378                 sdb_strbuf_sprintf(conn->errbuf,
379                                 "LOOKUP: Invalid object type %d", type);
380                 return -1;
381         }
383         buf = sdb_strbuf_create(1024);
384         if (! buf) {
385                 char errbuf[1024];
386                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
387                                 "buffer to handle LOOKUP command: %s",
388                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
390                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
391                 return -1;
392         }
393         f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
394         if (! f) {
395                 char errbuf[1024];
396                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
397                                 "JSON formatter to handle LOOKUP command: %s",
398                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
400                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
401                 sdb_strbuf_destroy(buf);
402                 return -1;
403         }
405         sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
407         if (sdb_store_scan(SDB_HOST, m, filter, lookup_tojson, f)) {
408                 sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup hosts");
409                 sdb_strbuf_sprintf(conn->errbuf, "Failed to lookup hosts");
410                 sdb_strbuf_destroy(buf);
411                 free(f);
412                 return -1;
413         }
414         sdb_store_json_finish(f);
416         sdb_connection_send(conn, CONNECTION_DATA,
417                         (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
418         sdb_strbuf_destroy(buf);
419         free(f);
420         return 0;
421 } /* sdb_fe_exec_lookup */
423 int
424 sdb_fe_exec_timeseries(sdb_conn_t *conn,
425                 const char *hostname, const char *metric,
426                 sdb_timeseries_opts_t *opts)
428         sdb_strbuf_t *buf;
429         uint32_t res_type = htonl(CONNECTION_TIMESERIES);
431         buf = sdb_strbuf_create(1024);
432         if (! buf) {
433                 char errbuf[1024];
434                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
435                                 "buffer to handle TIMESERIES command: %s",
436                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
438                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
439                 return -1;
440         }
442         sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
443         if (sdb_store_fetch_timeseries(hostname, metric, opts, buf)) {
444                 sdb_log(SDB_LOG_ERR, "frontend: Failed to fetch time-series");
445                 sdb_strbuf_sprintf(conn->errbuf, "Failed to fetch time-series");
446                 sdb_strbuf_destroy(buf);
447                 return -1;
448         }
450         sdb_connection_send(conn, CONNECTION_DATA,
451                         (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
452         sdb_strbuf_destroy(buf);
453         return 0;
454 } /* sdb_fe_exec_timeseries */
456 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */