LCOV - code coverage report
Current view: top level - ogr - swq.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 475 511 93.0 %
Date: 2024-11-21 22:18:42 Functions: 15 15 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 <queue>
      36             : #include <string>
      37             : 
      38             : #include "cpl_error.h"
      39             : #include "cpl_multiproc.h"
      40             : #include "cpl_time.h"
      41             : #include "swq_parser.hpp"
      42             : 
      43             : #define YYSTYPE swq_expr_node *
      44             : 
      45             : /************************************************************************/
      46             : /*                               swqlex()                               */
      47             : /************************************************************************/
      48             : 
      49         112 : void swqerror(swq_parse_context *context, const char *msg)
      50             : {
      51         224 :     CPLString osMsg;
      52         112 :     osMsg.Printf("SQL Expression Parsing Error: %s. Occurred around :\n", msg);
      53             : 
      54         112 :     int n = static_cast<int>(context->pszLastValid - context->pszInput);
      55             : 
      56        2264 :     for (int i = std::max(0, n - 40);
      57        2264 :          i < n + 40 && context->pszInput[i] != '\0'; i++)
      58        2152 :         osMsg += context->pszInput[i];
      59         112 :     osMsg += "\n";
      60         427 :     for (int i = 0; i < std::min(n, 40); i++)
      61         315 :         osMsg += " ";
      62         112 :     osMsg += "^";
      63             : 
      64         112 :     CPLError(CE_Failure, CPLE_AppDefined, "%s", osMsg.c_str());
      65         112 : }
      66             : 
      67             : /************************************************************************/
      68             : /*                               swqlex()                               */
      69             : /*                                                                      */
      70             : /*      Read back a token from the input.                               */
      71             : /************************************************************************/
      72             : 
      73       78952 : int swqlex(YYSTYPE *ppNode, swq_parse_context *context)
      74             : {
      75       78952 :     const char *pszInput = context->pszNext;
      76             : 
      77       78952 :     *ppNode = nullptr;
      78             : 
      79             :     /* -------------------------------------------------------------------- */
      80             :     /*      Do we have a start symbol to return?                            */
      81             :     /* -------------------------------------------------------------------- */
      82       78952 :     if (context->nStartToken != 0)
      83             :     {
      84        8043 :         int nRet = context->nStartToken;
      85        8043 :         context->nStartToken = 0;
      86        8043 :         return nRet;
      87             :     }
      88             : 
      89             :     /* -------------------------------------------------------------------- */
      90             :     /*      Skip white space.                                               */
      91             :     /* -------------------------------------------------------------------- */
      92      113242 :     while (*pszInput == ' ' || *pszInput == '\t' || *pszInput == 10 ||
      93       70909 :            *pszInput == 13)
      94       42333 :         pszInput++;
      95             : 
      96       70909 :     context->pszLastValid = pszInput;
      97             : 
      98       70909 :     if (*pszInput == '\0')
      99             :     {
     100        7932 :         context->pszNext = pszInput;
     101        7932 :         return EOF;
     102             :     }
     103             : 
     104             :     /* -------------------------------------------------------------------- */
     105             :     /*      Handle string constants.                                        */
     106             :     /* -------------------------------------------------------------------- */
     107       62977 :     if (*pszInput == '"' || *pszInput == '\'')
     108             :     {
     109        3172 :         char chQuote = *pszInput;
     110        3172 :         bool bFoundEndQuote = false;
     111             : 
     112        3172 :         int nRet = *pszInput == '"' ? SWQT_IDENTIFIER : SWQT_STRING;
     113             : 
     114        3172 :         pszInput++;
     115             : 
     116        3172 :         char *token = static_cast<char *>(CPLMalloc(strlen(pszInput) + 1));
     117        3172 :         int i_token = 0;
     118             : 
     119       43050 :         while (*pszInput != '\0')
     120             :         {
     121             :             // Not totally sure we need to preserve this way of escaping for
     122             :             // strings between double-quotes
     123       43049 :             if (chQuote == '"' && *pszInput == '\\')
     124             :             {
     125           0 :                 pszInput++;
     126           0 :                 if (*pszInput == '\0')
     127           0 :                     break;
     128             :             }
     129       43049 :             else if (chQuote == '\'' && *pszInput == '\'' &&
     130        1717 :                      pszInput[1] == '\'')
     131           3 :                 pszInput++;
     132       43046 :             else if (*pszInput == chQuote)
     133             :             {
     134        3171 :                 pszInput++;
     135        3171 :                 bFoundEndQuote = true;
     136        3171 :                 break;
     137             :             }
     138             : 
     139       39878 :             token[i_token++] = *(pszInput++);
     140             :         }
     141        3172 :         token[i_token] = '\0';
     142             : 
     143        3172 :         if (!bFoundEndQuote)
     144             :         {
     145           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     146             :                      "Did not find end-of-string character");
     147           1 :             CPLFree(token);
     148           1 :             return YYerror;
     149             :         }
     150             : 
     151        3171 :         *ppNode = new swq_expr_node(token);
     152        3171 :         CPLFree(token);
     153             : 
     154        3171 :         context->pszNext = pszInput;
     155             : 
     156        3171 :         return nRet;
     157             :     }
     158             : 
     159             :     /* -------------------------------------------------------------------- */
     160             :     /*      Handle numbers.                                                 */
     161             :     /* -------------------------------------------------------------------- */
     162       59805 :     else if (*pszInput >= '0' && *pszInput <= '9')
     163             :     {
     164       25252 :         CPLString osToken;
     165       12626 :         const char *pszNext = pszInput + 1;
     166             : 
     167       12626 :         osToken += *pszInput;
     168             : 
     169             :         // collect non-decimal part of number
     170       21577 :         while (*pszNext >= '0' && *pszNext <= '9')
     171        8951 :             osToken += *(pszNext++);
     172             : 
     173             :         // collect decimal places.
     174       12626 :         if (*pszNext == '.')
     175             :         {
     176         473 :             osToken += *(pszNext++);
     177        2092 :             while (*pszNext >= '0' && *pszNext <= '9')
     178        1619 :                 osToken += *(pszNext++);
     179             :         }
     180             : 
     181             :         // collect exponent
     182       12626 :         if (*pszNext == 'e' || *pszNext == 'E')
     183             :         {
     184           9 :             osToken += *(pszNext++);
     185           9 :             if (*pszNext == '-' || *pszNext == '+')
     186           9 :                 osToken += *(pszNext++);
     187          34 :             while (*pszNext >= '0' && *pszNext <= '9')
     188          25 :                 osToken += *(pszNext++);
     189             :         }
     190             : 
     191       12626 :         context->pszNext = pszNext;
     192             : 
     193       24770 :         if (strstr(osToken, ".") || strstr(osToken, "e") ||
     194       12144 :             strstr(osToken, "E"))
     195             :         {
     196         482 :             *ppNode = new swq_expr_node(CPLAtof(osToken));
     197         482 :             return SWQT_FLOAT_NUMBER;
     198             :         }
     199             :         else
     200             :         {
     201       24315 :             if (osToken.size() > 19 ||
     202       12171 :                 (osToken.size() >= 19 && osToken > "9223372036854775807"))
     203             :             {
     204          12 :                 *ppNode = new swq_expr_node(CPLAtof(osToken));
     205          12 :                 if (osToken == "9223372036854775808")
     206          11 :                     (*ppNode)->string_value = CPLStrdup(osToken);
     207          12 :                 return SWQT_FLOAT_NUMBER;
     208             :             }
     209             : 
     210       12132 :             GIntBig nVal = CPLAtoGIntBig(osToken);
     211       12132 :             if (CPL_INT64_FITS_ON_INT32(nVal))
     212       12035 :                 *ppNode = new swq_expr_node(static_cast<int>(nVal));
     213             :             else
     214          97 :                 *ppNode = new swq_expr_node(nVal);
     215       12132 :             return SWQT_INTEGER_NUMBER;
     216             :         }
     217             :     }
     218             : 
     219             :     /* -------------------------------------------------------------------- */
     220             :     /*      Handle alpha-numerics.                                          */
     221             :     /* -------------------------------------------------------------------- */
     222       47179 :     else if (isalnum(static_cast<unsigned char>(*pszInput)))
     223             :     {
     224       26130 :         int nReturn = SWQT_IDENTIFIER;
     225       26130 :         CPLString osToken;
     226       26130 :         const char *pszNext = pszInput + 1;
     227             : 
     228       26130 :         osToken += *pszInput;
     229             : 
     230             :         // collect text characters
     231       90185 :         while (isalnum(static_cast<unsigned char>(*pszNext)) ||
     232      116315 :                *pszNext == '_' || static_cast<unsigned char>(*pszNext) > 127)
     233       90185 :             osToken += *(pszNext++);
     234             : 
     235       26130 :         context->pszNext = pszNext;
     236             : 
     237       26130 :         if (EQUAL(osToken, "IN"))
     238         150 :             nReturn = SWQT_IN;
     239       25980 :         else if (EQUAL(osToken, "LIKE"))
     240          53 :             nReturn = SWQT_LIKE;
     241       25927 :         else if (EQUAL(osToken, "ILIKE"))
     242          31 :             nReturn = SWQT_ILIKE;
     243       25896 :         else if (EQUAL(osToken, "ESCAPE"))
     244           3 :             nReturn = SWQT_ESCAPE;
     245       25893 :         else if (EQUAL(osToken, "EXCEPT"))
     246          14 :             nReturn = SWQT_EXCEPT;
     247       25879 :         else if (EQUAL(osToken, "EXCLUDE"))
     248           2 :             nReturn = SWQT_EXCLUDE;
     249       25877 :         else if (EQUAL(osToken, "NULL"))
     250         303 :             nReturn = SWQT_NULL;
     251       25574 :         else if (EQUAL(osToken, "IS"))
     252         219 :             nReturn = SWQT_IS;
     253       25355 :         else if (EQUAL(osToken, "NOT"))
     254         265 :             nReturn = SWQT_NOT;
     255       25090 :         else if (EQUAL(osToken, "AND"))
     256        2502 :             nReturn = SWQT_AND;
     257       22588 :         else if (EQUAL(osToken, "OR"))
     258        2277 :             nReturn = SWQT_OR;
     259       20311 :         else if (EQUAL(osToken, "BETWEEN"))
     260          29 :             nReturn = SWQT_BETWEEN;
     261       20282 :         else if (EQUAL(osToken, "SELECT"))
     262        2817 :             nReturn = SWQT_SELECT;
     263       17465 :         else if (EQUAL(osToken, "LEFT"))
     264          27 :             nReturn = SWQT_LEFT;
     265       17438 :         else if (EQUAL(osToken, "JOIN"))
     266          72 :             nReturn = SWQT_JOIN;
     267       17366 :         else if (EQUAL(osToken, "WHERE"))
     268        1170 :             nReturn = SWQT_WHERE;
     269       16196 :         else if (EQUAL(osToken, "ON"))
     270          72 :             nReturn = SWQT_ON;
     271       16124 :         else if (EQUAL(osToken, "ORDER"))
     272         105 :             nReturn = SWQT_ORDER;
     273       16019 :         else if (EQUAL(osToken, "BY"))
     274         105 :             nReturn = SWQT_BY;
     275       15914 :         else if (EQUAL(osToken, "FROM"))
     276        2806 :             nReturn = SWQT_FROM;
     277       13108 :         else if (EQUAL(osToken, "AS"))
     278         158 :             nReturn = SWQT_AS;
     279       12950 :         else if (EQUAL(osToken, "ASC"))
     280          27 :             nReturn = SWQT_ASC;
     281       12923 :         else if (EQUAL(osToken, "DESC"))
     282          20 :             nReturn = SWQT_DESC;
     283       12903 :         else if (EQUAL(osToken, "DISTINCT"))
     284          47 :             nReturn = SWQT_DISTINCT;
     285       12856 :         else if (EQUAL(osToken, "CAST"))
     286          90 :             nReturn = SWQT_CAST;
     287       12766 :         else if (EQUAL(osToken, "UNION"))
     288           5 :             nReturn = SWQT_UNION;
     289       12761 :         else if (EQUAL(osToken, "ALL"))
     290           5 :             nReturn = SWQT_ALL;
     291       12756 :         else if (EQUAL(osToken, "LIMIT"))
     292          35 :             nReturn = SWQT_LIMIT;
     293       12721 :         else if (EQUAL(osToken, "OFFSET"))
     294          19 :             nReturn = SWQT_OFFSET;
     295       12702 :         else if (EQUAL(osToken, "HIDDEN"))
     296             :         {
     297          21 :             *ppNode = new swq_expr_node(osToken);
     298          21 :             nReturn = SWQT_HIDDEN;
     299             :         }
     300             : 
     301             :         // Unhandled by OGR SQL.
     302       12681 :         else if (EQUAL(osToken, "OUTER") || EQUAL(osToken, "INNER"))
     303           0 :             nReturn = SWQT_RESERVED_KEYWORD;
     304             : 
     305             :         else
     306             :         {
     307       12681 :             *ppNode = new swq_expr_node(osToken);
     308       12681 :             nReturn = SWQT_IDENTIFIER;
     309             :         }
     310             : 
     311       26130 :         return nReturn;
     312             :     }
     313             : 
     314             :     /* -------------------------------------------------------------------- */
     315             :     /*      Handle special tokens.                                          */
     316             :     /* -------------------------------------------------------------------- */
     317             :     else
     318             :     {
     319       21049 :         context->pszNext = pszInput + 1;
     320       21049 :         return *pszInput;
     321             :     }
     322             : }
     323             : 
     324             : /************************************************************************/
     325             : /*                        swq_select_summarize()                        */
     326             : /************************************************************************/
     327             : 
     328         527 : const char *swq_select_summarize(swq_select *select_info, int dest_column,
     329             :                                  const char *pszValue, const double *pdfValue)
     330             : 
     331             : {
     332             :     /* -------------------------------------------------------------------- */
     333             :     /*      Do various checking.                                            */
     334             :     /* -------------------------------------------------------------------- */
     335         527 :     if (select_info->query_mode == SWQM_RECORDSET)
     336           0 :         return "swq_select_summarize() called on non-summary query.";
     337             : 
     338        1054 :     if (dest_column < 0 ||
     339         527 :         dest_column >= static_cast<int>(select_info->column_defs.size()))
     340           0 :         return "dest_column out of range in swq_select_summarize().";
     341             : 
     342         527 :     const swq_col_def *def = &select_info->column_defs[dest_column];
     343         527 :     if (def->col_func == SWQCF_NONE && !def->distinct_flag)
     344           0 :         return nullptr;
     345             : 
     346         527 :     if (select_info->query_mode == SWQM_DISTINCT_LIST &&
     347         290 :         select_info->order_specs > 0)
     348             :     {
     349         275 :         if (select_info->order_specs > 1)
     350           1 :             return "Can't ORDER BY a DISTINCT list by more than one key.";
     351             : 
     352         548 :         if (select_info->order_defs[0].field_index !=
     353         274 :             select_info->column_defs[0].field_index)
     354           1 :             return "Only selected DISTINCT field can be used for ORDER BY.";
     355             :     }
     356             : 
     357             :     /* -------------------------------------------------------------------- */
     358             :     /*      Create the summary information if this is the first row         */
     359             :     /*      being processed.                                                */
     360             :     /* -------------------------------------------------------------------- */
     361         525 :     if (select_info->column_summary.empty())
     362             :     {
     363          51 :         select_info->column_summary.resize(select_info->column_defs.size());
     364         138 :         for (std::size_t i = 0; i < select_info->column_defs.size(); i++)
     365             :         {
     366          87 :             if (def->distinct_flag)
     367             :             {
     368          26 :                 swq_summary::Comparator oComparator;
     369          26 :                 if (select_info->order_specs > 0)
     370             :                 {
     371          18 :                     CPLAssert(select_info->order_specs == 1);
     372          18 :                     CPLAssert(select_info->column_defs.size() == 1);
     373          18 :                     oComparator.bSortAsc =
     374          18 :                         CPL_TO_BOOL(select_info->order_defs[0].ascending_flag);
     375             :                 }
     376          45 :                 if (select_info->column_defs[i].field_type == SWQ_INTEGER ||
     377          19 :                     -select_info->column_defs[i].field_type == SWQ_INTEGER64)
     378             :                 {
     379           7 :                     oComparator.eType = SWQ_INTEGER64;
     380             :                 }
     381          19 :                 else if (select_info->column_defs[i].field_type == SWQ_FLOAT)
     382             :                 {
     383           4 :                     oComparator.eType = SWQ_FLOAT;
     384             :                 }
     385             :                 else
     386             :                 {
     387          15 :                     oComparator.eType = SWQ_STRING;
     388             :                 }
     389          26 :                 select_info->column_summary[i].oSetDistinctValues =
     390          52 :                     std::set<CPLString, swq_summary::Comparator>(oComparator);
     391             :             }
     392          87 :             select_info->column_summary[i].min =
     393          87 :                 std::numeric_limits<double>::infinity();
     394         174 :             select_info->column_summary[i].max =
     395          87 :                 -std::numeric_limits<double>::infinity();
     396          87 :             select_info->column_summary[i].osMin = "9999/99/99 99:99:99";
     397          87 :             select_info->column_summary[i].osMax = "0000/00/00 00:00:00";
     398             :         }
     399          51 :         assert(!select_info->column_summary.empty());
     400             :     }
     401             : 
     402             :     /* -------------------------------------------------------------------- */
     403             :     /*      If distinct processing is on, process that now.                 */
     404             :     /* -------------------------------------------------------------------- */
     405         525 :     swq_summary &summary = select_info->column_summary[dest_column];
     406             : 
     407         525 :     if (def->distinct_flag)
     408             :     {
     409         310 :         if (pszValue == nullptr)
     410          19 :             pszValue = SZ_OGR_NULL;
     411             :         try
     412             :         {
     413         310 :             if (!cpl::contains(summary.oSetDistinctValues, pszValue))
     414             :             {
     415         115 :                 summary.oSetDistinctValues.insert(pszValue);
     416         115 :                 if (select_info->order_specs == 0)
     417             :                 {
     418             :                     // If not sorted, keep values in their original order
     419          25 :                     summary.oVectorDistinctValues.emplace_back(pszValue);
     420             :                 }
     421         115 :                 summary.count++;
     422             :             }
     423             :         }
     424           0 :         catch (std::bad_alloc &)
     425             :         {
     426           0 :             return "Out of memory";
     427             :         }
     428             : 
     429         310 :         return nullptr;
     430             :     }
     431             : 
     432             :     /* -------------------------------------------------------------------- */
     433             :     /*      Process various options.                                        */
     434             :     /* -------------------------------------------------------------------- */
     435             : 
     436         215 :     switch (def->col_func)
     437             :     {
     438          47 :         case SWQCF_MIN:
     439          47 :             if (pdfValue)
     440             :             {
     441          26 :                 if (*pdfValue < summary.min)
     442           9 :                     summary.min = *pdfValue;
     443          26 :                 summary.count++;
     444             :             }
     445          21 :             else if (pszValue && pszValue[0] != '\0')
     446             :             {
     447          17 :                 if (summary.count == 0 || strcmp(pszValue, summary.osMin) < 0)
     448             :                 {
     449          13 :                     summary.osMin = pszValue;
     450             :                 }
     451          17 :                 summary.count++;
     452             :             }
     453          47 :             break;
     454          46 :         case SWQCF_MAX:
     455          46 :             if (pdfValue)
     456             :             {
     457          25 :                 if (*pdfValue > summary.max)
     458          15 :                     summary.max = *pdfValue;
     459          25 :                 summary.count++;
     460             :             }
     461          21 :             else if (pszValue && pszValue[0] != '\0')
     462             :             {
     463          17 :                 if (summary.count == 0 || strcmp(pszValue, summary.osMax) > 0)
     464             :                 {
     465           9 :                     summary.osMax = pszValue;
     466             :                 }
     467          17 :                 summary.count++;
     468             :             }
     469          46 :             break;
     470          52 :         case SWQCF_AVG:
     471             :         case SWQCF_SUM:
     472          52 :             if (pdfValue)
     473             :             {
     474          39 :                 summary.count++;
     475             : 
     476             :                 // Cf KahanBabushkaNeumaierSum of
     477             :                 // https://en.wikipedia.org/wiki/Kahan_summation_algorithm#Further_enhancements
     478             :                 // We set a number of temporary variables as volatile, to
     479             :                 // prevent potential undesired compiler optimizations.
     480             : 
     481          39 :                 const double dfNewVal = *pdfValue;
     482          39 :                 const volatile double new_sum_acc = summary.sum_acc + dfNewVal;
     483          39 :                 if (summary.sum_only_finite_terms && std::isfinite(dfNewVal))
     484             :                 {
     485          33 :                     if (std::fabs(summary.sum_acc) >= std::fabs(dfNewVal))
     486             :                     {
     487             :                         const volatile double diff =
     488          18 :                             (summary.sum_acc - new_sum_acc);
     489          18 :                         summary.sum_correction += (diff + dfNewVal);
     490             :                     }
     491             :                     else
     492             :                     {
     493          15 :                         const volatile double diff = (dfNewVal - new_sum_acc);
     494          15 :                         summary.sum_correction += (diff + summary.sum_acc);
     495             :                     }
     496             :                 }
     497             :                 else
     498             :                 {
     499           6 :                     summary.sum_only_finite_terms = false;
     500             :                 }
     501          39 :                 summary.sum_acc = new_sum_acc;
     502             :             }
     503          13 :             else if (pszValue && pszValue[0] != '\0')
     504             :             {
     505           2 :                 if (def->field_type == SWQ_DATE ||
     506           2 :                     def->field_type == SWQ_TIME ||
     507           2 :                     def->field_type == SWQ_TIMESTAMP)
     508             :                 {
     509             :                     OGRField sField;
     510           2 :                     if (OGRParseDate(pszValue, &sField, 0))
     511             :                     {
     512             :                         struct tm brokendowntime;
     513           2 :                         brokendowntime.tm_year = sField.Date.Year - 1900;
     514           2 :                         brokendowntime.tm_mon = sField.Date.Month - 1;
     515           2 :                         brokendowntime.tm_mday = sField.Date.Day;
     516           2 :                         brokendowntime.tm_hour = sField.Date.Hour;
     517           2 :                         brokendowntime.tm_min = sField.Date.Minute;
     518           2 :                         brokendowntime.tm_sec =
     519           2 :                             static_cast<int>(sField.Date.Second);
     520           2 :                         summary.count++;
     521           2 :                         summary.sum_acc += CPLYMDHMSToUnixTime(&brokendowntime);
     522           2 :                         summary.sum_acc +=
     523           2 :                             fmod(static_cast<double>(sField.Date.Second), 1.0);
     524           2 :                     }
     525             :                 }
     526             :                 else
     527             :                 {
     528             :                     return "swq_select_summarize() - AVG()/SUM() called on "
     529           0 :                            "unexpected field type";
     530             :                 }
     531             :             }
     532          52 :             break;
     533             : 
     534          50 :         case SWQCF_COUNT:
     535          50 :             if (pdfValue || pszValue)
     536          50 :                 summary.count++;
     537          50 :             break;
     538             : 
     539          20 :         case SWQCF_STDDEV_POP:
     540             :         case SWQCF_STDDEV_SAMP:
     541             :         {
     542          18 :             const auto UpdateVariance = [&summary](double dfValue)
     543             :             {
     544             :                 // Welford's online algorithm for variance:
     545             :                 // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
     546          18 :                 summary.count++;
     547          18 :                 const double dfDelta = dfValue - summary.mean_for_variance;
     548          18 :                 summary.mean_for_variance += dfDelta / summary.count;
     549          18 :                 const double dfDelta2 = dfValue - summary.mean_for_variance;
     550          18 :                 summary.sq_dist_from_mean_acc += dfDelta * dfDelta2;
     551          38 :             };
     552             : 
     553          20 :             if (pdfValue)
     554             :             {
     555          14 :                 UpdateVariance(*pdfValue);
     556             :             }
     557           6 :             else if (pszValue && pszValue[0] != '\0')
     558             :             {
     559           4 :                 if (def->field_type == SWQ_DATE ||
     560           4 :                     def->field_type == SWQ_TIME ||
     561           4 :                     def->field_type == SWQ_TIMESTAMP)
     562             :                 {
     563             :                     OGRField sField;
     564           4 :                     if (OGRParseDate(pszValue, &sField, 0))
     565             :                     {
     566             :                         struct tm brokendowntime;
     567           4 :                         brokendowntime.tm_year = sField.Date.Year - 1900;
     568           4 :                         brokendowntime.tm_mon = sField.Date.Month - 1;
     569           4 :                         brokendowntime.tm_mday = sField.Date.Day;
     570           4 :                         brokendowntime.tm_hour = sField.Date.Hour;
     571           4 :                         brokendowntime.tm_min = sField.Date.Minute;
     572           4 :                         brokendowntime.tm_sec =
     573           4 :                             static_cast<int>(sField.Date.Second);
     574             : 
     575           4 :                         UpdateVariance(static_cast<double>(
     576           4 :                             CPLYMDHMSToUnixTime(&brokendowntime)));
     577           4 :                     }
     578             :                 }
     579             :                 else
     580             :                 {
     581             :                     return "swq_select_summarize() - STDDEV() called on "
     582           0 :                            "unexpected field type";
     583             :                 }
     584             :             }
     585             : 
     586          20 :             break;
     587             :         }
     588             : 
     589           0 :         case SWQCF_NONE:
     590           0 :             break;
     591             : 
     592           0 :         case SWQCF_CUSTOM:
     593           0 :             return "swq_select_summarize() called on custom field function.";
     594             :     }
     595             : 
     596         215 :     return nullptr;
     597             : }
     598             : 
     599             : /************************************************************************/
     600             : /*                      sort comparison functions.                      */
     601             : /************************************************************************/
     602             : 
     603        1333 : static bool Compare(swq_field_type eType, const CPLString &a,
     604             :                     const CPLString &b)
     605             : {
     606        1333 :     if (a == SZ_OGR_NULL)
     607          52 :         return b != SZ_OGR_NULL;
     608        1281 :     else if (b == SZ_OGR_NULL)
     609          24 :         return false;
     610             :     else
     611             :     {
     612        1257 :         if (eType == SWQ_INTEGER64)
     613         160 :             return CPLAtoGIntBig(a) < CPLAtoGIntBig(b);
     614        1097 :         else if (eType == SWQ_FLOAT)
     615         802 :             return CPLAtof(a) < CPLAtof(b);
     616         295 :         else if (eType == SWQ_STRING)
     617         295 :             return a < b;
     618             :         else
     619             :         {
     620           0 :             CPLAssert(false);
     621             :             return false;
     622             :         }
     623             :     }
     624             : }
     625             : 
     626             : #ifndef DOXYGEN_SKIP
     627        1333 : bool swq_summary::Comparator::operator()(const CPLString &a,
     628             :                                          const CPLString &b) const
     629             : {
     630        1333 :     if (bSortAsc)
     631             :     {
     632         992 :         return Compare(eType, a, b);
     633             :     }
     634             :     else
     635             :     {
     636         341 :         return Compare(eType, b, a);
     637             :     }
     638             : }
     639             : #endif
     640             : 
     641             : /************************************************************************/
     642             : /*                         swq_identify_field()                         */
     643             : /************************************************************************/
     644             : int swq_identify_field_internal(const char *table_name, const char *field_token,
     645             :                                 swq_field_list *field_list,
     646             :                                 swq_field_type *this_type, int *table_id,
     647             :                                 int bOneMoreTimeOK);
     648             : 
     649       23293 : int swq_identify_field(const char *table_name, const char *field_token,
     650             :                        swq_field_list *field_list, swq_field_type *this_type,
     651             :                        int *table_id)
     652             : 
     653             : {
     654       23293 :     return swq_identify_field_internal(table_name, field_token, field_list,
     655       23293 :                                        this_type, table_id, TRUE);
     656             : }
     657             : 
     658       23298 : int swq_identify_field_internal(const char *table_name, const char *field_token,
     659             :                                 swq_field_list *field_list,
     660             :                                 swq_field_type *this_type, int *table_id,
     661             :                                 int bOneMoreTimeOK)
     662             : 
     663             : {
     664       23298 :     if (table_name == nullptr)
     665        8292 :         table_name = "";
     666             : 
     667             :     int tables_enabled;
     668             : 
     669       23298 :     if (field_list->table_count > 0 && field_list->table_ids != nullptr)
     670       15412 :         tables_enabled = TRUE;
     671             :     else
     672        7886 :         tables_enabled = FALSE;
     673             : 
     674             :     /* -------------------------------------------------------------------- */
     675             :     /*      Search for matching field.                                      */
     676             :     /* -------------------------------------------------------------------- */
     677       25326 :     for (int pass = 0; pass < 2; ++pass)
     678             :     {
     679      202856 :         for (int i = 0; i < field_list->count; i++)
     680             :         {
     681      200828 :             if ((pass == 0 && strcmp(field_list->names[i], field_token) != 0) ||
     682        7594 :                 (pass == 1 && !EQUAL(field_list->names[i], field_token)))
     683             :             {
     684      177769 :                 continue;
     685             :             }
     686             : 
     687       23059 :             int t_id = 0;
     688             : 
     689             :             // Do the table specifications match?/
     690       23059 :             if (tables_enabled)
     691             :             {
     692       15174 :                 t_id = field_list->table_ids[i];
     693       15174 :                 if (table_name[0] != '\0' &&
     694       13476 :                     !EQUAL(table_name,
     695             :                            field_list->table_defs[t_id].table_alias))
     696          83 :                     continue;
     697             : 
     698             :                 // if( t_id != 0 && table_name[0] == '\0' )
     699             :                 //     continue;
     700             :             }
     701        7885 :             else if (table_name[0] != '\0')
     702           0 :                 break;
     703             : 
     704             :             // We have a match, return various information.
     705       22976 :             if (this_type != nullptr)
     706             :             {
     707       22976 :                 if (field_list->types != nullptr)
     708       22976 :                     *this_type = field_list->types[i];
     709             :                 else
     710           0 :                     *this_type = SWQ_OTHER;
     711             :             }
     712             : 
     713       22976 :             if (table_id != nullptr)
     714       22976 :                 *table_id = t_id;
     715             : 
     716       22976 :             if (field_list->ids == nullptr)
     717        7885 :                 return i;
     718             :             else
     719       15091 :                 return field_list->ids[i];
     720             :         }
     721             :     }
     722             : 
     723             :     /* -------------------------------------------------------------------- */
     724             :     /*      When there is no ambiguity, try to accept quoting errors...     */
     725             :     /* -------------------------------------------------------------------- */
     726         644 :     if (bOneMoreTimeOK &&
     727         322 :         !CPLTestBool(CPLGetConfigOption("OGR_SQL_STRICT", "FALSE")))
     728             :     {
     729         322 :         if (table_name[0])
     730             :         {
     731             :             CPLString osAggregatedName(
     732           4 :                 CPLSPrintf("%s.%s", table_name, field_token));
     733             : 
     734             :             // Check there's no table called table_name, or a field called with
     735             :             // the aggregated name.
     736           4 :             int i = 0;  // Used after for.
     737          19 :             for (; i < field_list->count; i++)
     738             :             {
     739          18 :                 if (tables_enabled)
     740             :                 {
     741          18 :                     int t_id = field_list->table_ids[i];
     742          18 :                     if (EQUAL(table_name,
     743             :                               field_list->table_defs[t_id].table_alias))
     744           3 :                         break;
     745             :                 }
     746             :             }
     747           4 :             if (i == field_list->count)
     748             :             {
     749           1 :                 int ret = swq_identify_field_internal(nullptr, osAggregatedName,
     750             :                                                       field_list, this_type,
     751             :                                                       table_id, FALSE);
     752           1 :                 if (ret >= 0)
     753             :                 {
     754           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
     755             :                              "Passed field name %s.%s should have been "
     756             :                              "surrounded by double quotes. "
     757             :                              "Accepted since there is no ambiguity...",
     758             :                              table_name, field_token);
     759             :                 }
     760           1 :                 return ret;
     761             :             }
     762             :         }
     763             :         else
     764             :         {
     765             :             // If the fieldname is a.b (and there's no . in b), then
     766             :             // it might be an error in providing it as being quoted where it
     767             :             // should not have been quoted.
     768         318 :             const char *pszDot = strchr(field_token, '.');
     769         318 :             if (pszDot && strchr(pszDot + 1, '.') == nullptr)
     770             :             {
     771           8 :                 CPLString osTableName(field_token);
     772           4 :                 osTableName.resize(pszDot - field_token);
     773           4 :                 CPLString osFieldName(pszDot + 1);
     774             : 
     775           4 :                 int ret = swq_identify_field_internal(osTableName, osFieldName,
     776             :                                                       field_list, this_type,
     777             :                                                       table_id, FALSE);
     778           4 :                 if (ret >= 0)
     779             :                 {
     780           4 :                     CPLError(CE_Warning, CPLE_AppDefined,
     781             :                              "Passed field name %s should NOT have been "
     782             :                              "surrounded by double quotes. "
     783             :                              "Accepted since there is no ambiguity...",
     784             :                              field_token);
     785             :                 }
     786           4 :                 return ret;
     787             :             }
     788             :         }
     789             :     }
     790             : 
     791             :     /* -------------------------------------------------------------------- */
     792             :     /*      No match, return failure.                                       */
     793             :     /* -------------------------------------------------------------------- */
     794         317 :     if (this_type != nullptr)
     795         317 :         *this_type = SWQ_OTHER;
     796             : 
     797         317 :     if (table_id != nullptr)
     798         317 :         *table_id = 0;
     799             : 
     800         317 :     return -1;
     801             : }
     802             : 
     803             : /************************************************************************/
     804             : /*                          swq_expr_compile()                          */
     805             : /************************************************************************/
     806             : 
     807        5135 : CPLErr swq_expr_compile(const char *where_clause, int field_count,
     808             :                         char **field_names, swq_field_type *field_types,
     809             :                         int bCheck,
     810             :                         swq_custom_func_registrar *poCustomFuncRegistrar,
     811             :                         swq_expr_node **expr_out)
     812             : 
     813             : {
     814             :     swq_field_list field_list;
     815             : 
     816        5135 :     field_list.count = field_count;
     817        5135 :     field_list.names = field_names;
     818        5135 :     field_list.types = field_types;
     819        5135 :     field_list.table_ids = nullptr;
     820        5135 :     field_list.ids = nullptr;
     821             : 
     822        5135 :     field_list.table_count = 0;
     823        5135 :     field_list.table_defs = nullptr;
     824             : 
     825        5135 :     return swq_expr_compile2(where_clause, &field_list, bCheck,
     826       10270 :                              poCustomFuncRegistrar, expr_out);
     827             : }
     828             : 
     829             : /************************************************************************/
     830             : /*                       swq_fixup_expression()                         */
     831             : /************************************************************************/
     832             : 
     833       10958 : static void swq_fixup_expression(swq_expr_node *node)
     834             : {
     835       21916 :     std::queue<swq_expr_node *> nodes;
     836       10958 :     nodes.push(node);
     837       44944 :     while (!nodes.empty())
     838             :     {
     839       33986 :         node = nodes.front();
     840       33986 :         nodes.pop();
     841       33986 :         if (node->eNodeType == SNT_OPERATION)
     842             :         {
     843       11829 :             const swq_op eOp = node->nOperation;
     844       11829 :             if ((eOp == SWQ_OR || eOp == SWQ_AND) && node->nSubExprCount > 2)
     845             :             {
     846         320 :                 std::vector<swq_expr_node *> exprs;
     847        4815 :                 for (int i = 0; i < node->nSubExprCount; i++)
     848             :                 {
     849        4655 :                     swq_fixup_expression(node->papoSubExpr[i]);
     850        4655 :                     exprs.push_back(node->papoSubExpr[i]);
     851             :                 }
     852         160 :                 node->nSubExprCount = 0;
     853         160 :                 CPLFree(node->papoSubExpr);
     854         160 :                 node->papoSubExpr = nullptr;
     855             : 
     856         374 :                 while (exprs.size() > 2)
     857             :                 {
     858         428 :                     std::vector<swq_expr_node *> new_exprs;
     859        4706 :                     for (size_t i = 0; i < exprs.size(); i++)
     860             :                     {
     861        4492 :                         if (i + 1 < exprs.size())
     862             :                         {
     863        4335 :                             auto cur_expr = new swq_expr_node(eOp);
     864        4335 :                             cur_expr->field_type = SWQ_BOOLEAN;
     865        4335 :                             cur_expr->PushSubExpression(exprs[i]);
     866        4335 :                             cur_expr->PushSubExpression(exprs[i + 1]);
     867        4335 :                             i++;
     868        4335 :                             new_exprs.push_back(cur_expr);
     869             :                         }
     870             :                         else
     871             :                         {
     872         157 :                             new_exprs.push_back(exprs[i]);
     873             :                         }
     874             :                     }
     875         214 :                     exprs = std::move(new_exprs);
     876             :                 }
     877         160 :                 CPLAssert(exprs.size() == 2);
     878         160 :                 node->PushSubExpression(exprs[0]);
     879         320 :                 node->PushSubExpression(exprs[1]);
     880             :             }
     881             :             else
     882             :             {
     883       34697 :                 for (int i = 0; i < node->nSubExprCount; i++)
     884             :                 {
     885       23028 :                     nodes.push(node->papoSubExpr[i]);
     886             :                 }
     887             :             }
     888             :         }
     889             :     }
     890       10958 : }
     891             : 
     892             : /************************************************************************/
     893             : /*                       swq_fixup_expression()                         */
     894             : /************************************************************************/
     895             : 
     896        7926 : void swq_fixup(swq_parse_context *psParseContext)
     897             : {
     898        7926 :     if (psParseContext->poRoot)
     899             :     {
     900        5133 :         swq_fixup_expression(psParseContext->poRoot);
     901             :     }
     902        7926 :     auto psSelect = psParseContext->poCurSelect;
     903       10724 :     while (psSelect)
     904             :     {
     905        2798 :         if (psSelect->where_expr)
     906             :         {
     907        1170 :             swq_fixup_expression(psSelect->where_expr);
     908             :         }
     909        2798 :         psSelect = psSelect->poOtherSelect;
     910             :     }
     911        7926 : }
     912             : 
     913             : /************************************************************************/
     914             : /*                       swq_create_and_or_or()                         */
     915             : /************************************************************************/
     916             : 
     917        4750 : swq_expr_node *swq_create_and_or_or(swq_op op, swq_expr_node *left,
     918             :                                     swq_expr_node *right)
     919             : {
     920        4750 :     auto poNode = new swq_expr_node(op);
     921        4750 :     poNode->field_type = SWQ_BOOLEAN;
     922             : 
     923        4750 :     if (left->eNodeType == SNT_OPERATION && left->nOperation == op)
     924             :     {
     925             :         // Temporary non-binary formulation
     926        4320 :         if (right->eNodeType == SNT_OPERATION && right->nOperation == op)
     927             :         {
     928           7 :             poNode->nSubExprCount = left->nSubExprCount + right->nSubExprCount;
     929           7 :             poNode->papoSubExpr = static_cast<swq_expr_node **>(
     930          14 :                 CPLRealloc(left->papoSubExpr,
     931           7 :                            sizeof(swq_expr_node *) * poNode->nSubExprCount));
     932           7 :             memcpy(poNode->papoSubExpr + left->nSubExprCount,
     933           7 :                    right->papoSubExpr,
     934           7 :                    right->nSubExprCount * sizeof(swq_expr_node *));
     935             : 
     936           7 :             right->nSubExprCount = 0;
     937           7 :             CPLFree(right->papoSubExpr);
     938           7 :             right->papoSubExpr = nullptr;
     939           7 :             delete right;
     940             :         }
     941             :         else
     942             :         {
     943        4313 :             poNode->nSubExprCount = left->nSubExprCount;
     944        4313 :             poNode->papoSubExpr = left->papoSubExpr;
     945        4313 :             poNode->PushSubExpression(right);
     946             :         }
     947             : 
     948        4320 :         left->nSubExprCount = 0;
     949        4320 :         left->papoSubExpr = nullptr;
     950        4320 :         delete left;
     951             :     }
     952         430 :     else if (right->eNodeType == SNT_OPERATION && right->nOperation == op)
     953             :     {
     954             :         // Temporary non-binary formulation
     955           8 :         poNode->nSubExprCount = right->nSubExprCount;
     956           8 :         poNode->papoSubExpr = right->papoSubExpr;
     957           8 :         poNode->PushSubExpression(left);
     958             : 
     959           8 :         right->nSubExprCount = 0;
     960           8 :         right->papoSubExpr = nullptr;
     961           8 :         delete right;
     962             :     }
     963             :     else
     964             :     {
     965         422 :         poNode->PushSubExpression(left);
     966         422 :         poNode->PushSubExpression(right);
     967             :     }
     968             : 
     969        4750 :     return poNode;
     970             : }
     971             : 
     972             : /************************************************************************/
     973             : /*                         swq_expr_compile2()                          */
     974             : /************************************************************************/
     975             : 
     976        5135 : CPLErr swq_expr_compile2(const char *where_clause, swq_field_list *field_list,
     977             :                          int bCheck,
     978             :                          swq_custom_func_registrar *poCustomFuncRegistrar,
     979             :                          swq_expr_node **expr_out)
     980             : 
     981             : {
     982        5135 :     swq_parse_context context;
     983             : 
     984        5135 :     context.pszInput = where_clause;
     985        5135 :     context.pszNext = where_clause;
     986        5135 :     context.pszLastValid = where_clause;
     987        5135 :     context.nStartToken = SWQT_VALUE_START;
     988        5135 :     context.bAcceptCustomFuncs = poCustomFuncRegistrar != nullptr;
     989             : 
     990       10266 :     if (swqparse(&context) == 0 && bCheck &&
     991        5131 :         context.poRoot->Check(field_list, FALSE, FALSE,
     992             :                               poCustomFuncRegistrar) != SWQ_ERROR)
     993             :     {
     994        5101 :         *expr_out = context.poRoot;
     995             : 
     996        5101 :         return CE_None;
     997             :     }
     998             :     else
     999             :     {
    1000          34 :         delete context.poRoot;
    1001          34 :         *expr_out = nullptr;
    1002          34 :         return CE_Failure;
    1003             :     }
    1004             : }
    1005             : 
    1006             : /************************************************************************/
    1007             : /*                        swq_is_reserved_keyword()                     */
    1008             : /************************************************************************/
    1009             : 
    1010             : static const char *const apszSQLReservedKeywords[] = {
    1011             :     "OR",    "AND",      "NOT",    "LIKE",   "IS",   "NULL", "IN",    "BETWEEN",
    1012             :     "CAST",  "DISTINCT", "ESCAPE", "SELECT", "LEFT", "JOIN", "WHERE", "ON",
    1013             :     "ORDER", "BY",       "FROM",   "AS",     "ASC",  "DESC", "UNION", "ALL"};
    1014             : 
    1015        1400 : int swq_is_reserved_keyword(const char *pszStr)
    1016             : {
    1017       34946 :     for (const auto &pszKeyword : apszSQLReservedKeywords)
    1018             :     {
    1019       33555 :         if (EQUAL(pszStr, pszKeyword))
    1020           9 :             return TRUE;
    1021             :     }
    1022        1391 :     return FALSE;
    1023             : }
    1024             : 
    1025             : /************************************************************************/
    1026             : /*                          SWQFieldTypeToString()                      */
    1027             : /************************************************************************/
    1028             : 
    1029           2 : const char *SWQFieldTypeToString(swq_field_type field_type)
    1030             : {
    1031           2 :     switch (field_type)
    1032             :     {
    1033           1 :         case SWQ_INTEGER:
    1034           1 :             return "integer";
    1035           0 :         case SWQ_INTEGER64:
    1036           0 :             return "bigint";
    1037           0 :         case SWQ_FLOAT:
    1038           0 :             return "float";
    1039           0 :         case SWQ_STRING:
    1040           0 :             return "string";
    1041           0 :         case SWQ_BOOLEAN:
    1042           0 :             return "boolean";
    1043           0 :         case SWQ_DATE:
    1044           0 :             return "date";
    1045           0 :         case SWQ_TIME:
    1046           0 :             return "time";
    1047           0 :         case SWQ_TIMESTAMP:
    1048           0 :             return "timestamp";
    1049           1 :         case SWQ_GEOMETRY:
    1050           1 :             return "geometry";
    1051           0 :         case SWQ_NULL:
    1052           0 :             return "null";
    1053           0 :         default:
    1054           0 :             return "unknown";
    1055             :     }
    1056             : }

Generated by: LCOV version 1.14