NCBI C++ ToolKit
query_func_promote.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: query_func_promote.cpp 39115 2017-08-01 18:58:09Z asztalos $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors: Bob Falk
27  *
28  * File Description:
29  * Implementation for query execution using a type-promotion approach
30  * for comparisons.
31  */
32 
33 #include <ncbi_pch.hpp>
34 
37 #include <corelib/ncbiobj.hpp>
38 #include <corelib/ncbistd.hpp>
39 
41 #include <gui/objutils/utils.hpp>
42 
43 #include <cmath>
44 
46 
47 /******************************************************************************
48  *
49  * CQueryFuncPromoteBase Implementation
50  *
51  *****************************************************************************/
52 
55 {
56  IQueryParseUserObject* uo = qnode.GetValue().GetUserObject();
57  return dynamic_cast<TEvalResult*>(uo);
58 }
59 
62 {
63  TEvalResult* c = this->GetQueryNodeValue(qnode);
64  if (!c) {
65  qnode.GetValue().SetUserObject((c = new TEvalResult(&qnode)));
66  }
67  return c;
68 }
69 
71 {
72  // Type information for query tree elements that are not data fields
73  // are set during pre-processing of the query since they are static.
74  // Only field data types need to be set dymamically (where querying
75  // each individual data element)
76  if (tree_val.IsDataField()) {
77  if (tree_val.GetDataType() == QueryValueType::eFieldString ||
79  tree_val.GetDataType() == QueryValueType::eUndefined ) {
80  if (!m_QExec->ResolveIdentifier(tree_val.GetFieldID(),
81  tree_val.m_String)) {
82  tree_val.m_String = "";
83  return false;
84  }
85  }
86  else if (tree_val.GetDataType() == QueryValueType::eFieldInt) {
87  if (!m_QExec->ResolveIdentifier(tree_val.GetFieldID(),
88  tree_val.m_Int)) {
89  tree_val.m_Int = 0;
90  return false;
91  }
92  }
93  else if (tree_val.GetDataType() == QueryValueType::eFieldBool) {
94  if (!m_QExec->ResolveIdentifier(tree_val.GetFieldID(),
95  tree_val.m_Bool)) {
96  tree_val.m_Bool = false;
97  return false;
98  }
99  }
100  else {
101  if (!m_QExec->ResolveIdentifier(tree_val.GetFieldID(),
102  tree_val.m_Double)) {
103  tree_val.m_Double = 0.0;
104  return false;
105  }
106  }
107  }
108 
109  return true;
110 }
111 
112 // From NStr::StringToBool but faster because it doesn't throw an exception
113 static const char* s_kTrueString = "true";
114 static const char* s_kFalseString = "false";
115 static const char* s_kTString = "t";
116 static const char* s_kFString = "f";
117 static const char* s_kYesString = "yes";
118 static const char* s_kNoString = "no";
119 static const char* s_kYString = "y";
120 static const char* s_kNString = "n";
121 bool FastStringToBool(const CTempString str, bool& isbool)
122 {
123  isbool = true;
124  if (AStrEquiv(str, s_kTrueString, PNocase()) ||
128  errno = 0;
129  return true;
130  }
131  if (AStrEquiv(str, s_kFalseString, PNocase()) ||
135  errno = 0;
136  return false;
137  }
138 
139  isbool = false;
140  return false;
141 }
142 
144 {
145  // Type info for query tree elements that are not data fields
146  // is set during pre-processing of the query since they are static.
147  // Only field data types may need to be set dynamically (while querying
148  // each individual data element)
149  if (tree_val.IsDataField()) {
150 
151  // If the data field was identified (during pre-processing) as a seq-id,
152  // do not try to convert it to another (non-string) type
153  if (tree_val.GetDataType() == QueryValueType::eFieldSeqID) {
154  return ResolveFieldValue(tree_val);
155  }
156 
157  // We normally use this function (SetCompareType) when we are not
158  // certain of the underlying type for the field. For that reason
159  // we (re)set the field type to string here since it is the most
160  // general.
162 
163  // Look up the field using the data source
164  if (!ResolveFieldValue(tree_val)) {
165  return false;
166  }
167 
168  // Get first non-blank character - this speeds up checking
169  // for possible conversions
170  std::size_t first_char = tree_val.m_String.find_first_not_of(" \t\r\n");
171  if (first_char == std::string::npos)
172  return true;
173 
174  int ch = tolower(tree_val.m_String[first_char]);
175 
176  // Check for boolean. For performance, use function that does not throw
177  // an exception when not found
178  if (ch == 'y' || ch == 'n' || ch == 't' || ch == 'f') {
179  bool found = false;
180  tree_val.m_Bool = FastStringToBool(tree_val.m_String, found);
181 
182  if (found) {
184  }
185  }
186  // See if it can be an number. StringToInt/Double() are slow so
187  // avoid them if possible. Valid numbers: 0.5 +5 -5 .5 0x5A
188  else if (isdigit(ch) || ch == '-' || ch == '+' || ch == '.') {
189  // Try integer convert. This will not convert floats, e.g. 1.0
191  if (errno != 0 && !tree_val.m_Int) {
192  // See if it can be a float/double:
194  if (!(errno != 0 &&
195  (tree_val.m_Double == HUGE_VAL || tree_val.m_Double == -HUGE_VAL || !tree_val.m_Double)))
197  }
198  else {
200  }
201  }
202  }
203 
204  return true;
205 }
206 
209 {
210  // Search for the promotion rule based on the types of the two data items and
211  // the operator.
212  std::vector<CPromoteRule>::iterator iter =
213  std::lower_bound(m_PromoteRules.begin(),
214  m_PromoteRules.end(), pr);
215 
216  // All items should be covered (and if not, we can't compare the items).
217  if (iter == m_PromoteRules.end() ||
218  !(*iter == pr)) {
219  //_TRACE("Error finding promote type");
221  }
222 
223  return (*iter).m_PromotedType;
224 }
225 
230 {
231 
232 
233  // Add promotion rule for both 'type1 op type2' and 'type2 op type1'.
234  m_PromoteRules.push_back(CPromoteRule(op, type1, type2, ptype));
235  m_PromoteRules.push_back(CPromoteRule(op, type2, type1, ptype));
236 }
237 
239  objects::CScope* scope)
240 {
241  CQueryParseNode& qnode = tr.GetValue();
242 
243  // Each node in the query parse tree (one element in tree for each token in
244  // the query) has a corresponding user value which we create and insert here,
245  // during query pre-processing.
246  IQueryParseUserObject* uo = qnode.GetUserObject();
247  TEvalResult* tree_val = dynamic_cast<TEvalResult*>(uo);
248  if (!tree_val) {
249  tree_val = new TEvalResult(&tr);
250  qnode.SetUserObject(tree_val);
251  }
252 
253  //
254  // Do any required analyisis or type-conversions to the individual query nodes
255  // here during query pre-processing. This is more efficient since conversions or
256  // lookups done here only need to be done once.
257  //
258 
259  tree_val->SetIsDataField(false);
260  tree_val->SetScope(scope);
262 
263  // If this query token is a string, determine if it is the name of a data field
264  // or (if not) if the string can also be converted to another type, e.g. bool.
265  if (qnode.GetType() == CQueryParseNode::eString) {
266  //
267  // If the string value (before parsing) is enclosed in quotes, then don't
268  // try to look the string up in the data - it's just a string.
269  if ((qnode.GetOrig().length() >= 2) &&
270  ((qnode.GetOrig()[0] == '\"' &&
271  qnode.GetOrig()[qnode.GetOrig().length()-1] == '\"'))) {
272 
273  tree_val->m_String = qnode.GetStrValue();
274  }
275  // Also check to see if string was enclosed in single quotes. Single quotes
276  // are (currently) not stripped, so we check for them in the parsed token
277  // and strip them if they are there.
278  else if ((qnode.GetOrig().length() >= 2) &&
279  ((qnode.GetOrig()[0] == '\'' &&
280  qnode.GetOrig()[qnode.GetOrig().length()-1] == '\''))) {
281  // If the single quotes were not stripped by the parser, strip
282  // them here
283  if (qnode.GetStrValue().length() == qnode.GetOrig().length())
284  tree_val->m_String = qnode.GetStrValue().substr(1,
285  qnode.GetStrValue().length()-2);
286  else
287  tree_val->m_String = qnode.GetStrValue();
288  }
289  // No enclosing quotes - look it up to see if it is an identfier for a
290  // data field
291  else if (m_QExec->HasIdentifier(qnode.GetStrValue())) {
292  tree_val->SetIsDataField(true);
293  tree_val->SetFieldID(m_QExec->GetIdentifier(qnode.GetStrValue()));
294 
295  // Check name to see if it is a seq-id field:
296  if (NStr::Compare(qnode.GetStrValue(), "seq-id", NStr::eNocase) == 0) {
298  }
299 
300  // look up field type. Not all query execution environments will
301  // have this (schema) information available.
302  CQueryParseNode::EType field_type =
304 
305  switch (field_type) {
308  break;
311  break;
314  break;
317  break;
318  default:
319  // Field type not available (we will have to determine it
320  // at evaluation time)
321  break;
322  }
323  }
324  // String without quotes that is not a data field
325  else {
326  tree_val->m_String = qnode.GetStrValue();
327  }
328 
329  //
330  // If the string is not a data field, determine if it can
331  // be converted to a boolean, integer, or float in that order
332  // of priority (bool is defined as: {true,false,yes,no,y,n,t,f},
333  // not 1 or 0 - those are integers). If it can't be converted,
334  // its type is just string. If it can be converted, its type is
335  // String{Bool, Int, Float}. This allows us, if needed, to have
336  // different promotion rules for a > 27 vs a > "27" (type Int
337  // vs. StringInt)
338  //
339  if (!tree_val->IsDataField()) {
340  try {
341  tree_val->m_Bool = NStr::StringToBool(tree_val->m_String);
343  }
344  catch (CStringException&) {
345  // See if it can be an integer (this will not convert floats, e.g.
346  // 1.0 will not convert)
347  try {
348  tree_val->m_Int = NStr::StringToInt(tree_val->m_String);
350  }
351  catch (CStringException&) {
352  // See if it can be a float:
353  try {
354  tree_val->m_Double = NStr::StringToDouble(tree_val->m_String);
356  }
357  catch (CStringException&) {
358  // It's just a string:
360  }
361  }
362  }
363  }
364  }
365  //
366  // Node in tree was not a string, so it can't be a data field. Check if
367  // it was one of the other primitive data types.
368  else if (qnode.GetType() == CQueryParseNode::eBoolConst) {
370  tree_val->m_Bool = qnode.GetBool();
371  }
372  else if (qnode.GetType() == CQueryParseNode::eIntConst) {
374  tree_val->m_Int = qnode.GetInt();
375  }
376  else if (qnode.GetType() == CQueryParseNode::eFloatConst) {
378  tree_val->m_Double = qnode.GetDouble();
379  }
380  else if (qnode.GetType() == CQueryParseNode::eIdentifier) {
381  if (m_QExec->HasIdentifier(qnode.GetStrValue())) {
382  tree_val->SetIsDataField(true);
383  tree_val->SetFieldID(m_QExec->GetIdentifier(qnode.GetStrValue()));
384 
385  // Check name to see if it is a seq-id field:
386  if (NStr::Compare(qnode.GetStrValue(), "seq-id", NStr::eNocase) == 0) {
388  }
389 
390  // look up field type. Not all query execution environments will
391  // have this (schema) information available.
392  CQueryParseNode::EType field_type =
394 
395  switch (field_type) {
398  break;
401  break;
404  break;
407  break;
408  default:
409  // Field type not available (we will have to determine it
410  // at evaluation time)
411  break;
412  }
413  }
414  }
415  //
416  // If a node in the query tree is not a primitive data type, then
417  // it must be a logical or comparison operator. These query objects
418  // have a type of BoolConst since they are the boolean result of a
419  // subexpression. (we could do additional validation checks
420  // here, or check for expressions supported by our query execution
421  // object, throwing an error for any expression not included...)
422  else if (!qnode.IsValue() &&
423  qnode.GetType() != CQueryParseNode::eFunction &&
424  qnode.GetType() != CQueryParseNode::eSelect &&
425  qnode.GetType() != CQueryParseNode::eFrom &&
426  qnode.GetType() != CQueryParseNode::eWhere &&
427  qnode.GetType() != CQueryParseNode::eList) {
429 
431  this->MakeArgVector(tr, args);
432 
433  // Check if operator has a valid number of parameters. Normal query
434  // parsing seems to catch these so this is more of a verification.
435  if (args.size() < GetArgCountMin(qnode.GetType()) ||
436  args.size() > GetArgCountMax(qnode.GetType()) ) {
437  NCBI_THROW(CQueryExecException, eWrongArgumentCount,
438  "Error: operator " +
439  qnode.GetNodeTypeAsString() +
440  " has " + NStr::SizetToString(args.size()) + " operands.");
441  }
442 
443  // Many expressions have just 2 arguments (==, !=, <, >, <=, >=, etc)
444  // The two expressions that have > 2 args are 'between' and 'in'
445  // and for both of those, we evaluate the first argument against each
446  // of the subsequent arguments. We also have the NOT expression.
447  // Also AND and OR can have (after tree flattening) > 2 operands.
448  TEvalResult* val1 = GetQueryNodeValue(*args[0]);
449  for (size_t i=1; i<args.size(); ++i) {
450  TEvalResult* val2 = GetQueryNodeValue(*args[i]);
451 
452  if (val1->GetDataType() != QueryValueType::eUndefined &&
454 
455  if (qnode.IsLogic()) {
456 
457  // These could throw an exception if a (static)
458  // type cannot be converted to a boolean.
459  if (val1->GetQueryNode()->GetValue().IsValue()) {
460  if (!val1->IsDataField())
462  else
464  }
465 
466  if (val2->GetQueryNode()->GetValue().IsValue()) {
467  if (!val2->IsDataField())
469  else
471  }
472 
473  tree_val->AddPromotedType(CPromoteRule(qnode.GetType(),
474  val1->GetDataType(),
475  val2->GetDataType(),
477  }
478  else {
479  CPromoteRule pr(qnode.GetType(),
480  val1->GetDataType(),
481  val2->GetDataType(),
483 
484  QueryValueType::EBaseType promoted_type =
485  GetPromotedType(pr);
486 
487  // Both data types are defined (otherwise we would not be here) but
488  // there is no valid type-basis for comparison.
489  if (promoted_type == QueryValueType::eUndefined) {
490  string msg = "Unable to compare: " +
491  args[0]->GetValue().GetOriginalText() +
492  " with: " +
493  args[i]->GetValue().GetOriginalText() +
494  " using operator: " +
495  qnode.GetNodeTypeAsString();
497  eIncompatibleType,
498  msg);
499  }
500  else {
501  // This can throw an exception that a (static) type from
502  // the query string cannot be promoted to the target
503  // promotion type.
504  pr.m_PromotedType = promoted_type;
505  tree_val->AddPromotedType(pr);
506  //applies to 1 or many??
507  if (!val1->IsDataField())
508  val1->PromoteTo(promoted_type);
509  if (!val2->IsDataField())
510  val2->PromoteTo(promoted_type);
511  }
512  }
513  }
514  }
515  }
516 }
517 
520  CQueryParseTree::TNode& qnode,
523 {
524  TEvalResult* node_value = GetQueryNodeValue(qnode);
525  TEvalResult* val1 = GetQueryNodeValue(*arg1);
526  TEvalResult* val2 = GetQueryNodeValue(*arg2);
527 
528  // Get underlying data and data type. May require extracting
529  // data from data source (if value node is an identifier). Data
530  // type for comparison depends potentially on comparison type
531  // and underlying type of both nodes.
533 
534  // This returns false if types don't match - maybe we should force
535  // promotion to the matching promote entry, if available.
536  if ( !node_value->HasPromoteType(comparison_idx,
537  val1->GetDataType(),
538  val2->GetDataType()) ) {
539  // Determine the underlying type for each argument (e.g. is it a boolean, a
540  // string that can be converted to a boolean, etc.)
541  bool data_available;
542 
543  data_available = SetCompareType(*val1);
544  if (data_available)
545  data_available = SetCompareType(*val2);
546 
547  // Not every element of every data source, e.g. node in a tree
548  // has every property. When properties or fields are not available
549  // return undefined so that the comparison will evaluate to false.
550  if (!data_available) {
552  }
553 
554  CPromoteRule pr;
555  pr.m_CompareOperator = qnode.GetValue().GetType();
556  pr.m_Type1 = val1->GetDataType();
557  pr.m_Type2 = val2->GetDataType();
558 
559  promoted_type = GetPromotedType(pr);
560 
561  if (promoted_type == QueryValueType::eUndefined) {
562  // Can't convert - not necessarily a real error e.g., a
563  // field may have a blank value where usually it as an int.
564  // So just set result to false here if either of the values
565  // came from a data field. If neither came from a field,
566  // this has bad syntax so throw an exception
567  if (!val1->IsDataField() && !val2->IsDataField()) {
568  string msg = "Unable to compare: " +
569  arg1->GetValue().GetOriginalText() +
570  " with: " +
571  arg2->GetValue().GetOriginalText() +
572  " using operator: " +
573  qnode.GetValue().GetNodeTypeAsString();
575  eIncompatibleType,
576  msg);
577  }
578 
579  TEvalResult* eval_res = GetQueryNodeValue(qnode);
580  eval_res->SetValue(qnode->IsNot() ? true : false);
582  }
583  }
584  else {
585  promoted_type = node_value->GetPromoteType(comparison_idx);
586 
587  // If the node represents an identifier, this retrieves
588  // the corresponding value from the data source.
589  bool found;
590  found = ResolveFieldValue(*val1);
591 
592  // Not every element of every data source, e.g. node in a tree
593  // has every property. When properties or fields are not available
594  // return undefined so that the comparison will evaluate to false.
595  if (!found) {
597  }
598 
599  found = ResolveFieldValue(*val2);
600 
601  if (!found) {
603  }
604  }
605 
606  // Promote both data elements to the required comparison (may not require a
607  // change if types were already compatible)
608  val1->PromoteTo(promoted_type);
609  val2->PromoteTo(promoted_type);
610 
611  return promoted_type;
612 }
613 
614 
615 ///////////////////////////////////////////////////////////////////////////////
616 /// class CQueryFunctionRTVar implementation
617 ///
619 {
620  _ASSERT(qnode.GetValue().GetType() == CQueryParseNode::eSelect);
621  CMacroQueryExec* macro_query_exec = dynamic_cast<CMacroQueryExec*>(m_QExec);
622  CQueryNodeValue* node_value = dynamic_cast<CQueryNodeValue*>(qnode->GetUserObject());
623  _ASSERT(macro_query_exec && node_value);
624 
625  if (macro_query_exec && node_value) {
626  node_value->SetRef(macro_query_exec->GetOrCreateRTVar(qnode.GetValue().GetOriginalText()));
627  }
628 }
629 
630 ///////////////////////////////////////////////////////////////////////////////
631 /// CQueryFuncAssignment
632 ///
634 {
636  MakeArgVector(qnode, user_args);
637 
638  vector<CRef<CQueryNodeValue>> arguments;
639  arguments.reserve(user_args.size());
640  for (auto&& it : user_args) {
641  CRef<CQueryNodeValue> user_obj = Ref(dynamic_cast<CQueryNodeValue*>((*it)->GetUserObject()));
642  if (user_obj) {
643  arguments.push_back(user_obj);
644  }
645  }
646 
647  size_t arg_size = arguments.size();
648  if (arg_size < 2 || arguments[0]->GetDataType() != QueryValueType::eRef) {
649  for (auto& iter : arguments) {
650  if (iter->GetDataType() == QueryValueType::eUndefined) {
651  TEvalResult* eval_res = GetQueryNodeValue(qnode);
653  return;
654  }
655  }
656  NCBI_THROW(macro::CMacroExecException, eWrongArguments,
657  "Wrong type of argument in function: " + qnode.GetValue().GetOrig());
658  }
659 
660  CRef<CQueryNodeValue> node_value = arguments[1];
661  arguments[0]->AssignToRef(*node_value);
662 }
663 
664 ///////////////////////////////////////////////////////////////////////////////
665 /// class CQueryFuncFunction implementation
666 ///
668 {
669  string fname = qnode->GetOriginalText();
670 
671  // Get vector of arguments to function and resolve values for any which come
672  // from the data source (value types and macro variables would already be
673  // resolved at this point).
675  this->MakeArgVector(qnode, args);
676 
677  // for each function argument:
678  for (size_t i = 0; i < args.size(); ++i) {
679  TEvalResult* val = GetQueryNodeValue(*args[i]);
680 
681  // we don't check for success here, but if we do not find a
682  // parameter for a function the function could fail, but
683  // that is better reported by the function.
685  }
686 
687  m_QExec->CallFunction(fname, qnode);
688 }
689 
690 
691 /******************************************************************************
692  *
693  * CQueryFuncPromoteValue Implementation
694  *
695  *****************************************************************************/
696 
698 {
699  TEvalResult* res = MakeQueryNodeValue(qnode);
700 
701  // We only use one instance of the query parse tree to evaluate all
702  // the nodes, so that means we inherit the value in 'res' from
703  // the previous execution. That's why we reset the value here
704  // in the queries 'leaf' (value) nodes.
705  res->Reset();
706 }
707 
708 
709 /******************************************************************************
710 *
711 * CQueryFuncPromoteIdentifier Implementation
712 *
713 *****************************************************************************/
714 
716 {
717  TEvalResult* res = MakeQueryNodeValue(qnode);
718 
719  // We only use one instance of the query parse tree to evaluate all
720  // the nodes, so that means we inherit the value in 'res' from
721  // the previous execution. That's why we reset the value here
722  // in the queries 'leaf' (value) nodes.
723  res->Reset();
724 
725  // Resolve any node values that are varaibles here. If an identifier
726  // is a value type, it is already set and if it comes from the data
727  // source (a field type), then it is (looked up and) resolved when an
728  // operator is applied to it
729  string identifier = qnode.GetValue().GetOriginalText();
730  CMacroQueryExec* mqe = dynamic_cast<CMacroQueryExec*>(m_QExec);
731 
732  if (mqe && mqe->GetMacroRep()) {
733  // This will also set the type if the variable is unidentified
734  bool found = mqe->GetMacroRep()->GetNodeValue(identifier, *res);
735  if (!found) {
736  // Try to look it up in the run time variable list
737  mqe->ResolveRTVar(identifier, *res);
738  }
739  }
740 }
741 
742 
743 /******************************************************************************
744  *
745  * CQueryFuncPromoteLogic Implementation
746  *
747  *****************************************************************************/
749 {
751  this->MakeArgVector(qnode, args);
752 
753  if (args.size() == 0) {
754  // If a logical function has no arguments.... Is it truely logical?
755  NCBI_THROW(CQueryExecException, eWrongArgumentCount,
756  "No arguments for logical function " +
757  qnode->GetNodeTypeAsString());
758  }
759 
760  // operation interpretor
761  //
762  CQueryParseNode::EType node_type = qnode.GetValue().GetType();
763 
764  // check if it is a binary operator:
765  if (node_type == CQueryParseNode::eSub ||
766  node_type == CQueryParseNode::eXor) {
767 
768  if (args.size() != 2) {
769  NCBI_THROW(CQueryExecException, eWrongArgumentCount,
770  "Error: Binary operator: " +
771  qnode.GetValue().GetNodeTypeAsString() +
772  " has: " + NStr::SizetToString(args.size()) + " operands.");
773  }
774 
775  // Since this is a logical operator, both arguments must be the
776  // boolean result of a previous expression or promotable to
777  // boolean. Static types may have already been promoted to bool,
778  // but in thas case "PromoteTo(eBool)" is a no-op.
779  TEvalResult* val1 = GetQueryNodeValue(*args[0]);
780  TEvalResult* val2 = GetQueryNodeValue(*args[1]);
781 
782  if (val1->IsDataField()) {
783  if (!m_QExec->ResolveIdentifier(val1->GetFieldID(),
784  val1->m_Bool)) {
785  NCBI_THROW(CQueryExecException, eIncompatibleType,
786  "Error: Unable to convert field: " +
787  args[0]->GetValue().GetStrValue() +
788  " to boolean value.");
789  }
790  val1->SetValue(val1->m_Bool);
791  } else if (args[0]->GetValue().IsValue()) {
793  val1->SetValue(val1->m_Bool);
794  }
795 
796  if (val2->IsDataField()) {
797  if (!m_QExec->ResolveIdentifier(val2->GetFieldID(),
798  val2->m_Bool)) {
799  NCBI_THROW(CQueryExecException, eIncompatibleType,
800  "Error: Unable to convert field: " +
801  args[1]->GetValue().GetStrValue() +
802  " to boolean value.");
803  }
804  val2->SetValue(val1->m_Bool);
805  } else if (args[1]->GetValue().IsValue()) {
807  val2->SetValue(val2->m_Bool);
808  }
809 
810  TEvalResult* res = MakeQueryNodeValue(qnode);
811  switch (node_type) {
813  res->SetValue(val1->GetValue() && !val2->GetValue());
814  break;
816  res->SetValue(val1->GetValue() != val2->GetValue());
817  break;
818  default:
819  break;
820  }
821 
822  // not can be an attribute of a node, but this is for !=. I don't
823  // think it can show up as part of a logical expression, since
824  // we have the separate 'NOT' operator for that.
825  if (qnode.GetValue().IsNot())
826  res->SetValue(!res->GetValue());
827  }
828  else if (node_type == CQueryParseNode::eNot) {
829  if (args.size() != 1) {
830  NCBI_THROW(CQueryExecException, eWrongArgumentCount,
831  "Error: Unary NOT operator has: " +
832  NStr::SizetToString(args.size()) + " operands.");
833  }
834 
835  TEvalResult* val1 = GetQueryNodeValue(*args[0]);
836  TEvalResult* res = MakeQueryNodeValue(qnode);
837 
838  if (val1->IsDataField()) {
839  if (!m_QExec->ResolveIdentifier(val1->GetFieldID(),
840  val1->m_Bool)) {
841  NCBI_THROW(CQueryExecException, eIncompatibleType,
842  "Error: Unable to convert field: " +
843  args[0]->GetValue().GetStrValue() +
844  " to boolean value.");
845  }
846  val1->SetValue(val1->m_Bool);
847  } else if (args[0]->GetValue().IsValue()) {
849  val1->SetValue(val1->m_Bool);
850  }
851 
852  res->SetValue(!val1->GetValue());
853  }
854  else {
855  NCBI_THROW(CQueryExecException, eExecParseError,
856  "Error: Unexpected logical operand: " +
857  qnode->GetOriginalText());
858  }
859 }
860 
861 /******************************************************************************
862 *
863 * CQueryFuncPromoteAndOr Implementation
864 *
865 *****************************************************************************/
867 {
869  this->MakeArgVector(qnode, args);
870 
871  if (args.size() == 0) {
872  // If a logical function has no arguments.... Is it truely logical?
873  NCBI_THROW(CQueryExecException, eWrongArgumentCount,
874  "No arguments for logical function " +
875  qnode->GetNodeTypeAsString());
876  }
877 
878  // operation interpretor
879  //
880  CQueryParseNode::EType node_type = qnode.GetValue().GetType();
881 
882 
883  // check if it is an n-ary operator (trees can be flattened to combine
884  // adjacent ands and ors into a single operator (a or b or c) == (or: a,b,c)
885  if (node_type == CQueryParseNode::eAnd ||
886  node_type == CQueryParseNode::eOr) {
887 
888  if (args.size() < 2) {
889  NCBI_THROW(CQueryExecException, eWrongArgumentCount,
890  "Error: n-ary operator: " +
891  qnode.GetValue().GetNodeTypeAsString() +
892  " has: " + NStr::SizetToString(args.size()) + " operands.");
893  }
894 
895  TEvalResult* res = MakeQueryNodeValue(qnode);
896 
897  // Set default answer based on logical operation type:
898  bool result;
899  if (node_type == CQueryParseNode::eAnd)
900  result = true;
901  else if (node_type == CQueryParseNode::eOr)
902  result = false;
903 
904  // Check arguments 1 at a time. For AND, every argument must be true.
905  // For OR, every argument must be false (or we break early)
906  for (size_t i = 0; i < args.size(); ++i) {
907 
908  // Since these are logical operators, all arguments must be the
909  // boolean result of a previous expression or promotable to
910  // boolean. Static types may have already been promoted to bool,
911  // but in that case "PromoteTo(eBool)" is a no-op.
912 
913  // For efficiency, with the AND and OR logical operators only,
914  // we evalute any subexpressions one at a time since either all must
915  // be true (AND) or none (OR). So if node is not a simple value,
916  // traverse child subtree here:
917  if (!args[i]->GetValue().IsValue()) {
918  CQueryExecEvalFunc visitor(*m_QExec);
919  TreeDepthFirstTraverse(*args[i], visitor);
920  }
921 
922  TEvalResult* val = GetQueryNodeValue(*args[i]);
923 
924  if (val->IsDataField()) {
925  if (!m_QExec->ResolveIdentifier(val->GetFieldID(),
926  val->m_Bool)) {
927  NCBI_THROW(CQueryExecException, eIncompatibleType,
928  "Error: Unable to convert field: " +
929  args[i]->GetValue().GetStrValue() +
930  " to boolean value.");
931  }
932  val->SetValue(val->m_Bool);
933  }
934  else if (args[i]->GetValue().IsValue() || args[i]->GetValue().GetType() == CQueryParseNode::eFunction) {
935  val->PromoteTo(QueryValueType::eBool);
936  val->SetValue(val->m_Bool);
937  }
938 
939  // We can break early if any AND parameters are false
940  // or any OR parameters are true:
941  if (node_type == CQueryParseNode::eAnd) {
942  if (!val->GetValue()) {
943  result = false;
944  break;
945  }
946  }
947  else if (node_type == CQueryParseNode::eOr) {
948  if (val->GetValue()) {
949  result = true;
950  break;
951  }
952  }
953  }
954  res->SetValue(result);
955 
956  // not can be an attribute of a node, but this is for !=. I don't
957  // think it can show up as part of a logical expression, since
958  // we have the separate 'NOT' operator for that.
959  if (qnode.GetValue().IsNot())
960  res->SetValue(!res->GetValue());
961  }
962  else {
963  NCBI_THROW(CQueryExecException, eExecParseError,
964  "Error: Unexpected logical operand: " +
965  qnode->GetOriginalText());
966  }
967 }
968 
969 
970 
971 /******************************************************************************
972  *
973  * CQueryFuncPromoteCompare Implementation
974  *
975  *****************************************************************************/
976 
978  NStr::ECase c,
981 {
982  SetCaseSensitive(c);
983  SetStringMatching(matching);
984 }
985 
986 /******************************************************************************
987  *
988  * CQueryFuncEqualityCompares Implementation
989  *
990  *****************************************************************************/
992 {
1007 
1021 
1034 
1046 
1057 
1067 
1076 
1084 
1091 
1097 
1102 
1106 
1109 
1111 
1112  std::sort(m_PromoteRules.begin(), m_PromoteRules.end());
1113  m_PromoteRules.erase(std::unique(m_PromoteRules.begin(), m_PromoteRules.end()),
1114  m_PromoteRules.end());
1115 }
1116 
1117 /******************************************************************************
1118  *
1119  * CQueryFuncPromoteEq Implementation
1120  *
1121  *****************************************************************************/
1123 : CQueryFuncEqualityCompares(CQueryParseNode::eEQ, c, matching)
1124 {
1126 }
1127 
1129 {
1131  this->MakeArgVector(qnode, args);
1132 
1133  // get operation type
1134  CQueryParseNode::EType node_type = qnode.GetValue().GetType();
1135 
1136  // handle equality comparison
1137  if (node_type == CQueryParseNode::eEQ) {
1138 
1139  TEvalResult* eval_res = GetQueryNodeValue(qnode);
1140 
1141  if (args.size() != 2) {
1142  NCBI_THROW(CQueryExecException, eWrongArgumentCount,
1143  "Error: Binary operator " +
1144  qnode.GetValue().GetNodeTypeAsString() +
1145  " has " + NStr::SizetToString(args.size()) + " operands.");
1146  }
1147 
1148 
1149  // Get underlying data and data type. Data my require extracting
1150  // data from current record (tree node). Data type for comparison
1151  // depends potentially on comparison type and underlying type of both
1152  // nodes.
1153  QueryValueType::EBaseType promoted_type =
1154  ResolveAndPromote(0, qnode, args[0], args[1]);
1155 
1156  TEvalResult* val1 = GetQueryNodeValue(*args[0]);
1157  TEvalResult* val2 = GetQueryNodeValue(*args[1]);
1158 
1159  // If a field was not present in the data, the equivalent of a NULL
1160  // value in SQL, we return by default true for 'not equal' comparisons
1161  // and false for 'equal' comparisons. But as a special case, if the
1162  // field is being compared to the (constant) empty string "", we
1163  // we reverse that since users would expect empty (blank) and missing
1164  // (null) to be equivalent.
1165  // This is a bit of a hack since we don't support IS NULL
1166  // keyword like SQL does (and IS NULL is not intuitive for users)
1167  if (promoted_type == QueryValueType::eUndefined) {
1168  // Add special case for prop="" and prop!="" (again because we don't
1169  // support null). A user would expect that field!=""
1170  // returns true for null fields, and that field="" returns
1171  // false for null fields.
1172  if (val1->IsDataField() &&
1173  val2->GetDataType() == QueryValueType::eString &&
1174  val2->m_String == "") {
1175  eval_res->SetValue(qnode->IsNot() ? false : true);
1176  }
1177  else if (val2->IsDataField() &&
1178  val1->GetDataType() == QueryValueType::eString &&
1179  val1->m_String == "") {
1180  eval_res->SetValue(qnode->IsNot() ? false : true);
1181  }
1182  else {
1183  // not comparing to blank, return true for '!=' and false for '='
1184  eval_res->SetValue(qnode->IsNot() ? true : false);
1185  }
1186 
1187  return;
1188  }
1189 
1190  if (promoted_type == QueryValueType::eBool) {
1191  eval_res->SetValue(val1->m_Bool == val2->m_Bool);
1192  }
1193  else if (promoted_type == QueryValueType::eInt) {
1194  eval_res->SetValue(val1->m_Int == val2->m_Int);
1195  }
1196  else if (promoted_type == QueryValueType::eFloat) {
1197  eval_res->SetValue(val1->m_Double == val2->m_Double);
1198  }
1199  else if (promoted_type == QueryValueType::eString) {
1200  // Support phonetic, wildcard and reg-ex comparisons
1201  // We need to pass the pattern (if there is one) to the
1202  // CStringMatching ctor, and then pass the field to
1203  // CStringMatching::MatchString(). If neither is a field or
1204  // both are fields, do simple string comparison.
1205  // Also ePlainSearch in CStringMatching looks for substrings but
1206  // here for 'name'=value, we only want exact matches.
1207  if (val1->IsDataField() && !val2->IsDataField() &&
1210  bool result = sm.MatchString(val1->m_String.c_str());
1211  eval_res->SetValue(result);
1212  }
1213  else if (val2->IsDataField() && !val1->IsDataField() &&
1216  bool result = sm.MatchString(val2->m_String.c_str());
1217  eval_res->SetValue(result);
1218  }
1219  else {
1220  int result = NStr::Compare(val1->m_String.c_str(),
1221  val2->m_String.c_str(),
1222  GetCaseSensitive());
1223  eval_res->SetValue(result==0);
1224  }
1225  }
1226  else if (promoted_type == QueryValueType::eSeqID) {
1227  try {
1228  // For efficiency, do a simple string check first, and
1229  // if true, do not continue. Also, if either field is blank,
1230  // do not continue past the string check.
1231  int result = NStr::CompareNocase(val1->m_String.c_str(),
1232  val2->m_String.c_str());
1233  if (result == 0) {
1234  eval_res->SetValue(true);
1235  }
1236  else if (val1->m_String=="" || val2->m_String=="") {
1237  eval_res->SetValue(false);
1238  }
1239  else {
1240  objects::CSeq_id seq_id1(val1->m_String);
1241  objects::CSeq_id seq_id2(val2->m_String);
1242 
1243  bool match = CSeqUtils::Match(seq_id1, seq_id2, val1->GetScope());
1244 
1245  eval_res->SetValue(match);
1246  }
1247  }
1248  catch (CException& e) {
1249  NCBI_THROW(CQueryExecException, eObjManagerError,
1250  "Object Manager Error: " +
1251  e.GetMsg());
1252  }
1253  }
1254  // !=
1255  if (qnode->IsNot())
1256  eval_res->SetValue(!eval_res->GetValue());
1257 
1258 
1259  }
1260  else {
1261  NCBI_THROW(CQueryExecException, eExecParseError,
1262  "Error - Unhandled comparison operator: " +
1263  qnode.GetValue().GetNodeTypeAsString());
1264  }
1265 }
1266 
1267 /******************************************************************************
1268  *
1269  * CQueryFuncPromoteIn Implementation
1270  *
1271  *****************************************************************************/
1273 : CQueryFuncEqualityCompares(CQueryParseNode::eIn, c, matching)
1274 {
1276 }
1277 
1279 {
1281  this->MakeArgVector(qnode, args);
1282 
1283  // get operation type
1284  CQueryParseNode::EType node_type = qnode.GetValue().GetType();
1285 
1286  if (node_type == CQueryParseNode::eIn) {
1287  TEvalResult* eval_res = GetQueryNodeValue(qnode);
1288 
1289  // In operator can have any number of operands > 1:
1290  if (args.size() <= 1) {
1291  NCBI_THROW(CQueryExecException, eWrongArgumentCount,
1292  "Error: 'IN' operator has " +
1293  NStr::SizetToString(args.size()) + " operands.");
1294  }
1295 
1296  // Get underlying data and data type. Since there may be any number
1297  // of operands, iterate through them and check each time if they
1298  // need to be promoted. This allows the user to mix types between
1299  // the parms, e.g. : dist IN (0.57, "", 1) without throwing an
1300  // exception.
1301  TEvalResult* val1 = GetQueryNodeValue(*args[0]);
1302 
1303  bool found = false;
1304  for (size_t i=1; i<args.size() && !found; ++i) {
1305  TEvalResult* val2 = GetQueryNodeValue(*args[i]);
1306 
1307  QueryValueType::EBaseType promoted_type =
1308  ResolveAndPromote(i-1, qnode, args[0], args[i]);
1309 
1310  if (promoted_type != QueryValueType::eUndefined) {
1311  // Promote both data elements to the required comparison
1312  //val1->PromoteTo(promoted_type, m_ExecEval);
1313  //val2->PromoteTo(promoted_type, m_ExecEval);
1314 
1315  if (promoted_type == QueryValueType::eBool) {
1316  found = (val1->m_Bool == val2->m_Bool);
1317  }
1318  else if (promoted_type == QueryValueType::eInt) {
1319  found = (val1->m_Int == val2->m_Int);
1320  }
1321  else if (promoted_type == QueryValueType::eFloat) {
1322  found = (val1->m_Double == val2->m_Double);
1323  }
1324  else if (promoted_type == QueryValueType::eString) {
1325  // Support phonetic, wildcard and reg-ex comparisons
1326  if (val1->IsDataField() && !val2->IsDataField() &&
1329  found = sm.MatchString(val1->m_String.c_str());
1330  }
1331  else if (val2->IsDataField() && !val1->IsDataField() &&
1334  found = sm.MatchString(val2->m_String.c_str());
1335  }
1336  else {
1337  int result = NStr::Compare(val1->m_String.c_str(),
1338  val2->m_String.c_str(),
1339  GetCaseSensitive());
1340  found = (result==0);
1341  }
1342  }
1343  else if (promoted_type == QueryValueType::eSeqID) {
1344  try {
1345  // For efficiency, do a simple string check first, and
1346  // if true, do not continue. Also, if either field is blank,
1347  // do not continue past the string check.
1348  int result = NStr::CompareNocase(val1->m_String.c_str(),
1349  val2->m_String.c_str());
1350  if (result == 0) {
1351  found = true;
1352  }
1353  else if (val1->m_String!="" && val2->m_String!="") {
1354  objects::CSeq_id seq_id1(val1->m_String);
1355  objects::CSeq_id seq_id2(val2->m_String);
1356 
1357  found = CSeqUtils::Match(seq_id1, seq_id2, val1->GetScope());
1358  }
1359  }
1360  catch (CException& e) {
1361  NCBI_THROW(CQueryExecException, eObjManagerError,
1362  "Object Manager Error: " +
1363  e.GetMsg());
1364  }
1365  }
1366  }
1367  }
1368 
1369  if (qnode->IsNot())
1370  eval_res->SetValue(!found);
1371  else
1372  eval_res->SetValue(found);
1373  }
1374  else {
1375  NCBI_THROW(CQueryExecException, eExecParseError,
1376  "Error - Unhandled comparison operator: " +
1377  qnode.GetValue().GetNodeTypeAsString());
1378  }
1379 }
1380 
1381 
1382 
1383 /******************************************************************************
1384  *
1385  * CQueryFuncLike Implementation
1386  *
1387  *****************************************************************************/
1390 {
1392 }
1393 
1395 {
1396  //
1397  // Like: second parameter has to be string{,int,float,bool} and promotion is always to string for
1398  // comparison. First argument can be anything except the result of a subexpression (boolean
1399  // result). Seq-ids in like expressions are treated as regular strings.
1408 
1417 
1426 
1435 
1444 
1453 
1462 
1471 
1480 
1489 
1498 
1507 
1516 
1517 
1518  std::sort(m_PromoteRules.begin(), m_PromoteRules.end());
1519  m_PromoteRules.erase(std::unique(m_PromoteRules.begin(), m_PromoteRules.end()),
1520  m_PromoteRules.end());
1521 }
1522 
1524 {
1526  this->MakeArgVector(qnode, args);
1527 
1528  // get operation type
1529  CQueryParseNode::EType node_type = qnode.GetValue().GetType();
1530 
1531  // handle all the binary comparisons
1532  if (node_type == CQueryParseNode::eLike) {
1533 
1534  TEvalResult* eval_res = MakeQueryNodeValue(qnode);
1535 
1536  if (args.size() != 2) {
1537  NCBI_THROW(CQueryExecException, eWrongArgumentCount,
1538  "Error: Binary operator " +
1539  qnode.GetValue().GetNodeTypeAsString() +
1540  " has " + NStr::SizetToString(args.size()) + " operands.");
1541  }
1542 
1543  // Get underlying data and data type. Data my require extracting
1544  // data from current record (tree node). Data type for comparison
1545  // depends potentially on comparison type and underlying type of both
1546  // nodes.
1547  QueryValueType::EBaseType promoted_type =
1548  ResolveAndPromote(0, qnode, args[0], args[1]);
1549 
1550  if (promoted_type == QueryValueType::eUndefined) {
1551  eval_res->SetValue(false);
1552  return;
1553  }
1554 
1555  TEvalResult* val1 = GetQueryNodeValue(*args[0]);
1556  TEvalResult* val2 = GetQueryNodeValue(*args[1]);
1557 
1558  if (promoted_type == QueryValueType::eString) {
1559  bool result = NStr::MatchesMask(val1->m_String.c_str(),
1560  val2->m_String.c_str(),
1561  GetCaseSensitive());
1562  eval_res->SetValue(result);
1563  }
1564  // !=
1565  if (qnode->IsNot())
1566  eval_res->SetValue(!eval_res->GetValue());
1567 
1568  }
1569  else {
1570  NCBI_THROW(CQueryExecException, eExecParseError,
1571  "Error - Unhandled comparison operator: " +
1572  qnode.GetValue().GetNodeTypeAsString());
1573  }
1574 }
1575 
1576 
1577 /******************************************************************************
1578  *
1579  * CQueryFuncGtLtCompares Implementation
1580  *
1581  *****************************************************************************/
1583 {
1599 
1613 
1626 
1638 
1649 
1659 
1668 
1676 
1683 
1689 
1694 
1698 
1701 
1703 
1704  std::sort(m_PromoteRules.begin(), m_PromoteRules.end());
1705  m_PromoteRules.erase(std::unique(m_PromoteRules.begin(), m_PromoteRules.end()),
1706  m_PromoteRules.end());
1707 }
1708 
1709 
1710 
1711 
1712 /******************************************************************************
1713  *
1714  * CQueryFuncPromoteGtLt Implementation
1715  *
1716  *****************************************************************************/
1718  NStr::ECase c)
1719 : CQueryFuncGtLtCompares(op_type, c)
1720 {
1721  InitTypePromotionRules(op_type);
1722 }
1723 
1725 {
1727  this->MakeArgVector(qnode, args);
1728 
1729  // get operation type
1730  CQueryParseNode::EType node_type = qnode.GetValue().GetType();
1731 
1732  // handle all the binary comparisons
1733  if (node_type == CQueryParseNode::eGT ||
1734  node_type == CQueryParseNode::eGE ||
1735  node_type == CQueryParseNode::eLT ||
1736  node_type == CQueryParseNode::eLE) {
1737 
1738  TEvalResult* eval_res = GetQueryNodeValue(qnode);
1739 
1740  if (args.size() != 2) {
1741  NCBI_THROW(CQueryExecException, eWrongArgumentCount,
1742  "Error: Binary operator " +
1743  qnode.GetValue().GetNodeTypeAsString() +
1744  " has " + NStr::SizetToString(args.size()) + " operands.");
1745  }
1746 
1747  // Get underlying data and data type. May require extracting
1748  // data from data source (if value node is an identifier). Data
1749  // type for comparison depends potentially on comparison type
1750  // and underlying type of both nodes.
1751  QueryValueType::EBaseType promoted_type =
1752  ResolveAndPromote(0, qnode, args[0], args[1]);
1753 
1754  if (promoted_type == QueryValueType::eUndefined) {
1755  eval_res->SetValue(false);
1756  return;
1757  }
1758 
1759  TEvalResult* val1 = GetQueryNodeValue(*args[0]);
1760  TEvalResult* val2 = GetQueryNodeValue(*args[1]);
1761 
1762  /// Apply each of the possible comparison types for eache of the possible
1763  /// promotion types
1764  switch (node_type) {
1765  case CQueryParseNode::eGT:
1766  if (promoted_type == QueryValueType::eBool) {
1767  eval_res->SetValue(val1->m_Bool > val2->m_Bool);
1768  }
1769  else if (promoted_type == QueryValueType::eInt) {
1770  eval_res->SetValue(val1->m_Int > val2->m_Int);
1771  }
1772  else if (promoted_type == QueryValueType::eFloat) {
1773  eval_res->SetValue(val1->m_Double > val2->m_Double);
1774  }
1775  else if (promoted_type == QueryValueType::eString) {
1776  int result = NStr::Compare(val1->m_String.c_str(),
1777  val2->m_String.c_str(),
1778  GetCaseSensitive());
1779  eval_res->SetValue(result>0);
1780  }
1781  break;
1782 
1783  case CQueryParseNode::eGE:
1784  if (promoted_type == QueryValueType::eBool) {
1785  eval_res->SetValue(val1->m_Bool >= val2->m_Bool);
1786  }
1787  else if (promoted_type == QueryValueType::eInt) {
1788  eval_res->SetValue(val1->m_Int >= val2->m_Int);
1789  }
1790  else if (promoted_type == QueryValueType::eFloat) {
1791  eval_res->SetValue(val1->m_Double >= val2->m_Double);
1792  }
1793  else if (promoted_type == QueryValueType::eString) {
1794  int result = NStr::Compare(val1->m_String.c_str(),
1795  val2->m_String.c_str(),
1796  GetCaseSensitive());
1797  eval_res->SetValue(result>=0);
1798  }
1799  break;
1800 
1801  case CQueryParseNode::eLT:
1802  if (promoted_type == QueryValueType::eBool) {
1803  eval_res->SetValue(val1->m_Bool < val2->m_Bool);
1804  }
1805  else if (promoted_type == QueryValueType::eInt) {
1806  eval_res->SetValue(val1->m_Int < val2->m_Int);
1807  }
1808  else if (promoted_type == QueryValueType::eFloat) {
1809  eval_res->SetValue(val1->m_Double < val2->m_Double);
1810  }
1811  else if (promoted_type == QueryValueType::eString) {
1812  int result = NStr::Compare(val1->m_String.c_str(),
1813  val2->m_String.c_str(),
1814  GetCaseSensitive());
1815  eval_res->SetValue(result<0);
1816  }
1817  break;
1818 
1819  case CQueryParseNode::eLE:
1820  if (promoted_type == QueryValueType::eBool) {
1821  eval_res->SetValue(val1->m_Bool <= val2->m_Bool );
1822  }
1823  else if (promoted_type == QueryValueType::eInt) {
1824  eval_res->SetValue(val1->m_Int <= val2->m_Int);
1825  }
1826  else if (promoted_type == QueryValueType::eFloat) {
1827  eval_res->SetValue(val1->m_Double <= val2->m_Double);
1828  }
1829  else if (promoted_type == QueryValueType::eString) {
1830  int result = NStr::Compare(val1->m_String.c_str(),
1831  val2->m_String.c_str(),
1832  GetCaseSensitive());
1833  eval_res->SetValue(result<=0);
1834  }
1835  break;
1836 
1837  default:
1838  // We already check for this wih previous 'if' so should not happen
1839  NCBI_THROW(CQueryExecException, eExecParseError,
1840  "Error - Unhandled operator: " +
1841  qnode.GetValue().GetNodeTypeAsString());
1842  break;
1843  }
1844  }
1845  else {
1846  NCBI_THROW(CQueryExecException, eExecParseError,
1847  "Error - Unhandled comparison operator: " +
1848  qnode.GetValue().GetNodeTypeAsString());
1849  }
1850 }
1851 
1852 /******************************************************************************
1853  *
1854  * CQueryFuncPromoteBetween Implementation
1855  *
1856  *****************************************************************************/
1859 {
1861 }
1862 
1864 {
1866  this->MakeArgVector(qnode, args);
1867 
1868  // get operation type (should be between)
1869  CQueryParseNode::EType node_type = qnode.GetValue().GetType();
1870 
1871  if (node_type == CQueryParseNode::eBetween) {
1872  TEvalResult* eval_res = GetQueryNodeValue(qnode);
1873 
1874  if (args.size() != 3) {
1875  NCBI_THROW(CQueryExecException, eWrongArgumentCount,
1876  "Error: Ternary operator " +
1877  qnode.GetValue().GetNodeTypeAsString() +
1878  " has " + NStr::SizetToString(args.size()) + " operands.");
1879  }
1880 
1881 
1882  // Get underlying data and data type. This is a ternary operator
1883  // and is equivalent to (a>b && a<c) || a==b || a==c. So get a
1884  // separate promotion type for (a,b) and (a,c) and do the comparisons
1885  // separately.
1886  QueryValueType::EBaseType promoted_type1 =
1887  ResolveAndPromote(0, qnode, args[0], args[1]);
1888 
1889  if (promoted_type1 == QueryValueType::eUndefined) {
1890  eval_res->SetValue(false);
1891  return;
1892  }
1893 
1894  TEvalResult* val1 = GetQueryNodeValue(*args[0]);
1895  TEvalResult* val2 = GetQueryNodeValue(*args[1]);
1896  TEvalResult* val3 = GetQueryNodeValue(*args[2]);
1897 
1898 
1899  // Set is_gt to true if val1 > val2, and set
1900  // is_eq to true if val1 == val2.
1901  bool is_gt = false;
1902  bool is_eq = false;
1903  if (promoted_type1 == QueryValueType::eInt) {
1904  is_gt = val1->m_Int > val2->m_Int;
1905  is_eq = val1->m_Int == val2->m_Int;
1906  }
1907  else if (promoted_type1 == QueryValueType::eFloat) {
1908  is_gt = val1->m_Double > val2->m_Double;
1909  is_eq = val1->m_Double == val2->m_Double;
1910  }
1911  else if (promoted_type1 == QueryValueType::eString) {
1912  int result = NStr::Compare(val1->m_String.c_str(),
1913  val2->m_String.c_str(),
1914  GetCaseSensitive());
1915  is_gt = (result > 0);
1916  is_eq = (result == 0);
1917  }
1918 
1919  // If val1==val2 (is_eq), we are done, otherwise
1920  // now set is_lt to true if val1 < val3.
1921  bool is_lt = false;
1922  if (!is_eq) {
1923  QueryValueType::EBaseType promoted_type2 =
1924  ResolveAndPromote(1, qnode, args[0], args[2]);
1925 
1926  if (promoted_type2 == QueryValueType::eUndefined) {
1927  eval_res->SetValue(false);
1928  return;
1929  }
1930 
1931  if (promoted_type2 == QueryValueType::eInt) {
1932  is_lt = val1->m_Int < val3->m_Int;
1933  is_eq = val1->m_Int == val3->m_Int;
1934  }
1935  else if (promoted_type2 == QueryValueType::eFloat) {
1936  is_lt = val1->m_Double < val3->m_Double;
1937  is_eq = val1->m_Double == val3->m_Double;
1938  }
1939  else if (promoted_type2 == QueryValueType::eString) {
1940  int result = NStr::Compare(val1->m_String.c_str(),
1941  val3->m_String.c_str(),
1942  GetCaseSensitive());
1943  is_lt = (result < 0);
1944  is_eq = (result == 0);
1945  }
1946  }
1947 
1948  // 'a' between 1 and 5 should give the same result as
1949  // 'a' between 5 and 1, so we check if the value 'a' is
1950  // greater than one value and less than the other, e.g.
1951  // check if is_gt == is_lt. The number is also between if
1952  // it is equal to either of the two bounding values.
1953  // a >= 1 and a <= 5;
1954  // a >= 5 and a <= 1;
1955  bool is_between = (is_gt == is_lt) || is_eq;
1956  if (qnode->IsNot())
1957  eval_res->SetValue(!is_between);
1958  else
1959  eval_res->SetValue(is_between);
1960  }
1961  else {
1962  NCBI_THROW(CQueryExecException, eExecParseError,
1963  "Error - Unhandled comparison operator: " +
1964  qnode.GetValue().GetNodeTypeAsString());
1965  }
1966 }
1967 
1968 
class CMacroQueryExec
bool ResolveRTVar(const string &identifier, CQueryNodeValue &v)
Return the value of RT variable in node "v".
macro::CMacroRep * GetMacroRep()
CRef< CQueryNodeValue > GetOrCreateRTVar(const string &name)
Gets or creates run-time vars (used in assignment in Do clause)
class CPromoteRule
QueryValueType::EBaseType m_Type1
The extended type for the first element to compare.
QueryValueType::EBaseType m_Type2
The extended type for the second element to compare.
CQueryParseNode::EType m_CompareOperator
The comparison operator applied to the two values, e.g. ==, <, ...
QueryValueType::EBaseType m_PromotedType
The type to be used for the comparison.
Expression evaluation visitor functor.
Definition: query_exec.hpp:249
class CQueryExecException
virtual void Evaluate(CQueryParseTree::TNode &node)
Function implements the assignment operator.
class CQueryFuncEqualityCompares
virtual void InitTypePromotionRules(CQueryParseNode::EType op_type)
Initialize promotion table for eqaulity comparisons (e.g. == or In)
virtual void Evaluate(CQueryParseTree::TNode &qnode)
Function implements functions calls in the do section of macro.
class CQueryFuncGtLtCompares
virtual void InitTypePromotionRules(CQueryParseNode::EType op_type)
Initialize promotion table for the comparisons.
CQueryFuncLike(NStr::ECase c=NStr::eCase)
Ctor.
virtual void Evaluate(CQueryParseTree::TNode &qnode)
Evaluate the node to see if 'Like' returns true.
virtual void InitTypePromotionRules(CQueryParseNode::EType op_type)
Initialize promotion rules for 'Like' comparison.
virtual void Evaluate(CQueryParseTree::TNode &qnode)
Try to convert the arguments into boolean values and, if successful, apply the specified boolean oper...
class CQueryFuncPromoteBase
void SetCaseSensitive(NStr::ECase e)
Get/set case sensitivity for all string compares for this function.
void AddPromoteTypes(CQueryParseNode::EType op, QueryValueType::EBaseType type1, QueryValueType::EBaseType type2, QueryValueType::EBaseType ptype)
Add the promtion rules for both 'type1 op type2' and 'type2 op type1'.
bool SetCompareType(TEvalResult &tree_value)
Given data for a node, determine its underlying data type (e.g.
virtual size_t GetArgCountMax(CQueryParseNode::EType)
CStringMatching::EStringMatching m_StringMatchAlgo
Determines how string equality comaparisons will be handled (wildcards, regex, exact....
std::vector< CPromoteRule > m_PromoteRules
Vector of sorted promotion rules.
TEvalResult * MakeQueryNodeValue(CQueryParseTree::TNode &qnode)
Get user object for 'qnode' or create a new object if it doesn't exist.
QueryValueType::EBaseType ResolveAndPromote(size_t comparison_idx, CQueryParseTree::TNode &qnode, CQueryParseTree::TNode *arg1, CQueryParseTree::TNode *arg2)
For the operator in 'qnode', retrive from data source (if ncessary) the data values arg1 and arg2 and...
virtual size_t GetArgCountMin(CQueryParseNode::EType)
bool ResolveFieldValue(TEvalResult &tree_val)
Get the value of a field from the data source and store the result in the CQueryNodeValue object.
void SetStringMatching(CStringMatching::EStringMatching m)
Get/set string matching algorithm.
QueryValueType::EBaseType GetPromotedType(const CPromoteRule &pr)
Given data from two nodes and a comparison operator (e.g.
NStr::ECase GetCaseSensitive() const
void PreProcess(CQueryParseTree::TNode &tr, objects::CScope *scope)
Resolve, if possible, the data type for the query node tr and, for operator nodes (e....
TEvalResult * GetQueryNodeValue(CQueryParseTree::TNode &qnode)
Get the user object for node 'qnode' or NULL if not created.
CQueryFuncPromoteBetween(NStr::ECase c=NStr::eCase)
Ctor.
virtual void Evaluate(CQueryParseTree::TNode &qnode)
Evaluate the node to see if 'Between' returns true.
class CQueryFuncPromoteCompare
CQueryFuncPromoteCompare(CQueryParseNode::EType op_type, NStr::ECase c=NStr::eCase, CStringMatching::EStringMatching matching=(CStringMatching::ePlainSearch))
Ctors for compare must include op_type for loading promotion rules.
CQueryFuncPromoteEq(NStr::ECase c=NStr::eCase, CStringMatching::EStringMatching matching=CStringMatching::ePlainSearch)
Ctor.
virtual void Evaluate(CQueryParseTree::TNode &qnode)
Evaluate the node to see if '==' returns true.
CQueryFuncPromoteGtLt(CQueryParseNode::EType op_type, NStr::ECase c=NStr::eCase)
Ctor includes op_type since we will create a separate instance for each of >,<, >= and <=.
virtual void Evaluate(CQueryParseTree::TNode &qnode)
Evaluate the node to see if 'greater than/less than' returns true.
virtual void Evaluate(CQueryParseTree::TNode &qnode)
Create the user object for the node if it does not yet exist and reset the boolean 'result' value for...
CQueryFuncPromoteIn(NStr::ECase c=NStr::eCase, CStringMatching::EStringMatching matching=CStringMatching::ePlainSearch)
ctor
virtual void Evaluate(CQueryParseTree::TNode &qnode)
Evaluate the node to see if 'In' returns true.
virtual void Evaluate(CQueryParseTree::TNode &qnode)
Try to convert the arguments into boolean values and, if successful, apply the specified boolean oper...
virtual void Evaluate(CQueryParseTree::TNode &qnode)
Create the user object for the node if it does not yet exist and reset the boolean 'result' value for...
virtual void Evaluate(CQueryParseTree::TNode &qnode)
Function is a placeholder for run-time variable processing.
class CQueryNodeValue
Int8 m_Int
Int data, if data was an integer or converted into one.
bool IsDataField() const
bool HasPromoteType(size_t arg_idx, QueryValueType::EBaseType t1, QueryValueType::EBaseType t2)
Return true if there is a promote entry defined for the specified argument pair at 'idx' only if the ...
bool AssignToRef(const CQueryNodeValue &source)
void SetFieldID(TFieldIDType fid)
CQueryParseTree::TNode * GetQueryNode()
Get corresponding query node.
void PromoteTo(QueryValueType::EBaseType pt)
Convert current value to the type 'pt'. Does not update m_DataType.
bool m_Bool
Bool data, if data base a constant boolean or converted into one.
virtual void Reset()
Reset user object (for reuse without reallocation)
string m_String
String data, if data came from a string or data field in the tree.
void SetIsDataField(bool b)
Set/Get to indicate if this is a field from the data source or simple string.
double m_Double
Floating point data, if data was a double or converted into one.
void SetDataType(QueryValueType::EBaseType dt)
Set/get underlying data type.
objects::CScope * GetScope()
void AddPromotedType(const CPromoteRule &pr)
Append a new promote rule.
bool GetValue() const
Set boolean result value (result of (sub)expression).
TFieldIDType GetFieldID() const
void SetScope(objects::CScope *s)
Set/Get CScope used for comparing seq-ids.
void SetRef(CRef< CQueryNodeValue > node)
QueryValueType::EBaseType GetPromoteType(size_t arg_idx)
Get the promotion type for a specific argument pair, or eUndefined if no rule is available.
QueryValueType::EBaseType GetDataType() const
Query node class.
Definition: query_parse.hpp:79
CStringException –.
Definition: ncbistr.hpp:4506
class CTextSearch
EStringMatching
String matching algorithms.
@ ePlainSearch
Plain search.
bool MatchString(const CTempString &str)
Matches a string to a pattern, using the specified string matching algorithm.
CTempString implements a light-weight string on top of a storage buffer whose lifetime management is ...
Definition: tempstr.hpp:65
definition of a Culling tree
Definition: ncbi_tree.hpp:100
Base class for query node user defined object.
Definition: query_parse.hpp:60
Include a standard set of the NCBI C++ Toolkit most basic headers.
static const char * str(char *buf, int n)
Definition: stats.c:84
#define NCBI_THROW(exception_class, err_code, message)
Generic macro to throw an exception, given the exception class, error code and message string.
Definition: ncbiexpt.hpp:704
const string & GetMsg(void) const
Get message string.
Definition: ncbiexpt.cpp:461
static bool Match(const objects::CSeq_id &id1, const objects::CSeq_id &id2, objects::CScope *scope=NULL)
check to see if two seq-ids are identical.
CRef< C > Ref(C *object)
Helper functions to get CRef<> and CConstRef<> objects.
Definition: ncbiobj.hpp:2015
CQueryExec * m_QExec
Definition: query_exec.hpp:119
const string & GetOrig() const
const IQueryParseUserObject * GetUserObject() const
Get user object.
string GetNodeTypeAsString() const
Return query node type as a string (for debugging output)
Int8 GetInt() const
double GetDouble() const
vector< CQueryParseTree::TNode * > TArgVector
Vector for easy argument access.
Definition: query_exec.hpp:72
virtual TFieldID GetIdentifier(const std::string &)
Definition: query_exec.hpp:211
EType
Query node type.
Definition: query_parse.hpp:84
bool IsValue() const
Returns TRUE if node is value (INT, String, etc.)
virtual bool ResolveIdentifier(const std::string &, bool &)
If query has an identifier, this will resolve it in an application-specific way.
Definition: query_exec.hpp:192
void SetUserObject(IQueryParseUserObject *obj)
virtual void CallFunction(const string &name, CQueryParseTree::TNode &node)
Extend this function to look up and invoke functions that appear in the query.
Definition: query_exec.hpp:177
virtual CQueryParseNode::EType IdentifierType(const std::string &)
Some applications may know the type of an identifier.
Definition: query_exec.hpp:216
void MakeArgVector(CQueryParseTree::TNode &qnode, TArgVector &args)
Created vector of arguments (translate sub-nodes to vector)
Definition: query_exec.cpp:44
const string & GetStrValue() const
bool GetBool() const
virtual bool HasIdentifier(const std::string &)
Definition: query_exec.hpp:210
EType GetType() const
bool IsLogic() const
Returns TRUE if node describes logical operation (AND, OR, etc.)
@ eFunction
Function.
Definition: query_parse.hpp:91
@ eIdentifier
Identifier like db.field (Org, Fld12, etc.)
Definition: query_parse.hpp:86
@ eFloatConst
Floating point const.
Definition: query_parse.hpp:88
@ eIntConst
Integer const.
Definition: query_parse.hpp:87
@ eBoolConst
Boolean (TRUE or FALSE)
Definition: query_parse.hpp:89
@ eString
String ("free text")
Definition: query_parse.hpp:90
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
static bool StringToBool(const CTempString str)
Convert string to bool.
Definition: ncbistr.cpp:2821
static string SizetToString(size_t value, TNumToStringFlags flags=0, int base=10)
Convert size_t to string.
Definition: ncbistr.cpp:2751
static int CompareNocase(const CTempString s1, SIZE_TYPE pos, SIZE_TYPE n, const char *s2)
Case-insensitive compare of a substring with another string.
Definition: ncbistr.cpp:219
static int StringToInt(const CTempString str, TStringToNumFlags flags=0, int base=10)
Convert string to int.
Definition: ncbistr.cpp:630
static bool MatchesMask(CTempString str, CTempString mask, ECase use_case=eCase)
Match "str" against the "mask".
Definition: ncbistr.cpp:389
static double StringToDouble(const CTempStringEx str, TStringToNumFlags flags=0)
Convert string to double.
Definition: ncbistr.cpp:1387
PNocase_Generic< string > PNocase
Definition: ncbistr.hpp:4908
bool AStrEquiv(const Arg1 &x, const Arg2 &y, Pred pr)
Check equivalence of arguments using predicate.
Definition: ncbistr.hpp:5037
static int Compare(const CTempString s1, SIZE_TYPE pos, SIZE_TYPE n, const char *s2, ECase use_case=eCase)
Compare of a substring with another string.
Definition: ncbistr.hpp:5297
ECase
Which type of string comparison.
Definition: ncbistr.hpp:1204
@ fConvErr_NoThrow
Do not throw an exception on error.
Definition: ncbistr.hpp:285
@ eNocase
Case insensitive compare.
Definition: ncbistr.hpp:1206
Fun TreeDepthFirstTraverse(TTreeNode &tree_node, Fun func)
Depth-first tree traversal algorithm.
Definition: ncbi_tree.hpp:504
const TValue & GetValue(void) const
Return node's value.
Definition: ncbi_tree.hpp:184
int i
Macro exceptions.
EBaseType
Set of all possible types for nodes.
constexpr auto sort(_Init &&init)
int tolower(Uchar c)
Definition: ncbictype.hpp:72
int isdigit(Uchar c)
Definition: ncbictype.hpp:64
Portable reference counted smart and weak pointers using CWeakRef, CRef, CObject and CObjectEx.
static int match(register const pcre_uchar *eptr, register const pcre_uchar *ecode, const pcre_uchar *mstart, int offset_top, match_data *md, eptrblock *eptrb, unsigned int rdepth)
Definition: pcre_exec.c:513
static const char * s_kFString
static const char * s_kYString
static const char * s_kNoString
static const char * s_kFalseString
static const char * s_kYesString
static const char * s_kNString
bool FastStringToBool(const CTempString str, bool &isbool)
static const char * s_kTrueString
static const char * s_kTString
#define _ASSERT
else result
Definition: token2.c:20
Modified on Wed May 01 14:24:08 2024 by modify_doxy.py rev. 669887