/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 |