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 static int
46 list_tojson(sdb_store_obj_t *obj,
47 sdb_store_matcher_t __attribute__((unused)) *filter,
48 void *user_data)
49 {
50 sdb_store_json_formatter_t *f = user_data;
51 return sdb_store_json_emit(f, obj);
52 } /* list_tojson */
54 static int
55 lookup_tojson(sdb_store_obj_t *obj, sdb_store_matcher_t *filter,
56 void *user_data)
57 {
58 sdb_store_json_formatter_t *f = user_data;
59 return sdb_store_json_emit_full(f, obj, filter);
60 } /* lookup_tojson */
62 /*
63 * query implementations
64 */
66 static int
67 exec_fetch(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
68 int type, const char *hostname, const char *name,
69 sdb_store_matcher_t *filter)
70 {
71 uint32_t res_type = htonl(SDB_CONNECTION_FETCH);
73 sdb_store_obj_t *host;
74 sdb_store_obj_t *obj;
76 sdb_store_json_formatter_t *f;
78 if ((! name) || ((type == SDB_HOST) && hostname)
79 || ((type != SDB_HOST) && (! hostname))) {
80 /* This is a programming error, not something the client did wrong */
81 sdb_strbuf_sprintf(errbuf, "INTERNAL ERROR: invalid "
82 "arguments to FETCH(%s, %s, %s)",
83 SDB_STORE_TYPE_TO_NAME(type), hostname, name);
84 return -1;
85 }
86 if (type == SDB_HOST)
87 hostname = name;
89 host = sdb_store_get_host(store, hostname);
90 if ((! host)
91 || (filter && (! sdb_store_matcher_matches(filter, host, NULL)))) {
92 sdb_strbuf_sprintf(errbuf, "Failed to fetch %s %s: "
93 "host %s not found", SDB_STORE_TYPE_TO_NAME(type),
94 name, hostname);
95 sdb_object_deref(SDB_OBJ(host));
96 return -1;
97 }
98 if (type == SDB_HOST) {
99 obj = host;
100 }
101 else {
102 obj = sdb_store_get_child(host, type, name);
103 if ((! obj)
104 || (filter && (! sdb_store_matcher_matches(filter, obj, NULL)))) {
105 sdb_strbuf_sprintf(errbuf, "Failed to fetch %s %s.%s: "
106 "%s not found", SDB_STORE_TYPE_TO_NAME(type),
107 hostname, name, name);
108 if (obj)
109 sdb_object_deref(SDB_OBJ(obj));
110 sdb_object_deref(SDB_OBJ(host));
111 return -1;
112 }
113 sdb_object_deref(SDB_OBJ(host));
114 }
115 host = NULL;
117 f = sdb_store_json_formatter(buf, type, /* flags = */ 0);
118 if (! f) {
119 char err[1024];
120 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
121 "JSON formatter to handle FETCH command: %s",
122 sdb_strerror(errno, err, sizeof(err)));
124 sdb_strbuf_sprintf(errbuf, "Out of memory");
125 sdb_object_deref(SDB_OBJ(obj));
126 return -1;
127 }
129 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
130 if (sdb_store_json_emit_full(f, obj, filter)) {
131 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
132 "%s %s.%s to JSON", SDB_STORE_TYPE_TO_NAME(type),
133 hostname, name);
134 sdb_strbuf_sprintf(errbuf, "Out of memory");
135 sdb_object_deref(SDB_OBJ(f));
136 sdb_object_deref(SDB_OBJ(obj));
137 return -1;
138 }
140 sdb_object_deref(SDB_OBJ(obj));
141 sdb_store_json_finish(f);
142 sdb_object_deref(SDB_OBJ(f));
144 return SDB_CONNECTION_DATA;
145 } /* exec_fetch */
147 static int
148 exec_list(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
149 int type, sdb_store_matcher_t *filter)
150 {
151 uint32_t res_type = htonl(SDB_CONNECTION_LIST);
152 sdb_store_json_formatter_t *f;
154 f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
155 if (! f) {
156 char err[1024];
157 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
158 "JSON formatter to handle LIST command: %s",
159 sdb_strerror(errno, err, sizeof(err)));
161 sdb_strbuf_sprintf(errbuf, "Out of memory");
162 return -1;
163 }
165 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
166 if (sdb_store_scan(store, type, /* m = */ NULL, filter, list_tojson, f)) {
167 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
168 "store to JSON");
169 sdb_strbuf_sprintf(errbuf, "Out of memory");
170 sdb_object_deref(SDB_OBJ(f));
171 return -1;
172 }
174 sdb_store_json_finish(f);
175 sdb_object_deref(SDB_OBJ(f));
177 return SDB_CONNECTION_DATA;
178 } /* exec_list */
180 static int
181 exec_lookup(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
182 int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter)
183 {
184 uint32_t res_type = htonl(SDB_CONNECTION_LOOKUP);
185 sdb_store_json_formatter_t *f;
187 f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
188 if (! f) {
189 char err[1024];
190 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
191 "JSON formatter to handle LOOKUP command: %s",
192 sdb_strerror(errno, err, sizeof(err)));
194 sdb_strbuf_sprintf(errbuf, "Out of memory");
195 return -1;
196 }
198 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
200 if (sdb_store_scan(store, type, m, filter, lookup_tojson, f)) {
201 sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup %ss",
202 SDB_STORE_TYPE_TO_NAME(type));
203 sdb_strbuf_sprintf(errbuf, "Failed to lookup %ss",
204 SDB_STORE_TYPE_TO_NAME(type));
205 sdb_object_deref(SDB_OBJ(f));
206 return -1;
207 }
209 sdb_store_json_finish(f);
210 sdb_object_deref(SDB_OBJ(f));
212 return SDB_CONNECTION_DATA;
213 } /* exec_lookup */
215 static int
216 exec_timeseries(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
217 const char *hostname, const char *metric,
218 sdb_timeseries_opts_t *opts)
219 {
220 uint32_t res_type = htonl(SDB_CONNECTION_TIMESERIES);
222 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
223 if (sdb_store_fetch_timeseries(store, hostname, metric, opts, buf)) {
224 sdb_log(SDB_LOG_ERR, "frontend: Failed to fetch time-series");
225 sdb_strbuf_sprintf(errbuf, "Failed to fetch time-series");
226 return -1;
227 }
229 return SDB_CONNECTION_DATA;
230 } /* exec_timeseries */
232 /*
233 * public API
234 */
236 int
237 sdb_store_query_execute(sdb_store_t *store, sdb_store_query_t *q,
238 sdb_strbuf_t *buf, sdb_strbuf_t *errbuf)
239 {
240 sdb_timeseries_opts_t ts_opts;
241 sdb_ast_node_t *ast;
243 if (! q)
244 return -1;
245 if (! q->ast) {
246 sdb_log(SDB_LOG_ERR, "store: Invalid empty query");
247 return -1;
248 }
250 ast = q->ast;
251 switch (ast->type) {
252 case SDB_AST_TYPE_FETCH:
253 return exec_fetch(store, buf, errbuf, SDB_AST_FETCH(ast)->obj_type,
254 SDB_AST_FETCH(ast)->hostname, SDB_AST_FETCH(ast)->name,
255 q->filter);
257 case SDB_AST_TYPE_LIST:
258 return exec_list(store, buf, errbuf, SDB_AST_LIST(ast)->obj_type,
259 q->filter);
261 case SDB_AST_TYPE_LOOKUP:
262 return exec_lookup(store, buf, errbuf, SDB_AST_LOOKUP(ast)->obj_type,
263 q->matcher, q->filter);
265 case SDB_AST_TYPE_TIMESERIES:
266 ts_opts.start = SDB_AST_TIMESERIES(ast)->start;
267 ts_opts.end = SDB_AST_TIMESERIES(ast)->end;
268 return exec_timeseries(store, buf, errbuf,
269 SDB_AST_TIMESERIES(ast)->hostname,
270 SDB_AST_TIMESERIES(ast)->metric, &ts_opts);
272 default:
273 sdb_log(SDB_LOG_ERR, "store: Invalid query of type %s",
274 SDB_AST_TYPE_TO_STRING(ast));
275 return -1;
276 }
278 return 0;
279 } /* sdb_store_query_execute */
281 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */