Code

97698fd300be6af87607c13d9b7c2336cea54b48
[sysdb.git] / src / frontend / query.c
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 #ifdef HAVE_CONFIG_H
29 #       include "config.h"
30 #endif
32 #include "sysdb.h"
34 #include "core/store.h"
35 #include "frontend/connection-private.h"
36 #include "parser/ast.h"
37 #include "parser/parser.h"
38 #include "utils/error.h"
39 #include "utils/proto.h"
40 #include "utils/strbuf.h"
42 #include <errno.h>
43 #include <string.h>
45 /*
46  * private helper functions
47  */
49 static char *
50 sstrdup(const char *s)
51 {
52         return s ? strdup(s) : NULL;
53 } /* sstrdup */
55 static int
56 query_exec(sdb_conn_t *conn, sdb_ast_node_t *ast)
57 {
58         sdb_store_query_t *q;
59         sdb_strbuf_t *buf;
60         int status;
62         if (! ast) {
63                 sdb_strbuf_sprintf(conn->errbuf, "out of memory");
64                 return -1;
65         }
67         q = sdb_store_query_prepare(ast);
68         if (! q) {
69                 /* this shouldn't happen */
70                 sdb_strbuf_sprintf(conn->errbuf, "failed to compile AST");
71                 sdb_log(SDB_LOG_ERR, "frontend: failed to compile AST");
72                 return -1;
73         }
75         buf = sdb_strbuf_create(1024);
76         if (! buf) {
77                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
78                 sdb_object_deref(SDB_OBJ(q));
79                 return -1;
80         }
81         status = sdb_store_query_execute(q, buf, conn->errbuf);
82         if (status < 0) {
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 execute query '%s'", query);
87         }
88         else
89                 sdb_connection_send(conn, status,
90                                 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
92         sdb_strbuf_destroy(buf);
93         sdb_object_deref(SDB_OBJ(q));
94         return status < 0 ? status : 0;
95 } /* query_exec */
97 /*
98  * public API
99  */
101 int
102 sdb_fe_query(sdb_conn_t *conn)
104         sdb_llist_t *parsetree;
105         sdb_ast_node_t *ast = NULL;
106         int status = 0;
108         if ((! conn) || (conn->cmd != SDB_CONNECTION_QUERY))
109                 return -1;
111         parsetree = sdb_parser_parse(sdb_strbuf_string(conn->buf),
112                         (int)conn->cmd_len, conn->errbuf);
113         if (! parsetree) {
114                 char query[conn->cmd_len + 1];
115                 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
116                 query[sizeof(query) - 1] = '\0';
117                 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse query '%s': %s",
118                                 query, sdb_strbuf_string(conn->errbuf));
119                 return -1;
120         }
122         switch (sdb_llist_len(parsetree)) {
123                 case 0:
124                         /* skipping empty command; send back an empty reply */
125                         sdb_connection_send(conn, SDB_CONNECTION_DATA, 0, NULL);
126                         break;
127                 case 1:
128                         ast = SDB_AST_NODE(sdb_llist_get(parsetree, 0));
129                         break;
131                 default:
132                         {
133                                 char query[conn->cmd_len + 1];
134                                 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
135                                 query[sizeof(query) - 1] = '\0';
136                                 sdb_log(SDB_LOG_WARNING, "frontend: Ignoring %zu command%s "
137                                                 "in multi-statement query '%s'",
138                                                 sdb_llist_len(parsetree) - 1,
139                                                 sdb_llist_len(parsetree) == 2 ? "" : "s",
140                                                 query);
141                                 ast = SDB_AST_NODE(sdb_llist_get(parsetree, 0));
142                         }
143         }
145         if (ast) {
146                 status = query_exec(conn, ast);
147                 sdb_object_deref(SDB_OBJ(ast));
148         }
149         sdb_llist_destroy(parsetree);
150         return status;
151 } /* sdb_fe_query */
153 int
154 sdb_fe_fetch(sdb_conn_t *conn)
156         sdb_ast_node_t *ast;
157         char hostname[conn->cmd_len + 1];
158         char name[conn->cmd_len + 1];
159         uint32_t type;
160         int status;
162         if ((! conn) || (conn->cmd != SDB_CONNECTION_FETCH))
163                 return -1;
165         if (conn->cmd_len < sizeof(uint32_t)) {
166                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
167                                 "FETCH command", conn->cmd_len);
168                 sdb_strbuf_sprintf(conn->errbuf, "FETCH: Invalid command length %d",
169                                 conn->cmd_len);
170                 return -1;
171         }
173         /* TODO: support other types besides hosts */
174         hostname[0] = '\0';
176         sdb_proto_unmarshal_int32(SDB_STRBUF_STR(conn->buf), &type);
177         strncpy(name, sdb_strbuf_string(conn->buf) + sizeof(uint32_t),
178                         conn->cmd_len - sizeof(uint32_t));
179         name[sizeof(name) - 1] = '\0';
181         ast = sdb_ast_fetch_create((int)type,
182                         hostname[0] ? strdup(hostname) : NULL,
183                         name[0] ? strdup(name) : NULL,
184                         /* filter = */ NULL);
185         status = query_exec(conn, ast);
186         sdb_object_deref(SDB_OBJ(ast));
187         return status;
188 } /* sdb_fe_fetch */
190 int
191 sdb_fe_list(sdb_conn_t *conn)
193         sdb_ast_node_t *ast;
194         uint32_t type = SDB_HOST;
195         int status;
197         if ((! conn) || (conn->cmd != SDB_CONNECTION_LIST))
198                 return -1;
200         if (conn->cmd_len == sizeof(uint32_t))
201                 sdb_proto_unmarshal_int32(SDB_STRBUF_STR(conn->buf), &type);
202         else if (conn->cmd_len) {
203                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
204                                 "LIST command", conn->cmd_len);
205                 sdb_strbuf_sprintf(conn->errbuf, "LIST: Invalid command length %d",
206                                 conn->cmd_len);
207                 return -1;
208         }
210         ast = sdb_ast_list_create((int)type, /* filter = */ NULL);
211         status = query_exec(conn, ast);
212         sdb_object_deref(SDB_OBJ(ast));
213         return status;
214 } /* sdb_fe_list */
216 int
217 sdb_fe_lookup(sdb_conn_t *conn)
219         sdb_ast_node_t *ast, *m;
220         const char *matcher;
221         size_t matcher_len;
223         uint32_t type;
224         int status;
226         if ((! conn) || (conn->cmd != SDB_CONNECTION_LOOKUP))
227                 return -1;
229         if (conn->cmd_len < sizeof(uint32_t)) {
230                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
231                                 "LOOKUP command", conn->cmd_len);
232                 sdb_strbuf_sprintf(conn->errbuf, "LOOKUP: Invalid command length %d",
233                                 conn->cmd_len);
234                 return -1;
235         }
236         sdb_proto_unmarshal_int32(SDB_STRBUF_STR(conn->buf), &type);
238         matcher = sdb_strbuf_string(conn->buf) + sizeof(uint32_t);
239         matcher_len = conn->cmd_len - sizeof(uint32_t);
240         m = sdb_parser_parse_conditional(matcher, (int)matcher_len, conn->errbuf);
241         if (! m) {
242                 char expr[matcher_len + 1];
243                 strncpy(expr, matcher, sizeof(expr));
244                 expr[sizeof(expr) - 1] = '\0';
245                 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse lookup condition '%s': %s",
246                                 expr, sdb_strbuf_string(conn->errbuf));
247                 return -1;
248         }
250         ast = sdb_ast_lookup_create((int)type, m, /* filter = */ NULL);
251         /* run analyzer using the full context */
252         if (ast && sdb_parser_analyze(ast, conn->errbuf)) {
253                 char expr[matcher_len + 1];
254                 char err[sdb_strbuf_len(conn->errbuf) + sizeof(expr) + 64];
255                 strncpy(expr, matcher, sizeof(expr));
256                 expr[sizeof(expr) - 1] = '\0';
257                 snprintf(err, sizeof(err), "Failed to parse lookup condition '%s': %s",
258                                 expr, sdb_strbuf_string(conn->errbuf));
259                 sdb_strbuf_sprintf(conn->errbuf, "%s", err);
260                 status = -1;
261         }
262         else
263                 status = query_exec(conn, ast);
264         if (! ast)
265                 sdb_object_deref(SDB_OBJ(m));
266         sdb_object_deref(SDB_OBJ(ast));
267         return status;
268 } /* sdb_fe_lookup */
270 int
271 sdb_fe_store(sdb_conn_t *conn)
273         sdb_ast_node_t *ast;
274         const char *buf = sdb_strbuf_string(conn->buf);
275         size_t len = conn->cmd_len;
276         uint32_t type;
277         ssize_t n;
278         int status;
280         if ((! conn) || (conn->cmd != SDB_CONNECTION_STORE))
281                 return -1;
283         if ((n = sdb_proto_unmarshal_int32(buf, len, &type)) < 0) {
284                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %zu for "
285                                 "STORE command", len);
286                 sdb_strbuf_sprintf(conn->errbuf,
287                                 "STORE: Invalid command length %zu", len);
288                 return -1;
289         }
291         switch (type) {
292                 case SDB_HOST:
293                 {
294                         sdb_proto_host_t host;
295                         if (sdb_proto_unmarshal_host(buf, len, &host) < 0) {
296                                 sdb_strbuf_sprintf(conn->errbuf,
297                                                 "STORE: Failed to unmarshal host object");
298                                 return -1;
299                         }
300                         ast = sdb_ast_store_create(SDB_HOST, /* host */ NULL,
301                                         /* parent */ 0, NULL, sstrdup(host.name), host.last_update,
302                                         /* metric store */ NULL, NULL, SDB_DATA_NULL);
303                 }
304                 break;
306                 case SDB_SERVICE:
307                 {
308                         sdb_proto_service_t svc;
309                         if (sdb_proto_unmarshal_service(buf, len, &svc) < 0) {
310                                 sdb_strbuf_sprintf(conn->errbuf,
311                                                 "STORE: Failed to unmarshal service object");
312                                 return -1;
313                         }
314                         ast = sdb_ast_store_create(SDB_SERVICE, sstrdup(svc.hostname),
315                                         /* parent */ 0, NULL, sstrdup(svc.name), svc.last_update,
316                                         /* metric store */ NULL, NULL, SDB_DATA_NULL);
317                 }
318                 break;
320                 case SDB_METRIC:
321                 {
322                         sdb_proto_metric_t metric;
323                         if (sdb_proto_unmarshal_metric(buf, len, &metric) < 0) {
324                                 sdb_strbuf_sprintf(conn->errbuf,
325                                                 "STORE: Failed to unmarshal metric object");
326                                 return -1;
327                         }
328                         ast = sdb_ast_store_create(SDB_METRIC, sstrdup(metric.hostname),
329                                         /* parent */ 0, NULL, sstrdup(metric.name), metric.last_update,
330                                         sstrdup(metric.store_type), sstrdup(metric.store_id),
331                                         SDB_DATA_NULL);
332                 }
333                 break;
334         }
336         if (type & SDB_ATTRIBUTE) {
337                 sdb_proto_attribute_t attr;
338                 const char *hostname, *parent;
339                 int parent_type;
340                 if (sdb_proto_unmarshal_attribute(buf, len, &attr) < 0) {
341                         sdb_strbuf_sprintf(conn->errbuf,
342                                         "STORE: Failed to unmarshal attribute object");
343                         return -1;
344                 }
345                 if (attr.parent_type == SDB_HOST) {
346                         hostname = attr.parent;
347                         parent_type = 0;
348                         parent = NULL;
349                 }
350                 else {
351                         hostname = attr.hostname;
352                         parent_type = attr.parent_type;
353                         parent = attr.parent;
354                 }
355                 ast = sdb_ast_store_create(SDB_ATTRIBUTE, sstrdup(hostname),
356                                 parent_type, sstrdup(parent), sstrdup(attr.key),
357                                 attr.last_update, /* metric store */ NULL, NULL,
358                                 attr.value);
359         }
361         if (! ast) {
362                 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d for "
363                                 "STORE COMMAND", type);
364                 sdb_strbuf_sprintf(conn->errbuf, "STORE: Invalid object type %d", type);
365                 return -1;
366         }
368         status = sdb_parser_analyze(ast, conn->errbuf);
369         if (! status)
370                 status = query_exec(conn, ast);
371         sdb_object_deref(SDB_OBJ(ast));
372         return status;
373 } /* sdb_fe_store */
375 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */