CUBRID Engine  latest
cnf.c
Go to the documentation of this file.
1 /*
2  * Copyright 2008 Search Solution Corporation
3  * Copyright 2016 CUBRID Corporation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 /*
20  * cnf.c - convert arbitrary boolean expressions to conjunctive normal form
21  */
22 
23 #ident "$Id$"
24 
25 #include <stdarg.h>
26 #include <ctype.h>
27 #include <assert.h>
28 
29 #include "error_manager.h"
30 #include "parser.h"
31 
33 {
35  TRANSFORM_CNF_OR_PRUNE = 1, /* -- not used */
37 };
38 typedef enum cnf_mode CNF_MODE;
39 
40 typedef struct find_id_info FIND_ID_INFO;
42 {
43  UINTPTR id;
44  UINTPTR join_spec;
46  bool found;
48 };
49 
52 {
53  int max_number_of_nodes; /* the max number of nodes */
54  int number_of_nodes; /* the number of nodes */
55  unsigned int accumulated_opcode; /* the accumulated value of op type */
56  unsigned int accumulated_node_type; /* the accumulated value of node type */
57 };
58 
61 #if defined(ENABLE_UNUSED_FUNCTION)
62 static PT_NODE *pt_aof_to_cnf (PARSER_CONTEXT * parser, PT_NODE * node);
63 static PT_NODE *pt_distributes_disjunction (PARSER_CONTEXT * parser, PT_NODE * node_1, PT_NODE * node_2);
64 static PT_NODE *pt_flatten_and_or (PARSER_CONTEXT * parser, PT_NODE * node);
65 #endif /* ENABLE_UNUSED_FUNCTION */
66 static int count_and_or (PARSER_CONTEXT * parser, const PT_NODE * node);
67 static PT_NODE *pt_transform_cnf_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
68 static PT_NODE *pt_transform_cnf_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
69 static PT_NODE *pt_find_name_id_pre (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
70 static PT_NODE *pt_find_name_id_post (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
71 static void pt_tag_term_with_id (PARSER_CONTEXT * parser, PT_NODE * term, UINTPTR id, UINTPTR join_spec,
72  bool tag_subqueries);
73 static void pt_tag_terms_with_id (PARSER_CONTEXT * parser, PT_NODE * terms, UINTPTR id, UINTPTR join_spec);
74 static void pt_tag_terms_with_specs (PARSER_CONTEXT * parser, PT_NODE * terms, PT_NODE * join_spec, UINTPTR join_id);
75 static PT_NODE *pt_tag_start_of_cnf_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
76 static PT_NODE *pt_calculate_similarity (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
77 
78 
79 /*
80  * pt_tag_start_of_cnf_post() - labels the node as CNF start if it has
81  * logical descendants (next / or_next)
82  * and removes their is_cnf_start label.
83  * This way, only the actual cnf start
84  * node gets to keep its label.
85  */
86 static PT_NODE *
87 pt_tag_start_of_cnf_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
88 {
89  if (node == NULL || node->type_enum != PT_TYPE_LOGICAL)
90  {
91  return node;
92  }
93 
94  if (node->next && node->next->type_enum == PT_TYPE_LOGICAL)
95  {
96  node->next->flag.is_cnf_start = false;
97  }
98 
99  if (node->or_next && node->or_next->type_enum == PT_TYPE_LOGICAL)
100  {
101  node->or_next->flag.is_cnf_start = false;
102  }
103 
104  node->flag.is_cnf_start = true;
105  return node;
106 }
107 
108 
109 /*
110  * pt_and_or_form () - Converts a parse tree of boolean expressions into
111  * an equivalent tree which is in and-or form. the basic algorithm is to
112  * recursively push in negation.
113  * return: a pointer to a node of type PT_EXPR
114  * parser(in):
115  * node(in/out): a parse tree node of type PT_EXPR
116  */
117 static PT_NODE *
119 {
120  PT_NODE *arg1, *temp;
121  PT_OP_TYPE neg_op_type;
122 
123  switch (node->info.expr.op)
124  {
125  case PT_NOT:
126  /* unfold NOT expression */
127  arg1 = node->info.expr.arg1;
128  switch (arg1->info.expr.op)
129  {
130  case PT_NOT:
131  /* NOT (NOT expr) == expr */
132  node = pt_and_or_form (parser, arg1->info.expr.arg1);
133 
134  /* free NOT node(arg1) */
135  arg1->info.expr.arg1 = NULL;
136  arg1->next = NULL;
137  arg1->or_next = NULL;
138  parser_free_tree (parser, arg1);
139  break;
140 
141  case PT_AND:
142  /* NOT (expr AND expr) == (NOT expr) OR (NOT expr) */
143  node->info.expr.op = PT_OR;
144  temp = pt_negate_expr (parser, arg1->info.expr.arg1);
145  node->info.expr.arg1 = pt_and_or_form (parser, temp);
146  temp = pt_negate_expr (parser, arg1->info.expr.arg2);
147  node->info.expr.arg2 = pt_and_or_form (parser, temp);
148  break;
149 
150  case PT_OR:
151  /* NOT (expr OR expr) == (NOT expr) AND (NOT expr) */
152  node->info.expr.op = PT_AND;
153  temp = pt_negate_expr (parser, arg1->info.expr.arg1);
154  node->info.expr.arg1 = pt_and_or_form (parser, temp);
155  temp = pt_negate_expr (parser, arg1->info.expr.arg2);
156  node->info.expr.arg2 = pt_and_or_form (parser, temp);
157  break;
158 
159  default:
160  neg_op_type = pt_negate_op (arg1->info.expr.op);
161  if (neg_op_type != 0)
162  {
163  arg1->info.expr.op = neg_op_type;
164 
165  /* free NOT node(node) */
166  node->info.expr.arg1 = NULL;
167  node->next = NULL;
168  node->or_next = NULL;
169  parser_free_tree (parser, node);
170  node = arg1;
171  }
172  break;
173  } /* switch (arg1->info.expr.op) */
174  break;
175 
176  case PT_AND:
177  case PT_OR:
178  node->info.expr.arg1 = pt_and_or_form (parser, node->info.expr.arg1);
179  node->info.expr.arg2 = pt_and_or_form (parser, node->info.expr.arg2);
180  break;
181 
182  default:
183  break;
184  } /* switch (node->info.expr.op) */
185 
186  return node;
187 }
188 
189 
190 /*
191  * pt_negate_expr () - Converts a parse tree of boolean expressions into
192  * an equivalent tree which is in conjunctive normal form
193  * return: returns a pointer to a node of type PT_EXPR which itself is
194  * an expression of type PT_TYPE_LOGICAL except that it is negated.
195  * parser(in):
196  * node(in): a parse tree node of type PT_EXPR
197  *
198  * Note :
199  * the expression is created by creating a new node of type PT_EXPR and
200  * assigning new_node->arg1 the original node which was input,
201  * and assigning new_node->op the enum value PT_NOT
202  */
203 
204 static PT_NODE *
206 {
207  PT_NODE *temp;
208  PT_OP_TYPE neg_op_type;
209 
210  neg_op_type = pt_negate_op (node->info.expr.op);
211  if (neg_op_type == 0)
212  {
213  if (node->info.expr.op == PT_NOT)
214  {
215  /* special case; negating 'NOT expr' */
216  temp = node->info.expr.arg1;
217  node->info.expr.arg1 = NULL;
218  node->next = NULL;
219  node->or_next = NULL;
220  parser_free_tree (parser, node);
221  node = temp;
222  }
223  else
224  {
225  /* making 'NOT expr' */
226  temp = parser_new_node (parser, PT_EXPR);
227  temp->type_enum = PT_TYPE_LOGICAL;
228  temp->info.expr.paren_type = node->info.expr.paren_type;
229  temp->info.expr.op = PT_NOT;
230  temp->info.expr.arg1 = node;
231  temp->info.expr.location = node->info.expr.location;
232  node = temp;
233  }
234  }
235  else
236  {
237  /* making 'arg1 <negated op> arg2' */
238  node->info.expr.op = neg_op_type;
239  }
240 
241  return node;
242 }
243 
244 #if defined(ENABLE_UNUSED_FUNCTION)
245 /*
246  * pt_aof_to_cnf () - Converts a parse tree of boolean expressions already in
247  * and-or-form into an equivalent tree which is in conjunctive normal form
248  * return: returns a pointer to a node of type PT_EXPR which itself
249  * is an expression of type PT_TYPE_LOGICAL.
250  * parser(in):
251  * node(in): a parse tree node of type PT_EXPR
252  */
253 
254 static PT_NODE *
255 pt_aof_to_cnf (PARSER_CONTEXT * parser, PT_NODE * node)
256 {
257  PT_NODE *result = node;
258 
259  if (node->node_type != PT_EXPR && node->node_type != PT_VALUE)
260  {
261  PT_INTERNAL_ERROR (parser, "cnf");
262  }
263 
264  switch (node->info.expr.op)
265  {
266 
267  case PT_AND:
268  result->info.expr.arg1 = pt_aof_to_cnf (parser, node->info.expr.arg1);
269  result->info.expr.arg2 = pt_aof_to_cnf (parser, node->info.expr.arg2);
270  break;
271 
272  case PT_OR:
273  result =
274  pt_distributes_disjunction (parser, pt_aof_to_cnf (parser, node->info.expr.arg1),
275  pt_aof_to_cnf (parser, node->info.expr.arg2));
276  break;
277  default:
278  break;
279  }
280  result->spec_ident = 0;
281 
282  return result;
283 }
284 
285 /*
286  * pt_distributes_disjunction () - distributes disjunction such that (P and Q)
287  * or R == (P or R) and (Q or R). If the two nodes are atoms or
288  * disjunctions, then a disjunctive expression is returned
289  * return: returns a pointer to a node of type PT_EXPR
290  * parser(in):
291  * node_1(in): a parse tree node of type PT_EXPR
292  * node_2(in): a parse tree node of type PT_EXPR
293  */
294 static PT_NODE *
295 pt_distributes_disjunction (PARSER_CONTEXT * parser, PT_NODE * node_1, PT_NODE * node_2)
296 {
297  PT_NODE *new_node, *temp_1, *temp_2;
298 
299  new_node = parser_new_node (parser, PT_EXPR);
300  if (new_node == NULL)
301  {
302  return NULL;
303  }
304 
305  if (node_1->info.expr.op == PT_AND)
306  {
307  temp_1 = parser_new_node (parser, PT_EXPR);
308  if (temp_1 == NULL)
309  {
310  return NULL;
311  }
312 
313  temp_1->type_enum = PT_TYPE_LOGICAL;
314  temp_1->info.expr.paren_type = 1;
315 
316  temp_1->info.expr.op = PT_OR;
317  temp_1->info.expr.arg1 = node_1->info.expr.arg1;
318  temp_1->info.expr.arg2 = node_2;
319  temp_1->info.expr.location = node_1->info.expr.location;
320 
321  temp_2 = parser_new_node (parser, PT_EXPR);
322  if (temp_2 == NULL)
323  {
324  return NULL;
325  }
326 
327  temp_2->type_enum = PT_TYPE_LOGICAL;
328  temp_2->info.expr.paren_type = 1;
329 
330  temp_2->info.expr.op = PT_OR;
331  temp_2->info.expr.arg1 = node_1->info.expr.arg2;
332 
333  /* need to keep it a tree -- so copy */
334  temp_2->info.expr.arg2 = parser_copy_tree (parser, node_2);
335  temp_2->info.expr.location = node_1->info.expr.location;
336 
337  new_node->type_enum = PT_TYPE_LOGICAL;
338  new_node->info.expr.paren_type = node_1->info.expr.paren_type;
339  new_node->info.expr.op = PT_AND;
340 
341  new_node->info.expr.arg1 = pt_aof_to_cnf (parser, temp_1);
342  new_node->info.expr.arg2 = pt_aof_to_cnf (parser, temp_2);
343  new_node->info.expr.location = node_1->info.expr.location;
344  }
345  else if (node_2->info.expr.op == PT_AND)
346  {
347  temp_1 = parser_new_node (parser, PT_EXPR);
348  if (temp_1 == NULL)
349  {
350  return NULL;
351  }
352 
353  temp_1->type_enum = PT_TYPE_LOGICAL;
354  temp_1->info.expr.paren_type = 1;
355 
356  temp_1->info.expr.op = PT_OR;
357  temp_1->info.expr.arg1 = node_2->info.expr.arg1;
358  temp_1->info.expr.arg2 = node_1;
359  temp_1->info.expr.location = node_2->info.expr.location;
360 
361  temp_2 = parser_new_node (parser, PT_EXPR);
362  if (temp_2 == NULL)
363  {
364  return NULL;
365  }
366 
367  temp_2->type_enum = PT_TYPE_LOGICAL;
368  temp_2->info.expr.paren_type = 1;
369 
370  temp_2->info.expr.op = PT_OR;
371  temp_2->info.expr.arg1 = node_2->info.expr.arg2;
372 
373  /* need to keep it a tree -- so copy */
374  temp_2->info.expr.arg2 = parser_copy_tree (parser, node_1);
375  temp_2->info.expr.location = node_2->info.expr.location;
376 
377  new_node->type_enum = PT_TYPE_LOGICAL;
378  new_node->info.expr.paren_type = node_2->info.expr.paren_type;
379  new_node->info.expr.op = PT_AND;
380 
381  new_node->info.expr.arg1 = pt_aof_to_cnf (parser, temp_1);
382  new_node->info.expr.arg2 = pt_aof_to_cnf (parser, temp_2);
383  new_node->info.expr.location = node_2->info.expr.location;
384  }
385  else
386  {
387  new_node->type_enum = PT_TYPE_LOGICAL;
388  new_node->info.expr.paren_type = 1;
389  new_node->info.expr.op = PT_OR;
390 
391  new_node->info.expr.arg1 = node_1;
392  new_node->info.expr.arg2 = node_2;
393  new_node->info.expr.location = node_1->info.expr.location;
394  }
395 
396  return new_node;
397 }
398 
399 /*
400  * pt_flatten_and_or () -
401  * return: a list of conjuncts which are implicitly anded together
402  * parser(in):
403  * node(in/out): a parse tree node of type PT_EXPR
404  */
405 
406 static PT_NODE *
407 pt_flatten_and_or (PARSER_CONTEXT * parser, PT_NODE * node)
408 {
409  PT_NODE *list;
410 
411  assert (parser != NULL && node != NULL);
412 
413  if (node->node_type != PT_EXPR && node->node_type != PT_VALUE)
414  {
415  PT_INTERNAL_ERROR (parser, "cnf");
416  }
417 
418  list = node;
419 
420  if (node->info.expr.op == PT_AND)
421  {
422  /* convert left part of AND into CNF */
423  list = pt_flatten_and_or (parser, node->info.expr.arg1);
424 
425  /* convert right part of AND into CNF */
426  list = parser_append_node (pt_flatten_and_or (parser, node->info.expr.arg2), list);
427 
428  /* free the AND node */
429  node->info.expr.arg1 = NULL;
430  node->info.expr.arg2 = NULL;
431  node->next = NULL;
432  node->or_next = NULL;
433  parser_free_tree (parser, node);
434  }
435  else if (node->info.expr.op == PT_OR)
436  {
437  /* convert left part of OR into CNF */
438  list = pt_flatten_and_or (parser, node->info.expr.arg1);
439 
440  /* convert right part of OR into CNF */
441  list = parser_append_node_or (pt_flatten_and_or (parser, node->info.expr.arg2), list);
442 
443  /* free the OR node */
444  node->info.expr.arg1 = NULL;
445  node->info.expr.arg2 = NULL;
446  node->next = NULL;
447  node->or_next = NULL;
448  parser_free_tree (parser, node);
449  }
450 
451  return list;
452 }
453 #endif /* ENABLE_UNUSED_FUNCTION */
454 
455 /*
456  * count_and_or () -
457  * return:
458  * parser(in):
459  * node(in):
460  */
461 static int
462 count_and_or (PARSER_CONTEXT * parser, const PT_NODE * node)
463 {
464  int cnf_cnt, left, right;
465 
466  switch (node->info.expr.op)
467  {
468  case PT_AND:
469  left = count_and_or (parser, node->info.expr.arg1);
470  if (left > 100) /* pruning */
471  {
472  return left;
473  }
474  right = count_and_or (parser, node->info.expr.arg2);
475  cnf_cnt = left + right;
476  break;
477  case PT_OR:
478  left = count_and_or (parser, node->info.expr.arg1);
479  if (left > 100) /* pruning */
480  {
481  return left;
482  }
483  right = count_and_or (parser, node->info.expr.arg2);
484  cnf_cnt = left * right;
485  break;
486  default:
487  cnf_cnt = 1;
488  break;
489  }
490 
491  return cnf_cnt;
492 }
493 
494 /*
495  * pt_transform_cnf_pre () -
496  * return:
497  * parser(in):
498  * node(in):
499  * arg(in):
500  * continue_walk(in/out):
501  */
502 static PT_NODE *
503 pt_transform_cnf_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
504 {
505  CNF_MODE *mode = (CNF_MODE *) arg;
506 
507  if (!node || node->node_type != PT_EXPR)
508  {
509  if (node && node->node_type == PT_SELECT)
510  {
511  /* meet subquery in search condition. prune and go ahead */
512  *continue_walk = PT_STOP_WALK;
513  }
514  return node;
515  }
516 
517  switch (node->info.expr.op)
518  {
519  case PT_AND:
520  break;
521  case PT_OR:
522  /* OR-tree prune mode */
523  if (*mode == TRANSFORM_CNF_OR_PRUNE)
524  {
525  *continue_walk = PT_STOP_WALK;
526  }
527  break;
528  default:
529  break;
530  }
531 
532  return node;
533 }
534 
535 /*
536  * pt_calculate_similarity () - calculate the similarity of the pt_node
537  * for quick comparision.
538  * return: PT_NODE
539  * parser(in):
540  * node(in):
541  * arg(in):
542  * continue_walk(in/out)
543  */
544 static PT_NODE *
545 pt_calculate_similarity (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
546 {
547  SIMILARITY_CONTEXT *ctx = (SIMILARITY_CONTEXT *) arg;
548 
549  ctx->number_of_nodes++;
550  if (ctx->max_number_of_nodes != -1 && ctx->number_of_nodes > ctx->max_number_of_nodes)
551  {
552  *continue_walk = PT_STOP_WALK;
553  return node;
554  }
555 
557  ctx->accumulated_node_type += node->node_type;
558  if (node->node_type == PT_EXPR)
559  {
561  ctx->accumulated_opcode += node->info.expr.op;
562  }
563 
564  if (node->flag.is_cnf_start)
565  {
566  *continue_walk = PT_CONTINUE_WALK;
567  }
568  else
569  {
570  *continue_walk = PT_LEAF_WALK;
571  }
572  return node;
573 }
574 
575 /*
576  * pt_transform_cnf_post () -
577  * return: CNF/DNF list
578  * parser(in):
579  * node(in):
580  * arg(in):
581  * continue_walk(in/out):
582  */
583 static PT_NODE *
584 pt_transform_cnf_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
585 {
586  PT_NODE *list, *lhs, *rhs, *last, *conj, *temp, *lhs_next, *rhs_next;
587  CNF_MODE *mode = (CNF_MODE *) arg;
588  unsigned int save_custom;
589  char *lhs_str, *rhs_str;
590  SIMILARITY_CONTEXT lhs_ctx, rhs_ctx;
591  PT_NODE *common_list, *lhs_prev, *rhs_prev, *arg1, *arg2;
592  bool common_found;
593  int num_markers;
594  int lhs_count, rhs_count;
595 
596  if (!node || node->node_type != PT_EXPR)
597  {
598  if (node && node->node_type == PT_SELECT)
599  {
600  /* meet subquery in search condition. prune and go ahead */
601  *continue_walk = PT_CONTINUE_WALK;
602  }
603  return node;
604  }
605 
606  switch (node->info.expr.op)
607  {
608 
609  case PT_AND:
610  /* LHS CNF/DNF list AND RHS CNF/DNF list ==> LHS -(next)-> RHS */
611 
612  /* append RHS list to LHS list */
613  list = node->info.expr.arg1;
614  for (last = list; last->next; last = last->next)
615  {
616  ;
617  }
618  last->next = node->info.expr.arg2;
619 
620  /* free AND node excluding LHS list and RHS list */
621  node->info.expr.arg1 = node->info.expr.arg2 = NULL;
622 
623  /* AND node should not have 'next' and 'or_next' node->next = node->or_next = NULL */
624  parser_free_tree (parser, node);
625  break;
626 
627  case PT_OR:
628  /* OR-tree prune mode */
629  if (*mode == TRANSFORM_CNF_OR_PRUNE)
630  {
631  list = node;
632  /* '*continue_walk' was changed to PT_STOP_WALK by pt_transform_cnf_pre() to prune OR-tree */
633  *continue_walk = PT_CONTINUE_WALK;
634  break;
635  }
636 
637 
638  save_custom = parser->custom_print;
639  parser->custom_print |= PT_CONVERT_RANGE;
640 
641  common_list = NULL;
642 
643  for (lhs = node->info.expr.arg1, lhs_count = 0; lhs; lhs = lhs->next)
644  {
645  ++lhs_count;
646  }
647  for (rhs = node->info.expr.arg2, rhs_count = 0; rhs; rhs = rhs->next)
648  {
649  ++rhs_count;
650  }
651 
652  /* traverse LHS list */
653  lhs_prev = NULL;
654  for (lhs = node->info.expr.arg1; lhs; lhs = lhs_next)
655  {
656  lhs_next = lhs->next;
657 
658  num_markers = 0;
659  (void) parser_walk_tree (parser, lhs, pt_count_input_markers, &num_markers, NULL, NULL);
660  if (num_markers > 0)
661  { /* found input marker, give up */
662  lhs_prev = lhs;
663  continue;
664  }
665 
666  common_found = false;
667 
668  lhs_str = NULL;
669 
670  lhs_ctx.max_number_of_nodes = -1;
671  lhs_ctx.number_of_nodes = 0;
672  lhs_ctx.accumulated_opcode = 0;
673  lhs_ctx.accumulated_node_type = 0;
674  (void) parser_walk_tree (parser, lhs, pt_calculate_similarity, &lhs_ctx, NULL, NULL);
675 
676  /* traverse RHS list */
677  rhs_prev = NULL;
678  for (rhs = node->info.expr.arg2; rhs; rhs = rhs_next)
679  {
680  rhs_next = rhs->next;
681 
682  rhs_ctx.max_number_of_nodes = lhs_ctx.number_of_nodes;
683  rhs_ctx.number_of_nodes = 0;
684  rhs_ctx.accumulated_opcode = 0;
685  rhs_ctx.accumulated_node_type = 0;
686  (void) parser_walk_tree (parser, rhs, pt_calculate_similarity, &rhs_ctx, NULL, NULL);
687 
688  /*
689  * Because the cost of parser_print_tree() is very high. we use
690  * a simple way to test whether lhs and rhs node are similar.
691  * Only when they are similar, we call parser_print_tree().
692  */
693  if (lhs_ctx.number_of_nodes == rhs_ctx.number_of_nodes
694  && lhs_ctx.accumulated_node_type == rhs_ctx.accumulated_node_type
695  && lhs_ctx.accumulated_opcode == rhs_ctx.accumulated_opcode)
696  {
697  if (lhs_str == NULL)
698  {
699  /* get parse tree string */
700  lhs_str = parser_print_tree (parser, lhs);
701  }
702  /* get parse tree string */
703  rhs_str = parser_print_tree (parser, rhs);
704 
705  if (!pt_str_compare (lhs_str, rhs_str, CASE_SENSITIVE))
706  { /* found common cnf */
707  common_found = true;
708  break;
709  }
710  }
711 
712  rhs_prev = rhs;
713  }
714 
715  if (common_found)
716  {
717  common_list = parser_append_node (parser_copy_tree (parser, lhs), common_list);
718 
719  /* delete from lhs list */
720  if (lhs_prev == NULL)
721  {
722  node->info.expr.arg1 = lhs_next;
723  }
724  else
725  {
726  lhs_prev->next = lhs_next;
727  }
728 
729  /* free compacted node */
730  lhs->next = NULL;
731  parser_free_tree (parser, lhs);
732 
733  /* delete from rhs list */
734  if (rhs_prev == NULL)
735  {
736  node->info.expr.arg2 = rhs_next;
737  }
738  else
739  {
740  rhs_prev->next = rhs_next;
741  }
742 
743  /* free compacted node */
744  rhs->next = NULL;
745  parser_free_tree (parser, rhs);
746 
747  continue;
748  }
749 
750  lhs_prev = lhs;
751  }
752 
753  parser->custom_print = save_custom; /* restore */
754 
755  lhs = node->info.expr.arg1;
756  rhs = node->info.expr.arg2;
757 
758  /* fully compacted */
759  if (lhs == NULL || rhs == NULL)
760  {
761  /* free OR node excluding LHS list and RHS list */
762  node->info.expr.arg1 = node->info.expr.arg2 = NULL;
763 
764  /* OR node should not have 'next' and 'or_next' node->next = node->or_next = NULL */
765  parser_free_tree (parser, node);
766 
767  /* A and B or B ==> B and (A or true) ==> B */
768  list = common_list;
769  break;
770  }
771 
772  /* OR-tree compact mode */
773  if (*mode == TRANSFORM_CNF_OR_COMPACT)
774  {
775  arg1 = arg2 = NULL; /* init */
776 
777  /* rollback to AND-OR tree */
778  if (lhs)
779  { /* build AND-tree */
780  lhs_next = lhs->next;
781  lhs->next = NULL;
782 
783  arg1 = lhs;
784 
785  for (lhs = lhs_next; lhs; lhs = lhs_next)
786  {
787  lhs_next = lhs->next;
788  lhs->next = NULL;
789 
790  arg1 = pt_and (parser, arg1, lhs);
791  }
792  if (arg1 && arg1->info.expr.op == PT_AND)
793  {
794  arg1->info.expr.paren_type = 1;
795  }
796  }
797 
798  if (rhs)
799  { /* build AND-tree */
800  rhs_next = rhs->next;
801  rhs->next = NULL;
802 
803  arg2 = rhs;
804 
805  for (rhs = rhs_next; rhs; rhs = rhs_next)
806  {
807  rhs_next = rhs->next;
808  rhs->next = NULL;
809 
810  arg2 = pt_and (parser, arg2, rhs);
811  }
812  if (arg2 && arg2->info.expr.op == PT_AND)
813  {
814  arg2->info.expr.paren_type = 1;
815  }
816  }
817 
818  if (arg1 && arg2)
819  { /* build OR-tree */
820  list = pt_and (parser, arg1, arg2);
821  list->info.expr.op = PT_OR;
822  }
823  else
824  {
825  /* A and B or B ==> B and (A or true) ==> B */
826  list = NULL;
827  }
828 
829  if (list != NULL && list->info.expr.op == PT_OR)
830  {
831  list->info.expr.paren_type = 1;
832  }
833 
834  list = parser_append_node (list, common_list);
835 
836  break;
837  }
838 
839  /* redo cnf transformation */
840  lhs = node->info.expr.arg1;
841  rhs = node->info.expr.arg2;
842 
843  if (lhs->next == NULL && rhs->next == NULL)
844  {
845  /* special case; one LHS node OR one RHS node ==> LHS -(or_next)-> RHS */
846 
847  /* append RHS node to LHS node */
848  list = node->info.expr.arg1;
849  for (temp = list; temp->or_next; temp = temp->or_next)
850  {
851  ;
852  }
853 
854  temp->or_next = node->info.expr.arg2;
855 
856  /* free OR node excluding LHS list and RHS list */
857  node->info.expr.arg1 = node->info.expr.arg2 = NULL;
858 
859  /* OR node should not have 'next' and 'or_next' node->next = node->or_next = NULL */
860  parser_free_tree (parser, node);
861  }
862  else
863  {
864  list = last = NULL;
865 
866  /* traverse LHS list */
867  for (lhs = node->info.expr.arg1; lhs; lhs = lhs_next)
868  {
869  lhs_next = lhs->next;
870  lhs->next = NULL; /* cut off link temporarily */
871 
872  /* traverse RHS list */
873  for (rhs = node->info.expr.arg2; rhs; rhs = rhs_next)
874  {
875  rhs_next = rhs->next;
876  rhs->next = NULL; /* cut off link temporarily */
877 
878  /* clone LHS node (conjunctive) if RHS is the last node, this LHS is the last one to be used; so
879  * point to directly without cloning */
880  if (rhs_next == NULL)
881  conj = lhs;
882  else
883  conj = parser_copy_tree_list (parser, lhs);
884 
885  /* append RHS node clone to LHS node clone */
886  for (temp = conj; temp->or_next; temp = temp->or_next)
887  {
888  ;
889  }
890  /* if LHS is the last node, this RHS is the last one to be used; so point to directly without cloning
891  */
892  if (lhs_next == NULL)
893  {
894  temp->or_next = rhs;
895  }
896  else
897  {
898  temp->or_next = parser_copy_tree_list (parser, rhs);
899  rhs->next = rhs_next; /* relink RHS list */
900  }
901 
902  /* and then link the conjunctive to the CNF list */
903  if (last)
904  last->next = conj;
905  else
906  list = last = conj;
907 
908  for (last = conj; last->next; last = last->next)
909  {
910  ;
911  }
912  } /* for (rhs = ...) */
913  } /* for (lhs = ...) */
914 
915  /* free OR node excluding LHS list and RHS list */
916  node->info.expr.arg1 = node->info.expr.arg2 = NULL;
917 
918  /* OR node should not have 'next' and 'or_next' node->next = node->or_next = NULL */
919  parser_free_tree (parser, node);
920  }
921 
922  list = parser_append_node (list, common_list);
923  break;
924 
925  default:
926  list = node;
927  break;
928  } /* switch (node->info.expr.op) */
929 
930  /* return CNF/DNF list */
931  return list;
932 }
933 
934 /*
935  * pt_cnf () -
936  * return:
937  * parser(in):
938  * node(in/out):
939  */
940 PT_NODE *
941 pt_cnf (PARSER_CONTEXT * parser, PT_NODE * node)
942 {
943  PT_NODE *list, *cnf, *next, *last;
944  CNF_MODE mode;
945 
946  if (node == NULL)
947  {
948  return node;
949  }
950 
951  list = last = NULL;
952  do
953  {
954  /* isolate this node */
955  next = node->next;
956  node->next = NULL;
957 
959  {
960  /* if it is a value, it should be a const value which was a const expression and folded as a const value. if
961  * the case, skip this node and go ahead. */
962 
963  /* if it is already in CNF */
964  cnf = node;
965  /* and then link it to the CNF list */
966  if (last)
967  {
968  last->next = cnf;
969  }
970  else
971  {
972  list = last = cnf;
973  }
974 
975  /* adjust last pointer */
976  for (last = cnf; last->next; last = last->next)
977  {
978  ;
979  }
980  }
981  else
982  {
983  /* transform the tree to AND-OR form which does not have NOT expression */
984  node = pt_and_or_form (parser, node);
985 
986  /* if the number of result nodes of CNF list to be made is too big, do CNF transformation in OR-tree prune
987  * mode */
988  mode = (count_and_or (parser, node) > 100) ? TRANSFORM_CNF_OR_COMPACT : TRANSFORM_CNF_AND_OR;
989 
990  /* transform the tree to CNF list */
991  cnf = parser_walk_tree (parser, node, pt_transform_cnf_pre, &mode, pt_transform_cnf_post, &mode);
992  /* and then link it to the CNF list */
993  if (last)
994  {
995  last->next = cnf;
996  }
997  else
998  {
999  list = last = cnf;
1000  }
1001 
1002  /* adjust last pointer and mark that they have been transformed */
1003  for (last = cnf; last->next; last = last->next)
1004  {
1006  }
1008  }
1009 
1010  /* next node */
1011  node = next;
1012  }
1013  while (next);
1014 
1015  list = parser_walk_tree (parser, list, NULL, NULL, pt_tag_start_of_cnf_post, NULL);
1016  return list;
1017 }
1018 
1019 
1020 /*
1021  * pt_find_name_id_pre () -
1022  * return: true if node is a name with the given spec id
1023  * parser(in):
1024  * tree(in):
1025  * arg(in/out):
1026  * continue_walk(in/out):
1027  */
1028 static PT_NODE *
1029 pt_find_name_id_pre (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
1030 {
1031  FIND_ID_INFO *info = (FIND_ID_INFO *) arg;
1032 
1033  *continue_walk = PT_CONTINUE_WALK;
1034 
1035  if (tree->node_type == PT_NAME && tree->info.name.spec_id == info->id)
1036  {
1037  info->found = true;
1038  }
1039  else
1040  {
1041  if (PT_IS_QUERY_NODE_TYPE (tree->node_type))
1042  {
1043  if (tree->info.query.correlation_level == 1 && info->tag_subqueries && !info->in_query)
1044  {
1045  info->in_query = tree;
1046 
1047  /* if this subquery is correlated 1, test it for tagging */
1048  pt_tag_term_with_id (parser, tree, info->id, info->join_spec, false);
1049  }
1050  else if (tree->info.query.correlation_level == 0)
1051  {
1052  *continue_walk = PT_LIST_WALK;
1053  }
1054  }
1055  }
1056 
1057  return tree;
1058 }
1059 
1060 
1061 /*
1062  * pt_find_name_id_post () - Resets the query node to zero on the way back up
1063  * return:
1064  * parser(in):
1065  * tree(in):
1066  * arg(in/out):
1067  * continue_walk(in/out):
1068  */
1069 static PT_NODE *
1070 pt_find_name_id_post (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
1071 {
1072  FIND_ID_INFO *info = (FIND_ID_INFO *) arg;
1073 
1074  *continue_walk = PT_CONTINUE_WALK;
1075 
1076  if (info->in_query == tree)
1077  {
1078  info->in_query = NULL;
1079  }
1080 
1081  return tree;
1082 }
1083 
1084 
1085 /*
1086  * pt_tag_term_with_id () -
1087  * return:
1088  * parser(in):
1089  * term(in/out): CNF tree
1090  * id(in): spec id to test and tag term with
1091  * join_spec(in):
1092  * tag_subqueries(in):
1093  */
1094 static void
1095 pt_tag_term_with_id (PARSER_CONTEXT * parser, PT_NODE * term, UINTPTR id, UINTPTR join_spec, bool tag_subqueries)
1096 {
1097  FIND_ID_INFO info;
1098 
1099  info.found = 0;
1100  info.id = id;
1101  info.join_spec = join_spec;
1103  info.in_query = NULL;
1104 
1105  parser_walk_leaves (parser, term, pt_find_name_id_pre, &info, pt_find_name_id_post, &info);
1106  if (info.found)
1107  {
1108  term->spec_ident = join_spec;
1109  }
1110 }
1111 
1112 
1113 /*
1114  * pt_tag_terms_with_id () -
1115  * return:
1116  * parser(in):
1117  * terms(in/out): CNF tree
1118  * id(in): spec id to test and tag term with
1119  * join_spec(in):
1120  */
1121 static void
1122 pt_tag_terms_with_id (PARSER_CONTEXT * parser, PT_NODE * terms, UINTPTR id, UINTPTR join_spec)
1123 {
1124  while (terms)
1125  {
1126  if (terms->node_type == PT_EXPR && terms->info.expr.op == PT_AND)
1127  {
1128  pt_tag_terms_with_id (parser, terms->info.expr.arg1, id, join_spec);
1129  pt_tag_terms_with_id (parser, terms->info.expr.arg2, id, join_spec);
1130  }
1131  else
1132  {
1133  pt_tag_term_with_id (parser, terms, id, join_spec, true);
1134  }
1135  terms = terms->next;
1136  }
1137 }
1138 
1139 
1140 /*
1141  * pt_tag_terms_with_specs () - test each term in the CNF predicate for
1142  * membership of the spec id equivalence class, and tag the term if found
1143  * return:
1144  * parser(in):
1145  * terms(in/out): CNF tree
1146  * join_spec(in): specs to walk and tag terms of
1147  * join_id(in):
1148  *
1149  * Note :
1150  * The tag goes in the "etc" field.
1151  * Specs visited last will override tags of earlier specs.
1152  * In thi manner it is guaranteed that a predicate will be tagged
1153  * the same left to right order (subpaths visited after node, and before
1154  * rest of list) that the unoptimised query generation path will use.
1155  * A term tagged with spec A will be guaranteed not to depend on
1156  * a later join spec, B, or suppath spec C of A.
1157  *
1158  * WHACKED SPECS (specs representing selector variables) should not
1159  * be used to tag terms since they will go away during XASL generation.
1160  */
1161 
1162 static void
1163 pt_tag_terms_with_specs (PARSER_CONTEXT * parser, PT_NODE * terms, PT_NODE * join_spec, UINTPTR join_id)
1164 {
1165  PT_NODE *specs = join_spec->info.spec.path_entities;
1166 
1167  if (join_spec->info.spec.derived_table_type == PT_IS_WHACKED_SPEC)
1168  {
1169  return;
1170  }
1171 
1172  pt_tag_terms_with_id (parser, terms, join_spec->info.spec.id, join_id);
1173 
1174  while (specs)
1175  {
1176  pt_tag_terms_with_specs (parser, terms, specs, join_id);
1177 
1178  specs = specs->next;
1179  }
1180 }
1181 
1182 /*
1183  * pt_do_cnf () - apply pt_cnf to search conditions
1184  * return:
1185  * parser(in): the parser context used to derive the node
1186  * node(in/out): parse tree node of a sql statement
1187  * dummy(in): not used but required by parser_walk_tree functions
1188  * continue_walk(in): used for pruning fruitless subtree walks
1189  */
1190 PT_NODE *
1191 pt_do_cnf (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
1192 {
1193  PT_NODE *list, *spec;
1194 
1195  /* only do CNF conversion if it is SELECT */
1196  if (node->node_type != PT_SELECT)
1197  {
1198  return node;
1199  }
1200 
1201  /* do CNF conversion if it has WHERE */
1202  list = node->info.query.q.select.where;
1203  if (list)
1204  {
1205  /* to make pt_cnf() work */
1206  for (; list; list = list->next)
1207  {
1209  }
1210 
1211  /* do CNF transformation */
1212  node->info.query.q.select.where = pt_cnf (parser, node->info.query.q.select.where);
1213 
1214  /* test each term in the CNF predicate for membership of the spec id */
1215  for (spec = node->info.query.q.select.from; spec; spec = spec->next)
1216  {
1217  pt_tag_terms_with_specs (parser, node->info.query.q.select.where, spec, spec->info.spec.id);
1218  }
1219  }
1220 
1221  /* do CNF conversion if it has HAVING */
1222  list = node->info.query.q.select.having;
1223  if (list)
1224  {
1225  /* to make pt_cnf() work */
1226  for (; list; list = list->next)
1227  {
1229  }
1230 
1231  /* do CNF transformation */
1232  node->info.query.q.select.having = pt_cnf (parser, node->info.query.q.select.having);
1233  }
1234 
1235  return node;
1236 }
int max_number_of_nodes
Definition: cnf.c:53
static PT_NODE * pt_tag_start_of_cnf_post(PARSER_CONTEXT *parser, PT_NODE *node, void *arg, int *continue_walk)
Definition: cnf.c:87
PT_NODE * next
Definition: parse_tree.h:3447
PT_NAME_INFO name
Definition: parse_tree.h:3318
static PT_NODE * pt_transform_cnf_pre(PARSER_CONTEXT *parser, PT_NODE *node, void *arg, int *continue_walk)
Definition: cnf.c:503
UINTPTR id
Definition: parse_tree.h:2144
PT_STATEMENT_INFO info
Definition: parse_tree.h:3487
PT_NODE * pt_do_cnf(PARSER_CONTEXT *parser, PT_NODE *node, void *arg, int *continue_walk)
Definition: cnf.c:1191
unsigned int accumulated_opcode
Definition: cnf.c:55
#define PT_EXPR_INFO_CNF_DONE
Definition: parse_tree.h:2207
#define PT_EXPR_INFO_SET_FLAG(e, f)
Definition: parse_tree.h:2239
PT_SPEC_INFO spec
Definition: parse_tree.h:3346
int correlation_level
Definition: parse_tree.h:2745
static void pt_tag_terms_with_specs(PARSER_CONTEXT *parser, PT_NODE *terms, PT_NODE *join_spec, UINTPTR join_id)
Definition: cnf.c:1163
static void pt_tag_term_with_id(PARSER_CONTEXT *parser, PT_NODE *term, UINTPTR id, UINTPTR join_spec, bool tag_subqueries)
Definition: cnf.c:1095
PT_EXPR_INFO expr
Definition: parse_tree.h:3299
UINTPTR id
Definition: cnf.c:43
union pt_query_info::@124 q
PT_NODE * path_entities
Definition: parse_tree.h:2138
UINTPTR join_spec
Definition: cnf.c:44
unsigned int custom_print
Definition: parse_tree.h:3556
PT_NODE * parser_append_node_or(PT_NODE *node, PT_NODE *list)
unsigned int accumulated_node_type
Definition: cnf.c:56
int number_of_nodes
Definition: cnf.c:54
cnf_mode
Definition: cnf.c:32
static PT_NODE * pt_and_or_form(PARSER_CONTEXT *parser, PT_NODE *node)
Definition: cnf.c:118
PT_TYPE_ENUM type_enum
Definition: parse_tree.h:3457
PT_NODE * or_next
Definition: parse_tree.h:3448
bool found
Definition: cnf.c:46
PT_MISC_TYPE derived_table_type
Definition: parse_tree.h:2147
PT_NODE * arg2
Definition: parse_tree.h:2198
#define assert(x)
PT_NODE * pt_and(PARSER_CONTEXT *parser_ptr, const PT_NODE *expression1, const PT_NODE *expression2)
#define PT_IS_QUERY_NODE_TYPE(x)
Definition: parse_tree.h:115
PT_NODE * parser_walk_leaves(PARSER_CONTEXT *parser, PT_NODE *node, PT_NODE_WALK_FUNCTION pre_function, void *pre_argument, PT_NODE_WALK_FUNCTION post_function, void *post_argument)
PT_NODE_TYPE node_type
Definition: parse_tree.h:3439
static enum scanner_mode mode
PT_NODE * parser_copy_tree(PARSER_CONTEXT *parser, const PT_NODE *tree)
PT_NODE * having
Definition: parse_tree.h:2692
PT_NODE * from
Definition: parse_tree.h:2686
UINTPTR spec_id
Definition: parse_tree.h:2543
int pt_str_compare(const char *p, const char *q, CASE_SENSITIVENESS case_flag)
SP_PARSER_CTX * parser
PT_NODE * arg1
Definition: parse_tree.h:2197
#define NULL
Definition: freelistheap.h:34
PT_NODE * where
Definition: parse_tree.h:2687
PT_QUERY_INFO query
Definition: parse_tree.h:3325
short location
Definition: parse_tree.h:2243
PT_NODE * parser_append_node(PT_NODE *node, PT_NODE *list)
static int count_and_or(PARSER_CONTEXT *parser, const PT_NODE *node)
Definition: cnf.c:462
PT_NODE * parser_new_node(PARSER_CONTEXT *parser, PT_NODE_TYPE node_type)
bool tag_subqueries
Definition: cnf.c:47
static PT_NODE * pt_find_name_id_pre(PARSER_CONTEXT *parser, PT_NODE *tree, void *arg, int *continue_walk)
Definition: cnf.c:1029
PT_NODE * in_query
Definition: cnf.c:45
void parser_free_tree(PARSER_CONTEXT *parser, PT_NODE *tree)
PT_NODE * spec
static PT_NODE * pt_find_name_id_post(PARSER_CONTEXT *parser, PT_NODE *tree, void *arg, int *continue_walk)
Definition: cnf.c:1070
static PT_NODE * pt_transform_cnf_post(PARSER_CONTEXT *parser, PT_NODE *node, void *arg, int *continue_walk)
Definition: cnf.c:584
PT_OP_TYPE
Definition: parse_tree.h:1320
static void pt_tag_terms_with_id(PARSER_CONTEXT *parser, PT_NODE *terms, UINTPTR id, UINTPTR join_spec)
Definition: cnf.c:1122
char * parser_print_tree(PARSER_CONTEXT *parser, const PT_NODE *node)
PT_OP_TYPE pt_negate_op(PT_OP_TYPE op)
PT_OP_TYPE op
Definition: parse_tree.h:2200
#define PT_INTERNAL_ERROR(parser, what)
Definition: parse_tree.h:112
PT_NODE * pt_cnf(PARSER_CONTEXT *parser, PT_NODE *node)
Definition: cnf.c:941
static PT_NODE * pt_negate_expr(PARSER_CONTEXT *parser, PT_NODE *node)
Definition: cnf.c:205
#define PT_EXPR_INFO_CLEAR_FLAG(e, f)
Definition: parse_tree.h:2240
UINTPTR spec_ident
Definition: parse_tree.h:3451
struct parser_node::@132 flag
PT_SELECT_INFO select
Definition: parse_tree.h:2781
PT_NODE * pt_count_input_markers(PARSER_CONTEXT *parser, PT_NODE *node, void *arg, int *continue_walk)
enum cnf_mode CNF_MODE
Definition: cnf.c:38
PT_NODE * parser_walk_tree(PARSER_CONTEXT *parser, PT_NODE *node, PT_NODE_WALK_FUNCTION pre_function, void *pre_argument, PT_NODE_WALK_FUNCTION post_function, void *post_argument)
#define PT_EXPR_INFO_IS_FLAGED(e, f)
Definition: parse_tree.h:2238
unsigned is_cnf_start
Definition: parse_tree.h:3476
PT_NODE * parser_copy_tree_list(PARSER_CONTEXT *parser, PT_NODE *tree)
static PT_NODE * pt_calculate_similarity(PARSER_CONTEXT *parser, PT_NODE *node, void *arg, int *continue_walk)
Definition: cnf.c:545