mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-02 06:12:41 -06:00
1235 lines
45 KiB
C++
1235 lines
45 KiB
C++
/******************************************************************************
|
|
*
|
|
* Component: OGR SQL Engine
|
|
* Purpose: swq_select class implementation.
|
|
* Author: Frank Warmerdam <warmerdam@pobox.com>
|
|
*
|
|
******************************************************************************
|
|
* Copyright (C) 2010 Frank Warmerdam <warmerdam@pobox.com>
|
|
* Copyright (c) 2010-2014, Even Rouault <even dot rouault at mines-paris dot org>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "swq.h"
|
|
#include "swq_parser.hpp"
|
|
#include "ogr_geometry.h"
|
|
|
|
/************************************************************************/
|
|
/* swq_select() */
|
|
/************************************************************************/
|
|
|
|
swq_select::swq_select()
|
|
|
|
{
|
|
query_mode = 0;
|
|
raw_select = NULL;
|
|
|
|
result_columns = 0;
|
|
column_defs = NULL;
|
|
column_summary = NULL;
|
|
|
|
table_count = 0;
|
|
table_defs = NULL;
|
|
|
|
join_count = 0;
|
|
join_defs = NULL;
|
|
|
|
where_expr = NULL;
|
|
|
|
order_specs = 0;
|
|
order_defs = NULL;
|
|
|
|
poOtherSelect = NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~swq_select() */
|
|
/************************************************************************/
|
|
|
|
swq_select::~swq_select()
|
|
|
|
{
|
|
int i;
|
|
|
|
delete where_expr;
|
|
CPLFree( raw_select );
|
|
|
|
for( i = 0; i < table_count; i++ )
|
|
{
|
|
swq_table_def *table_def = table_defs + i;
|
|
|
|
CPLFree( table_def->data_source );
|
|
CPLFree( table_def->table_name );
|
|
CPLFree( table_def->table_alias );
|
|
}
|
|
if( table_defs != NULL )
|
|
CPLFree( table_defs );
|
|
|
|
for( i = 0; i < result_columns; i++ )
|
|
{
|
|
CPLFree( column_defs[i].table_name );
|
|
CPLFree( column_defs[i].field_name );
|
|
CPLFree( column_defs[i].field_alias );
|
|
|
|
delete column_defs[i].expr;
|
|
|
|
if( column_summary != NULL
|
|
&& column_summary[i].distinct_list != NULL )
|
|
{
|
|
int j;
|
|
|
|
for( j = 0; j < column_summary[i].count; j++ )
|
|
CPLFree( column_summary[i].distinct_list[j] );
|
|
|
|
CPLFree( column_summary[i].distinct_list );
|
|
}
|
|
}
|
|
|
|
CPLFree( column_defs );
|
|
|
|
CPLFree( column_summary );
|
|
|
|
for( i = 0; i < order_specs; i++ )
|
|
{
|
|
CPLFree( order_defs[i].table_name );
|
|
CPLFree( order_defs[i].field_name );
|
|
}
|
|
|
|
CPLFree( order_defs );
|
|
|
|
for( i = 0; i < join_count; i++ )
|
|
{
|
|
delete join_defs[i].poExpr;
|
|
}
|
|
CPLFree( join_defs );
|
|
|
|
delete poOtherSelect;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* preparse() */
|
|
/* */
|
|
/* Parse the expression but without knowing the available */
|
|
/* tables and fields. */
|
|
/************************************************************************/
|
|
|
|
CPLErr swq_select::preparse( const char *select_statement,
|
|
int bAcceptCustomFuncs )
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Prepare a parser context. */
|
|
/* -------------------------------------------------------------------- */
|
|
swq_parse_context context;
|
|
|
|
context.pszInput = select_statement;
|
|
context.pszNext = select_statement;
|
|
context.pszLastValid = select_statement;
|
|
context.nStartToken = SWQT_SELECT_START;
|
|
context.bAcceptCustomFuncs = bAcceptCustomFuncs;
|
|
context.poCurSelect = this;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do the parse. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( swqparse( &context ) != 0 )
|
|
{
|
|
delete context.poRoot;
|
|
return CE_Failure;
|
|
}
|
|
|
|
postpreparse();
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* postpreparse() */
|
|
/************************************************************************/
|
|
|
|
void swq_select::postpreparse()
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Reorder the joins in the order they appear in the SQL string. */
|
|
/* -------------------------------------------------------------------- */
|
|
int i;
|
|
for(i = 0; i < join_count / 2; i++)
|
|
{
|
|
swq_join_def sTmp;
|
|
memcpy(&sTmp, &join_defs[i], sizeof(swq_join_def));
|
|
memcpy(&join_defs[i], &join_defs[join_count - 1 - i], sizeof(swq_join_def));
|
|
memcpy(&join_defs[join_count - 1 - i], &sTmp, sizeof(swq_join_def));
|
|
}
|
|
|
|
/* We make that strong assumption in ogr_gensql */
|
|
for(i = 0; i < join_count; i++)
|
|
{
|
|
CPLAssert(join_defs[i].secondary_table == i + 1);
|
|
}
|
|
|
|
if( poOtherSelect != NULL)
|
|
poOtherSelect->postpreparse();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Dump() */
|
|
/************************************************************************/
|
|
|
|
void swq_select::Dump( FILE *fp )
|
|
|
|
{
|
|
int i;
|
|
|
|
fprintf( fp, "SELECT Statement:\n" );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* query mode. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( query_mode == SWQM_SUMMARY_RECORD )
|
|
fprintf( fp, " QUERY MODE: SUMMARY RECORD\n" );
|
|
else if( query_mode == SWQM_RECORDSET )
|
|
fprintf( fp, " QUERY MODE: RECORDSET\n" );
|
|
else if( query_mode == SWQM_DISTINCT_LIST )
|
|
fprintf( fp, " QUERY MODE: DISTINCT LIST\n" );
|
|
else
|
|
fprintf( fp, " QUERY MODE: %d/unknown\n", query_mode );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* column_defs */
|
|
/* -------------------------------------------------------------------- */
|
|
fprintf( fp, " Result Columns:\n" );
|
|
for( i = 0; i < result_columns; i++ )
|
|
{
|
|
swq_col_def *def = column_defs + i;
|
|
|
|
fprintf( fp, " Table name: %s\n", def->table_name );
|
|
fprintf( fp, " Name: %s\n", def->field_name );
|
|
|
|
if( def->field_alias )
|
|
fprintf( fp, " Alias: %s\n", def->field_alias );
|
|
|
|
if( def->col_func == SWQCF_NONE )
|
|
/* nothing */;
|
|
else if( def->col_func == SWQCF_AVG )
|
|
fprintf( fp, " Function: AVG\n" );
|
|
else if( def->col_func == SWQCF_MIN )
|
|
fprintf( fp, " Function: MIN\n" );
|
|
else if( def->col_func == SWQCF_MAX )
|
|
fprintf( fp, " Function: MAX\n" );
|
|
else if( def->col_func == SWQCF_COUNT )
|
|
fprintf( fp, " Function: COUNT\n" );
|
|
else if( def->col_func == SWQCF_SUM )
|
|
fprintf( fp, " Function: SUM\n" );
|
|
else if( def->col_func == SWQCF_CUSTOM )
|
|
fprintf( fp, " Function: CUSTOM\n" );
|
|
else
|
|
fprintf( fp, " Function: UNKNOWN!\n" );
|
|
|
|
if( def->distinct_flag )
|
|
fprintf( fp, " DISTINCT flag set\n" );
|
|
|
|
fprintf( fp, " Field Index: %d, Table Index: %d\n",
|
|
def->field_index, def->table_index );
|
|
|
|
fprintf( fp, " Field Type: %d\n", def->field_type );
|
|
fprintf( fp, " Target Type: %d\n", def->target_type );
|
|
fprintf( fp, " Target SubType: %d\n", def->target_subtype );
|
|
fprintf( fp, " Length: %d, Precision: %d\n",
|
|
def->field_length, def->field_precision );
|
|
|
|
if( def->expr != NULL )
|
|
{
|
|
fprintf( fp, " Expression:\n" );
|
|
def->expr->Dump( fp, 3 );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* table_defs */
|
|
/* -------------------------------------------------------------------- */
|
|
fprintf( fp, " Table Defs: %d\n", table_count );
|
|
for( i = 0; i < table_count; i++ )
|
|
{
|
|
fprintf( fp, " datasource=%s, table_name=%s, table_alias=%s\n",
|
|
table_defs[i].data_source,
|
|
table_defs[i].table_name,
|
|
table_defs[i].table_alias );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* join_defs */
|
|
/* -------------------------------------------------------------------- */
|
|
if( join_count > 0 )
|
|
fprintf( fp, " joins:\n" );
|
|
|
|
for( i = 0; i < join_count; i++ )
|
|
{
|
|
fprintf( fp, " %d:\n", i );
|
|
join_defs[i].poExpr->Dump( fp, 4 );
|
|
fprintf( fp, " Secondary Table: %d\n",
|
|
join_defs[i].secondary_table );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Where clause. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( where_expr != NULL )
|
|
{
|
|
fprintf( fp, " WHERE:\n" );
|
|
where_expr->Dump( fp, 2 );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Order by */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
for( i = 0; i < order_specs; i++ )
|
|
{
|
|
fprintf( fp, " ORDER BY: %s (%d/%d)",
|
|
order_defs[i].field_name,
|
|
order_defs[i].table_index,
|
|
order_defs[i].field_index );
|
|
if( order_defs[i].ascending_flag )
|
|
fprintf( fp, " ASC\n" );
|
|
else
|
|
fprintf( fp, " DESC\n" );
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Unparse() */
|
|
/************************************************************************/
|
|
|
|
char* swq_select::Unparse()
|
|
{
|
|
int i;
|
|
CPLString osSelect("SELECT ");
|
|
if( query_mode == SWQM_DISTINCT_LIST )
|
|
osSelect += "DISTINCT ";
|
|
|
|
for( i = 0; i < result_columns; i++ )
|
|
{
|
|
swq_col_def *def = column_defs + i;
|
|
|
|
if( i > 0 )
|
|
osSelect += ", ";
|
|
|
|
if( def->expr != NULL && def->col_func == SWQCF_NONE )
|
|
{
|
|
char* pszTmp = def->expr->Unparse(NULL, '"');
|
|
osSelect += pszTmp;
|
|
CPLFree(pszTmp);
|
|
}
|
|
else
|
|
{
|
|
if( def->col_func == SWQCF_AVG )
|
|
osSelect += "AVG(";
|
|
else if( def->col_func == SWQCF_MIN )
|
|
osSelect += "MIN(";
|
|
else if( def->col_func == SWQCF_MAX )
|
|
osSelect += "MAX(";
|
|
else if( def->col_func == SWQCF_COUNT )
|
|
osSelect += "COUNT(";
|
|
else if( def->col_func == SWQCF_SUM )
|
|
osSelect += "SUM(";
|
|
|
|
if( def->distinct_flag && def->col_func == SWQCF_COUNT )
|
|
osSelect += "DISTINCT ";
|
|
|
|
if( (def->field_alias == NULL || table_count > 1) &&
|
|
def->table_name != NULL && def->table_name[0] != '\0' )
|
|
{
|
|
osSelect += swq_expr_node::QuoteIfNecessary(def->table_name, '"');
|
|
osSelect += ".";
|
|
}
|
|
osSelect += swq_expr_node::QuoteIfNecessary(def->field_name, '"');
|
|
}
|
|
|
|
if( def->field_alias != NULL &&
|
|
strcmp(def->field_name, def->field_alias) != 0 )
|
|
{
|
|
osSelect += " AS ";
|
|
osSelect += swq_expr_node::QuoteIfNecessary(def->field_alias, '"');
|
|
}
|
|
|
|
if( def->col_func != SWQCF_NONE )
|
|
osSelect += ")";
|
|
}
|
|
|
|
osSelect += " FROM ";
|
|
if( table_defs[0].data_source != NULL )
|
|
{
|
|
osSelect += "'";
|
|
osSelect += table_defs[0].data_source;
|
|
osSelect += "'.";
|
|
}
|
|
osSelect += swq_expr_node::QuoteIfNecessary(table_defs[0].table_name, '"');
|
|
if( table_defs[0].table_alias != NULL &&
|
|
strcmp(table_defs[0].table_name, table_defs[0].table_alias) != 0 )
|
|
{
|
|
osSelect += " AS ";
|
|
osSelect += swq_expr_node::QuoteIfNecessary(table_defs[0].table_alias, '"');
|
|
}
|
|
|
|
for( i = 0; i < join_count; i++ )
|
|
{
|
|
int iTable = join_defs[i].secondary_table;
|
|
osSelect += " JOIN ";
|
|
if( table_defs[iTable].data_source != NULL )
|
|
{
|
|
osSelect += "'";
|
|
osSelect += table_defs[iTable].data_source;
|
|
osSelect += "'.";
|
|
}
|
|
osSelect += swq_expr_node::QuoteIfNecessary(table_defs[iTable].table_name, '"');
|
|
if( table_defs[iTable].table_alias != NULL &&
|
|
strcmp(table_defs[iTable].table_name, table_defs[iTable].table_alias) != 0 )
|
|
{
|
|
osSelect += " AS ";
|
|
osSelect += swq_expr_node::QuoteIfNecessary(table_defs[iTable].table_alias, '"');
|
|
}
|
|
osSelect += " ON ";
|
|
char* pszTmp = join_defs[i].poExpr->Unparse(NULL, '"');
|
|
osSelect += pszTmp;
|
|
CPLFree(pszTmp);
|
|
}
|
|
|
|
if( where_expr != NULL )
|
|
{
|
|
osSelect += " WHERE ";
|
|
char* pszTmp = where_expr->Unparse(NULL, '"');
|
|
osSelect += pszTmp;
|
|
CPLFree(pszTmp);
|
|
}
|
|
|
|
for( i = 0; i < order_specs; i++ )
|
|
{
|
|
osSelect += " ORDER BY ";
|
|
osSelect += swq_expr_node::QuoteIfNecessary(order_defs[i].field_name, '"');
|
|
if( !order_defs[i].ascending_flag )
|
|
osSelect += " DESC";
|
|
}
|
|
|
|
return CPLStrdup(osSelect);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PushField() */
|
|
/* */
|
|
/* Create a new field definition by name and possibly alias. */
|
|
/************************************************************************/
|
|
|
|
int swq_select::PushField( swq_expr_node *poExpr, const char *pszAlias,
|
|
int distinct_flag )
|
|
|
|
{
|
|
if( query_mode == SWQM_DISTINCT_LIST && distinct_flag )
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported, "SELECT DISTINCT and COUNT(DISTINCT...) not supported together");
|
|
return FALSE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Grow the array. */
|
|
/* -------------------------------------------------------------------- */
|
|
result_columns++;
|
|
|
|
column_defs = (swq_col_def *)
|
|
CPLRealloc( column_defs, sizeof(swq_col_def) * result_columns );
|
|
|
|
swq_col_def *col_def = column_defs + result_columns - 1;
|
|
|
|
memset( col_def, 0, sizeof(swq_col_def) );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try to capture a field name. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poExpr->eNodeType == SNT_COLUMN )
|
|
{
|
|
col_def->table_name =
|
|
CPLStrdup(poExpr->table_name ? poExpr->table_name : "");
|
|
col_def->field_name =
|
|
CPLStrdup(poExpr->string_value);
|
|
}
|
|
else if( poExpr->eNodeType == SNT_OPERATION
|
|
&& (poExpr->nOperation == SWQ_CAST ||
|
|
(poExpr->nOperation >= SWQ_AVG &&
|
|
poExpr->nOperation <= SWQ_SUM))
|
|
&& poExpr->nSubExprCount >= 1
|
|
&& poExpr->papoSubExpr[0]->eNodeType == SNT_COLUMN )
|
|
{
|
|
col_def->table_name =
|
|
CPLStrdup(poExpr->papoSubExpr[0]->table_name ?
|
|
poExpr->papoSubExpr[0]->table_name : "");
|
|
col_def->field_name =
|
|
CPLStrdup(poExpr->papoSubExpr[0]->string_value);
|
|
}
|
|
else
|
|
{
|
|
col_def->table_name = CPLStrdup("");
|
|
col_def->field_name = CPLStrdup("");
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Initialize fields. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pszAlias != NULL )
|
|
col_def->field_alias = CPLStrdup( pszAlias );
|
|
else if( pszAlias == NULL && poExpr->eNodeType == SNT_OPERATION
|
|
&& poExpr->nSubExprCount >= 1
|
|
&& ( poExpr->nOperation == SWQ_CONCAT ||
|
|
poExpr->nOperation == SWQ_SUBSTR )
|
|
&& poExpr->papoSubExpr[0]->eNodeType == SNT_COLUMN )
|
|
{
|
|
const swq_operation *op = swq_op_registrar::GetOperator(
|
|
(swq_op) poExpr->nOperation );
|
|
|
|
col_def->field_alias = CPLStrdup( CPLSPrintf("%s_%s", op->pszName,
|
|
poExpr->papoSubExpr[0]->string_value));
|
|
}
|
|
|
|
col_def->table_index = -1;
|
|
col_def->field_index = -1;
|
|
col_def->field_type = SWQ_OTHER;
|
|
col_def->field_precision = -1;
|
|
col_def->target_type = SWQ_OTHER;
|
|
col_def->target_subtype = OFSTNone;
|
|
col_def->col_func = SWQCF_NONE;
|
|
col_def->distinct_flag = distinct_flag;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we have a CAST operator in play? */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poExpr->eNodeType == SNT_OPERATION
|
|
&& poExpr->nOperation == SWQ_CAST )
|
|
{
|
|
const char *pszTypeName = poExpr->papoSubExpr[1]->string_value;
|
|
int parse_precision = 0;
|
|
|
|
if( EQUAL(pszTypeName,"character") )
|
|
{
|
|
col_def->target_type = SWQ_STRING;
|
|
col_def->field_length = 1;
|
|
}
|
|
else if( strcasecmp(pszTypeName,"boolean") == 0 )
|
|
{
|
|
col_def->target_type = SWQ_BOOLEAN;
|
|
}
|
|
else if( strcasecmp(pszTypeName,"integer") == 0 )
|
|
{
|
|
col_def->target_type = SWQ_INTEGER;
|
|
}
|
|
else if( strcasecmp(pszTypeName,"bigint") == 0 )
|
|
{
|
|
col_def->target_type = SWQ_INTEGER64;
|
|
}
|
|
else if( strcasecmp(pszTypeName,"smallint") == 0 )
|
|
{
|
|
col_def->target_type = SWQ_INTEGER;
|
|
col_def->target_subtype = OFSTInt16;
|
|
}
|
|
else if( strcasecmp(pszTypeName,"float") == 0 )
|
|
{
|
|
col_def->target_type = SWQ_FLOAT;
|
|
}
|
|
else if( strcasecmp(pszTypeName,"numeric") == 0 )
|
|
{
|
|
col_def->target_type = SWQ_FLOAT;
|
|
parse_precision = 1;
|
|
}
|
|
else if( strcasecmp(pszTypeName,"timestamp") == 0 )
|
|
{
|
|
col_def->target_type = SWQ_TIMESTAMP;
|
|
}
|
|
else if( strcasecmp(pszTypeName,"date") == 0 )
|
|
{
|
|
col_def->target_type = SWQ_DATE;
|
|
}
|
|
else if( strcasecmp(pszTypeName,"time") == 0 )
|
|
{
|
|
col_def->target_type = SWQ_TIME;
|
|
}
|
|
else if( strcasecmp(pszTypeName,"geometry") == 0 )
|
|
{
|
|
col_def->target_type = SWQ_GEOMETRY;
|
|
}
|
|
else
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Unrecognized typename %s in CAST operator.",
|
|
pszTypeName );
|
|
CPLFree(col_def->table_name);
|
|
col_def->table_name = NULL;
|
|
CPLFree(col_def->field_name);
|
|
col_def->field_name = NULL;
|
|
CPLFree(col_def->field_alias);
|
|
col_def->field_alias = NULL;
|
|
result_columns--;
|
|
return FALSE;
|
|
}
|
|
|
|
if( col_def->target_type == SWQ_GEOMETRY )
|
|
{
|
|
if( poExpr->nSubExprCount > 2 )
|
|
{
|
|
if( poExpr->papoSubExpr[2]->field_type != SWQ_STRING )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"First argument of CAST operator should be an geometry type identifier." );
|
|
CPLFree(col_def->table_name);
|
|
col_def->table_name = NULL;
|
|
CPLFree(col_def->field_name);
|
|
col_def->field_name = NULL;
|
|
CPLFree(col_def->field_alias);
|
|
col_def->field_alias = NULL;
|
|
result_columns--;
|
|
return FALSE;
|
|
}
|
|
|
|
col_def->eGeomType =
|
|
OGRFromOGCGeomType(poExpr->papoSubExpr[2]->string_value);
|
|
|
|
// SRID
|
|
if( poExpr->nSubExprCount > 3 )
|
|
{
|
|
col_def->nSRID = (int)poExpr->papoSubExpr[3]->int_value;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// field width.
|
|
if( poExpr->nSubExprCount > 2 )
|
|
{
|
|
if( poExpr->papoSubExpr[2]->field_type != SWQ_INTEGER )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"First argument of CAST operator should be of integer type." );
|
|
CPLFree(col_def->table_name);
|
|
col_def->table_name = NULL;
|
|
CPLFree(col_def->field_name);
|
|
col_def->field_name = NULL;
|
|
CPLFree(col_def->field_alias);
|
|
col_def->field_alias = NULL;
|
|
result_columns--;
|
|
return FALSE;
|
|
}
|
|
col_def->field_length = (int)poExpr->papoSubExpr[2]->int_value;
|
|
}
|
|
|
|
// field width.
|
|
if( poExpr->nSubExprCount > 3 && parse_precision )
|
|
{
|
|
col_def->field_precision = (int)poExpr->papoSubExpr[3]->int_value;
|
|
if( col_def->field_precision == 0 )
|
|
{
|
|
if( col_def->field_length < 10 )
|
|
col_def->target_type = SWQ_INTEGER;
|
|
else if( col_def->field_length < 19 )
|
|
col_def->target_type = SWQ_INTEGER64;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we have a special column function in play? */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poExpr->eNodeType == SNT_OPERATION
|
|
&& poExpr->nOperation >= SWQ_AVG
|
|
&& poExpr->nOperation <= SWQ_SUM )
|
|
{
|
|
if( poExpr->nSubExprCount != 1 )
|
|
{
|
|
const swq_operation *poOp =
|
|
swq_op_registrar::GetOperator( (swq_op)poExpr->nOperation );
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Column Summary Function '%s' has wrong number of arguments.",
|
|
poOp->pszName );
|
|
CPLFree(col_def->table_name);
|
|
col_def->table_name = NULL;
|
|
CPLFree(col_def->field_name);
|
|
col_def->field_name = NULL;
|
|
CPLFree(col_def->field_alias);
|
|
col_def->field_alias = NULL;
|
|
result_columns--;
|
|
return FALSE;
|
|
}
|
|
else if( poExpr->papoSubExpr[0]->eNodeType != SNT_COLUMN )
|
|
{
|
|
const swq_operation *poOp =
|
|
swq_op_registrar::GetOperator( (swq_op)poExpr->nOperation );
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Argument of column Summary Function '%s' should be a column.",
|
|
poOp->pszName );
|
|
CPLFree(col_def->table_name);
|
|
col_def->table_name = NULL;
|
|
CPLFree(col_def->field_name);
|
|
col_def->field_name = NULL;
|
|
CPLFree(col_def->field_alias);
|
|
col_def->field_alias = NULL;
|
|
result_columns--;
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
col_def->col_func =
|
|
(swq_col_func) poExpr->nOperation;
|
|
|
|
swq_expr_node *poSubExpr = poExpr->papoSubExpr[0];
|
|
|
|
poExpr->papoSubExpr[0] = NULL;
|
|
poExpr->nSubExprCount = 0;
|
|
delete poExpr;
|
|
|
|
poExpr = poSubExpr;
|
|
}
|
|
}
|
|
|
|
col_def->expr = poExpr;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PushTableDef() */
|
|
/************************************************************************/
|
|
|
|
int swq_select::PushTableDef( const char *pszDataSource,
|
|
const char *pszName,
|
|
const char *pszAlias )
|
|
|
|
{
|
|
table_count++;
|
|
|
|
table_defs = (swq_table_def *)
|
|
CPLRealloc( table_defs, sizeof(swq_table_def) * table_count );
|
|
|
|
if( pszDataSource != NULL )
|
|
table_defs[table_count-1].data_source = CPLStrdup(pszDataSource);
|
|
else
|
|
table_defs[table_count-1].data_source = NULL;
|
|
|
|
table_defs[table_count-1].table_name = CPLStrdup(pszName);
|
|
|
|
if( pszAlias != NULL )
|
|
table_defs[table_count-1].table_alias = CPLStrdup(pszAlias);
|
|
else
|
|
table_defs[table_count-1].table_alias = CPLStrdup(pszName);
|
|
|
|
return table_count-1;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PushOrderBy() */
|
|
/************************************************************************/
|
|
|
|
void swq_select::PushOrderBy( const char* pszTableName, const char *pszFieldName, int bAscending )
|
|
|
|
{
|
|
order_specs++;
|
|
order_defs = (swq_order_def *)
|
|
CPLRealloc( order_defs, sizeof(swq_order_def) * order_specs );
|
|
|
|
order_defs[order_specs-1].table_name = CPLStrdup(pszTableName ? pszTableName : "");
|
|
order_defs[order_specs-1].field_name = CPLStrdup(pszFieldName);
|
|
order_defs[order_specs-1].table_index = -1;
|
|
order_defs[order_specs-1].field_index = -1;
|
|
order_defs[order_specs-1].ascending_flag = bAscending;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PushJoin() */
|
|
/************************************************************************/
|
|
|
|
void swq_select::PushJoin( int iSecondaryTable, swq_expr_node* poExpr )
|
|
|
|
{
|
|
join_count++;
|
|
join_defs = (swq_join_def *)
|
|
CPLRealloc( join_defs, sizeof(swq_join_def) * join_count );
|
|
|
|
join_defs[join_count-1].secondary_table = iSecondaryTable;
|
|
join_defs[join_count-1].poExpr = poExpr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PushUnionAll() */
|
|
/************************************************************************/
|
|
|
|
void swq_select::PushUnionAll( swq_select* poOtherSelectIn )
|
|
{
|
|
CPLAssert(poOtherSelect == NULL);
|
|
poOtherSelect = poOtherSelectIn;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* expand_wildcard() */
|
|
/* */
|
|
/* This function replaces the '*' in a "SELECT *" with the list */
|
|
/* provided list of fields. Itis used by swq_select_parse(), */
|
|
/* but may be called in advance by applications wanting the */
|
|
/* "default" field list to be different than the full list of */
|
|
/* fields. */
|
|
/************************************************************************/
|
|
|
|
CPLErr swq_select::expand_wildcard( swq_field_list *field_list,
|
|
int bAlwaysPrefixWithTableName )
|
|
|
|
{
|
|
int isrc;
|
|
|
|
/* ==================================================================== */
|
|
/* Check each pre-expansion field. */
|
|
/* ==================================================================== */
|
|
for( isrc = 0; isrc < result_columns; isrc++ )
|
|
{
|
|
const char *src_tablename = column_defs[isrc].table_name;
|
|
const char *src_fieldname = column_defs[isrc].field_name;
|
|
int itable, new_fields, i, iout;
|
|
|
|
if( *src_fieldname == '\0'
|
|
|| src_fieldname[strlen(src_fieldname)-1] != '*' )
|
|
continue;
|
|
|
|
/* We don't want to expand COUNT(*) */
|
|
if( column_defs[isrc].col_func == SWQCF_COUNT )
|
|
continue;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Parse out the table name, verify it, and establish the */
|
|
/* number of fields to insert from it. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( src_tablename[0] == 0 && strcmp(src_fieldname,"*") == 0 )
|
|
{
|
|
itable = -1;
|
|
new_fields = field_list->count;
|
|
}
|
|
else
|
|
{
|
|
for( itable = 0; itable < field_list->table_count; itable++ )
|
|
{
|
|
if( strcasecmp(src_tablename,
|
|
field_list->table_defs[itable].table_alias ) == 0 )
|
|
break;
|
|
}
|
|
|
|
if( itable == field_list->table_count )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Table %s not recognised from %s.%s definition.",
|
|
src_tablename, src_tablename, src_fieldname );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* count the number of fields in this table. */
|
|
new_fields = 0;
|
|
for( i = 0; i < field_list->count; i++ )
|
|
{
|
|
if( field_list->table_ids[i] == itable )
|
|
new_fields++;
|
|
}
|
|
}
|
|
|
|
if (new_fields > 0)
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Reallocate the column list larger. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLFree( column_defs[isrc].table_name );
|
|
CPLFree( column_defs[isrc].field_name );
|
|
delete column_defs[isrc].expr;
|
|
|
|
column_defs = (swq_col_def *)
|
|
CPLRealloc( column_defs,
|
|
sizeof(swq_col_def) *
|
|
(result_columns + new_fields - 1 ) );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Push the old definitions that came after the one to be */
|
|
/* replaced further up in the array. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (new_fields != 1)
|
|
{
|
|
for( i = result_columns-1; i > isrc; i-- )
|
|
{
|
|
memcpy( column_defs + i + new_fields - 1,
|
|
column_defs + i,
|
|
sizeof( swq_col_def ) );
|
|
}
|
|
}
|
|
|
|
result_columns += (new_fields - 1 );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Zero out all the stuff in the target column definitions. */
|
|
/* -------------------------------------------------------------------- */
|
|
memset( column_defs + isrc, 0,
|
|
new_fields * sizeof(swq_col_def) );
|
|
}
|
|
else
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* The wildcard expands to nothing */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLFree( column_defs[isrc].table_name );
|
|
CPLFree( column_defs[isrc].field_name );
|
|
delete column_defs[isrc].expr;
|
|
|
|
memmove( column_defs + isrc,
|
|
column_defs + isrc + 1,
|
|
sizeof( swq_col_def ) * (result_columns-1-isrc) );
|
|
|
|
result_columns --;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Assign the selected fields. */
|
|
/* -------------------------------------------------------------------- */
|
|
iout = isrc;
|
|
|
|
for( i = 0; i < field_list->count; i++ )
|
|
{
|
|
swq_col_def *def;
|
|
int compose = (itable != -1) || bAlwaysPrefixWithTableName;
|
|
|
|
/* skip this field if it isn't in the target table. */
|
|
if( itable != -1 && itable != field_list->table_ids[i] )
|
|
continue;
|
|
|
|
/* set up some default values. */
|
|
def = column_defs + iout;
|
|
def->field_precision = -1;
|
|
def->target_type = SWQ_OTHER;
|
|
def->target_subtype = OFSTNone;
|
|
|
|
/* does this field duplicate an earlier one? */
|
|
if( field_list->table_ids[i] != 0
|
|
&& !compose )
|
|
{
|
|
int other;
|
|
|
|
for( other = 0; other < i; other++ )
|
|
{
|
|
if( strcasecmp(field_list->names[i],
|
|
field_list->names[other]) == 0 )
|
|
{
|
|
compose = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int itable = field_list->table_ids[i];
|
|
const char *field_name = field_list->names[i];
|
|
const char *table_alias =
|
|
field_list->table_defs[itable].table_alias;
|
|
|
|
def->table_name = CPLStrdup(table_alias);
|
|
def->field_name = CPLStrdup(field_name);
|
|
if( !compose )
|
|
def->field_alias = CPLStrdup( field_list->names[i] );
|
|
|
|
iout++;
|
|
|
|
/* All the other table info will be provided by the later
|
|
parse operation. */
|
|
}
|
|
|
|
/* If there are several occurrences of '*', go on, but stay on the */
|
|
/* same index in case '*' is expanded to nothing */
|
|
/* (the -- is to compensate the fact that isrc will be incremented in */
|
|
/* the after statement of the for loop) */
|
|
isrc --;
|
|
}
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CheckCompatibleJoinExpr() */
|
|
/************************************************************************/
|
|
|
|
static int CheckCompatibleJoinExpr( swq_expr_node* poExpr,
|
|
int secondary_table,
|
|
swq_field_list* field_list )
|
|
{
|
|
if( poExpr->eNodeType == SNT_CONSTANT )
|
|
return TRUE;
|
|
|
|
if( poExpr->eNodeType == SNT_COLUMN )
|
|
{
|
|
CPLAssert( poExpr->field_index != -1 );
|
|
CPLAssert( poExpr->table_index != -1 );
|
|
if( poExpr->table_index != 0 && poExpr->table_index != secondary_table )
|
|
{
|
|
if( poExpr->table_name )
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Field %s.%s in JOIN clause does not correspond to the primary table nor the joint (secondary) table.",
|
|
poExpr->table_name,
|
|
poExpr->string_value );
|
|
else
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Field %s in JOIN clause does not correspond to the primary table nor the joint (secondary) table.",
|
|
poExpr->string_value );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if( poExpr->eNodeType == SNT_OPERATION )
|
|
{
|
|
for(int i=0;i<poExpr->nSubExprCount;i++)
|
|
{
|
|
if( !CheckCompatibleJoinExpr( poExpr->papoSubExpr[i],
|
|
secondary_table,
|
|
field_list ) )
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* parse() */
|
|
/* */
|
|
/* This method really does post-parse processing. */
|
|
/************************************************************************/
|
|
|
|
CPLErr swq_select::parse( swq_field_list *field_list,
|
|
swq_select_parse_options* poParseOptions )
|
|
{
|
|
int i;
|
|
CPLErr eError;
|
|
|
|
int bAlwaysPrefixWithTableName = poParseOptions &&
|
|
poParseOptions->bAlwaysPrefixWithTableName;
|
|
eError = expand_wildcard( field_list, bAlwaysPrefixWithTableName );
|
|
if( eError != CE_None )
|
|
return eError;
|
|
|
|
swq_custom_func_registrar* poCustomFuncRegistrar = NULL;
|
|
if( poParseOptions != NULL )
|
|
poCustomFuncRegistrar = poParseOptions->poCustomFuncRegistrar;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Identify field information. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( i = 0; i < result_columns; i++ )
|
|
{
|
|
swq_col_def *def = column_defs + i;
|
|
|
|
if( def->expr != NULL && def->expr->eNodeType != SNT_COLUMN )
|
|
{
|
|
def->field_index = -1;
|
|
def->table_index = -1;
|
|
|
|
if( def->expr->Check( field_list, TRUE, FALSE, poCustomFuncRegistrar ) == SWQ_ERROR )
|
|
return CE_Failure;
|
|
|
|
def->field_type = def->expr->field_type;
|
|
}
|
|
else
|
|
{
|
|
swq_field_type this_type;
|
|
|
|
/* identify field */
|
|
def->field_index = swq_identify_field( def->table_name,
|
|
def->field_name, field_list,
|
|
&this_type,
|
|
&(def->table_index) );
|
|
|
|
/* record field type */
|
|
def->field_type = this_type;
|
|
|
|
if( def->field_index == -1 && def->col_func != SWQCF_COUNT )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Unrecognised field name %s.",
|
|
def->table_name[0] ? CPLSPrintf("%s.%s", def->table_name, def->field_name) : def->field_name );
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
/* identify column function if present */
|
|
if( (def->col_func == SWQCF_MIN
|
|
|| def->col_func == SWQCF_MAX
|
|
|| def->col_func == SWQCF_AVG
|
|
|| def->col_func == SWQCF_SUM)
|
|
&& (def->field_type == SWQ_STRING ||
|
|
def->field_type == SWQ_GEOMETRY) )
|
|
{
|
|
// possibly this is already enforced by the checker?
|
|
const swq_operation *op = swq_op_registrar::GetOperator(
|
|
(swq_op) def->col_func );
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Use of field function %s() on %s field %s illegal.",
|
|
op->pszName,
|
|
SWQFieldTypeToString(def->field_type),
|
|
def->field_name );
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check if we are producing a one row summary result or a set */
|
|
/* of records. Generate an error if we get conflicting */
|
|
/* indications. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
int bAllowDistinctOnMultipleFields = (
|
|
poParseOptions && poParseOptions->bAllowDistinctOnMultipleFields );
|
|
if( query_mode == SWQM_DISTINCT_LIST && result_columns > 1 &&
|
|
!bAllowDistinctOnMultipleFields )
|
|
{
|
|
CPLError( CE_Failure, CPLE_NotSupported,
|
|
"SELECT DISTINCT not supported on multiple columns." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
for( i = 0; i < result_columns; i++ )
|
|
{
|
|
swq_col_def *def = column_defs + i;
|
|
int this_indicator = -1;
|
|
|
|
if( query_mode == SWQM_DISTINCT_LIST && def->field_type == SWQ_GEOMETRY )
|
|
{
|
|
int bAllowDistinctOnGeometryField = (
|
|
poParseOptions && poParseOptions->bAllowDistinctOnGeometryField );
|
|
if( !bAllowDistinctOnGeometryField )
|
|
{
|
|
CPLError( CE_Failure, CPLE_NotSupported,
|
|
"SELECT DISTINCT on a geometry not supported." );
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
if( def->col_func == SWQCF_MIN
|
|
|| def->col_func == SWQCF_MAX
|
|
|| def->col_func == SWQCF_AVG
|
|
|| def->col_func == SWQCF_SUM
|
|
|| def->col_func == SWQCF_COUNT )
|
|
{
|
|
this_indicator = SWQM_SUMMARY_RECORD;
|
|
if( def->col_func == SWQCF_COUNT &&
|
|
def->distinct_flag &&
|
|
def->field_type == SWQ_GEOMETRY )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"SELECT COUNT DISTINCT on a geometry not supported." );
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
else if( def->col_func == SWQCF_NONE )
|
|
{
|
|
if( query_mode == SWQM_DISTINCT_LIST )
|
|
{
|
|
def->distinct_flag = TRUE;
|
|
this_indicator = SWQM_DISTINCT_LIST;
|
|
}
|
|
else
|
|
this_indicator = SWQM_RECORDSET;
|
|
}
|
|
|
|
if( this_indicator != query_mode
|
|
&& this_indicator != -1
|
|
&& query_mode != 0 )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Field list implies mixture of regular recordset mode, summary mode or distinct field list mode." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
if( this_indicator != -1 )
|
|
query_mode = this_indicator;
|
|
}
|
|
|
|
if (result_columns == 0)
|
|
{
|
|
query_mode = SWQM_RECORDSET;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Process column names in JOIN specs. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( i = 0; i < join_count; i++ )
|
|
{
|
|
swq_join_def *def = join_defs + i;
|
|
if( def->poExpr->Check( field_list, TRUE, TRUE, poCustomFuncRegistrar ) == SWQ_ERROR )
|
|
return CE_Failure;
|
|
if( !CheckCompatibleJoinExpr( def->poExpr, def->secondary_table, field_list ) )
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Process column names in order specs. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( i = 0; i < order_specs; i++ )
|
|
{
|
|
swq_order_def *def = order_defs + i;
|
|
|
|
/* identify field */
|
|
swq_field_type field_type;
|
|
def->field_index = swq_identify_field( def->table_name,
|
|
def->field_name, field_list,
|
|
&field_type, &(def->table_index) );
|
|
if( def->field_index == -1 )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Unrecognised field name %s in ORDER BY.",
|
|
def->table_name[0] ? CPLSPrintf("%s.%s", def->table_name, def->field_name) : def->field_name );
|
|
return CE_Failure;
|
|
}
|
|
|
|
if( def->table_index != 0 )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Cannot use field '%s' of a secondary table in a ORDER BY clause",
|
|
def->field_name );
|
|
return CE_Failure;
|
|
}
|
|
|
|
if( field_type == SWQ_GEOMETRY )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Cannot use geometry field '%s' in a ORDER BY clause",
|
|
def->field_name );
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Post process the where clause, subbing in field indexes and */
|
|
/* doing final validation. */
|
|
/* -------------------------------------------------------------------- */
|
|
int bAllowFieldsInSecondaryTablesInWhere = FALSE;
|
|
if( poParseOptions != NULL )
|
|
bAllowFieldsInSecondaryTablesInWhere = poParseOptions->bAllowFieldsInSecondaryTablesInWhere;
|
|
if( where_expr != NULL
|
|
&& where_expr->Check( field_list, bAllowFieldsInSecondaryTablesInWhere, FALSE, poCustomFuncRegistrar ) == SWQ_ERROR )
|
|
{
|
|
return CE_Failure;
|
|
}
|
|
|
|
return CE_None;
|
|
}
|