Coverage Report

Created: 2025-06-13 06:18

/src/gdal/ogr/swq.cpp
Line
Count
Source (jump to first uncovered line)
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
void swqerror(swq_parse_context *context, const char *msg)
49
0
{
50
0
    CPLString osMsg;
51
0
    osMsg.Printf("SQL Expression Parsing Error: %s. Occurred around :\n", msg);
52
53
0
    int n = static_cast<int>(context->pszLastValid - context->pszInput);
54
55
0
    for (int i = std::max(0, n - 40);
56
0
         i < n + 40 && context->pszInput[i] != '\0'; i++)
57
0
        osMsg += context->pszInput[i];
58
0
    osMsg += "\n";
59
0
    for (int i = 0; i < std::min(n, 40); i++)
60
0
        osMsg += " ";
61
0
    osMsg += "^";
62
63
0
    CPLError(CE_Failure, CPLE_AppDefined, "%s", osMsg.c_str());
64
0
}
65
66
/************************************************************************/
67
/*                               swqlex()                               */
68
/*                                                                      */
69
/*      Read back a token from the input.                               */
70
/************************************************************************/
71
72
int swqlex(YYSTYPE *ppNode, swq_parse_context *context)
73
0
{
74
0
    const char *pszInput = context->pszNext;
75
76
0
    *ppNode = nullptr;
77
78
    /* -------------------------------------------------------------------- */
79
    /*      Do we have a start symbol to return?                            */
80
    /* -------------------------------------------------------------------- */
81
0
    if (context->nStartToken != 0)
82
0
    {
83
0
        int nRet = context->nStartToken;
84
0
        context->nStartToken = 0;
85
0
        return nRet;
86
0
    }
87
88
    /* -------------------------------------------------------------------- */
89
    /*      Skip white space.                                               */
90
    /* -------------------------------------------------------------------- */
91
0
    while (*pszInput == ' ' || *pszInput == '\t' || *pszInput == 10 ||
92
0
           *pszInput == 13)
93
0
        pszInput++;
94
95
0
    context->pszLastValid = pszInput;
96
97
0
    if (*pszInput == '\0')
98
0
    {
99
0
        context->pszNext = pszInput;
100
0
        return EOF;
101
0
    }
102
103
    /* -------------------------------------------------------------------- */
104
    /*      Handle string constants.                                        */
105
    /* -------------------------------------------------------------------- */
106
0
    if (*pszInput == '"' || *pszInput == '\'')
107
0
    {
108
0
        char chQuote = *pszInput;
109
0
        bool bFoundEndQuote = false;
110
111
0
        int nRet = *pszInput == '"' ? SWQT_IDENTIFIER : SWQT_STRING;
112
113
0
        pszInput++;
114
115
0
        char *token = static_cast<char *>(CPLMalloc(strlen(pszInput) + 1));
116
0
        int i_token = 0;
117
118
0
        while (*pszInput != '\0')
119
0
        {
120
            // Not totally sure we need to preserve this way of escaping for
121
            // strings between double-quotes
122
0
            if (chQuote == '"' && *pszInput == '\\')
123
0
            {
124
0
                pszInput++;
125
0
                if (*pszInput == '\0')
126
0
                    break;
127
0
            }
128
0
            else if (chQuote == '\'' && *pszInput == '\'' &&
129
0
                     pszInput[1] == '\'')
130
0
                pszInput++;
131
0
            else if (*pszInput == chQuote)
132
0
            {
133
0
                pszInput++;
134
0
                bFoundEndQuote = true;
135
0
                break;
136
0
            }
137
138
0
            token[i_token++] = *(pszInput++);
139
0
        }
140
0
        token[i_token] = '\0';
141
142
0
        if (!bFoundEndQuote)
143
0
        {
144
0
            CPLError(CE_Failure, CPLE_AppDefined,
145
0
                     "Did not find end-of-string character");
146
0
            CPLFree(token);
147
0
            return YYerror;
148
0
        }
149
150
0
        *ppNode = new swq_expr_node(token);
151
0
        CPLFree(token);
152
153
0
        context->pszNext = pszInput;
154
155
0
        return nRet;
156
0
    }
157
158
    /* -------------------------------------------------------------------- */
159
    /*      Handle numbers.                                                 */
160
    /* -------------------------------------------------------------------- */
161
0
    else if (*pszInput >= '0' && *pszInput <= '9')
162
0
    {
163
0
        CPLString osToken;
164
0
        const char *pszNext = pszInput + 1;
165
166
0
        osToken += *pszInput;
167
168
        // collect non-decimal part of number
169
0
        while (*pszNext >= '0' && *pszNext <= '9')
170
0
            osToken += *(pszNext++);
171
172
        // collect decimal places.
173
0
        if (*pszNext == '.')
174
0
        {
175
0
            osToken += *(pszNext++);
176
0
            while (*pszNext >= '0' && *pszNext <= '9')
177
0
                osToken += *(pszNext++);
178
0
        }
179
180
        // collect exponent
181
0
        if (*pszNext == 'e' || *pszNext == 'E')
182
0
        {
183
0
            osToken += *(pszNext++);
184
0
            if (*pszNext == '-' || *pszNext == '+')
185
0
                osToken += *(pszNext++);
186
0
            while (*pszNext >= '0' && *pszNext <= '9')
187
0
                osToken += *(pszNext++);
188
0
        }
189
190
0
        context->pszNext = pszNext;
191
192
0
        if (strstr(osToken, ".") || strstr(osToken, "e") ||
193
0
            strstr(osToken, "E"))
194
0
        {
195
0
            *ppNode = new swq_expr_node(CPLAtof(osToken));
196
0
            return SWQT_FLOAT_NUMBER;
197
0
        }
198
0
        else
199
0
        {
200
0
            if (osToken.size() > 19 ||
201
0
                (osToken.size() >= 19 && osToken > "9223372036854775807"))
202
0
            {
203
0
                *ppNode = new swq_expr_node(CPLAtof(osToken));
204
0
                if (osToken == "9223372036854775808")
205
0
                    (*ppNode)->string_value = CPLStrdup(osToken);
206
0
                return SWQT_FLOAT_NUMBER;
207
0
            }
208
209
0
            GIntBig nVal = CPLAtoGIntBig(osToken);
210
0
            if (CPL_INT64_FITS_ON_INT32(nVal))
211
0
                *ppNode = new swq_expr_node(static_cast<int>(nVal));
212
0
            else
213
0
                *ppNode = new swq_expr_node(nVal);
214
0
            return SWQT_INTEGER_NUMBER;
215
0
        }
216
0
    }
217
218
    /* -------------------------------------------------------------------- */
219
    /*      Handle alpha-numerics.                                          */
220
    /* -------------------------------------------------------------------- */
221
0
    else if (isalnum(static_cast<unsigned char>(*pszInput)))
222
0
    {
223
0
        int nReturn = SWQT_IDENTIFIER;
224
0
        CPLString osToken;
225
0
        const char *pszNext = pszInput + 1;
226
227
0
        osToken += *pszInput;
228
229
        // collect text characters
230
0
        while (isalnum(static_cast<unsigned char>(*pszNext)) ||
231
0
               *pszNext == '_' || static_cast<unsigned char>(*pszNext) > 127)
232
0
            osToken += *(pszNext++);
233
234
0
        context->pszNext = pszNext;
235
236
0
        if (EQUAL(osToken, "IN"))
237
0
            nReturn = SWQT_IN;
238
0
        else if (EQUAL(osToken, "LIKE"))
239
0
            nReturn = SWQT_LIKE;
240
0
        else if (EQUAL(osToken, "ILIKE"))
241
0
            nReturn = SWQT_ILIKE;
242
0
        else if (EQUAL(osToken, "ESCAPE"))
243
0
            nReturn = SWQT_ESCAPE;
244
0
        else if (EQUAL(osToken, "EXCEPT"))
245
0
            nReturn = SWQT_EXCEPT;
246
0
        else if (EQUAL(osToken, "EXCLUDE"))
247
0
            nReturn = SWQT_EXCLUDE;
248
0
        else if (EQUAL(osToken, "NULL"))
249
0
            nReturn = SWQT_NULL;
250
0
        else if (EQUAL(osToken, "IS"))
251
0
            nReturn = SWQT_IS;
252
0
        else if (EQUAL(osToken, "NOT"))
253
0
            nReturn = SWQT_NOT;
254
0
        else if (EQUAL(osToken, "AND"))
255
0
            nReturn = SWQT_AND;
256
0
        else if (EQUAL(osToken, "OR"))
257
0
            nReturn = SWQT_OR;
258
0
        else if (EQUAL(osToken, "BETWEEN"))
259
0
            nReturn = SWQT_BETWEEN;
260
0
        else if (EQUAL(osToken, "SELECT"))
261
0
            nReturn = SWQT_SELECT;
262
0
        else if (EQUAL(osToken, "LEFT"))
263
0
            nReturn = SWQT_LEFT;
264
0
        else if (EQUAL(osToken, "JOIN"))
265
0
            nReturn = SWQT_JOIN;
266
0
        else if (EQUAL(osToken, "WHERE"))
267
0
            nReturn = SWQT_WHERE;
268
0
        else if (EQUAL(osToken, "ON"))
269
0
            nReturn = SWQT_ON;
270
0
        else if (EQUAL(osToken, "ORDER"))
271
0
            nReturn = SWQT_ORDER;
272
0
        else if (EQUAL(osToken, "BY"))
273
0
            nReturn = SWQT_BY;
274
0
        else if (EQUAL(osToken, "FROM"))
275
0
            nReturn = SWQT_FROM;
276
0
        else if (EQUAL(osToken, "AS"))
277
0
            nReturn = SWQT_AS;
278
0
        else if (EQUAL(osToken, "ASC"))
279
0
            nReturn = SWQT_ASC;
280
0
        else if (EQUAL(osToken, "DESC"))
281
0
            nReturn = SWQT_DESC;
282
0
        else if (EQUAL(osToken, "DISTINCT"))
283
0
            nReturn = SWQT_DISTINCT;
284
0
        else if (EQUAL(osToken, "CAST"))
285
0
            nReturn = SWQT_CAST;
286
0
        else if (EQUAL(osToken, "UNION"))
287
0
            nReturn = SWQT_UNION;
288
0
        else if (EQUAL(osToken, "ALL"))
289
0
            nReturn = SWQT_ALL;
290
0
        else if (EQUAL(osToken, "LIMIT"))
291
0
            nReturn = SWQT_LIMIT;
292
0
        else if (EQUAL(osToken, "OFFSET"))
293
0
            nReturn = SWQT_OFFSET;
294
0
        else if (EQUAL(osToken, "HIDDEN"))
295
0
        {
296
0
            *ppNode = new swq_expr_node(osToken);
297
0
            nReturn = SWQT_HIDDEN;
298
0
        }
299
300
        // Unhandled by OGR SQL.
301
0
        else if (EQUAL(osToken, "OUTER") || EQUAL(osToken, "INNER"))
302
0
            nReturn = SWQT_RESERVED_KEYWORD;
303
304
0
        else
305
0
        {
306
0
            *ppNode = new swq_expr_node(osToken);
307
0
            nReturn = SWQT_IDENTIFIER;
308
0
        }
309
310
0
        return nReturn;
311
0
    }
312
313
    /* -------------------------------------------------------------------- */
314
    /*      Handle special tokens.                                          */
315
    /* -------------------------------------------------------------------- */
316
0
    else
317
0
    {
318
0
        context->pszNext = pszInput + 1;
319
0
        return *pszInput;
320
0
    }
321
0
}
322
323
/************************************************************************/
324
/*                        swq_select_summarize()                        */
325
/************************************************************************/
326
327
const char *swq_select_summarize(swq_select *select_info, int dest_column,
328
                                 const char *pszValue, const double *pdfValue)
329
330
0
{
331
    /* -------------------------------------------------------------------- */
332
    /*      Do various checking.                                            */
333
    /* -------------------------------------------------------------------- */
334
0
    if (select_info->query_mode == SWQM_RECORDSET)
335
0
        return "swq_select_summarize() called on non-summary query.";
336
337
0
    if (dest_column < 0 ||
338
0
        dest_column >= static_cast<int>(select_info->column_defs.size()))
339
0
        return "dest_column out of range in swq_select_summarize().";
340
341
0
    const swq_col_def *def = &select_info->column_defs[dest_column];
342
0
    if (def->col_func == SWQCF_NONE && !def->distinct_flag)
343
0
        return nullptr;
344
345
0
    if (select_info->query_mode == SWQM_DISTINCT_LIST &&
346
0
        select_info->order_specs > 0)
347
0
    {
348
0
        if (select_info->order_specs > 1)
349
0
            return "Can't ORDER BY a DISTINCT list by more than one key.";
350
351
0
        if (select_info->order_defs[0].field_index !=
352
0
            select_info->column_defs[0].field_index)
353
0
            return "Only selected DISTINCT field can be used for ORDER BY.";
354
0
    }
355
356
    /* -------------------------------------------------------------------- */
357
    /*      Create the summary information if this is the first row         */
358
    /*      being processed.                                                */
359
    /* -------------------------------------------------------------------- */
360
0
    if (select_info->column_summary.empty())
361
0
    {
362
0
        select_info->column_summary.resize(select_info->column_defs.size());
363
0
        for (std::size_t i = 0; i < select_info->column_defs.size(); i++)
364
0
        {
365
0
            if (def->distinct_flag)
366
0
            {
367
0
                swq_summary::Comparator oComparator;
368
0
                if (select_info->order_specs > 0)
369
0
                {
370
0
                    CPLAssert(select_info->order_specs == 1);
371
0
                    CPLAssert(select_info->column_defs.size() == 1);
372
0
                    oComparator.bSortAsc =
373
0
                        CPL_TO_BOOL(select_info->order_defs[0].ascending_flag);
374
0
                }
375
0
                if (select_info->column_defs[i].field_type == SWQ_INTEGER ||
376
0
                    -select_info->column_defs[i].field_type == SWQ_INTEGER64)
377
0
                {
378
0
                    oComparator.eType = SWQ_INTEGER64;
379
0
                }
380
0
                else if (select_info->column_defs[i].field_type == SWQ_FLOAT)
381
0
                {
382
0
                    oComparator.eType = SWQ_FLOAT;
383
0
                }
384
0
                else
385
0
                {
386
0
                    oComparator.eType = SWQ_STRING;
387
0
                }
388
0
                select_info->column_summary[i].oSetDistinctValues =
389
0
                    std::set<CPLString, swq_summary::Comparator>(oComparator);
390
0
            }
391
0
            select_info->column_summary[i].min =
392
0
                std::numeric_limits<double>::infinity();
393
0
            select_info->column_summary[i].max =
394
0
                -std::numeric_limits<double>::infinity();
395
0
            select_info->column_summary[i].osMin = "9999/99/99 99:99:99";
396
0
            select_info->column_summary[i].osMax = "0000/00/00 00:00:00";
397
0
        }
398
0
        assert(!select_info->column_summary.empty());
399
0
    }
400
401
    /* -------------------------------------------------------------------- */
402
    /*      If distinct processing is on, process that now.                 */
403
    /* -------------------------------------------------------------------- */
404
0
    swq_summary &summary = select_info->column_summary[dest_column];
405
406
0
    if (def->distinct_flag)
407
0
    {
408
0
        if (pszValue == nullptr)
409
0
            pszValue = SZ_OGR_NULL;
410
0
        try
411
0
        {
412
0
            if (!cpl::contains(summary.oSetDistinctValues, pszValue))
413
0
            {
414
0
                summary.oSetDistinctValues.insert(pszValue);
415
0
                if (select_info->order_specs == 0)
416
0
                {
417
                    // If not sorted, keep values in their original order
418
0
                    summary.oVectorDistinctValues.emplace_back(pszValue);
419
0
                }
420
0
                summary.count++;
421
0
            }
422
0
        }
423
0
        catch (std::bad_alloc &)
424
0
        {
425
0
            return "Out of memory";
426
0
        }
427
428
0
        return nullptr;
429
0
    }
430
431
    /* -------------------------------------------------------------------- */
432
    /*      Process various options.                                        */
433
    /* -------------------------------------------------------------------- */
434
435
0
    switch (def->col_func)
436
0
    {
437
0
        case SWQCF_MIN:
438
0
            if (pdfValue)
439
0
            {
440
0
                if (*pdfValue < summary.min)
441
0
                    summary.min = *pdfValue;
442
0
                summary.count++;
443
0
            }
444
0
            else if (pszValue && pszValue[0] != '\0')
445
0
            {
446
0
                if (summary.count == 0 || strcmp(pszValue, summary.osMin) < 0)
447
0
                {
448
0
                    summary.osMin = pszValue;
449
0
                }
450
0
                summary.count++;
451
0
            }
452
0
            break;
453
0
        case SWQCF_MAX:
454
0
            if (pdfValue)
455
0
            {
456
0
                if (*pdfValue > summary.max)
457
0
                    summary.max = *pdfValue;
458
0
                summary.count++;
459
0
            }
460
0
            else if (pszValue && pszValue[0] != '\0')
461
0
            {
462
0
                if (summary.count == 0 || strcmp(pszValue, summary.osMax) > 0)
463
0
                {
464
0
                    summary.osMax = pszValue;
465
0
                }
466
0
                summary.count++;
467
0
            }
468
0
            break;
469
0
        case SWQCF_AVG:
470
0
        case SWQCF_SUM:
471
0
            if (pdfValue)
472
0
            {
473
0
                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
0
                const double dfNewVal = *pdfValue;
481
0
                const volatile double new_sum_acc = summary.sum_acc + dfNewVal;
482
0
                if (summary.sum_only_finite_terms && std::isfinite(dfNewVal))
483
0
                {
484
0
                    if (std::fabs(summary.sum_acc) >= std::fabs(dfNewVal))
485
0
                    {
486
0
                        const volatile double diff =
487
0
                            (summary.sum_acc - new_sum_acc);
488
0
                        summary.sum_correction += (diff + dfNewVal);
489
0
                    }
490
0
                    else
491
0
                    {
492
0
                        const volatile double diff = (dfNewVal - new_sum_acc);
493
0
                        summary.sum_correction += (diff + summary.sum_acc);
494
0
                    }
495
0
                }
496
0
                else
497
0
                {
498
0
                    summary.sum_only_finite_terms = false;
499
0
                }
500
0
                summary.sum_acc = new_sum_acc;
501
0
            }
502
0
            else if (pszValue && pszValue[0] != '\0')
503
0
            {
504
0
                if (def->field_type == SWQ_DATE ||
505
0
                    def->field_type == SWQ_TIME ||
506
0
                    def->field_type == SWQ_TIMESTAMP)
507
0
                {
508
0
                    OGRField sField;
509
0
                    if (OGRParseDate(pszValue, &sField, 0))
510
0
                    {
511
0
                        struct tm brokendowntime;
512
0
                        brokendowntime.tm_year = sField.Date.Year - 1900;
513
0
                        brokendowntime.tm_mon = sField.Date.Month - 1;
514
0
                        brokendowntime.tm_mday = sField.Date.Day;
515
0
                        brokendowntime.tm_hour = sField.Date.Hour;
516
0
                        brokendowntime.tm_min = sField.Date.Minute;
517
0
                        brokendowntime.tm_sec =
518
0
                            static_cast<int>(sField.Date.Second);
519
0
                        summary.count++;
520
0
                        summary.sum_acc += CPLYMDHMSToUnixTime(&brokendowntime);
521
0
                        summary.sum_acc +=
522
0
                            fmod(static_cast<double>(sField.Date.Second), 1.0);
523
0
                    }
524
0
                }
525
0
                else
526
0
                {
527
0
                    return "swq_select_summarize() - AVG()/SUM() called on "
528
0
                           "unexpected field type";
529
0
                }
530
0
            }
531
0
            break;
532
533
0
        case SWQCF_COUNT:
534
0
            if (pdfValue || pszValue)
535
0
                summary.count++;
536
0
            break;
537
538
0
        case SWQCF_STDDEV_POP:
539
0
        case SWQCF_STDDEV_SAMP:
540
0
        {
541
0
            const auto UpdateVariance = [&summary](double dfValue)
542
0
            {
543
                // Welford's online algorithm for variance:
544
                // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
545
0
                summary.count++;
546
0
                const double dfDelta = dfValue - summary.mean_for_variance;
547
0
                summary.mean_for_variance += dfDelta / summary.count;
548
0
                const double dfDelta2 = dfValue - summary.mean_for_variance;
549
0
                summary.sq_dist_from_mean_acc += dfDelta * dfDelta2;
550
0
            };
551
552
0
            if (pdfValue)
553
0
            {
554
0
                UpdateVariance(*pdfValue);
555
0
            }
556
0
            else if (pszValue && pszValue[0] != '\0')
557
0
            {
558
0
                if (def->field_type == SWQ_DATE ||
559
0
                    def->field_type == SWQ_TIME ||
560
0
                    def->field_type == SWQ_TIMESTAMP)
561
0
                {
562
0
                    OGRField sField;
563
0
                    if (OGRParseDate(pszValue, &sField, 0))
564
0
                    {
565
0
                        struct tm brokendowntime;
566
0
                        brokendowntime.tm_year = sField.Date.Year - 1900;
567
0
                        brokendowntime.tm_mon = sField.Date.Month - 1;
568
0
                        brokendowntime.tm_mday = sField.Date.Day;
569
0
                        brokendowntime.tm_hour = sField.Date.Hour;
570
0
                        brokendowntime.tm_min = sField.Date.Minute;
571
0
                        brokendowntime.tm_sec =
572
0
                            static_cast<int>(sField.Date.Second);
573
574
0
                        UpdateVariance(static_cast<double>(
575
0
                            CPLYMDHMSToUnixTime(&brokendowntime)));
576
0
                    }
577
0
                }
578
0
                else
579
0
                {
580
0
                    return "swq_select_summarize() - STDDEV() called on "
581
0
                           "unexpected field type";
582
0
                }
583
0
            }
584
585
0
            break;
586
0
        }
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
0
    }
594
595
0
    return nullptr;
596
0
}
597
598
/************************************************************************/
599
/*                      sort comparison functions.                      */
600
/************************************************************************/
601
602
static bool Compare(swq_field_type eType, const CPLString &a,
603
                    const CPLString &b)
604
0
{
605
0
    if (a == SZ_OGR_NULL)
606
0
        return b != SZ_OGR_NULL;
607
0
    else if (b == SZ_OGR_NULL)
608
0
        return false;
609
0
    else
610
0
    {
611
0
        if (eType == SWQ_INTEGER64)
612
0
            return CPLAtoGIntBig(a) < CPLAtoGIntBig(b);
613
0
        else if (eType == SWQ_FLOAT)
614
0
            return CPLAtof(a) < CPLAtof(b);
615
0
        else if (eType == SWQ_STRING)
616
0
            return a < b;
617
0
        else
618
0
        {
619
0
            CPLAssert(false);
620
0
            return false;
621
0
        }
622
0
    }
623
0
}
624
625
#ifndef DOXYGEN_SKIP
626
bool swq_summary::Comparator::operator()(const CPLString &a,
627
                                         const CPLString &b) const
628
0
{
629
0
    if (bSortAsc)
630
0
    {
631
0
        return Compare(eType, a, b);
632
0
    }
633
0
    else
634
0
    {
635
0
        return Compare(eType, b, a);
636
0
    }
637
0
}
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
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
0
{
653
0
    return swq_identify_field_internal(table_name, field_token, field_list,
654
0
                                       this_type, table_id, TRUE);
655
0
}
656
657
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
0
{
663
0
    if (table_name == nullptr)
664
0
        table_name = "";
665
666
0
    int tables_enabled;
667
668
0
    if (field_list->table_count > 0 && field_list->table_ids != nullptr)
669
0
        tables_enabled = TRUE;
670
0
    else
671
0
        tables_enabled = FALSE;
672
673
    /* -------------------------------------------------------------------- */
674
    /*      Search for matching field.                                      */
675
    /* -------------------------------------------------------------------- */
676
0
    for (int pass = 0; pass < 2; ++pass)
677
0
    {
678
0
        for (int i = 0; i < field_list->count; i++)
679
0
        {
680
0
            if ((pass == 0 && strcmp(field_list->names[i], field_token) != 0) ||
681
0
                (pass == 1 && !EQUAL(field_list->names[i], field_token)))
682
0
            {
683
0
                continue;
684
0
            }
685
686
0
            int t_id = 0;
687
688
            // Do the table specifications match?/
689
0
            if (tables_enabled)
690
0
            {
691
0
                t_id = field_list->table_ids[i];
692
0
                if (table_name[0] != '\0' &&
693
0
                    !EQUAL(table_name,
694
0
                           field_list->table_defs[t_id].table_alias))
695
0
                    continue;
696
697
                // if( t_id != 0 && table_name[0] == '\0' )
698
                //     continue;
699
0
            }
700
0
            else if (table_name[0] != '\0')
701
0
                break;
702
703
            // We have a match, return various information.
704
0
            if (this_type != nullptr)
705
0
            {
706
0
                if (field_list->types != nullptr)
707
0
                    *this_type = field_list->types[i];
708
0
                else
709
0
                    *this_type = SWQ_OTHER;
710
0
            }
711
712
0
            if (table_id != nullptr)
713
0
                *table_id = t_id;
714
715
0
            if (field_list->ids == nullptr)
716
0
                return i;
717
0
            else
718
0
                return field_list->ids[i];
719
0
        }
720
0
    }
721
722
    /* -------------------------------------------------------------------- */
723
    /*      When there is no ambiguity, try to accept quoting errors...     */
724
    /* -------------------------------------------------------------------- */
725
0
    if (bOneMoreTimeOK &&
726
0
        !CPLTestBool(CPLGetConfigOption("OGR_SQL_STRICT", "FALSE")))
727
0
    {
728
0
        if (table_name[0])
729
0
        {
730
0
            CPLString osAggregatedName(
731
0
                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
0
            int i = 0;  // Used after for.
736
0
            for (; i < field_list->count; i++)
737
0
            {
738
0
                if (tables_enabled)
739
0
                {
740
0
                    int t_id = field_list->table_ids[i];
741
0
                    if (EQUAL(table_name,
742
0
                              field_list->table_defs[t_id].table_alias))
743
0
                        break;
744
0
                }
745
0
            }
746
0
            if (i == field_list->count)
747
0
            {
748
0
                int ret = swq_identify_field_internal(nullptr, osAggregatedName,
749
0
                                                      field_list, this_type,
750
0
                                                      table_id, FALSE);
751
0
                if (ret >= 0)
752
0
                {
753
0
                    CPLError(CE_Warning, CPLE_AppDefined,
754
0
                             "Passed field name %s.%s should have been "
755
0
                             "surrounded by double quotes. "
756
0
                             "Accepted since there is no ambiguity...",
757
0
                             table_name, field_token);
758
0
                }
759
0
                return ret;
760
0
            }
761
0
        }
762
0
        else
763
0
        {
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
0
            const char *pszDot = strchr(field_token, '.');
768
0
            if (pszDot && strchr(pszDot + 1, '.') == nullptr)
769
0
            {
770
0
                CPLString osTableName(field_token);
771
0
                osTableName.resize(pszDot - field_token);
772
0
                CPLString osFieldName(pszDot + 1);
773
774
0
                int ret = swq_identify_field_internal(osTableName, osFieldName,
775
0
                                                      field_list, this_type,
776
0
                                                      table_id, FALSE);
777
0
                if (ret >= 0)
778
0
                {
779
0
                    CPLError(CE_Warning, CPLE_AppDefined,
780
0
                             "Passed field name %s should NOT have been "
781
0
                             "surrounded by double quotes. "
782
0
                             "Accepted since there is no ambiguity...",
783
0
                             field_token);
784
0
                }
785
0
                return ret;
786
0
            }
787
0
        }
788
0
    }
789
790
    /* -------------------------------------------------------------------- */
791
    /*      No match, return failure.                                       */
792
    /* -------------------------------------------------------------------- */
793
0
    if (this_type != nullptr)
794
0
        *this_type = SWQ_OTHER;
795
796
0
    if (table_id != nullptr)
797
0
        *table_id = 0;
798
799
0
    return -1;
800
0
}
801
802
/************************************************************************/
803
/*                          swq_expr_compile()                          */
804
/************************************************************************/
805
806
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
0
{
813
0
    swq_field_list field_list;
814
815
0
    field_list.count = field_count;
816
0
    field_list.names = field_names;
817
0
    field_list.types = field_types;
818
0
    field_list.table_ids = nullptr;
819
0
    field_list.ids = nullptr;
820
821
0
    field_list.table_count = 0;
822
0
    field_list.table_defs = nullptr;
823
824
0
    return swq_expr_compile2(where_clause, &field_list, bCheck,
825
0
                             poCustomFuncRegistrar, expr_out);
826
0
}
827
828
/************************************************************************/
829
/*                       swq_fixup_expression()                         */
830
/************************************************************************/
831
832
void swq_fixup(swq_parse_context *psParseContext)
833
0
{
834
0
    if (psParseContext->poRoot)
835
0
    {
836
0
        psParseContext->poRoot->RebalanceAndOr();
837
0
    }
838
0
    auto psSelect = psParseContext->poCurSelect;
839
0
    while (psSelect)
840
0
    {
841
0
        if (psSelect->where_expr)
842
0
        {
843
0
            psSelect->where_expr->RebalanceAndOr();
844
0
        }
845
0
        psSelect = psSelect->poOtherSelect;
846
0
    }
847
0
}
848
849
/************************************************************************/
850
/*                       swq_create_and_or_or()                         */
851
/************************************************************************/
852
853
swq_expr_node *swq_create_and_or_or(swq_op op, swq_expr_node *left,
854
                                    swq_expr_node *right)
855
0
{
856
0
    auto poNode = new swq_expr_node(op);
857
0
    poNode->field_type = SWQ_BOOLEAN;
858
859
0
    if (left->eNodeType == SNT_OPERATION && left->nOperation == op)
860
0
    {
861
        // Temporary non-binary formulation
862
0
        if (right->eNodeType == SNT_OPERATION && right->nOperation == op)
863
0
        {
864
0
            poNode->nSubExprCount = left->nSubExprCount + right->nSubExprCount;
865
0
            poNode->papoSubExpr = static_cast<swq_expr_node **>(
866
0
                CPLRealloc(left->papoSubExpr,
867
0
                           sizeof(swq_expr_node *) * poNode->nSubExprCount));
868
0
            memcpy(poNode->papoSubExpr + left->nSubExprCount,
869
0
                   right->papoSubExpr,
870
0
                   right->nSubExprCount * sizeof(swq_expr_node *));
871
872
0
            right->nSubExprCount = 0;
873
0
            CPLFree(right->papoSubExpr);
874
0
            right->papoSubExpr = nullptr;
875
0
            delete right;
876
0
        }
877
0
        else
878
0
        {
879
0
            poNode->nSubExprCount = left->nSubExprCount;
880
0
            poNode->papoSubExpr = left->papoSubExpr;
881
0
            poNode->PushSubExpression(right);
882
0
        }
883
884
0
        left->nSubExprCount = 0;
885
0
        left->papoSubExpr = nullptr;
886
0
        delete left;
887
0
    }
888
0
    else if (right->eNodeType == SNT_OPERATION && right->nOperation == op)
889
0
    {
890
        // Temporary non-binary formulation
891
0
        poNode->nSubExprCount = right->nSubExprCount;
892
0
        poNode->papoSubExpr = right->papoSubExpr;
893
0
        poNode->PushSubExpression(left);
894
895
0
        right->nSubExprCount = 0;
896
0
        right->papoSubExpr = nullptr;
897
0
        delete right;
898
0
    }
899
0
    else
900
0
    {
901
0
        poNode->PushSubExpression(left);
902
0
        poNode->PushSubExpression(right);
903
0
    }
904
905
0
    return poNode;
906
0
}
907
908
/************************************************************************/
909
/*                         swq_expr_compile2()                          */
910
/************************************************************************/
911
912
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
0
{
918
0
    swq_parse_context context;
919
920
0
    context.pszInput = where_clause;
921
0
    context.pszNext = where_clause;
922
0
    context.pszLastValid = where_clause;
923
0
    context.nStartToken = SWQT_VALUE_START;
924
0
    context.bAcceptCustomFuncs = poCustomFuncRegistrar != nullptr;
925
926
0
    if (swqparse(&context) == 0 && bCheck &&
927
0
        context.poRoot->Check(field_list, FALSE, FALSE,
928
0
                              poCustomFuncRegistrar) != SWQ_ERROR)
929
0
    {
930
0
        *expr_out = context.poRoot;
931
932
0
        return CE_None;
933
0
    }
934
0
    else
935
0
    {
936
0
        delete context.poRoot;
937
0
        *expr_out = nullptr;
938
0
        return CE_Failure;
939
0
    }
940
0
}
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
int swq_is_reserved_keyword(const char *pszStr)
952
0
{
953
0
    for (const auto &pszKeyword : apszSQLReservedKeywords)
954
0
    {
955
0
        if (EQUAL(pszStr, pszKeyword))
956
0
            return TRUE;
957
0
    }
958
0
    return FALSE;
959
0
}
960
961
/************************************************************************/
962
/*                          SWQFieldTypeToString()                      */
963
/************************************************************************/
964
965
const char *SWQFieldTypeToString(swq_field_type field_type)
966
0
{
967
0
    switch (field_type)
968
0
    {
969
0
        case SWQ_INTEGER:
970
0
            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
0
        case SWQ_GEOMETRY:
986
0
            return "geometry";
987
0
        case SWQ_NULL:
988
0
            return "null";
989
0
        default:
990
0
            return "unknown";
991
0
    }
992
0
}
993
994
//! @cond Doxygen_Suppress
995
996
0
swq_custom_func_registrar::~swq_custom_func_registrar() = default;
997
998
//! @endcond