Code

Let the first operand of ANY/ALL iterators be an expression.
[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                 {
155                         int type;
156                         assert(ITER_M(m)->m);
157                         if ((ITER_M(m)->iter->type == TYPED_EXPR)
158                                         || (ITER_M(m)->iter->type == FIELD_VALUE)) {
159                                 type = (int)ITER_M(m)->iter->data.data.integer;
160                         }
161                         else {
162                                 iter_error(errbuf, m->type, -1, context);
163                                 return -1;
164                         }
165                         if ((context != SDB_HOST)
166                                         && (context != SDB_SERVICE)
167                                         && (context != SDB_METRIC)) {
168                                 iter_error(errbuf, m->type, type, context);
169                                 return -1;
170                         }
171                         if (type == context) {
172                                 iter_error(errbuf, m->type, type, context);
173                                 return -1;
174                         }
175                         if ((type != SDB_SERVICE)
176                                         && (type != SDB_METRIC)
177                                         && (type != SDB_ATTRIBUTE)
178                                         && (type != SDB_FIELD_BACKEND)) {
179                                 iter_error(errbuf, m->type, type, context);
180                                 return -1;
181                         }
182                         if ((context == SDB_SERVICE) && (type == SDB_METRIC)) {
183                                 iter_error(errbuf, m->type, type, context);
184                                 return -1;
185                         }
186                         else if ((context == SDB_METRIC) && (type == SDB_SERVICE)) {
187                                 iter_error(errbuf, m->type, type, context);
188                                 return -1;
189                         }
190                         /* any ary operator will do but these are the once
191                          * we currently support */
192                         if ((ITER_M(m)->m->type != MATCHER_LT)
193                                         && (ITER_M(m)->m->type != MATCHER_LE)
194                                         && (ITER_M(m)->m->type != MATCHER_EQ)
195                                         && (ITER_M(m)->m->type != MATCHER_NE)
196                                         && (ITER_M(m)->m->type != MATCHER_GE)
197                                         && (ITER_M(m)->m->type != MATCHER_GT)
198                                         && (ITER_M(m)->m->type != MATCHER_REGEX)
199                                         && (ITER_M(m)->m->type != MATCHER_NREGEX)) {
200                                 iter_op_error(errbuf, m->type,
201                                                 CMP_M(ITER_M(m)->m)->left->data_type,
202                                                 ITER_M(m)->m->type,
203                                                 CMP_M(ITER_M(m)->m)->right->data_type);
204                                 return -1;
205                         }
206                         if (type == SDB_FIELD_BACKEND) {
207                                 if (CMP_M(ITER_M(m)->m)->right->data_type < 0)
208                                         return 0; /* skip further type checks */
209                                 if (CMP_M(ITER_M(m)->m)->right->data_type & SDB_TYPE_ARRAY) {
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                                 if ((CMP_M(ITER_M(m)->m)->left->data_type & 0xff)
217                                                 != CMP_M(ITER_M(m)->m)->right->data_type) {
218                                         iter_op_error(errbuf, m->type,
219                                                         CMP_M(ITER_M(m)->m)->left->data_type,
220                                                         ITER_M(m)->m->type,
221                                                         CMP_M(ITER_M(m)->m)->right->data_type);
222                                         return -1;
223                                 }
224                         }
225                         else if (analyze_matcher(type, m->type, ITER_M(m)->m, errbuf))
226                                 return -1;
227                         break;
228                 }
230                 case MATCHER_LT:
231                 case MATCHER_LE:
232                 case MATCHER_EQ:
233                 case MATCHER_NE:
234                 case MATCHER_GE:
235                 case MATCHER_GT:
236                         assert(CMP_M(m)->right);
237                         if ((parent_type == MATCHER_ALL)
238                                         || (parent_type == MATCHER_ANY)) {
239                                 // TODO: assert(! CMP_M(m)->left);
240                         }
241                         else {
242                                 assert(CMP_M(m)->left);
243                         }
245                         if (analyze_expr(context, CMP_M(m)->left, errbuf))
246                                 return -1;
247                         if (analyze_expr(context, CMP_M(m)->right, errbuf))
248                                 return -1;
250                         if ((CMP_M(m)->left->data_type > 0)
251                                         && (CMP_M(m)->right->data_type > 0)) {
252                                 if (CMP_M(m)->left->data_type == CMP_M(m)->right->data_type)
253                                         return 0;
254                                 cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
255                                                 CMP_M(m)->right->data_type);
256                                 return -1;
257                         }
258                         if ((CMP_M(m)->left->data_type > 0)
259                                         && (CMP_M(m)->left->data_type & SDB_TYPE_ARRAY)) {
260                                 cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
261                                                 CMP_M(m)->right->data_type);
262                                 return -1;
263                         }
264                         if ((CMP_M(m)->right->data_type > 0)
265                                         && (CMP_M(m)->right->data_type & SDB_TYPE_ARRAY)) {
266                                 cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
267                                                 CMP_M(m)->right->data_type);
268                                 return -1;
269                         }
270                         break;
272                 case MATCHER_IN:
273                 case MATCHER_NIN:
274                         if (analyze_expr(context, CMP_M(m)->left, errbuf))
275                                 return -1;
276                         if (analyze_expr(context, CMP_M(m)->right, errbuf))
277                                 return -1;
279                         /* the left operand may be a scalar or an array but the element
280                          * type has to match */
281                         if ((CMP_M(m)->right->data_type > 0)
282                                         && (! (CMP_M(m)->right->data_type & SDB_TYPE_ARRAY))) {
283                                 cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
284                                                 CMP_M(m)->right->data_type);
285                                 return -1;
286                         }
287                         if ((CMP_M(m)->left->data_type > 0)
288                                         && (CMP_M(m)->right->data_type > 0)) {
289                                 if ((CMP_M(m)->left->data_type & 0xff)
290                                                 != (CMP_M(m)->right->data_type & 0xff)) {
291                                         cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
292                                                         CMP_M(m)->right->data_type);
293                                         return -1;
294                                 }
295                         }
296                         break;
298                 case MATCHER_REGEX:
299                 case MATCHER_NREGEX:
300                         if (analyze_expr(context, CMP_M(m)->left, errbuf))
301                                 return -1;
302                         if (analyze_expr(context, CMP_M(m)->right, errbuf))
303                                 return -1;
305                         /* all types are supported for the left operand */
306                         if ((CMP_M(m)->right->data_type > 0)
307                                         && (CMP_M(m)->right->data_type != SDB_TYPE_REGEX)
308                                         && (CMP_M(m)->right->data_type != SDB_TYPE_STRING)) {
309                                 cmp_error(errbuf, m->type, CMP_M(m)->left->data_type,
310                                                 CMP_M(m)->right->data_type);
311                                 return -1;
312                         }
313                         break;
315                 case MATCHER_ISNULL:
316                 case MATCHER_ISNNULL:
317                         if (analyze_expr(context, ISNULL_M(m)->expr, errbuf))
318                                 return -1;
319                         break;
321                 default:
322                         sdb_strbuf_sprintf(errbuf, "Unknown matcher type %d", m->type);
323                         return -1;
324         }
325         return 0;
326 } /* analyze_matcher */
328 /*
329  * public API
330  */
332 int
333 sdb_fe_analyze(sdb_conn_node_t *node, sdb_strbuf_t *errbuf)
335         sdb_store_matcher_t *m = NULL, *filter = NULL;
336         int context = -1;
337         int status = 0;
339         if (! node)
340                 return -1;
342         /* For now, this function checks basic matcher attributes only;
343          * later, this may be turned into one of multiple AST visitors. */
344         if (node->cmd == SDB_CONNECTION_FETCH) {
345                 conn_fetch_t *fetch = CONN_FETCH(node);
346                 if ((fetch->type == SDB_HOST) && fetch->name) {
347                         sdb_strbuf_sprintf(errbuf, "Unexpected STRING '%s'", fetch->name);
348                         return -1;
349                 }
350                 if ((fetch->type != SDB_HOST) && (! fetch->name)) {
351                         sdb_strbuf_sprintf(errbuf, "Missing %s name",
352                                         SDB_STORE_TYPE_TO_NAME(fetch->type));
353                         return -1;
354                 }
355                 if (fetch->filter)
356                         filter = fetch->filter->matcher;
357                 context = fetch->type;
358         }
359         else if (node->cmd == SDB_CONNECTION_LIST) {
360                 if (CONN_LIST(node)->filter)
361                         filter = CONN_LIST(node)->filter->matcher;
362                 context = CONN_LIST(node)->type;
363         }
364         else if (node->cmd == SDB_CONNECTION_LOOKUP) {
365                 if (CONN_LOOKUP(node)->matcher)
366                         m = CONN_LOOKUP(node)->matcher->matcher;
367                 if (CONN_LOOKUP(node)->filter)
368                         filter = CONN_LOOKUP(node)->filter->matcher;
369                 context = CONN_LOOKUP(node)->type;
370         }
371         else if ((node->cmd == SDB_CONNECTION_STORE_HOST)
372                         || (node->cmd == SDB_CONNECTION_STORE_SERVICE)
373                         || (node->cmd == SDB_CONNECTION_STORE_METRIC)
374                         || (node->cmd == SDB_CONNECTION_STORE_ATTRIBUTE)) {
375                 return 0;
376         }
377         else if (node->cmd == SDB_CONNECTION_TIMESERIES) {
378                 return 0;
379         }
380         else {
381                 sdb_strbuf_sprintf(errbuf,
382                                 "Don't know how to analyze command %#x", node->cmd);
383                 return -1;
384         }
386         if (analyze_matcher(context, -1, m, errbuf))
387                 status = -1;
388         if (analyze_matcher(-1, -1, filter, errbuf))
389                 status = -1;
390         return status;
391 } /* sdb_fe_analyze */
393 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */