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_obj_t *current_host;
48 sdb_store_writer_t *w;
49 sdb_object_t *wd;
50 } iter_t;
52 static int
53 maybe_emit_host(iter_t *iter, sdb_store_obj_t *obj)
54 {
55 if ((obj->type == SDB_HOST) || (obj->type == SDB_ATTRIBUTE))
56 return 0;
57 if (iter->current_host == obj->parent)
58 return 0;
59 iter->current_host = obj->parent;
60 return sdb_store_emit(obj->parent, iter->w, iter->wd);
61 } /* maybe_emit_host */
63 static int
64 list_tojson(sdb_store_obj_t *obj,
65 sdb_store_matcher_t __attribute__((unused)) *filter,
66 void *user_data)
67 {
68 iter_t *iter = user_data;
69 maybe_emit_host(iter, obj);
70 return sdb_store_emit(obj, iter->w, iter->wd);
71 } /* list_tojson */
73 static int
74 lookup_tojson(sdb_store_obj_t *obj, sdb_store_matcher_t *filter,
75 void *user_data)
76 {
77 iter_t *iter = user_data;
78 maybe_emit_host(iter, obj);
79 return sdb_store_emit_full(obj, filter, iter->w, iter->wd);
80 } /* lookup_tojson */
82 /*
83 * query implementations
84 */
86 static int
87 exec_fetch(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
88 int type, const char *hostname, const char *name,
89 sdb_store_matcher_t *filter)
90 {
91 uint32_t res_type = htonl(SDB_CONNECTION_FETCH);
93 sdb_store_obj_t *host;
94 sdb_store_obj_t *obj;
96 sdb_store_json_formatter_t *f;
97 int status = 0;
99 if ((! name) || ((type == SDB_HOST) && hostname)
100 || ((type != SDB_HOST) && (! hostname))) {
101 /* This is a programming error, not something the client did wrong */
102 sdb_strbuf_sprintf(errbuf, "INTERNAL ERROR: invalid "
103 "arguments to FETCH(%s, %s, %s)",
104 SDB_STORE_TYPE_TO_NAME(type), hostname, name);
105 return -1;
106 }
107 if (type == SDB_HOST)
108 hostname = name;
110 host = sdb_store_get_host(store, hostname);
111 if ((! host)
112 || (filter && (! sdb_store_matcher_matches(filter, host, NULL)))) {
113 sdb_strbuf_sprintf(errbuf, "Failed to fetch %s %s: "
114 "host %s not found", SDB_STORE_TYPE_TO_NAME(type),
115 name, hostname);
116 sdb_object_deref(SDB_OBJ(host));
117 return -1;
118 }
119 if (type == SDB_HOST) {
120 obj = host;
121 }
122 else {
123 obj = sdb_store_get_child(host, type, name);
124 if ((! obj)
125 || (filter && (! sdb_store_matcher_matches(filter, obj, NULL)))) {
126 sdb_strbuf_sprintf(errbuf, "Failed to fetch %s %s.%s: "
127 "%s not found", SDB_STORE_TYPE_TO_NAME(type),
128 hostname, name, name);
129 if (obj)
130 sdb_object_deref(SDB_OBJ(obj));
131 sdb_object_deref(SDB_OBJ(host));
132 return -1;
133 }
134 sdb_object_deref(SDB_OBJ(host));
135 }
136 host = NULL;
138 f = sdb_store_json_formatter(buf, type, /* flags = */ 0);
139 if (! f) {
140 char err[1024];
141 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
142 "JSON formatter to handle FETCH command: %s",
143 sdb_strerror(errno, err, sizeof(err)));
145 sdb_strbuf_sprintf(errbuf, "Out of memory");
146 sdb_object_deref(SDB_OBJ(obj));
147 return -1;
148 }
150 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
151 if (type != SDB_HOST)
152 status = sdb_store_emit(obj->parent,
153 &sdb_store_json_writer, SDB_OBJ(f));
154 if (status || sdb_store_emit_full(obj, filter,
155 &sdb_store_json_writer, SDB_OBJ(f))) {
156 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
157 "%s %s.%s to JSON", SDB_STORE_TYPE_TO_NAME(type),
158 hostname, name);
159 sdb_strbuf_sprintf(errbuf, "Out of memory");
160 sdb_object_deref(SDB_OBJ(f));
161 sdb_object_deref(SDB_OBJ(obj));
162 return -1;
163 }
165 sdb_object_deref(SDB_OBJ(obj));
166 sdb_store_json_finish(f);
167 sdb_object_deref(SDB_OBJ(f));
169 return SDB_CONNECTION_DATA;
170 } /* exec_fetch */
172 static int
173 exec_list(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
174 int type, sdb_store_matcher_t *filter)
175 {
176 uint32_t res_type = htonl(SDB_CONNECTION_LIST);
177 iter_t iter = { NULL, &sdb_store_json_writer, NULL };
178 sdb_store_json_formatter_t *f;
180 f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
181 if (! f) {
182 char err[1024];
183 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
184 "JSON formatter to handle LIST command: %s",
185 sdb_strerror(errno, err, sizeof(err)));
187 sdb_strbuf_sprintf(errbuf, "Out of memory");
188 return -1;
189 }
191 iter.wd = SDB_OBJ(f);
192 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
193 if (sdb_store_scan(store, type, /* m = */ NULL, filter, list_tojson, &iter)) {
194 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
195 "store to JSON");
196 sdb_strbuf_sprintf(errbuf, "Out of memory");
197 sdb_object_deref(SDB_OBJ(f));
198 return -1;
199 }
201 sdb_store_json_finish(f);
202 sdb_object_deref(SDB_OBJ(f));
204 return SDB_CONNECTION_DATA;
205 } /* exec_list */
207 static int
208 exec_lookup(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
209 int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter)
210 {
211 uint32_t res_type = htonl(SDB_CONNECTION_LOOKUP);
212 iter_t iter = { NULL, &sdb_store_json_writer, NULL };
213 sdb_store_json_formatter_t *f;
215 f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
216 if (! f) {
217 char err[1024];
218 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
219 "JSON formatter to handle LOOKUP command: %s",
220 sdb_strerror(errno, err, sizeof(err)));
222 sdb_strbuf_sprintf(errbuf, "Out of memory");
223 return -1;
224 }
226 iter.wd = SDB_OBJ(f);
227 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
228 if (sdb_store_scan(store, type, m, filter, lookup_tojson, &iter)) {
229 sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup %ss",
230 SDB_STORE_TYPE_TO_NAME(type));
231 sdb_strbuf_sprintf(errbuf, "Failed to lookup %ss",
232 SDB_STORE_TYPE_TO_NAME(type));
233 sdb_object_deref(SDB_OBJ(f));
234 return -1;
235 }
237 sdb_store_json_finish(f);
238 sdb_object_deref(SDB_OBJ(f));
240 return SDB_CONNECTION_DATA;
241 } /* exec_lookup */
243 static int
244 exec_timeseries(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
245 const char *hostname, const char *metric,
246 sdb_timeseries_opts_t *opts)
247 {
248 uint32_t res_type = htonl(SDB_CONNECTION_TIMESERIES);
250 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
251 if (sdb_store_fetch_timeseries(store, hostname, metric, opts, buf)) {
252 sdb_log(SDB_LOG_ERR, "frontend: Failed to fetch time-series");
253 sdb_strbuf_sprintf(errbuf, "Failed to fetch time-series");
254 return -1;
255 }
257 return SDB_CONNECTION_DATA;
258 } /* exec_timeseries */
260 /*
261 * public API
262 */
264 int
265 sdb_store_query_execute(sdb_store_t *store, sdb_store_query_t *q,
266 sdb_strbuf_t *buf, sdb_strbuf_t *errbuf)
267 {
268 sdb_timeseries_opts_t ts_opts;
269 sdb_ast_node_t *ast;
271 if (! q)
272 return -1;
273 if (! q->ast) {
274 sdb_log(SDB_LOG_ERR, "store: Invalid empty query");
275 return -1;
276 }
278 ast = q->ast;
279 switch (ast->type) {
280 case SDB_AST_TYPE_FETCH:
281 return exec_fetch(store, buf, errbuf, SDB_AST_FETCH(ast)->obj_type,
282 SDB_AST_FETCH(ast)->hostname, SDB_AST_FETCH(ast)->name,
283 q->filter);
285 case SDB_AST_TYPE_LIST:
286 return exec_list(store, buf, errbuf, SDB_AST_LIST(ast)->obj_type,
287 q->filter);
289 case SDB_AST_TYPE_LOOKUP:
290 return exec_lookup(store, buf, errbuf, SDB_AST_LOOKUP(ast)->obj_type,
291 q->matcher, q->filter);
293 case SDB_AST_TYPE_TIMESERIES:
294 ts_opts.start = SDB_AST_TIMESERIES(ast)->start;
295 ts_opts.end = SDB_AST_TIMESERIES(ast)->end;
296 return exec_timeseries(store, buf, errbuf,
297 SDB_AST_TIMESERIES(ast)->hostname,
298 SDB_AST_TIMESERIES(ast)->metric, &ts_opts);
300 default:
301 sdb_log(SDB_LOG_ERR, "store: Invalid query of type %s",
302 SDB_AST_TYPE_TO_STRING(ast));
303 return -1;
304 }
306 return 0;
307 } /* sdb_store_query_execute */
309 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */