diff options
Diffstat (limited to 'connectivity/source/parse')
-rw-r--r-- | connectivity/source/parse/PColumn.cxx | 271 | ||||
-rw-r--r-- | connectivity/source/parse/internalnode.cxx | 64 | ||||
-rw-r--r-- | connectivity/source/parse/sqlbison.y | 4835 | ||||
-rw-r--r-- | connectivity/source/parse/sqlflex.l | 808 | ||||
-rw-r--r-- | connectivity/source/parse/sqliterator.cxx | 2117 | ||||
-rw-r--r-- | connectivity/source/parse/sqlnode.cxx | 2789 |
6 files changed, 10884 insertions, 0 deletions
diff --git a/connectivity/source/parse/PColumn.cxx b/connectivity/source/parse/PColumn.cxx new file mode 100644 index 000000000..5ada4136c --- /dev/null +++ b/connectivity/source/parse/PColumn.cxx @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <connectivity/PColumn.hxx> +#include <TConnection.hxx> + +#include <comphelper/types.hxx> + +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +using namespace ::comphelper; +using namespace connectivity; +using namespace dbtools; +using namespace connectivity::parse; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; + + +OParseColumn::OParseColumn(const Reference<XPropertySet>& _xColumn, bool _bCase) + : connectivity::sdbcx::OColumn( getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))) + , getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME))) + , getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE))) + , getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DESCRIPTION))) + , getINT32(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISNULLABLE))) + , getINT32(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION))) + , getINT32(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE))) + , getINT32(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))) + , getBOOL(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT))) + , false + , getBOOL(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY))) + , _bCase + , getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CATALOGNAME))) + , getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCHEMANAME))) + , getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TABLENAME))) + ) + , m_bFunction(false) + , m_bDbasePrecisionChanged(false) + , m_bAggregateFunction(false) + , m_bIsSearchable( true ) +{ + construct(); +} + + +OParseColumn::OParseColumn( const OUString& Name, + const OUString& TypeName, + const OUString& DefaultValue, + const OUString& Description, + sal_Int32 IsNullable, + sal_Int32 Precision, + sal_Int32 Scale, + sal_Int32 Type, + bool IsAutoIncrement, + bool IsCurrency, + bool _bCase, + const OUString& CatalogName, + const OUString& SchemaName, + const OUString& TableName + ) : connectivity::sdbcx::OColumn(Name, + TypeName, + DefaultValue, + Description, + IsNullable, + Precision, + Scale, + Type, + IsAutoIncrement, + false, + IsCurrency, + _bCase, + CatalogName, + SchemaName, + TableName) + , m_bFunction(false) + , m_bDbasePrecisionChanged(false) + , m_bAggregateFunction(false) + , m_bIsSearchable( true ) +{ + construct(); +} + + +::rtl::Reference< OSQLColumns > OParseColumn::createColumnsForResultSet( const Reference< XResultSetMetaData >& _rxResMetaData, + const Reference< XDatabaseMetaData >& _rxDBMetaData,const Reference< XNameAccess>& i_xQueryColumns ) +{ + sal_Int32 nColumnCount = _rxResMetaData->getColumnCount(); + ::rtl::Reference aReturn( new OSQLColumns ); aReturn->reserve( nColumnCount ); + + StringMap aColumnMap; + for ( sal_Int32 i = 1; i <= nColumnCount; ++i ) + { + rtl::Reference<OParseColumn> pColumn = createColumnForResultSet( _rxResMetaData, _rxDBMetaData, i,aColumnMap ); + aReturn->push_back( pColumn ); + if ( i_xQueryColumns.is() && i_xQueryColumns->hasByName(pColumn->getRealName()) ) + { + Reference<XPropertySet> xColumn(i_xQueryColumns->getByName(pColumn->getRealName()),UNO_QUERY_THROW); + OUString sLabel; + xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_LABEL)) >>= sLabel; + if ( !sLabel.isEmpty() ) + pColumn->setLabel(sLabel); + } + } + + return aReturn; +} + + +rtl::Reference<OParseColumn> OParseColumn::createColumnForResultSet( const Reference< XResultSetMetaData >& _rxResMetaData, + const Reference< XDatabaseMetaData >& _rxDBMetaData, sal_Int32 _nColumnPos, StringMap& _rColumns ) +{ + OUString sLabel = _rxResMetaData->getColumnLabel( _nColumnPos ); + // retrieve the name of the column + // check for duplicate entries + if(_rColumns.find(sLabel) != _rColumns.end()) + { + OUString sAlias(sLabel); + sal_Int32 searchIndex=1; + while(_rColumns.find(sAlias) != _rColumns.end()) + { + sAlias = sLabel + OUString::number(searchIndex++); + } + sLabel = sAlias; + } + _rColumns.emplace(sLabel,0); + rtl::Reference<OParseColumn> pColumn = new OParseColumn( + sLabel, + _rxResMetaData->getColumnTypeName( _nColumnPos ), + OUString(), + OUString(), + _rxResMetaData->isNullable( _nColumnPos ), + _rxResMetaData->getPrecision( _nColumnPos ), + _rxResMetaData->getScale( _nColumnPos ), + _rxResMetaData->getColumnType( _nColumnPos ), + _rxResMetaData->isAutoIncrement( _nColumnPos ), + _rxResMetaData->isCurrency( _nColumnPos ), + _rxDBMetaData->supportsMixedCaseQuotedIdentifiers(), + _rxResMetaData->getCatalogName( _nColumnPos ), + _rxResMetaData->getSchemaName( _nColumnPos ), + _rxResMetaData->getTableName( _nColumnPos ) + ); + pColumn->setIsSearchable( _rxResMetaData->isSearchable( _nColumnPos ) ); + pColumn->setRealName(_rxResMetaData->getColumnName( _nColumnPos )); + pColumn->setLabel(sLabel); + return pColumn; +} + + +OParseColumn::~OParseColumn() +{ +} + +void OParseColumn::construct() +{ + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FUNCTION), PROPERTY_ID_FUNCTION, 0, &m_bFunction, cppu::UnoType<decltype(m_bFunction)>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_AGGREGATEFUNCTION), PROPERTY_ID_AGGREGATEFUNCTION, 0, &m_bAggregateFunction, cppu::UnoType<decltype(m_bAggregateFunction)>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_REALNAME), PROPERTY_ID_REALNAME, 0, &m_aRealName, cppu::UnoType<decltype(m_aRealName)>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DBASEPRECISIONCHANGED), PROPERTY_ID_DBASEPRECISIONCHANGED, 0, &m_bDbasePrecisionChanged, cppu::UnoType<decltype(m_bDbasePrecisionChanged)>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISSEARCHABLE), PROPERTY_ID_ISSEARCHABLE, 0, &m_bIsSearchable, cppu::UnoType<decltype(m_bIsSearchable)>::get()); + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_LABEL), PROPERTY_ID_LABEL, 0, &m_sLabel, cppu::UnoType<decltype(m_sLabel)>::get()); +} + +::cppu::IPropertyArrayHelper* OParseColumn::createArrayHelper() const +{ + return doCreateArrayHelper(); +} + +::cppu::IPropertyArrayHelper & SAL_CALL OParseColumn::getInfoHelper() +{ + OSL_ENSURE( !isNew(), "OParseColumn::getInfoHelper: a *new* ParseColumn?" ); + return *OParseColumn_PROP::getArrayHelper(); +} + + +OOrderColumn::OOrderColumn( const Reference<XPropertySet>& _xColumn, const OUString& i_rOriginatingTableName, + bool _bCase, bool _bAscending ) + : connectivity::sdbcx::OColumn( + getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))), + getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME))), + getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE))), + getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DESCRIPTION))), + getINT32(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISNULLABLE))), + getINT32(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION))), + getINT32(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE))), + getINT32(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))), + getBOOL(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT))), + false, + getBOOL(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY))), + _bCase, + getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CATALOGNAME))), + getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCHEMANAME))), + i_rOriginatingTableName + ) + ,m_bAscending(_bAscending) +{ + construct(); +} + + +OOrderColumn::OOrderColumn( const Reference<XPropertySet>& _xColumn, bool _bCase, bool _bAscending ) + : connectivity::sdbcx::OColumn( + getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME))), + getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME))), + getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE))), + getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DESCRIPTION))), + getINT32(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISNULLABLE))), + getINT32(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION))), + getINT32(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE))), + getINT32(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))), + getBOOL(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT))), + false, + getBOOL(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY))), + _bCase, + getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CATALOGNAME))), + getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCHEMANAME))), + getString(_xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TABLENAME))) + ) + ,m_bAscending(_bAscending) +{ + construct(); +} + + +OOrderColumn::~OOrderColumn() +{ +} + + +void OOrderColumn::construct() +{ + registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISASCENDING), PROPERTY_ID_ISASCENDING, + PropertyAttribute::READONLY, const_cast< bool* >( &m_bAscending ), cppu::UnoType<decltype(m_bAscending)>::get() ); +} + +::cppu::IPropertyArrayHelper* OOrderColumn::createArrayHelper() const +{ + return doCreateArrayHelper(); +} + +::cppu::IPropertyArrayHelper & SAL_CALL OOrderColumn::getInfoHelper() +{ + OSL_ENSURE( !isNew(), "OOrderColumn::getInfoHelper: a *new* OrderColumn?" ); + return *OOrderColumn_PROP::getArrayHelper(); +} + +css::uno::Sequence< OUString > SAL_CALL OOrderColumn::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdb.OrderColumn" }; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/parse/internalnode.cxx b/connectivity/source/parse/internalnode.cxx new file mode 100644 index 000000000..1906ad3d4 --- /dev/null +++ b/connectivity/source/parse/internalnode.cxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <connectivity/sqlparse.hxx> +#include <connectivity/internalnode.hxx> + +using namespace connectivity; + + +OSQLInternalNode::OSQLInternalNode(const char* pNewValue, + SQLNodeType eNodeType, + sal_uInt32 nNodeID) + : OSQLParseNode(pNewValue,eNodeType,nNodeID) +{ + OSL_ENSURE(OSQLParser::s_pGarbageCollector, "Collector not initialized"); + (*OSQLParser::s_pGarbageCollector)->push_back(this); +} + + +OSQLInternalNode::OSQLInternalNode(std::string_view NewValue, + SQLNodeType eNodeType, + sal_uInt32 nNodeID) + :OSQLParseNode(NewValue,eNodeType,nNodeID) +{ + OSL_ENSURE(OSQLParser::s_pGarbageCollector, "Collector not initialized"); + (*OSQLParser::s_pGarbageCollector)->push_back(this); +} + + +OSQLInternalNode::OSQLInternalNode(const OUString &NewValue, + SQLNodeType eNodeType, + sal_uInt32 nNodeID) + :OSQLParseNode(NewValue,eNodeType,nNodeID) +{ + OSL_ENSURE(OSQLParser::s_pGarbageCollector, "Collector not initialized"); + (*OSQLParser::s_pGarbageCollector)->push_back(this); +} + + +OSQLInternalNode::~OSQLInternalNode() +{ + // remove the node from the garbage list + + OSL_ENSURE(OSQLParser::s_pGarbageCollector, "Collector not initialized"); + (*OSQLParser::s_pGarbageCollector)->erase(this); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/parse/sqlbison.y b/connectivity/source/parse/sqlbison.y new file mode 100644 index 000000000..e0e9969ab --- /dev/null +++ b/connectivity/source/parse/sqlbison.y @@ -0,0 +1,4835 @@ +%glr-parser +%token-table +%{ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vector> +#include <string.h> + +#include <connectivity/sqlnode.hxx> +#include <connectivity/sqlparse.hxx> +#include <connectivity/sqlbison_exports.hxx> +#include <connectivity/sqlscan.hxx> +#include <connectivity/internalnode.hxx> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/XNumberFormatter.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/util/XNumberFormats.hpp> +#include <com/sun/star/util/NumberFormat.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/i18n/KParseType.hpp> +#include <com/sun/star/i18n/KParseTokens.hpp> + +#include <osl/diagnose.h> +#include "connectivity/dbconversion.hxx" +#include <rtl/ustrbuf.hxx> +#include <sal/macros.h> +#include <sal/log.hxx> + +#if defined _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4324) // structure was padded due to alignment specifier +#pragma warning(disable: 4065) // switch statement contains 'default' but no 'case' labels +#pragma warning(disable: 4702) // unreachable code +#endif + +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wwrite-strings" +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +inline connectivity::OSQLInternalNode* newNode(const char* pNewValue, + const connectivity::SQLNodeType eNodeType, + const sal_uInt32 nNodeID = 0); + +inline connectivity::OSQLInternalNode* newNode(const OString& _newValue, + const connectivity::SQLNodeType eNodeType, + const sal_uInt32 nNodeID = 0); + +inline connectivity::OSQLInternalNode* newNode(const OUString& _newValue, + const connectivity::SQLNodeType eNodeType, + const sal_uInt32 nNodeID = 0); + + +// yyi is the internal number of the rule that is currently being reduced +// This can be mapped to external rule number via the yyrmap. +#if defined YYBISON && YYBISON >= 30800 +#define SQL_NEW_RULE newNode("", SQLNodeType::Rule, yyr1[yyrule]) +#define SQL_NEW_LISTRULE newNode("", SQLNodeType::ListRule, yyr1[yyrule]) +#define SQL_NEW_COMMALISTRULE newNode("", SQLNodeType::CommaListRule, yyr1[yyrule]) +#else +#define SQL_NEW_RULE newNode("", SQLNodeType::Rule, yyr1[yyn]) +#define SQL_NEW_LISTRULE newNode("", SQLNodeType::ListRule, yyr1[yyn]) +#define SQL_NEW_COMMALISTRULE newNode("", SQLNodeType::CommaListRule, yyr1[yyn]) +#endif + + +extern connectivity::OSQLParser* xxx_pGLOBAL_SQLPARSER; + +#define YYERROR_VERBOSE + +#define SQLyyerror(s) \ +{ \ + xxx_pGLOBAL_SQLPARSER->error(s); \ +} + +using namespace connectivity; +#define SQLyylex xxx_pGLOBAL_SQLPARSER->SQLlex +%} + /* symbolic tokens */ + +%union { + connectivity::OSQLParseNode * pParseNode; +} +%type <pParseNode> '(' ')' ',' ':' ';' '?' '[' ']' '{' '}' '.' 'K' 'M' 'G' 'T' 'P' + +%token <pParseNode> SQL_TOKEN_STRING SQL_TOKEN_ACCESS_DATE SQL_TOKEN_INT SQL_TOKEN_REAL_NUM +%token <pParseNode> SQL_TOKEN_INTNUM SQL_TOKEN_APPROXNUM SQL_TOKEN_NOT SQL_TOKEN_NAME + + +%nonassoc <pParseNode> SQL_TOKEN_UMINUS + + + + /* literal keyword tokens */ + +%token <pParseNode> SQL_TOKEN_ALL SQL_TOKEN_ALTER SQL_TOKEN_AMMSC SQL_TOKEN_ANY SQL_TOKEN_AS SQL_TOKEN_ASC SQL_TOKEN_AT SQL_TOKEN_AUTHORIZATION SQL_TOKEN_AVG + +%token <pParseNode> SQL_TOKEN_BETWEEN SQL_TOKEN_BIT SQL_TOKEN_BOTH SQL_TOKEN_BY + +%token <pParseNode> SQL_TOKEN_CAST SQL_TOKEN_CHARACTER SQL_TOKEN_CHECK SQL_TOKEN_COLLATE SQL_TOKEN_COMMIT SQL_TOKEN_CONTINUE SQL_TOKEN_CONVERT SQL_TOKEN_COUNT SQL_TOKEN_CREATE SQL_TOKEN_CROSS +%token <pParseNode> SQL_TOKEN_CURRENT SQL_TOKEN_CURSOR + +%token <pParseNode> SQL_TOKEN_DATE SQL_TOKEN_DATEVALUE SQL_TOKEN_DAY SQL_TOKEN_DEC SQL_TOKEN_DECIMAL SQL_TOKEN_DECLARE SQL_TOKEN_DEFAULT SQL_TOKEN_DELETE SQL_TOKEN_DESC +%token <pParseNode> SQL_TOKEN_DISTINCT SQL_TOKEN_DOUBLE SQL_TOKEN_DROP + +%token <pParseNode> SQL_TOKEN_ESCAPE SQL_TOKEN_EXCEPT SQL_TOKEN_EXISTS SQL_TOKEN_FALSE SQL_TOKEN_FETCH SQL_TOKEN_FLOAT SQL_TOKEN_FOR SQL_TOKEN_FOREIGN SQL_TOKEN_FOUND SQL_TOKEN_FROM SQL_TOKEN_FULL + +%token <pParseNode> SQL_TOKEN_GRANT SQL_TOKEN_GROUP SQL_TOKEN_HAVING SQL_TOKEN_IN SQL_TOKEN_INDICATOR SQL_TOKEN_INNER SQL_TOKEN_INTEGER SQL_TOKEN_INTO SQL_TOKEN_IS SQL_TOKEN_INTERSECT + +%left <pParseNode> SQL_TOKEN_JOIN +%token <pParseNode> SQL_TOKEN_KEY SQL_TOKEN_LEADING SQL_TOKEN_LIKE SQL_TOKEN_LOCAL SQL_TOKEN_LOWER +%token <pParseNode> SQL_TOKEN_MAX SQL_TOKEN_MIN SQL_TOKEN_NATURAL SQL_TOKEN_NCHAR SQL_TOKEN_NULL SQL_TOKEN_NUMERIC + +%token <pParseNode> SQL_TOKEN_OCTET_LENGTH SQL_TOKEN_OF SQL_TOKEN_ON SQL_TOKEN_OPTION SQL_TOKEN_ORDER SQL_TOKEN_OUTER + +%token <pParseNode> SQL_TOKEN_PRECISION SQL_TOKEN_PRIMARY SQL_TOKEN_PRIVILEGES SQL_TOKEN_PROCEDURE SQL_TOKEN_PUBLIC +%token <pParseNode> SQL_TOKEN_REAL SQL_TOKEN_REFERENCES SQL_TOKEN_ROLLBACK + +%token <pParseNode> SQL_TOKEN_SCHEMA SQL_TOKEN_SELECT SQL_TOKEN_SET SQL_TOKEN_SIZE SQL_TOKEN_SMALLINT SQL_TOKEN_SOME SQL_TOKEN_SQLCODE SQL_TOKEN_SQLERROR SQL_TOKEN_SUM + +%token <pParseNode> SQL_TOKEN_TABLE SQL_TOKEN_TIME SQL_TOKEN_TIMESTAMP SQL_TOKEN_TIMEZONE_HOUR SQL_TOKEN_TIMEZONE_MINUTE SQL_TOKEN_TO SQL_TOKEN_TRAILING SQL_TOKEN_TRANSLATE SQL_TOKEN_TRIM SQL_TOKEN_TRUE SQL_TOKEN_UNION +%token <pParseNode> SQL_TOKEN_UNIQUE SQL_TOKEN_UNKNOWN SQL_TOKEN_UPDATE SQL_TOKEN_UPPER SQL_TOKEN_USAGE SQL_TOKEN_USER SQL_TOKEN_USING SQL_TOKEN_VALUES SQL_TOKEN_VIEW +%token <pParseNode> SQL_TOKEN_WHERE SQL_TOKEN_WITH SQL_TOKEN_WORK SQL_TOKEN_ZONE + +/* ODBC KEYWORDS */ +%token <pParseNode> SQL_TOKEN_CALL SQL_TOKEN_D SQL_TOKEN_FN SQL_TOKEN_T SQL_TOKEN_TS SQL_TOKEN_OJ +/* string functions */ +%token <pParseNode> SQL_TOKEN_ASCII SQL_TOKEN_BIT_LENGTH SQL_TOKEN_CHAR SQL_TOKEN_CHAR_LENGTH SQL_TOKEN_SQL_TOKEN_INTNUM +%token <pParseNode> SQL_TOKEN_CONCAT +%token <pParseNode> SQL_TOKEN_DIFFERENCE SQL_TOKEN_INSERT SQL_TOKEN_LCASE SQL_TOKEN_LEFT SQL_TOKEN_LENGTH SQL_TOKEN_LOCATE +%token <pParseNode> SQL_TOKEN_LOCATE_2 SQL_TOKEN_LTRIM SQL_TOKEN_POSITION SQL_TOKEN_REPEAT SQL_TOKEN_REPLACE +%token <pParseNode> SQL_TOKEN_RIGHT SQL_TOKEN_RTRIM SQL_TOKEN_SOUNDEX SQL_TOKEN_SPACE SQL_TOKEN_SUBSTRING SQL_TOKEN_UCASE + +/* time and date functions */ +%token <pParseNode> SQL_TOKEN_CURRENT_DATE SQL_TOKEN_CURRENT_TIME SQL_TOKEN_CURRENT_TIMESTAMP SQL_TOKEN_CURDATE SQL_TOKEN_CURTIME +%token <pParseNode> SQL_TOKEN_DAYNAME SQL_TOKEN_DAYOFMONTH SQL_TOKEN_DAYOFWEEK SQL_TOKEN_DAYOFYEAR SQL_TOKEN_EXTRACT +%token <pParseNode> SQL_TOKEN_HOUR SQL_TOKEN_MILLISECOND SQL_TOKEN_MINUTE SQL_TOKEN_MONTH SQL_TOKEN_MONTHNAME SQL_TOKEN_NOW SQL_TOKEN_QUARTER SQL_TOKEN_DATEDIFF +%token <pParseNode> SQL_TOKEN_SECOND SQL_TOKEN_TIMESTAMPADD SQL_TOKEN_TIMESTAMPDIFF SQL_TOKEN_TIMEVALUE SQL_TOKEN_WEEK SQL_TOKEN_WEEKDAY SQL_TOKEN_YEAR SQL_TOKEN_YEARDAY + +/* numeric functions */ +%token <pParseNode> SQL_TOKEN_ABS SQL_TOKEN_ACOS SQL_TOKEN_ASIN SQL_TOKEN_ATAN SQL_TOKEN_ATAN2 SQL_TOKEN_CEILING +%token <pParseNode> SQL_TOKEN_COS SQL_TOKEN_COT SQL_TOKEN_DEGREES SQL_TOKEN_EXP SQL_TOKEN_FLOOR SQL_TOKEN_LOGF SQL_TOKEN_LOG SQL_TOKEN_LN +%token <pParseNode> SQL_TOKEN_LOG10 SQL_TOKEN_MOD SQL_TOKEN_PI SQL_TOKEN_POWER SQL_TOKEN_RADIANS SQL_TOKEN_RAND SQL_TOKEN_ROUNDMAGIC +%token <pParseNode> SQL_TOKEN_ROUND SQL_TOKEN_SIGN SQL_TOKEN_SIN SQL_TOKEN_SQRT SQL_TOKEN_TAN SQL_TOKEN_TRUNCATE + +// computational operation +%token <pParseNode> SQL_TOKEN_EVERY SQL_TOKEN_INTERSECTION SQL_TOKEN_FUSION SQL_TOKEN_COLLECT SQL_TOKEN_VAR_POP SQL_TOKEN_VAR_SAMP +%token <pParseNode> SQL_TOKEN_STDDEV_SAMP SQL_TOKEN_STDDEV_POP + +%token <pParseNode> SQL_TOKEN_RANK SQL_TOKEN_DENSE_RANK SQL_TOKEN_PERCENT_RANK SQL_TOKEN_CUME_DIST SQL_TOKEN_PERCENTILE_CONT SQL_TOKEN_PERCENTILE_DISC SQL_TOKEN_WITHIN SQL_TOKEN_ARRAY_AGG +%token <pParseNode> SQL_TOKEN_CASE SQL_TOKEN_THEN SQL_TOKEN_END SQL_TOKEN_NULLIF SQL_TOKEN_COALESCE SQL_TOKEN_WHEN SQL_TOKEN_ELSE +%token <pParseNode> SQL_TOKEN_BEFORE SQL_TOKEN_AFTER SQL_TOKEN_INSTEAD SQL_TOKEN_EACH SQL_TOKEN_REFERENCING SQL_TOKEN_BEGIN SQL_TOKEN_ATOMIC SQL_TOKEN_TRIGGER SQL_TOKEN_ROW SQL_TOKEN_STATEMENT +%token <pParseNode> SQL_TOKEN_NEW SQL_TOKEN_OLD +%token <pParseNode> SQL_TOKEN_VALUE SQL_TOKEN_CURRENT_CATALOG SQL_TOKEN_CURRENT_DEFAULT_TRANSFORM_GROUP SQL_TOKEN_CURRENT_PATH SQL_TOKEN_CURRENT_ROLE SQL_TOKEN_CURRENT_SCHEMA SQL_TOKEN_CURRENT_USER +%token <pParseNode> SQL_TOKEN_SESSION_USER SQL_TOKEN_SYSTEM_USER SQL_TOKEN_VARCHAR SQL_TOKEN_VARBINARY SQL_TOKEN_VARYING SQL_TOKEN_OBJECT SQL_TOKEN_NCLOB SQL_TOKEN_NATIONAL +%token <pParseNode> SQL_TOKEN_LARGE SQL_TOKEN_CLOB SQL_TOKEN_BLOB SQL_TOKEN_BIGINT SQL_TOKEN_BINARY SQL_TOKEN_WITHOUT SQL_TOKEN_BOOLEAN SQL_TOKEN_INTERVAL +// window function +%token <pParseNode> SQL_TOKEN_OVER SQL_TOKEN_ROW_NUMBER SQL_TOKEN_NTILE SQL_TOKEN_LEAD SQL_TOKEN_LAG SQL_TOKEN_RESPECT SQL_TOKEN_IGNORE SQL_TOKEN_NULLS +%token <pParseNode> SQL_TOKEN_FIRST_VALUE SQL_TOKEN_LAST_VALUE SQL_TOKEN_NTH_VALUE SQL_TOKEN_FIRST SQL_TOKEN_LAST +%token <pParseNode> SQL_TOKEN_EXCLUDE SQL_TOKEN_OTHERS SQL_TOKEN_TIES SQL_TOKEN_FOLLOWING SQL_TOKEN_UNBOUNDED SQL_TOKEN_PRECEDING SQL_TOKEN_RANGE SQL_TOKEN_ROWS +%token <pParseNode> SQL_TOKEN_PARTITION SQL_TOKEN_WINDOW SQL_TOKEN_NO +// LIMIT and OFFSEt +%token <pParseNode> SQL_TOKEN_LIMIT SQL_TOKEN_OFFSET SQL_TOKEN_NEXT SQL_TOKEN_ONLY + + /* operators */ +%left SQL_TOKEN_NAME +%left <pParseNode> SQL_TOKEN_OR +%left <pParseNode> SQL_TOKEN_AND + +%left <pParseNode> SQL_LESSEQ SQL_GREATEQ SQL_NOTEQUAL SQL_LESS SQL_GREAT SQL_EQUAL /* '<' '>' = <> < > <= >= != */ +%left <pParseNode> '+' '-' SQL_CONCAT +%left <pParseNode> '*' '/' +%left SQL_TOKEN_NATURAL SQL_TOKEN_CROSS SQL_TOKEN_FULL SQL_TOKEN_LEFT SQL_TOKEN_RIGHT +%left ')' +%right '=' +%right '.' +%right '(' + + +%token <pParseNode> SQL_TOKEN_INVALIDSYMBOL + +/*%type <pParseNode> sql_single_statement */ + +%type <pParseNode> sql /*schema */ +%type <pParseNode> column_def_opt_list column_def_opt table_constraint_def column_commalist +%type <pParseNode> view_def opt_with_check_option opt_column_commalist privilege_def +%type <pParseNode> opt_with_grant_option privileges operation_commalist operation +%type <pParseNode> grantee_commalist grantee opt_order_by_clause ordering_spec_commalist +%type <pParseNode> ordering_spec opt_asc_desc manipulative_statement commit_statement +%type <pParseNode> /*delete_statement_positioned*/ delete_statement_searched fetch_statement +%type <pParseNode> insert_statement values_or_query_spec +%type <pParseNode> rollback_statement select_statement_into opt_all_distinct +%type <pParseNode> /*update_statement_positioned*/ assignment_commalist assignment +%type <pParseNode> update_statement_searched target_commalist target opt_where_clause +%type <pParseNode> select_statement selection table_exp from_clause table_ref_commalist table_ref +%type <pParseNode> where_clause opt_group_by_clause column_ref_commalist opt_having_clause +%type <pParseNode> search_condition predicate comparison_predicate comparison_predicate_part_2 between_predicate between_predicate_part_2 +%type <pParseNode> like_predicate opt_escape test_for_null null_predicate_part_2 in_predicate in_predicate_part_2 character_like_predicate_part_2 other_like_predicate_part_2 +%type <pParseNode> all_or_any_predicate any_all_some existence_test subquery quantified_comparison_predicate_part_2 +%type <pParseNode> scalar_exp_commalist parameter_ref literal parenthesized_boolean_value_expression +%type <pParseNode> column_ref data_type column cursor parameter range_variable user /*like_check*/ +/* new rules at OJ */ +%type <pParseNode> derived_column as_clause table_name num_primary term num_value_exp +%type <pParseNode> value_exp_primary num_value_fct unsigned_value_spec cast_spec set_fct_spec scalar_subquery +%type <pParseNode> position_exp extract_exp length_exp general_value_spec +%type <pParseNode> general_set_fct set_fct_type query_exp non_join_query_exp joined_table +%type <pParseNode> non_join_query_term non_join_query_primary simple_table +%type <pParseNode> table_value_const_list row_value_constructor /*row_value_const_list*/ row_value_constructor_elem +%type <pParseNode> qualified_join value_exp query_term join_type outer_join_type join_condition boolean_term +%type <pParseNode> boolean_factor boolean_primary named_columns_join join_spec +%type <pParseNode> cast_operand cast_target factor datetime_value_exp /*interval_value_exp*/ datetime_term datetime_factor +%type <pParseNode> datetime_primary datetime_value_fct time_zone time_zone_specifier /*interval_term*/ interval_qualifier +%type <pParseNode> start_field non_second_datetime_field end_field single_datetime_field extract_field datetime_field time_zone_field +%type <pParseNode> char_length_exp octet_length_exp bit_length_exp select_sublist string_value_exp +%type <pParseNode> char_value_exp concatenation char_factor char_primary string_value_fct char_substring_fct fold +%type <pParseNode> form_conversion char_translation trim_fct trim_operands trim_spec bit_value_fct bit_substring_fct op_column_commalist +%type <pParseNode> /*bit_concatenation*/ bit_value_exp bit_factor bit_primary collate_clause char_value_fct unique_spec value_exp_commalist in_predicate_value unique_test update_source +%type <pParseNode> function_arg_commalist3 string_function_3Argument function_arg_commalist4 string_function_4Argument function_arg_commalist2 string_function_1Argument string_function_2Argument +%type <pParseNode> date_function_0Argument date_function_1Argument function_name12 function_name23 function_name1 function_name2 function_name3 function_name0 numeric_function_0Argument numeric_function_1Argument numeric_function_2Argument +%type <pParseNode> all query_primary sql_not for_length upper_lower comparison column_val cross_union /*opt_schema_element_list*/ +%type <pParseNode> /*op_authorization op_schema*/ nil_fkt schema_element base_table_def base_table_element base_table_element_commalist +%type <pParseNode> column_def odbc_fct_spec odbc_call_spec odbc_fct_type op_parameter union_statement +%type <pParseNode> op_odbc_call_parameter odbc_parameter_commalist odbc_parameter function_args_commalist function_arg +%type <pParseNode> catalog_name schema_name table_node numeric_function string_function function_name date_function table_primary_as_range_column opt_as +%type <pParseNode> ordered_set_function inverse_distribution_function hypothetical_set_function hypothetical_set_function_value_expression_list rank_function_type within_group_specification inverse_distribution_function_type array_aggregate_function inverse_distribution_function_argument +%type <pParseNode> case_expression else_clause result_expression result case_abbreviation case_specification searched_when_clause simple_when_clause searched_case simple_case +%type <pParseNode> when_operand_list when_operand case_operand +%type <pParseNode> trigger_definition trigger_name trigger_action_time trigger_event transition_table_or_variable_list triggered_action trigger_column_list triggered_when_clause triggered_SQL_statement SQL_procedure_statement old_transition_variable_name new_transition_variable_name +%type <pParseNode> op_referencing op_trigger_columnlist op_triggered_action_for opt_row trigger_for SQL_procedure_statement_list transition_table_or_variable old_transition_table_name new_transition_table_name transition_table_name +%type <pParseNode> searched_when_clause_list simple_when_clause_list predefined_type opt_char_set_spec opt_collate_clause character_string_type national_character_string_type +%type <pParseNode> binary_string_type numeric_type boolean_type datetime_type interval_type opt_paren_precision paren_char_length opt_paren_char_large_length paren_character_large_object_length +%type <pParseNode> large_object_length opt_multiplier character_large_object_type national_character_large_object_type binary_large_object_string_type opt_with_or_without_time_zone +%type <pParseNode> approximate_numeric_type exact_numeric_type opt_paren_precision_scale +/* window function rules */ +%type <pParseNode> window_function window_function_type ntile_function number_of_tiles lead_or_lag_function lead_or_lag lead_or_lag_extent offset default_expression null_treatment +%type <pParseNode> first_or_last_value_function first_or_last_value nth_value_function nth_row from_first_or_last window_name_or_specification in_line_window_specification opt_lead_or_lag_function +%type <pParseNode> opt_null_treatment opt_from_first_or_last simple_value_specification dynamic_parameter_specification window_name window_clause window_definition_list window_definition +%type <pParseNode> new_window_name existing_window_name window_partition_clause window_partition_column_reference_list window_partition_column_reference window_frame_clause +%type <pParseNode> window_frame_units window_frame_extent window_frame_start window_frame_preceding window_frame_between window_frame_bound_1 window_frame_bound_2 window_frame_bound window_frame_following window_frame_exclusion +%type <pParseNode> opt_window_frame_clause opt_window_partition_clause opt_existing_window_name window_specification opt_window_frame_exclusion opt_window_clause opt_offset +%type <pParseNode> opt_fetch_first_row_count fetch_first_clause offset_row_count fetch_first_row_count first_or_next row_or_rows opt_result_offset_clause result_offset_clause +/* LIMIT and OFFSET */ +%type <pParseNode> opt_limit_offset_clause limit_offset_clause opt_fetch_first_clause +%% + +/* Return Parse Tree to OSQLParser + * (the access over yyval after calling the parser fails, + * + */ +sql_single_statement: + sql + { xxx_pGLOBAL_SQLPARSER->setParseTree( $1 ); } + | sql ';' + { xxx_pGLOBAL_SQLPARSER->setParseTree( $1 ); } + ; + + /* schema definition language */ + /* Note: other ``sql:sal_Unicode() rules appear later in the grammar */ + +sql: + manipulative_statement + | schema_element + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; + +/*** + +op_authorization: + {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_AUTHORIZATION user + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +op_schema: + {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_NAME + | SQL_TOKEN_NAME '.' SQL_TOKEN_NAME + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($3); + } + ; + +schema: + SQL_TOKEN_CREATE SQL_TOKEN_SCHEMA op_schema op_authorization opt_schema_element_list + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + } + ; + +opt_schema_element_list: + {$$ = SQL_NEW_RULE;} + | schema_glement_list + ; + +schema_element_list: + schema_element + {$$ = SQL_NEW_LISTRULE; + $$->append($1);} + | schema_element_list schema_element + {$1->append($2); + $$ = $1;} + ; +*/ + +schema_element: + base_table_def + | view_def + | privilege_def + | trigger_definition + ; + +base_table_def: + SQL_TOKEN_CREATE SQL_TOKEN_TABLE table_node '(' base_table_element_commalist ')' + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($5); + $$->append(newNode(")", SQLNodeType::Punctuation));} + ; + +base_table_element_commalist: + base_table_element + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | base_table_element_commalist ',' base_table_element + {$1->append($3); + $$ = $1;} + ; + +base_table_element: + column_def + | table_constraint_def + ; + +column_def: + column data_type column_def_opt_list + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; + +column_def_opt_list: + /* empty */ {$$ = SQL_NEW_LISTRULE;} + | column_def_opt_list column_def_opt + {$1->append($2); + $$ = $1;} + ; + +nil_fkt: + datetime_value_fct + ; +unique_spec: + SQL_TOKEN_UNIQUE + | SQL_TOKEN_PRIMARY SQL_TOKEN_KEY + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +column_def_opt: + SQL_TOKEN_NOT SQL_TOKEN_NULL + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + | unique_spec + | SQL_TOKEN_DEFAULT literal + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + | SQL_TOKEN_DEFAULT SQL_TOKEN_NULL + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + | SQL_TOKEN_DEFAULT SQL_TOKEN_USER + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + | SQL_TOKEN_DEFAULT nil_fkt + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | SQL_TOKEN_CHECK + | SQL_TOKEN_CHECK '(' search_condition ')' + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation));} + | SQL_TOKEN_REFERENCES table_node + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + | SQL_TOKEN_REFERENCES table_node '(' column_commalist ')' + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($4); + $$->append(newNode(")", SQLNodeType::Punctuation));} + ; + +table_constraint_def: + unique_spec '(' column_commalist ')' + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation));} + | SQL_TOKEN_FOREIGN SQL_TOKEN_KEY '(' column_commalist ')' SQL_TOKEN_REFERENCES table_node + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($4); + $$->append(newNode(")", SQLNodeType::Punctuation)); + $$->append($6); + $$->append($7);} + | SQL_TOKEN_FOREIGN SQL_TOKEN_KEY '(' column_commalist ')' SQL_TOKEN_REFERENCES table_node '(' column_commalist ')' + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($4); + $$->append(newNode(")", SQLNodeType::Punctuation)); + $$->append($6); + $$->append($7); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($9); + $$->append(newNode(")", SQLNodeType::Punctuation));} + | SQL_TOKEN_CHECK '(' search_condition ')' + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation));} + ; +op_column_commalist: + /* empty */ {$$ = SQL_NEW_RULE;} + | '(' column_commalist ')' + {$$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +column_commalist: + column_commalist ',' column + {$1->append($3); + $$ = $1;} + | column + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + ; + +view_def: + SQL_TOKEN_CREATE SQL_TOKEN_VIEW table_node opt_column_commalist SQL_TOKEN_AS select_statement opt_with_check_option + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append($6); + $$->append($7);} + ; + +opt_with_check_option: + /* empty */ {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_WITH SQL_TOKEN_CHECK SQL_TOKEN_OPTION + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3);} + ; + +opt_column_commalist: + /* empty */ {$$ = SQL_NEW_RULE;} + | '(' column_commalist ')' + {$$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation));} + ; + +privilege_def: + SQL_TOKEN_GRANT privileges SQL_TOKEN_ON table_node SQL_TOKEN_TO grantee_commalist + opt_with_grant_option + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append($6); + $$->append($7);} + ; + +opt_with_grant_option: + /* empty */ {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_WITH SQL_TOKEN_GRANT SQL_TOKEN_OPTION + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3);} + ; + +privileges: + SQL_TOKEN_ALL SQL_TOKEN_PRIVILEGES + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + | operation_commalist + ; + +operation_commalist: + operation + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | operation_commalist ',' operation + {$1->append($3); + $$ = $1;} + ; + +operation: + SQL_TOKEN_SELECT + | SQL_TOKEN_INSERT opt_column_commalist + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + | SQL_TOKEN_DELETE + | SQL_TOKEN_UPDATE opt_column_commalist + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + | SQL_TOKEN_REFERENCES opt_column_commalist + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + | SQL_TOKEN_USAGE + ; + + +grantee_commalist: + grantee + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | grantee_commalist ',' grantee + {$1->append($3); + $$ = $1;} + ; + +grantee: + SQL_TOKEN_PUBLIC + | user + ; + + /* module language */ + +opt_order_by_clause: + /* empty */ {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_ORDER SQL_TOKEN_BY ordering_spec_commalist + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3);} + ; + +ordering_spec_commalist: + ordering_spec + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | ordering_spec_commalist ',' ordering_spec + {$1->append($3); + $$ = $1;} + ; + +ordering_spec: +/* SQL_TOKEN_INTNUM opt_asc_desc + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} +*/ + predicate opt_asc_desc + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + + | row_value_constructor_elem opt_asc_desc + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + ; + +opt_asc_desc: + {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_ASC + | SQL_TOKEN_DESC + ; + + +/*** +manipulative_statement_list: + manipulative_statement + {$$ = SQL_NEW_LISTRULE; + $$->append($1);} + | manipulative_statement_list manipulative_statement + {$1->append($2); + $$ = $1;} + ; +***/ + +sql_not: +/* vide */ + {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_NOT + ; + +/* manipulative statements */ + +manipulative_statement: + commit_statement +/* | delete_statement_positioned*/ + | delete_statement_searched + | fetch_statement + | insert_statement + | rollback_statement + | select_statement_into +/* | update_statement_positioned*/ + | update_statement_searched + | union_statement + | '{' odbc_call_spec '}' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("{", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode("}", SQLNodeType::Punctuation)); + } + ; + +union_statement: + select_statement + | union_statement SQL_TOKEN_UNION all select_statement + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + ; +commit_statement: + SQL_TOKEN_COMMIT SQL_TOKEN_WORK + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + ; +/* +delete_statement_positioned: + SQL_TOKEN_DELETE SQL_TOKEN_FROM table_node SQL_TOKEN_WHERE SQL_TOKEN_CURRENT SQL_TOKEN_OF cursor + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append($6); + $$->append($7);} + ; +*/ +delete_statement_searched: + SQL_TOKEN_DELETE SQL_TOKEN_FROM table_node opt_where_clause + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4);} + ; + +fetch_statement: + SQL_TOKEN_FETCH cursor SQL_TOKEN_INTO target_commalist + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4);} + ; + +insert_statement: + SQL_TOKEN_INSERT SQL_TOKEN_INTO table_node opt_column_commalist query_exp + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5);} + ; +values_or_query_spec: + SQL_TOKEN_VALUES '(' table_value_const_list ')' + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; + +table_value_const_list: + row_value_constructor + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | table_value_const_list ',' row_value_constructor + {$1->append($3); + $$ = $1;} + ; +/* +row_value_const_list: + row_value_constructor_elem + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | row_value_const_list ',' row_value_constructor_elem + {$1->append($3); + $$ = $1;} + ; +*/ +row_value_constructor: + row_value_constructor_elem +/* | '(' row_value_const_list ')' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + */ + ; +row_value_constructor_elem: + value_exp /*[^')']*/ + | SQL_TOKEN_DEFAULT + ; + + +rollback_statement: + SQL_TOKEN_ROLLBACK SQL_TOKEN_WORK + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + ; + + + /* INTO target_commalist herausgenommen */ +select_statement_into: + SQL_TOKEN_SELECT opt_all_distinct selection SQL_TOKEN_INTO target_commalist table_exp + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append($6); } + ; + +opt_all_distinct: + {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_ALL + | SQL_TOKEN_DISTINCT + + ; +/* +update_statement_positioned: + SQL_TOKEN_UPDATE table_node SQL_TOKEN_SET assignment_commalist + SQL_TOKEN_WHERE SQL_TOKEN_CURRENT SQL_TOKEN_OF cursor + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append($6); + $$->append($7); + $$->append($8);} + ; +*/ +assignment_commalist: + assignment + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | assignment_commalist ',' assignment + {$1->append($3); + $$ = $1;} + ; + +assignment: + column SQL_EQUAL update_source + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3);} + ; +update_source: + value_exp + | SQL_TOKEN_DEFAULT + ; +update_statement_searched: + SQL_TOKEN_UPDATE table_node SQL_TOKEN_SET assignment_commalist opt_where_clause + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5);} + ; + +target_commalist: + target + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | target_commalist ',' target + {$1->append($3); + $$ = $1;} + ; + +target: + parameter_ref + ; + +opt_where_clause: + /* empty */ {$$ = SQL_NEW_RULE;} + | where_clause + ; + + /* query expressions */ + +query_term: + non_join_query_term + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; +/* SELECT STATEMENT */ +select_statement: + SQL_TOKEN_SELECT opt_all_distinct selection table_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + ; + +selection: + '*' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("*", SQLNodeType::Punctuation)); + } + | scalar_exp_commalist + ; +opt_result_offset_clause: + /* empty */ {$$ = SQL_NEW_RULE;} + | result_offset_clause + ; +result_offset_clause: + SQL_TOKEN_OFFSET offset_row_count row_or_rows + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +opt_fetch_first_row_count: + /* empty */ {$$ = SQL_NEW_RULE;} + | fetch_first_row_count + ; +first_or_next: + SQL_TOKEN_FIRST + | SQL_TOKEN_NEXT + ; +row_or_rows: + SQL_TOKEN_ROW + | SQL_TOKEN_ROWS + ; +opt_fetch_first_clause: + /* empty */ {$$ = SQL_NEW_RULE;} + | fetch_first_clause + ; +fetch_first_clause: + SQL_TOKEN_FETCH first_or_next opt_fetch_first_row_count row_or_rows SQL_TOKEN_ONLY + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + } + ; +offset_row_count: + literal + ; +fetch_first_row_count: + literal + ; + +opt_limit_offset_clause: + /* empty */ {$$ = SQL_NEW_RULE;} + | limit_offset_clause + ; +opt_offset: + /* empty */ {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_OFFSET SQL_TOKEN_INTNUM + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +limit_offset_clause: + SQL_TOKEN_LIMIT SQL_TOKEN_INTNUM opt_offset + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +table_exp: + from_clause opt_where_clause opt_group_by_clause opt_having_clause opt_window_clause opt_order_by_clause opt_limit_offset_clause opt_result_offset_clause opt_fetch_first_clause + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append($6); + $$->append($7); + $$->append($8); + $$->append($9); + } + ; + +from_clause: + SQL_TOKEN_FROM table_ref_commalist + { $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); } + ; + +table_ref_commalist: + + table_ref + { $$ = SQL_NEW_COMMALISTRULE; + $$->append($1); } + | table_ref_commalist ',' table_ref + { $1->append($3); + $$ = $1; } + ; + +opt_as: + /* empty */ {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_AS + ; +opt_row: + /* empty */ {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_ROW + ; +table_primary_as_range_column: + {$$ = SQL_NEW_RULE;} + | opt_as SQL_TOKEN_NAME op_column_commalist + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +table_ref: + table_node table_primary_as_range_column + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | subquery range_variable op_column_commalist + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | joined_table + | '{' SQL_TOKEN_OJ joined_table '}' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("{", SQLNodeType::Punctuation)); + $$->append($2); + $$->append($3); + $$->append(newNode("}", SQLNodeType::Punctuation)); + } + | '(' joined_table ')' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +where_clause: + SQL_TOKEN_WHERE search_condition + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + ; + +opt_group_by_clause: + /* empty */ {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_GROUP SQL_TOKEN_BY column_ref_commalist + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3);} + ; + +column_ref_commalist: + column_ref + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | set_fct_spec + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | column_ref_commalist ',' column_ref + {$1->append($3); + $$ = $1;} + | column_ref_commalist ',' set_fct_spec + {$1->append($3); + $$ = $1;} + ; + +opt_having_clause: + /* empty */ {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_HAVING search_condition + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + ; + + /* search conditions */ +boolean_primary: + predicate + | '(' search_condition ')' + { // boolean_primary: rule 2 + $$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | row_value_constructor_elem /*[^')' ',']*/ + { + if(xxx_pGLOBAL_SQLPARSER->inPredicateCheck())// boolean_primary: rule 3 + { + $$ = SQL_NEW_RULE; + sal_Int16 nErg = 0; + if ( SQL_ISTOKEN( $1, NULL)) + { + OSQLParseNode* pColumnRef = newNode("", SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::column_ref)); + pColumnRef->append(newNode(xxx_pGLOBAL_SQLPARSER->getFieldName(),SQLNodeType::Name)); + OSQLParseNode* pTFN = new OSQLInternalNode("", SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::test_for_null)); + pTFN->append(pColumnRef); + + OSQLParseNode* pNPP2 = new OSQLInternalNode("", SQLNodeType::Rule, OSQLParser::RuleID(OSQLParseNode::null_predicate_part_2)); + pNPP2->append(new OSQLInternalNode("", SQLNodeType::Keyword, SQL_TOKEN_IS)); + pNPP2->append(new OSQLInternalNode("", SQLNodeType::Rule, OSQLParser::RuleID(OSQLParseNode::sql_not))); + pNPP2->append(new OSQLInternalNode("", SQLNodeType::Keyword, SQL_TOKEN_NULL)); + pTFN->append(pNPP2); + + $$->append(pTFN); + + nErg = 1; + } + else + { + nErg = xxx_pGLOBAL_SQLPARSER->buildComparisonRule($$,$1); + } + if(nErg == 1) + { + OSQLParseNode* pTemp = $$; + $$ = pTemp->removeAt((sal_uInt32)0); + delete pTemp; + } + else + { + delete $$; + if(nErg) + YYERROR; + else + YYABORT; + } + } + else + YYERROR; + } + ; +parenthesized_boolean_value_expression: + '(' search_condition ')' + { // boolean_primary: rule 2 + $$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +boolean_factor: + boolean_primary %dprec 2 + | SQL_TOKEN_NOT boolean_primary %dprec 1 + { // boolean_factor: rule 1 + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +boolean_term: + boolean_factor + | boolean_term SQL_TOKEN_AND boolean_factor + { + $$ = SQL_NEW_RULE; // boolean_term: rule 1 + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +search_condition: + boolean_term + | search_condition SQL_TOKEN_OR boolean_term + { + $$ = SQL_NEW_RULE; // search_condition + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +predicate: + comparison_predicate %dprec 1 + | between_predicate + | all_or_any_predicate + | existence_test + | unique_test + | test_for_null %dprec 2 + | in_predicate + | like_predicate + ; +comparison_predicate_part_2: + comparison row_value_constructor + { + $$ = SQL_NEW_RULE; // comparison_predicate: rule 1 + $$->append($1); + $$->append($2); + } +comparison_predicate: + row_value_constructor comparison row_value_constructor + { + $$ = SQL_NEW_RULE; // comparison_predicate: rule 1 + $$->append($1); + $$->append($2); + $$->append($3); + } + | comparison row_value_constructor + { + if(xxx_pGLOBAL_SQLPARSER->inPredicateCheck()) // comparison_predicate: rule 2 + { + $$ = SQL_NEW_RULE; + sal_Int16 nErg = xxx_pGLOBAL_SQLPARSER->buildPredicateRule($$,$2,$1); + if(nErg == 1) + { + OSQLParseNode* pTemp = $$; + $$ = pTemp->removeAt((sal_uInt32)0); + delete pTemp; + } + else + { + delete $$; + YYABORT; + } + } + else + { + YYERROR; + } + } + ; +comparison: + SQL_LESS + | SQL_NOTEQUAL + | SQL_EQUAL + | SQL_GREAT + | SQL_LESSEQ + | SQL_GREATEQ + | SQL_TOKEN_IS sql_not SQL_TOKEN_DISTINCT SQL_TOKEN_FROM + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + | SQL_TOKEN_IS sql_not + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +between_predicate_part_2: + sql_not SQL_TOKEN_BETWEEN row_value_constructor SQL_TOKEN_AND row_value_constructor + { + if (xxx_pGLOBAL_SQLPARSER->inPredicateCheck()) // between_predicate: rule 2 + { + $$ = SQL_NEW_RULE; + + sal_Int16 nErg = xxx_pGLOBAL_SQLPARSER->buildPredicateRule($$,$3,$2,$5); + if(nErg == 1) + { + OSQLParseNode* pTemp = $$; + $$ = pTemp->removeAt((sal_uInt32)0); + OSQLParseNode* pColumnRef = $$->removeAt((sal_uInt32)0); + $$->insert(0,$1); + OSQLParseNode* pBetween_predicate = new OSQLInternalNode("", SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::between_predicate)); + pBetween_predicate->append(pColumnRef); + pBetween_predicate->append($$); + $$ = pBetween_predicate; + + delete pTemp; + delete $4; + } + else + { + delete $$; + YYABORT; + } + } + else + { + $$ = SQL_NEW_RULE; // between_predicate: rule 1 + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + } + } +between_predicate: + row_value_constructor between_predicate_part_2 + { + $$ = SQL_NEW_RULE; // between_predicate: rule 1 + $$->append($1); + $$->append($2); + } + | between_predicate_part_2 + ; +character_like_predicate_part_2: + sql_not SQL_TOKEN_LIKE string_value_exp opt_escape + { + $$ = SQL_NEW_RULE; // like_predicate: rule 1 + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + ; +other_like_predicate_part_2: + sql_not SQL_TOKEN_LIKE value_exp_primary opt_escape + { + $$ = SQL_NEW_RULE; // like_predicate: rule 1 + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + ; +like_predicate: + row_value_constructor character_like_predicate_part_2 + { + $$ = SQL_NEW_RULE; // like_predicate: rule 1 + $$->append($1); + $$->append($2); + } + | row_value_constructor other_like_predicate_part_2 + { + $$ = SQL_NEW_RULE; // like_predicate: rule 3 + $$->append($1); + $$->append($2); + } + | character_like_predicate_part_2 + { + if (xxx_pGLOBAL_SQLPARSER->inPredicateCheck()) // like_predicate: rule 5 + { + OSQLParseNode* pColumnRef = newNode("", SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::column_ref)); + pColumnRef->append(newNode(xxx_pGLOBAL_SQLPARSER->getFieldName(),SQLNodeType::Name)); + + $$ = SQL_NEW_RULE; + $$->append(pColumnRef); + $$->append($1); + OSQLParseNode* p2nd = $1->removeAt(2); + OSQLParseNode* p3rd = $1->removeAt(2); + if ( !xxx_pGLOBAL_SQLPARSER->buildLikeRule($1,p2nd,p3rd) ) + { + delete $$; + YYABORT; + } + $1->append(p3rd); + } + else + YYERROR; + } + | other_like_predicate_part_2 + { + if (xxx_pGLOBAL_SQLPARSER->inPredicateCheck()) // like_predicate: rule 6 + { + OSQLParseNode* pColumnRef = newNode("", SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::column_ref)); + pColumnRef->append(newNode(xxx_pGLOBAL_SQLPARSER->getFieldName(),SQLNodeType::Name)); + + $$ = SQL_NEW_RULE; + $$->append(pColumnRef); + $$->append($1); + OSQLParseNode* p2nd = $1->removeAt(2); + OSQLParseNode* p3rd = $1->removeAt(2); + if ( !xxx_pGLOBAL_SQLPARSER->buildLikeRule($1,p2nd,p3rd) ) + { + delete $$; + YYABORT; + } + $1->append(p3rd); + } + else + YYERROR; + } + ; + +opt_escape: + /* empty */ {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_ESCAPE string_value_exp + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + | '{' SQL_TOKEN_ESCAPE SQL_TOKEN_STRING '}' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("{", SQLNodeType::Punctuation)); + $$->append($2); + $$->append($3); + $$->append(newNode("}", SQLNodeType::Punctuation)); + } + ; + +null_predicate_part_2: + SQL_TOKEN_IS sql_not SQL_TOKEN_NULL + { + $$ = SQL_NEW_RULE; // test_for_null: rule 1 + $$->append($1); + $$->append($2); + $$->append($3); + } + | SQL_TOKEN_IS sql_not SQL_TOKEN_UNKNOWN + { + $$ = SQL_NEW_RULE; // test_for_null: rule 1 + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +test_for_null: + row_value_constructor null_predicate_part_2 + { + $$ = SQL_NEW_RULE; // test_for_null: rule 1 + $$->append($1); + $$->append($2); + } + | null_predicate_part_2 + { + if (xxx_pGLOBAL_SQLPARSER->inPredicateCheck())// test_for_null: rule 2 + { + OSQLParseNode* pColumnRef = newNode("", SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::column_ref)); + pColumnRef->append(newNode(xxx_pGLOBAL_SQLPARSER->getFieldName(),SQLNodeType::Name)); + + $$ = SQL_NEW_RULE; + $$->append(pColumnRef); + $$->append($1); + } + else + YYERROR; + } + ; +in_predicate_value: + subquery + {$$ = SQL_NEW_RULE; + $$->append($1); + } + | '(' value_exp_commalist ')' + {$$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +in_predicate_part_2: + sql_not SQL_TOKEN_IN in_predicate_value + { + $$ = SQL_NEW_RULE;// in_predicate: rule 1 + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +in_predicate: + row_value_constructor in_predicate_part_2 + { + $$ = SQL_NEW_RULE;// in_predicate: rule 1 + $$->append($1); + $$->append($2); + } + | in_predicate_part_2 + { + if ( xxx_pGLOBAL_SQLPARSER->inPredicateCheck() )// in_predicate: rule 2 + { + OSQLParseNode* pColumnRef = newNode("", SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::column_ref)); + pColumnRef->append(newNode(xxx_pGLOBAL_SQLPARSER->getFieldName(),SQLNodeType::Name)); + + $$ = SQL_NEW_RULE; + $$->append(pColumnRef); + $$->append($1); + } + else + YYERROR; + } + ; +quantified_comparison_predicate_part_2: + comparison any_all_some subquery + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +all_or_any_predicate: + row_value_constructor quantified_comparison_predicate_part_2 + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | quantified_comparison_predicate_part_2 + { + if(xxx_pGLOBAL_SQLPARSER->inPredicateCheck()) + { + OSQLParseNode* pColumnRef = newNode("", SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::column_ref)); + pColumnRef->append(newNode(xxx_pGLOBAL_SQLPARSER->getFieldName(),SQLNodeType::Name)); + + $$ = SQL_NEW_RULE; + $$->append(pColumnRef); + $$->append($1); + } + else + YYERROR; + } + ; + +any_all_some: + SQL_TOKEN_ANY + | SQL_TOKEN_ALL + | SQL_TOKEN_SOME + ; + +existence_test: + SQL_TOKEN_EXISTS subquery + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + ; +unique_test: + SQL_TOKEN_UNIQUE subquery + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2);} + ; +subquery: + '(' query_exp ')' + {$$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation));} + ; + + /* scalar expressions */ +scalar_exp_commalist: + select_sublist + { + $$ = SQL_NEW_COMMALISTRULE; + $$->append($1); + } + | scalar_exp_commalist ',' select_sublist + { + $1->append($3); + $$ = $1; + } + ; +select_sublist: +/* table_node '.' '*' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append(newNode("*", SQLNodeType::Punctuation)); + } +*/ + derived_column + + ; + +parameter_ref: + parameter + ; + +/* +op_like: + '*' + { + $$ = newNode("*", SQLNodeType::Punctuation); + } + | '?' + { + $$ = newNode("?", SQLNodeType::Punctuation); + } + | op_like '*' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("*", SQLNodeType::Punctuation)); + xxx_pGLOBAL_SQLPARSER->reduceLiteral($$, sal_False); + } + | op_like '?' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("?", SQLNodeType::Punctuation)); + xxx_pGLOBAL_SQLPARSER->reduceLiteral($$, sal_False); + } + ; +*/ + +literal: +/* SQL_TOKEN_STRING + | */SQL_TOKEN_INT + | SQL_TOKEN_REAL_NUM + | SQL_TOKEN_INTNUM + | SQL_TOKEN_APPROXNUM + | SQL_TOKEN_ACCESS_DATE +/* rules for predicate check */ + | literal SQL_TOKEN_STRING + { + if (xxx_pGLOBAL_SQLPARSER->inPredicateCheck()) + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + OSQLParser::reduceLiteral($$, true); + } + else + YYERROR; + } + | literal SQL_TOKEN_INT + { + if (xxx_pGLOBAL_SQLPARSER->inPredicateCheck()) + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + OSQLParser::reduceLiteral($$, true); + } + else + YYERROR; + } + | literal SQL_TOKEN_REAL_NUM + { + if (xxx_pGLOBAL_SQLPARSER->inPredicateCheck()) + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + OSQLParser::reduceLiteral($$, true); + } + else + YYERROR; + } + | literal SQL_TOKEN_APPROXNUM + { + if (xxx_pGLOBAL_SQLPARSER->inPredicateCheck()) + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + OSQLParser::reduceLiteral($$, true); + } + else + YYERROR; + } + ; + + /* miscellaneous */ +as_clause: + /* empty */ {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_AS column + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | column + ; +position_exp: + SQL_TOKEN_POSITION '(' value_exp SQL_TOKEN_IN value_exp ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | SQL_TOKEN_POSITION '(' value_exp_commalist ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +num_value_fct: + position_exp + | extract_exp + | length_exp + ; +char_length_exp: + SQL_TOKEN_CHAR_LENGTH '(' value_exp ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | SQL_TOKEN_SQL_TOKEN_INTNUM '(' value_exp ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + + ; +octet_length_exp: + SQL_TOKEN_OCTET_LENGTH '(' value_exp ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +bit_length_exp: + SQL_TOKEN_BIT_LENGTH '(' value_exp ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +length_exp: + char_length_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | octet_length_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | bit_length_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; +datetime_field: + non_second_datetime_field + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | SQL_TOKEN_SECOND + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; +extract_field: + time_zone_field + | datetime_field + | value_exp + ; +time_zone_field: + SQL_TOKEN_TIMEZONE_HOUR + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | SQL_TOKEN_TIMEZONE_MINUTE + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; +extract_exp: + SQL_TOKEN_EXTRACT '(' extract_field SQL_TOKEN_FROM value_exp ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +unsigned_value_spec: + general_value_spec + | literal + ; +general_value_spec: + parameter + | SQL_TOKEN_USER + | SQL_TOKEN_NULL + | SQL_TOKEN_FALSE + | SQL_TOKEN_TRUE + | SQL_TOKEN_VALUE + | SQL_TOKEN_CURRENT_CATALOG + | SQL_TOKEN_CURRENT_DEFAULT_TRANSFORM_GROUP + | SQL_TOKEN_CURRENT_PATH + | SQL_TOKEN_CURRENT_ROLE + | SQL_TOKEN_CURRENT_SCHEMA + | SQL_TOKEN_CURRENT_USER + | SQL_TOKEN_SESSION_USER + | SQL_TOKEN_SYSTEM_USER + ; +set_fct_spec: + general_set_fct + | '{' odbc_fct_spec '}' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("{", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode("}", SQLNodeType::Punctuation)); + } + | function_name '(' ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | function_name0 '(' ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | function_name1 '(' function_arg ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | function_name2 '(' function_arg_commalist2 ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | function_name3 '(' function_arg_commalist3 ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | string_function_4Argument '(' function_arg_commalist4 ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | function_name '(' function_args_commalist ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | function_name12 '(' function_args_commalist ')' + { + if ( $3->count() == 1 || $3->count() == 2 ) + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + else + YYERROR; + } + | function_name23 '(' function_args_commalist ')' + { + if ( $3->count() == 2 || $3->count() == 3) + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + else + YYERROR; + } + ; +function_name0: + date_function_0Argument + | numeric_function_0Argument + ; +function_name1: + string_function_1Argument + | date_function_1Argument + | numeric_function_1Argument + ; +function_name2: + string_function_2Argument + | numeric_function_2Argument + ; +function_name12: + SQL_TOKEN_ROUND + | SQL_TOKEN_WEEK + | SQL_TOKEN_LOGF + | SQL_TOKEN_LOG + ; +function_name23: + SQL_TOKEN_LOCATE + | SQL_TOKEN_DATEDIFF + ; +function_name3: + string_function_3Argument + ; +function_name: + string_function + | date_function + | numeric_function + | SQL_TOKEN_NAME + ; +string_function_1Argument: + SQL_TOKEN_LENGTH + | SQL_TOKEN_ASCII + | SQL_TOKEN_LCASE + | SQL_TOKEN_LTRIM + | SQL_TOKEN_RTRIM + | SQL_TOKEN_SPACE + | SQL_TOKEN_UCASE + ; + +string_function_2Argument: + SQL_TOKEN_REPEAT + | SQL_TOKEN_LEFT + | SQL_TOKEN_RIGHT + ; +string_function_3Argument: + SQL_TOKEN_REPLACE + ; +string_function_4Argument: + SQL_TOKEN_INSERT + ; + +string_function: + SQL_TOKEN_CHAR + | SQL_TOKEN_CONCAT + | SQL_TOKEN_DIFFERENCE + | SQL_TOKEN_LOCATE_2 + | SQL_TOKEN_SOUNDEX + ; +date_function_0Argument: + SQL_TOKEN_CURDATE + | SQL_TOKEN_CURTIME + | SQL_TOKEN_NOW + ; +date_function_1Argument: + SQL_TOKEN_DAYOFWEEK + | SQL_TOKEN_DAYOFMONTH + | SQL_TOKEN_DAYOFYEAR + | SQL_TOKEN_MONTH + | SQL_TOKEN_DAYNAME + | SQL_TOKEN_MONTHNAME + | SQL_TOKEN_QUARTER + | SQL_TOKEN_HOUR + | SQL_TOKEN_MINUTE + | SQL_TOKEN_SECOND + | SQL_TOKEN_YEAR + | SQL_TOKEN_DAY + | SQL_TOKEN_TIMEVALUE + | SQL_TOKEN_DATEVALUE + ; + +date_function: + SQL_TOKEN_TIMESTAMPADD + | SQL_TOKEN_TIMESTAMPDIFF + ; +numeric_function_0Argument: + SQL_TOKEN_PI + ; +numeric_function_1Argument: + SQL_TOKEN_ABS + | SQL_TOKEN_ACOS + | SQL_TOKEN_ASIN + | SQL_TOKEN_ATAN + | SQL_TOKEN_CEILING + | SQL_TOKEN_COS + | SQL_TOKEN_COT + | SQL_TOKEN_DEGREES + | SQL_TOKEN_FLOOR + | SQL_TOKEN_SIGN + | SQL_TOKEN_SIN + | SQL_TOKEN_SQRT + | SQL_TOKEN_TAN + | SQL_TOKEN_EXP + | SQL_TOKEN_LOG10 + | SQL_TOKEN_LN + | SQL_TOKEN_RADIANS + | SQL_TOKEN_ROUNDMAGIC + ; +numeric_function_2Argument: + SQL_TOKEN_ATAN2 + | SQL_TOKEN_MOD + | SQL_TOKEN_POWER + ; +numeric_function: + SQL_TOKEN_RAND + | SQL_TOKEN_TRUNCATE + ; + +window_function: + window_function_type SQL_TOKEN_OVER window_name_or_specification + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +window_function_type : + rank_function_type '(' ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | SQL_TOKEN_ROW_NUMBER '(' ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | general_set_fct + | ntile_function + | lead_or_lag_function + | first_or_last_value_function + | nth_value_function +; +ntile_function : + SQL_TOKEN_NTILE '(' number_of_tiles ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +dynamic_parameter_specification: + parameter + ; +simple_value_specification: + literal + ; +number_of_tiles : + simple_value_specification + | dynamic_parameter_specification + ; +opt_lead_or_lag_function: + /* empty */ {$$ = SQL_NEW_RULE;} + | ',' offset + { + $$ = SQL_NEW_RULE; + $$->append(newNode(",", SQLNodeType::Punctuation)); + $$->append($2); + } + | ',' offset ',' default_expression + { + $$ = SQL_NEW_RULE; + $$->append(newNode(",", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(",", SQLNodeType::Punctuation)); + $$->append($4); + } + ; +opt_null_treatment: + /* empty */ {$$ = SQL_NEW_RULE;} + | null_treatment + ; + +lead_or_lag_function: + lead_or_lag '(' lead_or_lag_extent opt_lead_or_lag_function ')' opt_null_treatment + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append($4); + $$->append(newNode(")", SQLNodeType::Punctuation)); + $$->append($6); + } + ; +lead_or_lag: + SQL_TOKEN_LEAD + | SQL_TOKEN_LAG + ; +lead_or_lag_extent: + value_exp + ; +offset: + SQL_TOKEN_INTNUM + ; +default_expression: + value_exp + ; +null_treatment: + SQL_TOKEN_RESPECT SQL_TOKEN_NULLS + | SQL_TOKEN_IGNORE SQL_TOKEN_NULLS + ; +first_or_last_value_function: + first_or_last_value '(' value_exp ')' opt_null_treatment + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + $$->append($5); + } + ; +first_or_last_value : + SQL_TOKEN_FIRST_VALUE + | SQL_TOKEN_LAST_VALUE + ; +opt_from_first_or_last: + /* empty */ {$$ = SQL_NEW_RULE;} + | from_first_or_last + ; +nth_value_function: + SQL_TOKEN_NTH_VALUE '(' value_exp ',' nth_row ')' opt_from_first_or_last opt_null_treatment + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(",", SQLNodeType::Punctuation)); + $$->append($5); + $$->append(newNode(")", SQLNodeType::Punctuation)); + $$->append($7); + $$->append($8); + } + ; +nth_row: + simple_value_specification + | dynamic_parameter_specification + ; +from_first_or_last: + SQL_TOKEN_FROM SQL_TOKEN_FIRST + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | SQL_TOKEN_FROM SQL_TOKEN_LAST + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +window_name: + SQL_TOKEN_NAME + ; +window_name_or_specification: + window_name + | in_line_window_specification + ; +in_line_window_specification: + window_specification + ; +opt_window_clause: + /* empty */ {$$ = SQL_NEW_RULE;} + | window_clause + ; +window_clause: + SQL_TOKEN_WINDOW window_definition_list + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +window_definition_list: + window_definition_list ',' window_definition + {$1->append($3); + $$ = $1;} + | window_definition + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + ; +window_definition: + new_window_name SQL_TOKEN_AS window_specification + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +new_window_name: + window_name + ; +window_specification: + '(' + opt_existing_window_name + opt_window_partition_clause + opt_order_by_clause + opt_window_frame_clause + ')' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +opt_existing_window_name: + /* empty */ {$$ = SQL_NEW_RULE;} + | existing_window_name + ; +opt_window_partition_clause: + /* empty */ {$$ = SQL_NEW_RULE;} + | window_partition_clause + ; +opt_window_frame_clause: + /* empty */ {$$ = SQL_NEW_RULE;} + | window_frame_clause + ; +existing_window_name: + window_name + ; +window_partition_clause: + SQL_TOKEN_PARTITION SQL_TOKEN_BY window_partition_column_reference_list + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +window_partition_column_reference_list: + window_partition_column_reference_list ',' window_partition_column_reference + {$1->append($3); + $$ = $1;} + | window_partition_column_reference + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + ; +window_partition_column_reference: + column_ref opt_collate_clause + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +opt_window_frame_exclusion: + /* empty */ {$$ = SQL_NEW_RULE;} + | window_frame_exclusion + ; +window_frame_clause: + window_frame_units window_frame_extent opt_window_frame_exclusion + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +window_frame_units: + SQL_TOKEN_ROWS + | SQL_TOKEN_RANGE + ; +window_frame_extent: + window_frame_start + | window_frame_between + ; +window_frame_start: + SQL_TOKEN_UNBOUNDED SQL_TOKEN_PRECEDING + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | window_frame_preceding + | SQL_TOKEN_CURRENT SQL_TOKEN_ROW + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +window_frame_preceding: + unsigned_value_spec SQL_TOKEN_PRECEDING + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +window_frame_between: + SQL_TOKEN_BETWEEN window_frame_bound_1 SQL_TOKEN_AND window_frame_bound_2 + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + ; +window_frame_bound_1: + window_frame_bound + ; +window_frame_bound_2: + window_frame_bound + ; +window_frame_bound: + window_frame_start + | SQL_TOKEN_UNBOUNDED SQL_TOKEN_FOLLOWING + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | window_frame_following + ; +window_frame_following: + unsigned_value_spec SQL_TOKEN_FOLLOWING + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +window_frame_exclusion: + SQL_TOKEN_EXCLUDE SQL_TOKEN_CURRENT SQL_TOKEN_ROW + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | SQL_TOKEN_EXCLUDE SQL_TOKEN_GROUP + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | SQL_TOKEN_EXCLUDE SQL_TOKEN_TIES + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | SQL_TOKEN_EXCLUDE SQL_TOKEN_NO SQL_TOKEN_OTHERS + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +op_parameter: + {$$ = SQL_NEW_RULE;} + | '?' SQL_EQUAL + { + $$ = SQL_NEW_RULE; + $$->append(newNode("?", SQLNodeType::Punctuation)); + $$->append($2); + } + ; +odbc_call_spec: + op_parameter SQL_TOKEN_CALL table_node op_odbc_call_parameter + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + ; + +op_odbc_call_parameter: + {$$ = SQL_NEW_RULE;} + | '(' odbc_parameter_commalist ')' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; + +odbc_parameter_commalist: + odbc_parameter + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | odbc_parameter_commalist ',' odbc_parameter + { + $1->append($3); + $$ = $1; + } + ; +odbc_parameter: + /* empty */ {$$ = SQL_NEW_RULE;} + | literal + | parameter + ; + +odbc_fct_spec: + odbc_fct_type SQL_TOKEN_STRING + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | SQL_TOKEN_FN set_fct_spec + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; + +odbc_fct_type: + SQL_TOKEN_D + | SQL_TOKEN_T + | SQL_TOKEN_TS + ; + +general_set_fct: + set_fct_type '(' opt_all_distinct function_arg ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append($4); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | SQL_TOKEN_COUNT '(' '*' ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append(newNode("*", SQLNodeType::Punctuation)); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | SQL_TOKEN_COUNT '(' opt_all_distinct function_arg ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append($4); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | ordered_set_function + | array_aggregate_function + ; +set_fct_type: + SQL_TOKEN_AVG + | SQL_TOKEN_MAX + | SQL_TOKEN_MIN + | SQL_TOKEN_SUM + | SQL_TOKEN_EVERY + | SQL_TOKEN_ANY + | SQL_TOKEN_SOME + | SQL_TOKEN_STDDEV_POP + | SQL_TOKEN_STDDEV_SAMP + | SQL_TOKEN_VAR_SAMP + | SQL_TOKEN_VAR_POP + | SQL_TOKEN_COLLECT + | SQL_TOKEN_FUSION + | SQL_TOKEN_INTERSECTION + ; + +ordered_set_function: + hypothetical_set_function + | inverse_distribution_function + ; +hypothetical_set_function: + rank_function_type '(' hypothetical_set_function_value_expression_list ')' within_group_specification + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + $$->append($5); + } + | rank_function_type '(' hypothetical_set_function_value_expression_list SQL_TOKEN_BY value_exp_commalist ')' within_group_specification + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append(newNode(")", SQLNodeType::Punctuation)); + $$->append($7); + } + ; + +within_group_specification: + { + $$ = SQL_NEW_RULE; + } + | SQL_TOKEN_WITHIN SQL_TOKEN_GROUP '(' opt_order_by_clause ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($4); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +hypothetical_set_function_value_expression_list: + value_exp_commalist + ; + +inverse_distribution_function: + inverse_distribution_function_type '('inverse_distribution_function_argument ')' within_group_specification + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +inverse_distribution_function_argument: + num_value_exp + ; +inverse_distribution_function_type: + SQL_TOKEN_PERCENTILE_CONT + | SQL_TOKEN_PERCENTILE_DISC + ; + +array_aggregate_function: + SQL_TOKEN_ARRAY_AGG '(' value_exp opt_order_by_clause ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append($4); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; + +rank_function_type: + SQL_TOKEN_RANK + | SQL_TOKEN_DENSE_RANK + | SQL_TOKEN_PERCENT_RANK + | SQL_TOKEN_CUME_DIST + ; +outer_join_type: + SQL_TOKEN_LEFT %prec SQL_TOKEN_LEFT + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | SQL_TOKEN_RIGHT %prec SQL_TOKEN_RIGHT + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | SQL_TOKEN_FULL %prec SQL_TOKEN_FULL + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; +join_condition: + SQL_TOKEN_ON search_condition + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +join_spec: + join_condition + | named_columns_join + ; +join_type: + /* empty */ {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_INNER + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | outer_join_type + | outer_join_type SQL_TOKEN_OUTER + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +cross_union: + table_ref SQL_TOKEN_CROSS SQL_TOKEN_JOIN table_ref + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + ; + +qualified_join: + /* when SQL_TOKEN_NATURAL, then no join_spec */ + table_ref SQL_TOKEN_NATURAL join_type SQL_TOKEN_JOIN table_ref + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + } + | table_ref join_type SQL_TOKEN_JOIN table_ref join_spec + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + } + | cross_union + ; +joined_table: + qualified_join + ; +named_columns_join: + SQL_TOKEN_USING '(' column_commalist ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +simple_table: + select_statement + | values_or_query_spec + ; + +non_join_query_primary: + simple_table + | '(' non_join_query_exp ')' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +non_join_query_term: + non_join_query_primary + | query_term SQL_TOKEN_INTERSECT all query_primary + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + ; +query_primary: + non_join_query_primary + ; +non_join_query_exp: + non_join_query_term + | query_exp SQL_TOKEN_UNION all query_term + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + | query_exp SQL_TOKEN_EXCEPT all query_term + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + ; +all: + /* empty*/ {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_ALL + ; +query_exp: + non_join_query_exp /*[^')']*/ + ; +scalar_subquery: + subquery + ; +cast_operand: + value_exp + ; +cast_target: + table_node + | data_type + ; +cast_spec: + SQL_TOKEN_CAST '(' cast_operand SQL_TOKEN_AS cast_target ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +value_exp_primary: + unsigned_value_spec + | column_ref + | set_fct_spec + | scalar_subquery + | case_expression + | window_function + | '(' value_exp ')' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | cast_spec + ; + +num_primary: + value_exp_primary + | num_value_fct + ; +factor: + num_primary + | '-' num_primary %prec SQL_TOKEN_UMINUS + { + $$ = SQL_NEW_RULE; + $$->append(newNode("-", SQLNodeType::Punctuation)); + $$->append($2); + } + | '+' num_primary %prec SQL_TOKEN_UMINUS + { + $$ = SQL_NEW_RULE; + $$->append(newNode("+", SQLNodeType::Punctuation)); + $$->append($2); + } + ; + +term: + factor + | term '*' factor + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("*", SQLNodeType::Punctuation)); + $$->append($3); + } + | term '/' factor + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("/", SQLNodeType::Punctuation)); + $$->append($3); + } + ; + +num_value_exp: + term + | num_value_exp '+' term + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("+", SQLNodeType::Punctuation)); + $$->append($3); + } + | num_value_exp '-' term + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("-", SQLNodeType::Punctuation)); + $$->append($3); + } + ; +datetime_primary: +/* value_exp_primary + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + |*/ datetime_value_fct + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; +datetime_value_fct: + SQL_TOKEN_CURRENT_DATE + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | SQL_TOKEN_CURRENT_TIME + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | SQL_TOKEN_CURRENT_TIMESTAMP + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; +time_zone: + SQL_TOKEN_AT time_zone_specifier + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +time_zone_specifier: + SQL_TOKEN_LOCAL + { + $$ = SQL_NEW_RULE; + $$->append($1); + } +/* | SQL_TOKEN_TIME SQL_TOKEN_ZONE interval_value_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + }*/ + ; +datetime_factor: + datetime_primary + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | datetime_primary time_zone + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +datetime_term: + datetime_factor + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; +/* +interval_term: + literal + | interval_term '*' factor + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("*", SQLNodeType::Punctuation)); + $$->append($3); + } + | interval_term '/' factor + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("/", SQLNodeType::Punctuation)); + $$->append($3); + } + ; +*/ +datetime_value_exp: + datetime_term + { + $$ = SQL_NEW_RULE; + $$->append($1); + } +/* | interval_value_exp '+' datetime_term + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("+", SQLNodeType::Punctuation)); + $$->append($3); + } + | datetime_value_exp '+' interval_term + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("+", SQLNodeType::Punctuation)); + $$->append($3); + } + | datetime_value_exp '-' interval_term + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("-", SQLNodeType::Punctuation)); + $$->append($3); + } +*/ ; +/* +interval_value_exp: + interval_term + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | interval_value_exp '+' interval_term + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("+", SQLNodeType::Punctuation)); + $$->append($3); + } + | interval_value_exp '-' interval_term + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("-", SQLNodeType::Punctuation)); + $$->append($3); + } + | '(' datetime_value_exp '-' datetime_term ')' interval_qualifier + { + $$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode("-", SQLNodeType::Punctuation)); + $$->append($4); + $$->append(newNode(")", SQLNodeType::Punctuation)); + $$->append($6); + } + ; +*/ +non_second_datetime_field: + SQL_TOKEN_YEAR + | SQL_TOKEN_MONTH + | SQL_TOKEN_DAY + | SQL_TOKEN_HOUR + | SQL_TOKEN_MINUTE + ; +start_field: + non_second_datetime_field opt_paren_precision + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +end_field: + non_second_datetime_field + | SQL_TOKEN_SECOND opt_paren_precision + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; + +single_datetime_field: + non_second_datetime_field opt_paren_precision + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | SQL_TOKEN_SECOND opt_paren_precision_scale + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; + +interval_qualifier: + start_field SQL_TOKEN_TO end_field + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | single_datetime_field + ; + +function_arg_commalist2: + function_arg ',' function_arg + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1); + $$->append($3);} + ; +function_arg_commalist3: + function_arg ',' function_arg ',' function_arg + { + $$ = SQL_NEW_COMMALISTRULE; + $$->append($1); + $$->append($3); + $$->append($5); + } + ; +function_arg_commalist4: + function_arg ',' function_arg ',' function_arg ',' function_arg + { + $$ = SQL_NEW_COMMALISTRULE; + $$->append($1); + $$->append($3); + $$->append($5); + $$->append($7); + } + ; +value_exp_commalist: + value_exp + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | value_exp_commalist ',' value_exp + {$1->append($3); + $$ = $1;} + /* this rule is only valid if we check predicates */ + | value_exp_commalist ';' value_exp + { + if (xxx_pGLOBAL_SQLPARSER->inPredicateCheck()) + { + $1->append($3); + $$ = $1; + } + else + YYERROR; + } + ; +function_arg: + result + | value_exp comparison value_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | value_exp SQL_TOKEN_USING value_exp comparison value_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + | value_exp SQL_TOKEN_BY value_exp_commalist + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +function_args_commalist: + function_arg + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | function_args_commalist ',' function_arg + {$1->append($3); + $$ = $1;} + /* this rule is only valid if we check predicates */ + | function_args_commalist ';' function_arg + { + if (xxx_pGLOBAL_SQLPARSER->inPredicateCheck()) + { + $1->append($3); + $$ = $1; + } + else + YYERROR; + } + ; + +value_exp: + num_value_exp /*[^')']*/ + | string_value_exp + | datetime_value_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; +string_value_exp: + char_value_exp +/* | bit_value_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + } +*/ ; +char_value_exp: + char_factor + | concatenation + ; +concatenation: + char_value_exp '+' char_factor + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("+", SQLNodeType::Punctuation)); + $$->append($3); + } + | value_exp SQL_CONCAT value_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; + +char_primary: + SQL_TOKEN_STRING + | string_value_fct + ; +collate_clause: + SQL_TOKEN_COLLATE table_node + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +char_factor: + char_primary + | char_primary collate_clause + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +string_value_fct: + char_value_fct + | bit_value_fct + ; +bit_value_fct: + bit_substring_fct + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; +bit_substring_fct: + SQL_TOKEN_SUBSTRING '(' bit_value_exp SQL_TOKEN_FROM string_value_exp for_length ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append($6); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +bit_value_exp: + bit_factor + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; +/* + bit_concatenation + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | +bit_concatenation: + bit_value_exp '+' bit_factor + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("+", SQLNodeType::Punctuation)); + $$->append($3); + } + ; +*/ +bit_factor: + bit_primary + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; +bit_primary: + {$$ = SQL_NEW_RULE;} +/* value_exp_primary + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | string_value_fct + { + $$ = SQL_NEW_RULE; + $$->append($1); + }*/ + ; +char_value_fct: + char_substring_fct + | fold + | form_conversion + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | char_translation + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + | trim_fct + { + $$ = SQL_NEW_RULE; + $$->append($1); + } + ; +for_length: + {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_FOR value_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +char_substring_fct: + SQL_TOKEN_SUBSTRING '(' value_exp SQL_TOKEN_FROM value_exp for_length ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append($6); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | SQL_TOKEN_SUBSTRING '(' value_exp_commalist ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +upper_lower: + SQL_TOKEN_UPPER + | SQL_TOKEN_LOWER + ; +fold: + upper_lower '(' value_exp ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +form_conversion: + SQL_TOKEN_CONVERT '(' string_value_exp SQL_TOKEN_USING table_node ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | SQL_TOKEN_CONVERT '(' cast_operand ',' cast_target ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(",", SQLNodeType::Punctuation)); + $$->append($5); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +char_translation: + SQL_TOKEN_TRANSLATE '(' string_value_exp SQL_TOKEN_USING table_node ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +trim_fct: + SQL_TOKEN_TRIM '(' trim_operands ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +trim_operands: + trim_spec value_exp SQL_TOKEN_FROM value_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + | trim_spec SQL_TOKEN_FROM value_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | value_exp SQL_TOKEN_FROM value_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | SQL_TOKEN_FROM value_exp + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | value_exp + ; + +trim_spec: + SQL_TOKEN_BOTH + | SQL_TOKEN_LEADING + | SQL_TOKEN_TRAILING + ; + +derived_column: + value_exp as_clause + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +/* Tabellenname */ +table_node: + table_name + | schema_name + | catalog_name +; +catalog_name: + SQL_TOKEN_NAME '.' schema_name + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($3); + } + | SQL_TOKEN_NAME ':' schema_name + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode(":", SQLNodeType::Punctuation)); + $$->append($3); + } +; +schema_name: + SQL_TOKEN_NAME '.' table_name + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($3); + } +; + +table_name: + SQL_TOKEN_NAME + {$$ = SQL_NEW_RULE; + $$->append($1);} +; +/* Columns */ +column_ref: + column + {$$ = SQL_NEW_RULE; + $$->append($1);} +/* | table_node '.' column_val %prec '.' + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($3);} +*/ + | SQL_TOKEN_NAME '.' column_val %prec '.' + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($3); + } + | SQL_TOKEN_NAME '.' SQL_TOKEN_NAME '.' column_val %prec '.' + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($5);} + | SQL_TOKEN_NAME '.' SQL_TOKEN_NAME '.' SQL_TOKEN_NAME '.' column_val %prec '.' + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($5); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($7); + } + | SQL_TOKEN_NAME ':' SQL_TOKEN_NAME '.' SQL_TOKEN_NAME '.' column_val %prec '.' + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode(":", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($5); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($7); + } +/* | SQL_TOKEN_NAME ';' SQL_TOKEN_NAME '.' SQL_TOKEN_NAME '.' column_val + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode(";", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($5); + $$->append(newNode(".", SQLNodeType::Punctuation)); + $$->append($7); + } +*/ ; + + /* data types */ +column_val: + column + {$$ = SQL_NEW_RULE; + $$->append($1);} + | '*' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("*", SQLNodeType::Punctuation)); + } + ; +data_type: + predefined_type + ; +opt_char_set_spec: + {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_CHARACTER SQL_TOKEN_SET SQL_TOKEN_NAME + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +opt_collate_clause: + {$$ = SQL_NEW_RULE;} + | collate_clause + ; +predefined_type: + character_string_type opt_char_set_spec opt_collate_clause + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | national_character_string_type opt_collate_clause + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | binary_string_type + | numeric_type + | boolean_type + | datetime_type + | interval_type + ; +character_string_type: + SQL_TOKEN_CHARACTER opt_paren_precision + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | SQL_TOKEN_CHAR opt_paren_precision + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | SQL_TOKEN_CHARACTER SQL_TOKEN_VARYING paren_char_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | SQL_TOKEN_CHAR SQL_TOKEN_VARYING paren_char_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | SQL_TOKEN_VARCHAR paren_char_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | character_large_object_type + ; +opt_paren_precision: + {$$ = SQL_NEW_RULE;} + | paren_char_length + ; +paren_char_length: + '(' SQL_TOKEN_INTNUM ')' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +opt_paren_char_large_length: + {$$ = SQL_NEW_RULE;} + | paren_character_large_object_length + ; +paren_character_large_object_length: + '(' large_object_length ')' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; + +large_object_length: + SQL_TOKEN_INTNUM opt_multiplier + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +opt_multiplier: + {$$ = SQL_NEW_RULE;} + | 'K' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("K", SQLNodeType::Punctuation)); + } + | 'M' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("M", SQLNodeType::Punctuation)); + } + | 'G' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("G", SQLNodeType::Punctuation)); + } + | 'T' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("T", SQLNodeType::Punctuation)); + } + | 'P' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("P", SQLNodeType::Punctuation)); + } + ; +character_large_object_type: + SQL_TOKEN_CHARACTER SQL_TOKEN_LARGE SQL_TOKEN_OBJECT opt_paren_char_large_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + | SQL_TOKEN_CHAR SQL_TOKEN_LARGE SQL_TOKEN_OBJECT opt_paren_char_large_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + | SQL_TOKEN_CLOB opt_paren_char_large_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +national_character_string_type: + SQL_TOKEN_NATIONAL SQL_TOKEN_CHARACTER opt_paren_precision + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | SQL_TOKEN_NATIONAL SQL_TOKEN_CHAR opt_paren_precision + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | SQL_TOKEN_NCHAR opt_paren_precision + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | SQL_TOKEN_NATIONAL SQL_TOKEN_CHARACTER SQL_TOKEN_VARYING paren_char_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + | SQL_TOKEN_NATIONAL SQL_TOKEN_CHAR SQL_TOKEN_VARYING paren_char_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + | SQL_TOKEN_NCHAR SQL_TOKEN_VARYING paren_char_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | national_character_large_object_type + ; +national_character_large_object_type: + SQL_TOKEN_NATIONAL SQL_TOKEN_CHARACTER SQL_TOKEN_LARGE SQL_TOKEN_OBJECT opt_paren_char_large_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + } + | SQL_TOKEN_NCHAR SQL_TOKEN_LARGE SQL_TOKEN_OBJECT opt_paren_char_large_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + | SQL_TOKEN_NCLOB opt_paren_char_large_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +binary_string_type: + SQL_TOKEN_BINARY opt_paren_precision + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | SQL_TOKEN_BINARY SQL_TOKEN_VARYING paren_char_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | SQL_TOKEN_VARBINARY paren_char_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | binary_large_object_string_type + ; +binary_large_object_string_type: + SQL_TOKEN_BINARY SQL_TOKEN_LARGE SQL_TOKEN_OBJECT opt_paren_char_large_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + | SQL_TOKEN_BLOB opt_paren_char_large_length + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +numeric_type: + exact_numeric_type + | approximate_numeric_type + ; +opt_paren_precision_scale: + {$$ = SQL_NEW_RULE;} + | '(' SQL_TOKEN_INTNUM ')' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | '(' SQL_TOKEN_INTNUM ',' SQL_TOKEN_INTNUM ')' + { + $$ = SQL_NEW_RULE; + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode(",", SQLNodeType::Punctuation)); + $$->append($4); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +exact_numeric_type: + SQL_TOKEN_NUMERIC opt_paren_precision_scale + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | SQL_TOKEN_DECIMAL opt_paren_precision_scale + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | SQL_TOKEN_DEC opt_paren_precision_scale + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + | SQL_TOKEN_SMALLINT + | SQL_TOKEN_INTEGER + | SQL_TOKEN_INT + | SQL_TOKEN_BIGINT + ; +approximate_numeric_type: + SQL_TOKEN_FLOAT '(' SQL_TOKEN_INTNUM ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | SQL_TOKEN_FLOAT + | SQL_TOKEN_REAL + | SQL_TOKEN_DOUBLE + | SQL_TOKEN_DOUBLE SQL_TOKEN_PRECISION + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +boolean_type: + SQL_TOKEN_BOOLEAN +; +datetime_type: + SQL_TOKEN_DATE + | SQL_TOKEN_TIME opt_paren_precision opt_with_or_without_time_zone + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | SQL_TOKEN_TIMESTAMP opt_paren_precision opt_with_or_without_time_zone + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +opt_with_or_without_time_zone: + {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_WITH SQL_TOKEN_TIME SQL_TOKEN_ZONE + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + | SQL_TOKEN_WITHOUT SQL_TOKEN_TIME SQL_TOKEN_ZONE + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +interval_type: + SQL_TOKEN_INTERVAL interval_qualifier + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; + /* the various things you can name */ + +column: + SQL_TOKEN_NAME + | SQL_TOKEN_POSITION + { + sal_uInt32 nNod = $$->getRuleID(); + delete $$; + $$ = newNode(OSQLParser::TokenIDToStr(nNod), SQLNodeType::Name); + } + | SQL_TOKEN_CHAR_LENGTH + { + sal_uInt32 nNod = $$->getRuleID(); + delete $$; + $$ = newNode(OSQLParser::TokenIDToStr(nNod), SQLNodeType::Name); + } + | SQL_TOKEN_EXTRACT + { + sal_uInt32 nNod = $$->getRuleID(); + delete $$; + $$ = newNode(OSQLParser::TokenIDToStr(nNod), SQLNodeType::Name); + } + ; +case_expression: + case_abbreviation + | case_specification + ; +case_abbreviation: + SQL_TOKEN_NULLIF '(' value_exp_commalist ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | SQL_TOKEN_COALESCE '(' value_exp ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + | SQL_TOKEN_COALESCE '(' value_exp_commalist ')' + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append(newNode("(", SQLNodeType::Punctuation)); + $$->append($3); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + ; +case_specification: + simple_case + | searched_case + ; +simple_case: + SQL_TOKEN_CASE case_operand simple_when_clause_list else_clause SQL_TOKEN_END + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + } + ; +searched_case: + SQL_TOKEN_CASE searched_when_clause_list else_clause SQL_TOKEN_END + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + ; +simple_when_clause_list: + simple_when_clause + { + $$ = SQL_NEW_LISTRULE; + $$->append($1); + } + | searched_when_clause_list simple_when_clause + { + $1->append($2); + $$ = $1; + } + ; +simple_when_clause: + SQL_TOKEN_WHEN when_operand_list SQL_TOKEN_THEN result + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + ; +when_operand_list: + when_operand + {$$ = SQL_NEW_COMMALISTRULE; + $$->append($1);} + | when_operand_list ',' when_operand + {$1->append($3); + $$ = $1;} + ; +when_operand: + row_value_constructor_elem + | comparison_predicate_part_2 %dprec 1 + | between_predicate_part_2 + | in_predicate_part_2 + | character_like_predicate_part_2 + | null_predicate_part_2 %dprec 2 +; +searched_when_clause_list: + searched_when_clause + { + $$ = SQL_NEW_LISTRULE; + $$->append($1); + } + | searched_when_clause_list searched_when_clause + { + $1->append($2); + $$ = $1; + } + ; +searched_when_clause: + SQL_TOKEN_WHEN search_condition SQL_TOKEN_THEN result + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + ; +else_clause: + {$$ = SQL_NEW_RULE;} + | SQL_TOKEN_ELSE result + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +result: + result_expression + ; +result_expression: + value_exp + ; +case_operand: + row_value_constructor_elem + ; + +cursor: SQL_TOKEN_NAME + {$$ = SQL_NEW_RULE; + $$->append($1);} + ; + +/*** +module: SQL_TOKEN_NAME + {$$ = SQL_NEW_RULE; + $$->append($1);} + ; +***/ + +parameter: + ':' SQL_TOKEN_NAME + {$$ = SQL_NEW_RULE; + $$->append(newNode(":", SQLNodeType::Punctuation)); + $$->append($2);} + | '?' + {$$ = SQL_NEW_RULE; // test + $$->append(newNode("?", SQLNodeType::Punctuation));} + | '[' SQL_TOKEN_NAME ']' + {$$ = SQL_NEW_RULE; + $$->append(newNode("[", SQLNodeType::Punctuation)); + $$->append($2); + $$->append(newNode("]", SQLNodeType::Punctuation));} + ; + +/*** +procedure: SQL_TOKEN_NAME + {$$ = SQL_NEW_RULE; + $$->append($1);} + ; +***/ + +range_variable: + {$$ = SQL_NEW_RULE;} + | opt_as SQL_TOKEN_NAME + {$$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; + +user: SQL_TOKEN_NAME + ; + +/* PREDICATECHECK RULES */ +sql: + search_condition /* checking predicats */ + { + if (xxx_pGLOBAL_SQLPARSER->inPredicateCheck()) // sql: rule 1 + { + $$ = $1; + if ( SQL_ISRULE($$,search_condition) ) + { + $$->insert(0,newNode("(", SQLNodeType::Punctuation)); + $$->append(newNode(")", SQLNodeType::Punctuation)); + } + } + else + YYERROR; + } + | '(' sql ')' /* checking predicats */ + ; +trigger_definition: + SQL_TOKEN_CREATE SQL_TOKEN_TRIGGER trigger_name trigger_action_time trigger_event SQL_TOKEN_ON table_name op_referencing triggered_action + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + $$->append($5); + $$->append($6); + $$->append($7); + $$->append($8); + $$->append($9); + } + ; +op_referencing: + { + $$ = SQL_NEW_RULE; + } + | SQL_TOKEN_REFERENCING transition_table_or_variable_list + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +trigger_action_time: + SQL_TOKEN_BEFORE + | SQL_TOKEN_AFTER + | SQL_TOKEN_INSTEAD SQL_TOKEN_OF + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } +; +trigger_event: + SQL_TOKEN_INSERT + | SQL_TOKEN_DELETE + | SQL_TOKEN_UPDATE op_trigger_columnlist + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +op_trigger_columnlist: + { + $$ = SQL_NEW_RULE; + } + | SQL_TOKEN_OF trigger_column_list + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +trigger_column_list: + column_commalist + ; +triggered_action: + op_triggered_action_for triggered_when_clause triggered_SQL_statement + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +op_triggered_action_for: + { + $$ = SQL_NEW_RULE; + } + | SQL_TOKEN_FOR SQL_TOKEN_EACH trigger_for + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + } + ; +trigger_for: + SQL_TOKEN_ROW + | SQL_TOKEN_STATEMENT + ; +triggered_when_clause: + { + $$ = SQL_NEW_RULE; + } + | SQL_TOKEN_WHEN parenthesized_boolean_value_expression + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + } + ; +triggered_SQL_statement: + SQL_procedure_statement + | SQL_TOKEN_BEGIN SQL_TOKEN_ATOMIC SQL_procedure_statement_list ';' SQL_TOKEN_END + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append(newNode(";", SQLNodeType::Punctuation)); + $$->append($5); + } + ; +SQL_procedure_statement_list: + SQL_procedure_statement + { + $$ = SQL_NEW_LISTRULE; + $$->append($1); + } + | SQL_procedure_statement_list ';' SQL_procedure_statement + { + $1->append($3); + $$ = $1; + } + ; +SQL_procedure_statement: + sql + ; + +transition_table_or_variable_list: + transition_table_or_variable + { + $$ = SQL_NEW_LISTRULE; + $$->append($1); + } + | transition_table_or_variable_list transition_table_or_variable + { + $1->append($2); + $$ = $1; + } + ; + +transition_table_or_variable: + SQL_TOKEN_OLD opt_row opt_as old_transition_variable_name + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + | SQL_TOKEN_NEW opt_row opt_as new_transition_variable_name + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + | SQL_TOKEN_OLD SQL_TOKEN_TABLE opt_as old_transition_table_name + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } + | SQL_TOKEN_NEW SQL_TOKEN_TABLE opt_as new_transition_table_name + { + $$ = SQL_NEW_RULE; + $$->append($1); + $$->append($2); + $$->append($3); + $$->append($4); + } +; +old_transition_table_name: + transition_table_name +; +new_transition_table_name: + transition_table_name +; +transition_table_name: + SQL_TOKEN_NAME +; +old_transition_variable_name: + SQL_TOKEN_NAME +; +new_transition_variable_name: + SQL_TOKEN_NAME +; +trigger_name: + SQL_TOKEN_NAME +; +%% + +#if defined _MSC_VER +#pragma warning(pop) +#endif + +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::osl; +using namespace ::dbtools; + +connectivity::OSQLParser* xxx_pGLOBAL_SQLPARSER; + +connectivity::OSQLInternalNode* newNode(const char* pNewValue, + const connectivity::SQLNodeType eNodeType, + const sal_uInt32 nNodeID) +{ + return new connectivity::OSQLInternalNode(pNewValue, eNodeType, nNodeID); +} + +connectivity::OSQLInternalNode* newNode(const OString& _newValue, + const connectivity::SQLNodeType eNodeType, + const sal_uInt32 nNodeID) +{ + return new connectivity::OSQLInternalNode(_newValue, eNodeType, nNodeID); +} + +connectivity::OSQLInternalNode* newNode(const OUString& _newValue, + const connectivity::SQLNodeType eNodeType, + const sal_uInt32 nNodeID) +{ + return new connectivity::OSQLInternalNode(_newValue, eNodeType, nNodeID); +} + +OParseContext::OParseContext() +{ +} + + +OParseContext::~OParseContext() +{ +} + + +OUString OParseContext::getErrorMessage(ErrorCode _eCode) const +{ + OUString aMsg; + switch (_eCode) + { + case ErrorCode::General: aMsg = "Syntax error in SQL expression"; break; + case ErrorCode::ValueNoLike: aMsg = "The value #1 can not be used with LIKE."; break; + case ErrorCode::FieldNoLike: aMsg = "LIKE can not be used with this field."; break; + case ErrorCode::InvalidCompare: aMsg = "The entered criterion can not be compared with this field."; break; + case ErrorCode::InvalidIntCompare: aMsg = "The field can not be compared with a number."; break; + case ErrorCode::InvalidDateCompare: aMsg = "The field can not be compared with a date."; break; + case ErrorCode::InvalidRealCompare: aMsg = "The field can not be compared with a floating point number."; break; + case ErrorCode::InvalidTableNosuch: aMsg = "The database does not contain a table named \"#\"."; break; + case ErrorCode::InvalidTableOrQuery: aMsg = "The database does contain neither a table nor a query named \"#\"."; break; + case ErrorCode::InvalidColumn: aMsg = "The column \"#1\" is unknown in the table \"#2\"."; break; + case ErrorCode::InvalidTableExist: aMsg = "The database already contains a table or view with name \"#\"."; break; + case ErrorCode::InvalidQueryExist: aMsg = "The database already contains a query with name \"#\"."; break; + default: + OSL_FAIL( "OParseContext::getErrorMessage: unknown error code!" ); + break; + } + return aMsg; +} + + +OString OParseContext::getIntlKeywordAscii(InternationalKeyCode _eKey) const +{ + OString aKeyword; + switch (_eKey) + { + case InternationalKeyCode::Like: aKeyword = "LIKE"; break; + case InternationalKeyCode::Not: aKeyword = "NOT"; break; + case InternationalKeyCode::Null: aKeyword = "NULL"; break; + case InternationalKeyCode::True: aKeyword = "True"; break; + case InternationalKeyCode::False: aKeyword = "False"; break; + case InternationalKeyCode::Is: aKeyword = "IS"; break; + case InternationalKeyCode::Between: aKeyword = "BETWEEN"; break; + case InternationalKeyCode::Or: aKeyword = "OR"; break; + case InternationalKeyCode::And: aKeyword = "AND"; break; + case InternationalKeyCode::Avg: aKeyword = "AVG"; break; + case InternationalKeyCode::Count: aKeyword = "COUNT"; break; + case InternationalKeyCode::Max: aKeyword = "MAX"; break; + case InternationalKeyCode::Min: aKeyword = "MIN"; break; + case InternationalKeyCode::Sum: aKeyword = "SUM"; break; + case InternationalKeyCode::Every: aKeyword = "EVERY"; break; + case InternationalKeyCode::Any: aKeyword = "ANY"; break; + case InternationalKeyCode::Some: aKeyword = "SOME"; break; + case InternationalKeyCode::StdDevPop: aKeyword = "STDDEV_POP"; break; + case InternationalKeyCode::StdDevSamp: aKeyword = "STDDEV_SAMP"; break; + case InternationalKeyCode::VarSamp: aKeyword = "VAR_SAMP"; break; + case InternationalKeyCode::VarPop: aKeyword = "VAR_POP"; break; + case InternationalKeyCode::Collect: aKeyword = "COLLECT"; break; + case InternationalKeyCode::Fusion: aKeyword = "FUSION"; break; + case InternationalKeyCode::Intersection:aKeyword = "INTERSECTION"; break; + case InternationalKeyCode::None: break; + default: + OSL_FAIL( "OParseContext::getIntlKeywordAscii: unknown key!" ); + break; + } + return aKeyword; +} + + +IParseContext::InternationalKeyCode OParseContext::getIntlKeyCode(const OString& rToken) const +{ + static IParseContext::InternationalKeyCode Intl_TokenID[] = + { + InternationalKeyCode::Like, InternationalKeyCode::Not, InternationalKeyCode::Null, InternationalKeyCode::True, + InternationalKeyCode::False, InternationalKeyCode::Is, InternationalKeyCode::Between, InternationalKeyCode::Or, + InternationalKeyCode::And, InternationalKeyCode::Avg, InternationalKeyCode::Count, InternationalKeyCode::Max, + InternationalKeyCode::Min, InternationalKeyCode::Sum, InternationalKeyCode::Every,InternationalKeyCode::Any,InternationalKeyCode::Some, + InternationalKeyCode::StdDevPop,InternationalKeyCode::StdDevSamp,InternationalKeyCode::VarSamp, + InternationalKeyCode::VarPop,InternationalKeyCode::Collect,InternationalKeyCode::Fusion,InternationalKeyCode::Intersection + }; + + sal_uInt32 nCount = SAL_N_ELEMENTS( Intl_TokenID ); + for (sal_uInt32 i = 0; i < nCount; i++) + { + OString aKey = getIntlKeywordAscii(Intl_TokenID[i]); + if (rToken.equalsIgnoreAsciiCase(aKey)) + return Intl_TokenID[i]; + } + + return InternationalKeyCode::None; +} + + +static Locale& impl_getLocaleInstance( ) +{ + static Locale s_aLocale( "en", "US", "" ); + return s_aLocale; +} + + +Locale OParseContext::getPreferredLocale( ) const +{ + return getDefaultLocale(); +} + + +const Locale& OParseContext::getDefaultLocale() +{ + return impl_getLocaleInstance(); +} + +// The (unfortunately global) yylval for the handing over of +// values from the Scanner to the Parser. The global variable +// is only used for a short term, the Parser reads the variable +// immediately after the call of the Scanner into a same named own +// member variable. + + +OUString ConvertLikeToken(const OSQLParseNode* pTokenNode, const OSQLParseNode* pEscapeNode, bool bInternational) +{ + OUStringBuffer aMatchStr(0); + if (pTokenNode->isToken()) + { + sal_Unicode cEscape = 0; + if (pEscapeNode->count()) + cEscape = pEscapeNode->getChild(1)->getTokenValue().toChar(); + + // Change place holder + aMatchStr = pTokenNode->getTokenValue(); + const sal_Int32 nLen = aMatchStr.getLength(); + OUStringBuffer sSearch,sReplace; + if ( bInternational ) + { + sSearch.append("%_"); + sReplace.append("*?"); + } + else + { + sSearch.append("*?"); + sReplace.append("%_"); + } + + bool wasEscape = false; + for (sal_Int32 i = 0; i < nLen; i++) + { + const sal_Unicode c = aMatchStr[i]; + // SQL standard requires the escape to be followed + // by a meta-character ('%', '_' or itself), else error + // We are more lenient here and let it escape anything. + // Especially since some databases (e.g. Microsoft SQL Server) + // have more meta-characters than the standard, such as e.g. '[' and ']' + if (wasEscape) + { + wasEscape=false; + continue; + } + if (c == cEscape) + { + wasEscape=true; + continue; + } + int match = -1; + if (c == sSearch[0]) + match=0; + else if (c == sSearch[1]) + match=1; + + if (match != -1) + { + aMatchStr[i] = sReplace[match]; + } + } + } + return aMatchStr.makeStringAndClear(); +} + +sal_uInt32 OSQLParser::s_nRuleIDs[OSQLParseNode::rule_count + 1]; +OSQLParser::RuleIDMap OSQLParser::s_aReverseRuleIDLookup; +OParseContext OSQLParser::s_aDefaultContext; + +sal_Int32 OSQLParser::s_nRefCount = 0; +// ::osl::Mutex OSQLParser::s_aMutex; +OSQLScanner* OSQLParser::s_pScanner = nullptr; +OSQLParseNodesGarbageCollector* OSQLParser::s_pGarbageCollector = nullptr; +css::uno::Reference< css::i18n::XLocaleData4> OSQLParser::s_xLocaleData = nullptr; + +void setParser(OSQLParser* _pParser) +{ + xxx_pGLOBAL_SQLPARSER = _pParser; +} + +void OSQLParser::setParseTree(OSQLParseNode* pNewParseTree) +{ + ::osl::MutexGuard aGuard(getMutex()); + m_pParseTree.reset(pNewParseTree); +} + + +/** Delete all comments in a query. + + See also getComment()/concatComment() implementation for + OQueryController::translateStatement(). + */ +static OUString delComment( const OUString& rQuery ) +{ + // First a quick search if there is any "--" or "//" or "/*", if not then the whole + // copying loop is pointless. + if (rQuery.indexOf("--") < 0 && rQuery.indexOf("//") < 0 && + rQuery.indexOf("/*") < 0) + return rQuery; + + const sal_Unicode* pCopy = rQuery.getStr(); + sal_Int32 nQueryLen = rQuery.getLength(); + bool bIsText1 = false; // "text" + bool bIsText2 = false; // 'text' + bool bComment2 = false; // /* comment */ + bool bComment = false; // -- or // comment + OUStringBuffer aBuf(nQueryLen); + for (sal_Int32 i=0; i < nQueryLen; ++i) + { + if (bComment2) + { + if ((i+1) < nQueryLen) + { + if (pCopy[i]=='*' && pCopy[i+1]=='/') + { + bComment2 = false; + ++i; + } + } + else + { + // comment can't close anymore, actually an error, but... + } + continue; + } + if (pCopy[i] == '\n') + bComment = false; + else if (!bComment) + { + if (pCopy[i] == '\"' && !bIsText2) + bIsText1 = !bIsText1; + else if (pCopy[i] == '\'' && !bIsText1) + bIsText2 = !bIsText2; + if (!bIsText1 && !bIsText2 && (i+1) < nQueryLen) + { + if ((pCopy[i]=='-' && pCopy[i+1]=='-') || (pCopy[i]=='/' && pCopy[i+1]=='/')) + bComment = true; + else if ((pCopy[i]=='/' && pCopy[i+1]=='*')) + bComment2 = true; + } + } + if (!bComment && !bComment2) + aBuf.append( &pCopy[i], 1); + } + return aBuf.makeStringAndClear(); +} + +std::unique_ptr<OSQLParseNode> OSQLParser::parseTree(OUString& rErrorMessage, + const OUString& rStatement, + bool bInternational) +{ + + + // Guard the parsing + ::osl::MutexGuard aGuard(getMutex()); + // must be reset + setParser(this); + + // delete comments before parsing + OUString sTemp = delComment(rStatement); + + // defines how to scan + s_pScanner->SetRule(OSQLScanner::GetSQLRule()); // initial + s_pScanner->prepareScan(sTemp, m_pContext, bInternational); + + SQLyylval.pParseNode = nullptr; + // SQLyypvt = NULL; + m_pParseTree = nullptr; + m_sErrorMessage = ""; + + // start parsing + if (SQLyyparse() != 0) + { + // only set the error message, if it's not already set + if (m_sErrorMessage.isEmpty()) + m_sErrorMessage = s_pScanner->getErrorMessage(); + if (m_sErrorMessage.isEmpty()) + m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::General); + + rErrorMessage = m_sErrorMessage; + + // clear the garbage collector + (*s_pGarbageCollector)->clearAndDelete(); + m_pParseTree.release(); // because the garbage collector deleted it + return nullptr; + } + else + { + (*s_pGarbageCollector)->clear(); + + // return result: + // to work around a bug in MKS YACC return the member m_pParseTree + // instead of Sdbyyval.pParseNode + + SAL_WARN_IF(!m_pParseTree, "connectivity.parse", + "OSQLParser: Parser did not create ParseTree"); + return std::move(m_pParseTree); + } +} + +OString OSQLParser::TokenIDToStr(sal_uInt32 nTokenID, const IParseContext* pContext) +{ + OString aStr; + if (pContext) + { + IParseContext::InternationalKeyCode eKeyCode = IParseContext::InternationalKeyCode::None; + switch( nTokenID ) + { + case SQL_TOKEN_LIKE: eKeyCode = IParseContext::InternationalKeyCode::Like; break; + case SQL_TOKEN_NOT: eKeyCode = IParseContext::InternationalKeyCode::Not; break; + case SQL_TOKEN_NULL: eKeyCode = IParseContext::InternationalKeyCode::Null; break; + case SQL_TOKEN_TRUE: eKeyCode = IParseContext::InternationalKeyCode::True; break; + case SQL_TOKEN_FALSE: eKeyCode = IParseContext::InternationalKeyCode::False; break; + case SQL_TOKEN_IS: eKeyCode = IParseContext::InternationalKeyCode::Is; break; + case SQL_TOKEN_BETWEEN: eKeyCode = IParseContext::InternationalKeyCode::Between; break; + case SQL_TOKEN_OR: eKeyCode = IParseContext::InternationalKeyCode::Or; break; + case SQL_TOKEN_AND: eKeyCode = IParseContext::InternationalKeyCode::And; break; + case SQL_TOKEN_AVG: eKeyCode = IParseContext::InternationalKeyCode::Avg; break; + case SQL_TOKEN_COUNT: eKeyCode = IParseContext::InternationalKeyCode::Count; break; + case SQL_TOKEN_MAX: eKeyCode = IParseContext::InternationalKeyCode::Max; break; + case SQL_TOKEN_MIN: eKeyCode = IParseContext::InternationalKeyCode::Min; break; + case SQL_TOKEN_SUM: eKeyCode = IParseContext::InternationalKeyCode::Sum; break; + } + if ( eKeyCode != IParseContext::InternationalKeyCode::None ) + aStr = pContext->getIntlKeywordAscii(eKeyCode); + } + + if (aStr.isEmpty()) + { + // coverity[unsigned_compare : SUPPRESS] - YYTRANSLATE is out of our control + aStr = yytname[YYTRANSLATE(nTokenID)]; + if(aStr.startsWith("SQL_TOKEN_")) + aStr = aStr.copy(10); + switch( nTokenID ) + { + case SQL_TOKEN_OJ: + case SQL_TOKEN_TS: + case SQL_TOKEN_T: + case SQL_TOKEN_D: + aStr = aStr.toAsciiLowerCase(); + } + } + return aStr; +} + +#if OSL_DEBUG_LEVEL > 0 +OUString OSQLParser::RuleIDToStr(sal_uInt32 nRuleID) +{ + OSL_ENSURE(nRuleID < SAL_N_ELEMENTS(yytname), "OSQLParser::RuleIDToStr: Invalid nRuleId!"); + return OUString::createFromAscii(yytname[nRuleID]); +} +#endif + + +sal_uInt32 OSQLParser::StrToRuleID(const OString & rValue) +{ + // Search for the given name in yytname and return the index + // (or UNKNOWN_RULE, if not found) + static sal_uInt32 nLen = SAL_N_ELEMENTS(yytname); + for (sal_uInt32 i = YYTRANSLATE(SQL_TOKEN_INVALIDSYMBOL); i < (nLen-1); i++) + { + if (rValue == yytname[i]) + return i; + } + + // Not found + return OSQLParseNode::UNKNOWN_RULE; +} + + +OSQLParseNode::Rule OSQLParser::RuleIDToRule( sal_uInt32 _nRule ) +{ + OSQLParser::RuleIDMap::const_iterator i (s_aReverseRuleIDLookup.find(_nRule)); + if (i == s_aReverseRuleIDLookup.end()) + { + SAL_INFO("connectivity.parse", + "connectivity::OSQLParser::RuleIDToRule cannot reverse-lookup rule. " + "Reverse mapping incomplete? " + "_nRule='" << _nRule << "' " + "yytname[_nRule]='" << yytname[_nRule] << "'"); + return OSQLParseNode::UNKNOWN_RULE; + } + else + return i->second; +} + + +sal_uInt32 OSQLParser::RuleID(OSQLParseNode::Rule eRule) +{ + return s_nRuleIDs[(sal_uInt16)eRule]; +} + +sal_Int16 OSQLParser::buildNode(OSQLParseNode*& pAppend,OSQLParseNode* pCompare,OSQLParseNode* pLiteral,OSQLParseNode* pLiteral2) +{ + OSQLParseNode* pColumnRef = new OSQLInternalNode("", SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::column_ref)); + pColumnRef->append(new OSQLInternalNode(m_sFieldName,SQLNodeType::Name)); + OSQLParseNode* pComp = nullptr; + if ( SQL_ISTOKEN( pCompare, BETWEEN) && pLiteral2 ) + pComp = new OSQLInternalNode("", SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::between_predicate_part_2)); + else + pComp = new OSQLInternalNode("", SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::comparison_predicate)); + + pComp->append(pColumnRef); + pComp->append(pCompare); + pComp->append(pLiteral); + if ( pLiteral2 ) + { + pComp->append(new OSQLInternalNode("", SQLNodeType::Keyword,SQL_TOKEN_AND)); + pComp->append(pLiteral2); + } + pAppend->append(pComp); + return 1; +} + +sal_Int16 OSQLParser::buildStringNodes(OSQLParseNode*& pLiteral) +{ + if(!pLiteral) + return 1; + + if(SQL_ISRULE(pLiteral,set_fct_spec) || SQL_ISRULE(pLiteral,general_set_fct) || SQL_ISRULE(pLiteral,column_ref) + || SQL_ISRULE(pLiteral,subquery)) + return 1; // here I have a function that I can't transform into a string + + if(pLiteral->getNodeType() == SQLNodeType::IntNum || pLiteral->getNodeType() == SQLNodeType::ApproxNum || pLiteral->getNodeType() == SQLNodeType::AccessDate) + { + OSQLParseNode* pParent = pLiteral->getParent(); + + OSQLParseNode* pNewNode = new OSQLInternalNode(pLiteral->getTokenValue(), SQLNodeType::String); + pParent->replace(pLiteral, pNewNode); + delete pLiteral; + pLiteral = nullptr; + return 1; + } + + for(size_t i=0;i<pLiteral->count();++i) + { + OSQLParseNode* pChild = pLiteral->getChild(i); + buildStringNodes(pChild); + } + if(SQL_ISRULE(pLiteral,term) || SQL_ISRULE(pLiteral,value_exp_primary)) + { + m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::InvalidCompare); + return 0; + } + return 1; +} + +sal_Int16 OSQLParser::buildComparisonRule(OSQLParseNode*& pAppend,OSQLParseNode* pLiteral) +{ + OSQLParseNode* pComp = new OSQLInternalNode("=", SQLNodeType::Equal); + return buildPredicateRule(pAppend,pLiteral,pComp); +} + + + +void OSQLParser::reduceLiteral(OSQLParseNode*& pLiteral, bool bAppendBlank) +{ + OSL_ENSURE(pLiteral->isRule(), "This is no Rule"); + OSL_ENSURE(pLiteral->count() == 2, "OSQLParser::ReduceLiteral() Invalid count"); + OSQLParseNode* pTemp = pLiteral; + OUStringBuffer aValue(pLiteral->getChild(0)->getTokenValue()); + if (bAppendBlank) + { + aValue.append(" "); + } + + aValue.append(pLiteral->getChild(1)->getTokenValue()); + + pLiteral = new OSQLInternalNode(aValue.makeStringAndClear(),SQLNodeType::String); + delete pTemp; +} + + +void OSQLParser::error(const char *fmt) +{ + if(m_sErrorMessage.isEmpty()) + { + OUString sStr(fmt,strlen(fmt),RTL_TEXTENCODING_UTF8); + OUString sSQL_TOKEN("SQL_TOKEN_"); + + sal_Int32 nPos1 = sStr.indexOf(sSQL_TOKEN); + if(nPos1 != -1) + { + OUString sFirst = sStr.copy(0,nPos1); + sal_Int32 nPos2 = sStr.indexOf(sSQL_TOKEN,nPos1+1); + if(nPos2 != -1) + { + sFirst += sStr.subView(nPos1+sSQL_TOKEN.getLength(),nPos2-nPos1-sSQL_TOKEN.getLength()); + sFirst += sStr.subView(nPos2+sSQL_TOKEN.getLength()); + } + else + sFirst += sStr.subView(nPos1+sSQL_TOKEN.getLength()); + + m_sErrorMessage = sFirst; + } + else + m_sErrorMessage = sStr; + + OUString aError = s_pScanner->getErrorMessage(); + if(!aError.isEmpty()) + { + m_sErrorMessage += ", "; + m_sErrorMessage += aError; + } + } +} + +int OSQLParser::SQLlex() +{ + return OSQLScanner::SQLlex(); +} diff --git a/connectivity/source/parse/sqlflex.l b/connectivity/source/parse/sqlflex.l new file mode 100644 index 000000000..34a4067ea --- /dev/null +++ b/connectivity/source/parse/sqlflex.l @@ -0,0 +1,808 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +%{ + +#include "sal/config.h" + +#define YY_EXIT 1 // YY_FATAL will not halt the application + +#ifndef _CSTDARG_ +#include <cstdarg> +#endif + +#include <string.h> + +#if defined _MSC_VER +#pragma warning ( push ) +// Silence warnings about redefinition of INT8_MIN etc in stdint.h +// The flex-generated workdir/LexTarget/idlc/source/scanner.cxx defines them prior to these includes +#pragma warning ( disable : 4005 ) +#endif +#include <connectivity/internalnode.hxx> +#if defined _MSC_VER +#pragma warning(pop) +#endif + +#ifndef INCLUDED_CONNECTIVITY_SOURCE_PARSE_SQLFLEX_L +#define INCLUDED_CONNECTIVITY_SOURCE_PARSE_SQLFLEX_L + +#ifndef SQLYYDEBUG +#define SQLYYDEBUG 1 +#endif + +#include "sqlbison.hxx" +#undef SQLyylex +#undef SQLyyerror +#endif +#include <osl/diagnose.h> +#include <rtl/strbuf.hxx> +#include <connectivity/sqlparse.hxx> +#include <connectivity/sqlscan.hxx> + +#if defined _MSC_VER +/**/ +#ifdef yywrap +#undef yywrap +#define yywrap() 1 +#endif +/**/ +#endif +#define YY_NO_UNISTD_H + +using namespace connectivity; + +// Creation of the pages for the tokens +// Pages generally are created from the Lexer + +static sal_Int32 gatherString(int delim, sal_Int32 nTyp); +static sal_Int32 gatherName(const char*); +static sal_Int32 gatherNamePre(const char* ); +// has to be set before the parser starts +OSQLScanner* xxx_pGLOBAL_SQLSCAN = nullptr; + +#define SQL_NEW_NODE(text, token) \ + SQLyylval.pParseNode = new OSQLInternalNode(text, token); + +#define SQL_NEW_KEYWORD(token) \ + SQLyylval.pParseNode = new OSQLInternalNode("", SQLNodeType::Keyword, (token)); return token; + +#define SQL_NEW_INTNUM SQL_NEW_NODE(OUString(SQLyytext,strlen(SQLyytext),RTL_TEXTENCODING_UTF8), SQLNodeType::IntNum); return SQL_TOKEN_INTNUM; +#define SQL_NEW_APPROXNUM SQL_NEW_NODE(OUString(SQLyytext,strlen(SQLyytext),RTL_TEXTENCODING_UTF8), SQLNodeType::ApproxNum); return SQL_TOKEN_APPROXNUM; +#define SQL_NEW_DATE SQL_NEW_NODE(OUString(SQLyytext,strlen(SQLyytext),RTL_TEXTENCODING_UTF8), SQLNodeType::AccessDate); return SQL_TOKEN_ACCESS_DATE; + +#define YY_INPUT(buf,result,max_size) \ +{ \ + int c = xxx_pGLOBAL_SQLSCAN->SQLyygetc(); \ + result = (c == EOF) ? YY_NULL : (buf[0] = c, 1);\ +} + +// coverity[+kill] +static void do_fatal_error(const char* msg) +{ + xxx_pGLOBAL_SQLSCAN->SQLyyerror(msg); + /*hack to silence -Wunused-function*/ + if ((0)) yy_fatal_error(msg); +} + +#define YY_FATAL_ERROR(msg) \ +{ \ + do_fatal_error(msg); \ +} + +%} + +%s SQL +%s PREDICATE_ENG +%s PREDICATE_GER +%s DATE +%s STRING + +%option noyywrap +%option never-interactive +%% + +ABS {SQL_NEW_KEYWORD(SQL_TOKEN_ABS); } +ACOS {SQL_NEW_KEYWORD(SQL_TOKEN_ACOS); } +AFTER {SQL_NEW_KEYWORD(SQL_TOKEN_AFTER); } +ALL {SQL_NEW_KEYWORD(SQL_TOKEN_ALL); } +ALTER {SQL_NEW_KEYWORD(SQL_TOKEN_ALTER); } +AND {SQL_NEW_KEYWORD(SQL_TOKEN_AND); } +ANY {SQL_NEW_KEYWORD(SQL_TOKEN_ANY); } +ARRAY_AGG {SQL_NEW_KEYWORD(SQL_TOKEN_ARRAY_AGG); } +AS {SQL_NEW_KEYWORD(SQL_TOKEN_AS); } +ASC {SQL_NEW_KEYWORD(SQL_TOKEN_ASC); } +ASCII {SQL_NEW_KEYWORD(SQL_TOKEN_ASCII); } +ASIN {SQL_NEW_KEYWORD(SQL_TOKEN_ASIN); } +AT {SQL_NEW_KEYWORD(SQL_TOKEN_AT); } +ATAN {SQL_NEW_KEYWORD(SQL_TOKEN_ATAN); } +ATAN2 {SQL_NEW_KEYWORD(SQL_TOKEN_ATAN2); } +ATOMIC {SQL_NEW_KEYWORD(SQL_TOKEN_ATOMIC); } +AUTHORIZATION {SQL_NEW_KEYWORD(SQL_TOKEN_AUTHORIZATION); } +AVG {SQL_NEW_KEYWORD(SQL_TOKEN_AVG); } + +BEFORE {SQL_NEW_KEYWORD(SQL_TOKEN_BEFORE); } +BEGIN {SQL_NEW_KEYWORD(SQL_TOKEN_BEGIN); } +BETWEEN {SQL_NEW_KEYWORD(SQL_TOKEN_BETWEEN); } +BIGINT {SQL_NEW_KEYWORD(SQL_TOKEN_BIGINT); } +BINARY {SQL_NEW_KEYWORD(SQL_TOKEN_BINARY); } +BIT {SQL_NEW_KEYWORD(SQL_TOKEN_BIT); } +BIT_LENGTH {SQL_NEW_KEYWORD(SQL_TOKEN_BIT_LENGTH); } +BLOB {SQL_NEW_KEYWORD(SQL_TOKEN_BLOB); } +BOTH {SQL_NEW_KEYWORD(SQL_TOKEN_BOTH); } +BY {SQL_NEW_KEYWORD(SQL_TOKEN_BY); } + +CALL {SQL_NEW_KEYWORD(SQL_TOKEN_CALL); } +CASE {SQL_NEW_KEYWORD(SQL_TOKEN_CASE); } +CAST {SQL_NEW_KEYWORD(SQL_TOKEN_CAST); } +CEILING {SQL_NEW_KEYWORD(SQL_TOKEN_CEILING); } +CHAR {SQL_NEW_KEYWORD(SQL_TOKEN_CHAR); } +CHARACTER {SQL_NEW_KEYWORD(SQL_TOKEN_CHARACTER); } +CHAR(ACTER)?_LENGTH {SQL_NEW_KEYWORD(SQL_TOKEN_CHAR_LENGTH); } +CHECK {SQL_NEW_KEYWORD(SQL_TOKEN_CHECK); } +CLOB {SQL_NEW_KEYWORD(SQL_TOKEN_CLOB); } +COALESCE {SQL_NEW_KEYWORD(SQL_TOKEN_COALESCE); } +COLLATE {SQL_NEW_KEYWORD(SQL_TOKEN_COLLATE); } +COLLECT {SQL_NEW_KEYWORD(SQL_TOKEN_COLLECT); } +COMMIT {SQL_NEW_KEYWORD(SQL_TOKEN_COMMIT); } +CONCAT {SQL_NEW_KEYWORD(SQL_TOKEN_CONCAT); } +CONTINUE {SQL_NEW_KEYWORD(SQL_TOKEN_CONTINUE); } +CONVERT {SQL_NEW_KEYWORD(SQL_TOKEN_CONVERT); } +COS {SQL_NEW_KEYWORD(SQL_TOKEN_COS); } +COT {SQL_NEW_KEYWORD(SQL_TOKEN_COT); } +COUNT {SQL_NEW_KEYWORD(SQL_TOKEN_COUNT); } +CREATE {SQL_NEW_KEYWORD(SQL_TOKEN_CREATE); } +CROSS {SQL_NEW_KEYWORD(SQL_TOKEN_CROSS); } +CUME_RANK {SQL_NEW_KEYWORD(SQL_TOKEN_CUME_DIST); } +CURRENT {SQL_NEW_KEYWORD(SQL_TOKEN_CURRENT); } +CURRENT_DATE {SQL_NEW_KEYWORD(SQL_TOKEN_CURRENT_DATE); } +CURRENT_CATALOG {SQL_NEW_KEYWORD(SQL_TOKEN_CURRENT_CATALOG); } +CURRENT_DEFAULT_TRANSFORM_GROUP {SQL_NEW_KEYWORD(SQL_TOKEN_CURRENT_DEFAULT_TRANSFORM_GROUP); } +CURRENT_PATH {SQL_NEW_KEYWORD(SQL_TOKEN_CURRENT_PATH); } +CURRENT_ROLE {SQL_NEW_KEYWORD(SQL_TOKEN_CURRENT_ROLE); } +CURRENT_SCHEMA {SQL_NEW_KEYWORD(SQL_TOKEN_CURRENT_SCHEMA); } +CURRENT_USER {SQL_NEW_KEYWORD(SQL_TOKEN_CURRENT_USER); } +CURDATE {SQL_NEW_KEYWORD(SQL_TOKEN_CURDATE); } +CURRENT_TIME {SQL_NEW_KEYWORD(SQL_TOKEN_CURRENT_TIME); } +CURTIME {SQL_NEW_KEYWORD(SQL_TOKEN_CURTIME); } +CURRENT_TIMESTAMP {SQL_NEW_KEYWORD(SQL_TOKEN_CURRENT_TIMESTAMP); } +CURSOR {SQL_NEW_KEYWORD(SQL_TOKEN_CURSOR); } + +D {SQL_NEW_KEYWORD(SQL_TOKEN_D); } +DATE {SQL_NEW_KEYWORD(SQL_TOKEN_DATE); } +DATEDIFF {SQL_NEW_KEYWORD(SQL_TOKEN_DATEDIFF); } +DATEVALUE {SQL_NEW_KEYWORD(SQL_TOKEN_DATEVALUE); } +DAY {SQL_NEW_KEYWORD(SQL_TOKEN_DAY); } +DAYNAME {SQL_NEW_KEYWORD(SQL_TOKEN_DAYNAME); } +DAYOFMONTH {SQL_NEW_KEYWORD(SQL_TOKEN_DAYOFMONTH); } +DAYOFWEEK {SQL_NEW_KEYWORD(SQL_TOKEN_DAYOFWEEK); } +DAYOFYEAR {SQL_NEW_KEYWORD(SQL_TOKEN_DAYOFYEAR); } +DEC {SQL_NEW_KEYWORD(SQL_TOKEN_DEC); } +DECIMAL {SQL_NEW_KEYWORD(SQL_TOKEN_DECIMAL); } +DECLARE {SQL_NEW_KEYWORD(SQL_TOKEN_DECLARE); } +DEFAULT {SQL_NEW_KEYWORD(SQL_TOKEN_DEFAULT); } +DEGREES {SQL_NEW_KEYWORD(SQL_TOKEN_DEGREES); } +DELETE {SQL_NEW_KEYWORD(SQL_TOKEN_DELETE); } +DENSE_RANK {SQL_NEW_KEYWORD(SQL_TOKEN_DENSE_RANK); } +DESC {SQL_NEW_KEYWORD(SQL_TOKEN_DESC); } +DIFFERENCE {SQL_NEW_KEYWORD(SQL_TOKEN_DIFFERENCE); } +DISTINCT {SQL_NEW_KEYWORD(SQL_TOKEN_DISTINCT); } +DOUBLE {SQL_NEW_KEYWORD(SQL_TOKEN_DOUBLE); } +DROP {SQL_NEW_KEYWORD(SQL_TOKEN_DROP); } + +EACH {SQL_NEW_KEYWORD(SQL_TOKEN_EACH); } +ELSE {SQL_NEW_KEYWORD(SQL_TOKEN_ELSE); } +END {SQL_NEW_KEYWORD(SQL_TOKEN_END); } +EVERY {SQL_NEW_KEYWORD(SQL_TOKEN_EVERY); } +ESCAPE {SQL_NEW_KEYWORD(SQL_TOKEN_ESCAPE); } +EXCEPT {SQL_NEW_KEYWORD(SQL_TOKEN_EXCEPT); } +EXCLUDE {SQL_NEW_KEYWORD(SQL_TOKEN_EXCLUDE); } +EXISTS {SQL_NEW_KEYWORD(SQL_TOKEN_EXISTS); } +EXP {SQL_NEW_KEYWORD(SQL_TOKEN_EXP); } +EXTRACT {SQL_NEW_KEYWORD(SQL_TOKEN_EXTRACT); } + +FALSE {SQL_NEW_KEYWORD(SQL_TOKEN_FALSE); } +FETCH {SQL_NEW_KEYWORD(SQL_TOKEN_FETCH); } +FIRST {SQL_NEW_KEYWORD(SQL_TOKEN_FIRST); } +FIRST_VALUE {SQL_NEW_KEYWORD(SQL_TOKEN_FIRST_VALUE); } +FLOAT {SQL_NEW_KEYWORD(SQL_TOKEN_FLOAT); } +FLOOR {SQL_NEW_KEYWORD(SQL_TOKEN_FLOOR); } +FN {SQL_NEW_KEYWORD(SQL_TOKEN_FN); } +FOLLOWING {SQL_NEW_KEYWORD(SQL_TOKEN_FOLLOWING); } +FOR {SQL_NEW_KEYWORD(SQL_TOKEN_FOR); } +FOREIGN {SQL_NEW_KEYWORD(SQL_TOKEN_FOREIGN); } +FOUND {SQL_NEW_KEYWORD(SQL_TOKEN_FOUND); } +FROM {SQL_NEW_KEYWORD(SQL_TOKEN_FROM); } +FULL {SQL_NEW_KEYWORD(SQL_TOKEN_FULL); } +FUSION {SQL_NEW_KEYWORD(SQL_TOKEN_FUSION); } + +GRANT {SQL_NEW_KEYWORD(SQL_TOKEN_GRANT); } +GROUP {SQL_NEW_KEYWORD(SQL_TOKEN_GROUP); } + +HAVING {SQL_NEW_KEYWORD(SQL_TOKEN_HAVING); } +HOUR {SQL_NEW_KEYWORD(SQL_TOKEN_HOUR); } + +IGNORE {SQL_NEW_KEYWORD(SQL_TOKEN_IGNORE); } +IN {SQL_NEW_KEYWORD(SQL_TOKEN_IN); } +INNER {SQL_NEW_KEYWORD(SQL_TOKEN_INNER); } +INSERT {SQL_NEW_KEYWORD(SQL_TOKEN_INSERT); } +INSTEAD {SQL_NEW_KEYWORD(SQL_TOKEN_INSTEAD); } +INT(EGER)? {SQL_NEW_KEYWORD(SQL_TOKEN_INTEGER); } +INTERSECT {SQL_NEW_KEYWORD(SQL_TOKEN_INTERSECT); } +INTERVAL {SQL_NEW_KEYWORD(SQL_TOKEN_INTERVAL); } +INTERSECTION {SQL_NEW_KEYWORD(SQL_TOKEN_INTERSECTION); } +INTO {SQL_NEW_KEYWORD(SQL_TOKEN_INTO); } +IS {SQL_NEW_KEYWORD(SQL_TOKEN_IS); } + +JOIN {SQL_NEW_KEYWORD(SQL_TOKEN_JOIN); } + +KEY {SQL_NEW_KEYWORD(SQL_TOKEN_KEY); } + +LAG {SQL_NEW_KEYWORD(SQL_TOKEN_LAG); } +LARGE {SQL_NEW_KEYWORD(SQL_TOKEN_LARGE); } +LAST {SQL_NEW_KEYWORD(SQL_TOKEN_LAST); } +LAST_VALUE {SQL_NEW_KEYWORD(SQL_TOKEN_LAST_VALUE); } +LCASE {SQL_NEW_KEYWORD(SQL_TOKEN_LCASE); } +LEAD {SQL_NEW_KEYWORD(SQL_TOKEN_LEAD); } +LEADING {SQL_NEW_KEYWORD(SQL_TOKEN_LEADING); } +LEFT {SQL_NEW_KEYWORD(SQL_TOKEN_LEFT); } +LENGTH {SQL_NEW_KEYWORD(SQL_TOKEN_LENGTH); } +LIKE {SQL_NEW_KEYWORD(SQL_TOKEN_LIKE); } +LIMIT {SQL_NEW_KEYWORD(SQL_TOKEN_LIMIT); } +LN {SQL_NEW_KEYWORD(SQL_TOKEN_LN); } +LOCAL {SQL_NEW_KEYWORD(SQL_TOKEN_LOCAL); } +LOCATE {SQL_NEW_KEYWORD(SQL_TOKEN_LOCATE); } +LOG {SQL_NEW_KEYWORD(SQL_TOKEN_LOG); } +LOGF {SQL_NEW_KEYWORD(SQL_TOKEN_LOGF); } +LOG10 {SQL_NEW_KEYWORD(SQL_TOKEN_LOG10); } +LOWER {SQL_NEW_KEYWORD(SQL_TOKEN_LOWER); } +LTRIM {SQL_NEW_KEYWORD(SQL_TOKEN_LTRIM); } + +MAX {SQL_NEW_KEYWORD(SQL_TOKEN_MAX); } +MILLISECOND {SQL_NEW_KEYWORD(SQL_TOKEN_MILLISECOND); } +MIN {SQL_NEW_KEYWORD(SQL_TOKEN_MIN); } +MINUTE {SQL_NEW_KEYWORD(SQL_TOKEN_MINUTE); } +MOD {SQL_NEW_KEYWORD(SQL_TOKEN_MOD); } +MONTH {SQL_NEW_KEYWORD(SQL_TOKEN_MONTH); } +MONTHNAME {SQL_NEW_KEYWORD(SQL_TOKEN_MONTHNAME); } + +NATIONAL {SQL_NEW_KEYWORD(SQL_TOKEN_NATIONAL); } +NATURAL {SQL_NEW_KEYWORD(SQL_TOKEN_NATURAL); } +NCHAR {SQL_NEW_KEYWORD(SQL_TOKEN_NCHAR); } +NCLOB {SQL_NEW_KEYWORD(SQL_TOKEN_NCLOB); } +NEW {SQL_NEW_KEYWORD(SQL_TOKEN_NEW); } +NEXT {SQL_NEW_KEYWORD(SQL_TOKEN_NEXT); } +NO {SQL_NEW_KEYWORD(SQL_TOKEN_NO); } +NOT {SQL_NEW_KEYWORD(SQL_TOKEN_NOT); } +NOW {SQL_NEW_KEYWORD(SQL_TOKEN_NOW); } +NTH_VALUE {SQL_NEW_KEYWORD(SQL_TOKEN_NTH_VALUE); } +NTILE {SQL_NEW_KEYWORD(SQL_TOKEN_NTILE); } +NULL {SQL_NEW_KEYWORD(SQL_TOKEN_NULL); } +NULLIF {SQL_NEW_KEYWORD(SQL_TOKEN_NULLIF); } +NULLS {SQL_NEW_KEYWORD(SQL_TOKEN_NULLS); } +NUMERIC {SQL_NEW_KEYWORD(SQL_TOKEN_NUMERIC); } + +OBJECT {SQL_NEW_KEYWORD(SQL_TOKEN_OBJECT); } +OCTET_LENGTH {SQL_NEW_KEYWORD(SQL_TOKEN_OCTET_LENGTH); } +OF {SQL_NEW_KEYWORD(SQL_TOKEN_OF); } +OFFSET {SQL_NEW_KEYWORD(SQL_TOKEN_OFFSET); } +OJ {SQL_NEW_KEYWORD(SQL_TOKEN_OJ); } +OLD {SQL_NEW_KEYWORD(SQL_TOKEN_OLD); } +ON {SQL_NEW_KEYWORD(SQL_TOKEN_ON); } +ONLY {SQL_NEW_KEYWORD(SQL_TOKEN_ONLY); } +OPTION {SQL_NEW_KEYWORD(SQL_TOKEN_OPTION); } +OR {SQL_NEW_KEYWORD(SQL_TOKEN_OR); } +ORDER {SQL_NEW_KEYWORD(SQL_TOKEN_ORDER); } +OTHERS {SQL_NEW_KEYWORD(SQL_TOKEN_OTHERS); } +OUTER {SQL_NEW_KEYWORD(SQL_TOKEN_OUTER); } +OVER {SQL_NEW_KEYWORD(SQL_TOKEN_OVER); } + +PARTITION {SQL_NEW_KEYWORD(SQL_TOKEN_PARTITION); } +PERCENT_RANK {SQL_NEW_KEYWORD(SQL_TOKEN_PERCENT_RANK); } +PERCENTILE_CONT {SQL_NEW_KEYWORD(SQL_TOKEN_PERCENTILE_CONT); } +PERCENTILE_DISC {SQL_NEW_KEYWORD(SQL_TOKEN_PERCENTILE_DISC); } +PI {SQL_NEW_KEYWORD(SQL_TOKEN_PI); } +POSITION {SQL_NEW_KEYWORD(SQL_TOKEN_POSITION); } +POWER {SQL_NEW_KEYWORD(SQL_TOKEN_POWER); } +PRECEDING {SQL_NEW_KEYWORD(SQL_TOKEN_PRECEDING); } +PRECISION {SQL_NEW_KEYWORD(SQL_TOKEN_PRECISION); } +PRIMARY {SQL_NEW_KEYWORD(SQL_TOKEN_PRIMARY); } +PRIVILEGES {SQL_NEW_KEYWORD(SQL_TOKEN_PRIVILEGES); } +PROCEDURE {SQL_NEW_KEYWORD(SQL_TOKEN_PROCEDURE); } +PUBLIC {SQL_NEW_KEYWORD(SQL_TOKEN_PUBLIC); } + +QUARTER {SQL_NEW_KEYWORD(SQL_TOKEN_QUARTER); } + +RADIANS {SQL_NEW_KEYWORD(SQL_TOKEN_RADIANS); } +RAND {SQL_NEW_KEYWORD(SQL_TOKEN_RAND); } +RANGE {SQL_NEW_KEYWORD(SQL_TOKEN_RANGE); } +RANK {SQL_NEW_KEYWORD(SQL_TOKEN_RANK); } +REAL {SQL_NEW_KEYWORD(SQL_TOKEN_REAL); } +REFERENCES {SQL_NEW_KEYWORD(SQL_TOKEN_REFERENCES); } +REFERENCING {SQL_NEW_KEYWORD(SQL_TOKEN_REFERENCING); } +REPEAT {SQL_NEW_KEYWORD(SQL_TOKEN_REPEAT); } +REPLACE {SQL_NEW_KEYWORD(SQL_TOKEN_REPLACE); } +RESPECT {SQL_NEW_KEYWORD(SQL_TOKEN_RESPECT); } +ROLLBACK {SQL_NEW_KEYWORD(SQL_TOKEN_ROLLBACK); } +ROUND {SQL_NEW_KEYWORD(SQL_TOKEN_ROUND); } +ROUNDMAGIC {SQL_NEW_KEYWORD(SQL_TOKEN_ROUNDMAGIC); } +ROW {SQL_NEW_KEYWORD(SQL_TOKEN_ROW); } +ROWS {SQL_NEW_KEYWORD(SQL_TOKEN_ROWS); } +ROW_NUMBER {SQL_NEW_KEYWORD(SQL_TOKEN_ROW_NUMBER); } +RIGHT {SQL_NEW_KEYWORD(SQL_TOKEN_RIGHT); } +RTRIM {SQL_NEW_KEYWORD(SQL_TOKEN_RTRIM); } + +SCHEMA {SQL_NEW_KEYWORD(SQL_TOKEN_SCHEMA); } +SECOND {SQL_NEW_KEYWORD(SQL_TOKEN_SECOND); } +SELECT {SQL_NEW_KEYWORD(SQL_TOKEN_SELECT); } +SET {SQL_NEW_KEYWORD(SQL_TOKEN_SET); } +SIZE {SQL_NEW_KEYWORD(SQL_TOKEN_SIZE); } +SIGN {SQL_NEW_KEYWORD(SQL_TOKEN_SIGN); } +SIN {SQL_NEW_KEYWORD(SQL_TOKEN_SIN); } +SMALLINT {SQL_NEW_KEYWORD(SQL_TOKEN_SMALLINT); } +SOME {SQL_NEW_KEYWORD(SQL_TOKEN_SOME); } +SOUNDEX {SQL_NEW_KEYWORD(SQL_TOKEN_SOUNDEX); } +SPACE {SQL_NEW_KEYWORD(SQL_TOKEN_SPACE); } +SQRT {SQL_NEW_KEYWORD(SQL_TOKEN_SQRT); } +STDDEV_POP {SQL_NEW_KEYWORD(SQL_TOKEN_STDDEV_POP); } +STDDEV_SAMP {SQL_NEW_KEYWORD(SQL_TOKEN_STDDEV_SAMP); } +STATEMENT {SQL_NEW_KEYWORD(SQL_TOKEN_STATEMENT); } +SUBSTRING {SQL_NEW_KEYWORD(SQL_TOKEN_SUBSTRING); } +SUM {SQL_NEW_KEYWORD(SQL_TOKEN_SUM); } +SESSION_USER {SQL_NEW_KEYWORD(SQL_TOKEN_SESSION_USER); } +SYSTEM_USER {SQL_NEW_KEYWORD(SQL_TOKEN_SYSTEM_USER); } + +TABLE {SQL_NEW_KEYWORD(SQL_TOKEN_TABLE); } +TAN {SQL_NEW_KEYWORD(SQL_TOKEN_TAN); } +THEN {SQL_NEW_KEYWORD(SQL_TOKEN_THEN); } +TIES {SQL_NEW_KEYWORD(SQL_TOKEN_TIES); } +TIME {SQL_NEW_KEYWORD(SQL_TOKEN_TIME); } +TIMESTAMP {SQL_NEW_KEYWORD(SQL_TOKEN_TIMESTAMP); } +TIMESTAMPADD {SQL_NEW_KEYWORD(SQL_TOKEN_TIMESTAMPADD); } +TIMESTAMPDIFF {SQL_NEW_KEYWORD(SQL_TOKEN_TIMESTAMPDIFF); } +TIMEVALUE {SQL_NEW_KEYWORD(SQL_TOKEN_TIMEVALUE); } +TIMEZONE_HOUR {SQL_NEW_KEYWORD(SQL_TOKEN_TIMEZONE_HOUR); } +TIMEZONE_MINUTE {SQL_NEW_KEYWORD(SQL_TOKEN_TIMEZONE_MINUTE); } +TO {SQL_NEW_KEYWORD(SQL_TOKEN_TO); } +TRAILING {SQL_NEW_KEYWORD(SQL_TOKEN_TRAILING); } +TRANSLATE {SQL_NEW_KEYWORD(SQL_TOKEN_TRANSLATE); } +TRIGGER {SQL_NEW_KEYWORD(SQL_TOKEN_TRIGGER); } +TRIM {SQL_NEW_KEYWORD(SQL_TOKEN_TRIM); } +TRUE {SQL_NEW_KEYWORD(SQL_TOKEN_TRUE); } +TRUNCATE {SQL_NEW_KEYWORD(SQL_TOKEN_TRUNCATE); } +TS {SQL_NEW_KEYWORD(SQL_TOKEN_TS); } +T {SQL_NEW_KEYWORD(SQL_TOKEN_T); } + +UCASE {SQL_NEW_KEYWORD(SQL_TOKEN_UCASE); } +UNBOUNDED {SQL_NEW_KEYWORD(SQL_TOKEN_UNBOUNDED); } +UNION {SQL_NEW_KEYWORD(SQL_TOKEN_UNION); } +UNIQUE {SQL_NEW_KEYWORD(SQL_TOKEN_UNIQUE); } +UNKNOWN {SQL_NEW_KEYWORD(SQL_TOKEN_UNKNOWN); } +UPDATE {SQL_NEW_KEYWORD(SQL_TOKEN_UPDATE); } +UPPER {SQL_NEW_KEYWORD(SQL_TOKEN_UPPER); } +USAGE {SQL_NEW_KEYWORD(SQL_TOKEN_USAGE); } +USER {SQL_NEW_KEYWORD(SQL_TOKEN_USER); } +USING {SQL_NEW_KEYWORD(SQL_TOKEN_USING); } + +VARBINARY {SQL_NEW_KEYWORD(SQL_TOKEN_VARBINARY); } +VARCHAR {SQL_NEW_KEYWORD(SQL_TOKEN_VARCHAR); } +VARYING {SQL_NEW_KEYWORD(SQL_TOKEN_VARYING); } +VAR_POP {SQL_NEW_KEYWORD(SQL_TOKEN_VAR_POP); } +VAR_SAMP {SQL_NEW_KEYWORD(SQL_TOKEN_VAR_SAMP); } +VALUE {SQL_NEW_KEYWORD(SQL_TOKEN_VALUE); } +VALUES {SQL_NEW_KEYWORD(SQL_TOKEN_VALUES); } +VIEW {SQL_NEW_KEYWORD(SQL_TOKEN_VIEW); } + +WEEK {SQL_NEW_KEYWORD(SQL_TOKEN_WEEK); } +WEEKDAY {SQL_NEW_KEYWORD(SQL_TOKEN_WEEKDAY); } +WHEN {SQL_NEW_KEYWORD(SQL_TOKEN_WHEN); } +WHERE {SQL_NEW_KEYWORD(SQL_TOKEN_WHERE); } +WITH {SQL_NEW_KEYWORD(SQL_TOKEN_WITH); } +WITHIN {SQL_NEW_KEYWORD(SQL_TOKEN_WITHIN); } +WITHOUT {SQL_NEW_KEYWORD(SQL_TOKEN_WITHOUT); } +WORK {SQL_NEW_KEYWORD(SQL_TOKEN_WORK); } + +YEAR {SQL_NEW_KEYWORD(SQL_TOKEN_YEAR); } +YEARDAY {SQL_NEW_KEYWORD(SQL_TOKEN_YEARDAY); } + +ZONE {SQL_NEW_KEYWORD(SQL_TOKEN_ZONE); } + +"<" { SQL_NEW_NODE(OUString(SQLyytext,strlen(SQLyytext),RTL_TEXTENCODING_UTF8), SQLNodeType::Less);return SQL_LESS;} +">" { SQL_NEW_NODE(OUString(SQLyytext,strlen(SQLyytext),RTL_TEXTENCODING_UTF8), SQLNodeType::Great);return SQL_GREAT;} +"=" { SQL_NEW_NODE(OUString(SQLyytext,strlen(SQLyytext),RTL_TEXTENCODING_UTF8), SQLNodeType::Equal);return SQL_EQUAL;} +"<=" { SQL_NEW_NODE(OUString(SQLyytext,strlen(SQLyytext),RTL_TEXTENCODING_UTF8), SQLNodeType::LessEq);return SQL_LESSEQ;} +">=" { SQL_NEW_NODE(OUString(SQLyytext,strlen(SQLyytext),RTL_TEXTENCODING_UTF8), SQLNodeType::GreatEq);return SQL_GREATEQ;} +"<>" { SQL_NEW_NODE(OUString(SQLyytext,strlen(SQLyytext),RTL_TEXTENCODING_UTF8), SQLNodeType::NotEqual);return SQL_NOTEQUAL;} +"!=" { SQL_NEW_NODE(OUString(SQLyytext,strlen(SQLyytext),RTL_TEXTENCODING_UTF8), SQLNodeType::NotEqual);return SQL_NOTEQUAL;} +"||" { SQL_NEW_NODE(OUString(SQLyytext,strlen(SQLyytext),RTL_TEXTENCODING_UTF8), SQLNodeType::Concat);return SQL_CONCAT;} + + +[-+*/:(),.;?{}] { return SQLyytext[0]; } + + +<SQL>[A-Za-z\200-\277\300-\337\340-\357\360-\367\370-\373\374-\375][A-Za-z\200-\277\300-\337\340-\357\360-\367\370-\373\374-\375_0-9]* {return gatherName( SQLyytext);} + +<SQL>([0-9]+) {SQL_NEW_INTNUM; } + +<SQL>("."[0-9]*) | +<SQL>([0-9]+"."[0-9]*) | +<SQL>[0-9]+[eE][+-]?[0-9]+ | +<SQL>[0-9]+"."[0-9]*[eE][+-]?[0-9]+ | +<SQL>"."[0-9]*[eE][+-]?[0-9]+ {SQL_NEW_APPROXNUM; } + +<PREDICATE_GER,PREDICATE_ENG,DATE>[A-Za-z\200-\277\300-\337\340-\357\360-\367\370-\373\374-\375][A-Za-z0-9_%.,*?\200-\277\300-\337\340-\357\360-\367\370-\373\374-\375]* {return gatherNamePre(SQLyytext);} + +<PREDICATE_GER,PREDICATE_ENG>([0-9]+) {SQL_NEW_INTNUM; } +<PREDICATE_ENG>([0-9]{1,3}(","[0-9]{3})+) {SQL_NEW_INTNUM; } +<PREDICATE_GER>([0-9]{1,3}("."[0-9]{3})+) {SQL_NEW_INTNUM; } + +<PREDICATE_ENG>([0-9]+"."[0-9]+) | +<PREDICATE_ENG>([0-9]{1,3}(","[0-9]{3})+"."[0-9]+) | +<PREDICATE_ENG>("."[0-9]+) {SQL_NEW_APPROXNUM; } +<PREDICATE_ENG>[0-9]+[eE][+-]?[0-9]+ | +<PREDICATE_ENG>[0-9]+"."[0-9]*[eE][+-]?[0-9]+ | +<PREDICATE_ENG>"."[0-9]*[eE][+-]?[0-9]+ {SQL_NEW_APPROXNUM; } + +<PREDICATE_GER>([0-9]+","[0-9]+) | +<PREDICATE_GER>([0-9]{1,3}("."[0-9]{3})+","[0-9]+) | +<PREDICATE_GER>(","[0-9]+) {SQL_NEW_APPROXNUM; } +<PREDICATE_GER>[0-9]+[eE][+-]?[0-9]+ | +<PREDICATE_GER>[0-9]+","[0-9]*[eE][+-]?[0-9]+ | +<PREDICATE_GER>","[0-9]*[eE][+-]?[0-9]+ {SQL_NEW_APPROXNUM; } + +<PREDICATE_GER,PREDICATE_ENG>[0-9.,][A-Za-z0-9_.,%]* {return gatherNamePre(SQLyytext);} + +<SQL>\" { return gatherString('\"',0); } +<SQL>` { return gatherString('`' ,0); } + +<PREDICATE_GER,PREDICATE_ENG,DATE,SQL>"[" { return gatherString(']' ,0);} + +\' { return gatherString('\'',1); } + +<PREDICATE_GER,PREDICATE_ENG,DATE># { return gatherString('#' ,2); } + +<DATE>[0-9]{1,4}[^ ]*[0-9] | +<DATE>[0-9]{1,4}[^ ]*[0-9][ ][0-9]{1,4}[^ ]*[0-9] { SQL_NEW_DATE; } + +<STRING>["-""+""*""/"":""("")"",""."";""?""{""}"] { return SQLyytext[0]; } /* */ +<STRING>"[" { return gatherString(']' ,0); } +<STRING>[^ ':["?"]* { return gatherNamePre(SQLyytext); } + +\n {} + +[ \t\r]+ {} + +"--".*$ {} + +. {YY_FATAL_ERROR("Invalid symbol"); return SQL_TOKEN_INVALIDSYMBOL;} + +%% + +// Kludge around a bug (well, Posix incompatibility) in flex 2.5.x +// http://bugs.debian.org/cgi-bin/bugreport.cgi?archive=no&bug=189332 +#if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 + + #ifndef YY_FLUSH_BUFFER + #define YY_FLUSH_BUFFER SQLyy_flush_buffer(YY_CURRENT_BUFFER ) + #endif + + #ifndef yytext_ptr + #define yytext_ptr SQLyytext + #endif + +#endif + +// Versions of flex apparently differ in whether input() resp. yyinput() returns +// zero or EOF upon end of file: +inline bool checkeof(int c) { return c == 0 || c == EOF; } + +/* + * Read SQL string literal + * Valid strings: + * '' 'a string' 'quote '' within string' + * "" "a string" "quote "" within string" + * nTyp == 0 -> SQLNodeType::Name + * nTyp == 1 -> SQLNodeType::String + * nTyp == 2 -> SQLNodeType::AccessDate + */ +sal_Int32 gatherString(int delim, sal_Int32 nTyp) +{ + int ch; + OStringBuffer sBuffer(256); + + assert(nTyp == 0 || nTyp == 1 || nTyp == 2); + + while (!checkeof(ch = yyinput())) + { + if (ch == delim) + { + if ((ch = yyinput()) != delim) + { + if (!checkeof(ch)) + unput(ch); + + switch(nTyp) + { + case 0: + SQL_NEW_NODE(OStringToOUString(sBuffer.makeStringAndClear(),RTL_TEXTENCODING_UTF8), SQLNodeType::Name); + return SQL_TOKEN_NAME; + case 1: + SQL_NEW_NODE(OStringToOUString(sBuffer.makeStringAndClear(),RTL_TEXTENCODING_UTF8), SQLNodeType::String); + return SQL_TOKEN_STRING; + case 2: + SQL_NEW_NODE(OStringToOUString(sBuffer.makeStringAndClear(),RTL_TEXTENCODING_UTF8), SQLNodeType::AccessDate); + return SQL_TOKEN_ACCESS_DATE; + } + } + else + { + sBuffer.append(static_cast<char>(ch)); + } + + } + else if (nTyp == 2 && (ch == '\r' || ch == '\n') ) + break; + else + { + sBuffer.append(static_cast<char>(ch)); + } + } + YY_FATAL_ERROR("Unterminated name string"); + return SQL_TOKEN_INVALIDSYMBOL; +} + +sal_Int32 mapEnumToToken(IParseContext::InternationalKeyCode _eKeyCode ) +{ + sal_Int32 nTokenID = 0; + switch( _eKeyCode ) + { + case IParseContext::InternationalKeyCode::Like: nTokenID = SQL_TOKEN_LIKE; break; + case IParseContext::InternationalKeyCode::Not: nTokenID = SQL_TOKEN_NOT; break; + case IParseContext::InternationalKeyCode::Null: nTokenID = SQL_TOKEN_NULL; break; + case IParseContext::InternationalKeyCode::True: nTokenID = SQL_TOKEN_TRUE; break; + case IParseContext::InternationalKeyCode::False: nTokenID = SQL_TOKEN_FALSE; break; + case IParseContext::InternationalKeyCode::Is: nTokenID = SQL_TOKEN_IS; break; + case IParseContext::InternationalKeyCode::Between: nTokenID = SQL_TOKEN_BETWEEN; break; + case IParseContext::InternationalKeyCode::Or: nTokenID = SQL_TOKEN_OR; break; + case IParseContext::InternationalKeyCode::And: nTokenID = SQL_TOKEN_AND; break; + case IParseContext::InternationalKeyCode::Avg: nTokenID = SQL_TOKEN_AVG; break; + case IParseContext::InternationalKeyCode::Count: nTokenID = SQL_TOKEN_COUNT; break; + case IParseContext::InternationalKeyCode::Max: nTokenID = SQL_TOKEN_MAX; break; + case IParseContext::InternationalKeyCode::Min: nTokenID = SQL_TOKEN_MIN; break; + case IParseContext::InternationalKeyCode::Sum: nTokenID = SQL_TOKEN_SUM; break; + case IParseContext::InternationalKeyCode::Every: nTokenID = SQL_TOKEN_EVERY; break; + case IParseContext::InternationalKeyCode::Any: nTokenID = SQL_TOKEN_ANY; break; + case IParseContext::InternationalKeyCode::Some: nTokenID = SQL_TOKEN_SOME; break; + case IParseContext::InternationalKeyCode::StdDevPop: nTokenID = SQL_TOKEN_STDDEV_POP; break; + case IParseContext::InternationalKeyCode::StdDevSamp: nTokenID = SQL_TOKEN_STDDEV_SAMP; break; + case IParseContext::InternationalKeyCode::VarSamp: nTokenID = SQL_TOKEN_VAR_SAMP; break; + case IParseContext::InternationalKeyCode::VarPop: nTokenID = SQL_TOKEN_VAR_POP; break; + case IParseContext::InternationalKeyCode::Collect: nTokenID = SQL_TOKEN_COLLECT; break; + case IParseContext::InternationalKeyCode::Fusion: nTokenID = SQL_TOKEN_FUSION; break; + case IParseContext::InternationalKeyCode::Intersection: nTokenID = SQL_TOKEN_INTERSECTION; break; + default: + OSL_FAIL( "mapEnumToToken: unsupported key!" ); + } + return nTokenID; +} +/* + * Read SQL Name literal + * Valid Names or international keywords: + * As we have international keywords, we test first on them + */ +sal_Int32 gatherName(const char* text) +{ + sal_Int32 nToken; + OSL_ENSURE(xxx_pGLOBAL_SQLSCAN,"You forgot to set the scanner!"); + IParseContext::InternationalKeyCode eKeyCode = xxx_pGLOBAL_SQLSCAN->getInternationalTokenID(text); + switch (eKeyCode) + { + case IParseContext::InternationalKeyCode::Like: + case IParseContext::InternationalKeyCode::Not: + case IParseContext::InternationalKeyCode::Null: + case IParseContext::InternationalKeyCode::True: + case IParseContext::InternationalKeyCode::False: + case IParseContext::InternationalKeyCode::Is: + case IParseContext::InternationalKeyCode::Between: + case IParseContext::InternationalKeyCode::Or: + case IParseContext::InternationalKeyCode::And: + case IParseContext::InternationalKeyCode::Count: + case IParseContext::InternationalKeyCode::Avg: + case IParseContext::InternationalKeyCode::Max: + case IParseContext::InternationalKeyCode::Min: + case IParseContext::InternationalKeyCode::Sum: + case IParseContext::InternationalKeyCode::Every: + case IParseContext::InternationalKeyCode::Any: + case IParseContext::InternationalKeyCode::Some: + case IParseContext::InternationalKeyCode::StdDevPop: + case IParseContext::InternationalKeyCode::StdDevSamp: + case IParseContext::InternationalKeyCode::VarSamp: + case IParseContext::InternationalKeyCode::VarPop: + case IParseContext::InternationalKeyCode::Collect: + case IParseContext::InternationalKeyCode::Fusion: + case IParseContext::InternationalKeyCode::Intersection: + nToken = mapEnumToToken(eKeyCode); + SQL_NEW_KEYWORD(nToken); + break; + default: + SQL_NEW_NODE(OUString(text,strlen(text),RTL_TEXTENCODING_UTF8), SQLNodeType::Name); + return SQL_TOKEN_NAME; + } +} +/** + Read SQL Name literal for predicate check + Valid Names or international keywords: + As we have international keywords, we test first on them +*/ +sal_Int32 gatherNamePre(const char* text) +{ + sal_Int32 nToken; + OSL_ENSURE(xxx_pGLOBAL_SQLSCAN,"You forgot to set the scanner!"); + IParseContext::InternationalKeyCode eKeyCode = xxx_pGLOBAL_SQLSCAN->getInternationalTokenID(text); + switch (eKeyCode) + { + case IParseContext::InternationalKeyCode::Like: + case IParseContext::InternationalKeyCode::Not: + case IParseContext::InternationalKeyCode::Null: + case IParseContext::InternationalKeyCode::True: + case IParseContext::InternationalKeyCode::False: + case IParseContext::InternationalKeyCode::Is: + case IParseContext::InternationalKeyCode::Between: + case IParseContext::InternationalKeyCode::Or: + case IParseContext::InternationalKeyCode::And: + case IParseContext::InternationalKeyCode::Count: + case IParseContext::InternationalKeyCode::Avg: + case IParseContext::InternationalKeyCode::Max: + case IParseContext::InternationalKeyCode::Min: + case IParseContext::InternationalKeyCode::Sum: + case IParseContext::InternationalKeyCode::Every: + case IParseContext::InternationalKeyCode::Any: + case IParseContext::InternationalKeyCode::Some: + case IParseContext::InternationalKeyCode::StdDevPop: + case IParseContext::InternationalKeyCode::StdDevSamp: + case IParseContext::InternationalKeyCode::VarSamp: + case IParseContext::InternationalKeyCode::VarPop: + case IParseContext::InternationalKeyCode::Collect: + case IParseContext::InternationalKeyCode::Fusion: + case IParseContext::InternationalKeyCode::Intersection: + nToken = mapEnumToToken(eKeyCode); + SQL_NEW_KEYWORD(nToken); + break; + default: + // we need a special handling for parameter + { + OString sStmt = xxx_pGLOBAL_SQLSCAN->getStatement(); + sal_Int32 nLength = strlen(text); + sal_Int32 nPos = xxx_pGLOBAL_SQLSCAN->GetCurrentPos() - nLength - 2; + if (sStmt.getStr()[nPos] == ':') + { + SQL_NEW_NODE(OUString(text,nLength,RTL_TEXTENCODING_UTF8), SQLNodeType::Name); + nToken = SQL_TOKEN_NAME; + } + else + { + SQL_NEW_NODE(OUString(text,nLength,RTL_TEXTENCODING_UTF8), SQLNodeType::String); + nToken = SQL_TOKEN_STRING; + } + } + } + return nToken; +} + +using namespace connectivity; + +static bool IN_SQLyyerror; +//------------------------------------------------------------------------------ +OSQLScanner::OSQLScanner() + : m_pContext(nullptr) + , m_nCurrentPos(0) + , m_bInternational(false) + , m_nRule(0) // 0 is INITIAL +{ + IN_SQLyyerror = false; +} + +//------------------------------------------------------------------------------ +OSQLScanner::~OSQLScanner() +{ +} +//------------------------------------------------------------------------------ +void OSQLScanner::SQLyyerror(char const *fmt) +{ + + if(IN_SQLyyerror) + return; + IN_SQLyyerror = true; + + OSL_ENSURE(m_pContext, "OSQLScanner::SQLyyerror: No Context set"); + m_sErrorMessage = OUString(fmt,strlen(fmt),RTL_TEXTENCODING_UTF8); + if (m_nCurrentPos < m_sStatement.getLength()) + { + m_sErrorMessage += ": "; + + OUString aError; + OUStringBuffer Buffer(256); + + int ch = SQLyytext ? (SQLyytext[0] == 0 ? ' ' : SQLyytext[0]): ' '; + Buffer.append((sal_Unicode)ch); + while (!checkeof(ch = yyinput())) + { + if (ch == ' ') + { + if ((ch = yyinput()) != ' ') + { + if (!checkeof(ch)) + unput(ch); + } + aError = Buffer.makeStringAndClear(); + break; + } + else + { + Buffer.append((sal_Unicode)ch); + } + } + m_sErrorMessage += aError; + } + IN_SQLyyerror = false; + YY_FLUSH_BUFFER; +} + +//------------------------------------------------------------------------------ +void OSQLScanner::prepareScan(const OUString & rNewStatement, const IParseContext* pContext, bool bInternational) +{ + YY_FLUSH_BUFFER; + BEGIN(m_nRule); + + m_sErrorMessage = OUString(); + m_sStatement = OUStringToOString(rNewStatement, RTL_TEXTENCODING_UTF8); + m_nCurrentPos = 0; + m_bInternational = bInternational; + m_pContext = pContext; +} + +//------------------------------------------------------------------------------ +sal_Int32 OSQLScanner::SQLyygetc(void) +{ + sal_Int32 nPos = (m_nCurrentPos >= m_sStatement.getLength()) ? EOF : m_sStatement.getStr()[m_nCurrentPos]; + m_nCurrentPos++; + return nPos; +} + +//------------------------------------------------------------------------------ +IParseContext::InternationalKeyCode OSQLScanner::getInternationalTokenID(const char* sToken) const +{ + OSL_ENSURE(m_pContext, "OSQLScanner::getInternationalTokenID: No Context set"); + return (m_bInternational) ? m_pContext->getIntlKeyCode(OString(sToken) ) : IParseContext::InternationalKeyCode::None; +} +sal_Int32 OSQLScanner::GetGERRule() { return PREDICATE_GER; } +sal_Int32 OSQLScanner::GetENGRule() { return PREDICATE_ENG; } +sal_Int32 OSQLScanner::GetSQLRule() { return SQL; } +sal_Int32 OSQLScanner::GetDATERule() { return DATE; } +sal_Int32 OSQLScanner::GetSTRINGRule() { return STRING; } +void OSQLScanner::setScanner(bool _bNull) +{ + xxx_pGLOBAL_SQLSCAN = _bNull ? nullptr : this; +} +sal_Int32 OSQLScanner::SQLlex() +{ + return SQLyylex(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/connectivity/source/parse/sqliterator.cxx b/connectivity/source/parse/sqliterator.cxx new file mode 100644 index 000000000..fcebd2879 --- /dev/null +++ b/connectivity/source/parse/sqliterator.cxx @@ -0,0 +1,2117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <connectivity/sqliterator.hxx> +#include <connectivity/sdbcx/VTable.hxx> +#include <connectivity/sqlparse.hxx> +#include <sqlbison.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/sqlerror.hxx> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdb/ErrorCondition.hpp> +#ifdef SQL_TEST_PARSETREEITERATOR +#include <iostream> +#endif +#include <connectivity/PColumn.hxx> +#include <tools/diagnose_ex.h> +#include <TConnection.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbmetadata.hxx> +#include <com/sun/star/sdb/SQLFilterOperator.hpp> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> + +#include <iterator> +#include <memory> + +using namespace ::comphelper; +using namespace ::connectivity; +using namespace ::connectivity::sdbcx; +using namespace ::dbtools; +using namespace ::connectivity::parse; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; + +namespace connectivity +{ + struct OSQLParseTreeIteratorImpl + { + std::vector< TNodePair > m_aJoinConditions; + Reference< XConnection > m_xConnection; + Reference< XDatabaseMetaData > m_xDatabaseMetaData; + Reference< XNameAccess > m_xTableContainer; + Reference< XNameAccess > m_xQueryContainer; + + std::shared_ptr< OSQLTables > m_pTables; // all tables which participate in the SQL statement + std::shared_ptr< OSQLTables > m_pSubTables; // all tables from sub queries not the tables from the select tables + std::shared_ptr< QueryNameSet > m_pForbiddenQueryNames; + + TraversalParts m_nIncludeMask; + + bool m_bIsCaseSensitive; + + OSQLParseTreeIteratorImpl( const Reference< XConnection >& _rxConnection, const Reference< XNameAccess >& _rxTables ) + :m_xConnection( _rxConnection ) + ,m_nIncludeMask( TraversalParts::All ) + ,m_bIsCaseSensitive( true ) + { + OSL_PRECOND( m_xConnection.is(), "OSQLParseTreeIteratorImpl::OSQLParseTreeIteratorImpl: invalid connection!" ); + m_xDatabaseMetaData = m_xConnection->getMetaData(); + + m_bIsCaseSensitive = m_xDatabaseMetaData.is() && m_xDatabaseMetaData->supportsMixedCaseQuotedIdentifiers(); + m_pTables = std::make_shared<OSQLTables>( m_bIsCaseSensitive ); + m_pSubTables = std::make_shared<OSQLTables>( m_bIsCaseSensitive ); + + m_xTableContainer = _rxTables; + + DatabaseMetaData aMetaData( m_xConnection ); + if ( aMetaData.supportsSubqueriesInFrom() ) + { + // connections might support the XQueriesSupplier interface, if they implement the css.sdb.Connection + // service + Reference< XQueriesSupplier > xSuppQueries( m_xConnection, UNO_QUERY ); + if ( xSuppQueries.is() ) + m_xQueryContainer = xSuppQueries->getQueries(); + } + } + + public: + bool isQueryAllowed( const OUString& _rQueryName ) + { + if ( !m_pForbiddenQueryNames ) + return true; + if ( m_pForbiddenQueryNames->find( _rQueryName ) == m_pForbiddenQueryNames->end() ) + return true; + return false; + } + }; + + namespace { + + /** helper class for temporarily adding a query name to a list of forbidden query names + */ + class ForbidQueryName + { + std::shared_ptr< QueryNameSet >& m_rpAllForbiddenNames; + OUString m_sForbiddenQueryName; + + public: + ForbidQueryName( OSQLParseTreeIteratorImpl& _rIteratorImpl, const OUString& _rForbiddenQueryName ) + :m_rpAllForbiddenNames( _rIteratorImpl.m_pForbiddenQueryNames ) + ,m_sForbiddenQueryName( _rForbiddenQueryName ) + { + if ( !m_rpAllForbiddenNames ) + m_rpAllForbiddenNames = std::make_shared<QueryNameSet>(); + m_rpAllForbiddenNames->insert( m_sForbiddenQueryName ); + } + + ~ForbidQueryName() + { + m_rpAllForbiddenNames->erase( m_sForbiddenQueryName ); + } + }; + + } +} + +OSQLParseTreeIterator::OSQLParseTreeIterator(const Reference< XConnection >& _rxConnection, + const Reference< XNameAccess >& _rxTables, + const OSQLParser& _rParser ) + :m_rParser( _rParser ) + ,m_pImpl( new OSQLParseTreeIteratorImpl( _rxConnection, _rxTables ) ) +{ + setParseTree(nullptr); +} + + +OSQLParseTreeIterator::OSQLParseTreeIterator( const OSQLParseTreeIterator& _rParentIterator, const OSQLParser& _rParser, const OSQLParseNode* pRoot ) + :m_rParser( _rParser ) + ,m_pImpl( new OSQLParseTreeIteratorImpl( _rParentIterator.m_pImpl->m_xConnection, _rParentIterator.m_pImpl->m_xTableContainer ) ) +{ + m_pImpl->m_pForbiddenQueryNames = _rParentIterator.m_pImpl->m_pForbiddenQueryNames; + setParseTree( pRoot ); +} + + +OSQLParseTreeIterator::~OSQLParseTreeIterator() +{ + dispose(); +} + + +const OSQLTables& OSQLParseTreeIterator::getTables() const +{ + return *m_pImpl->m_pTables; +} + + +bool OSQLParseTreeIterator::isCaseSensitive() const +{ + return m_pImpl->m_bIsCaseSensitive; +} + + +void OSQLParseTreeIterator::dispose() +{ + m_aSelectColumns = nullptr; + m_aGroupColumns = nullptr; + m_aOrderColumns = nullptr; + m_aParameters = nullptr; + m_pImpl->m_xTableContainer = nullptr; + m_pImpl->m_xDatabaseMetaData = nullptr; + m_aCreateColumns = nullptr; + m_pImpl->m_pTables->clear(); + m_pImpl->m_pSubTables->clear(); +} + +void OSQLParseTreeIterator::setParseTree(const OSQLParseNode * pNewParseTree) +{ + m_pImpl->m_pTables->clear(); + m_pImpl->m_pSubTables->clear(); + + m_aSelectColumns = new OSQLColumns(); + m_aGroupColumns = new OSQLColumns(); + m_aOrderColumns = new OSQLColumns(); + m_aParameters = new OSQLColumns(); + m_aCreateColumns = new OSQLColumns(); + + m_pParseTree = pNewParseTree; + if (!m_pParseTree) + { + m_eStatementType = OSQLStatementType::Unknown; + return; + } + + // If m_pParseTree, but no connection then return + if ( !m_pImpl->m_xTableContainer.is() ) + return; + + m_xErrors.reset(); + + + // Determine statement type ... + if (SQL_ISRULE(m_pParseTree,select_statement) || SQL_ISRULE(m_pParseTree,union_statement) ) + { + m_eStatementType = OSQLStatementType::Select; + } + else if (SQL_ISRULE(m_pParseTree,insert_statement)) + { + m_eStatementType = OSQLStatementType::Insert; + } + else if (SQL_ISRULE(m_pParseTree,update_statement_searched)) + { + m_eStatementType = OSQLStatementType::Update; + } + else if (SQL_ISRULE(m_pParseTree,delete_statement_searched)) + { + m_eStatementType = OSQLStatementType::Delete; + } + else if (m_pParseTree->count() == 3 && SQL_ISRULE(m_pParseTree->getChild(1),odbc_call_spec)) + { + m_eStatementType = OSQLStatementType::OdbcCall; + } + else if (SQL_ISRULE(m_pParseTree->getChild(0),base_table_def)) + { + m_eStatementType = OSQLStatementType::CreateTable; + m_pParseTree = m_pParseTree->getChild(0); + } + else + { + m_eStatementType = OSQLStatementType::Unknown; + //aIteratorStatus.setInvalidStatement(); + return; + } +} + + +namespace +{ + + void impl_getRowString( const Reference< XRow >& _rxRow, const sal_Int32 _nColumnIndex, OUString& _out_rString ) + { + _out_rString = _rxRow->getString( _nColumnIndex ); + if ( _rxRow->wasNull() ) + _out_rString.clear(); + } + + + OUString lcl_findTableInMetaData( + const Reference< XDatabaseMetaData >& _rxDBMeta, const OUString& _rCatalog, + const OUString& _rSchema, const OUString& _rTableName ) + { + OUString sComposedName; + + static constexpr OUStringLiteral s_sWildcard = u"%" ; + + // we want all catalogues, all schemas, all tables + Sequence< OUString > sTableTypes { "VIEW", "TABLE", s_sWildcard }; // this last one just to be sure to include anything else... + + if ( _rxDBMeta.is() ) + { + sComposedName.clear(); + + Reference< XResultSet> xRes = _rxDBMeta->getTables( + !_rCatalog.isEmpty() ? Any( _rCatalog ) : Any(), !_rSchema.isEmpty() ? _rSchema : s_sWildcard, _rTableName, sTableTypes ); + + Reference< XRow > xCurrentRow( xRes, UNO_QUERY ); + if ( xCurrentRow.is() && xRes->next() ) + { + OUString sCatalog, sSchema, sName; + + impl_getRowString( xCurrentRow, 1, sCatalog ); + impl_getRowString( xCurrentRow, 2, sSchema ); + impl_getRowString( xCurrentRow, 3, sName ); + + sComposedName = ::dbtools::composeTableName( + _rxDBMeta, + sCatalog, + sSchema, + sName, + false, + ::dbtools::EComposeRule::InDataManipulation + ); + } + } + return sComposedName; + } +} + + +void OSQLParseTreeIterator::impl_getQueryParameterColumns( const OSQLTable& _rQuery ) +{ + if ( !( m_pImpl->m_nIncludeMask & TraversalParts::Parameters ) ) + // parameters not to be included in the traversal + return; + + ::rtl::Reference pSubQueryParameterColumns( new OSQLColumns() ); + + // get the command and the EscapeProcessing properties from the sub query + OUString sSubQueryCommand; + bool bEscapeProcessing = false; + try + { + Reference< XPropertySet > xQueryProperties( _rQuery, UNO_QUERY_THROW ); + OSL_VERIFY( xQueryProperties->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_COMMAND ) ) >>= sSubQueryCommand ); + OSL_VERIFY( xQueryProperties->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_ESCAPEPROCESSING ) ) >>= bEscapeProcessing ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.parse"); + } + + // parse the sub query + do { + + if ( !bEscapeProcessing || ( sSubQueryCommand.isEmpty() ) ) + break; + + OUString sError; + std::unique_ptr< OSQLParseNode > pSubQueryNode( const_cast< OSQLParser& >( m_rParser ).parseTree( sError, sSubQueryCommand ) ); + if (!pSubQueryNode) + break; + + OSQLParseTreeIterator aSubQueryIterator( *this, m_rParser, pSubQueryNode.get() ); + aSubQueryIterator.impl_traverse( TraversalParts::Parameters | TraversalParts::SelectColumns ); + // SelectColumns might also contain parameters #i77635# + pSubQueryParameterColumns = aSubQueryIterator.getParameters(); + aSubQueryIterator.dispose(); + + } while ( false ); + + // copy the parameters of the sub query to our own parameter array + m_aParameters->insert( m_aParameters->end(), pSubQueryParameterColumns->begin(), pSubQueryParameterColumns->end() ); +} + + +OSQLTable OSQLParseTreeIterator::impl_locateRecordSource( const OUString& _rComposedName ) +{ + if ( _rComposedName.isEmpty() ) + { + SAL_WARN( "connectivity.parse", "OSQLParseTreeIterator::impl_locateRecordSource: no object name at all?" ); + return OSQLTable(); + } + + OSQLTable aReturn; + OUString sComposedName( _rComposedName ); + + try + { + OUString sCatalog, sSchema, sName; + qualifiedNameComponents( m_pImpl->m_xDatabaseMetaData, sComposedName, sCatalog, sSchema, sName, ::dbtools::EComposeRule::InDataManipulation ); + + // check whether there is a query with the given name + bool bQueryDoesExist = m_pImpl->m_xQueryContainer.is() && m_pImpl->m_xQueryContainer->hasByName( sComposedName ); + + // check whether the table container contains an object with the given name + if ( !bQueryDoesExist && !m_pImpl->m_xTableContainer->hasByName( sComposedName ) ) + sComposedName = lcl_findTableInMetaData( m_pImpl->m_xDatabaseMetaData, sCatalog, sSchema, sName ); + bool bTableDoesExist = m_pImpl->m_xTableContainer->hasByName( sComposedName ); + + // now obtain the object + + // if we're creating a table, and there already is a table or query with the same name, + // this is worth an error + if ( OSQLStatementType::CreateTable == m_eStatementType ) + { + if ( bQueryDoesExist ) + impl_appendError( IParseContext::ErrorCode::InvalidQueryExist, &sName ); + else if ( bTableDoesExist ) + impl_appendError( IParseContext::ErrorCode::InvalidTableExist, &sName ); + else + aReturn = impl_createTableObject( sName, sCatalog, sSchema ); + } + else + { + // queries win over tables, so if there's a query with this name, take this, no matter if + // there's a table, too + if ( bQueryDoesExist ) + { + if ( !m_pImpl->isQueryAllowed( sComposedName ) ) + { + impl_appendError( m_rParser.getErrorHelper().getSQLException( sdb::ErrorCondition::PARSER_CYCLIC_SUB_QUERIES, nullptr ) ); + return nullptr; + } + + m_pImpl->m_xQueryContainer->getByName( sComposedName ) >>= aReturn; + + // collect the parameters from the sub query + ForbidQueryName aForbidName( *m_pImpl, sComposedName ); + impl_getQueryParameterColumns( aReturn ); + } + else if ( bTableDoesExist ) + m_pImpl->m_xTableContainer->getByName( sComposedName ) >>= aReturn; + else + { + if ( m_pImpl->m_xQueryContainer.is() ) + // the connection on which we're working supports sub queries in from (else + // m_xQueryContainer would not have been set), so emit a better error message + impl_appendError( IParseContext::ErrorCode::InvalidTableOrQuery, &sName ); + else + impl_appendError( IParseContext::ErrorCode::InvalidTableNosuch, &sName ); + } + } + } + catch(Exception&) + { + impl_appendError( IParseContext::ErrorCode::InvalidTableNosuch, &sComposedName ); + } + + return aReturn; +} + + +void OSQLParseTreeIterator::traverseOneTableName( OSQLTables& _rTables,const OSQLParseNode * pTableName, const OUString & rTableRange ) +{ + if ( !( m_pImpl->m_nIncludeMask & TraversalParts::TableNames ) ) + // tables should not be included in the traversal + return; + + OSL_ENSURE(pTableName != nullptr,"OSQLParseTreeIterator::traverseOneTableName: pTableName == NULL"); + + Any aCatalog; + OUString aSchema,aTableName,aComposedName; + OUString aTableRange(rTableRange); + + // Get table name + OSQLParseNode::getTableComponents(pTableName,aCatalog,aSchema,aTableName,m_pImpl->m_xDatabaseMetaData); + + // create the composed name like DOMAIN.USER.TABLE1 + aComposedName = ::dbtools::composeTableName(m_pImpl->m_xDatabaseMetaData, + aCatalog.hasValue() ? ::comphelper::getString(aCatalog) : OUString(), + aSchema, + aTableName, + false, + ::dbtools::EComposeRule::InDataManipulation); + + // if there is no alias for the table name assign the original name to it + if ( aTableRange.isEmpty() ) + aTableRange = aComposedName; + + // get the object representing this table/query + OSQLTable aTable = impl_locateRecordSource( aComposedName ); + if ( aTable.is() ) + _rTables[ aTableRange ] = aTable; +} + +void OSQLParseTreeIterator::impl_fillJoinConditions(const OSQLParseNode* i_pJoinCondition) +{ + if (i_pJoinCondition->count() == 3 && // Expression with brackets + SQL_ISPUNCTUATION(i_pJoinCondition->getChild(0),"(") && + SQL_ISPUNCTUATION(i_pJoinCondition->getChild(2),")")) + { + impl_fillJoinConditions(i_pJoinCondition->getChild(1)); + } + else if (SQL_ISRULEOR2(i_pJoinCondition,search_condition,boolean_term) && // AND/OR logic operation: + i_pJoinCondition->count() == 3) + { + // Only allow AND logic operation + if ( SQL_ISTOKEN(i_pJoinCondition->getChild(1),AND) ) + { + impl_fillJoinConditions(i_pJoinCondition->getChild(0)); + impl_fillJoinConditions(i_pJoinCondition->getChild(1)); + } + } + else if (SQL_ISRULE(i_pJoinCondition,comparison_predicate)) + { + // only the comparison of columns is allowed + OSL_ENSURE(i_pJoinCondition->count() == 3,"OQueryDesignView::InsertJoinConnection: error in the parse tree"); + if (SQL_ISRULE(i_pJoinCondition->getChild(0),column_ref) && + SQL_ISRULE(i_pJoinCondition->getChild(2),column_ref) && + i_pJoinCondition->getChild(1)->getNodeType() == SQLNodeType::Equal) + { + m_pImpl->m_aJoinConditions.push_back( TNodePair(i_pJoinCondition->getChild(0),i_pJoinCondition->getChild(2)) ); + } + } +} + +std::vector< TNodePair >& OSQLParseTreeIterator::getJoinConditions() const +{ + return m_pImpl->m_aJoinConditions; +} + +void OSQLParseTreeIterator::getQualified_join( OSQLTables& _rTables, const OSQLParseNode *pTableRef, OUString& aTableRange ) +{ + OSL_PRECOND( SQL_ISRULE( pTableRef, cross_union ) || SQL_ISRULE( pTableRef, qualified_join ) , + "OSQLParseTreeIterator::getQualified_join: illegal node!" ); + + aTableRange.clear(); + + const OSQLParseNode* pNode = getTableNode(_rTables,pTableRef->getChild(0),aTableRange); + if ( isTableNode( pNode ) ) + traverseOneTableName( _rTables, pNode, aTableRange ); + + sal_uInt32 nPos = 4; + if( SQL_ISRULE(pTableRef,cross_union) || pTableRef->getChild(1)->getTokenID() != SQL_TOKEN_NATURAL) + { + nPos = 3; + // join_condition,named_columns_join + if ( SQL_ISRULE( pTableRef, qualified_join ) ) + { + const OSQLParseNode* pJoin_spec = pTableRef->getChild(4); + if ( SQL_ISRULE( pJoin_spec, join_condition ) ) + { + impl_fillJoinConditions(pJoin_spec->getChild(1)); + } + else + { + const OSQLParseNode* pColumnCommalist = pJoin_spec->getChild(2); + // All columns in the column_commalist ... + for (size_t i = 0; i < pColumnCommalist->count(); i++) + { + const OSQLParseNode * pCol = pColumnCommalist->getChild(i); + // add twice because the column must exists in both tables + m_pImpl->m_aJoinConditions.push_back( TNodePair(pCol,pCol) ); + } + } + } + } + + pNode = getTableNode(_rTables,pTableRef->getChild(nPos),aTableRange); + if ( isTableNode( pNode ) ) + traverseOneTableName( _rTables, pNode, aTableRange ); +} + +const OSQLParseNode* OSQLParseTreeIterator::getTableNode( OSQLTables& _rTables, const OSQLParseNode *pTableRef,OUString& rTableRange ) +{ + OSL_PRECOND( SQL_ISRULE( pTableRef, table_ref ) || SQL_ISRULE( pTableRef, joined_table ) + || SQL_ISRULE( pTableRef, qualified_join ) || SQL_ISRULE( pTableRef, cross_union ), + "OSQLParseTreeIterator::getTableNode: only to be called for table_ref nodes!" ); + + const OSQLParseNode* pTableNameNode = nullptr; + + if ( SQL_ISRULE( pTableRef, joined_table ) ) + { + getQualified_join( _rTables, pTableRef->getChild(1), rTableRange ); + } + if ( SQL_ISRULE( pTableRef, qualified_join ) || SQL_ISRULE( pTableRef, cross_union ) ) + { + getQualified_join( _rTables, pTableRef, rTableRange ); + } + else + { + rTableRange = OSQLParseNode::getTableRange(pTableRef); + if ( ( pTableRef->count() == 4 ) // '{' SQL_TOKEN_OJ joined_table '}' + || ( pTableRef->count() == 5 ) // '(' joined_table ')' range_variable op_column_commalist + ) + { + getQualified_join( _rTables, pTableRef->getChild(6 - pTableRef->count()), rTableRange ); + } + else if ( pTableRef->count() == 3 ) // subquery range_variable op_column_commalist || '(' joined_table ')' + { + const OSQLParseNode* pSubQuery = pTableRef->getChild(0); + if ( pSubQuery->isToken() ) + { + getQualified_join( _rTables, pTableRef->getChild(1), rTableRange ); + } + else + { + OSL_ENSURE( pSubQuery->count() == 3, "sub queries should have 3 children!" ); + const OSQLParseNode* pQueryExpression = pSubQuery->getChild(1); + if ( SQL_ISRULE( pQueryExpression, select_statement ) ) + { + getSelect_statement( *m_pImpl->m_pSubTables, pQueryExpression ); + // TODO: now, we need to setup an OSQLTable from pQueryExpression in some way + // and stick it in _rTables[rTableRange]. Probably fake it by + // setting up a full OSQLParseTreeIterator on pQueryExpression + // and using its m_aSelectColumns + // This is necessary in stuff like "SELECT * FROM tbl1 INNER JOIN (SELECT foo, bar FROM tbl2) AS tbl3" + // so that setSelectColumnName() can expand the "*" correctly. + // See e.g. R_UserAndLastSubscription query of https://bugs.libreoffice.org/attachment.cgi?id=71871 + } + else + { + SAL_WARN( "connectivity.parse", "OSQLParseTreeIterator::getTableNode: subquery which is no select_statement: not yet implemented!" ); + } + } + } + else if ( pTableRef->count() == 2 ) // table_node table_primary_as_range_column + { + pTableNameNode = pTableRef->getChild(0); + } + else + SAL_WARN( "connectivity.parse", "OSQLParseTreeIterator::getTableNode: unhandled case!" ); + } + + return pTableNameNode; +} + +void OSQLParseTreeIterator::getSelect_statement(OSQLTables& _rTables,const OSQLParseNode* pSelect) +{ + if(SQL_ISRULE(pSelect,union_statement)) + { + getSelect_statement(_rTables,pSelect->getChild(0)); + //getSelect_statement(pSelect->getChild(3)); + return; + } + OSQLParseNode * pTableRefCommalist = pSelect->getChild(3)->getChild(0)->getChild(1); + + OSL_ENSURE(pTableRefCommalist != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(SQL_ISRULE(pTableRefCommalist,table_ref_commalist),"OSQLParseTreeIterator: error in parse tree!"); + + const OSQLParseNode* pTableName = nullptr; + OUString aTableRange; + for (size_t i = 0; i < pTableRefCommalist->count(); i++) + { // Process FROM clause + aTableRange.clear(); + + const OSQLParseNode* pTableListElement = pTableRefCommalist->getChild(i); + if ( isTableNode( pTableListElement ) ) + { + traverseOneTableName( _rTables, pTableListElement, aTableRange ); + } + else if ( SQL_ISRULE( pTableListElement, table_ref ) ) + { + // Table references can be made up of table names, table names (+),'('joined_table')'(+) + pTableName = pTableListElement->getChild(0); + if( isTableNode( pTableName ) ) + { // Found table names + aTableRange = OSQLParseNode::getTableRange(pTableListElement); + traverseOneTableName( _rTables, pTableName, aTableRange ); + } + else if(SQL_ISPUNCTUATION(pTableName,"{")) + { // '{' SQL_TOKEN_OJ joined_table '}' + getQualified_join( _rTables, pTableListElement->getChild(2), aTableRange ); + } + else + { // '(' joined_table ')' range_variable op_column_commalist + getTableNode( _rTables, pTableListElement, aTableRange ); + } + } + else if (SQL_ISRULE( pTableListElement, qualified_join ) || SQL_ISRULE( pTableListElement, cross_union ) ) + { + getQualified_join( _rTables, pTableListElement, aTableRange ); + } + else if ( SQL_ISRULE( pTableListElement, joined_table ) ) + { + getQualified_join( _rTables, pTableListElement->getChild(1), aTableRange ); + } + + // if (! aIteratorStatus.IsSuccessful()) break; + } +} + +bool OSQLParseTreeIterator::traverseTableNames(OSQLTables& _rTables) +{ + if ( m_pParseTree == nullptr ) + return false; + + OSQLParseNode* pTableName = nullptr; + + switch ( m_eStatementType ) + { + case OSQLStatementType::Select: + getSelect_statement( _rTables, m_pParseTree ); + break; + + case OSQLStatementType::CreateTable: + case OSQLStatementType::Insert: + case OSQLStatementType::Delete: + pTableName = m_pParseTree->getChild(2); + break; + + case OSQLStatementType::Update: + pTableName = m_pParseTree->getChild(1); + break; + default: + break; + } + + if ( pTableName ) + { + traverseOneTableName( _rTables, pTableName, OUString() ); + } + + return !hasErrors(); +} + +OUString OSQLParseTreeIterator::getColumnAlias(const OSQLParseNode* _pDerivedColumn) +{ + OSL_ENSURE(SQL_ISRULE(_pDerivedColumn,derived_column),"No derived column!"); + OUString sColumnAlias; + if(_pDerivedColumn->getChild(1)->count() == 2) + sColumnAlias = _pDerivedColumn->getChild(1)->getChild(1)->getTokenValue(); + else if(!_pDerivedColumn->getChild(1)->isRule()) + sColumnAlias = _pDerivedColumn->getChild(1)->getTokenValue(); + return sColumnAlias; +} + + +namespace +{ + void lcl_getColumnRange( const OSQLParseNode* _pColumnRef, const Reference< XConnection >& _rxConnection, + OUString& _out_rColumnName, OUString& _out_rTableRange, + const OSQLColumns* _pSelectColumns, OUString& _out_rColumnAliasIfPresent ) + { + _out_rColumnName.clear(); + _out_rTableRange.clear(); + _out_rColumnAliasIfPresent.clear(); + if ( SQL_ISRULE( _pColumnRef, column_ref ) ) + { + if( _pColumnRef->count() > 1 ) + { + for ( sal_Int32 i=0; i<static_cast<sal_Int32>(_pColumnRef->count())-2; ++i ) + _pColumnRef->getChild(i)->parseNodeToStr( _out_rTableRange, _rxConnection, nullptr, false, false ); + _out_rColumnName = _pColumnRef->getChild( _pColumnRef->count()-1 )->getChild(0)->getTokenValue(); + } + else + _out_rColumnName = _pColumnRef->getChild(0)->getTokenValue(); + + // look up the column in the select column, to find a possible alias + if ( _pSelectColumns ) + { + for (const Reference< XPropertySet >& xColumn : *_pSelectColumns) + { + try + { + OUString sName, sTableName; + xColumn->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_REALNAME ) ) >>= sName; + xColumn->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_TABLENAME ) ) >>= sTableName; + if ( sName == _out_rColumnName && ( _out_rTableRange.isEmpty() || sTableName == _out_rTableRange ) ) + { + xColumn->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_NAME ) ) >>= _out_rColumnAliasIfPresent; + break; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.parse"); + } + } + } + } + else if(SQL_ISRULE(_pColumnRef,general_set_fct) || SQL_ISRULE(_pColumnRef,set_fct_spec)) + { // Function + _pColumnRef->parseNodeToStr( _out_rColumnName, _rxConnection ); + } + else if(_pColumnRef->getNodeType() == SQLNodeType::Name) + _out_rColumnName = _pColumnRef->getTokenValue(); + } +} + + +void OSQLParseTreeIterator::getColumnRange( const OSQLParseNode* _pColumnRef, + OUString& _rColumnName, + OUString& _rTableRange) const +{ + OUString sDummy; + lcl_getColumnRange( _pColumnRef, m_pImpl->m_xConnection, _rColumnName, _rTableRange, nullptr, sDummy ); +} + + +void OSQLParseTreeIterator::getColumnRange( const OSQLParseNode* _pColumnRef, + OUString& _rColumnName, + OUString& _rTableRange, + OUString& _out_rColumnAliasIfPresent ) const +{ + lcl_getColumnRange( _pColumnRef, m_pImpl->m_xConnection, _rColumnName, _rTableRange, &*m_aSelectColumns, _out_rColumnAliasIfPresent ); +} + + +void OSQLParseTreeIterator::getColumnRange( const OSQLParseNode* _pColumnRef, + const Reference< XConnection >& _rxConnection, OUString& _out_rColumnName, OUString& _out_rTableRange ) +{ + OUString sDummy; + lcl_getColumnRange( _pColumnRef, _rxConnection, _out_rColumnName, _out_rTableRange, nullptr, sDummy ); +} + + +void OSQLParseTreeIterator::traverseCreateColumns(const OSQLParseNode* pSelectNode) +{ + // aIteratorStatus.Clear(); + + if (!pSelectNode || m_eStatementType != OSQLStatementType::CreateTable || m_pImpl->m_pTables->empty()) + { + impl_appendError( IParseContext::ErrorCode::General ); + return; + } + if (!SQL_ISRULE(pSelectNode,base_table_element_commalist)) + return ; + + for (size_t i = 0; i < pSelectNode->count(); i++) + { + OSQLParseNode *pColumnRef = pSelectNode->getChild(i); + + if (SQL_ISRULE(pColumnRef,column_def)) + { + OUString aColumnName; + OUString aTypeName; + sal_Int32 nType = DataType::VARCHAR; + aColumnName = pColumnRef->getChild(0)->getTokenValue(); + + OSQLParseNode *pDatatype = pColumnRef->getChild(1); + if (pDatatype && SQL_ISRULE(pDatatype,character_string_type)) + { + const OSQLParseNode *pType = pDatatype->getChild(0); + aTypeName = pType->getTokenValue(); + if (pDatatype->count() == 2 && (pType->getTokenID() == SQL_TOKEN_CHAR || pType->getTokenID() == SQL_TOKEN_CHARACTER )) + nType = DataType::CHAR; + } + else if(pDatatype && pDatatype->getNodeType() == SQLNodeType::Keyword) + { + aTypeName = "VARCHAR"; + } + + if (!aTypeName.isEmpty()) + { + //TODO:Create a new class for create statement to handle field length + rtl::Reference<OParseColumn> pColumn = new OParseColumn(aColumnName,aTypeName,OUString(),OUString(), + ColumnValue::NULLABLE_UNKNOWN,0,0,nType,false,false,isCaseSensitive(), + OUString(),OUString(),OUString()); + pColumn->setFunction(false); + pColumn->setRealName(aColumnName); + + m_aCreateColumns->push_back(pColumn); + } + } + + } +} + +bool OSQLParseTreeIterator::traverseSelectColumnNames(const OSQLParseNode* pSelectNode) +{ + if ( !( m_pImpl->m_nIncludeMask & TraversalParts::SelectColumns ) ) + return true; + + if (!pSelectNode || m_eStatementType != OSQLStatementType::Select || m_pImpl->m_pTables->empty()) + { + impl_appendError( IParseContext::ErrorCode::General ); + return false; + } + + if(SQL_ISRULE(pSelectNode,union_statement)) + { + return traverseSelectColumnNames( pSelectNode->getChild( 0 ) ) + /*&& traverseSelectColumnNames( pSelectNode->getChild( 3 ) )*/; + } + + // nyi: more checks for correct structure! + if (pSelectNode->getChild(2)->isRule() && SQL_ISPUNCTUATION(pSelectNode->getChild(2)->getChild(0),"*")) + { + // SELECT * ... + setSelectColumnName("*", "", ""); + } + else if (SQL_ISRULE(pSelectNode->getChild(2),scalar_exp_commalist)) + { + // SELECT column[,column] or SELECT COUNT(*) ... + OSQLParseNode * pSelection = pSelectNode->getChild(2); + + for (size_t i = 0; i < pSelection->count(); i++) + { + OSQLParseNode *pColumnRef = pSelection->getChild(i); + + //if (SQL_ISRULE(pColumnRef,select_sublist)) + if (SQL_ISRULE(pColumnRef,derived_column) && + SQL_ISRULE(pColumnRef->getChild(0),column_ref) && + pColumnRef->getChild(0)->count() == 3 && + SQL_ISPUNCTUATION(pColumnRef->getChild(0)->getChild(2),"*")) + { + // All the table's columns + OUString aTableRange; + pColumnRef->getChild(0)->parseNodeToStr( aTableRange, m_pImpl->m_xConnection, nullptr, false, false ); + setSelectColumnName("*", "", aTableRange); + continue; + } + else if (SQL_ISRULE(pColumnRef,derived_column)) + { + OUString aColumnAlias(getColumnAlias(pColumnRef)); // can be empty + OUString sColumnName; + OUString aTableRange; + sal_Int32 nType = DataType::VARCHAR; + bool bFkt(false); + pColumnRef = pColumnRef->getChild(0); + while ( + pColumnRef->getKnownRuleID() != OSQLParseNode::subquery && + pColumnRef->count() == 3 && + SQL_ISPUNCTUATION(pColumnRef->getChild(0),"(") && + SQL_ISPUNCTUATION(pColumnRef->getChild(2),")") + ) + pColumnRef = pColumnRef->getChild(1); + + if (SQL_ISRULE(pColumnRef,column_ref)) + { + getColumnRange(pColumnRef,sColumnName,aTableRange); + OSL_ENSURE(!sColumnName.isEmpty(),"Column name must not be empty!"); + } + else /*if (SQL_ISRULE(pColumnRef,general_set_fct) || SQL_ISRULE(pColumnRef,set_fct_spec) || + SQL_ISRULE(pColumnRef,position_exp) || SQL_ISRULE(pColumnRef,extract_exp) || + SQL_ISRULE(pColumnRef,length_exp) || SQL_ISRULE(pColumnRef,char_value_fct)|| + SQL_ISRULE(pColumnRef,num_value_exp) || SQL_ISRULE(pColumnRef,term))*/ + { + // Function call present + pColumnRef->parseNodeToStr( sColumnName, m_pImpl->m_xConnection ); + // check if the column is also a parameter + traverseSearchCondition(pColumnRef); // num_value_exp + + if ( pColumnRef->isRule() ) + { + // FIXME: the if condition is not quite right + // many expressions are rules, e.g. "5+3" + // or even: "colName + 1" + bFkt = true; + nType = getFunctionReturnType(pColumnRef); + } + } + /* + else + { + aIteratorStatus.setStatementTooComplex(); + return; + } + */ + if(aColumnAlias.isEmpty()) + aColumnAlias = sColumnName; + setSelectColumnName(sColumnName,aColumnAlias,aTableRange,bFkt,nType,SQL_ISRULE(pColumnRef,general_set_fct) || SQL_ISRULE(pColumnRef,set_fct_spec)); + } + } + } + + return !hasErrors(); +} + + +bool OSQLParseTreeIterator::traverseOrderByColumnNames(const OSQLParseNode* pSelectNode) +{ + traverseByColumnNames( pSelectNode, true ); + return !hasErrors(); +} + +void OSQLParseTreeIterator::traverseByColumnNames(const OSQLParseNode* pSelectNode, bool _bOrder) +{ + // aIteratorStatus.Clear(); + + if (pSelectNode == nullptr) + { + //aIteratorStatus.setInvalidStatement(); + return; + } + + if (m_eStatementType != OSQLStatementType::Select) + { + //aIteratorStatus.setInvalidStatement(); + return; + } + + if(SQL_ISRULE(pSelectNode,union_statement)) + { + traverseByColumnNames(pSelectNode->getChild(0),_bOrder); + return; + } + + OSL_ENSURE(pSelectNode->count() >= 4,"OSQLParseTreeIterator: error in parse tree!"); + + OSQLParseNode * pTableExp = pSelectNode->getChild(3); + OSL_ENSURE(pTableExp != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(SQL_ISRULE(pTableExp,table_exp),"OSQLParseTreeIterator:table_exp error in parse tree!"); + OSL_ENSURE(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"OSQLParseTreeIterator: error in parse tree!"); + + sal_uInt32 nPos = ( _bOrder ? ORDER_BY_CHILD_POS : 2 ); + + OSQLParseNode * pOptByClause = pTableExp->getChild(nPos); + OSL_ENSURE(pOptByClause != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + if ( pOptByClause->count() == 0 ) + return; + + OSL_ENSURE(pOptByClause->count() == 3,"OSQLParseTreeIterator: error in parse tree!"); + + OSQLParseNode * pOrderingSpecCommalist = pOptByClause->getChild(2); + OSL_ENSURE(pOrderingSpecCommalist != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(!_bOrder || SQL_ISRULE(pOrderingSpecCommalist,ordering_spec_commalist),"OSQLParseTreeIterator:ordering_spec_commalist error in parse tree!"); + OSL_ENSURE(pOrderingSpecCommalist->count() > 0,"OSQLParseTreeIterator: error in parse tree!"); + + OUString sColumnName; + OUString aTableRange; + sal_uInt32 nCount = pOrderingSpecCommalist->count(); + for (sal_uInt32 i = 0; i < nCount; ++i) + { + OSQLParseNode* pColumnRef = pOrderingSpecCommalist->getChild(i); + OSL_ENSURE(pColumnRef != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + if ( _bOrder ) + { + OSL_ENSURE(SQL_ISRULE(pColumnRef,ordering_spec),"OSQLParseTreeIterator:ordering_spec error in parse tree!"); + OSL_ENSURE(pColumnRef->count() == 2,"OSQLParseTreeIterator: error in parse tree!"); + + pColumnRef = pColumnRef->getChild(0); + } + OSL_ENSURE(pColumnRef != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + aTableRange.clear(); + sColumnName.clear(); + if ( SQL_ISRULE(pColumnRef,column_ref) ) + { + // Column name (and TableRange): + getColumnRange(pColumnRef,sColumnName,aTableRange); + } + else + { // here I found a predicate + pColumnRef->parseNodeToStr( sColumnName, m_pImpl->m_xConnection, nullptr, false, false ); + } + OSL_ENSURE(!sColumnName.isEmpty(),"sColumnName must not be empty!"); + if ( _bOrder ) + { + // Ascending/Descending + OSQLParseNode * pOptAscDesc = pColumnRef->getParent()->getChild(1); + OSL_ENSURE(pOptAscDesc != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + + bool bAscending = ! (pOptAscDesc && SQL_ISTOKEN(pOptAscDesc,DESC)); + setOrderByColumnName(sColumnName, aTableRange,bAscending); + } + else + setGroupByColumnName(sColumnName, aTableRange); + } +} + +bool OSQLParseTreeIterator::traverseGroupByColumnNames(const OSQLParseNode* pSelectNode) +{ + traverseByColumnNames( pSelectNode, false ); + return !hasErrors(); +} + + +namespace +{ + OUString lcl_generateParameterName( const OSQLParseNode& _rParentNode, const OSQLParseNode& _rParamNode ) + { + OUString sColumnName( "param" ); + const sal_Int32 nCount = static_cast<sal_Int32>(_rParentNode.count()); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + if ( _rParentNode.getChild(i) == &_rParamNode ) + { + sColumnName += OUString::number( i+1 ); + break; + } + } + return sColumnName; + } +} + + +void OSQLParseTreeIterator::traverseParameters(const OSQLParseNode* _pNode) +{ + if ( _pNode == nullptr ) + return; + + OUString sColumnName, sTableRange, aColumnAlias; + const OSQLParseNode* pParent = _pNode->getParent(); + if ( pParent != nullptr ) + { + if ( SQL_ISRULE(pParent,comparison_predicate) ) // x = X + { + sal_uInt32 nPos = 0; + if ( pParent->getChild(nPos) == _pNode ) + nPos = 2; + const OSQLParseNode* pOther = pParent->getChild(nPos); + if ( SQL_ISRULE( pOther, column_ref ) ) + getColumnRange( pOther, sColumnName, sTableRange, aColumnAlias); + else + pOther->parseNodeToStr( sColumnName, m_pImpl->m_xConnection, nullptr, false, false ); + } // if ( SQL_ISRULE(pParent,comparison_predicate) ) // x = X + else if ( SQL_ISRULE(pParent,other_like_predicate_part_2) ) + { + const OSQLParseNode* pOther = pParent->getParent()->getChild(0); + if ( SQL_ISRULE( pOther, column_ref ) ) + getColumnRange( pOther, sColumnName, sTableRange, aColumnAlias); + else + pOther->parseNodeToStr( sColumnName, m_pImpl->m_xConnection, nullptr, false, false ); + } + else if ( SQL_ISRULE(pParent,between_predicate_part_2) ) + { + const OSQLParseNode* pOther = pParent->getParent()->getChild(0); + if ( SQL_ISRULE( pOther, column_ref ) ) + getColumnRange( pOther, sColumnName, sTableRange, aColumnAlias); + else + { + pOther->parseNodeToStr( sColumnName, m_pImpl->m_xConnection, nullptr, false, false ); + lcl_generateParameterName( *pParent, *_pNode ); + } + } + else if ( pParent->getNodeType() == SQLNodeType::CommaListRule ) + { + lcl_generateParameterName( *pParent, *_pNode ); + } + } + traverseParameter( _pNode, pParent, sColumnName, sTableRange, aColumnAlias ); + const sal_uInt32 nCount = _pNode->count(); + for (sal_uInt32 i = 0; i < nCount; ++i) + { + const OSQLParseNode* pChild = _pNode->getChild(i); + traverseParameters( pChild ); + } +} + +bool OSQLParseTreeIterator::traverseSelectionCriteria(const OSQLParseNode* pSelectNode) +{ + if ( pSelectNode == nullptr ) + return false; + + + // Analyse parse tree (depending on statement type) + // and set pointer to WHERE clause: + OSQLParseNode * pWhereClause = nullptr; + + if (m_eStatementType == OSQLStatementType::Select) + { + if(SQL_ISRULE(pSelectNode,union_statement)) + { + return traverseSelectionCriteria( pSelectNode->getChild( 0 ) ) + && traverseSelectionCriteria( pSelectNode->getChild( 3 ) ); + } + OSL_ENSURE(pSelectNode->count() >= 4,"OSQLParseTreeIterator: error in parse tree!"); + + OSQLParseNode * pTableExp = pSelectNode->getChild(3); + OSL_ENSURE(pTableExp != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(SQL_ISRULE(pTableExp,table_exp),"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"OSQLParseTreeIterator: error in parse tree!"); + + pWhereClause = pTableExp->getChild(1); + } else if (SQL_ISRULE(pSelectNode,update_statement_searched)) { + OSL_ENSURE(pSelectNode->count() == 5,"OSQLParseTreeIterator: error in parse tree!"); + pWhereClause = pSelectNode->getChild(4); + } else if (SQL_ISRULE(pSelectNode,delete_statement_searched)) { + OSL_ENSURE(pSelectNode->count() == 4,"OSQLParseTreeIterator: error in parse tree!"); + pWhereClause = pSelectNode->getChild(3); + } else if (SQL_ISRULE(pSelectNode,delete_statement_positioned)) { + // nyi + SAL_WARN( "connectivity.parse","OSQLParseTreeIterator::getSelectionCriteria: positioned nyi"); + } else { + // Other statement, no selection criteria + return false; + } + + if (!pWhereClause || !SQL_ISRULE(pWhereClause,where_clause)) + { + // The WHERE clause is optional most of the time; which means it could be a "optional_where_clause". + OSL_ENSURE(pWhereClause && SQL_ISRULE(pWhereClause,opt_where_clause),"OSQLParseTreeIterator: error in parse tree!"); + return false; + } + + // But if it's a where_clause, then it must not be empty + OSL_ENSURE(pWhereClause->count() == 2,"OSQLParseTreeIterator: error in parse tree!"); + + OSQLParseNode * pComparisonPredicate = pWhereClause->getChild(1); + OSL_ENSURE(pComparisonPredicate != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + + + // Process the comparison criteria now + + + traverseSearchCondition(pComparisonPredicate); + + return !hasErrors(); +} + + +void OSQLParseTreeIterator::traverseSearchCondition(OSQLParseNode const * pSearchCondition) +{ + if ( + SQL_ISRULE(pSearchCondition,boolean_primary) && + pSearchCondition->count() == 3 && + SQL_ISPUNCTUATION(pSearchCondition->getChild(0),"(") && + SQL_ISPUNCTUATION(pSearchCondition->getChild(2),")") + ) + { + // Round brackets + traverseSearchCondition(pSearchCondition->getChild(1)); + } + // The first element is an OR logical operation + else if ( SQL_ISRULE(pSearchCondition,search_condition) && pSearchCondition->count() == 3 ) + { + // if this assert fails, the SQL grammar has changed! + assert(SQL_ISTOKEN(pSearchCondition->getChild(1),OR)); + // Then process recursively (use the same row) ... + traverseSearchCondition(pSearchCondition->getChild(0)); +// if (! aIteratorStatus.IsSuccessful()) +// return; + + // Continue with the right child + traverseSearchCondition(pSearchCondition->getChild(2)); + } + // The first element is an AND logical operation (again) + else if ( SQL_ISRULE(pSearchCondition,boolean_term) && pSearchCondition->count() == 3 ) + { + // Then process recursively (use the same row) + traverseSearchCondition(pSearchCondition->getChild(0)); +// if (! aIteratorStatus.IsSuccessful()) +// return; + + // Continue with the right child + traverseSearchCondition(pSearchCondition->getChild(2)); + } + // Else, process single search criteria (like =, !=, ..., LIKE, IS NULL etc.) + else if (SQL_ISRULE(pSearchCondition,comparison_predicate) ) + { + OUString aValue; + pSearchCondition->getChild(2)->parseNodeToStr( aValue, m_pImpl->m_xConnection, nullptr, false, false ); + traverseOnePredicate(pSearchCondition->getChild(0),aValue,pSearchCondition->getChild(2)); + impl_fillJoinConditions(pSearchCondition); +// if (! aIteratorStatus.IsSuccessful()) +// return; + } + else if (SQL_ISRULE(pSearchCondition,like_predicate) /*&& SQL_ISRULE(pSearchCondition->getChild(0),column_ref)*/) + { + OSL_ENSURE(pSearchCondition->count() == 2,"OSQLParseTreeIterator: error in parse tree!"); + const OSQLParseNode* pPart2 = pSearchCondition->getChild(1); + + sal_Int32 nCurentPos = pPart2->count()-2; + + OSQLParseNode * pNum_value_exp = pPart2->getChild(nCurentPos); + OSQLParseNode * pOptEscape = pPart2->getChild(nCurentPos+1); + + OSL_ENSURE(pNum_value_exp != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(pOptEscape != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + + if (pOptEscape->count() != 0) + { + // aIteratorStatus.setStatementTooComplex(); + return; + } + + OUString aValue; + OSQLParseNode * pParam = nullptr; + if (SQL_ISRULE(pNum_value_exp,parameter)) + pParam = pNum_value_exp; + else if(pNum_value_exp->isToken()) + // Normal value + aValue = pNum_value_exp->getTokenValue(); + else + { + pNum_value_exp->parseNodeToStr( aValue, m_pImpl->m_xConnection, nullptr, false, false ); + pParam = pNum_value_exp; + } + + traverseOnePredicate(pSearchCondition->getChild(0),aValue,pParam); +// if (! aIteratorStatus.IsSuccessful()) +// return; + } + else if (SQL_ISRULE(pSearchCondition,in_predicate)) + { + OSL_ENSURE(pSearchCondition->count() == 2,"OSQLParseTreeIterator: error in parse tree!"); + const OSQLParseNode* pPart2 = pSearchCondition->getChild(1); + + traverseSearchCondition(pSearchCondition->getChild(0)); + // if (! aIteratorStatus.IsSuccessful()) return; + + OSQLParseNode* pChild = pPart2->getChild(2); + if ( SQL_ISRULE(pChild->getChild(0),subquery) ) + { + traverseTableNames( *m_pImpl->m_pSubTables ); + traverseSelectionCriteria(pChild->getChild(0)->getChild(1)); + } + else + { // '(' value_exp_commalist ')' + pChild = pChild->getChild(1); + sal_Int32 nCount = pChild->count(); + for (sal_Int32 i=0; i < nCount; ++i) + { + traverseSearchCondition(pChild->getChild(i)); + } + } + } + else if (SQL_ISRULE(pSearchCondition,test_for_null) /*&& SQL_ISRULE(pSearchCondition->getChild(0),column_ref)*/) + { + OSL_ENSURE(pSearchCondition->count() == 2,"OSQLParseTreeIterator: error in parse tree!"); + const OSQLParseNode* pPart2 = pSearchCondition->getChild(1); + OSL_ENSURE(SQL_ISTOKEN(pPart2->getChild(0),IS),"OSQLParseTreeIterator: error in parse tree!"); + + OUString aString; + traverseOnePredicate(pSearchCondition->getChild(0),aString,nullptr); + // if (! aIteratorStatus.IsSuccessful()) return; + } + else if (SQL_ISRULE(pSearchCondition,num_value_exp) || SQL_ISRULE(pSearchCondition,term)) + { + OUString aString; + traverseOnePredicate(pSearchCondition->getChild(0),aString,pSearchCondition->getChild(0)); + traverseOnePredicate(pSearchCondition->getChild(2),aString,pSearchCondition->getChild(2)); + } + // Just pass on the error +} + +void OSQLParseTreeIterator::traverseParameter(const OSQLParseNode* _pParseNode + ,const OSQLParseNode* _pParentNode + ,const OUString& _aColumnName + ,OUString& _aTableRange + ,const OUString& _rColumnAlias) +{ + if ( !SQL_ISRULE( _pParseNode, parameter ) ) + return; + + if ( !( m_pImpl->m_nIncludeMask & TraversalParts::Parameters ) ) + // parameters not to be included in the traversal + return; + + OSL_ENSURE(_pParseNode->count() > 0,"OSQLParseTreeIterator: error in parse tree!"); + OSQLParseNode * pMark = _pParseNode->getChild(0); + OUString sParameterName; + + if (SQL_ISPUNCTUATION(pMark,"?")) + { + sParameterName = !_rColumnAlias.isEmpty() + ? _rColumnAlias + : !_aColumnName.isEmpty() + ? _aColumnName + : OUString("?"); + } + else if (SQL_ISPUNCTUATION(pMark,":")) + { + sParameterName = _pParseNode->getChild(1)->getTokenValue(); + } + else if (SQL_ISPUNCTUATION(pMark,"[")) + { + sParameterName = _pParseNode->getChild(1)->getTokenValue(); + } + else + { + SAL_WARN( "connectivity.parse","OSQLParseTreeIterator: error in parse tree!"); + } + + // found a parameter + if ( _pParentNode && (SQL_ISRULE(_pParentNode,general_set_fct) || SQL_ISRULE(_pParentNode,set_fct_spec)) ) + {// found a function as column_ref + OUString sFunctionName; + _pParentNode->getChild(0)->parseNodeToStr( sFunctionName, m_pImpl->m_xConnection, nullptr, false, false ); + const sal_uInt32 nCount = _pParentNode->count(); + sal_uInt32 i = 0; + for(; i < nCount;++i) + { + if ( _pParentNode->getChild(i) == _pParseNode ) + break; + } + sal_Int32 nType = ::connectivity::OSQLParser::getFunctionParameterType( _pParentNode->getChild(0)->getTokenID(), i-1); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn( sParameterName, + OUString(), + OUString(), + OUString(), + ColumnValue::NULLABLE_UNKNOWN, + 0, + 0, + nType, + false, + false, + isCaseSensitive(), + OUString(), + OUString(), + OUString()); + pColumn->setFunction(true); + pColumn->setAggregateFunction(true); + pColumn->setRealName(sFunctionName); + m_aParameters->push_back(pColumn); + } + else + { + bool bNotFound = true; + OSQLColumns::const_iterator aIter = ::connectivity::find( + m_aSelectColumns->begin(), + m_aSelectColumns->end(), + _aColumnName,::comphelper::UStringMixEqual( isCaseSensitive() ) + ); + if(aIter != m_aSelectColumns->end()) + { + rtl::Reference<OParseColumn> pNewColumn = new OParseColumn(*aIter,isCaseSensitive()); + pNewColumn->setName(sParameterName); + pNewColumn->setRealName(_aColumnName); + m_aParameters->push_back(pNewColumn); + bNotFound = false; + } + else if(!_aColumnName.isEmpty())// search in the tables for the right one + { + + Reference<XPropertySet> xColumn = findColumn( _aColumnName, _aTableRange, true ); + + if ( xColumn.is() ) + { + rtl::Reference<OParseColumn> pNewColumn = new OParseColumn(xColumn,isCaseSensitive()); + pNewColumn->setName(sParameterName); + pNewColumn->setRealName(_aColumnName); + m_aParameters->push_back(pNewColumn); + bNotFound = false; + } + } + if ( bNotFound ) + { + sal_Int32 nType = DataType::VARCHAR; + OSQLParseNode* pParent = _pParentNode ? _pParentNode->getParent() : nullptr; + if ( pParent && (SQL_ISRULE(pParent,general_set_fct) || SQL_ISRULE(pParent,set_fct_spec)) ) + { + const sal_uInt32 nCount = _pParentNode->count(); + sal_uInt32 i = 0; + for(; i < nCount;++i) + { + if ( _pParentNode->getChild(i) == _pParseNode ) + break; + } + nType = ::connectivity::OSQLParser::getFunctionParameterType( pParent->getChild(0)->getTokenID(), i+1); + } + + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), sParameterName)); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn(aNewColName, + OUString(), + OUString(), + OUString(), + ColumnValue::NULLABLE_UNKNOWN, + 0, + 0, + nType, + false, + false, + isCaseSensitive(), + OUString(), + OUString(), + OUString()); + pColumn->setName(aNewColName); + pColumn->setRealName(sParameterName); + m_aParameters->push_back(pColumn); + } + } +} + +void OSQLParseTreeIterator::traverseOnePredicate( + OSQLParseNode const * pColumnRef, + OUString& rValue, + OSQLParseNode const * pParseNode) +{ + if ( !pParseNode ) + return; + + // Column name (and TableRange): + OUString aColumnName, aTableRange, sColumnAlias; + getColumnRange( pColumnRef, aColumnName, aTableRange, sColumnAlias); + + OUString aName; + + /*if (SQL_ISRULE(pParseNode,parameter)) + traverseParameter( pParseNode, pColumnRef, aColumnName, aTableRange, sColumnAlias ); + else */if (SQL_ISRULE(pParseNode,column_ref))// Column-Name (and TableRange): + getColumnRange(pParseNode,aName,rValue); + else + { + traverseSearchCondition(pParseNode); + // if (! aIteratorStatus.IsSuccessful()) return; + } +} + + +void OSQLParseTreeIterator::traverseAll() +{ + impl_traverse( TraversalParts::All ); +} + + +void OSQLParseTreeIterator::impl_traverse( TraversalParts _nIncludeMask ) +{ + // resets our errors + m_xErrors.reset(); + + m_pImpl->m_nIncludeMask = _nIncludeMask; + + if ( !traverseTableNames( *m_pImpl->m_pTables ) ) + return; + + switch ( m_eStatementType ) + { + case OSQLStatementType::Select: + { + const OSQLParseNode* pSelectNode = m_pParseTree; + traverseParameters( pSelectNode ); + if ( !traverseSelectColumnNames( pSelectNode ) + || !traverseOrderByColumnNames( pSelectNode ) + || !traverseGroupByColumnNames( pSelectNode ) + || !traverseSelectionCriteria( pSelectNode ) + ) + return; + } + break; + case OSQLStatementType::CreateTable: + { + //0 | 1 | 2 |3| 4 |5 + //create table sc.foo ( a char(20), b char ) + const OSQLParseNode* pCreateNode = m_pParseTree->getChild(4); + traverseCreateColumns(pCreateNode); + } + break; + case OSQLStatementType::Insert: + break; + default: + break; + } +} + +// Dummy implementations + + +OSQLTable OSQLParseTreeIterator::impl_createTableObject( const OUString& rTableName, + const OUString& rCatalogName, const OUString& rSchemaName ) +{ + OSL_PRECOND( m_eStatementType == OSQLStatementType::CreateTable, + "OSQLParseTreeIterator::impl_createTableObject: only to be called for CREATE TABLE statements!" ); + // (in all other cases, m_pTables is to contain the table objects as obtained from the tables + // container of the connection (m_xTablesContainer) + + OSQLTable aReturnTable = new OTable( + nullptr, + false, + rTableName, + "Table", + "New Created Table", + rSchemaName, + rCatalogName + ); + return aReturnTable; +} + +void OSQLParseTreeIterator::appendColumns(const OUString& _rTableAlias, const OSQLTable& _rTable) +{ + if (!_rTable.is()) + return; + + Reference<XNameAccess> xColumns = _rTable->getColumns(); + if ( !xColumns.is() ) + return; + + Sequence< OUString > aColNames = xColumns->getElementNames(); + const OUString* pBegin = aColNames.getConstArray(); + const OUString* pEnd = pBegin + aColNames.getLength(); + + ::comphelper::UStringMixLess aCompare(isCaseSensitive()); + std::vector<OUString> aSelectColumnNames = getSelectColumnNames(); + + for(;pBegin != pEnd;++pBegin) + { + OUString aName(getUniqueColumnName(aSelectColumnNames, *pBegin)); + Reference< XPropertySet > xColumn; + if(xColumns->hasByName(*pBegin) && (xColumns->getByName(*pBegin) >>= xColumn) && xColumn.is()) + { + rtl::Reference<OParseColumn> pColumn = new OParseColumn(aName + , getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPENAME))) + , getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DEFAULTVALUE))) + , getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_DESCRIPTION))) + , getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISNULLABLE))) + , getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRECISION))) + , getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCALE))) + , getINT32(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE))) + , getBOOL(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT))) + , getBOOL(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISCURRENCY))) + , isCaseSensitive() + , getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CATALOGNAME))) + , getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_SCHEMANAME))) + , getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TABLENAME)))); + + pColumn->setTableName(_rTableAlias); + pColumn->setRealName(*pBegin); + m_aSelectColumns->push_back(pColumn); + // update aSelectColumnNames with newly insert aName + aSelectColumnNames.insert(std::upper_bound(aSelectColumnNames.begin(), aSelectColumnNames.end(), aName, aCompare), aName); + } + else + impl_appendError( IParseContext::ErrorCode::InvalidColumn, pBegin, &_rTableAlias ); + } +} + +void OSQLParseTreeIterator::setSelectColumnName(const OUString & rColumnName,const OUString & rColumnAlias, const OUString & rTableRange, bool bFkt, sal_Int32 _nType, bool bAggFkt) +{ + if(rColumnName.toChar() == '*' && rTableRange.isEmpty()) + { // SELECT * ... + for (auto const& table : *m_pImpl->m_pTables) + appendColumns(table.first, table.second); + } + else if( rColumnName.toChar() == '*' && !rTableRange.isEmpty() ) + { // SELECT <table>.* + OSQLTables::const_iterator aFind = m_pImpl->m_pTables->find(rTableRange); + + if(aFind != m_pImpl->m_pTables->end()) + appendColumns(rTableRange, aFind->second); + } + else if ( rTableRange.isEmpty() ) + { // SELECT <something> ... + // without table specified + if ( !bFkt ) + { + Reference< XPropertySet> xNewColumn; + + for (auto const& table : *m_pImpl->m_pTables) + { + if ( !table.second.is() ) + continue; + + Reference<XNameAccess> xColumns = table.second->getColumns(); + Reference< XPropertySet > xColumn; + if ( !xColumns->hasByName( rColumnName ) + || !( xColumns->getByName( rColumnName ) >>= xColumn ) + ) + continue; + + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), rColumnAlias)); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn(xColumn,isCaseSensitive()); + xNewColumn = pColumn; + pColumn->setTableName(table.first); + pColumn->setName(aNewColName); + pColumn->setRealName(rColumnName); + + break; + } + + if ( !xNewColumn.is() ) + { + // no function (due to the above !bFkt), no existing column + // => assume an expression + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), rColumnAlias)); + // did not find a column with this name in any of the tables + rtl::Reference<OParseColumn> pColumn = new OParseColumn( + aNewColName, + "VARCHAR", + // TODO: does this match with _nType? + // Or should be fill this from the getTypeInfo of the connection? + OUString(), + OUString(), + ColumnValue::NULLABLE_UNKNOWN, + 0, + 0, + _nType, + false, + false, + isCaseSensitive(), + OUString(), + OUString(), + OUString() + ); + + xNewColumn = pColumn; + pColumn->setRealName( rColumnName ); + } + + m_aSelectColumns->push_back( xNewColumn ); + } + else + { + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), rColumnAlias)); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn(aNewColName,OUString(),OUString(),OUString(), + ColumnValue::NULLABLE_UNKNOWN,0,0,_nType,false,false,isCaseSensitive(), + OUString(),OUString(),OUString()); + pColumn->setFunction(true); + pColumn->setAggregateFunction(bAggFkt); + pColumn->setRealName(rColumnName); + + m_aSelectColumns->push_back(pColumn); + } + } + else // ColumnName and TableName exist + { + OSQLTables::const_iterator aFind = m_pImpl->m_pTables->find(rTableRange); + + bool bError = false; + if (aFind != m_pImpl->m_pTables->end() && aFind->second.is()) + { + if (bFkt) + { + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), rColumnAlias)); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn(aNewColName,OUString(),OUString(),OUString(), + ColumnValue::NULLABLE_UNKNOWN,0,0,_nType,false,false,isCaseSensitive(), + OUString(),OUString(),OUString()); + pColumn->setFunction(true); + pColumn->setAggregateFunction(bAggFkt); + pColumn->setRealName(rColumnName); + SAL_WARN("connectivity.parse", "Trying to construct a column with Function==true and a TableName; this makes no sense."); + assert(false); + pColumn->setTableName(aFind->first); + + m_aSelectColumns->push_back(pColumn); + } + else + { + Reference< XPropertySet > xColumn; + if (aFind->second->getColumns()->hasByName(rColumnName) && (aFind->second->getColumns()->getByName(rColumnName) >>= xColumn)) + { + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), rColumnAlias)); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn(xColumn,isCaseSensitive()); + pColumn->setName(aNewColName); + pColumn->setRealName(rColumnName); + pColumn->setTableName(aFind->first); + + m_aSelectColumns->push_back(pColumn); + } + else + bError = true; + } + } + else + bError = true; + + // Table does not exist or lacking field + if (bError) + { + OUString aNewColName(getUniqueColumnName(getSelectColumnNames(), rColumnAlias)); + + rtl::Reference<OParseColumn> pColumn = new OParseColumn(aNewColName,OUString(),OUString(),OUString(), + ColumnValue::NULLABLE_UNKNOWN,0,0,DataType::VARCHAR,false,false,isCaseSensitive(), + OUString(),OUString(),OUString()); + pColumn->setFunction(true); + pColumn->setAggregateFunction(bAggFkt); + + m_aSelectColumns->push_back(pColumn); + } + } +} + +std::vector<OUString> OSQLParseTreeIterator::getSelectColumnNames() const +{ + ::comphelper::UStringMixLess aCompare(isCaseSensitive()); + + std::vector<OUString> aColumnNames; + OUString sPropertyName = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME); + for (const auto& col : *m_aSelectColumns) + aColumnNames.push_back(getString(col->getPropertyValue(sPropertyName))); + std::sort(aColumnNames.begin(), aColumnNames.end(), aCompare); + + return aColumnNames; +} + +OUString OSQLParseTreeIterator::getUniqueColumnName(const std::vector<OUString>& rColumnNames, const OUString& rColumnName) const +{ + ::comphelper::UStringMixLess aCompare(isCaseSensitive()); + if (!std::binary_search(rColumnNames.begin(), rColumnNames.end(), rColumnName, aCompare)) + return rColumnName; + + OUString aAlias; + sal_Int32 i=1; + do + { + aAlias = rColumnName + OUString::number(i++); + } + while (std::binary_search(rColumnNames.begin(), rColumnNames.end(), aAlias, aCompare)); + return aAlias; +} + +void OSQLParseTreeIterator::setOrderByColumnName(const OUString & rColumnName, OUString & rTableRange, bool bAscending) +{ + Reference<XPropertySet> xColumn = findSelectColumn( rColumnName ); + if ( !xColumn.is() ) + xColumn = findColumn ( rColumnName, rTableRange, false ); + if ( xColumn.is() ) + m_aOrderColumns->push_back(new OOrderColumn( xColumn, rTableRange, isCaseSensitive(), bAscending ) ); + else + { + sal_Int32 nId = rColumnName.toInt32(); + if ( nId > 0 && o3tl::make_unsigned(nId) < m_aSelectColumns->size() ) + m_aOrderColumns->push_back( new OOrderColumn( (*m_aSelectColumns)[nId-1], isCaseSensitive(), bAscending ) ); + } + +#ifdef SQL_TEST_PARSETREEITERATOR + cout << "OSQLParseTreeIterator::setOrderByColumnName: " + << (const char *) rColumnName << ", " + << (const char *) rTableRange << ", " + << (bAscending ? "true" : "false") + << "\n"; +#endif +} + +void OSQLParseTreeIterator::setGroupByColumnName(const OUString & rColumnName, OUString & rTableRange) +{ + Reference<XPropertySet> xColumn = findColumn( rColumnName, rTableRange, false ); + if ( xColumn.is() ) + m_aGroupColumns->push_back(new OParseColumn(xColumn,isCaseSensitive())); + else + { + sal_Int32 nId = rColumnName.toInt32(); + if ( nId > 0 && o3tl::make_unsigned(nId) < m_aSelectColumns->size() ) + m_aGroupColumns->push_back(new OParseColumn((*m_aSelectColumns)[nId-1],isCaseSensitive())); + } + +#ifdef SQL_TEST_PARSETREEITERATOR + cout << "OSQLParseTreeIterator::setGroupByColumnName: " + << (const char *) rColumnName << ", " + << (const char *) rTableRange << ", " + << (bAscending ? "true" : "false") + << "\n"; +#endif +} + + +const OSQLParseNode* OSQLParseTreeIterator::getWhereTree() const +{ + if (!m_pParseTree) + return nullptr; + + // Analyse parse tree (depending on statement type) + // and set pointer to WHERE clause: + OSQLParseNode * pWhereClause = nullptr; + if(getStatementType() == OSQLStatementType::Select) + { + OSL_ENSURE(m_pParseTree->count() >= 4,"ParseTreeIterator: error in parse tree!"); + OSQLParseNode * pTableExp = m_pParseTree->getChild(3); + OSL_ENSURE(pTableExp != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(SQL_ISRULE(pTableExp,table_exp),"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"OSQLParseTreeIterator: error in parse tree!"); + + pWhereClause = pTableExp->getChild(1); + } + else if (SQL_ISRULE(m_pParseTree,update_statement_searched) || + SQL_ISRULE(m_pParseTree,delete_statement_searched)) + { + pWhereClause = m_pParseTree->getChild(m_pParseTree->count()-1); + } + if(pWhereClause && pWhereClause->count() != 2) + pWhereClause = nullptr; + return pWhereClause; +} + + +const OSQLParseNode* OSQLParseTreeIterator::getOrderTree() const +{ + if (!m_pParseTree || getStatementType() != OSQLStatementType::Select) + return nullptr; + + // Analyse parse tree (depending on statement type) + // and set pointer to ORDER clause: + + assert(SQL_ISRULE(m_pParseTree, select_statement) || SQL_ISRULE(m_pParseTree, union_statement)); + + auto pParseTree = m_pParseTree; + if(SQL_ISRULE(m_pParseTree, union_statement)) + { + assert(m_pParseTree->count() == 4); + pParseTree = pParseTree->getChild(3); + // since UNION is left-associative (at least in our grammar), + // possibly the left-hand (m_pParseTree->getChild(0)) is a union_statement, + // but the right hand cannot. + assert(SQL_ISRULE(pParseTree, select_statement)); + } + + OSQLParseNode * pOrderClause = nullptr; + OSL_ENSURE(pParseTree->count() == 4, "OSQLParseTreeIterator::getOrderTree: expected a SELECT, and a SELECT must have exactly four children"); + OSQLParseNode * pTableExp = pParseTree->getChild(3); + OSL_ENSURE(pTableExp != nullptr, "OSQLParseTreeIterator::getOrderTree: got NULL table_exp"); + OSL_ENSURE(SQL_ISRULE(pTableExp, table_exp), "OSQLParseTreeIterator::getOrderTree: expected table_exp but got something else"); + OSL_ENSURE(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"OSQLParseTreeIterator::getOrderTree: table_exp doesn't have the expected number of children"); + // tdf#141115 upgrade the above to an assert; + // this cannot go well if there are too few children + assert(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT); + + pOrderClause = pTableExp->getChild(ORDER_BY_CHILD_POS); + // If it is an order_by, it must not be empty + if(pOrderClause->count() != 3) + pOrderClause = nullptr; + return pOrderClause; +} + +const OSQLParseNode* OSQLParseTreeIterator::getGroupByTree() const +{ + if (!m_pParseTree || getStatementType() != OSQLStatementType::Select) + return nullptr; + + // Analyse parse tree (depending on statement type) + // and set pointer to ORDER clause: + OSQLParseNode * pGroupClause = nullptr; + OSL_ENSURE(m_pParseTree->count() >= 4,"ParseTreeIterator: error in parse tree!"); + OSQLParseNode * pTableExp = m_pParseTree->getChild(3); + OSL_ENSURE(pTableExp != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(SQL_ISRULE(pTableExp,table_exp),"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"OSQLParseTreeIterator: error in parse tree!"); + + pGroupClause = pTableExp->getChild(2); + // If it is an order_by, it must not be empty + if(pGroupClause->count() != 3) + pGroupClause = nullptr; + return pGroupClause; +} + +const OSQLParseNode* OSQLParseTreeIterator::getHavingTree() const +{ + if (!m_pParseTree || getStatementType() != OSQLStatementType::Select) + return nullptr; + + // Analyse parse tree (depending on statement type) + // and set pointer to ORDER clause: + OSQLParseNode * pHavingClause = nullptr; + OSL_ENSURE(m_pParseTree->count() >= 4,"ParseTreeIterator: error in parse tree!"); + OSQLParseNode * pTableExp = m_pParseTree->getChild(3); + OSL_ENSURE(pTableExp != nullptr,"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(SQL_ISRULE(pTableExp,table_exp),"OSQLParseTreeIterator: error in parse tree!"); + OSL_ENSURE(pTableExp->count() == TABLE_EXPRESSION_CHILD_COUNT,"OSQLParseTreeIterator: error in parse tree!"); + + pHavingClause = pTableExp->getChild(3); + // If it is an order_by, then it must not be empty + if(pHavingClause->count() < 1) + pHavingClause = nullptr; + return pHavingClause; +} + +bool OSQLParseTreeIterator::isTableNode(const OSQLParseNode* _pTableNode) +{ + return _pTableNode && (SQL_ISRULE(_pTableNode,catalog_name) || + SQL_ISRULE(_pTableNode,schema_name) || + SQL_ISRULE(_pTableNode,table_name)); +} + +const OSQLParseNode* OSQLParseTreeIterator::getSimpleWhereTree() const +{ + const OSQLParseNode* pNode = getWhereTree(); + return pNode ? pNode->getChild(1) : nullptr; +} + +const OSQLParseNode* OSQLParseTreeIterator::getSimpleOrderTree() const +{ + const OSQLParseNode* pNode = getOrderTree(); + return pNode ? pNode->getChild(2) : nullptr; +} + +const OSQLParseNode* OSQLParseTreeIterator::getSimpleGroupByTree() const +{ + const OSQLParseNode* pNode = getGroupByTree(); + return pNode ? pNode->getChild(2) : nullptr; +} + +const OSQLParseNode* OSQLParseTreeIterator::getSimpleHavingTree() const +{ + const OSQLParseNode* pNode = getHavingTree(); + return pNode ? pNode->getChild(1) : nullptr; +} + + +Reference< XPropertySet > OSQLParseTreeIterator::findSelectColumn( std::u16string_view rColumnName ) +{ + for (auto const& lookupColumn : *m_aSelectColumns) + { + Reference< XPropertySet > xColumn( lookupColumn ); + try + { + OUString sName; + xColumn->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_NAME ) ) >>= sName; + if ( sName == rColumnName ) + return xColumn; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.parse"); + } + } + return nullptr; +} + + +Reference< XPropertySet > OSQLParseTreeIterator::findColumn( const OUString & rColumnName, OUString & rTableRange, bool _bLookInSubTables ) +{ + Reference< XPropertySet > xColumn = findColumn( *m_pImpl->m_pTables, rColumnName, rTableRange ); + if ( !xColumn.is() && _bLookInSubTables ) + xColumn = findColumn( *m_pImpl->m_pSubTables, rColumnName, rTableRange ); + return xColumn; +} + + +Reference< XPropertySet > OSQLParseTreeIterator::findColumn(const OSQLTables& _rTables, const OUString & rColumnName, OUString & rTableRange) +{ + Reference< XPropertySet > xColumn; + if ( !rTableRange.isEmpty() ) + { + OSQLTables::const_iterator aFind = _rTables.find(rTableRange); + + if ( aFind != _rTables.end() + && aFind->second.is() + && aFind->second->getColumns().is() + && aFind->second->getColumns()->hasByName(rColumnName) ) + aFind->second->getColumns()->getByName(rColumnName) >>= xColumn; + } + if ( !xColumn.is() ) + { + for (auto const& table : _rTables) + { + if ( table.second.is() ) + { + Reference<XNameAccess> xColumns = table.second->getColumns(); + if( xColumns.is() && xColumns->hasByName(rColumnName) && (xColumns->getByName(rColumnName) >>= xColumn) ) + { + OSL_ENSURE(xColumn.is(),"Column isn't a propertyset!"); + // Cannot take "rTableRange = table.first" because that is the fully composed name + // that is, catalogName.schemaName.tableName + rTableRange = getString(xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TABLENAME))); + break; // This column must only exits once + } + } + } + } + return xColumn; +} + + +void OSQLParseTreeIterator::impl_appendError( IParseContext::ErrorCode _eError, const OUString* _pReplaceToken1, const OUString* _pReplaceToken2 ) +{ + OUString sErrorMessage = m_rParser.getContext().getErrorMessage( _eError ); + if ( _pReplaceToken1 ) + { + bool bTwoTokens = ( _pReplaceToken2 != nullptr ); + const char* pPlaceHolder1 = bTwoTokens ? "#1" : "#"; + const OUString sPlaceHolder1 = OUString::createFromAscii( pPlaceHolder1 ); + + sErrorMessage = sErrorMessage.replaceFirst( sPlaceHolder1, *_pReplaceToken1 ); + if ( _pReplaceToken2 ) + sErrorMessage = sErrorMessage.replaceFirst( "#2" , *_pReplaceToken2 ); + } + + impl_appendError( SQLException( + sErrorMessage, nullptr, getStandardSQLState( StandardSQLState::GENERAL_ERROR ), 1000, Any() ) ); +} + + +void OSQLParseTreeIterator::impl_appendError( const SQLException& _rError ) +{ + SAL_WARN("connectivity.parse", "Adding error " << exceptionToString(Any(_rError))); + if ( m_xErrors ) + { + SQLException* pErrorChain = &*m_xErrors; + while ( pErrorChain->NextException.hasValue() ) + pErrorChain = static_cast< SQLException* >( pErrorChain->NextException.pData ); + pErrorChain->NextException <<= _rError; + } + else + m_xErrors = _rError; +} + +sal_Int32 OSQLParseTreeIterator::getFunctionReturnType(const OSQLParseNode* _pNode ) +{ + sal_Int32 nType = DataType::OTHER; + OUString sFunctionName; + if ( SQL_ISRULE(_pNode,length_exp) ) + { + _pNode->getChild(0)->getChild(0)->parseNodeToStr(sFunctionName, m_pImpl->m_xConnection, nullptr, false, false ); + nType = ::connectivity::OSQLParser::getFunctionReturnType( sFunctionName, &m_rParser.getContext() ); + } + else if ( SQL_ISRULE(_pNode,num_value_exp) || SQL_ISRULE(_pNode,term) || SQL_ISRULE(_pNode,factor) ) + { + nType = DataType::DOUBLE; + } + else + { + _pNode->getChild(0)->parseNodeToStr(sFunctionName, m_pImpl->m_xConnection, nullptr, false, false ); + + // MIN and MAX have another return type, we have to check the expression itself. + // @see http://qa.openoffice.org/issues/show_bug.cgi?id=99566 + if ( SQL_ISRULE(_pNode,general_set_fct) && (SQL_ISTOKEN(_pNode->getChild(0),MIN) || SQL_ISTOKEN(_pNode->getChild(0),MAX) )) + { + const OSQLParseNode* pValueExp = _pNode->getChild(3); + if (SQL_ISRULE(pValueExp,column_ref)) + { + OUString sColumnName; + OUString aTableRange; + getColumnRange(pValueExp,sColumnName,aTableRange); + OSL_ENSURE(!sColumnName.isEmpty(),"Columnname must not be empty!"); + Reference<XPropertySet> xColumn = findColumn( sColumnName, aTableRange, true ); + + if ( xColumn.is() ) + { + xColumn->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_TYPE)) >>= nType; + } + } + else + { + if ( SQL_ISRULE(pValueExp,num_value_exp) || SQL_ISRULE(pValueExp,term) || SQL_ISRULE(pValueExp,factor) ) + { + nType = DataType::DOUBLE; + } + else if ( SQL_ISRULE(pValueExp,datetime_primary) ) + { + switch(pValueExp->getChild(0)->getTokenID() ) + { + case SQL_TOKEN_CURRENT_DATE: + nType = DataType::DATE; + break; + case SQL_TOKEN_CURRENT_TIME: + nType = DataType::TIME; + break; + case SQL_TOKEN_CURRENT_TIMESTAMP: + nType = DataType::TIMESTAMP; + break; + } + } + else if ( SQL_ISRULE(pValueExp,value_exp_primary) ) + { + nType = getFunctionReturnType(pValueExp->getChild(1)); + } + else if ( SQL_ISRULE(pValueExp,concatenation) + || SQL_ISRULE(pValueExp,char_factor) + || SQL_ISRULE(pValueExp,bit_value_fct) + || SQL_ISRULE(pValueExp,char_value_fct) + || SQL_ISRULE(pValueExp,char_substring_fct) + || SQL_ISRULE(pValueExp,fold) + || SQL_ISTOKEN(pValueExp,STRING) ) + { + nType = DataType::VARCHAR; + } + } + if ( nType == DataType::OTHER ) + nType = DataType::DOUBLE; + } + else + nType = ::connectivity::OSQLParser::getFunctionReturnType( sFunctionName, &m_rParser.getContext() ); + } + + return nType; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/connectivity/source/parse/sqlnode.cxx b/connectivity/source/parse/sqlnode.cxx new file mode 100644 index 000000000..75acac48f --- /dev/null +++ b/connectivity/source/parse/sqlnode.cxx @@ -0,0 +1,2789 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/macros.h> +#include <connectivity/sqlnode.hxx> +#include <connectivity/sqlerror.hxx> +#include <connectivity/sqlbison_exports.hxx> +#include <connectivity/internalnode.hxx> +#define YYBISON 1 +#include <sqlbison.hxx> +#include <connectivity/sqlparse.hxx> +#include <connectivity/sqlscan.hxx> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/util/XNumberFormatter.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <com/sun/star/i18n/LocaleData.hpp> +#include <com/sun/star/i18n/NumberFormatIndex.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdb/ErrorCondition.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/util/XNumberFormats.hpp> +#include <com/sun/star/util/NumberFormat.hpp> +#include <com/sun/star/i18n/KParseType.hpp> +#include <com/sun/star/i18n/KParseTokens.hpp> +#include <com/sun/star/i18n/CharacterClassification.hpp> +#include <connectivity/dbconversion.hxx> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/Date.hpp> +#include <TConnection.hxx> +#include <comphelper/numbers.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbmetadata.hxx> +#include <tools/diagnose_ex.h> +#include <string.h> +#include <algorithm> +#include <functional> +#include <memory> +#include <string_view> + +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star; +using namespace ::osl; +using namespace ::dbtools; +using namespace ::comphelper; + +namespace +{ + + bool lcl_saveConvertToNumber(const Reference< XNumberFormatter > & _xFormatter,sal_Int32 _nKey,const OUString& _sValue,double& _nrValue) + { + bool bRet = false; + try + { + _nrValue = _xFormatter->convertStringToNumber(_nKey, _sValue); + bRet = true; + } + catch(Exception&) + { + } + return bRet; + } + + void replaceAndReset(connectivity::OSQLParseNode*& _pResetNode,connectivity::OSQLParseNode* _pNewNode) + { + _pResetNode->getParent()->replace(_pResetNode, _pNewNode); + delete _pResetNode; + _pResetNode = _pNewNode; + } + + /** quotes a string and search for quotes inside the string and replace them with the new quote + @param rValue + The value to be quoted. + @param rQuot + The quote + @param rQuotToReplace + The quote to replace with + @return + The quoted string. + */ + OUString SetQuotation(std::u16string_view rValue, const OUString& rQuot, std::u16string_view rQuotToReplace) + { + OUString rNewValue = rQuot + rValue; + sal_Int32 nIndex = sal_Int32(-1); // Replace quotes with double quotes or the parser gets into problems + + if (!rQuot.isEmpty()) + { + do + { + nIndex += 2; + nIndex = rNewValue.indexOf(rQuot,nIndex); + if(nIndex != -1) + rNewValue = rNewValue.replaceAt(nIndex,rQuot.getLength(),rQuotToReplace); + } while (nIndex != -1); + } + + rNewValue += rQuot; + return rNewValue; + } + + bool columnMatchP(const connectivity::OSQLParseNode* pSubTree, const connectivity::SQLParseNodeParameter& rParam) + { + using namespace connectivity; + assert(SQL_ISRULE(pSubTree,column_ref)); + + if(!rParam.xField.is()) + return false; + + // retrieve the field's name & table range + OUString aFieldName; + try + { + sal_Int32 nNamePropertyId = PROPERTY_ID_NAME; + if ( rParam.xField->getPropertySetInfo()->hasPropertyByName( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_REALNAME ) ) ) + nNamePropertyId = PROPERTY_ID_REALNAME; + rParam.xField->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( nNamePropertyId ) ) >>= aFieldName; + } + catch ( Exception& ) + { + } + + if(!pSubTree->count()) + return false; + + const OSQLParseNode* pCol = pSubTree->getChild(pSubTree->count()-1); + if (SQL_ISRULE(pCol,column_val)) + { + assert(pCol->count() == 1); + pCol = pCol->getChild(0); + } + const OSQLParseNode* pTable(nullptr); + switch (pSubTree->count()) + { + case 1: + break; + case 3: + pTable = pSubTree->getChild(0); + break; + case 5: + case 7: + SAL_WARN("connectivity.parse", "SQL: catalog and/or schema in column_ref in predicate"); + break; + default: + SAL_WARN("connectivity.parse", "columnMatchP: SQL grammar changed; column_ref has " << pSubTree->count() << " children"); + assert(false); + break; + } + // TODO: not all DBMS match column names case-insensitively... + // see XDatabaseMetaData::supportsMixedCaseIdentifiers() + // and XDatabaseMetaData::supportsMixedCaseQuotedIdentifiers() + if ( // table name matches (or no table name)? + ( !pTable || pTable->getTokenValue().equalsIgnoreAsciiCase(rParam.sPredicateTableAlias) ) + && // column name matches? + pCol->getTokenValue().equalsIgnoreAsciiCase(aFieldName) + ) + return true; + return false; + } +} + +namespace connectivity +{ + +SQLParseNodeParameter::SQLParseNodeParameter( const Reference< XConnection >& _rxConnection, + const Reference< XNumberFormatter >& _xFormatter, const Reference< XPropertySet >& _xField, + const OUString &_sPredicateTableAlias, + const Locale& _rLocale, const IParseContext* _pContext, + bool _bIntl, bool _bQuote, OUString _sDecSep, bool _bPredicate, bool _bParseToSDBC ) + :rLocale(_rLocale) + ,aMetaData( _rxConnection ) + ,pParser( nullptr ) + ,pSubQueryHistory( std::make_shared<QueryNameSet>() ) + ,xFormatter(_xFormatter) + ,xField(_xField) + ,sPredicateTableAlias(_sPredicateTableAlias) + ,m_rContext( _pContext ? *_pContext : OSQLParser::s_aDefaultContext ) + ,sDecSep(_sDecSep) + ,bQuote(_bQuote) + ,bInternational(_bIntl) + ,bPredicate(_bPredicate) + ,bParseToSDBCLevel( _bParseToSDBC ) +{ +} + +OUString OSQLParseNode::convertDateString(const SQLParseNodeParameter& rParam, std::u16string_view rString) +{ + Date aDate = DBTypeConversion::toDate(rString); + Reference< XNumberFormatsSupplier > xSupplier(rParam.xFormatter->getNumberFormatsSupplier()); + Reference< XNumberFormatTypes > xTypes(xSupplier->getNumberFormats(), UNO_QUERY); + + double fDate = DBTypeConversion::toDouble(aDate,DBTypeConversion::getNULLDate(xSupplier)); + sal_Int32 nKey = xTypes->getFormatIndex(NumberFormatIndex::DATE_SYS_DDMMYYYY, rParam.rLocale); + return rParam.xFormatter->convertNumberToString(nKey, fDate); +} + + +OUString OSQLParseNode::convertDateTimeString(const SQLParseNodeParameter& rParam, const OUString& rString) +{ + DateTime aDate = DBTypeConversion::toDateTime(rString); + Reference< XNumberFormatsSupplier > xSupplier(rParam.xFormatter->getNumberFormatsSupplier()); + Reference< XNumberFormatTypes > xTypes(xSupplier->getNumberFormats(), UNO_QUERY); + + double fDateTime = DBTypeConversion::toDouble(aDate,DBTypeConversion::getNULLDate(xSupplier)); + sal_Int32 nKey = xTypes->getFormatIndex(NumberFormatIndex::DATETIME_SYS_DDMMYYYY_HHMMSS, rParam.rLocale); + return rParam.xFormatter->convertNumberToString(nKey, fDateTime); +} + + +OUString OSQLParseNode::convertTimeString(const SQLParseNodeParameter& rParam, std::u16string_view rString) +{ + css::util::Time aTime = DBTypeConversion::toTime(rString); + Reference< XNumberFormatsSupplier > xSupplier(rParam.xFormatter->getNumberFormatsSupplier()); + + Reference< XNumberFormatTypes > xTypes(xSupplier->getNumberFormats(), UNO_QUERY); + + double fTime = DBTypeConversion::toDouble(aTime); + sal_Int32 nKey = xTypes->getFormatIndex(NumberFormatIndex::TIME_HHMMSS, rParam.rLocale); + return rParam.xFormatter->convertNumberToString(nKey, fTime); +} + + +void OSQLParseNode::parseNodeToStr(OUString& rString, + const Reference< XConnection >& _rxConnection, + const IParseContext* pContext, + bool _bIntl, + bool _bQuote) const +{ + parseNodeToStr( + rString, _rxConnection, nullptr, nullptr, OUString(), + pContext ? pContext->getPreferredLocale() : OParseContext::getDefaultLocale(), + pContext, _bIntl, _bQuote, OUString("."), false ); +} + + +void OSQLParseNode::parseNodeToPredicateStr(OUString& rString, + const Reference< XConnection >& _rxConnection, + const Reference< XNumberFormatter > & xFormatter, + const css::lang::Locale& rIntl, + OUString _sDec, + const IParseContext* pContext ) const +{ + OSL_ENSURE(xFormatter.is(), "OSQLParseNode::parseNodeToPredicateStr:: no formatter!"); + + if (xFormatter.is()) + parseNodeToStr(rString, _rxConnection, xFormatter, nullptr, OUString(), rIntl, pContext, true, true, _sDec, true); +} + + +void OSQLParseNode::parseNodeToPredicateStr(OUString& rString, + const Reference< XConnection > & _rxConnection, + const Reference< XNumberFormatter > & xFormatter, + const Reference< XPropertySet > & _xField, + const OUString &_sPredicateTableAlias, + const css::lang::Locale& rIntl, + OUString _sDec, + const IParseContext* pContext ) const +{ + OSL_ENSURE(xFormatter.is(), "OSQLParseNode::parseNodeToPredicateStr:: no formatter!"); + + if (xFormatter.is()) + parseNodeToStr( rString, _rxConnection, xFormatter, _xField, _sPredicateTableAlias, rIntl, pContext, true, true, _sDec, true ); +} + + +void OSQLParseNode::parseNodeToStr(OUString& rString, + const Reference< XConnection > & _rxConnection, + const Reference< XNumberFormatter > & xFormatter, + const Reference< XPropertySet > & _xField, + const OUString &_sPredicateTableAlias, + const css::lang::Locale& rIntl, + const IParseContext* pContext, + bool _bIntl, + bool _bQuote, + OUString _sDecSep, + bool _bPredicate) const +{ + OSL_ENSURE( _rxConnection.is(), "OSQLParseNode::parseNodeToStr: invalid connection!" ); + + if ( !_rxConnection.is() ) + return; + + OUStringBuffer sBuffer(rString); + try + { + OSQLParseNode::impl_parseNodeToString_throw( sBuffer, + SQLParseNodeParameter( + _rxConnection, xFormatter, _xField, _sPredicateTableAlias, rIntl, pContext, + _bIntl, _bQuote, _sDecSep, _bPredicate, false + ) ); + } + catch( const SQLException& ) + { + SAL_WARN( "connectivity.parse", "OSQLParseNode::parseNodeToStr: this should not throw!" ); + // our callers don't expect this method to throw anything. The only known situation + // where impl_parseNodeToString_throw can throw is when there is a cyclic reference + // in the sub queries, but this cannot be the case here, as we do not parse to + // SDBC level. + } + rString = sBuffer.makeStringAndClear(); +} + +bool OSQLParseNode::parseNodeToExecutableStatement( OUString& _out_rString, const Reference< XConnection >& _rxConnection, + OSQLParser& _rParser, css::sdbc::SQLException* _pErrorHolder ) const +{ + OSL_PRECOND( _rxConnection.is(), "OSQLParseNode::parseNodeToExecutableStatement: invalid connection!" ); + SQLParseNodeParameter aParseParam( _rxConnection, + nullptr, nullptr, OUString(), OParseContext::getDefaultLocale(), nullptr, false, true, OUString("."), false, true ); + + if ( aParseParam.aMetaData.supportsSubqueriesInFrom() ) + { + Reference< XQueriesSupplier > xSuppQueries( _rxConnection, UNO_QUERY ); + OSL_ENSURE( xSuppQueries.is(), "OSQLParseNode::parseNodeToExecutableStatement: cannot substitute everything without a QueriesSupplier!" ); + if ( xSuppQueries.is() ) + aParseParam.xQueries = xSuppQueries->getQueries(); + } + + aParseParam.pParser = &_rParser; + + // LIMIT keyword differs in Firebird + OSQLParseNode* pTableExp = getChild(3); + Reference< XDatabaseMetaData > xMeta( _rxConnection->getMetaData() ); + OUString sLimitValue; + if( pTableExp->getChild(6)->count() >= 2 && pTableExp->getChild(6)->getChild(1) + && (xMeta->getURL().equalsIgnoreAsciiCase("sdbc:embedded:firebird") + || xMeta->getURL().startsWithIgnoreAsciiCase("sdbc:firebird:"))) + { + sLimitValue = pTableExp->getChild(6)->getChild(1)->getTokenValue(); + delete pTableExp->removeAt(6); + } + + _out_rString.clear(); + OUStringBuffer sBuffer; + bool bSuccess = false; + try + { + impl_parseNodeToString_throw( sBuffer, aParseParam ); + bSuccess = true; + } + catch( const SQLException& e ) + { + if ( _pErrorHolder ) + *_pErrorHolder = e; + } + + if(sLimitValue.getLength() > 0) + { + constexpr char SELECT_KEYWORD[] = "SELECT"; + sBuffer.insert(sBuffer.indexOf(SELECT_KEYWORD) + strlen(SELECT_KEYWORD), + OUStringConcatenation(" FIRST " + sLimitValue)); + } + + _out_rString = sBuffer.makeStringAndClear(); + return bSuccess; +} + + +namespace +{ + bool lcl_isAliasNamePresent( const OSQLParseNode& _rTableNameNode ) + { + return !OSQLParseNode::getTableRange(_rTableNameNode.getParent()).isEmpty(); + } +} + + +void OSQLParseNode::impl_parseNodeToString_throw(OUStringBuffer& rString, const SQLParseNodeParameter& rParam, bool bSimple) const +{ + if ( isToken() ) + { + parseLeaf(rString,rParam); + return; + } + + // Lets see how many nodes this subtree has + sal_uInt32 nCount = count(); + + bool bHandled = false; + switch ( getKnownRuleID() ) + { + // special handling for parameters + case parameter: + { + bSimple=false; + if(!rString.isEmpty()) + rString.append(" "); + if (nCount == 1) // ? + m_aChildren[0]->impl_parseNodeToString_throw( rString, rParam, false ); + else if (rParam.bParseToSDBCLevel && rParam.aMetaData.shouldSubstituteParameterNames()) + { + rString.append("?"); + } + else if (nCount == 2) // :Name + { + m_aChildren[0]->impl_parseNodeToString_throw( rString, rParam, false ); + rString.append(m_aChildren[1]->m_aNodeValue); + } // [Name] + else + { + assert (nCount == 3); + m_aChildren[0]->impl_parseNodeToString_throw( rString, rParam, false ); + rString.append(m_aChildren[1]->m_aNodeValue); + rString.append(m_aChildren[2]->m_aNodeValue); + } + bHandled = true; + } + break; + + // table refs + case table_ref: + bSimple=false; + if ( ( nCount == 2 ) || ( nCount == 3 ) || ( nCount == 5 ) ) + { + impl_parseTableRangeNodeToString_throw( rString, rParam ); + bHandled = true; + } + break; + + // table name - might be a query name + case table_name: + bSimple=false; + bHandled = impl_parseTableNameNodeToString_throw( rString, rParam ); + break; + + case as_clause: + bSimple=false; + assert(nCount == 0 || nCount == 2); + if (nCount == 2) + { + if ( rParam.aMetaData.generateASBeforeCorrelationName() ) + rString.append(" AS "); + m_aChildren[1]->impl_parseNodeToString_throw( rString, rParam, false ); + } + bHandled = true; + break; + + case opt_as: + assert(nCount == 0); + bHandled = true; + break; + + case like_predicate: + // Depending on whether international is given, LIKE is treated differently + // international: *, ? are placeholders + // else SQL92 conform: %, _ + impl_parseLikeNodeToString_throw( rString, rParam, bSimple ); + bHandled = true; + break; + + case general_set_fct: + case set_fct_spec: + case position_exp: + case extract_exp: + case length_exp: + case char_value_fct: + bSimple=false; + if (!addDateValue(rString, rParam)) + { + // Do not quote function name + SQLParseNodeParameter aNewParam(rParam); + aNewParam.bQuote = ( SQL_ISRULE(this,length_exp) || SQL_ISRULE(this,char_value_fct) ); + + m_aChildren[0]->impl_parseNodeToString_throw( rString, aNewParam, false ); + aNewParam.bQuote = rParam.bQuote; + //aNewParam.bPredicate = sal_False; // disable [ ] around names // look at i73215 + OUStringBuffer aStringPara; + for (sal_uInt32 i=1; i<nCount; i++) + { + const OSQLParseNode * pSubTree = m_aChildren[i].get(); + if (pSubTree) + { + pSubTree->impl_parseNodeToString_throw( aStringPara, aNewParam, false ); + + // In the comma lists, put commas in-between all subtrees + if ((m_eNodeType == SQLNodeType::CommaListRule) && (i < (nCount - 1))) + aStringPara.append(","); + } + else + i++; + } + rString.append(aStringPara); + } + bHandled = true; + break; + case odbc_call_spec: + case subquery: + case term: + case factor: + case window_function: + case cast_spec: + case num_value_exp: + bSimple = false; + break; + default: + break; + } // switch ( getKnownRuleID() ) + + if ( bHandled ) + return; + + for (auto i = m_aChildren.begin(); i != m_aChildren.end();) + { + const OSQLParseNode* pSubTree = i->get(); + if ( !pSubTree ) + { + ++i; + continue; + } + + SQLParseNodeParameter aNewParam(rParam); + + // don't replace the field for subqueries + if (rParam.xField.is() && SQL_ISRULE(pSubTree,subquery)) + aNewParam.xField = nullptr; + + // When we are building a criterion inside a query view, + // simplify criterion display by removing: + // "currentFieldName" + // "currentFieldName" = + // but only in simple expressions. + // This means anything that is made of: + // (see the rules conditionalised by inPredicateCheck() in sqlbison.y). + // - parentheses + // - logical operators (and, or, not) + // - comparison operators (IS, =, >, <, BETWEEN, LIKE, ...) + // but *not* e.g. in function arguments + if (bSimple && rParam.bPredicate && rParam.xField.is() && SQL_ISRULE(pSubTree,column_ref)) + { + if (columnMatchP(pSubTree, rParam)) + { + // skip field + ++i; + // if the following node is the comparison operator'=', + // we filter it as well + if (SQL_ISRULE(this, comparison_predicate)) + { + if(i != m_aChildren.end()) + { + pSubTree = i->get(); + if (pSubTree && pSubTree->getNodeType() == SQLNodeType::Equal) + ++i; + } + } + } + else + { + pSubTree->impl_parseNodeToString_throw( rString, aNewParam, bSimple ); + ++i; + + // In the comma lists, put commas in-between all subtrees + if ((m_eNodeType == SQLNodeType::CommaListRule) && (i != m_aChildren.end())) + rString.append(","); + } + } + else + { + pSubTree->impl_parseNodeToString_throw( rString, aNewParam, bSimple ); + ++i; + + // In the comma lists, put commas in-between all subtrees + if ((m_eNodeType == SQLNodeType::CommaListRule) && (i != m_aChildren.end())) + { + if (SQL_ISRULE(this,value_exp_commalist) && rParam.bPredicate) + rString.append(";"); + else + rString.append(","); + } + } + // The right hand-side of these operators is not simple + switch ( getKnownRuleID() ) + { + case general_set_fct: + case set_fct_spec: + case position_exp: + case extract_exp: + case length_exp: + case char_value_fct: + case odbc_call_spec: + case subquery: + case comparison_predicate: + case between_predicate: + case like_predicate: + case test_for_null: + case in_predicate: + case existence_test: + case unique_test: + case all_or_any_predicate: + case join_condition: + case comparison_predicate_part_2: + case parenthesized_boolean_value_expression: + case other_like_predicate_part_2: + case between_predicate_part_2: + bSimple=false; + break; + default: + break; + } + } +} + + +bool OSQLParseNode::impl_parseTableNameNodeToString_throw( OUStringBuffer& rString, const SQLParseNodeParameter& rParam ) const +{ + // is the table_name part of a table_ref? + OSL_ENSURE( getParent(), "OSQLParseNode::impl_parseTableNameNodeToString_throw: table_name without parent?" ); + if ( !getParent() || ( getParent()->getKnownRuleID() != table_ref ) ) + return false; + + // if it's a query, maybe we need to substitute the SQL statement ... + if ( !rParam.bParseToSDBCLevel ) + return false; + + if ( !rParam.xQueries.is() ) + // connection does not support queries in queries, or was no query supplier + return false; + + try + { + OUString sTableOrQueryName( getChild(0)->getTokenValue() ); + bool bIsQuery = rParam.xQueries->hasByName( sTableOrQueryName ); + if ( !bIsQuery ) + return false; + + // avoid recursion (e.g. "foo" defined as "SELECT * FROM bar" and "bar" defined as "SELECT * FROM foo". + if ( rParam.pSubQueryHistory->find( sTableOrQueryName ) != rParam.pSubQueryHistory->end() ) + { + OSL_ENSURE( rParam.pParser, "OSQLParseNode::impl_parseTableNameNodeToString_throw: no parser?" ); + if ( rParam.pParser ) + { + const SQLError& rErrors( rParam.pParser->getErrorHelper() ); + rErrors.raiseException( sdb::ErrorCondition::PARSER_CYCLIC_SUB_QUERIES ); + } + else + { + SQLError aErrors; + aErrors.raiseException( sdb::ErrorCondition::PARSER_CYCLIC_SUB_QUERIES ); + } + } + rParam.pSubQueryHistory->insert( sTableOrQueryName ); + + Reference< XPropertySet > xQuery( rParam.xQueries->getByName( sTableOrQueryName ), UNO_QUERY_THROW ); + + // substitute the query name with the constituting command + OUString sCommand; + OSL_VERIFY( xQuery->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_COMMAND ) ) >>= sCommand ); + + bool bEscapeProcessing = false; + OSL_VERIFY( xQuery->getPropertyValue( OMetaConnection::getPropMap().getNameByIndex( PROPERTY_ID_ESCAPEPROCESSING ) ) >>= bEscapeProcessing ); + + // the query we found here might itself be based on another query, so parse it recursively + OSL_ENSURE( rParam.pParser, "OSQLParseNode::impl_parseTableNameNodeToString_throw: cannot analyze sub queries without a parser!" ); + if ( bEscapeProcessing && rParam.pParser ) + { + OUString sError; + std::unique_ptr< OSQLParseNode > pSubQueryNode( rParam.pParser->parseTree( sError, sCommand ) ); + if (pSubQueryNode) + { + // parse the sub-select to SDBC level, too + OUStringBuffer sSubSelect; + pSubQueryNode->impl_parseNodeToString_throw( sSubSelect, rParam, false ); + if ( !sSubSelect.isEmpty() ) + sCommand = sSubSelect.makeStringAndClear(); + } + } + + rString.append( " ( " ); + rString.append(sCommand); + rString.append( " )" ); + + // append the query name as table alias, since it might be referenced in other + // parts of the statement - but only if there's no other alias name present + if ( !lcl_isAliasNamePresent( *this ) ) + { + rString.append( " AS " ); + if ( rParam.bQuote ) + rString.append(SetQuotation( sTableOrQueryName, + rParam.aMetaData.getIdentifierQuoteString(), rParam.aMetaData.getIdentifierQuoteString() )); + } + + // don't forget to remove the query name from the history, else multiple inclusions + // won't work + // #i69227# / 2006-10-10 / frank.schoenheit@sun.com + rParam.pSubQueryHistory->erase( sTableOrQueryName ); + + return true; + } + catch( const SQLException& ) + { + throw; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("connectivity.parse"); + } + return false; +} + + +void OSQLParseNode::impl_parseTableRangeNodeToString_throw(OUStringBuffer& rString, const SQLParseNodeParameter& rParam) const +{ + OSL_PRECOND( ( count() == 2 ) || ( count() == 3 ) || ( count() == 5 ) ,"Illegal count"); + + // rString += " "; + std::for_each(m_aChildren.begin(),m_aChildren.end(), + [&] (std::unique_ptr<OSQLParseNode> const & pNode) { pNode->impl_parseNodeToString_throw(rString, rParam, false); }); +} + + +void OSQLParseNode::impl_parseLikeNodeToString_throw( OUStringBuffer& rString, const SQLParseNodeParameter& rParam, bool bSimple ) const +{ + assert(SQL_ISRULE(this,like_predicate)); + OSL_ENSURE(count() == 2,"count != 2: Prepare for GPF"); + + const OSQLParseNode* pEscNode = nullptr; + const OSQLParseNode* pParaNode = nullptr; + + SQLParseNodeParameter aNewParam(rParam); + //aNewParam.bQuote = sal_True; // why setting this to true? @see https://bz.apache.org/ooo/show_bug.cgi?id=75557 + + if ( !(bSimple && rParam.bPredicate && rParam.xField.is() && SQL_ISRULE(m_aChildren[0],column_ref) && columnMatchP(m_aChildren[0].get(), rParam)) ) + m_aChildren[0]->impl_parseNodeToString_throw( rString, aNewParam, bSimple ); + + const OSQLParseNode* pPart2 = m_aChildren[1].get(); + pPart2->getChild(0)->impl_parseNodeToString_throw( rString, aNewParam, false ); + pPart2->getChild(1)->impl_parseNodeToString_throw( rString, aNewParam, false ); + pParaNode = pPart2->getChild(2); + pEscNode = pPart2->getChild(3); + + if (pParaNode->isToken()) + { + OUString aStr = ConvertLikeToken(pParaNode, pEscNode, rParam.bInternational); + rString.append(" "); + rString.append(SetQuotation(aStr, "\'", u"\'\'")); + } + else + pParaNode->impl_parseNodeToString_throw( rString, aNewParam, false ); + + pEscNode->impl_parseNodeToString_throw( rString, aNewParam, false ); +} + + +bool OSQLParseNode::getTableComponents(const OSQLParseNode* _pTableNode, + css::uno::Any &_rCatalog, + OUString &_rSchema, + OUString &_rTable, + const Reference< XDatabaseMetaData >& _xMetaData) +{ + OSL_ENSURE(_pTableNode,"Wrong use of getTableComponents! _pTableNode is not allowed to be null!"); + if(_pTableNode) + { + const bool bSupportsCatalog = _xMetaData.is() && _xMetaData->supportsCatalogsInDataManipulation(); + const bool bSupportsSchema = _xMetaData.is() && _xMetaData->supportsSchemasInDataManipulation(); + const OSQLParseNode* pTableNode = _pTableNode; + // clear the parameter given + _rCatalog = Any(); + _rSchema.clear(); + _rTable.clear(); + // see rule catalog_name: in sqlbison.y + if (SQL_ISRULE(pTableNode,catalog_name)) + { + OSL_ENSURE(pTableNode->getChild(0) && pTableNode->getChild(0)->isToken(),"Invalid parsenode!"); + _rCatalog <<= pTableNode->getChild(0)->getTokenValue(); + pTableNode = pTableNode->getChild(2); + } + // check if we have schema_name rule + if(SQL_ISRULE(pTableNode,schema_name)) + { + if ( bSupportsCatalog && !bSupportsSchema ) + _rCatalog <<= pTableNode->getChild(0)->getTokenValue(); + else + _rSchema = pTableNode->getChild(0)->getTokenValue(); + pTableNode = pTableNode->getChild(2); + } + // check if we have table_name rule + if(SQL_ISRULE(pTableNode,table_name)) + { + _rTable = pTableNode->getChild(0)->getTokenValue(); + } + else + { + SAL_WARN( "connectivity.parse","Error in parse tree!"); + } + } + return !_rTable.isEmpty(); +} + +void OSQLParser::killThousandSeparator(OSQLParseNode* pLiteral) +{ + if ( pLiteral ) + { + if ( s_xLocaleData->getLocaleItem( m_pData->aLocale ).decimalSeparator.toChar() == ',' ) + { + pLiteral->m_aNodeValue = pLiteral->m_aNodeValue.replace('.', sal_Unicode()); + // and replace decimal + pLiteral->m_aNodeValue = pLiteral->m_aNodeValue.replace(',', '.'); + } + else + pLiteral->m_aNodeValue = pLiteral->m_aNodeValue.replace(',', sal_Unicode()); + } +} + +OSQLParseNode* OSQLParser::convertNode(sal_Int32 nType, OSQLParseNode* pLiteral) +{ + if ( !pLiteral ) + return nullptr; + + OSQLParseNode* pReturn = pLiteral; + + if ( ( pLiteral->isRule() && !SQL_ISRULE(pLiteral,value_exp) ) || SQL_ISTOKEN(pLiteral,FALSE) || SQL_ISTOKEN(pLiteral,TRUE) ) + { + switch(nType) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + case DataType::CLOB: + if ( !SQL_ISRULE(pReturn,char_value_exp) && !buildStringNodes(pReturn) ) + pReturn = nullptr; + break; + default: + break; + } + } + else + { + switch(pLiteral->getNodeType()) + { + case SQLNodeType::String: + switch(nType) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + case DataType::CLOB: + break; + case DataType::DATE: + case DataType::TIME: + case DataType::TIMESTAMP: + if (m_xFormatter.is()) + pReturn = buildDate( nType, pReturn); + break; + default: + m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::InvalidCompare); + break; + } + break; + case SQLNodeType::AccessDate: + switch(nType) + { + case DataType::DATE: + case DataType::TIME: + case DataType::TIMESTAMP: + if ( m_xFormatter.is() ) + pReturn = buildDate( nType, pReturn); + else + m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::InvalidDateCompare); + break; + default: + m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::InvalidCompare); + break; + } + break; + case SQLNodeType::IntNum: + switch(nType) + { + case DataType::BIT: + case DataType::BOOLEAN: + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::INTEGER: + case DataType::BIGINT: + case DataType::FLOAT: + case DataType::REAL: + case DataType::DOUBLE: + // kill thousand separators if any + killThousandSeparator(pReturn); + break; + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + case DataType::CLOB: + pReturn = buildNode_STR_NUM(pReturn); + break; + default: + m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::InvalidIntCompare); + break; + } + break; + case SQLNodeType::ApproxNum: + switch(nType) + { + case DataType::DECIMAL: + case DataType::NUMERIC: + case DataType::FLOAT: + case DataType::REAL: + case DataType::DOUBLE: + // kill thousand separators if any + killThousandSeparator(pReturn); + break; + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + case DataType::CLOB: + pReturn = buildNode_STR_NUM(pReturn); + break; + case DataType::INTEGER: + default: + m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::InvalidRealCompare); + break; + } + break; + default: + ; + } + } + return pReturn; +} + +sal_Int16 OSQLParser::buildPredicateRule(OSQLParseNode*& pAppend, OSQLParseNode* pLiteral, OSQLParseNode* pCompare, OSQLParseNode* pLiteral2) +{ + OSL_ENSURE(inPredicateCheck(),"Only in predicate check allowed!"); + sal_Int16 nErg = 0; + if ( m_xField.is() ) + { + sal_Int32 nType = 0; + try + { + m_xField->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= nType; + } + catch( Exception& ) + { + return nErg; + } + + OSQLParseNode* pNode1 = convertNode(nType,pLiteral); + if ( pNode1 ) + { + OSQLParseNode* pNode2 = convertNode(nType,pLiteral2); + if ( m_sErrorMessage.isEmpty() ) + nErg = buildNode(pAppend,pCompare,pNode1,pNode2); + } + } + if (!pCompare->getParent()) // I have no parent so I was not used and I must die :-) + delete pCompare; + return nErg; +} + +sal_Int16 OSQLParser::buildLikeRule(OSQLParseNode* pAppend, OSQLParseNode*& pLiteral, const OSQLParseNode* pEscape) +{ + sal_Int16 nErg = 0; + sal_Int32 nType = 0; + + if (!m_xField.is()) + return nErg; + try + { + Any aValue; + { + aValue = m_xField->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)); + aValue >>= nType; + } + } + catch( Exception& ) + { + return nErg; + } + + switch (nType) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + case DataType::CLOB: + if(pLiteral->isRule()) + { + pAppend->append(pLiteral); + nErg = 1; + } + else + { + switch(pLiteral->getNodeType()) + { + case SQLNodeType::String: + pLiteral->m_aNodeValue = ConvertLikeToken(pLiteral, pEscape, false); + pAppend->append(pLiteral); + nErg = 1; + break; + case SQLNodeType::ApproxNum: + if (m_xFormatter.is() && m_nFormatKey) + { + sal_Int16 nScale = 0; + try + { + Any aValue = getNumberFormatProperty( m_xFormatter, m_nFormatKey, "Decimals" ); + aValue >>= nScale; + } + catch( Exception& ) + { + } + + pAppend->append(new OSQLInternalNode(stringToDouble(pLiteral->getTokenValue(),nScale),SQLNodeType::String)); + } + else + pAppend->append(new OSQLInternalNode(pLiteral->getTokenValue(),SQLNodeType::String)); + + delete pLiteral; + nErg = 1; + break; + default: + m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::ValueNoLike); + m_sErrorMessage = m_sErrorMessage.replaceAt(m_sErrorMessage.indexOf("#1"),2,pLiteral->getTokenValue()); + break; + } + } + break; + default: + m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::FieldNoLike); + break; + } + return nErg; +} + +OSQLParseNode* OSQLParser::buildNode_Date(const double& fValue, sal_Int32 nType) +{ + OSQLParseNode* pNewNode = new OSQLInternalNode("", SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::set_fct_spec)); + pNewNode->append(new OSQLInternalNode("{", SQLNodeType::Punctuation)); + OSQLParseNode* pDateNode = new OSQLInternalNode("", SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::odbc_fct_spec)); + pNewNode->append(pDateNode); + pNewNode->append(new OSQLInternalNode("}", SQLNodeType::Punctuation)); + + switch (nType) + { + case DataType::DATE: + { + Date aDate = DBTypeConversion::toDate(fValue,DBTypeConversion::getNULLDate(m_xFormatter->getNumberFormatsSupplier())); + OUString aString = DBTypeConversion::toDateString(aDate); + pDateNode->append(new OSQLInternalNode("", SQLNodeType::Keyword, SQL_TOKEN_D)); + pDateNode->append(new OSQLInternalNode(aString, SQLNodeType::String)); + break; + } + case DataType::TIME: + { + css::util::Time aTime = DBTypeConversion::toTime(fValue); + OUString aString = DBTypeConversion::toTimeString(aTime); + pDateNode->append(new OSQLInternalNode("", SQLNodeType::Keyword, SQL_TOKEN_T)); + pDateNode->append(new OSQLInternalNode(aString, SQLNodeType::String)); + break; + } + case DataType::TIMESTAMP: + { + DateTime aDateTime = DBTypeConversion::toDateTime(fValue,DBTypeConversion::getNULLDate(m_xFormatter->getNumberFormatsSupplier())); + if (aDateTime.Seconds || aDateTime.Minutes || aDateTime.Hours) + { + OUString aString = DBTypeConversion::toDateTimeString(aDateTime); + pDateNode->append(new OSQLInternalNode("", SQLNodeType::Keyword, SQL_TOKEN_TS)); + pDateNode->append(new OSQLInternalNode(aString, SQLNodeType::String)); + } + else + { + Date aDate(aDateTime.Day,aDateTime.Month,aDateTime.Year); + pDateNode->append(new OSQLInternalNode("", SQLNodeType::Keyword, SQL_TOKEN_D)); + pDateNode->append(new OSQLInternalNode(DBTypeConversion::toDateString(aDate), SQLNodeType::String)); + } + break; + } + } + + return pNewNode; +} + +OSQLParseNode* OSQLParser::buildNode_STR_NUM(OSQLParseNode*& _pLiteral) +{ + OSQLParseNode* pReturn = nullptr; + if ( _pLiteral ) + { + if (m_nFormatKey) + { + sal_Int16 nScale = 0; + try + { + Any aValue = getNumberFormatProperty( m_xFormatter, m_nFormatKey, "Decimals" ); + aValue >>= nScale; + } + catch( Exception& ) + { + } + + pReturn = new OSQLInternalNode(stringToDouble(_pLiteral->getTokenValue(),nScale),SQLNodeType::String); + } + else + pReturn = new OSQLInternalNode(_pLiteral->getTokenValue(),SQLNodeType::String); + + delete _pLiteral; + _pLiteral = nullptr; + } + return pReturn; +} + +OUString OSQLParser::stringToDouble(const OUString& _rValue,sal_Int16 _nScale) +{ + OUString aValue; + if(!m_xCharClass.is()) + m_xCharClass = CharacterClassification::create( m_xContext ); + if( s_xLocaleData.is() ) + { + try + { + ParseResult aResult = m_xCharClass->parsePredefinedToken(KParseType::ANY_NUMBER,_rValue,0,m_pData->aLocale,0,OUString(),KParseType::ANY_NUMBER,OUString()); + if((aResult.TokenType & KParseType::IDENTNAME) && aResult.EndPos == _rValue.getLength()) + { + aValue = OUString::number(aResult.Value); + sal_Int32 nPos = aValue.lastIndexOf('.'); + if((nPos+_nScale) < aValue.getLength()) + aValue = aValue.replaceAt(nPos+_nScale,aValue.getLength()-nPos-_nScale, u""); + aValue = aValue.replaceAt(aValue.lastIndexOf('.'),1,s_xLocaleData->getLocaleItem(m_pData->aLocale).decimalSeparator); + return aValue; + } + } + catch(Exception&) + { + } + } + return aValue; +} + + +::osl::Mutex& OSQLParser::getMutex() +{ + static ::osl::Mutex aMutex; + return aMutex; +} + + +std::unique_ptr<OSQLParseNode> OSQLParser::predicateTree(OUString& rErrorMessage, const OUString& rStatement, + const Reference< css::util::XNumberFormatter > & xFormatter, + const Reference< XPropertySet > & xField, + bool bUseRealName) +{ + // Guard the parsing + ::osl::MutexGuard aGuard(getMutex()); + // must be reset + setParser(this); + + + // reset the parser + m_xField = xField; + m_xFormatter = xFormatter; + + if (m_xField.is()) + { + sal_Int32 nType=0; + try + { + // get the field name + OUString aString; + + // retrieve the fields name + // #75243# use the RealName of the column if there is any otherwise the name which could be the alias + // of the field + Reference< XPropertySetInfo> xInfo = m_xField->getPropertySetInfo(); + if ( bUseRealName && xInfo->hasPropertyByName(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_REALNAME))) + m_xField->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_REALNAME)) >>= aString; + else + m_xField->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME)) >>= aString; + + m_sFieldName = aString; + + // get the field format key + if ( xInfo->hasPropertyByName(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FORMATKEY))) + m_xField->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FORMATKEY)) >>= m_nFormatKey; + else + m_nFormatKey = 0; + + // get the field type + m_xField->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_TYPE)) >>= nType; + } + catch ( Exception& ) + { + OSL_ASSERT(false); + } + + if (m_nFormatKey && m_xFormatter.is()) + { + Any aValue = getNumberFormatProperty( m_xFormatter, m_nFormatKey, OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_LOCALE) ); + OSL_ENSURE(aValue.getValueType() == cppu::UnoType<css::lang::Locale>::get(), "OSQLParser::PredicateTree : invalid language property !"); + + if (aValue.getValueType() == cppu::UnoType<css::lang::Locale>::get()) + aValue >>= m_pData->aLocale; + } + else + m_pData->aLocale = m_pContext->getPreferredLocale(); + + if ( m_xFormatter.is() ) + { + try + { + Reference< css::util::XNumberFormatsSupplier > xFormatSup = m_xFormatter->getNumberFormatsSupplier(); + if ( xFormatSup.is() ) + { + Reference< css::util::XNumberFormats > xFormats = xFormatSup->getNumberFormats(); + if ( xFormats.is() ) + { + css::lang::Locale aLocale; + aLocale.Language = "en"; + aLocale.Country = "US"; + OUString sFormat("YYYY-MM-DD"); + m_nDateFormatKey = xFormats->queryKey(sFormat,aLocale,false); + if ( m_nDateFormatKey == sal_Int32(-1) ) + m_nDateFormatKey = xFormats->addNew(sFormat, aLocale); + } + } + } + catch ( Exception& ) + { + SAL_WARN( "connectivity.parse","DateFormatKey"); + } + } + + switch (nType) + { + case DataType::DATE: + case DataType::TIME: + case DataType::TIMESTAMP: + s_pScanner->SetRule(OSQLScanner::GetDATERule()); + break; + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + case DataType::CLOB: + s_pScanner->SetRule(OSQLScanner::GetSTRINGRule()); + break; + default: + if ( s_xLocaleData->getLocaleItem( m_pData->aLocale ).decimalSeparator.toChar() == ',' ) + s_pScanner->SetRule(OSQLScanner::GetGERRule()); + else + s_pScanner->SetRule(OSQLScanner::GetENGRule()); + } + + } + else + s_pScanner->SetRule(OSQLScanner::GetSQLRule()); + + s_pScanner->prepareScan(rStatement, m_pContext, true); + + SQLyylval.pParseNode = nullptr; + // SQLyypvt = NULL; + m_pParseTree = nullptr; + m_sErrorMessage.clear(); + + // Start the parser + if (SQLyyparse() != 0) + { + m_sFieldName.clear(); + m_xField.clear(); + m_xFormatter.clear(); + m_nFormatKey = 0; + m_nDateFormatKey = 0; + + if (m_sErrorMessage.isEmpty()) + m_sErrorMessage = s_pScanner->getErrorMessage(); + if (m_sErrorMessage.isEmpty()) + m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::General); + + rErrorMessage = m_sErrorMessage; + + // clear the garbage collector + (*s_pGarbageCollector)->clearAndDelete(); + m_pParseTree.release(); // because the garbage collector deleted it + return nullptr; + } + else + { + (*s_pGarbageCollector)->clear(); + + m_sFieldName.clear(); + m_xField.clear(); + m_xFormatter.clear(); + m_nFormatKey = 0; + m_nDateFormatKey = 0; + + // Return the result (the root parse node): + + // Instead, the parse method sets the member pParseTree and simply returns that + OSL_ENSURE(m_pParseTree != nullptr,"OSQLParser: Parser did not return a ParseTree!"); + return std::move(m_pParseTree); + } +} + + +OSQLParser::OSQLParser(const css::uno::Reference< css::uno::XComponentContext >& rxContext, const IParseContext* _pContext) + :m_pContext(_pContext) + ,m_pData( new OSQLParser_Data ) + ,m_nFormatKey(0) + ,m_nDateFormatKey(0) + ,m_xContext(rxContext) +{ + + + setParser(this); + +#ifdef SQLYYDEBUG +#ifdef SQLYYDEBUG_ON + SQLyydebug = 1; +#endif +#endif + + ::osl::MutexGuard aGuard(getMutex()); + // Do we have to initialize the data? + if (s_nRefCount == 0) + { + s_pScanner = new OSQLScanner(); + s_pScanner->setScanner(); + s_pGarbageCollector = new OSQLParseNodesGarbageCollector(); + + if(!s_xLocaleData.is()) + s_xLocaleData = LocaleData::create(m_xContext); + + // reset to UNKNOWN_RULE + static_assert(OSQLParseNode::UNKNOWN_RULE==0, "UNKNOWN_RULE must be 0 for memset to 0 to work"); + memset(OSQLParser::s_nRuleIDs,0,sizeof(OSQLParser::s_nRuleIDs)); + + const struct + { + OSQLParseNode::Rule eRule; // the parse node's ID for the rule + OString sRuleName; // the name of the rule ("select_statement") + } aRuleDescriptions[] = + { + { OSQLParseNode::select_statement, "select_statement" }, + { OSQLParseNode::table_exp, "table_exp" }, + { OSQLParseNode::table_ref_commalist, "table_ref_commalist" }, + { OSQLParseNode::table_ref, "table_ref" }, + { OSQLParseNode::catalog_name, "catalog_name" }, + { OSQLParseNode::schema_name, "schema_name" }, + { OSQLParseNode::table_name, "table_name" }, + { OSQLParseNode::opt_column_commalist, "opt_column_commalist" }, + { OSQLParseNode::column_commalist, "column_commalist" }, + { OSQLParseNode::column_ref_commalist, "column_ref_commalist" }, + { OSQLParseNode::column_ref, "column_ref" }, + { OSQLParseNode::opt_order_by_clause, "opt_order_by_clause" }, + { OSQLParseNode::ordering_spec_commalist, "ordering_spec_commalist" }, + { OSQLParseNode::ordering_spec, "ordering_spec" }, + { OSQLParseNode::opt_asc_desc, "opt_asc_desc" }, + { OSQLParseNode::where_clause, "where_clause" }, + { OSQLParseNode::opt_where_clause, "opt_where_clause" }, + { OSQLParseNode::search_condition, "search_condition" }, + { OSQLParseNode::comparison, "comparison" }, + { OSQLParseNode::comparison_predicate, "comparison_predicate" }, + { OSQLParseNode::between_predicate, "between_predicate" }, + { OSQLParseNode::like_predicate, "like_predicate" }, + { OSQLParseNode::opt_escape, "opt_escape" }, + { OSQLParseNode::test_for_null, "test_for_null" }, + { OSQLParseNode::scalar_exp_commalist, "scalar_exp_commalist" }, + { OSQLParseNode::scalar_exp, "scalar_exp" }, + { OSQLParseNode::parameter_ref, "parameter_ref" }, + { OSQLParseNode::parameter, "parameter" }, + { OSQLParseNode::general_set_fct, "general_set_fct" }, + { OSQLParseNode::range_variable, "range_variable" }, + { OSQLParseNode::column, "column" }, + { OSQLParseNode::delete_statement_positioned, "delete_statement_positioned" }, + { OSQLParseNode::delete_statement_searched, "delete_statement_searched" }, + { OSQLParseNode::update_statement_positioned, "update_statement_positioned" }, + { OSQLParseNode::update_statement_searched, "update_statement_searched" }, + { OSQLParseNode::assignment_commalist, "assignment_commalist" }, + { OSQLParseNode::assignment, "assignment" }, + { OSQLParseNode::values_or_query_spec, "values_or_query_spec" }, + { OSQLParseNode::insert_statement, "insert_statement" }, + { OSQLParseNode::insert_atom_commalist, "insert_atom_commalist" }, + { OSQLParseNode::insert_atom, "insert_atom" }, + { OSQLParseNode::from_clause, "from_clause" }, + { OSQLParseNode::qualified_join, "qualified_join" }, + { OSQLParseNode::cross_union, "cross_union" }, + { OSQLParseNode::select_sublist, "select_sublist" }, + { OSQLParseNode::derived_column, "derived_column" }, + { OSQLParseNode::column_val, "column_val" }, + { OSQLParseNode::set_fct_spec, "set_fct_spec" }, + { OSQLParseNode::boolean_term, "boolean_term" }, + { OSQLParseNode::boolean_primary, "boolean_primary" }, + { OSQLParseNode::num_value_exp, "num_value_exp" }, + { OSQLParseNode::join_type, "join_type" }, + { OSQLParseNode::position_exp, "position_exp" }, + { OSQLParseNode::extract_exp, "extract_exp" }, + { OSQLParseNode::length_exp, "length_exp" }, + { OSQLParseNode::char_value_fct, "char_value_fct" }, + { OSQLParseNode::odbc_call_spec, "odbc_call_spec" }, + { OSQLParseNode::in_predicate, "in_predicate" }, + { OSQLParseNode::existence_test, "existence_test" }, + { OSQLParseNode::unique_test, "unique_test" }, + { OSQLParseNode::all_or_any_predicate, "all_or_any_predicate" }, + { OSQLParseNode::named_columns_join, "named_columns_join" }, + { OSQLParseNode::join_condition, "join_condition" }, + { OSQLParseNode::joined_table, "joined_table" }, + { OSQLParseNode::boolean_factor, "boolean_factor" }, + { OSQLParseNode::sql_not, "sql_not" }, + { OSQLParseNode::manipulative_statement, "manipulative_statement" }, + { OSQLParseNode::subquery, "subquery" }, + { OSQLParseNode::value_exp_commalist, "value_exp_commalist" }, + { OSQLParseNode::odbc_fct_spec, "odbc_fct_spec" }, + { OSQLParseNode::union_statement, "union_statement" }, + { OSQLParseNode::outer_join_type, "outer_join_type" }, + { OSQLParseNode::char_value_exp, "char_value_exp" }, + { OSQLParseNode::term, "term" }, + { OSQLParseNode::value_exp_primary, "value_exp_primary" }, + { OSQLParseNode::value_exp, "value_exp" }, + { OSQLParseNode::selection, "selection" }, + { OSQLParseNode::fold, "fold" }, + { OSQLParseNode::char_substring_fct, "char_substring_fct" }, + { OSQLParseNode::factor, "factor" }, + { OSQLParseNode::base_table_def, "base_table_def" }, + { OSQLParseNode::base_table_element_commalist, "base_table_element_commalist" }, + { OSQLParseNode::data_type, "data_type" }, + { OSQLParseNode::column_def, "column_def" }, + { OSQLParseNode::table_node, "table_node" }, + { OSQLParseNode::as_clause, "as_clause" }, + { OSQLParseNode::opt_as, "opt_as" }, + { OSQLParseNode::op_column_commalist, "op_column_commalist" }, + { OSQLParseNode::table_primary_as_range_column, "table_primary_as_range_column" }, + { OSQLParseNode::datetime_primary, "datetime_primary" }, + { OSQLParseNode::concatenation, "concatenation" }, + { OSQLParseNode::char_factor, "char_factor" }, + { OSQLParseNode::bit_value_fct, "bit_value_fct" }, + { OSQLParseNode::comparison_predicate_part_2, "comparison_predicate_part_2" }, + { OSQLParseNode::parenthesized_boolean_value_expression, "parenthesized_boolean_value_expression" }, + { OSQLParseNode::character_string_type, "character_string_type" }, + { OSQLParseNode::other_like_predicate_part_2, "other_like_predicate_part_2" }, + { OSQLParseNode::between_predicate_part_2, "between_predicate_part_2" }, + { OSQLParseNode::null_predicate_part_2, "null_predicate_part_2" }, + { OSQLParseNode::cast_spec, "cast_spec" }, + { OSQLParseNode::window_function, "window_function" } + }; + const size_t nRuleMapCount = std::size( aRuleDescriptions ); + // added a new rule? Adjust this map! + // +1 for UNKNOWN_RULE + static_assert(nRuleMapCount + 1 == static_cast<size_t>(OSQLParseNode::rule_count), "must be equal"); + + for (const auto & aRuleDescription : aRuleDescriptions) + { + // look up the rule description in the our identifier map + sal_uInt32 nParserRuleID = StrToRuleID( aRuleDescription.sRuleName ); + // map the parser's rule ID to the OSQLParseNode::Rule + s_aReverseRuleIDLookup[ nParserRuleID ] = aRuleDescription.eRule; + // and map the OSQLParseNode::Rule to the parser's rule ID + s_nRuleIDs[ aRuleDescription.eRule ] = nParserRuleID; + } + } + ++s_nRefCount; + + if (m_pContext == nullptr) + // take the default context + m_pContext = &s_aDefaultContext; + + m_pData->aLocale = m_pContext->getPreferredLocale(); +} + + +OSQLParser::~OSQLParser() +{ + ::osl::MutexGuard aGuard(getMutex()); + OSL_ENSURE(s_nRefCount > 0, "OSQLParser::~OSQLParser() : suspicious call : has a refcount of 0 !"); + if (!--s_nRefCount) + { + s_pScanner->setScanner(true); + delete s_pScanner; + s_pScanner = nullptr; + + delete s_pGarbageCollector; + s_pGarbageCollector = nullptr; + // Is only set the first time, so we should delete it only when there are no more instances + s_xLocaleData = nullptr; + + RuleIDMap().swap(s_aReverseRuleIDLookup); + } + m_pParseTree = nullptr; +} + +void OSQLParseNode::substituteParameterNames(OSQLParseNode const * _pNode) +{ + sal_Int32 nCount = _pNode->count(); + for(sal_Int32 i=0;i < nCount;++i) + { + OSQLParseNode* pChildNode = _pNode->getChild(i); + if(SQL_ISRULE(pChildNode,parameter) && pChildNode->count() > 1) + { + OSQLParseNode* pNewNode = new OSQLParseNode("?" ,SQLNodeType::Punctuation,0); + delete pChildNode->replace(pChildNode->getChild(0),pNewNode); + sal_Int32 nChildCount = pChildNode->count(); + for(sal_Int32 j=1;j < nChildCount;++j) + delete pChildNode->removeAt(1); + } + else + substituteParameterNames(pChildNode); + + } +} + +bool OSQLParser::extractDate(OSQLParseNode const * pLiteral,double& _rfValue) +{ + Reference< XNumberFormatsSupplier > xFormatSup = m_xFormatter->getNumberFormatsSupplier(); + Reference< XNumberFormatTypes > xFormatTypes; + if ( xFormatSup.is() ) + xFormatTypes.set(xFormatSup->getNumberFormats(), css::uno::UNO_QUERY); + + // if there is no format key, yet, make sure we have a feasible one for our locale + try + { + if ( !m_nFormatKey && xFormatTypes.is() ) + m_nFormatKey = ::dbtools::getDefaultNumberFormat( m_xField, xFormatTypes, m_pData->aLocale ); + } + catch( Exception& ) { } + const OUString& sValue = pLiteral->getTokenValue(); + sal_Int32 nTryFormat = m_nFormatKey; + bool bSuccess = lcl_saveConvertToNumber( m_xFormatter, nTryFormat, sValue, _rfValue ); + + // If our format key didn't do, try the default date format for our locale. + if ( !bSuccess && xFormatTypes.is() ) + { + try + { + nTryFormat = xFormatTypes->getStandardFormat( NumberFormat::DATE, m_pData->aLocale ); + } + catch( Exception& ) { } + bSuccess = lcl_saveConvertToNumber( m_xFormatter, nTryFormat, sValue, _rfValue ); + } + + // if this also didn't do, try ISO format + if ( !bSuccess && xFormatTypes.is() ) + { + try + { + nTryFormat = xFormatTypes->getFormatIndex( NumberFormatIndex::DATE_DIN_YYYYMMDD, m_pData->aLocale ); + } + catch( Exception& ) { } + bSuccess = lcl_saveConvertToNumber( m_xFormatter, nTryFormat, sValue, _rfValue ); + } + + // if this also didn't do, try fallback date format (en-US) + if ( !bSuccess ) + { + nTryFormat = m_nDateFormatKey; + bSuccess = lcl_saveConvertToNumber( m_xFormatter, nTryFormat, sValue, _rfValue ); + } + return bSuccess; +} + +OSQLParseNode* OSQLParser::buildDate(sal_Int32 _nType,OSQLParseNode*& pLiteral) +{ + // try converting the string into a date, according to our format key + double fValue = 0.0; + OSQLParseNode* pFCTNode = nullptr; + + if ( extractDate(pLiteral,fValue) ) + pFCTNode = buildNode_Date( fValue, _nType); + + delete pLiteral; + pLiteral = nullptr; + + if ( !pFCTNode ) + m_sErrorMessage = m_pContext->getErrorMessage(IParseContext::ErrorCode::InvalidDateCompare); + + return pFCTNode; +} + + +OSQLParseNode::OSQLParseNode(const char * pNewValue, + SQLNodeType eNewNodeType, + sal_uInt32 nNewNodeID) + :m_pParent(nullptr) + ,m_aNodeValue(pNewValue,strlen(pNewValue),RTL_TEXTENCODING_UTF8) + ,m_eNodeType(eNewNodeType) + ,m_nNodeID(nNewNodeID) +{ + OSL_ENSURE(m_eNodeType >= SQLNodeType::Rule && m_eNodeType <= SQLNodeType::Concat,"OSQLParseNode: created with invalid NodeType"); +} + +OSQLParseNode::OSQLParseNode(std::string_view _rNewValue, + SQLNodeType eNewNodeType, + sal_uInt32 nNewNodeID) + :m_pParent(nullptr) + ,m_aNodeValue(OStringToOUString(_rNewValue,RTL_TEXTENCODING_UTF8)) + ,m_eNodeType(eNewNodeType) + ,m_nNodeID(nNewNodeID) +{ + OSL_ENSURE(m_eNodeType >= SQLNodeType::Rule && m_eNodeType <= SQLNodeType::Concat,"OSQLParseNode: created with invalid NodeType"); +} + +OSQLParseNode::OSQLParseNode(const OUString &_rNewValue, + SQLNodeType eNewNodeType, + sal_uInt32 nNewNodeID) + :m_pParent(nullptr) + ,m_aNodeValue(_rNewValue) + ,m_eNodeType(eNewNodeType) + ,m_nNodeID(nNewNodeID) +{ + OSL_ENSURE(m_eNodeType >= SQLNodeType::Rule && m_eNodeType <= SQLNodeType::Concat,"OSQLParseNode: created with invalid NodeType"); +} + +OSQLParseNode::OSQLParseNode(const OSQLParseNode& rParseNode) +{ + // Set the getParent to NULL + m_pParent = nullptr; + + // Copy the members + m_aNodeValue = rParseNode.m_aNodeValue; + m_eNodeType = rParseNode.m_eNodeType; + m_nNodeID = rParseNode.m_nNodeID; + + + // Remember that we derived from Container. According to SV-Help the Container's + // copy ctor creates a new Container with the same pointers for content. + // This means after copying the Container, for all non-NULL pointers a copy is + // created and reattached instead of the old pointer. + + // If not a leaf, then process SubTrees + for (auto const& child : rParseNode.m_aChildren) + append(new OSQLParseNode(*child)); +} + + +OSQLParseNode& OSQLParseNode::operator=(const OSQLParseNode& rParseNode) +{ + if (this != &rParseNode) + { + // Copy the members - pParent remains the same + m_aNodeValue = rParseNode.m_aNodeValue; + m_eNodeType = rParseNode.m_eNodeType; + m_nNodeID = rParseNode.m_nNodeID; + + m_aChildren.clear(); + + for (auto const& child : rParseNode.m_aChildren) + append(new OSQLParseNode(*child)); + } + return *this; +} + + +bool OSQLParseNode::operator==(OSQLParseNode const & rParseNode) const +{ + // The members must be equal + bool bResult = (m_nNodeID == rParseNode.m_nNodeID) && + (m_eNodeType == rParseNode.m_eNodeType) && + (m_aNodeValue == rParseNode.m_aNodeValue) && + count() == rParseNode.count(); + + // Parameters are not equal! + bResult = bResult && !SQL_ISRULE(this, parameter); + + // compare children + for (size_t i=0; bResult && i < count(); i++) + bResult = *getChild(i) == *rParseNode.getChild(i); + + return bResult; +} + + +OSQLParseNode::~OSQLParseNode() +{ +} + + +void OSQLParseNode::append(OSQLParseNode* pNewNode) +{ + OSL_ENSURE(pNewNode != nullptr, "OSQLParseNode: invalid NewSubTree"); + OSL_ENSURE(pNewNode->getParent() == nullptr, "OSQLParseNode: Node is not an orphan"); + OSL_ENSURE(std::none_of(m_aChildren.begin(), m_aChildren.end(), + [&] (std::unique_ptr<OSQLParseNode> const & r) { return r.get() == pNewNode; }), + "OSQLParseNode::append() Node already element of parent"); + + // Create connection to getParent + pNewNode->setParent( this ); + // and attach the SubTree at the end + m_aChildren.emplace_back(pNewNode); +} + +bool OSQLParseNode::addDateValue(OUStringBuffer& rString, const SQLParseNodeParameter& rParam) const +{ + // special display for date/time values + if (!SQL_ISRULE(this,set_fct_spec) || !SQL_ISPUNCTUATION(m_aChildren[0],"{")) + return false; + + const OSQLParseNode* pODBCNode = m_aChildren[1].get(); + const OSQLParseNode* pODBCNodeChild = pODBCNode->m_aChildren[0].get(); + + if (pODBCNodeChild->getNodeType() != SQLNodeType::Keyword || !( + SQL_ISTOKEN(pODBCNodeChild, D) || + SQL_ISTOKEN(pODBCNodeChild, T) || + SQL_ISTOKEN(pODBCNodeChild, TS) )) + return false; + + OUString suQuote("'"); + if (rParam.bPredicate) + { + if (rParam.aMetaData.shouldEscapeDateTime()) + { + suQuote = "#"; + } + } + else + { + if (rParam.aMetaData.shouldEscapeDateTime()) + { + // suQuote = "'"; + return false; + } + } + + if (!rString.isEmpty()) + rString.append(" "); + rString.append(suQuote); + const OUString sTokenValue = pODBCNode->m_aChildren[1]->getTokenValue(); + if (SQL_ISTOKEN(pODBCNodeChild, D)) + { + rString.append(rParam.bPredicate ? convertDateString(rParam, sTokenValue) : sTokenValue); + } + else if (SQL_ISTOKEN(pODBCNodeChild, T)) + { + rString.append(rParam.bPredicate ? convertTimeString(rParam, sTokenValue) : sTokenValue); + } + else + { + rString.append(rParam.bPredicate ? convertDateTimeString(rParam, sTokenValue) : sTokenValue); + } + rString.append(suQuote); + return true; +} + +void OSQLParseNode::replaceNodeValue(const OUString& rTableAlias, const OUString& rColumnName) +{ + for (size_t i=0;i<count();++i) + { + if (SQL_ISRULE(this,column_ref) && count() == 1 && getChild(0)->getTokenValue() == rColumnName) + { + OSQLParseNode * pCol = removeAt(sal_uInt32(0)); + append(new OSQLParseNode(rTableAlias,SQLNodeType::Name)); + append(new OSQLParseNode(".",SQLNodeType::Punctuation)); + append(pCol); + } + else + getChild(i)->replaceNodeValue(rTableAlias,rColumnName); + } +} + +OSQLParseNode* OSQLParseNode::getByRule(OSQLParseNode::Rule eRule) const +{ + OSQLParseNode* pRetNode = nullptr; + if (isRule() && OSQLParser::RuleID(eRule) == getRuleID()) + pRetNode = const_cast<OSQLParseNode*>(this); + else + { + for (auto const& child : m_aChildren) + { + pRetNode = child->getByRule(eRule); + if (pRetNode) + break; + } + } + return pRetNode; +} + +static OSQLParseNode* MakeANDNode(OSQLParseNode *pLeftLeaf,OSQLParseNode *pRightLeaf) +{ + OSQLParseNode* pNewNode = new OSQLParseNode(OUString(),SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::boolean_term)); + pNewNode->append(pLeftLeaf); + pNewNode->append(new OSQLParseNode("AND",SQLNodeType::Keyword,SQL_TOKEN_AND)); + pNewNode->append(pRightLeaf); + return pNewNode; +} + +static OSQLParseNode* MakeORNode(OSQLParseNode *pLeftLeaf,OSQLParseNode *pRightLeaf) +{ + OSQLParseNode* pNewNode = new OSQLParseNode(OUString(),SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::search_condition)); + pNewNode->append(pLeftLeaf); + pNewNode->append(new OSQLParseNode("OR",SQLNodeType::Keyword,SQL_TOKEN_OR)); + pNewNode->append(pRightLeaf); + return pNewNode; +} + +void OSQLParseNode::disjunctiveNormalForm(OSQLParseNode*& pSearchCondition) +{ + if(!pSearchCondition) // no where condition at entry point + return; + + OSQLParseNode::absorptions(pSearchCondition); + // '(' search_condition ')' + if (SQL_ISRULE(pSearchCondition,boolean_primary)) + { + OSQLParseNode* pLeft = pSearchCondition->getChild(1); + disjunctiveNormalForm(pLeft); + } + // search_condition SQL_TOKEN_OR boolean_term + else if (SQL_ISRULE(pSearchCondition,search_condition)) + { + OSQLParseNode* pLeft = pSearchCondition->getChild(0); + disjunctiveNormalForm(pLeft); + + OSQLParseNode* pRight = pSearchCondition->getChild(2); + disjunctiveNormalForm(pRight); + } + // boolean_term SQL_TOKEN_AND boolean_factor + else if (SQL_ISRULE(pSearchCondition,boolean_term)) + { + OSQLParseNode* pLeft = pSearchCondition->getChild(0); + disjunctiveNormalForm(pLeft); + + OSQLParseNode* pRight = pSearchCondition->getChild(2); + disjunctiveNormalForm(pRight); + + OSQLParseNode* pNewNode = nullptr; + // '(' search_condition ')' on left side + if(pLeft->count() == 3 && SQL_ISRULE(pLeft,boolean_primary) && SQL_ISRULE(pLeft->getChild(1),search_condition)) + { + // and-or tree on left side + OSQLParseNode* pOr = pLeft->getChild(1); + OSQLParseNode* pNewLeft = nullptr; + OSQLParseNode* pNewRight = nullptr; + + // cut right from parent + OSQLParseNode* pOldRight = pSearchCondition->removeAt(2); + assert(pOldRight == pRight); + + pNewRight = MakeANDNode(pOr->removeAt(2), pOldRight); + pNewLeft = MakeANDNode(pOr->removeAt(sal_uInt32(0)), new OSQLParseNode(*pOldRight)); + pNewNode = MakeORNode(pNewLeft,pNewRight); + // and append new Node + replaceAndReset(pSearchCondition,pNewNode); + + disjunctiveNormalForm(pSearchCondition); + } + else if(pRight->count() == 3 && SQL_ISRULE(pRight,boolean_primary) && SQL_ISRULE(pRight->getChild(1),search_condition)) + { // '(' search_condition ')' on right side + // and-or tree on right side + // a and (b or c) + OSQLParseNode* pOr = pRight->getChild(1); + OSQLParseNode* pNewLeft = nullptr; + OSQLParseNode* pNewRight = nullptr; + + // cut left from parent + OSQLParseNode* pOldLeft = pSearchCondition->removeAt(sal_uInt32(0)); + assert(pOldLeft == pLeft); + + pNewRight = MakeANDNode(pOldLeft, pOr->removeAt(2)); + pNewLeft = MakeANDNode(new OSQLParseNode(*pOldLeft), pOr->removeAt(sal_uInt32(0))); + pNewNode = MakeORNode(pNewLeft,pNewRight); + + // and append new Node + replaceAndReset(pSearchCondition,pNewNode); + disjunctiveNormalForm(pSearchCondition); + } + else if(SQL_ISRULE(pLeft,boolean_primary) && (!SQL_ISRULE(pLeft->getChild(1),search_condition) || !SQL_ISRULE(pLeft->getChild(1),boolean_term))) + pSearchCondition->replace(pLeft, pLeft->removeAt(1)); + else if(SQL_ISRULE(pRight,boolean_primary) && (!SQL_ISRULE(pRight->getChild(1),search_condition) || !SQL_ISRULE(pRight->getChild(1),boolean_term))) + pSearchCondition->replace(pRight, pRight->removeAt(1)); + } +} + +void OSQLParseNode::negateSearchCondition(OSQLParseNode*& pSearchCondition, bool bNegate) +{ + if(!pSearchCondition) // no where condition at entry point + return; + // '(' search_condition ')' + if (pSearchCondition->count() == 3 && SQL_ISRULE(pSearchCondition,boolean_primary)) + { + OSQLParseNode* pRight = pSearchCondition->getChild(1); + negateSearchCondition(pRight,bNegate); + } + // search_condition SQL_TOKEN_OR boolean_term + else if (SQL_ISRULE(pSearchCondition,search_condition)) + { + OSQLParseNode* pLeft = pSearchCondition->getChild(0); + OSQLParseNode* pRight = pSearchCondition->getChild(2); + if(bNegate) + { + OSQLParseNode* pNewNode = new OSQLParseNode(OUString(),SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::boolean_term)); + pNewNode->append(pSearchCondition->removeAt(sal_uInt32(0))); + pNewNode->append(new OSQLParseNode("AND",SQLNodeType::Keyword,SQL_TOKEN_AND)); + pNewNode->append(pSearchCondition->removeAt(sal_uInt32(1))); + replaceAndReset(pSearchCondition,pNewNode); + + pLeft = pNewNode->getChild(0); + pRight = pNewNode->getChild(2); + } + + negateSearchCondition(pLeft,bNegate); + negateSearchCondition(pRight,bNegate); + } + // boolean_term SQL_TOKEN_AND boolean_factor + else if (SQL_ISRULE(pSearchCondition,boolean_term)) + { + OSQLParseNode* pLeft = pSearchCondition->getChild(0); + OSQLParseNode* pRight = pSearchCondition->getChild(2); + if(bNegate) + { + OSQLParseNode* pNewNode = new OSQLParseNode(OUString(),SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::search_condition)); + pNewNode->append(pSearchCondition->removeAt(sal_uInt32(0))); + pNewNode->append(new OSQLParseNode("OR",SQLNodeType::Keyword,SQL_TOKEN_OR)); + pNewNode->append(pSearchCondition->removeAt(sal_uInt32(1))); + replaceAndReset(pSearchCondition,pNewNode); + + pLeft = pNewNode->getChild(0); + pRight = pNewNode->getChild(2); + } + + negateSearchCondition(pLeft,bNegate); + negateSearchCondition(pRight,bNegate); + } + // SQL_TOKEN_NOT ( boolean_primary ) + else if (SQL_ISRULE(pSearchCondition,boolean_factor)) + { + OSQLParseNode *pNot = pSearchCondition->removeAt(sal_uInt32(0)); + delete pNot; + OSQLParseNode *pBooleanTest = pSearchCondition->removeAt(sal_uInt32(0)); + // TODO is this needed // pBooleanTest->setParent(NULL); + replaceAndReset(pSearchCondition,pBooleanTest); + + if (!bNegate) + negateSearchCondition(pSearchCondition, true); // negate all deeper values + } + // row_value_constructor comparison row_value_constructor + // row_value_constructor comparison any_all_some subquery + else if(bNegate && (SQL_ISRULE(pSearchCondition,comparison_predicate) || SQL_ISRULE(pSearchCondition,all_or_any_predicate))) + { + assert(pSearchCondition->count() == 3); + OSQLParseNode* pComparison = pSearchCondition->getChild(1); + if(SQL_ISRULE(pComparison, comparison)) + { + assert(pComparison->count() == 2 || + pComparison->count() == 4); + assert(SQL_ISTOKEN(pComparison->getChild(0), IS)); + + OSQLParseNode* pNot = pComparison->getChild(1); + OSQLParseNode* pNotNot = nullptr; + if(pNot->isRule()) // no NOT token (empty rule) + pNotNot = new OSQLParseNode("NOT",SQLNodeType::Keyword,SQL_TOKEN_NOT); + else + { + assert(SQL_ISTOKEN(pNot,NOT)); + pNotNot = new OSQLParseNode(OUString(),SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::sql_not)); + } + pComparison->replace(pNot, pNotNot); + delete pNot; + } + else + { + OSQLParseNode* pNewComparison; + switch(pComparison->getNodeType()) + { + default: + case SQLNodeType::Equal: + assert(pComparison->getNodeType() == SQLNodeType::Equal && + "OSQLParseNode::negateSearchCondition: unexpected node type!"); + pNewComparison = new OSQLParseNode("<>",SQLNodeType::NotEqual,SQL_NOTEQUAL); + break; + case SQLNodeType::Less: + pNewComparison = new OSQLParseNode(">=",SQLNodeType::GreatEq,SQL_GREATEQ); + break; + case SQLNodeType::Great: + pNewComparison = new OSQLParseNode("<=",SQLNodeType::LessEq,SQL_LESSEQ); + break; + case SQLNodeType::LessEq: + pNewComparison = new OSQLParseNode(">",SQLNodeType::Great,SQL_GREAT); + break; + case SQLNodeType::GreatEq: + pNewComparison = new OSQLParseNode("<",SQLNodeType::Less,SQL_LESS); + break; + case SQLNodeType::NotEqual: + pNewComparison = new OSQLParseNode("=",SQLNodeType::Equal,SQL_EQUAL); + break; + } + pSearchCondition->replace(pComparison, pNewComparison); + delete pComparison; + } + } + + else if(bNegate && (SQL_ISRULE(pSearchCondition,test_for_null) || + SQL_ISRULE(pSearchCondition,in_predicate) || + SQL_ISRULE(pSearchCondition,between_predicate) )) + { + OSQLParseNode* pPart2 = pSearchCondition->getChild(1); + sal_uInt32 nNotPos = 0; + if ( SQL_ISRULE( pSearchCondition, test_for_null ) ) + nNotPos = 1; + + OSQLParseNode* pNot = pPart2->getChild(nNotPos); + OSQLParseNode* pNotNot = nullptr; + if(pNot->isRule()) // no NOT token (empty rule) + pNotNot = new OSQLParseNode("NOT",SQLNodeType::Keyword,SQL_TOKEN_NOT); + else + { + assert(SQL_ISTOKEN(pNot,NOT)); + pNotNot = new OSQLParseNode(OUString(),SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::sql_not)); + } + pPart2->replace(pNot, pNotNot); + delete pNot; + } + else if(bNegate && SQL_ISRULE(pSearchCondition,like_predicate)) + { + OSQLParseNode* pNot = pSearchCondition->getChild( 1 )->getChild( 0 ); + OSQLParseNode* pNotNot = nullptr; + if(pNot->isRule()) + pNotNot = new OSQLParseNode("NOT",SQLNodeType::Keyword,SQL_TOKEN_NOT); + else + pNotNot = new OSQLParseNode(OUString(),SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::sql_not)); + pSearchCondition->getChild( 1 )->replace(pNot, pNotNot); + delete pNot; + } +} + +void OSQLParseNode::eraseBraces(OSQLParseNode*& pSearchCondition) +{ + if (!(pSearchCondition && (SQL_ISRULE(pSearchCondition,boolean_primary) || (pSearchCondition->count() == 3 && SQL_ISPUNCTUATION(pSearchCondition->getChild(0),"(") && + SQL_ISPUNCTUATION(pSearchCondition->getChild(2),")"))))) + return; + + OSQLParseNode* pRight = pSearchCondition->getChild(1); + absorptions(pRight); + // if child is not an or and tree then delete () around child + if(!(SQL_ISRULE(pSearchCondition->getChild(1),boolean_term) || SQL_ISRULE(pSearchCondition->getChild(1),search_condition)) || + SQL_ISRULE(pSearchCondition->getChild(1),boolean_term) || // and can always stand without () + (SQL_ISRULE(pSearchCondition->getChild(1),search_condition) && SQL_ISRULE(pSearchCondition->getParent(),search_condition))) + { + OSQLParseNode* pNode = pSearchCondition->removeAt(1); + replaceAndReset(pSearchCondition,pNode); + } +} + +void OSQLParseNode::absorptions(OSQLParseNode*& pSearchCondition) +{ + if(!pSearchCondition) // no where condition at entry point + return; + + eraseBraces(pSearchCondition); + + if(SQL_ISRULE(pSearchCondition,boolean_term) || SQL_ISRULE(pSearchCondition,search_condition)) + { + OSQLParseNode* pLeft = pSearchCondition->getChild(0); + absorptions(pLeft); + OSQLParseNode* pRight = pSearchCondition->getChild(2); + absorptions(pRight); + } + + sal_uInt32 nPos = 0; + // a and a || a or a + OSQLParseNode* pNewNode = nullptr; + if(( SQL_ISRULE(pSearchCondition,boolean_term) || SQL_ISRULE(pSearchCondition,search_condition)) + && *pSearchCondition->getChild(0) == *pSearchCondition->getChild(2)) + { + pNewNode = pSearchCondition->removeAt(sal_uInt32(0)); + replaceAndReset(pSearchCondition,pNewNode); + } + // ( a or b ) and a || ( b or c ) and a + // a and ( a or b ) || a and ( b or c ) + else if ( SQL_ISRULE(pSearchCondition,boolean_term) + && ( + ( SQL_ISRULE(pSearchCondition->getChild(nPos = 0),boolean_primary) + || SQL_ISRULE(pSearchCondition->getChild(nPos),search_condition) + ) + || ( SQL_ISRULE(pSearchCondition->getChild(nPos = 2),boolean_primary) + || SQL_ISRULE(pSearchCondition->getChild(nPos),search_condition) + ) + ) + ) + { + OSQLParseNode* p2ndSearch = pSearchCondition->getChild(nPos); + if ( SQL_ISRULE(p2ndSearch,boolean_primary) ) + p2ndSearch = p2ndSearch->getChild(1); + + if ( *p2ndSearch->getChild(0) == *pSearchCondition->getChild(2-nPos) ) // a and ( a or b) -> a or b + { + pNewNode = pSearchCondition->removeAt(sal_uInt32(0)); + replaceAndReset(pSearchCondition,pNewNode); + + } + else if ( *p2ndSearch->getChild(2) == *pSearchCondition->getChild(2-nPos) ) // a and ( b or a) -> a or b + { + pNewNode = pSearchCondition->removeAt(sal_uInt32(2)); + replaceAndReset(pSearchCondition,pNewNode); + } + else if ( p2ndSearch->getByRule(OSQLParseNode::search_condition) ) + { + // a and ( b or c ) -> ( a and b ) or ( a and c ) + // ( b or c ) and a -> ( a and b ) or ( a and c ) + OSQLParseNode* pC = p2ndSearch->removeAt(sal_uInt32(2)); + OSQLParseNode* pB = p2ndSearch->removeAt(sal_uInt32(0)); + OSQLParseNode* pA = pSearchCondition->removeAt(sal_uInt32(2)-nPos); + + OSQLParseNode* p1stAnd = MakeANDNode(pA,pB); + OSQLParseNode* p2ndAnd = MakeANDNode(new OSQLParseNode(*pA),pC); + pNewNode = MakeORNode(p1stAnd,p2ndAnd); + OSQLParseNode* pNode = new OSQLParseNode(OUString(),SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::boolean_primary)); + pNode->append(new OSQLParseNode("(",SQLNodeType::Punctuation)); + pNode->append(pNewNode); + pNode->append(new OSQLParseNode(")",SQLNodeType::Punctuation)); + OSQLParseNode::eraseBraces(p1stAnd); + OSQLParseNode::eraseBraces(p2ndAnd); + replaceAndReset(pSearchCondition,pNode); + } + } + // a or a and b || a or b and a + else if(SQL_ISRULE(pSearchCondition,search_condition) && SQL_ISRULE(pSearchCondition->getChild(2),boolean_term)) + { + if(*pSearchCondition->getChild(2)->getChild(0) == *pSearchCondition->getChild(0)) + { + pNewNode = pSearchCondition->removeAt(sal_uInt32(0)); + replaceAndReset(pSearchCondition,pNewNode); + } + else if(*pSearchCondition->getChild(2)->getChild(2) == *pSearchCondition->getChild(0)) + { + pNewNode = pSearchCondition->removeAt(sal_uInt32(0)); + replaceAndReset(pSearchCondition,pNewNode); + } + } + // a and b or a || b and a or a + else if(SQL_ISRULE(pSearchCondition,search_condition) && SQL_ISRULE(pSearchCondition->getChild(0),boolean_term)) + { + if(*pSearchCondition->getChild(0)->getChild(0) == *pSearchCondition->getChild(2)) + { + pNewNode = pSearchCondition->removeAt(sal_uInt32(2)); + replaceAndReset(pSearchCondition,pNewNode); + } + else if(*pSearchCondition->getChild(0)->getChild(2) == *pSearchCondition->getChild(2)) + { + pNewNode = pSearchCondition->removeAt(sal_uInt32(2)); + replaceAndReset(pSearchCondition,pNewNode); + } + } + eraseBraces(pSearchCondition); +} + +void OSQLParseNode::compress(OSQLParseNode *&pSearchCondition) +{ + if(!pSearchCondition) // no WHERE condition at entry point + return; + + OSQLParseNode::eraseBraces(pSearchCondition); + + if(SQL_ISRULE(pSearchCondition,boolean_term) || SQL_ISRULE(pSearchCondition,search_condition)) + { + OSQLParseNode* pLeft = pSearchCondition->getChild(0); + compress(pLeft); + + OSQLParseNode* pRight = pSearchCondition->getChild(2); + compress(pRight); + } + else if( SQL_ISRULE(pSearchCondition,boolean_primary) || (pSearchCondition->count() == 3 && SQL_ISPUNCTUATION(pSearchCondition->getChild(0),"(") && + SQL_ISPUNCTUATION(pSearchCondition->getChild(2),")"))) + { + OSQLParseNode* pRight = pSearchCondition->getChild(1); + compress(pRight); + // if child is not an or and tree then delete () around child + if(!(SQL_ISRULE(pSearchCondition->getChild(1),boolean_term) || SQL_ISRULE(pSearchCondition->getChild(1),search_condition)) || + (SQL_ISRULE(pSearchCondition->getChild(1),boolean_term) && SQL_ISRULE(pSearchCondition->getParent(),boolean_term)) || + (SQL_ISRULE(pSearchCondition->getChild(1),search_condition) && SQL_ISRULE(pSearchCondition->getParent(),search_condition))) + { + OSQLParseNode* pNode = pSearchCondition->removeAt(1); + replaceAndReset(pSearchCondition,pNode); + } + } + + // or with two and trees where one element of the and trees are equal + if(!(SQL_ISRULE(pSearchCondition,search_condition) && SQL_ISRULE(pSearchCondition->getChild(0),boolean_term) && SQL_ISRULE(pSearchCondition->getChild(2),boolean_term))) + return; + + if(*pSearchCondition->getChild(0)->getChild(0) == *pSearchCondition->getChild(2)->getChild(0)) + { + OSQLParseNode* pLeft = pSearchCondition->getChild(0)->removeAt(2); + OSQLParseNode* pRight = pSearchCondition->getChild(2)->removeAt(2); + OSQLParseNode* pNode = MakeORNode(pLeft,pRight); + + OSQLParseNode* pNewRule = new OSQLParseNode(OUString(),SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::boolean_primary)); + pNewRule->append(new OSQLParseNode("(",SQLNodeType::Punctuation)); + pNewRule->append(pNode); + pNewRule->append(new OSQLParseNode(")",SQLNodeType::Punctuation)); + + OSQLParseNode::eraseBraces(pLeft); + OSQLParseNode::eraseBraces(pRight); + + pNode = MakeANDNode(pSearchCondition->getChild(0)->removeAt(sal_uInt32(0)),pNewRule); + replaceAndReset(pSearchCondition,pNode); + } + else if(*pSearchCondition->getChild(0)->getChild(2) == *pSearchCondition->getChild(2)->getChild(0)) + { + OSQLParseNode* pLeft = pSearchCondition->getChild(0)->removeAt(sal_uInt32(0)); + OSQLParseNode* pRight = pSearchCondition->getChild(2)->removeAt(2); + OSQLParseNode* pNode = MakeORNode(pLeft,pRight); + + OSQLParseNode* pNewRule = new OSQLParseNode(OUString(),SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::boolean_primary)); + pNewRule->append(new OSQLParseNode("(",SQLNodeType::Punctuation)); + pNewRule->append(pNode); + pNewRule->append(new OSQLParseNode(")",SQLNodeType::Punctuation)); + + OSQLParseNode::eraseBraces(pLeft); + OSQLParseNode::eraseBraces(pRight); + + pNode = MakeANDNode(pSearchCondition->getChild(0)->removeAt(1),pNewRule); + replaceAndReset(pSearchCondition,pNode); + } + else if(*pSearchCondition->getChild(0)->getChild(0) == *pSearchCondition->getChild(2)->getChild(2)) + { + OSQLParseNode* pLeft = pSearchCondition->getChild(0)->removeAt(2); + OSQLParseNode* pRight = pSearchCondition->getChild(2)->removeAt(sal_uInt32(0)); + OSQLParseNode* pNode = MakeORNode(pLeft,pRight); + + OSQLParseNode* pNewRule = new OSQLParseNode(OUString(),SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::boolean_primary)); + pNewRule->append(new OSQLParseNode("(",SQLNodeType::Punctuation)); + pNewRule->append(pNode); + pNewRule->append(new OSQLParseNode(")",SQLNodeType::Punctuation)); + + OSQLParseNode::eraseBraces(pLeft); + OSQLParseNode::eraseBraces(pRight); + + pNode = MakeANDNode(pSearchCondition->getChild(0)->removeAt(sal_uInt32(0)),pNewRule); + replaceAndReset(pSearchCondition,pNode); + } + else if(*pSearchCondition->getChild(0)->getChild(2) == *pSearchCondition->getChild(2)->getChild(2)) + { + OSQLParseNode* pLeft = pSearchCondition->getChild(0)->removeAt(sal_uInt32(0)); + OSQLParseNode* pRight = pSearchCondition->getChild(2)->removeAt(sal_uInt32(0)); + OSQLParseNode* pNode = MakeORNode(pLeft,pRight); + + OSQLParseNode* pNewRule = new OSQLParseNode(OUString(),SQLNodeType::Rule,OSQLParser::RuleID(OSQLParseNode::boolean_primary)); + pNewRule->append(new OSQLParseNode("(",SQLNodeType::Punctuation)); + pNewRule->append(pNode); + pNewRule->append(new OSQLParseNode(")",SQLNodeType::Punctuation)); + + OSQLParseNode::eraseBraces(pLeft); + OSQLParseNode::eraseBraces(pRight); + + pNode = MakeANDNode(pSearchCondition->getChild(0)->removeAt(1),pNewRule); + replaceAndReset(pSearchCondition,pNode); + } +} +#if OSL_DEBUG_LEVEL > 1 + +void OSQLParseNode::showParseTree( OUString& rString ) const +{ + OUStringBuffer aBuf; + showParseTree( aBuf, 0 ); + rString = aBuf.makeStringAndClear(); +} + + +void OSQLParseNode::showParseTree( OUStringBuffer& _inout_rBuffer, sal_uInt32 nLevel ) const +{ + for ( sal_uInt32 j=0; j<nLevel; ++j) + _inout_rBuffer.appendAscii( " " ); + + if ( !isToken() ) + { + // Rule name as rule + _inout_rBuffer.appendAscii( "RULE_ID: " ); + _inout_rBuffer.append( (sal_Int32)getRuleID() ); + _inout_rBuffer.append( '(' ); + _inout_rBuffer.append( OSQLParser::RuleIDToStr( getRuleID() ) ); + _inout_rBuffer.append( ')' ); + _inout_rBuffer.append( '\n' ); + + // Get the first sub tree + for (auto const& child : m_aChildren) + child->showParseTree( _inout_rBuffer, nLevel+1 ); + } + else + { + // Found a token + switch (m_eNodeType) + { + + case SQLNodeType::Keyword: + _inout_rBuffer.appendAscii( "SQL_KEYWORD: " ); + _inout_rBuffer.append( OStringToOUString( OSQLParser::TokenIDToStr( getTokenID() ), RTL_TEXTENCODING_UTF8 ) ); + _inout_rBuffer.append( '\n' ); + break; + + case SQLNodeType::Name: + _inout_rBuffer.appendAscii( "SQL_NAME: " ); + _inout_rBuffer.append( '"' ); + _inout_rBuffer.append( m_aNodeValue ); + _inout_rBuffer.append( '"' ); + _inout_rBuffer.append( '\n' ); + break; + + case SQLNodeType::String: + _inout_rBuffer.appendAscii( "SQL_STRING: " ); + _inout_rBuffer.append( '\'' ); + _inout_rBuffer.append( m_aNodeValue ); + _inout_rBuffer.append( '\'' ); + _inout_rBuffer.append( '\n' ); + break; + + case SQLNodeType::IntNum: + _inout_rBuffer.appendAscii( "SQL_INTNUM: " ); + _inout_rBuffer.append( m_aNodeValue ); + _inout_rBuffer.append( '\n' ); + break; + + case SQLNodeType::ApproxNum: + _inout_rBuffer.appendAscii( "SQL_APPROXNUM: " ); + _inout_rBuffer.append( m_aNodeValue ); + _inout_rBuffer.append( '\n' ); + break; + + case SQLNodeType::Punctuation: + _inout_rBuffer.appendAscii( "SQL_PUNCTUATION: " ); + _inout_rBuffer.append( m_aNodeValue ); + _inout_rBuffer.append( '\n' ); + break; + + case SQLNodeType::Equal: + case SQLNodeType::Less: + case SQLNodeType::Great: + case SQLNodeType::LessEq: + case SQLNodeType::GreatEq: + case SQLNodeType::NotEqual: + _inout_rBuffer.append( m_aNodeValue ); + _inout_rBuffer.append( '\n' ); + break; + + case SQLNodeType::AccessDate: + _inout_rBuffer.appendAscii( "SQL_ACCESS_DATE: " ); + _inout_rBuffer.append( m_aNodeValue ); + _inout_rBuffer.append( '\n' ); + break; + + case SQLNodeType::Concat: + _inout_rBuffer.appendAscii( "||" ); + _inout_rBuffer.append( '\n' ); + break; + + default: + SAL_INFO( "connectivity.parse", "-- " << int( m_eNodeType ) ); + SAL_WARN( "connectivity.parse", "OSQLParser::ShowParseTree: unzulaessiger NodeType" ); + } + } +} +#endif // OSL_DEBUG_LEVEL > 0 + +// Insert methods + +void OSQLParseNode::insert(sal_uInt32 nPos, OSQLParseNode* pNewSubTree) +{ + OSL_ENSURE(pNewSubTree != nullptr, "OSQLParseNode: invalid NewSubTree"); + OSL_ENSURE(pNewSubTree->getParent() == nullptr, "OSQLParseNode: Node is not an orphan"); + + // Create connection to getParent + pNewSubTree->setParent( this ); + m_aChildren.emplace(m_aChildren.begin() + nPos, pNewSubTree); +} + +// removeAt methods + +OSQLParseNode* OSQLParseNode::removeAt(sal_uInt32 nPos) +{ + OSL_ENSURE(nPos < m_aChildren.size(),"Illegal position for removeAt"); + auto aPos(m_aChildren.begin() + nPos); + auto pNode = std::move(*aPos); + + // Set the getParent of the removed node to NULL + pNode->setParent( nullptr ); + + m_aChildren.erase(aPos); + return pNode.release(); +} + +// Replace methods + +OSQLParseNode* OSQLParseNode::replace(OSQLParseNode* pOldSubNode, OSQLParseNode* pNewSubNode ) +{ + OSL_ENSURE(pOldSubNode != nullptr && pNewSubNode != nullptr, "OSQLParseNode: invalid nodes"); + OSL_ENSURE(pNewSubNode->getParent() == nullptr, "OSQLParseNode: node already has getParent"); + OSL_ENSURE(std::any_of(m_aChildren.begin(), m_aChildren.end(), + [&] (std::unique_ptr<OSQLParseNode> const & r) { return r.get() == pOldSubNode; }), + "OSQLParseNode::Replace() Node not element of parent"); + OSL_ENSURE(std::none_of(m_aChildren.begin(), m_aChildren.end(), + [&] (std::unique_ptr<OSQLParseNode> const & r) { return r.get() == pNewSubNode; }), + "OSQLParseNode::Replace() Node already element of parent"); + + pOldSubNode->setParent( nullptr ); + pNewSubNode->setParent( this ); + auto it = std::find_if(m_aChildren.begin(), m_aChildren.end(), + [&pOldSubNode](const std::unique_ptr<OSQLParseNode>& rxChild) { return rxChild.get() == pOldSubNode; }); + if (it != m_aChildren.end()) + { + it->release(); + it->reset(pNewSubNode); + } + return pOldSubNode; +} + +void OSQLParseNode::parseLeaf(OUStringBuffer& rString, const SQLParseNodeParameter& rParam) const +{ + // Found a leaf + // Append content to the output string + switch (m_eNodeType) + { + case SQLNodeType::Keyword: + { + if (!rString.isEmpty()) + rString.append(" "); + + const OString sT = OSQLParser::TokenIDToStr(m_nNodeID, rParam.bInternational ? &rParam.m_rContext : nullptr); + rString.append(OStringToOUString(sT,RTL_TEXTENCODING_UTF8)); + } break; + case SQLNodeType::String: + if (!rString.isEmpty()) + rString.append(" "); + rString.append(SetQuotation(m_aNodeValue, "\'", u"\'\'")); + break; + case SQLNodeType::Name: + if (!rString.isEmpty()) + { + switch(rString[rString.getLength()-1]) + { + case ' ' : + case '.' : break; + default : + if ( rParam.aMetaData.getCatalogSeparator().isEmpty() + || rString[rString.getLength() - 1] != rParam.aMetaData.getCatalogSeparator().toChar() + ) + rString.append(" "); + break; + } + } + if (rParam.bQuote) + { + if (rParam.bPredicate) + { + rString.append("["); + rString.append(m_aNodeValue); + rString.append("]"); + } + else + rString.append(SetQuotation(m_aNodeValue, + rParam.aMetaData.getIdentifierQuoteString(), rParam.aMetaData.getIdentifierQuoteString() )); + } + else + rString.append(m_aNodeValue); + break; + case SQLNodeType::AccessDate: + if (!rString.isEmpty()) + rString.append(" "); + rString.append("#"); + rString.append(m_aNodeValue); + rString.append("#"); + break; + + case SQLNodeType::IntNum: + case SQLNodeType::ApproxNum: + { + OUString aTmp = m_aNodeValue; + static constexpr OUStringLiteral strPoint(u"."); + if (rParam.bInternational && rParam.bPredicate && rParam.sDecSep != strPoint) + aTmp = aTmp.replaceAll(strPoint, rParam.sDecSep); + + if (!rString.isEmpty()) + rString.append(" "); + rString.append(aTmp); + + } break; + case SQLNodeType::Punctuation: + if ( getParent() && SQL_ISRULE(getParent(),cast_spec) && m_aNodeValue.toChar() == '(' ) // no spaces in front of '(' + { + rString.append(m_aNodeValue); + break; + } + [[fallthrough]]; + default: + if (!rString.isEmpty() && m_aNodeValue.toChar() != '.' && m_aNodeValue.toChar() != ':' ) + { + switch( rString[rString.getLength() - 1] ) + { + case ' ' : + case '.' : break; + default : + if ( rParam.aMetaData.getCatalogSeparator().isEmpty() + || rString[rString.getLength() - 1] != rParam.aMetaData.getCatalogSeparator().toChar() + ) + rString.append(" "); + break; + } + } + rString.append(m_aNodeValue); + } +} + + +sal_Int32 OSQLParser::getFunctionReturnType(std::u16string_view _sFunctionName, const IParseContext* pContext) +{ + sal_Int32 nType = DataType::VARCHAR; + OString sFunctionName(OUStringToOString(_sFunctionName,RTL_TEXTENCODING_UTF8)); + + if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_ASCII,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_BIT_LENGTH,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_CHAR,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_CHAR_LENGTH,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_CONCAT,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_DIFFERENCE,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_INSERT,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_LCASE,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_LEFT,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_LENGTH,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_LOCATE,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_LOCATE_2,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_LTRIM,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_OCTET_LENGTH,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_POSITION,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_REPEAT,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_REPLACE,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_RIGHT,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_RTRIM,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_SOUNDEX,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_SPACE,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_SUBSTRING,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_UCASE,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_CURRENT_DATE,pContext))) nType = DataType::DATE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_CURRENT_TIME,pContext))) nType = DataType::TIME; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_CURRENT_TIMESTAMP,pContext))) nType = DataType::TIMESTAMP; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_CURDATE,pContext))) nType = DataType::DATE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_DATEDIFF,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_DATEVALUE,pContext))) nType = DataType::DATE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_CURTIME,pContext))) nType = DataType::TIME; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_DAYNAME,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_DAYOFMONTH,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_DAYOFWEEK,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_DAYOFYEAR,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_EXTRACT,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_HOUR,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_MINUTE,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_MONTH,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_MONTHNAME,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_NOW,pContext))) nType = DataType::TIMESTAMP; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_QUARTER,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_SECOND,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_TIMESTAMPADD,pContext))) nType = DataType::TIMESTAMP; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_TIMESTAMPDIFF,pContext))) nType = DataType::TIMESTAMP; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_TIMEVALUE,pContext))) nType = DataType::TIMESTAMP; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_WEEK,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_YEAR,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_ABS,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_ACOS,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_ASIN,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_ATAN,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_ATAN2,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_CEILING,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_COS,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_COT,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_DEGREES,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_EXP,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_FLOOR,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_LOGF,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_LOG,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_LOG10,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_LN,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_MOD,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_PI,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_POWER,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_RADIANS,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_RAND,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_ROUND,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_ROUNDMAGIC,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_SIGN,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_SIN,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_SQRT,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_TAN,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_TRUNCATE,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_COUNT,pContext))) nType = DataType::INTEGER; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_MAX,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_MIN,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_AVG,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_SUM,pContext))) nType = DataType::DOUBLE; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_LOWER,pContext))) nType = DataType::VARCHAR; + else if(sFunctionName.equalsIgnoreAsciiCase(TokenIDToStr(SQL_TOKEN_UPPER,pContext))) nType = DataType::VARCHAR; + + return nType; +} + +sal_Int32 OSQLParser::getFunctionParameterType(sal_uInt32 _nTokenId, sal_uInt32 _nPos) +{ + sal_Int32 nType = DataType::VARCHAR; + + if(_nTokenId == SQL_TOKEN_CHAR) nType = DataType::INTEGER; + else if(_nTokenId == SQL_TOKEN_INSERT) + { + if ( _nPos == 2 || _nPos == 3 ) + nType = DataType::INTEGER; + } + else if(_nTokenId == SQL_TOKEN_LEFT) + { + if ( _nPos == 2 ) + nType = DataType::INTEGER; + } + else if(_nTokenId == SQL_TOKEN_LOCATE) + { + if ( _nPos == 3 ) + nType = DataType::INTEGER; + } + else if(_nTokenId == SQL_TOKEN_LOCATE_2) + { + if ( _nPos == 3 ) + nType = DataType::INTEGER; + } + else if( _nTokenId == SQL_TOKEN_REPEAT || _nTokenId == SQL_TOKEN_RIGHT ) + { + if ( _nPos == 2 ) + nType = DataType::INTEGER; + } + else if(_nTokenId == SQL_TOKEN_SPACE ) + { + nType = DataType::INTEGER; + } + else if(_nTokenId == SQL_TOKEN_SUBSTRING) + { + if ( _nPos != 1 ) + nType = DataType::INTEGER; + } + else if(_nTokenId == SQL_TOKEN_DATEDIFF) + { + if ( _nPos != 1 ) + nType = DataType::TIMESTAMP; + } + else if(_nTokenId == SQL_TOKEN_DATEVALUE) + nType = DataType::DATE; + else if(_nTokenId == SQL_TOKEN_DAYNAME) + nType = DataType::DATE; + else if(_nTokenId == SQL_TOKEN_DAYOFMONTH) + nType = DataType::DATE; + else if(_nTokenId == SQL_TOKEN_DAYOFWEEK) + nType = DataType::DATE; + else if(_nTokenId == SQL_TOKEN_DAYOFYEAR) + nType = DataType::DATE; + else if(_nTokenId == SQL_TOKEN_EXTRACT) nType = DataType::VARCHAR; + else if(_nTokenId == SQL_TOKEN_HOUR) nType = DataType::TIME; + else if(_nTokenId == SQL_TOKEN_MINUTE) nType = DataType::TIME; + else if(_nTokenId == SQL_TOKEN_MONTH) nType = DataType::DATE; + else if(_nTokenId == SQL_TOKEN_MONTHNAME) nType = DataType::DATE; + else if(_nTokenId == SQL_TOKEN_NOW) nType = DataType::TIMESTAMP; + else if(_nTokenId == SQL_TOKEN_QUARTER) nType = DataType::DATE; + else if(_nTokenId == SQL_TOKEN_SECOND) nType = DataType::TIME; + else if(_nTokenId == SQL_TOKEN_TIMESTAMPADD) nType = DataType::TIMESTAMP; + else if(_nTokenId == SQL_TOKEN_TIMESTAMPDIFF) nType = DataType::TIMESTAMP; + else if(_nTokenId == SQL_TOKEN_TIMEVALUE) nType = DataType::TIMESTAMP; + else if(_nTokenId == SQL_TOKEN_WEEK) nType = DataType::DATE; + else if(_nTokenId == SQL_TOKEN_YEAR) nType = DataType::DATE; + + else if(_nTokenId == SQL_TOKEN_ABS) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_ACOS) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_ASIN) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_ATAN) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_ATAN2) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_CEILING) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_COS) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_COT) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_DEGREES) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_EXP) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_FLOOR) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_LOGF) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_LOG) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_LOG10) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_LN) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_MOD) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_PI) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_POWER) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_RADIANS) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_RAND) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_ROUND) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_ROUNDMAGIC) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_SIGN) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_SIN) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_SQRT) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_TAN) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_TRUNCATE) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_COUNT) nType = DataType::INTEGER; + else if(_nTokenId == SQL_TOKEN_MAX) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_MIN) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_AVG) nType = DataType::DOUBLE; + else if(_nTokenId == SQL_TOKEN_SUM) nType = DataType::DOUBLE; + + else if(_nTokenId == SQL_TOKEN_LOWER) nType = DataType::VARCHAR; + else if(_nTokenId == SQL_TOKEN_UPPER) nType = DataType::VARCHAR; + + return nType; +} + + +const SQLError& OSQLParser::getErrorHelper() const +{ + return m_pData->aErrors; +} + + +OSQLParseNode::Rule OSQLParseNode::getKnownRuleID() const +{ + if ( !isRule() ) + return UNKNOWN_RULE; + return OSQLParser::RuleIDToRule( getRuleID() ); +} + +OUString OSQLParseNode::getTableRange(const OSQLParseNode* _pTableRef) +{ + OSL_ENSURE(_pTableRef && _pTableRef->count() > 1 && _pTableRef->getKnownRuleID() == OSQLParseNode::table_ref,"Invalid node give, only table ref is allowed!"); + const sal_uInt32 nCount = _pTableRef->count(); + OUString sTableRange; + if ( nCount == 2 || (nCount == 3 && !_pTableRef->getChild(0)->isToken()) ) + { + const OSQLParseNode* pNode = _pTableRef->getChild(nCount - (nCount == 2 ? 1 : 2)); + OSL_ENSURE(pNode && (pNode->getKnownRuleID() == OSQLParseNode::table_primary_as_range_column + || pNode->getKnownRuleID() == OSQLParseNode::range_variable) + ,"SQL grammar changed!"); + if ( !pNode->isLeaf() ) + sTableRange = pNode->getChild(1)->getTokenValue(); + } // if ( nCount == 2 || nCount == 3 ) + + return sTableRange; +} + +OSQLParseNodesContainer::OSQLParseNodesContainer() +{ +} + +OSQLParseNodesContainer::~OSQLParseNodesContainer() +{ +} + +void OSQLParseNodesContainer::push_back(OSQLParseNode* _pNode) +{ + ::osl::MutexGuard aGuard(m_aMutex); + m_aNodes.push_back(_pNode); +} + +void OSQLParseNodesContainer::erase(OSQLParseNode* _pNode) +{ + ::osl::MutexGuard aGuard(m_aMutex); + if ( !m_aNodes.empty() ) + { + std::vector< OSQLParseNode* >::iterator aFind = std::find(m_aNodes.begin(), m_aNodes.end(),_pNode); + if ( aFind != m_aNodes.end() ) + m_aNodes.erase(aFind); + } +} + +void OSQLParseNodesContainer::clear() +{ + ::osl::MutexGuard aGuard(m_aMutex); + m_aNodes.clear(); +} + +void OSQLParseNodesContainer::clearAndDelete() +{ + ::osl::MutexGuard aGuard(m_aMutex); + // clear the garbage collector + while ( !m_aNodes.empty() ) + { + OSQLParseNode* pNode = m_aNodes[0]; + while ( pNode->getParent() ) + { + pNode = pNode->getParent(); + } + delete pNode; + } +} +} // namespace connectivity + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |