Code

Allow the left operand of comparison operations to be NULL (initially).
[sysdb.git] / src / frontend / analyzer.c
1 /*
2  * SysDB - src/frontend/analyzer.c
3  * Copyright (C) 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 #include "sysdb.h"
30 #include "core/store-private.h"
31 #include "frontend/connection-private.h"
32 #include "frontend/parser.h"
33 #include "utils/error.h"
34 #include "utils/strbuf.h"
36 #include <assert.h>
38 /*
39  * private helper functions
40  */
42 static void
43 iter_error(sdb_strbuf_t *errbuf, int op, int oper, int context)
44 {
45         sdb_strbuf_sprintf(errbuf, "Cannot use %s %s in %s context",
46                         MATCHER_SYM(op), SDB_STORE_TYPE_TO_NAME(oper),
47                         SDB_STORE_TYPE_TO_NAME(context));
48 } /* iter_error */
50 static void
51 iter_op_error(sdb_strbuf_t *errbuf, int op,
52                 int iter_type, int cmp, int value_type)
53 {
54         sdb_strbuf_sprintf(errbuf, "Invalid iterator %s %s %s %s",
55                         MATCHER_SYM(op), SDB_TYPE_TO_STRING(iter_type),
56                         MATCHER_SYM(cmp), SDB_TYPE_TO_STRING(value_type));
57         if ((iter_type & 0xff) != value_type)
58                 sdb_strbuf_append(errbuf, " (type mismatch)");
59         else
60                 sdb_strbuf_append(errbuf, " (invalid operator)");
61 } /* iter_op_error */
63 static void
64 cmp_error(sdb_strbuf_t *errbuf, int op, int left, int right)
65 {
66         sdb_strbuf_sprintf(errbuf, "Invalid operator %s for types %s and %s",
67                         MATCHER_SYM(op), SDB_TYPE_TO_STRING(left),
68                         SDB_TYPE_TO_STRING(right));
69 } /* cmp_error */
71 static void
72 op_error(sdb_strbuf_t *errbuf, int op, int left, int right)
73 {
74         sdb_strbuf_sprintf(errbuf, "Invalid operator %s for types %s and %s",
75                         SDB_DATA_OP_TO_STRING(op), SDB_TYPE_TO_STRING(left),
76                         SDB_TYPE_TO_STRING(right));
77 } /* cmp_error */
79 static int
80 analyze_expr(int context, sdb_store_expr_t *e, sdb_strbuf_t *errbuf)
81 {
82         if (! e)
83                 return 0;
85         if ((e->type < TYPED_EXPR) || (SDB_DATA_CONCAT < e->type)) {
86                 sdb_strbuf_sprintf(errbuf, "Invalid expression of type %d", e->type);
87                 return -1;
88         }
90         switch (e->type) {
91                 case TYPED_EXPR:
92                         if (analyze_expr((int)e->data.data.integer, e->left, errbuf))
93                                 return -1;
94                         if (context == (int)e->data.data.integer)
95                                 return 0;
96                         if ((e->data.data.integer == SDB_HOST) &&
97                                         ((context == SDB_SERVICE) || (context == SDB_METRIC)))
98                                 return 0;
99                         sdb_strbuf_sprintf(errbuf, "Invalid expression %s.%s "
100                                         "in %s context",
101                                         SDB_STORE_TYPE_TO_NAME(e->data.data.integer),
102                                         EXPR_TO_STRING(e->left), SDB_STORE_TYPE_TO_NAME(context));
103                         return -1;
105                 case ATTR_VALUE:
106                 case FIELD_VALUE:
107                 case 0:
108                         break;
110                 default:
111                         if (analyze_expr(context, e->left, errbuf))
112                                 return -1;
113                         if (analyze_expr(context, e->right, errbuf))
114                                 return -1;
116                         if ((e->left->data_type > 0) && (e->right->data_type > 0)) {
117                                 if (sdb_data_expr_type(e->type, e->left->data_type,
118                                                         e->right->data_type) < 0) {
119                                         op_error(errbuf, e->type, e->left->data_type,
120                                                         e->right->data_type);
121                                         return -1;
122                                 }
123                         }
124                         break;
125         }
126         return 0;
127 } /* analyze_expr */
129 static int
130 analyze_matcher(int context, int parent_type,
131                 sdb_store_matcher_t *m, sdb_strbuf_t *errbuf)
133         if (! m)
134                 return 0;
136         switch (m->type) {
137                 case MATCHER_OR:
138                 case MATCHER_AND:
139                         assert(OP_M(m)->left && OP_M(m)->right);
140                         if (analyze_matcher(context, m->type, OP_M(m)->left, errbuf))
141                                 return -1;
142                         if (analyze_matcher(context, m->type, OP_M(m)->right, errbuf))
143                                 return -1;
144                         break;
146                 case MATCHER_NOT:
147                         assert(UOP_M(m)->op);
148                         if (analyze_matcher(context, m->type, UOP_M(m)->op, errbuf))
149                                 return -1;
150                         break;
152                 case MATCHER_ANY:
153                 case MATCHER_ALL:
154                         assert(ITER_M(m)->m);
155                         if ((context != SDB_HOST)
156                                         && (context != SDB_SERVICE)
157                                         && (context != SDB_METRIC)) {
158                                 iter_error(errbuf, m->type, ITER_M(m)->type, context);
159                                 return -1;
160                         }
161                         if (ITER_M(m)->type == context) {
162                                 iter_error(errbuf, m->type, ITER_M(m)->type, context);
163                                 return -1;
164                         }
165                         if ((ITER_M(m)->type != SDB_SERVICE)
166                                         && (ITER_M(m)->type != SDB_METRIC)
167                                         && (ITER_M(m)->type != SDB_ATTRIBUTE)
168                                         && (ITER_M(m)->type != SDB_FIELD_BACKEND)) {
169                                 iter_error(errbuf, m->type, ITER_M(m)->type, context);
170                                 return -1;
171                         }
172                         if ((context == SDB_SERVICE)
173                                         && (ITER_M(m)->type == SDB_METRIC)) {
174                                 iter_error(errbuf, m->type, ITER_M(m)->type, context);
175                                 return -1;
176                         }
177                         else if ((context == SDB_METRIC)
178                                         && (ITER_M(m)->type == SDB_SERVICE)) {
179                                 iter_error(errbuf, m->type, ITER_M(m)->type, context);
180                                 return -1;
181                         }
182                         /* any ary operator will do but these are the once
183                          * we currently support */
184                         if ((ITER_M(m)->m->type != MATCHER_LT)
185                                         && (ITER_M(m)->m->type != MATCHER_LE)
186                                         && (ITER_M(m)->m->type != MATCHER_EQ)
187                                         && (ITER_M(m)->m->type != MATCHER_NE)
188                                         && (ITER_M(m)->m->type != MATCHER_GE)
189                                         && (ITER_M(m)->m->type != MATCHER_GT)
190                                         && (ITER_M(m)->m->type != MATCHER_REGEX)
191                                         && (ITER_M(m)->m->type != MATCHER_NREGEX)) {
192                                 iter_op_error(errbuf, m->type,
193                                                 CMP_M(ITER_M(m)->m)->left->data_type,
194                                                 ITER_M(m)->m->type,
195                                                 CMP_M(ITER_M(m)->m)->right->data_type);
196                                 return -1;
197                         }
198                         if (ITER_M(m)->type == SDB_FIELD_BACKEND) {
199                                 if (CMP_M(ITER_M(m)->m)->right->data_type < 0)
200                                         return 0; /* skip further type checks */
201                                 if (CMP_M(ITER_M(m)->m)->right->data_type & SDB_TYPE_ARRAY) {
202                                         iter_op_error(errbuf, m->type,
203                                                         CMP_M(ITER_M(m)->m)->left->data_type,
204                                                         ITER_M(m)->m->type,
205                                                         CMP_M(ITER_M(m)->m)->right->data_type);
206                                         return -1;
207                                 }
208                                 if ((CMP_M(ITER_M(m)->m)->left->data_type & 0xff)
209                                                 != CMP_M(ITER_M(m)->m)->right->data_type) {
210                                         iter_op_error(errbuf, m->type,
211                                                         CMP_M(ITER_M(m)->m)->left->data_type,
212                                                         ITER_M(m)->m->type,
213                                                         CMP_M(ITER_M(m)->m)->right->data_type);
214                                         return -1;
215                                 }
216                         }
217                         else if (analyze_matcher(ITER_M(m)->type, m->type,
218                                                 ITER_M(m)->m, errbuf))
219                                 return -1;
220                         break;
222                 case MATCHER_LT:
223                 case MATCHER_LE:
224                 case MATCHER_EQ:
225                 case MATCHER_NE:
226                 case MATCHER_GE:
227                 case MATCHER_GT:
228                         assert(CMP_M(m)->right);
229                         if ((parent_type == MATCHER_ALL)
230                                         || (parent_type == MATCHER_ANY)) {
231                                 // TODO: assert(! CMP_M(m)->left);
232                         }
233                         else {
234                                 assert(CMP_M(m)->left);
235                         }
237                         if (analyze_expr(context, CMP_M(m)->left, errbuf))
238                                 return -1;
239                         if (analyze_expr(context, CMP_M(m)->right, errbuf))
240                                 return -1;
242                         if ((CMP_M(m)->left->data_type > 0)
243                                         && (CMP_M(m)->right->data_type > 0)) {
244                                 if (CMP_M(m)->left->data_type == CMP_M(m)->right->data_type)
245                                         return 0;
246                                 cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
247                                                 CMP_M(m)->right->data_type);
248                                 return -1;
249                         }
250                         if ((CMP_M(m)->left->data_type > 0)
251                                         && (CMP_M(m)->left->data_type & SDB_TYPE_ARRAY)) {
252                                 cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
253                                                 CMP_M(m)->right->data_type);
254                                 return -1;
255                         }
256                         if ((CMP_M(m)->right->data_type > 0)
257                                         && (CMP_M(m)->right->data_type & SDB_TYPE_ARRAY)) {
258                                 cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
259                                                 CMP_M(m)->right->data_type);
260                                 return -1;
261                         }
262                         break;
264                 case MATCHER_IN:
265                 case MATCHER_NIN:
266                         if (analyze_expr(context, CMP_M(m)->left, errbuf))
267                                 return -1;
268                         if (analyze_expr(context, CMP_M(m)->right, errbuf))
269                                 return -1;
271                         /* the left operand may be a scalar or an array but the element
272                          * type has to match */
273                         if ((CMP_M(m)->right->data_type > 0)
274                                         && (! (CMP_M(m)->right->data_type & SDB_TYPE_ARRAY))) {
275                                 cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
276                                                 CMP_M(m)->right->data_type);
277                                 return -1;
278                         }
279                         if ((CMP_M(m)->left->data_type > 0)
280                                         && (CMP_M(m)->right->data_type > 0)) {
281                                 if ((CMP_M(m)->left->data_type & 0xff)
282                                                 != (CMP_M(m)->right->data_type & 0xff)) {
283                                         cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
284                                                         CMP_M(m)->right->data_type);
285                                         return -1;
286                                 }
287                         }
288                         break;
290                 case MATCHER_REGEX:
291                 case MATCHER_NREGEX:
292                         if (analyze_expr(context, CMP_M(m)->left, errbuf))
293                                 return -1;
294                         if (analyze_expr(context, CMP_M(m)->right, errbuf))
295                                 return -1;
297                         /* all types are supported for the left operand */
298                         if ((CMP_M(m)->right->data_type > 0)
299                                         && (CMP_M(m)->right->data_type != SDB_TYPE_REGEX)
300                                         && (CMP_M(m)->right->data_type != SDB_TYPE_STRING)) {
301                                 cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
302                                                 CMP_M(m)->right->data_type);
303                                 return -1;
304                         }
305                         break;
307                 case MATCHER_ISNULL:
308                 case MATCHER_ISNNULL:
309                         if (analyze_expr(context, ISNULL_M(m)->expr, errbuf))
310                                 return -1;
311                         break;
313                 default:
314                         sdb_strbuf_sprintf(errbuf, "Unknown matcher type %d", m->type);
315                         return -1;
316         }
317         return 0;
318 } /* analyze_matcher */
320 /*
321  * public API
322  */
324 int
325 sdb_fe_analyze(sdb_conn_node_t *node, sdb_strbuf_t *errbuf)
327         sdb_store_matcher_t *m = NULL, *filter = NULL;
328         int context = -1;
329         int status = 0;
331         if (! node)
332                 return -1;
334         /* For now, this function checks basic matcher attributes only;
335          * later, this may be turned into one of multiple AST visitors. */
336         if (node->cmd == SDB_CONNECTION_FETCH) {
337                 conn_fetch_t *fetch = CONN_FETCH(node);
338                 if ((fetch->type == SDB_HOST) && fetch->name) {
339                         sdb_strbuf_sprintf(errbuf, "Unexpected STRING '%s'", fetch->name);
340                         return -1;
341                 }
342                 if ((fetch->type != SDB_HOST) && (! fetch->name)) {
343                         sdb_strbuf_sprintf(errbuf, "Missing %s name",
344                                         SDB_STORE_TYPE_TO_NAME(fetch->type));
345                         return -1;
346                 }
347                 if (fetch->filter)
348                         filter = fetch->filter->matcher;
349                 context = fetch->type;
350         }
351         else if (node->cmd == SDB_CONNECTION_LIST) {
352                 if (CONN_LIST(node)->filter)
353                         filter = CONN_LIST(node)->filter->matcher;
354                 context = CONN_LIST(node)->type;
355         }
356         else if (node->cmd == SDB_CONNECTION_LOOKUP) {
357                 if (CONN_LOOKUP(node)->matcher)
358                         m = CONN_LOOKUP(node)->matcher->matcher;
359                 if (CONN_LOOKUP(node)->filter)
360                         filter = CONN_LOOKUP(node)->filter->matcher;
361                 context = CONN_LOOKUP(node)->type;
362         }
363         else if ((node->cmd == SDB_CONNECTION_STORE_HOST)
364                         || (node->cmd == SDB_CONNECTION_STORE_SERVICE)
365                         || (node->cmd == SDB_CONNECTION_STORE_METRIC)
366                         || (node->cmd == SDB_CONNECTION_STORE_ATTRIBUTE)) {
367                 return 0;
368         }
369         else if (node->cmd == SDB_CONNECTION_TIMESERIES) {
370                 return 0;
371         }
372         else {
373                 sdb_strbuf_sprintf(errbuf,
374                                 "Don't know how to analyze command %#x", node->cmd);
375                 return -1;
376         }
378         if (analyze_matcher(context, -1, m, errbuf))
379                 status = -1;
380         if (analyze_matcher(-1, -1, filter, errbuf))
381                 status = -1;
382         return status;
383 } /* sdb_fe_analyze */
385 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */