1 /*
2 * SysDB - src/core/store_exec.c
3 * Copyright (C) 2014-2015 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 "core/object.h"
29 #include "core/plugin.h"
30 #include "core/store-private.h"
31 #include "frontend/connection.h"
32 #include "parser/ast.h"
33 #include "utils/error.h"
35 #include <errno.h>
37 #include <arpa/inet.h>
38 #include <stdlib.h>
39 #include <string.h>
41 /*
42 * private helper functions
43 */
45 typedef struct {
46 sdb_store_json_formatter_t *f;
47 sdb_store_obj_t *current_host;
48 } iter_t;
50 static int
51 maybe_emit_host(iter_t *iter, sdb_store_obj_t *obj)
52 {
53 if ((obj->type == SDB_HOST) || (obj->type == SDB_ATTRIBUTE))
54 return 0;
55 if (iter->current_host == obj->parent)
56 return 0;
57 iter->current_host = obj->parent;
58 return sdb_store_json_emit(iter->f, obj->parent);
59 } /* maybe_emit_host */
61 static int
62 list_tojson(sdb_store_obj_t *obj,
63 sdb_store_matcher_t __attribute__((unused)) *filter,
64 void *user_data)
65 {
66 iter_t *iter = user_data;
67 maybe_emit_host(iter, obj);
68 return sdb_store_json_emit(iter->f, obj);
69 } /* list_tojson */
71 static int
72 lookup_tojson(sdb_store_obj_t *obj, sdb_store_matcher_t *filter,
73 void *user_data)
74 {
75 iter_t *iter = user_data;
76 maybe_emit_host(iter, obj);
77 return sdb_store_json_emit_full(iter->f, obj, filter);
78 } /* lookup_tojson */
80 /*
81 * query implementations
82 */
84 static int
85 exec_fetch(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
86 int type, const char *hostname, const char *name,
87 sdb_store_matcher_t *filter)
88 {
89 uint32_t res_type = htonl(SDB_CONNECTION_FETCH);
91 sdb_store_obj_t *host;
92 sdb_store_obj_t *obj;
94 sdb_store_json_formatter_t *f;
95 int status = 0;
97 if ((! name) || ((type == SDB_HOST) && hostname)
98 || ((type != SDB_HOST) && (! hostname))) {
99 /* This is a programming error, not something the client did wrong */
100 sdb_strbuf_sprintf(errbuf, "INTERNAL ERROR: invalid "
101 "arguments to FETCH(%s, %s, %s)",
102 SDB_STORE_TYPE_TO_NAME(type), hostname, name);
103 return -1;
104 }
105 if (type == SDB_HOST)
106 hostname = name;
108 host = sdb_store_get_host(store, hostname);
109 if ((! host)
110 || (filter && (! sdb_store_matcher_matches(filter, host, NULL)))) {
111 sdb_strbuf_sprintf(errbuf, "Failed to fetch %s %s: "
112 "host %s not found", SDB_STORE_TYPE_TO_NAME(type),
113 name, hostname);
114 sdb_object_deref(SDB_OBJ(host));
115 return -1;
116 }
117 if (type == SDB_HOST) {
118 obj = host;
119 }
120 else {
121 obj = sdb_store_get_child(host, type, name);
122 if ((! obj)
123 || (filter && (! sdb_store_matcher_matches(filter, obj, NULL)))) {
124 sdb_strbuf_sprintf(errbuf, "Failed to fetch %s %s.%s: "
125 "%s not found", SDB_STORE_TYPE_TO_NAME(type),
126 hostname, name, name);
127 if (obj)
128 sdb_object_deref(SDB_OBJ(obj));
129 sdb_object_deref(SDB_OBJ(host));
130 return -1;
131 }
132 sdb_object_deref(SDB_OBJ(host));
133 }
134 host = NULL;
136 f = sdb_store_json_formatter(buf, type, /* flags = */ 0);
137 if (! f) {
138 char err[1024];
139 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
140 "JSON formatter to handle FETCH command: %s",
141 sdb_strerror(errno, err, sizeof(err)));
143 sdb_strbuf_sprintf(errbuf, "Out of memory");
144 sdb_object_deref(SDB_OBJ(obj));
145 return -1;
146 }
148 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
149 if (type != SDB_HOST)
150 status = sdb_store_json_emit(f, obj->parent);
151 if (status || sdb_store_json_emit_full(f, obj, filter)) {
152 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
153 "%s %s.%s to JSON", SDB_STORE_TYPE_TO_NAME(type),
154 hostname, name);
155 sdb_strbuf_sprintf(errbuf, "Out of memory");
156 sdb_object_deref(SDB_OBJ(f));
157 sdb_object_deref(SDB_OBJ(obj));
158 return -1;
159 }
161 sdb_object_deref(SDB_OBJ(obj));
162 sdb_store_json_finish(f);
163 sdb_object_deref(SDB_OBJ(f));
165 return SDB_CONNECTION_DATA;
166 } /* exec_fetch */
168 static int
169 exec_list(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
170 int type, sdb_store_matcher_t *filter)
171 {
172 uint32_t res_type = htonl(SDB_CONNECTION_LIST);
173 iter_t iter = { NULL, NULL };
175 iter.f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
176 if (! iter.f) {
177 char err[1024];
178 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
179 "JSON formatter to handle LIST command: %s",
180 sdb_strerror(errno, err, sizeof(err)));
182 sdb_strbuf_sprintf(errbuf, "Out of memory");
183 return -1;
184 }
186 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
187 if (sdb_store_scan(store, type, /* m = */ NULL, filter, list_tojson, &iter)) {
188 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
189 "store to JSON");
190 sdb_strbuf_sprintf(errbuf, "Out of memory");
191 sdb_object_deref(SDB_OBJ(iter.f));
192 return -1;
193 }
195 sdb_store_json_finish(iter.f);
196 sdb_object_deref(SDB_OBJ(iter.f));
198 return SDB_CONNECTION_DATA;
199 } /* exec_list */
201 static int
202 exec_lookup(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
203 int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter)
204 {
205 uint32_t res_type = htonl(SDB_CONNECTION_LOOKUP);
206 iter_t iter = { NULL, NULL };
208 iter.f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
209 if (! iter.f) {
210 char err[1024];
211 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
212 "JSON formatter to handle LOOKUP command: %s",
213 sdb_strerror(errno, err, sizeof(err)));
215 sdb_strbuf_sprintf(errbuf, "Out of memory");
216 return -1;
217 }
219 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
221 if (sdb_store_scan(store, type, m, filter, lookup_tojson, &iter)) {
222 sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup %ss",
223 SDB_STORE_TYPE_TO_NAME(type));
224 sdb_strbuf_sprintf(errbuf, "Failed to lookup %ss",
225 SDB_STORE_TYPE_TO_NAME(type));
226 sdb_object_deref(SDB_OBJ(iter.f));
227 return -1;
228 }
230 sdb_store_json_finish(iter.f);
231 sdb_object_deref(SDB_OBJ(iter.f));
233 return SDB_CONNECTION_DATA;
234 } /* exec_lookup */
236 static int
237 exec_timeseries(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
238 const char *hostname, const char *metric,
239 sdb_timeseries_opts_t *opts)
240 {
241 uint32_t res_type = htonl(SDB_CONNECTION_TIMESERIES);
243 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
244 if (sdb_store_fetch_timeseries(store, hostname, metric, opts, buf)) {
245 sdb_log(SDB_LOG_ERR, "frontend: Failed to fetch time-series");
246 sdb_strbuf_sprintf(errbuf, "Failed to fetch time-series");
247 return -1;
248 }
250 return SDB_CONNECTION_DATA;
251 } /* exec_timeseries */
253 /*
254 * public API
255 */
257 int
258 sdb_store_query_execute(sdb_store_t *store, sdb_store_query_t *q,
259 sdb_strbuf_t *buf, sdb_strbuf_t *errbuf)
260 {
261 sdb_timeseries_opts_t ts_opts;
262 sdb_ast_node_t *ast;
264 if (! q)
265 return -1;
266 if (! q->ast) {
267 sdb_log(SDB_LOG_ERR, "store: Invalid empty query");
268 return -1;
269 }
271 ast = q->ast;
272 switch (ast->type) {
273 case SDB_AST_TYPE_FETCH:
274 return exec_fetch(store, buf, errbuf, SDB_AST_FETCH(ast)->obj_type,
275 SDB_AST_FETCH(ast)->hostname, SDB_AST_FETCH(ast)->name,
276 q->filter);
278 case SDB_AST_TYPE_LIST:
279 return exec_list(store, buf, errbuf, SDB_AST_LIST(ast)->obj_type,
280 q->filter);
282 case SDB_AST_TYPE_LOOKUP:
283 return exec_lookup(store, buf, errbuf, SDB_AST_LOOKUP(ast)->obj_type,
284 q->matcher, q->filter);
286 case SDB_AST_TYPE_TIMESERIES:
287 ts_opts.start = SDB_AST_TIMESERIES(ast)->start;
288 ts_opts.end = SDB_AST_TIMESERIES(ast)->end;
289 return exec_timeseries(store, buf, errbuf,
290 SDB_AST_TIMESERIES(ast)->hostname,
291 SDB_AST_TIMESERIES(ast)->metric, &ts_opts);
293 default:
294 sdb_log(SDB_LOG_ERR, "store: Invalid query of type %s",
295 SDB_AST_TYPE_TO_STRING(ast));
296 return -1;
297 }
299 return 0;
300 } /* sdb_store_query_execute */
302 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */