Code

store: Let sdb_store_scan() pass on filters to callback functions.
[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 typedef struct {
45         sdb_strbuf_t *buf;
46         size_t last_len;
47 } tojson_data_t;
49 static int
50 lookup_tojson(sdb_store_obj_t *obj, sdb_store_matcher_t *filter,
51                 void *user_data)
52 {
53         tojson_data_t *data = user_data;
54         int status;
56         if (filter && (! sdb_store_matcher_matches(filter, obj, NULL)))
57                 return 0;
59         if (sdb_strbuf_len(data->buf) > data->last_len)
60                 sdb_strbuf_append(data->buf, ",");
61         data->last_len = sdb_strbuf_len(data->buf);
62         status = sdb_store_host_tojson(obj, data->buf, filter, /* flags = */ 0);
63         return status;
64 } /* lookup_tojson */
66 /*
67  * public API
68  */
70 int
71 sdb_fe_query(sdb_conn_t *conn)
72 {
73         sdb_llist_t *parsetree;
74         sdb_conn_node_t *node = NULL;
75         int status = 0;
77         if ((! conn) || (conn->cmd != CONNECTION_QUERY))
78                 return -1;
80         parsetree = sdb_fe_parse(sdb_strbuf_string(conn->buf),
81                         (int)conn->cmd_len);
82         if (! parsetree) {
83                 char query[conn->cmd_len + 1];
84                 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
85                 query[sizeof(query) - 1] = '\0';
86                 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse query '%s'",
87                                 query);
88                 return -1;
89         }
91         switch (sdb_llist_len(parsetree)) {
92                 case 0:
93                         /* skipping empty command; send back an empty reply */
94                         sdb_connection_send(conn, CONNECTION_DATA, 0, NULL);
95                         break;
96                 case 1:
97                         node = SDB_CONN_NODE(sdb_llist_get(parsetree, 0));
98                         break;
100                 default:
101                         {
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_WARNING, "frontend: Ignoring %zu command%s "
106                                                 "in multi-statement query '%s'",
107                                                 sdb_llist_len(parsetree) - 1,
108                                                 sdb_llist_len(parsetree) == 2 ? "" : "s",
109                                                 query);
110                                 node = SDB_CONN_NODE(sdb_llist_get(parsetree, 0));
111                         }
112         }
114         if (node) {
115                 if (sdb_fe_analyze(node)) {
116                         char query[conn->cmd_len + 1];
117                         strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
118                         query[sizeof(query) - 1] = '\0';
119                         sdb_log(SDB_LOG_ERR, "frontend: Failed to verify query '%s'",
120                                         query);
121                         status = -1;
122                 }
123                 else
124                         status = sdb_fe_exec(conn, node);
125                 sdb_object_deref(SDB_OBJ(node));
126         }
128         sdb_llist_destroy(parsetree);
129         return status;
130 } /* sdb_fe_query */
132 int
133 sdb_fe_fetch(sdb_conn_t *conn)
135         char name[conn->cmd_len + 1];
136         int type;
138         if ((! conn) || (conn->cmd != CONNECTION_FETCH))
139                 return -1;
141         if (conn->cmd_len < sizeof(type)) {
142                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
143                                 "FETCH command", conn->cmd_len);
144                 sdb_strbuf_sprintf(conn->errbuf, "FETCH: Invalid command length %d",
145                                 conn->cmd_len);
146                 return -1;
147         }
149         type = sdb_proto_get_int(conn->buf, 0);
150         strncpy(name, sdb_strbuf_string(conn->buf) + sizeof(type),
151                         conn->cmd_len - sizeof(type));
152         name[sizeof(name) - 1] = '\0';
153         return sdb_fe_exec_fetch(conn, type, name, /* filter = */ NULL);
154 } /* sdb_fe_fetch */
156 int
157 sdb_fe_list(sdb_conn_t *conn)
159         int type = SDB_HOST;
161         if ((! conn) || (conn->cmd != CONNECTION_LIST))
162                 return -1;
164         if (conn->cmd_len == sizeof(uint32_t))
165                 type = sdb_proto_get_int(conn->buf, 0);
166         else if (conn->cmd_len) {
167                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
168                                 "LIST command", conn->cmd_len);
169                 sdb_strbuf_sprintf(conn->errbuf, "LIST: Invalid command length %d",
170                                 conn->cmd_len);
171                 return -1;
172         }
173         return sdb_fe_exec_list(conn, type, /* filter = */ NULL);
174 } /* sdb_fe_list */
176 int
177 sdb_fe_lookup(sdb_conn_t *conn)
179         sdb_store_matcher_t *m;
180         const char *matcher;
181         size_t matcher_len;
183         uint32_t type;
184         int status;
186         conn_matcher_t m_node = {
187                 { SDB_OBJECT_INIT, CONNECTION_MATCHER }, NULL
188         };
189         conn_lookup_t node = {
190                 { SDB_OBJECT_INIT, CONNECTION_LOOKUP },
191                 -1, &m_node, NULL
192         };
194         if ((! conn) || (conn->cmd != CONNECTION_LOOKUP))
195                 return -1;
197         if (conn->cmd_len < sizeof(type)) {
198                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
199                                 "LOOKUP command", conn->cmd_len);
200                 sdb_strbuf_sprintf(conn->errbuf, "LOOKUP: Invalid command length %d",
201                                 conn->cmd_len);
202                 return -1;
203         }
204         type = sdb_proto_get_int(conn->buf, 0);
206         matcher = sdb_strbuf_string(conn->buf) + sizeof(type);
207         matcher_len = conn->cmd_len - sizeof(type);
208         m = sdb_fe_parse_matcher(matcher, (int)matcher_len);
209         if (! m) {
210                 char expr[matcher_len + 1];
211                 strncpy(expr, matcher, sizeof(expr));
212                 expr[sizeof(expr) - 1] = '\0';
213                 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse "
214                                 "lookup condition '%s'", expr);
215                 return -1;
216         }
218         node.type = type;
219         m_node.matcher = m;
221         if (sdb_fe_analyze(SDB_CONN_NODE(&node))) {
222                 char expr[matcher_len + 1];
223                 strncpy(expr, matcher, sizeof(expr));
224                 expr[sizeof(expr) - 1] = '\0';
225                 sdb_log(SDB_LOG_ERR, "frontend: Failed to verify "
226                                 "lookup condition '%s'", expr);
227                 status = -1;
228         }
229         else
230                 status = sdb_fe_exec_lookup(conn, type, m, /* filter = */ NULL);
231         sdb_object_deref(SDB_OBJ(m));
232         return status;
233 } /* sdb_fe_lookup */
235 int
236 sdb_fe_exec(sdb_conn_t *conn, sdb_conn_node_t *node)
238         sdb_store_matcher_t *m = NULL, *filter = NULL;
240         if (! node)
241                 return -1;
243         switch (node->cmd) {
244                 case CONNECTION_FETCH:
245                         if (CONN_FETCH(node)->filter)
246                                 filter = CONN_FETCH(node)->filter->matcher;
247                         return sdb_fe_exec_fetch(conn, CONN_FETCH(node)->type,
248                                         CONN_FETCH(node)->name, filter);
249                 case CONNECTION_LIST:
250                         if (CONN_LIST(node)->filter)
251                                 filter = CONN_LIST(node)->filter->matcher;
252                         return sdb_fe_exec_list(conn, CONN_LIST(node)->type, filter);
253                 case CONNECTION_LOOKUP:
254                         if (CONN_LOOKUP(node)->matcher)
255                                 m = CONN_LOOKUP(node)->matcher->matcher;
256                         if (CONN_LOOKUP(node)->filter)
257                                 filter = CONN_LOOKUP(node)->filter->matcher;
258                         return sdb_fe_exec_lookup(conn,
259                                         CONN_LOOKUP(node)->type, m, filter);
260                 case CONNECTION_TIMESERIES:
261                         return sdb_fe_exec_timeseries(conn,
262                                         CONN_TS(node)->hostname, CONN_TS(node)->metric,
263                                         &CONN_TS(node)->opts);
265                 default:
266                         sdb_log(SDB_LOG_ERR, "frontend: Unknown command %i", node->cmd);
267                         return -1;
268         }
269         return -1;
270 } /* sdb_fe_exec */
272 int
273 sdb_fe_exec_fetch(sdb_conn_t *conn, int type, const char *name,
274                 sdb_store_matcher_t *filter)
276         sdb_strbuf_t *buf;
277         sdb_store_obj_t *host;
278         uint32_t res_type = htonl(CONNECTION_FETCH);
280         /* XXX: support other types */
281         if (type != SDB_HOST) {
282                 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
283                                 "in FETCH command", type);
284                 sdb_strbuf_sprintf(conn->errbuf,
285                                 "FETCH: Invalid object type %d", type);
286                 return -1;
287         }
289         host = sdb_store_get_host(name);
290         if (! host) {
291                 sdb_log(SDB_LOG_DEBUG, "frontend: Failed to fetch host '%s': "
292                                 "not found", name);
294                 sdb_strbuf_sprintf(conn->errbuf, "Host %s not found", name);
295                 return -1;
296         }
298         buf = sdb_strbuf_create(1024);
299         if (! buf) {
300                 char errbuf[1024];
301                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
302                                 "buffer to handle FETCH command: %s",
303                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
305                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
306                 sdb_strbuf_destroy(buf);
307                 sdb_object_deref(SDB_OBJ(host));
308                 return -1;
309         }
311         sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
312         if (sdb_store_host_tojson(host, buf, filter, /* flags = */ 0)) {
313                 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
314                                 "host '%s' to JSON", name);
315                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
316                 sdb_strbuf_destroy(buf);
317                 sdb_object_deref(SDB_OBJ(host));
318                 return -1;
319         }
321         sdb_connection_send(conn, CONNECTION_DATA,
322                         (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
323         sdb_strbuf_destroy(buf);
324         sdb_object_deref(SDB_OBJ(host));
325         return 0;
326 } /* sdb_fe_exec_fetch */
328 int
329 sdb_fe_exec_list(sdb_conn_t *conn, int type, sdb_store_matcher_t *filter)
331         sdb_strbuf_t *buf;
332         uint32_t res_type = htonl(CONNECTION_LIST);
334         int flags;
336         if (type == SDB_HOST)
337                 flags = SDB_SKIP_ALL;
338         else if (type == SDB_SERVICE)
339                 flags = (SDB_SKIP_ALL & (~SDB_SKIP_SERVICES))
340                         | SDB_SKIP_EMPTY_SERVICES;
341         else if (type == SDB_METRIC)
342                 flags = (SDB_SKIP_ALL & (~SDB_SKIP_METRICS))
343                         | SDB_SKIP_EMPTY_METRICS;
344         else {
345                 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
346                                 "for LIST command", type);
347                 sdb_strbuf_sprintf(conn->errbuf,
348                                 "LIST: Invalid object type %d", type);
349                 return -1;
350         }
352         buf = sdb_strbuf_create(1024);
353         if (! buf) {
354                 char errbuf[1024];
355                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
356                                 "buffer to handle LIST command: %s",
357                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
359                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
360                 sdb_strbuf_destroy(buf);
361                 return -1;
362         }
364         sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
365         if (sdb_store_tojson(buf, filter, flags)) {
366                 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
367                                 "store to JSON");
368                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
369                 sdb_strbuf_destroy(buf);
370                 return -1;
371         }
373         sdb_connection_send(conn, CONNECTION_DATA,
374                         (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
375         sdb_strbuf_destroy(buf);
376         return 0;
377 } /* sdb_fe_exec_list */
379 int
380 sdb_fe_exec_lookup(sdb_conn_t *conn, int type,
381                 sdb_store_matcher_t *m, sdb_store_matcher_t *filter)
383         tojson_data_t data = { NULL, 0 };
384         uint32_t res_type = htonl(CONNECTION_LOOKUP);
386         /* XXX: support other types */
387         if (type != SDB_HOST) {
388                 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
389                                 "in LOOKUP command", type);
390                 sdb_strbuf_sprintf(conn->errbuf,
391                                 "LOOKUP: Invalid object type %d", type);
392                 return -1;
393         }
395         data.buf = sdb_strbuf_create(1024);
396         if (! data.buf) {
397                 char errbuf[1024];
398                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
399                                 "buffer to handle LOOKUP command: %s",
400                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
402                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
403                 sdb_strbuf_destroy(data.buf);
404                 return -1;
405         }
407         sdb_strbuf_memcpy(data.buf, &res_type, sizeof(uint32_t));
408         sdb_strbuf_append(data.buf, "[");
410         data.last_len = sdb_strbuf_len(data.buf);
411         if (sdb_store_scan(SDB_HOST, m, filter, lookup_tojson, &data)) {
412                 sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup hosts");
413                 sdb_strbuf_sprintf(conn->errbuf, "Failed to lookup hosts");
414                 sdb_strbuf_destroy(data.buf);
415                 return -1;
416         }
418         sdb_strbuf_append(data.buf, "]");
420         sdb_connection_send(conn, CONNECTION_DATA,
421                         (uint32_t)sdb_strbuf_len(data.buf), sdb_strbuf_string(data.buf));
422         sdb_strbuf_destroy(data.buf);
423         return 0;
424 } /* sdb_fe_exec_lookup */
426 int
427 sdb_fe_exec_timeseries(sdb_conn_t *conn,
428                 const char *hostname, const char *metric,
429                 sdb_timeseries_opts_t *opts)
431         sdb_strbuf_t *buf;
432         uint32_t res_type = htonl(CONNECTION_TIMESERIES);
434         buf = sdb_strbuf_create(1024);
435         if (! buf) {
436                 char errbuf[1024];
437                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
438                                 "buffer to handle TIMESERIES command: %s",
439                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
441                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
442                 return -1;
443         }
445         sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
446         if (sdb_store_fetch_timeseries(hostname, metric, opts, buf)) {
447                 sdb_log(SDB_LOG_ERR, "frontend: Failed to fetch time-series");
448                 sdb_strbuf_sprintf(conn->errbuf, "Failed to fetch time-series");
449                 sdb_strbuf_destroy(buf);
450                 return -1;
451         }
453         sdb_connection_send(conn, CONNECTION_DATA,
454                         (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
455         sdb_strbuf_destroy(buf);
456         return 0;
457 } /* sdb_fe_exec_timeseries */
459 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */