Coverage Report

Created: 2025-06-09 07:43

/src/gdal/ogr/swq_select.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Component: OGR SQL Engine
4
 * Purpose: swq_select class implementation.
5
 * Author: Frank Warmerdam <warmerdam@pobox.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (C) 2010 Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "ogr_swq.h"
16
17
#include <algorithm>
18
#include <cstdio>
19
#include <cstring>
20
#include <string>
21
22
#include "cpl_conv.h"
23
#include "cpl_error.h"
24
#include "cpl_string.h"
25
#include "ogr_core.h"
26
#include "ogr_geometry.h"
27
#include "swq_parser.hpp"
28
29
//! @cond Doxygen_Suppress
30
/************************************************************************/
31
/*                             swq_select()                             */
32
/************************************************************************/
33
34
0
swq_select::swq_select() = default;
35
36
/************************************************************************/
37
/*                            ~swq_select()                             */
38
/************************************************************************/
39
40
swq_select::~swq_select()
41
42
0
{
43
0
    delete where_expr;
44
0
    CPLFree(raw_select);
45
46
0
    for (int i = 0; i < table_count; i++)
47
0
    {
48
0
        swq_table_def *table_def = table_defs + i;
49
50
0
        CPLFree(table_def->data_source);
51
0
        CPLFree(table_def->table_name);
52
0
        CPLFree(table_def->table_alias);
53
0
    }
54
0
    CPLFree(table_defs);
55
56
0
    for (auto &col : column_defs)
57
0
    {
58
0
        CPLFree(col.table_name);
59
0
        CPLFree(col.field_name);
60
0
        CPLFree(col.field_alias);
61
62
0
        delete col.expr;
63
0
    }
64
65
    // cppcheck-suppress constVariableReference
66
0
    for (auto &entry : m_exclude_fields)
67
0
    {
68
        // cppcheck-suppress constVariableReference
69
0
        for (auto &col : entry.second)
70
0
        {
71
0
            CPLFree(col.table_name);
72
0
            CPLFree(col.field_name);
73
0
            CPLFree(col.field_alias);
74
75
0
            delete col.expr;
76
0
        }
77
0
    }
78
79
0
    for (int i = 0; i < order_specs; i++)
80
0
    {
81
0
        CPLFree(order_defs[i].table_name);
82
0
        CPLFree(order_defs[i].field_name);
83
0
    }
84
85
0
    CPLFree(order_defs);
86
87
0
    for (int i = 0; i < join_count; i++)
88
0
    {
89
0
        delete join_defs[i].poExpr;
90
0
    }
91
0
    CPLFree(join_defs);
92
93
0
    delete poOtherSelect;
94
0
}
95
96
/************************************************************************/
97
/*                              preparse()                              */
98
/*                                                                      */
99
/*      Parse the expression but without knowing the available          */
100
/*      tables and fields.                                              */
101
/************************************************************************/
102
103
CPLErr swq_select::preparse(const char *select_statement,
104
                            int bAcceptCustomFuncs)
105
106
0
{
107
    /* -------------------------------------------------------------------- */
108
    /*      Prepare a parser context.                                       */
109
    /* -------------------------------------------------------------------- */
110
0
    swq_parse_context context;
111
112
0
    context.pszInput = select_statement;
113
0
    context.pszNext = select_statement;
114
0
    context.pszLastValid = select_statement;
115
0
    context.nStartToken = SWQT_SELECT_START;
116
0
    context.bAcceptCustomFuncs = bAcceptCustomFuncs;
117
0
    context.poCurSelect = this;
118
119
    /* -------------------------------------------------------------------- */
120
    /*      Do the parse.                                                   */
121
    /* -------------------------------------------------------------------- */
122
0
    if (swqparse(&context) != 0)
123
0
    {
124
0
        delete context.poRoot;
125
0
        return CE_Failure;
126
0
    }
127
128
    // Restore poCurSelect as it might have been modified by UNION ALL
129
0
    context.poCurSelect = this;
130
0
    swq_fixup(&context);
131
132
0
    postpreparse();
133
134
0
    return CE_None;
135
0
}
136
137
/************************************************************************/
138
/*                          postpreparse()                              */
139
/************************************************************************/
140
141
void swq_select::postpreparse()
142
0
{
143
    /* -------------------------------------------------------------------- */
144
    /*      Reorder the joins in the order they appear in the SQL string.   */
145
    /* -------------------------------------------------------------------- */
146
0
    for (int i = 0; i < join_count / 2; i++)
147
0
    {
148
0
        swq_join_def sTmp;
149
0
        memcpy(&sTmp, &join_defs[i], sizeof(swq_join_def));
150
0
        memcpy(&join_defs[i], &join_defs[join_count - 1 - i],
151
0
               sizeof(swq_join_def));
152
0
        memcpy(&join_defs[join_count - 1 - i], &sTmp, sizeof(swq_join_def));
153
0
    }
154
155
    // We make that strong assumption in ogr_gensql.
156
0
    for (int i = 0; i < join_count; i++)
157
0
    {
158
0
        CPLAssert(join_defs[i].secondary_table == i + 1);
159
0
    }
160
161
0
    if (poOtherSelect != nullptr)
162
0
        poOtherSelect->postpreparse();
163
0
}
164
165
/************************************************************************/
166
/*                               Unparse()                              */
167
/************************************************************************/
168
169
char *swq_select::Unparse()
170
0
{
171
0
    CPLString osSelect("SELECT ");
172
0
    if (query_mode == SWQM_DISTINCT_LIST)
173
0
        osSelect += "DISTINCT ";
174
175
0
    for (int i = 0; i < result_columns(); i++)
176
0
    {
177
0
        swq_col_def *def = &column_defs[i];
178
179
0
        if (i > 0)
180
0
            osSelect += ", ";
181
182
0
        if (def->expr != nullptr && def->col_func == SWQCF_NONE)
183
0
        {
184
0
            char *pszTmp = def->expr->Unparse(nullptr, '"');
185
0
            osSelect += pszTmp;
186
0
            CPLFree(pszTmp);
187
0
        }
188
0
        else
189
0
        {
190
0
            switch (def->col_func)
191
0
            {
192
0
                case SWQCF_NONE:
193
0
                    break;
194
0
                case SWQCF_AVG:
195
0
                    osSelect += "AVG(";
196
0
                    break;
197
0
                case SWQCF_MIN:
198
0
                    osSelect += "MIN(";
199
0
                    break;
200
0
                case SWQCF_MAX:
201
0
                    osSelect += "MAX(";
202
0
                    break;
203
0
                case SWQCF_COUNT:
204
0
                    osSelect += "COUNT(";
205
0
                    break;
206
0
                case SWQCF_SUM:
207
0
                    osSelect += "SUM(";
208
0
                    break;
209
0
                case SWQCF_STDDEV_POP:
210
0
                    osSelect += "STDDEV_POP(";
211
0
                    break;
212
0
                case SWQCF_STDDEV_SAMP:
213
0
                    osSelect += "STDDEV_SAMP(";
214
0
                    break;
215
0
                case SWQCF_CUSTOM:
216
0
                    break;
217
0
            }
218
219
0
            if (def->distinct_flag && def->col_func == SWQCF_COUNT)
220
0
                osSelect += "DISTINCT ";
221
222
0
            if ((def->field_alias == nullptr || table_count > 1) &&
223
0
                def->table_name != nullptr && def->table_name[0] != '\0')
224
0
            {
225
0
                osSelect +=
226
0
                    swq_expr_node::QuoteIfNecessary(def->table_name, '"');
227
0
                osSelect += ".";
228
0
            }
229
0
            osSelect += swq_expr_node::QuoteIfNecessary(def->field_name, '"');
230
0
            osSelect += ")";
231
0
        }
232
233
0
        if (def->field_alias != nullptr &&
234
0
            strcmp(def->field_name, def->field_alias) != 0)
235
0
        {
236
0
            osSelect += " AS ";
237
0
            osSelect += swq_expr_node::QuoteIfNecessary(def->field_alias, '"');
238
0
        }
239
0
    }
240
241
0
    osSelect += " FROM ";
242
0
    if (table_defs[0].data_source != nullptr)
243
0
    {
244
0
        osSelect += "'";
245
0
        osSelect += table_defs[0].data_source;
246
0
        osSelect += "'.";
247
0
    }
248
0
    osSelect += swq_expr_node::QuoteIfNecessary(table_defs[0].table_name, '"');
249
0
    if (table_defs[0].table_alias != nullptr &&
250
0
        strcmp(table_defs[0].table_name, table_defs[0].table_alias) != 0)
251
0
    {
252
0
        osSelect += " AS ";
253
0
        osSelect +=
254
0
            swq_expr_node::QuoteIfNecessary(table_defs[0].table_alias, '"');
255
0
    }
256
257
0
    for (int i = 0; i < join_count; i++)
258
0
    {
259
0
        int iTable = join_defs[i].secondary_table;
260
0
        osSelect += " JOIN ";
261
0
        if (table_defs[iTable].data_source != nullptr)
262
0
        {
263
0
            osSelect += "'";
264
0
            osSelect += table_defs[iTable].data_source;
265
0
            osSelect += "'.";
266
0
        }
267
0
        osSelect +=
268
0
            swq_expr_node::QuoteIfNecessary(table_defs[iTable].table_name, '"');
269
0
        if (table_defs[iTable].table_alias != nullptr &&
270
0
            strcmp(table_defs[iTable].table_name,
271
0
                   table_defs[iTable].table_alias) != 0)
272
0
        {
273
0
            osSelect += " AS ";
274
0
            osSelect += swq_expr_node::QuoteIfNecessary(
275
0
                table_defs[iTable].table_alias, '"');
276
0
        }
277
0
        osSelect += " ON ";
278
0
        char *pszTmp = join_defs[i].poExpr->Unparse(nullptr, '"');
279
0
        osSelect += pszTmp;
280
0
        CPLFree(pszTmp);
281
0
    }
282
283
0
    if (where_expr != nullptr)
284
0
    {
285
0
        osSelect += " WHERE ";
286
0
        char *pszTmp = where_expr->Unparse(nullptr, '"');
287
0
        osSelect += pszTmp;
288
0
        CPLFree(pszTmp);
289
0
    }
290
291
0
    if (order_specs > 0)
292
0
    {
293
0
        osSelect += " ORDER BY ";
294
0
        for (int i = 0; i < order_specs; i++)
295
0
        {
296
0
            if (i > 0)
297
0
                osSelect += ", ";
298
0
            osSelect +=
299
0
                swq_expr_node::QuoteIfNecessary(order_defs[i].field_name, '"');
300
0
            if (!order_defs[i].ascending_flag)
301
0
                osSelect += " DESC";
302
0
        }
303
0
    }
304
305
0
    if (limit >= 0)
306
0
    {
307
0
        osSelect += " LIMIT ";
308
0
        osSelect += CPLSPrintf(CPL_FRMT_GIB, limit);
309
0
    }
310
311
0
    if (offset > 0)
312
0
    {
313
0
        osSelect += " OFFSET ";
314
0
        osSelect += CPLSPrintf(CPL_FRMT_GIB, offset);
315
0
    }
316
317
0
    return CPLStrdup(osSelect);
318
0
}
319
320
/************************************************************************/
321
/*                             PushField()                              */
322
/*                                                                      */
323
/*      Create a new field definition by name and possibly alias.       */
324
/************************************************************************/
325
326
int swq_select::PushField(swq_expr_node *poExpr, const char *pszAlias,
327
                          bool distinct_flag, bool bHidden)
328
329
0
{
330
0
    if (query_mode == SWQM_DISTINCT_LIST && distinct_flag)
331
0
    {
332
0
        CPLError(CE_Failure, CPLE_NotSupported,
333
0
                 "SELECT DISTINCT and COUNT(DISTINCT...) "
334
0
                 "not supported together");
335
0
        return FALSE;
336
0
    }
337
338
    /* -------------------------------------------------------------------- */
339
    /*      Grow the array.                                                 */
340
    /* -------------------------------------------------------------------- */
341
342
0
    column_defs.emplace_back();
343
0
    swq_col_def *col_def = &column_defs.back();
344
345
0
    memset(col_def, 0, sizeof(swq_col_def));
346
347
    /* -------------------------------------------------------------------- */
348
    /*      Try to capture a field name.                                    */
349
    /* -------------------------------------------------------------------- */
350
0
    if (poExpr->eNodeType == SNT_COLUMN)
351
0
    {
352
0
        col_def->table_name =
353
0
            CPLStrdup(poExpr->table_name ? poExpr->table_name : "");
354
0
        col_def->field_name = CPLStrdup(poExpr->string_value);
355
356
        // Associate a column list from an EXCEPT () clause with its associated
357
        // wildcard
358
0
        if (EQUAL(col_def->field_name, "*"))
359
0
        {
360
0
            auto it = m_exclude_fields.find(-1);
361
362
0
            if (it != m_exclude_fields.end())
363
0
            {
364
0
                int curr_asterisk_pos =
365
0
                    static_cast<int>(column_defs.size() - 1);
366
0
                m_exclude_fields[curr_asterisk_pos] = std::move(it->second);
367
0
                m_exclude_fields.erase(it);
368
0
            }
369
0
        }
370
0
    }
371
0
    else if (poExpr->eNodeType == SNT_OPERATION &&
372
0
             (poExpr->nOperation == SWQ_CAST ||
373
0
              (poExpr->nOperation >= SWQ_AGGREGATE_BEGIN &&
374
0
               poExpr->nOperation <= SWQ_AGGREGATE_END)) &&
375
0
             poExpr->nSubExprCount >= 1 &&
376
0
             poExpr->papoSubExpr[0]->eNodeType == SNT_COLUMN)
377
0
    {
378
0
        col_def->table_name = CPLStrdup(poExpr->papoSubExpr[0]->table_name
379
0
                                            ? poExpr->papoSubExpr[0]->table_name
380
0
                                            : "");
381
0
        col_def->field_name = CPLStrdup(poExpr->papoSubExpr[0]->string_value);
382
0
    }
383
0
    else
384
0
    {
385
0
        col_def->table_name = CPLStrdup("");
386
0
        col_def->field_name = CPLStrdup("");
387
0
    }
388
389
    /* -------------------------------------------------------------------- */
390
    /*      Initialize fields.                                              */
391
    /* -------------------------------------------------------------------- */
392
0
    if (pszAlias != nullptr)
393
0
        col_def->field_alias = CPLStrdup(pszAlias);
394
0
    else if (poExpr->eNodeType == SNT_OPERATION && poExpr->nSubExprCount >= 1 &&
395
0
             (static_cast<swq_op>(poExpr->nOperation) == SWQ_CONCAT ||
396
0
              static_cast<swq_op>(poExpr->nOperation) == SWQ_SUBSTR) &&
397
0
             poExpr->papoSubExpr[0]->eNodeType == SNT_COLUMN)
398
0
    {
399
0
        const swq_operation *op = swq_op_registrar::GetOperator(
400
0
            static_cast<swq_op>(poExpr->nOperation));
401
402
0
        col_def->field_alias = CPLStrdup(CPLSPrintf(
403
0
            "%s_%s", op->pszName, poExpr->papoSubExpr[0]->string_value));
404
0
    }
405
406
0
    if (bHidden)
407
0
    {
408
0
        const char *pszDstFieldName =
409
0
            col_def->field_alias ? col_def->field_alias : col_def->field_name;
410
0
        if (!EQUAL(pszDstFieldName, "OGR_STYLE"))
411
0
        {
412
0
            CPLError(
413
0
                CE_Failure, CPLE_AppDefined,
414
0
                "HIDDEN keyword only supported on a column named OGR_STYLE");
415
0
            CPLFree(col_def->table_name);
416
0
            col_def->table_name = nullptr;
417
0
            CPLFree(col_def->field_name);
418
0
            col_def->field_name = nullptr;
419
0
            CPLFree(col_def->field_alias);
420
0
            col_def->field_alias = nullptr;
421
0
            column_defs.pop_back();
422
0
            return FALSE;
423
0
        }
424
0
    }
425
426
0
    col_def->table_index = -1;
427
0
    col_def->field_index = -1;
428
0
    col_def->field_type = SWQ_OTHER;
429
0
    col_def->field_precision = -1;
430
0
    col_def->target_type = SWQ_OTHER;
431
0
    col_def->target_subtype = OFSTNone;
432
0
    col_def->col_func = SWQCF_NONE;
433
0
    col_def->distinct_flag = distinct_flag;
434
0
    col_def->bHidden = bHidden;
435
436
    /* -------------------------------------------------------------------- */
437
    /*      Do we have a CAST operator in play?                             */
438
    /* -------------------------------------------------------------------- */
439
0
    if (poExpr->eNodeType == SNT_OPERATION && poExpr->nOperation == SWQ_CAST)
440
0
    {
441
0
        const char *pszTypeName = poExpr->papoSubExpr[1]->string_value;
442
0
        int parse_precision = 0;
443
444
0
        if (EQUAL(pszTypeName, "character"))
445
0
        {
446
0
            col_def->target_type = SWQ_STRING;
447
0
            col_def->field_length = 1;
448
0
        }
449
0
        else if (strcasecmp(pszTypeName, "boolean") == 0)
450
0
        {
451
0
            col_def->target_type = SWQ_BOOLEAN;
452
0
        }
453
0
        else if (strcasecmp(pszTypeName, "integer") == 0)
454
0
        {
455
0
            col_def->target_type = SWQ_INTEGER;
456
0
        }
457
0
        else if (strcasecmp(pszTypeName, "bigint") == 0)
458
0
        {
459
0
            col_def->target_type = SWQ_INTEGER64;
460
0
        }
461
0
        else if (strcasecmp(pszTypeName, "smallint") == 0)
462
0
        {
463
0
            col_def->target_type = SWQ_INTEGER;
464
0
            col_def->target_subtype = OFSTInt16;
465
0
        }
466
0
        else if (strcasecmp(pszTypeName, "float") == 0)
467
0
        {
468
0
            col_def->target_type = SWQ_FLOAT;
469
0
        }
470
0
        else if (strcasecmp(pszTypeName, "numeric") == 0)
471
0
        {
472
0
            col_def->target_type = SWQ_FLOAT;
473
0
            parse_precision = 1;
474
0
        }
475
0
        else if (strcasecmp(pszTypeName, "timestamp") == 0)
476
0
        {
477
0
            col_def->target_type = SWQ_TIMESTAMP;
478
0
        }
479
0
        else if (strcasecmp(pszTypeName, "date") == 0)
480
0
        {
481
0
            col_def->target_type = SWQ_DATE;
482
0
        }
483
0
        else if (strcasecmp(pszTypeName, "time") == 0)
484
0
        {
485
0
            col_def->target_type = SWQ_TIME;
486
0
        }
487
0
        else if (strcasecmp(pszTypeName, "geometry") == 0)
488
0
        {
489
0
            col_def->target_type = SWQ_GEOMETRY;
490
0
        }
491
0
        else
492
0
        {
493
0
            CPLError(CE_Failure, CPLE_AppDefined,
494
0
                     "Unrecognized typename %s in CAST operator.", pszTypeName);
495
0
            CPLFree(col_def->table_name);
496
0
            col_def->table_name = nullptr;
497
0
            CPLFree(col_def->field_name);
498
0
            col_def->field_name = nullptr;
499
0
            CPLFree(col_def->field_alias);
500
0
            col_def->field_alias = nullptr;
501
0
            column_defs.pop_back();
502
0
            return FALSE;
503
0
        }
504
505
0
        if (col_def->target_type == SWQ_GEOMETRY)
506
0
        {
507
0
            if (poExpr->nSubExprCount > 2)
508
0
            {
509
0
                if (poExpr->papoSubExpr[2]->field_type != SWQ_STRING)
510
0
                {
511
0
                    CPLError(CE_Failure, CPLE_AppDefined,
512
0
                             "First argument of CAST operator should be "
513
0
                             "a geometry type identifier.");
514
0
                    CPLFree(col_def->table_name);
515
0
                    col_def->table_name = nullptr;
516
0
                    CPLFree(col_def->field_name);
517
0
                    col_def->field_name = nullptr;
518
0
                    CPLFree(col_def->field_alias);
519
0
                    col_def->field_alias = nullptr;
520
0
                    column_defs.pop_back();
521
0
                    return FALSE;
522
0
                }
523
524
0
                col_def->eGeomType =
525
0
                    OGRFromOGCGeomType(poExpr->papoSubExpr[2]->string_value);
526
527
                // SRID
528
0
                if (poExpr->nSubExprCount > 3)
529
0
                {
530
0
                    col_def->nSRID =
531
0
                        static_cast<int>(poExpr->papoSubExpr[3]->int_value);
532
0
                }
533
0
            }
534
0
        }
535
0
        else
536
0
        {
537
            // field width.
538
0
            if (poExpr->nSubExprCount > 2)
539
0
            {
540
0
                if (poExpr->papoSubExpr[2]->field_type != SWQ_INTEGER)
541
0
                {
542
0
                    CPLError(CE_Failure, CPLE_AppDefined,
543
0
                             "First argument of CAST operator should be of "
544
0
                             "integer type.");
545
0
                    CPLFree(col_def->table_name);
546
0
                    col_def->table_name = nullptr;
547
0
                    CPLFree(col_def->field_name);
548
0
                    col_def->field_name = nullptr;
549
0
                    CPLFree(col_def->field_alias);
550
0
                    col_def->field_alias = nullptr;
551
0
                    column_defs.pop_back();
552
0
                    return FALSE;
553
0
                }
554
0
                col_def->field_length =
555
0
                    static_cast<int>(poExpr->papoSubExpr[2]->int_value);
556
0
            }
557
558
            // field width.
559
0
            if (poExpr->nSubExprCount > 3 && parse_precision)
560
0
            {
561
0
                col_def->field_precision =
562
0
                    static_cast<int>(poExpr->papoSubExpr[3]->int_value);
563
0
                if (col_def->field_precision == 0)
564
0
                {
565
0
                    if (col_def->field_length < 10)
566
0
                        col_def->target_type = SWQ_INTEGER;
567
0
                    else if (col_def->field_length < 19)
568
0
                        col_def->target_type = SWQ_INTEGER64;
569
0
                }
570
0
            }
571
0
        }
572
0
    }
573
574
    /* -------------------------------------------------------------------- */
575
    /*      Do we have a special column function in play?                   */
576
    /* -------------------------------------------------------------------- */
577
0
    if (poExpr->eNodeType == SNT_OPERATION &&
578
0
        static_cast<swq_op>(poExpr->nOperation) >= SWQ_AGGREGATE_BEGIN &&
579
0
        static_cast<swq_op>(poExpr->nOperation) <= SWQ_AGGREGATE_END)
580
0
    {
581
0
        if (poExpr->nSubExprCount != 1)
582
0
        {
583
0
            const swq_operation *poOp = swq_op_registrar::GetOperator(
584
0
                static_cast<swq_op>(poExpr->nOperation));
585
0
            CPLError(CE_Failure, CPLE_AppDefined,
586
0
                     "Column Summary Function '%s' has "
587
0
                     "wrong number of arguments.",
588
0
                     poOp->pszName);
589
0
            CPLFree(col_def->table_name);
590
0
            col_def->table_name = nullptr;
591
0
            CPLFree(col_def->field_name);
592
0
            col_def->field_name = nullptr;
593
0
            CPLFree(col_def->field_alias);
594
0
            col_def->field_alias = nullptr;
595
0
            column_defs.pop_back();
596
0
            return FALSE;
597
0
        }
598
0
        else if (poExpr->papoSubExpr[0]->eNodeType != SNT_COLUMN)
599
0
        {
600
0
            const swq_operation *poOp = swq_op_registrar::GetOperator(
601
0
                static_cast<swq_op>(poExpr->nOperation));
602
0
            CPLError(CE_Failure, CPLE_AppDefined,
603
0
                     "Argument of column Summary Function '%s' "
604
0
                     "should be a column.",
605
0
                     poOp->pszName);
606
0
            CPLFree(col_def->table_name);
607
0
            col_def->table_name = nullptr;
608
0
            CPLFree(col_def->field_name);
609
0
            col_def->field_name = nullptr;
610
0
            CPLFree(col_def->field_alias);
611
0
            col_def->field_alias = nullptr;
612
0
            column_defs.pop_back();
613
0
            return FALSE;
614
0
        }
615
0
        else
616
0
        {
617
0
            col_def->col_func = static_cast<swq_col_func>(poExpr->nOperation);
618
619
0
            swq_expr_node *poSubExpr = poExpr->papoSubExpr[0];
620
621
0
            poExpr->papoSubExpr[0] = nullptr;
622
0
            poExpr->nSubExprCount = 0;
623
0
            delete poExpr;
624
625
0
            poExpr = poSubExpr;
626
0
        }
627
0
    }
628
629
0
    col_def->expr = poExpr;
630
631
0
    return TRUE;
632
0
}
633
634
int swq_select::PushExcludeField(swq_expr_node *poExpr)
635
0
{
636
0
    if (poExpr->eNodeType != SNT_COLUMN)
637
0
    {
638
0
        return FALSE;
639
0
    }
640
641
    // Check if this column has already been excluded
642
0
    for (const auto &col_def : m_exclude_fields[-1])
643
0
    {
644
0
        if (EQUAL(poExpr->string_value, col_def.field_name) &&
645
0
            EQUAL(poExpr->table_name ? poExpr->table_name : "",
646
0
                  col_def.table_name))
647
0
        {
648
0
            CPLError(CE_Failure, CPLE_AppDefined,
649
0
                     "Field %s.%s repeated in EXCEPT/EXCLUDE expression.",
650
0
                     col_def.table_name, col_def.field_name);
651
652
0
            return FALSE;
653
0
        }
654
0
    }
655
656
0
    m_exclude_fields[-1].emplace_back();
657
0
    swq_col_def *col_def = &m_exclude_fields[-1].back();
658
0
    memset(col_def, 0, sizeof(swq_col_def));
659
660
0
    col_def->table_name =
661
0
        CPLStrdup(poExpr->table_name ? poExpr->table_name : "");
662
0
    col_def->field_name = CPLStrdup(poExpr->string_value);
663
0
    col_def->table_index = -1;
664
0
    col_def->field_index = -1;
665
666
0
    delete poExpr;
667
668
0
    return TRUE;
669
0
}
670
671
/************************************************************************/
672
/*                            PushTableDef()                            */
673
/************************************************************************/
674
675
int swq_select::PushTableDef(const char *pszDataSource, const char *pszName,
676
                             const char *pszAlias)
677
678
0
{
679
0
    table_count++;
680
681
0
    table_defs = static_cast<swq_table_def *>(
682
0
        CPLRealloc(table_defs, sizeof(swq_table_def) * table_count));
683
684
0
    if (pszDataSource != nullptr)
685
0
        table_defs[table_count - 1].data_source = CPLStrdup(pszDataSource);
686
0
    else
687
0
        table_defs[table_count - 1].data_source = nullptr;
688
689
0
    table_defs[table_count - 1].table_name = CPLStrdup(pszName);
690
691
0
    if (pszAlias != nullptr)
692
0
        table_defs[table_count - 1].table_alias = CPLStrdup(pszAlias);
693
0
    else
694
0
        table_defs[table_count - 1].table_alias = CPLStrdup(pszName);
695
696
0
    return table_count - 1;
697
0
}
698
699
/************************************************************************/
700
/*                            PushOrderBy()                             */
701
/************************************************************************/
702
703
void swq_select::PushOrderBy(const char *pszTableName, const char *pszFieldName,
704
                             int bAscending)
705
706
0
{
707
0
    order_specs++;
708
0
    order_defs = static_cast<swq_order_def *>(
709
0
        CPLRealloc(order_defs, sizeof(swq_order_def) * order_specs));
710
711
0
    order_defs[order_specs - 1].table_name =
712
0
        CPLStrdup(pszTableName ? pszTableName : "");
713
0
    order_defs[order_specs - 1].field_name = CPLStrdup(pszFieldName);
714
0
    order_defs[order_specs - 1].table_index = -1;
715
0
    order_defs[order_specs - 1].field_index = -1;
716
0
    order_defs[order_specs - 1].ascending_flag = bAscending;
717
0
}
718
719
/************************************************************************/
720
/*                              PushJoin()                              */
721
/************************************************************************/
722
723
void swq_select::PushJoin(int iSecondaryTable, swq_expr_node *poExpr)
724
725
0
{
726
0
    join_count++;
727
0
    join_defs = static_cast<swq_join_def *>(
728
0
        CPLRealloc(join_defs, sizeof(swq_join_def) * join_count));
729
730
0
    join_defs[join_count - 1].secondary_table = iSecondaryTable;
731
0
    join_defs[join_count - 1].poExpr = poExpr;
732
0
}
733
734
/************************************************************************/
735
/*                             PushUnionAll()                           */
736
/************************************************************************/
737
738
void swq_select::PushUnionAll(swq_select *poOtherSelectIn)
739
0
{
740
0
    CPLAssert(poOtherSelect == nullptr);
741
0
    poOtherSelect = poOtherSelectIn;
742
0
}
743
744
/************************************************************************/
745
/*                             SetLimit()                               */
746
/************************************************************************/
747
748
void swq_select::SetLimit(GIntBig nLimit)
749
750
0
{
751
0
    limit = nLimit;
752
0
}
753
754
/************************************************************************/
755
/*                            SetOffset()                               */
756
/************************************************************************/
757
758
void swq_select::SetOffset(GIntBig nOffset)
759
760
0
{
761
0
    offset = nOffset;
762
0
}
763
764
/************************************************************************/
765
/*                          expand_wildcard()                           */
766
/*                                                                      */
767
/*      This function replaces the '*' in a "SELECT *" with the list    */
768
/*      provided list of fields.  It is used by swq_select::parse(),    */
769
/*      but may be called in advance by applications wanting the        */
770
/*      "default" field list to be different than the full list of      */
771
/*      fields.                                                         */
772
/************************************************************************/
773
774
CPLErr swq_select::expand_wildcard(swq_field_list *field_list,
775
                                   int bAlwaysPrefixWithTableName)
776
777
0
{
778
0
    int columns_added = 0;
779
780
    /* ==================================================================== */
781
    /*      Check each pre-expansion field.                                 */
782
    /* ==================================================================== */
783
0
    for (int isrc = 0; isrc < result_columns(); isrc++)
784
0
    {
785
0
        const char *src_tablename = column_defs[isrc].table_name;
786
0
        const char *src_fieldname = column_defs[isrc].field_name;
787
0
        int itable;
788
789
0
        if (*src_fieldname == '\0' ||
790
0
            src_fieldname[strlen(src_fieldname) - 1] != '*')
791
0
            continue;
792
793
        // Don't want to expand COUNT(*).
794
0
        if (column_defs[isrc].col_func == SWQCF_COUNT)
795
0
            continue;
796
797
        // Parse out the table name and verify it
798
0
        if (src_tablename[0] == 0 && strcmp(src_fieldname, "*") == 0)
799
0
        {
800
0
            itable = -1;
801
0
        }
802
0
        else
803
0
        {
804
0
            for (itable = 0; itable < field_list->table_count; itable++)
805
0
            {
806
0
                if (EQUAL(src_tablename,
807
0
                          field_list->table_defs[itable].table_alias))
808
0
                    break;
809
0
            }
810
811
0
            if (itable == field_list->table_count)
812
0
            {
813
0
                CPLError(CE_Failure, CPLE_AppDefined,
814
0
                         "Table %s not recognised from %s.%s definition.",
815
0
                         src_tablename, src_tablename, src_fieldname);
816
0
                return CE_Failure;
817
0
            }
818
0
        }
819
820
        // Assign the selected fields. */
821
0
        std::vector<swq_col_def> expanded_columns;
822
0
        for (int i = 0; i < field_list->count; i++)
823
0
        {
824
0
            bool compose = (itable != -1) || bAlwaysPrefixWithTableName;
825
826
            // Skip this field if it isn't in the target table.
827
0
            if (itable != -1 && itable != field_list->table_ids[i])
828
0
                continue;
829
830
0
            auto table_id = field_list->table_ids[i];
831
832
            // Skip this field if we've excluded it with SELECT * EXCEPT ()
833
0
            if (IsFieldExcluded(isrc - columns_added,
834
0
                                field_list->table_defs[table_id].table_name,
835
0
                                field_list->names[i]))
836
0
            {
837
0
                if (field_list->types[i] == SWQ_GEOMETRY)
838
0
                {
839
                    // Need to store the fact that we explicitly excluded
840
                    // the geometry so we can prevent it from being implicitly
841
                    // included by OGRGenSQLResultsLayer
842
0
                    bExcludedGeometry = true;
843
0
                }
844
845
0
                continue;
846
0
            }
847
848
            // Set up some default values.
849
0
            expanded_columns.emplace_back();
850
0
            swq_col_def *def = &expanded_columns.back();
851
0
            def->field_precision = -1;
852
0
            def->target_type = SWQ_OTHER;
853
0
            def->target_subtype = OFSTNone;
854
855
            // Does this field duplicate an earlier one?
856
0
            if (field_list->table_ids[i] != 0 && !compose)
857
0
            {
858
0
                for (int other = 0; other < i; other++)
859
0
                {
860
0
                    if (EQUAL(field_list->names[i], field_list->names[other]))
861
0
                    {
862
0
                        compose = true;
863
0
                        break;
864
0
                    }
865
0
                }
866
0
            }
867
868
0
            int field_itable = field_list->table_ids[i];
869
0
            const char *field_name = field_list->names[i];
870
0
            const char *table_alias =
871
0
                field_list->table_defs[field_itable].table_alias;
872
873
0
            def->table_name = CPLStrdup(table_alias);
874
0
            def->field_name = CPLStrdup(field_name);
875
0
            if (!compose)
876
0
                def->field_alias = CPLStrdup(field_list->names[i]);
877
878
            // All the other table info will be provided by the later
879
            // parse operation.
880
0
        }
881
882
        // Splice expanded_columns in at the position of '*'
883
0
        CPLFree(column_defs[isrc].table_name);
884
0
        CPLFree(column_defs[isrc].field_name);
885
0
        CPLFree(column_defs[isrc].field_alias);
886
0
        delete column_defs[isrc].expr;
887
0
        auto pos = column_defs.erase(std::next(column_defs.begin(), isrc));
888
889
0
        column_defs.insert(pos, expanded_columns.begin(),
890
0
                           expanded_columns.end());
891
892
0
        columns_added += static_cast<int>(expanded_columns.size()) - 1;
893
894
0
        const auto it = m_exclude_fields.find(isrc);
895
0
        if (it != m_exclude_fields.end())
896
0
        {
897
0
            if (!it->second.empty())
898
0
            {
899
0
                const auto &field = it->second.front();
900
0
                CPLError(
901
0
                    CE_Failure, CPLE_AppDefined,
902
0
                    "Field %s specified in EXCEPT/EXCLUDE expression not found",
903
0
                    field.field_name);
904
0
                return CE_Failure;
905
0
            }
906
0
        }
907
0
    }
908
909
0
    return CE_None;
910
0
}
911
912
/************************************************************************/
913
/*                       CheckCompatibleJoinExpr()                      */
914
/************************************************************************/
915
916
static bool CheckCompatibleJoinExpr(swq_expr_node *poExpr, int secondary_table,
917
                                    swq_field_list *field_list)
918
0
{
919
0
    if (poExpr->eNodeType == SNT_CONSTANT)
920
0
        return true;
921
922
0
    if (poExpr->eNodeType == SNT_COLUMN)
923
0
    {
924
0
        CPLAssert(poExpr->field_index != -1);
925
0
        CPLAssert(poExpr->table_index != -1);
926
0
        if (poExpr->table_index != 0 && poExpr->table_index != secondary_table)
927
0
        {
928
0
            if (poExpr->table_name)
929
0
                CPLError(CE_Failure, CPLE_AppDefined,
930
0
                         "Field %s.%s in JOIN clause does not correspond to "
931
0
                         "the primary table nor the joint (secondary) table.",
932
0
                         poExpr->table_name, poExpr->string_value);
933
0
            else
934
0
                CPLError(CE_Failure, CPLE_AppDefined,
935
0
                         "Field %s in JOIN clause does not correspond to the "
936
0
                         "primary table nor the joint (secondary) table.",
937
0
                         poExpr->string_value);
938
0
            return false;
939
0
        }
940
941
0
        return true;
942
0
    }
943
944
0
    if (poExpr->eNodeType == SNT_OPERATION)
945
0
    {
946
0
        for (int i = 0; i < poExpr->nSubExprCount; i++)
947
0
        {
948
0
            if (!CheckCompatibleJoinExpr(poExpr->papoSubExpr[i],
949
0
                                         secondary_table, field_list))
950
0
                return false;
951
0
        }
952
0
        return true;
953
0
    }
954
955
0
    return false;
956
0
}
957
958
/************************************************************************/
959
/*                               parse()                                */
960
/*                                                                      */
961
/*      This method really does post-parse processing.                  */
962
/************************************************************************/
963
964
CPLErr swq_select::parse(swq_field_list *field_list,
965
                         swq_select_parse_options *poParseOptions)
966
0
{
967
0
    int bAlwaysPrefixWithTableName =
968
0
        poParseOptions && poParseOptions->bAlwaysPrefixWithTableName;
969
0
    CPLErr eError = expand_wildcard(field_list, bAlwaysPrefixWithTableName);
970
0
    if (eError != CE_None)
971
0
        return eError;
972
973
0
    swq_custom_func_registrar *poCustomFuncRegistrar = nullptr;
974
0
    if (poParseOptions != nullptr)
975
0
        poCustomFuncRegistrar = poParseOptions->poCustomFuncRegistrar;
976
977
    /* -------------------------------------------------------------------- */
978
    /*      Identify field information.                                     */
979
    /* -------------------------------------------------------------------- */
980
0
    for (int i = 0; i < result_columns(); i++)
981
0
    {
982
0
        swq_col_def *def = &column_defs[i];
983
984
0
        if (def->expr != nullptr && def->expr->eNodeType != SNT_COLUMN)
985
0
        {
986
0
            def->field_index = -1;
987
0
            def->table_index = -1;
988
989
0
            if (def->expr->Check(field_list, TRUE, FALSE,
990
0
                                 poCustomFuncRegistrar) == SWQ_ERROR)
991
0
                return CE_Failure;
992
993
0
            def->field_type = def->expr->field_type;
994
0
        }
995
0
        else
996
0
        {
997
0
            swq_field_type this_type;
998
999
            // Identify field.
1000
0
            def->field_index =
1001
0
                swq_identify_field(def->table_name, def->field_name, field_list,
1002
0
                                   &this_type, &(def->table_index));
1003
1004
            // Record field type.
1005
0
            def->field_type = this_type;
1006
1007
0
            if (def->field_index == -1 && !(def->col_func == SWQCF_COUNT &&
1008
0
                                            strcmp(def->field_name, "*") == 0))
1009
0
            {
1010
0
                CPLError(
1011
0
                    CE_Failure, CPLE_AppDefined, "Unrecognized field name %s.",
1012
0
                    def->table_name[0]
1013
0
                        ? CPLSPrintf("%s.%s", def->table_name, def->field_name)
1014
0
                        : def->field_name);
1015
0
                return CE_Failure;
1016
0
            }
1017
0
        }
1018
1019
        // Identify column function if present.
1020
0
        if (def->col_func != SWQCF_NONE && def->col_func != SWQCF_CUSTOM &&
1021
0
            def->col_func != SWQCF_COUNT &&
1022
0
            (def->field_type == SWQ_GEOMETRY ||
1023
0
             (def->field_type == SWQ_STRING && def->col_func != SWQCF_MIN &&
1024
0
              def->col_func != SWQCF_MAX)))
1025
0
        {
1026
            // Possibly this is already enforced by the checker?
1027
0
            const swq_operation *op = swq_op_registrar::GetOperator(
1028
0
                static_cast<swq_op>(def->col_func));
1029
0
            CPLError(CE_Failure, CPLE_AppDefined,
1030
0
                     "Use of field function %s() on %s field %s illegal.",
1031
0
                     op->pszName, SWQFieldTypeToString(def->field_type),
1032
0
                     def->field_name);
1033
0
            return CE_Failure;
1034
0
        }
1035
0
    }
1036
1037
    /* -------------------------------------------------------------------- */
1038
    /*      Check if we are producing a one row summary result or a set     */
1039
    /*      of records.  Generate an error if we get conflicting            */
1040
    /*      indications.                                                    */
1041
    /* -------------------------------------------------------------------- */
1042
1043
0
    int bAllowDistinctOnMultipleFields =
1044
0
        (poParseOptions && poParseOptions->bAllowDistinctOnMultipleFields);
1045
0
    if (query_mode == SWQM_DISTINCT_LIST && result_columns() > 1 &&
1046
0
        !bAllowDistinctOnMultipleFields)
1047
0
    {
1048
0
        CPLError(CE_Failure, CPLE_NotSupported,
1049
0
                 "SELECT DISTINCT not supported on multiple columns.");
1050
0
        return CE_Failure;
1051
0
    }
1052
1053
0
    for (int i = 0; i < result_columns(); i++)
1054
0
    {
1055
0
        swq_col_def *def = &column_defs[i];
1056
0
        int this_indicator = -1;
1057
1058
0
        if (query_mode == SWQM_DISTINCT_LIST && def->field_type == SWQ_GEOMETRY)
1059
0
        {
1060
0
            const bool bAllowDistinctOnGeometryField =
1061
0
                poParseOptions && poParseOptions->bAllowDistinctOnGeometryField;
1062
0
            if (!bAllowDistinctOnGeometryField)
1063
0
            {
1064
0
                CPLError(CE_Failure, CPLE_NotSupported,
1065
0
                         "SELECT DISTINCT on a geometry not supported.");
1066
0
                return CE_Failure;
1067
0
            }
1068
0
        }
1069
1070
0
        if (def->col_func == SWQCF_NONE)
1071
0
        {
1072
0
            if (query_mode == SWQM_DISTINCT_LIST)
1073
0
            {
1074
0
                def->distinct_flag = TRUE;
1075
0
                this_indicator = SWQM_DISTINCT_LIST;
1076
0
            }
1077
0
            else
1078
0
                this_indicator = SWQM_RECORDSET;
1079
0
        }
1080
0
        else if (def->col_func != SWQCF_CUSTOM)
1081
0
        {
1082
0
            this_indicator = SWQM_SUMMARY_RECORD;
1083
0
            if (def->col_func == SWQCF_COUNT && def->distinct_flag &&
1084
0
                def->field_type == SWQ_GEOMETRY)
1085
0
            {
1086
0
                CPLError(CE_Failure, CPLE_AppDefined,
1087
0
                         "SELECT COUNT DISTINCT on a geometry not supported.");
1088
0
                return CE_Failure;
1089
0
            }
1090
0
        }
1091
1092
0
        if (this_indicator != query_mode && this_indicator != -1 &&
1093
0
            query_mode != 0)
1094
0
        {
1095
0
            CPLError(CE_Failure, CPLE_AppDefined,
1096
0
                     "Field list implies mixture of regular recordset mode, "
1097
0
                     "summary mode or distinct field list mode.");
1098
0
            return CE_Failure;
1099
0
        }
1100
1101
0
        if (this_indicator != -1)
1102
0
            query_mode = this_indicator;
1103
0
    }
1104
1105
0
    if (result_columns() == 0)
1106
0
    {
1107
0
        query_mode = SWQM_RECORDSET;
1108
0
    }
1109
1110
    /* -------------------------------------------------------------------- */
1111
    /*      Process column names in JOIN specs.                             */
1112
    /* -------------------------------------------------------------------- */
1113
0
    for (int i = 0; i < join_count; i++)
1114
0
    {
1115
0
        swq_join_def *def = join_defs + i;
1116
0
        if (def->poExpr->Check(field_list, TRUE, TRUE, poCustomFuncRegistrar) ==
1117
0
            SWQ_ERROR)
1118
0
            return CE_Failure;
1119
0
        if (!CheckCompatibleJoinExpr(def->poExpr, def->secondary_table,
1120
0
                                     field_list))
1121
0
            return CE_Failure;
1122
0
    }
1123
1124
    /* -------------------------------------------------------------------- */
1125
    /*      Process column names in order specs.                            */
1126
    /* -------------------------------------------------------------------- */
1127
0
    for (int i = 0; i < order_specs; i++)
1128
0
    {
1129
0
        swq_order_def *def = order_defs + i;
1130
1131
        // Identify field.
1132
0
        swq_field_type field_type;
1133
0
        def->field_index =
1134
0
            swq_identify_field(def->table_name, def->field_name, field_list,
1135
0
                               &field_type, &(def->table_index));
1136
0
        if (def->field_index == -1)
1137
0
        {
1138
0
            CPLError(CE_Failure, CPLE_AppDefined,
1139
0
                     "Unrecognized field name %s in ORDER BY.",
1140
0
                     def->table_name[0]
1141
0
                         ? CPLSPrintf("%s.%s", def->table_name, def->field_name)
1142
0
                         : def->field_name);
1143
0
            return CE_Failure;
1144
0
        }
1145
1146
0
        if (def->table_index != 0)
1147
0
        {
1148
0
            CPLError(CE_Failure, CPLE_AppDefined,
1149
0
                     "Cannot use field '%s' of a secondary table in "
1150
0
                     "an ORDER BY clause",
1151
0
                     def->field_name);
1152
0
            return CE_Failure;
1153
0
        }
1154
1155
0
        if (field_type == SWQ_GEOMETRY)
1156
0
        {
1157
0
            CPLError(CE_Failure, CPLE_AppDefined,
1158
0
                     "Cannot use geometry field '%s' in an ORDER BY clause",
1159
0
                     def->field_name);
1160
0
            return CE_Failure;
1161
0
        }
1162
0
    }
1163
1164
    /* -------------------------------------------------------------------- */
1165
    /*      Post process the where clause, subbing in field indexes and     */
1166
    /*      doing final validation.                                         */
1167
    /* -------------------------------------------------------------------- */
1168
0
    int bAllowFieldsInSecondaryTablesInWhere = FALSE;
1169
0
    if (poParseOptions != nullptr)
1170
0
        bAllowFieldsInSecondaryTablesInWhere =
1171
0
            poParseOptions->bAllowFieldsInSecondaryTablesInWhere;
1172
0
    if (where_expr != nullptr &&
1173
0
        where_expr->Check(field_list, bAllowFieldsInSecondaryTablesInWhere,
1174
0
                          FALSE, poCustomFuncRegistrar) == SWQ_ERROR)
1175
0
    {
1176
0
        return CE_Failure;
1177
0
    }
1178
1179
0
    return CE_None;
1180
0
}
1181
1182
bool swq_select::IsFieldExcluded(int src_index, const char *pszTableName,
1183
                                 const char *pszFieldName)
1184
0
{
1185
0
    const auto list_it = m_exclude_fields.find(src_index);
1186
1187
0
    if (list_it == m_exclude_fields.end())
1188
0
    {
1189
0
        return false;
1190
0
    }
1191
1192
0
    auto &excluded_fields = list_it->second;
1193
1194
0
    auto it = std::partition(
1195
0
        excluded_fields.begin(), excluded_fields.end(),
1196
0
        [pszTableName, pszFieldName](const swq_col_def &exclude_field)
1197
0
        {
1198
0
            if (!(EQUAL(exclude_field.table_name, "") ||
1199
0
                  EQUAL(pszTableName, exclude_field.table_name)))
1200
0
            {
1201
0
                return true;
1202
0
            }
1203
1204
0
            return !EQUAL(pszFieldName, exclude_field.field_name);
1205
0
        });
1206
1207
0
    if (it != excluded_fields.end())
1208
0
    {
1209
0
        CPLFree(it->table_name);
1210
0
        CPLFree(it->field_name);
1211
0
        CPLFree(it->field_alias);
1212
1213
0
        delete it->expr;
1214
1215
0
        excluded_fields.erase(it);
1216
0
        return true;
1217
0
    }
1218
1219
0
    return false;
1220
0
}
1221
1222
//! @endcond