LCOV - code coverage report
Current view: top level - ogr - swq.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 441 477 92.5 %
Date: 2025-03-28 11:40:40 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Component: OGDI Driver Support Library
       4             :  * Purpose: Generic SQL WHERE Expression Implementation.
       5             :  * Author: Frank Warmerdam <warmerdam@pobox.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (C) 2001 Information Interoperability Institute (3i)
       9             :  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * Permission to use, copy, modify and distribute this software and
      12             :  * its documentation for any purpose and without fee is hereby granted,
      13             :  * provided that the above copyright notice appear in all copies, that
      14             :  * both the copyright notice and this permission notice appear in
      15             :  * supporting documentation, and that the name of 3i not be used
      16             :  * in advertising or publicity pertaining to distribution of the software
      17             :  * without specific, written prior permission.  3i makes no
      18             :  * representations about the suitability of this software for any purpose.
      19             :  * It is provided "as is" without express or implied warranty.
      20             :  ****************************************************************************/
      21             : 
      22             : #include "cpl_port.h"
      23             : #include "ogr_swq.h"
      24             : 
      25             : #include <cassert>
      26             : #include <cctype>
      27             : #include <cmath>
      28             : #include <cstddef>
      29             : #include <cstdlib>
      30             : #include <cstring>
      31             : #include <ctime>
      32             : 
      33             : #include <algorithm>
      34             : #include <limits>
      35             : #include <string>
      36             : 
      37             : #include "cpl_error.h"
      38             : #include "cpl_multiproc.h"
      39             : #include "cpl_time.h"
      40             : #include "swq_parser.hpp"
      41             : 
      42             : #define YYSTYPE swq_expr_node *
      43             : 
      44             : /************************************************************************/
      45             : /*                               swqlex()                               */
      46             : /************************************************************************/
      47             : 
      48         135 : void swqerror(swq_parse_context *context, const char *msg)
      49             : {
      50         270 :     CPLString osMsg;
      51         135 :     osMsg.Printf("SQL Expression Parsing Error: %s. Occurred around :\n", msg);
      52             : 
      53         135 :     int n = static_cast<int>(context->pszLastValid - context->pszInput);
      54             : 
      55        3348 :     for (int i = std::max(0, n - 40);
      56        3348 :          i < n + 40 && context->pszInput[i] != '\0'; i++)
      57        3213 :         osMsg += context->pszInput[i];
      58         135 :     osMsg += "\n";
      59        1290 :     for (int i = 0; i < std::min(n, 40); i++)
      60        1155 :         osMsg += " ";
      61         135 :     osMsg += "^";
      62             : 
      63         135 :     CPLError(CE_Failure, CPLE_AppDefined, "%s", osMsg.c_str());
      64         135 : }
      65             : 
      66             : /************************************************************************/
      67             : /*                               swqlex()                               */
      68             : /*                                                                      */
      69             : /*      Read back a token from the input.                               */
      70             : /************************************************************************/
      71             : 
      72       90902 : int swqlex(YYSTYPE *ppNode, swq_parse_context *context)
      73             : {
      74       90902 :     const char *pszInput = context->pszNext;
      75             : 
      76       90902 :     *ppNode = nullptr;
      77             : 
      78             :     /* -------------------------------------------------------------------- */
      79             :     /*      Do we have a start symbol to return?                            */
      80             :     /* -------------------------------------------------------------------- */
      81       90902 :     if (context->nStartToken != 0)
      82             :     {
      83        8052 :         int nRet = context->nStartToken;
      84        8052 :         context->nStartToken = 0;
      85        8052 :         return nRet;
      86             :     }
      87             : 
      88             :     /* -------------------------------------------------------------------- */
      89             :     /*      Skip white space.                                               */
      90             :     /* -------------------------------------------------------------------- */
      91      125282 :     while (*pszInput == ' ' || *pszInput == '\t' || *pszInput == 10 ||
      92       82850 :            *pszInput == 13)
      93       42432 :         pszInput++;
      94             : 
      95       82850 :     context->pszLastValid = pszInput;
      96             : 
      97       82850 :     if (*pszInput == '\0')
      98             :     {
      99        7918 :         context->pszNext = pszInput;
     100        7918 :         return EOF;
     101             :     }
     102             : 
     103             :     /* -------------------------------------------------------------------- */
     104             :     /*      Handle string constants.                                        */
     105             :     /* -------------------------------------------------------------------- */
     106       74932 :     if (*pszInput == '"' || *pszInput == '\'')
     107             :     {
     108        3162 :         char chQuote = *pszInput;
     109        3162 :         bool bFoundEndQuote = false;
     110             : 
     111        3162 :         int nRet = *pszInput == '"' ? SWQT_IDENTIFIER : SWQT_STRING;
     112             : 
     113        3162 :         pszInput++;
     114             : 
     115        3162 :         char *token = static_cast<char *>(CPLMalloc(strlen(pszInput) + 1));
     116        3162 :         int i_token = 0;
     117             : 
     118       42692 :         while (*pszInput != '\0')
     119             :         {
     120             :             // Not totally sure we need to preserve this way of escaping for
     121             :             // strings between double-quotes
     122       42691 :             if (chQuote == '"' && *pszInput == '\\')
     123             :             {
     124           0 :                 pszInput++;
     125           0 :                 if (*pszInput == '\0')
     126           0 :                     break;
     127             :             }
     128       42691 :             else if (chQuote == '\'' && *pszInput == '\'' &&
     129        1737 :                      pszInput[1] == '\'')
     130           3 :                 pszInput++;
     131       42688 :             else if (*pszInput == chQuote)
     132             :             {
     133        3161 :                 pszInput++;
     134        3161 :                 bFoundEndQuote = true;
     135        3161 :                 break;
     136             :             }
     137             : 
     138       39530 :             token[i_token++] = *(pszInput++);
     139             :         }
     140        3162 :         token[i_token] = '\0';
     141             : 
     142        3162 :         if (!bFoundEndQuote)
     143             :         {
     144           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     145             :                      "Did not find end-of-string character");
     146           1 :             CPLFree(token);
     147           1 :             return YYerror;
     148             :         }
     149             : 
     150        3161 :         *ppNode = new swq_expr_node(token);
     151        3161 :         CPLFree(token);
     152             : 
     153        3161 :         context->pszNext = pszInput;
     154             : 
     155        3161 :         return nRet;
     156             :     }
     157             : 
     158             :     /* -------------------------------------------------------------------- */
     159             :     /*      Handle numbers.                                                 */
     160             :     /* -------------------------------------------------------------------- */
     161       71770 :     else if (*pszInput >= '0' && *pszInput <= '9')
     162             :     {
     163       35758 :         CPLString osToken;
     164       17879 :         const char *pszNext = pszInput + 1;
     165             : 
     166       17879 :         osToken += *pszInput;
     167             : 
     168             :         // collect non-decimal part of number
     169       26852 :         while (*pszNext >= '0' && *pszNext <= '9')
     170        8973 :             osToken += *(pszNext++);
     171             : 
     172             :         // collect decimal places.
     173       17879 :         if (*pszNext == '.')
     174             :         {
     175         477 :             osToken += *(pszNext++);
     176        2144 :             while (*pszNext >= '0' && *pszNext <= '9')
     177        1667 :                 osToken += *(pszNext++);
     178             :         }
     179             : 
     180             :         // collect exponent
     181       17879 :         if (*pszNext == 'e' || *pszNext == 'E')
     182             :         {
     183           9 :             osToken += *(pszNext++);
     184           9 :             if (*pszNext == '-' || *pszNext == '+')
     185           9 :                 osToken += *(pszNext++);
     186          34 :             while (*pszNext >= '0' && *pszNext <= '9')
     187          25 :                 osToken += *(pszNext++);
     188             :         }
     189             : 
     190       17879 :         context->pszNext = pszNext;
     191             : 
     192       35272 :         if (strstr(osToken, ".") || strstr(osToken, "e") ||
     193       17393 :             strstr(osToken, "E"))
     194             :         {
     195         486 :             *ppNode = new swq_expr_node(CPLAtof(osToken));
     196         486 :             return SWQT_FLOAT_NUMBER;
     197             :         }
     198             :         else
     199             :         {
     200       34813 :             if (osToken.size() > 19 ||
     201       17420 :                 (osToken.size() >= 19 && osToken > "9223372036854775807"))
     202             :             {
     203          12 :                 *ppNode = new swq_expr_node(CPLAtof(osToken));
     204          12 :                 if (osToken == "9223372036854775808")
     205          11 :                     (*ppNode)->string_value = CPLStrdup(osToken);
     206          12 :                 return SWQT_FLOAT_NUMBER;
     207             :             }
     208             : 
     209       17381 :             GIntBig nVal = CPLAtoGIntBig(osToken);
     210       17381 :             if (CPL_INT64_FITS_ON_INT32(nVal))
     211       17284 :                 *ppNode = new swq_expr_node(static_cast<int>(nVal));
     212             :             else
     213          97 :                 *ppNode = new swq_expr_node(nVal);
     214       17381 :             return SWQT_INTEGER_NUMBER;
     215             :         }
     216             :     }
     217             : 
     218             :     /* -------------------------------------------------------------------- */
     219             :     /*      Handle alpha-numerics.                                          */
     220             :     /* -------------------------------------------------------------------- */
     221       53891 :     else if (isalnum(static_cast<unsigned char>(*pszInput)))
     222             :     {
     223       26324 :         int nReturn = SWQT_IDENTIFIER;
     224       26324 :         CPLString osToken;
     225       26324 :         const char *pszNext = pszInput + 1;
     226             : 
     227       26324 :         osToken += *pszInput;
     228             : 
     229             :         // collect text characters
     230       91032 :         while (isalnum(static_cast<unsigned char>(*pszNext)) ||
     231      117356 :                *pszNext == '_' || static_cast<unsigned char>(*pszNext) > 127)
     232       91032 :             osToken += *(pszNext++);
     233             : 
     234       26324 :         context->pszNext = pszNext;
     235             : 
     236       26324 :         if (EQUAL(osToken, "IN"))
     237         154 :             nReturn = SWQT_IN;
     238       26170 :         else if (EQUAL(osToken, "LIKE"))
     239          53 :             nReturn = SWQT_LIKE;
     240       26117 :         else if (EQUAL(osToken, "ILIKE"))
     241          31 :             nReturn = SWQT_ILIKE;
     242       26086 :         else if (EQUAL(osToken, "ESCAPE"))
     243           3 :             nReturn = SWQT_ESCAPE;
     244       26083 :         else if (EQUAL(osToken, "EXCEPT"))
     245          14 :             nReturn = SWQT_EXCEPT;
     246       26069 :         else if (EQUAL(osToken, "EXCLUDE"))
     247           2 :             nReturn = SWQT_EXCLUDE;
     248       26067 :         else if (EQUAL(osToken, "NULL"))
     249         307 :             nReturn = SWQT_NULL;
     250       25760 :         else if (EQUAL(osToken, "IS"))
     251         223 :             nReturn = SWQT_IS;
     252       25537 :         else if (EQUAL(osToken, "NOT"))
     253         269 :             nReturn = SWQT_NOT;
     254       25268 :         else if (EQUAL(osToken, "AND"))
     255        2504 :             nReturn = SWQT_AND;
     256       22764 :         else if (EQUAL(osToken, "OR"))
     257        2281 :             nReturn = SWQT_OR;
     258       20483 :         else if (EQUAL(osToken, "BETWEEN"))
     259          29 :             nReturn = SWQT_BETWEEN;
     260       20454 :         else if (EQUAL(osToken, "SELECT"))
     261        2858 :             nReturn = SWQT_SELECT;
     262       17596 :         else if (EQUAL(osToken, "LEFT"))
     263          27 :             nReturn = SWQT_LEFT;
     264       17569 :         else if (EQUAL(osToken, "JOIN"))
     265          72 :             nReturn = SWQT_JOIN;
     266       17497 :         else if (EQUAL(osToken, "WHERE"))
     267        1163 :             nReturn = SWQT_WHERE;
     268       16334 :         else if (EQUAL(osToken, "ON"))
     269          72 :             nReturn = SWQT_ON;
     270       16262 :         else if (EQUAL(osToken, "ORDER"))
     271         112 :             nReturn = SWQT_ORDER;
     272       16150 :         else if (EQUAL(osToken, "BY"))
     273         112 :             nReturn = SWQT_BY;
     274       16038 :         else if (EQUAL(osToken, "FROM"))
     275        2839 :             nReturn = SWQT_FROM;
     276       13199 :         else if (EQUAL(osToken, "AS"))
     277         163 :             nReturn = SWQT_AS;
     278       13036 :         else if (EQUAL(osToken, "ASC"))
     279          34 :             nReturn = SWQT_ASC;
     280       13002 :         else if (EQUAL(osToken, "DESC"))
     281          20 :             nReturn = SWQT_DESC;
     282       12982 :         else if (EQUAL(osToken, "DISTINCT"))
     283          47 :             nReturn = SWQT_DISTINCT;
     284       12935 :         else if (EQUAL(osToken, "CAST"))
     285          94 :             nReturn = SWQT_CAST;
     286       12841 :         else if (EQUAL(osToken, "UNION"))
     287           5 :             nReturn = SWQT_UNION;
     288       12836 :         else if (EQUAL(osToken, "ALL"))
     289           5 :             nReturn = SWQT_ALL;
     290       12831 :         else if (EQUAL(osToken, "LIMIT"))
     291          45 :             nReturn = SWQT_LIMIT;
     292       12786 :         else if (EQUAL(osToken, "OFFSET"))
     293          19 :             nReturn = SWQT_OFFSET;
     294       12767 :         else if (EQUAL(osToken, "HIDDEN"))
     295             :         {
     296          21 :             *ppNode = new swq_expr_node(osToken);
     297          21 :             nReturn = SWQT_HIDDEN;
     298             :         }
     299             : 
     300             :         // Unhandled by OGR SQL.
     301       12746 :         else if (EQUAL(osToken, "OUTER") || EQUAL(osToken, "INNER"))
     302           0 :             nReturn = SWQT_RESERVED_KEYWORD;
     303             : 
     304             :         else
     305             :         {
     306       12746 :             *ppNode = new swq_expr_node(osToken);
     307       12746 :             nReturn = SWQT_IDENTIFIER;
     308             :         }
     309             : 
     310       26324 :         return nReturn;
     311             :     }
     312             : 
     313             :     /* -------------------------------------------------------------------- */
     314             :     /*      Handle special tokens.                                          */
     315             :     /* -------------------------------------------------------------------- */
     316             :     else
     317             :     {
     318       27567 :         context->pszNext = pszInput + 1;
     319       27567 :         return *pszInput;
     320             :     }
     321             : }
     322             : 
     323             : /************************************************************************/
     324             : /*                        swq_select_summarize()                        */
     325             : /************************************************************************/
     326             : 
     327         527 : const char *swq_select_summarize(swq_select *select_info, int dest_column,
     328             :                                  const char *pszValue, const double *pdfValue)
     329             : 
     330             : {
     331             :     /* -------------------------------------------------------------------- */
     332             :     /*      Do various checking.                                            */
     333             :     /* -------------------------------------------------------------------- */
     334         527 :     if (select_info->query_mode == SWQM_RECORDSET)
     335           0 :         return "swq_select_summarize() called on non-summary query.";
     336             : 
     337        1054 :     if (dest_column < 0 ||
     338         527 :         dest_column >= static_cast<int>(select_info->column_defs.size()))
     339           0 :         return "dest_column out of range in swq_select_summarize().";
     340             : 
     341         527 :     const swq_col_def *def = &select_info->column_defs[dest_column];
     342         527 :     if (def->col_func == SWQCF_NONE && !def->distinct_flag)
     343           0 :         return nullptr;
     344             : 
     345         527 :     if (select_info->query_mode == SWQM_DISTINCT_LIST &&
     346         290 :         select_info->order_specs > 0)
     347             :     {
     348         275 :         if (select_info->order_specs > 1)
     349           1 :             return "Can't ORDER BY a DISTINCT list by more than one key.";
     350             : 
     351         548 :         if (select_info->order_defs[0].field_index !=
     352         274 :             select_info->column_defs[0].field_index)
     353           1 :             return "Only selected DISTINCT field can be used for ORDER BY.";
     354             :     }
     355             : 
     356             :     /* -------------------------------------------------------------------- */
     357             :     /*      Create the summary information if this is the first row         */
     358             :     /*      being processed.                                                */
     359             :     /* -------------------------------------------------------------------- */
     360         525 :     if (select_info->column_summary.empty())
     361             :     {
     362          51 :         select_info->column_summary.resize(select_info->column_defs.size());
     363         138 :         for (std::size_t i = 0; i < select_info->column_defs.size(); i++)
     364             :         {
     365          87 :             if (def->distinct_flag)
     366             :             {
     367          26 :                 swq_summary::Comparator oComparator;
     368          26 :                 if (select_info->order_specs > 0)
     369             :                 {
     370          18 :                     CPLAssert(select_info->order_specs == 1);
     371          18 :                     CPLAssert(select_info->column_defs.size() == 1);
     372          18 :                     oComparator.bSortAsc =
     373          18 :                         CPL_TO_BOOL(select_info->order_defs[0].ascending_flag);
     374             :                 }
     375          45 :                 if (select_info->column_defs[i].field_type == SWQ_INTEGER ||
     376          19 :                     -select_info->column_defs[i].field_type == SWQ_INTEGER64)
     377             :                 {
     378           7 :                     oComparator.eType = SWQ_INTEGER64;
     379             :                 }
     380          19 :                 else if (select_info->column_defs[i].field_type == SWQ_FLOAT)
     381             :                 {
     382           4 :                     oComparator.eType = SWQ_FLOAT;
     383             :                 }
     384             :                 else
     385             :                 {
     386          15 :                     oComparator.eType = SWQ_STRING;
     387             :                 }
     388          26 :                 select_info->column_summary[i].oSetDistinctValues =
     389          52 :                     std::set<CPLString, swq_summary::Comparator>(oComparator);
     390             :             }
     391          87 :             select_info->column_summary[i].min =
     392          87 :                 std::numeric_limits<double>::infinity();
     393         174 :             select_info->column_summary[i].max =
     394          87 :                 -std::numeric_limits<double>::infinity();
     395          87 :             select_info->column_summary[i].osMin = "9999/99/99 99:99:99";
     396          87 :             select_info->column_summary[i].osMax = "0000/00/00 00:00:00";
     397             :         }
     398          51 :         assert(!select_info->column_summary.empty());
     399             :     }
     400             : 
     401             :     /* -------------------------------------------------------------------- */
     402             :     /*      If distinct processing is on, process that now.                 */
     403             :     /* -------------------------------------------------------------------- */
     404         525 :     swq_summary &summary = select_info->column_summary[dest_column];
     405             : 
     406         525 :     if (def->distinct_flag)
     407             :     {
     408         310 :         if (pszValue == nullptr)
     409          19 :             pszValue = SZ_OGR_NULL;
     410             :         try
     411             :         {
     412         310 :             if (!cpl::contains(summary.oSetDistinctValues, pszValue))
     413             :             {
     414         115 :                 summary.oSetDistinctValues.insert(pszValue);
     415         115 :                 if (select_info->order_specs == 0)
     416             :                 {
     417             :                     // If not sorted, keep values in their original order
     418          25 :                     summary.oVectorDistinctValues.emplace_back(pszValue);
     419             :                 }
     420         115 :                 summary.count++;
     421             :             }
     422             :         }
     423           0 :         catch (std::bad_alloc &)
     424             :         {
     425           0 :             return "Out of memory";
     426             :         }
     427             : 
     428         310 :         return nullptr;
     429             :     }
     430             : 
     431             :     /* -------------------------------------------------------------------- */
     432             :     /*      Process various options.                                        */
     433             :     /* -------------------------------------------------------------------- */
     434             : 
     435         215 :     switch (def->col_func)
     436             :     {
     437          47 :         case SWQCF_MIN:
     438          47 :             if (pdfValue)
     439             :             {
     440          26 :                 if (*pdfValue < summary.min)
     441           9 :                     summary.min = *pdfValue;
     442          26 :                 summary.count++;
     443             :             }
     444          21 :             else if (pszValue && pszValue[0] != '\0')
     445             :             {
     446          17 :                 if (summary.count == 0 || strcmp(pszValue, summary.osMin) < 0)
     447             :                 {
     448          13 :                     summary.osMin = pszValue;
     449             :                 }
     450          17 :                 summary.count++;
     451             :             }
     452          47 :             break;
     453          46 :         case SWQCF_MAX:
     454          46 :             if (pdfValue)
     455             :             {
     456          25 :                 if (*pdfValue > summary.max)
     457          15 :                     summary.max = *pdfValue;
     458          25 :                 summary.count++;
     459             :             }
     460          21 :             else if (pszValue && pszValue[0] != '\0')
     461             :             {
     462          17 :                 if (summary.count == 0 || strcmp(pszValue, summary.osMax) > 0)
     463             :                 {
     464           9 :                     summary.osMax = pszValue;
     465             :                 }
     466          17 :                 summary.count++;
     467             :             }
     468          46 :             break;
     469          52 :         case SWQCF_AVG:
     470             :         case SWQCF_SUM:
     471          52 :             if (pdfValue)
     472             :             {
     473          39 :                 summary.count++;
     474             : 
     475             :                 // Cf KahanBabushkaNeumaierSum of
     476             :                 // https://en.wikipedia.org/wiki/Kahan_summation_algorithm#Further_enhancements
     477             :                 // We set a number of temporary variables as volatile, to
     478             :                 // prevent potential undesired compiler optimizations.
     479             : 
     480          39 :                 const double dfNewVal = *pdfValue;
     481          39 :                 const volatile double new_sum_acc = summary.sum_acc + dfNewVal;
     482          39 :                 if (summary.sum_only_finite_terms && std::isfinite(dfNewVal))
     483             :                 {
     484          33 :                     if (std::fabs(summary.sum_acc) >= std::fabs(dfNewVal))
     485             :                     {
     486             :                         const volatile double diff =
     487          18 :                             (summary.sum_acc - new_sum_acc);
     488          18 :                         summary.sum_correction += (diff + dfNewVal);
     489             :                     }
     490             :                     else
     491             :                     {
     492          15 :                         const volatile double diff = (dfNewVal - new_sum_acc);
     493          15 :                         summary.sum_correction += (diff + summary.sum_acc);
     494             :                     }
     495             :                 }
     496             :                 else
     497             :                 {
     498           6 :                     summary.sum_only_finite_terms = false;
     499             :                 }
     500          39 :                 summary.sum_acc = new_sum_acc;
     501             :             }
     502          13 :             else if (pszValue && pszValue[0] != '\0')
     503             :             {
     504           2 :                 if (def->field_type == SWQ_DATE ||
     505           2 :                     def->field_type == SWQ_TIME ||
     506           2 :                     def->field_type == SWQ_TIMESTAMP)
     507             :                 {
     508             :                     OGRField sField;
     509           2 :                     if (OGRParseDate(pszValue, &sField, 0))
     510             :                     {
     511             :                         struct tm brokendowntime;
     512           2 :                         brokendowntime.tm_year = sField.Date.Year - 1900;
     513           2 :                         brokendowntime.tm_mon = sField.Date.Month - 1;
     514           2 :                         brokendowntime.tm_mday = sField.Date.Day;
     515           2 :                         brokendowntime.tm_hour = sField.Date.Hour;
     516           2 :                         brokendowntime.tm_min = sField.Date.Minute;
     517           2 :                         brokendowntime.tm_sec =
     518           2 :                             static_cast<int>(sField.Date.Second);
     519           2 :                         summary.count++;
     520           2 :                         summary.sum_acc += CPLYMDHMSToUnixTime(&brokendowntime);
     521           2 :                         summary.sum_acc +=
     522           2 :                             fmod(static_cast<double>(sField.Date.Second), 1.0);
     523           2 :                     }
     524             :                 }
     525             :                 else
     526             :                 {
     527             :                     return "swq_select_summarize() - AVG()/SUM() called on "
     528           0 :                            "unexpected field type";
     529             :                 }
     530             :             }
     531          52 :             break;
     532             : 
     533          50 :         case SWQCF_COUNT:
     534          50 :             if (pdfValue || pszValue)
     535          50 :                 summary.count++;
     536          50 :             break;
     537             : 
     538          20 :         case SWQCF_STDDEV_POP:
     539             :         case SWQCF_STDDEV_SAMP:
     540             :         {
     541          18 :             const auto UpdateVariance = [&summary](double dfValue)
     542             :             {
     543             :                 // Welford's online algorithm for variance:
     544             :                 // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
     545          18 :                 summary.count++;
     546          18 :                 const double dfDelta = dfValue - summary.mean_for_variance;
     547          18 :                 summary.mean_for_variance += dfDelta / summary.count;
     548          18 :                 const double dfDelta2 = dfValue - summary.mean_for_variance;
     549          18 :                 summary.sq_dist_from_mean_acc += dfDelta * dfDelta2;
     550          38 :             };
     551             : 
     552          20 :             if (pdfValue)
     553             :             {
     554          14 :                 UpdateVariance(*pdfValue);
     555             :             }
     556           6 :             else if (pszValue && pszValue[0] != '\0')
     557             :             {
     558           4 :                 if (def->field_type == SWQ_DATE ||
     559           4 :                     def->field_type == SWQ_TIME ||
     560           4 :                     def->field_type == SWQ_TIMESTAMP)
     561             :                 {
     562             :                     OGRField sField;
     563           4 :                     if (OGRParseDate(pszValue, &sField, 0))
     564             :                     {
     565             :                         struct tm brokendowntime;
     566           4 :                         brokendowntime.tm_year = sField.Date.Year - 1900;
     567           4 :                         brokendowntime.tm_mon = sField.Date.Month - 1;
     568           4 :                         brokendowntime.tm_mday = sField.Date.Day;
     569           4 :                         brokendowntime.tm_hour = sField.Date.Hour;
     570           4 :                         brokendowntime.tm_min = sField.Date.Minute;
     571           4 :                         brokendowntime.tm_sec =
     572           4 :                             static_cast<int>(sField.Date.Second);
     573             : 
     574           4 :                         UpdateVariance(static_cast<double>(
     575           4 :                             CPLYMDHMSToUnixTime(&brokendowntime)));
     576           4 :                     }
     577             :                 }
     578             :                 else
     579             :                 {
     580             :                     return "swq_select_summarize() - STDDEV() called on "
     581           0 :                            "unexpected field type";
     582             :                 }
     583             :             }
     584             : 
     585          20 :             break;
     586             :         }
     587             : 
     588           0 :         case SWQCF_NONE:
     589           0 :             break;
     590             : 
     591           0 :         case SWQCF_CUSTOM:
     592           0 :             return "swq_select_summarize() called on custom field function.";
     593             :     }
     594             : 
     595         215 :     return nullptr;
     596             : }
     597             : 
     598             : /************************************************************************/
     599             : /*                      sort comparison functions.                      */
     600             : /************************************************************************/
     601             : 
     602        1333 : static bool Compare(swq_field_type eType, const CPLString &a,
     603             :                     const CPLString &b)
     604             : {
     605        1333 :     if (a == SZ_OGR_NULL)
     606          52 :         return b != SZ_OGR_NULL;
     607        1281 :     else if (b == SZ_OGR_NULL)
     608          24 :         return false;
     609             :     else
     610             :     {
     611        1257 :         if (eType == SWQ_INTEGER64)
     612         160 :             return CPLAtoGIntBig(a) < CPLAtoGIntBig(b);
     613        1097 :         else if (eType == SWQ_FLOAT)
     614         802 :             return CPLAtof(a) < CPLAtof(b);
     615         295 :         else if (eType == SWQ_STRING)
     616         295 :             return a < b;
     617             :         else
     618             :         {
     619           0 :             CPLAssert(false);
     620             :             return false;
     621             :         }
     622             :     }
     623             : }
     624             : 
     625             : #ifndef DOXYGEN_SKIP
     626        1333 : bool swq_summary::Comparator::operator()(const CPLString &a,
     627             :                                          const CPLString &b) const
     628             : {
     629        1333 :     if (bSortAsc)
     630             :     {
     631         992 :         return Compare(eType, a, b);
     632             :     }
     633             :     else
     634             :     {
     635         341 :         return Compare(eType, b, a);
     636             :     }
     637             : }
     638             : #endif
     639             : 
     640             : /************************************************************************/
     641             : /*                         swq_identify_field()                         */
     642             : /************************************************************************/
     643             : int swq_identify_field_internal(const char *table_name, const char *field_token,
     644             :                                 swq_field_list *field_list,
     645             :                                 swq_field_type *this_type, int *table_id,
     646             :                                 int bOneMoreTimeOK);
     647             : 
     648       23211 : int swq_identify_field(const char *table_name, const char *field_token,
     649             :                        swq_field_list *field_list, swq_field_type *this_type,
     650             :                        int *table_id)
     651             : 
     652             : {
     653       23211 :     return swq_identify_field_internal(table_name, field_token, field_list,
     654       23211 :                                        this_type, table_id, TRUE);
     655             : }
     656             : 
     657       23216 : int swq_identify_field_internal(const char *table_name, const char *field_token,
     658             :                                 swq_field_list *field_list,
     659             :                                 swq_field_type *this_type, int *table_id,
     660             :                                 int bOneMoreTimeOK)
     661             : 
     662             : {
     663       23216 :     if (table_name == nullptr)
     664        8294 :         table_name = "";
     665             : 
     666             :     int tables_enabled;
     667             : 
     668       23216 :     if (field_list->table_count > 0 && field_list->table_ids != nullptr)
     669       15331 :         tables_enabled = TRUE;
     670             :     else
     671        7885 :         tables_enabled = FALSE;
     672             : 
     673             :     /* -------------------------------------------------------------------- */
     674             :     /*      Search for matching field.                                      */
     675             :     /* -------------------------------------------------------------------- */
     676       25233 :     for (int pass = 0; pass < 2; ++pass)
     677             :     {
     678      202186 :         for (int i = 0; i < field_list->count; i++)
     679             :         {
     680      200169 :             if ((pass == 0 && strcmp(field_list->names[i], field_token) != 0) ||
     681        7529 :                 (pass == 1 && !EQUAL(field_list->names[i], field_token)))
     682             :             {
     683      177187 :                 continue;
     684             :             }
     685             : 
     686       22982 :             int t_id = 0;
     687             : 
     688             :             // Do the table specifications match?/
     689       22982 :             if (tables_enabled)
     690             :             {
     691       15099 :                 t_id = field_list->table_ids[i];
     692       15099 :                 if (table_name[0] != '\0' &&
     693       13389 :                     !EQUAL(table_name,
     694             :                            field_list->table_defs[t_id].table_alias))
     695          83 :                     continue;
     696             : 
     697             :                 // if( t_id != 0 && table_name[0] == '\0' )
     698             :                 //     continue;
     699             :             }
     700        7883 :             else if (table_name[0] != '\0')
     701           0 :                 break;
     702             : 
     703             :             // We have a match, return various information.
     704       22899 :             if (this_type != nullptr)
     705             :             {
     706       22899 :                 if (field_list->types != nullptr)
     707       22899 :                     *this_type = field_list->types[i];
     708             :                 else
     709           0 :                     *this_type = SWQ_OTHER;
     710             :             }
     711             : 
     712       22899 :             if (table_id != nullptr)
     713       22899 :                 *table_id = t_id;
     714             : 
     715       22899 :             if (field_list->ids == nullptr)
     716        7883 :                 return i;
     717             :             else
     718       15016 :                 return field_list->ids[i];
     719             :         }
     720             :     }
     721             : 
     722             :     /* -------------------------------------------------------------------- */
     723             :     /*      When there is no ambiguity, try to accept quoting errors...     */
     724             :     /* -------------------------------------------------------------------- */
     725         634 :     if (bOneMoreTimeOK &&
     726         317 :         !CPLTestBool(CPLGetConfigOption("OGR_SQL_STRICT", "FALSE")))
     727             :     {
     728         317 :         if (table_name[0])
     729             :         {
     730             :             CPLString osAggregatedName(
     731           4 :                 CPLSPrintf("%s.%s", table_name, field_token));
     732             : 
     733             :             // Check there's no table called table_name, or a field called with
     734             :             // the aggregated name.
     735           4 :             int i = 0;  // Used after for.
     736          19 :             for (; i < field_list->count; i++)
     737             :             {
     738          18 :                 if (tables_enabled)
     739             :                 {
     740          18 :                     int t_id = field_list->table_ids[i];
     741          18 :                     if (EQUAL(table_name,
     742             :                               field_list->table_defs[t_id].table_alias))
     743           3 :                         break;
     744             :                 }
     745             :             }
     746           4 :             if (i == field_list->count)
     747             :             {
     748           1 :                 int ret = swq_identify_field_internal(nullptr, osAggregatedName,
     749             :                                                       field_list, this_type,
     750             :                                                       table_id, FALSE);
     751           1 :                 if (ret >= 0)
     752             :                 {
     753           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
     754             :                              "Passed field name %s.%s should have been "
     755             :                              "surrounded by double quotes. "
     756             :                              "Accepted since there is no ambiguity...",
     757             :                              table_name, field_token);
     758             :                 }
     759           1 :                 return ret;
     760             :             }
     761             :         }
     762             :         else
     763             :         {
     764             :             // If the fieldname is a.b (and there's no . in b), then
     765             :             // it might be an error in providing it as being quoted where it
     766             :             // should not have been quoted.
     767         313 :             const char *pszDot = strchr(field_token, '.');
     768         313 :             if (pszDot && strchr(pszDot + 1, '.') == nullptr)
     769             :             {
     770           8 :                 CPLString osTableName(field_token);
     771           4 :                 osTableName.resize(pszDot - field_token);
     772           4 :                 CPLString osFieldName(pszDot + 1);
     773             : 
     774           4 :                 int ret = swq_identify_field_internal(osTableName, osFieldName,
     775             :                                                       field_list, this_type,
     776             :                                                       table_id, FALSE);
     777           4 :                 if (ret >= 0)
     778             :                 {
     779           4 :                     CPLError(CE_Warning, CPLE_AppDefined,
     780             :                              "Passed field name %s should NOT have been "
     781             :                              "surrounded by double quotes. "
     782             :                              "Accepted since there is no ambiguity...",
     783             :                              field_token);
     784             :                 }
     785           4 :                 return ret;
     786             :             }
     787             :         }
     788             :     }
     789             : 
     790             :     /* -------------------------------------------------------------------- */
     791             :     /*      No match, return failure.                                       */
     792             :     /* -------------------------------------------------------------------- */
     793         312 :     if (this_type != nullptr)
     794         312 :         *this_type = SWQ_OTHER;
     795             : 
     796         312 :     if (table_id != nullptr)
     797         312 :         *table_id = 0;
     798             : 
     799         312 :     return -1;
     800             : }
     801             : 
     802             : /************************************************************************/
     803             : /*                          swq_expr_compile()                          */
     804             : /************************************************************************/
     805             : 
     806        5101 : CPLErr swq_expr_compile(const char *where_clause, int field_count,
     807             :                         char **field_names, swq_field_type *field_types,
     808             :                         int bCheck,
     809             :                         swq_custom_func_registrar *poCustomFuncRegistrar,
     810             :                         swq_expr_node **expr_out)
     811             : 
     812             : {
     813             :     swq_field_list field_list;
     814             : 
     815        5101 :     field_list.count = field_count;
     816        5101 :     field_list.names = field_names;
     817        5101 :     field_list.types = field_types;
     818        5101 :     field_list.table_ids = nullptr;
     819        5101 :     field_list.ids = nullptr;
     820             : 
     821        5101 :     field_list.table_count = 0;
     822        5101 :     field_list.table_defs = nullptr;
     823             : 
     824        5101 :     return swq_expr_compile2(where_clause, &field_list, bCheck,
     825       10202 :                              poCustomFuncRegistrar, expr_out);
     826             : }
     827             : 
     828             : /************************************************************************/
     829             : /*                       swq_fixup_expression()                         */
     830             : /************************************************************************/
     831             : 
     832        7912 : void swq_fixup(swq_parse_context *psParseContext)
     833             : {
     834        7912 :     if (psParseContext->poRoot)
     835             :     {
     836        5099 :         psParseContext->poRoot->RebalanceAndOr();
     837             :     }
     838        7912 :     auto psSelect = psParseContext->poCurSelect;
     839       10730 :     while (psSelect)
     840             :     {
     841        2818 :         if (psSelect->where_expr)
     842             :         {
     843        1163 :             psSelect->where_expr->RebalanceAndOr();
     844             :         }
     845        2818 :         psSelect = psSelect->poOtherSelect;
     846             :     }
     847        7912 : }
     848             : 
     849             : /************************************************************************/
     850             : /*                       swq_create_and_or_or()                         */
     851             : /************************************************************************/
     852             : 
     853        4756 : swq_expr_node *swq_create_and_or_or(swq_op op, swq_expr_node *left,
     854             :                                     swq_expr_node *right)
     855             : {
     856        4756 :     auto poNode = new swq_expr_node(op);
     857        4756 :     poNode->field_type = SWQ_BOOLEAN;
     858             : 
     859        4756 :     if (left->eNodeType == SNT_OPERATION && left->nOperation == op)
     860             :     {
     861             :         // Temporary non-binary formulation
     862        4322 :         if (right->eNodeType == SNT_OPERATION && right->nOperation == op)
     863             :         {
     864           7 :             poNode->nSubExprCount = left->nSubExprCount + right->nSubExprCount;
     865           7 :             poNode->papoSubExpr = static_cast<swq_expr_node **>(
     866          14 :                 CPLRealloc(left->papoSubExpr,
     867           7 :                            sizeof(swq_expr_node *) * poNode->nSubExprCount));
     868           7 :             memcpy(poNode->papoSubExpr + left->nSubExprCount,
     869           7 :                    right->papoSubExpr,
     870           7 :                    right->nSubExprCount * sizeof(swq_expr_node *));
     871             : 
     872           7 :             right->nSubExprCount = 0;
     873           7 :             CPLFree(right->papoSubExpr);
     874           7 :             right->papoSubExpr = nullptr;
     875           7 :             delete right;
     876             :         }
     877             :         else
     878             :         {
     879        4315 :             poNode->nSubExprCount = left->nSubExprCount;
     880        4315 :             poNode->papoSubExpr = left->papoSubExpr;
     881        4315 :             poNode->PushSubExpression(right);
     882             :         }
     883             : 
     884        4322 :         left->nSubExprCount = 0;
     885        4322 :         left->papoSubExpr = nullptr;
     886        4322 :         delete left;
     887             :     }
     888         434 :     else if (right->eNodeType == SNT_OPERATION && right->nOperation == op)
     889             :     {
     890             :         // Temporary non-binary formulation
     891           8 :         poNode->nSubExprCount = right->nSubExprCount;
     892           8 :         poNode->papoSubExpr = right->papoSubExpr;
     893           8 :         poNode->PushSubExpression(left);
     894             : 
     895           8 :         right->nSubExprCount = 0;
     896           8 :         right->papoSubExpr = nullptr;
     897           8 :         delete right;
     898             :     }
     899             :     else
     900             :     {
     901         426 :         poNode->PushSubExpression(left);
     902         426 :         poNode->PushSubExpression(right);
     903             :     }
     904             : 
     905        4756 :     return poNode;
     906             : }
     907             : 
     908             : /************************************************************************/
     909             : /*                         swq_expr_compile2()                          */
     910             : /************************************************************************/
     911             : 
     912        5101 : CPLErr swq_expr_compile2(const char *where_clause, swq_field_list *field_list,
     913             :                          int bCheck,
     914             :                          swq_custom_func_registrar *poCustomFuncRegistrar,
     915             :                          swq_expr_node **expr_out)
     916             : 
     917             : {
     918        5101 :     swq_parse_context context;
     919             : 
     920        5101 :     context.pszInput = where_clause;
     921        5101 :     context.pszNext = where_clause;
     922        5101 :     context.pszLastValid = where_clause;
     923        5101 :     context.nStartToken = SWQT_VALUE_START;
     924        5101 :     context.bAcceptCustomFuncs = poCustomFuncRegistrar != nullptr;
     925             : 
     926       10198 :     if (swqparse(&context) == 0 && bCheck &&
     927        5097 :         context.poRoot->Check(field_list, FALSE, FALSE,
     928             :                               poCustomFuncRegistrar) != SWQ_ERROR)
     929             :     {
     930        5066 :         *expr_out = context.poRoot;
     931             : 
     932        5066 :         return CE_None;
     933             :     }
     934             :     else
     935             :     {
     936          35 :         delete context.poRoot;
     937          35 :         *expr_out = nullptr;
     938          35 :         return CE_Failure;
     939             :     }
     940             : }
     941             : 
     942             : /************************************************************************/
     943             : /*                        swq_is_reserved_keyword()                     */
     944             : /************************************************************************/
     945             : 
     946             : static const char *const apszSQLReservedKeywords[] = {
     947             :     "OR",    "AND",      "NOT",    "LIKE",   "IS",   "NULL", "IN",    "BETWEEN",
     948             :     "CAST",  "DISTINCT", "ESCAPE", "SELECT", "LEFT", "JOIN", "WHERE", "ON",
     949             :     "ORDER", "BY",       "FROM",   "AS",     "ASC",  "DESC", "UNION", "ALL"};
     950             : 
     951        1380 : int swq_is_reserved_keyword(const char *pszStr)
     952             : {
     953       34446 :     for (const auto &pszKeyword : apszSQLReservedKeywords)
     954             :     {
     955       33075 :         if (EQUAL(pszStr, pszKeyword))
     956           9 :             return TRUE;
     957             :     }
     958        1371 :     return FALSE;
     959             : }
     960             : 
     961             : /************************************************************************/
     962             : /*                          SWQFieldTypeToString()                      */
     963             : /************************************************************************/
     964             : 
     965           2 : const char *SWQFieldTypeToString(swq_field_type field_type)
     966             : {
     967           2 :     switch (field_type)
     968             :     {
     969           1 :         case SWQ_INTEGER:
     970           1 :             return "integer";
     971           0 :         case SWQ_INTEGER64:
     972           0 :             return "bigint";
     973           0 :         case SWQ_FLOAT:
     974           0 :             return "float";
     975           0 :         case SWQ_STRING:
     976           0 :             return "string";
     977           0 :         case SWQ_BOOLEAN:
     978           0 :             return "boolean";
     979           0 :         case SWQ_DATE:
     980           0 :             return "date";
     981           0 :         case SWQ_TIME:
     982           0 :             return "time";
     983           0 :         case SWQ_TIMESTAMP:
     984           0 :             return "timestamp";
     985           1 :         case SWQ_GEOMETRY:
     986           1 :             return "geometry";
     987           0 :         case SWQ_NULL:
     988           0 :             return "null";
     989           0 :         default:
     990           0 :             return "unknown";
     991             :     }
     992             : }

Generated by: LCOV version 1.14