1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_connectivity.hxx"
30 
31 #include <stdio.h>
32 #include <osl/diagnose.h>
33 #include <osl/thread.h>
34 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
35 #include <com/sun/star/sdbc/ResultSetType.hpp>
36 #include <com/sun/star/sdbc/FetchDirection.hpp>
37 #include <com/sun/star/lang/DisposedException.hpp>
38 #include <cppuhelper/typeprovider.hxx>
39 #include "propertyids.hxx"
40 #include "NStatement.hxx"
41 #include "NConnection.hxx"
42 #include "NDatabaseMetaData.hxx"
43 #include "NResultSet.hxx"
44 #include "NDebug.hxx"
45 #include "resource/evoab2_res.hrc"
46 #include <resource/common_res.hrc>
47 #include <connectivity/dbexception.hxx>
48 #include <tools/diagnose_ex.h>
49 
50 namespace connectivity { namespace evoab {
51 
52 //------------------------------------------------------------------------------
53 using namespace com::sun::star::uno;
54 using namespace com::sun::star::lang;
55 using namespace com::sun::star::beans;
56 using namespace com::sun::star::sdbc;
57 using namespace com::sun::star::sdbcx;
58 using namespace com::sun::star::container;
59 using namespace com::sun::star::io;
60 using namespace com::sun::star::util;
61 //------------------------------------------------------------------------------
62 OCommonStatement::OCommonStatement(OEvoabConnection* _pConnection)
63 	: OCommonStatement_IBase(m_aMutex)
64 	, ::comphelper::OPropertyContainer(OCommonStatement_IBase::rBHelper)
65     , OStatement_CBase( (::cppu::OWeakObject*)_pConnection, this )
66 	, m_xResultSet(NULL)
67 	, m_pResultSet(NULL)
68 	, m_pConnection(_pConnection)
69 	, m_aParser(_pConnection->getDriver().getMSFactory())
70 	, m_aSQLIterator( _pConnection, _pConnection->createCatalog()->getTables(), m_aParser, NULL )
71 	, m_pParseTree(NULL)
72     , m_nMaxFieldSize(0)
73     , m_nMaxRows(0)
74     , m_nQueryTimeOut(0)
75     , m_nFetchSize(0)
76     , m_nResultSetType(ResultSetType::FORWARD_ONLY)
77     , m_nFetchDirection(FetchDirection::FORWARD)
78     , m_nResultSetConcurrency(ResultSetConcurrency::UPDATABLE)
79     , m_bEscapeProcessing(sal_True)
80 	, rBHelper(OCommonStatement_IBase::rBHelper)
81 {
82 	m_pConnection->acquire();
83 
84 #define REGISTER_PROP( id, member ) \
85     registerProperty( \
86         OMetaConnection::getPropMap().getNameByIndex( id ), \
87         id, \
88         0, \
89         &member, \
90         ::getCppuType( &member ) \
91     );
92 
93     REGISTER_PROP( PROPERTY_ID_CURSORNAME, m_aCursorName );
94     REGISTER_PROP( PROPERTY_ID_MAXFIELDSIZE, m_nMaxFieldSize );
95     REGISTER_PROP( PROPERTY_ID_MAXROWS, m_nMaxRows );
96     REGISTER_PROP( PROPERTY_ID_QUERYTIMEOUT, m_nQueryTimeOut );
97     REGISTER_PROP( PROPERTY_ID_FETCHSIZE, m_nFetchSize );
98     REGISTER_PROP( PROPERTY_ID_RESULTSETTYPE, m_nResultSetType );
99     REGISTER_PROP( PROPERTY_ID_FETCHDIRECTION, m_nFetchDirection );
100     REGISTER_PROP( PROPERTY_ID_ESCAPEPROCESSING, m_bEscapeProcessing );
101     REGISTER_PROP( PROPERTY_ID_RESULTSETCONCURRENCY, m_nResultSetConcurrency );
102 }
103 // -----------------------------------------------------------------------------
104 OCommonStatement::~OCommonStatement()
105 {
106 }
107 //------------------------------------------------------------------------------
108 void OCommonStatement::disposeResultSet()
109 {
110 	// free the cursor if alive
111 	Reference< XComponent > xComp(m_xResultSet.get(), UNO_QUERY);
112 	if (xComp.is())
113 		xComp->dispose();
114 	m_xResultSet = Reference< XResultSet>();
115 }
116 //------------------------------------------------------------------------------
117 void OCommonStatement::disposing()
118 {
119 	::osl::MutexGuard aGuard(m_aMutex);
120 
121 	disposeResultSet();
122 
123 	if (m_pConnection)
124 		m_pConnection->release();
125 	m_pConnection = NULL;
126 
127 	dispose_ChildImpl();
128 	OCommonStatement_IBase::disposing();
129 }
130 //-----------------------------------------------------------------------------
131 Any SAL_CALL OCommonStatement::queryInterface( const Type & rType ) throw(RuntimeException)
132 {
133 	Any aRet = OCommonStatement_IBase::queryInterface(rType);
134 	if(!aRet.hasValue())
135 		aRet = ::comphelper::OPropertyContainer::queryInterface(rType);
136 	return aRet;
137 }
138 // -------------------------------------------------------------------------
139 Sequence< Type > SAL_CALL OCommonStatement::getTypes(  ) throw(RuntimeException)
140 {
141 	::cppu::OTypeCollection aTypes(	::getCppuType( (const Reference< XMultiPropertySet > *)0 ),
142 									::getCppuType( (const Reference< XFastPropertySet > *)0 ),
143 									::getCppuType( (const Reference< XPropertySet > *)0 ));
144 
145 	return ::comphelper::concatSequences(aTypes.getTypes(),OCommonStatement_IBase::getTypes());
146 }
147 // -------------------------------------------------------------------------
148 
149 //void SAL_CALL OCommonStatement::cancel(  ) throw(RuntimeException)
150 //{
151 //::osl::MutexGuard aGuard( m_aMutex );
152 //checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed);
153 //// cancel the current sql statement
154 //}
155 
156 // -------------------------------------------------------------------------
157 void SAL_CALL OCommonStatement::close(  ) throw(SQLException, RuntimeException)
158 {
159 	{
160 		::osl::MutexGuard aGuard( m_aMutex );
161 		checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed);
162 
163 	}
164 	dispose();
165 }
166 // -------------------------------------------------------------------------
167 
168 void OCommonStatement::reset() throw (SQLException)
169 {
170 	::osl::MutexGuard aGuard( m_aMutex );
171 	checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed);
172 
173 
174 	clearWarnings ();
175 
176 	if (m_xResultSet.get().is())
177 		clearMyResultSet();
178 }
179 
180 void OCommonStatement::clearMyResultSet () throw (SQLException)
181 {
182 	::osl::MutexGuard aGuard( m_aMutex );
183 	checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed);
184 
185     try
186     {
187 	    Reference<XCloseable> xCloseable;
188 	    if ( ::comphelper::query_interface( m_xResultSet.get(), xCloseable ) )
189 		    xCloseable->close();
190     }
191     catch( const DisposedException& ) { }
192 
193     m_xResultSet = Reference< XResultSet >();
194 }
195 
196 EBookQuery *
197 OCommonStatement::createTrue()
198 { // Not the world's most efficient unconditional true but ...
199 	return e_book_query_from_string("(exists \"full_name\")");
200 }
201 
202 EBookQuery *
203 OCommonStatement::createTest( const ::rtl::OUString &aColumnName,
204 							 EBookQueryTest eTest,
205 							 const ::rtl::OUString &aMatch )
206 {
207     ::rtl::OString sMatch = rtl::OUStringToOString( aMatch, RTL_TEXTENCODING_UTF8 );
208     ::rtl::OString sColumnName = rtl::OUStringToOString( aColumnName, RTL_TEXTENCODING_UTF8 );
209 
210 	return e_book_query_field_test( e_contact_field_id( sColumnName ),
211 									eTest, sMatch );
212 }
213 
214 // -------------------------------------------------------------------------
215 
216 ::rtl::OUString OCommonStatement::impl_getColumnRefColumnName_throw( const OSQLParseNode& _rColumnRef )
217 {
218     ENSURE_OR_THROW( SQL_ISRULE( &_rColumnRef, column_ref ), "internal error: only column_refs supported as LHS" );
219 
220     ::rtl::OUString sColumnName;
221     switch ( _rColumnRef.count() )
222     {
223     case 3: // SQL_TOKEN_NAME '.' column_val
224     {
225         const OSQLParseNode* pPunct = _rColumnRef.getChild( 1 );
226         const OSQLParseNode* pColVal = _rColumnRef.getChild( 2 );
227         if  (   SQL_ISPUNCTUATION( pPunct, "." )
228             &&  ( pColVal->count() == 1 )
229             )
230         {
231             sColumnName = pColVal->getChild( 0 )->getTokenValue();
232         }
233     }
234     break;
235 
236     case 1: // column
237     {
238         sColumnName = _rColumnRef.getChild( 0 )->getTokenValue();
239     }
240     break;
241     }
242 
243     if ( !sColumnName.getLength() )
244         m_pConnection->throwGenericSQLException( STR_QUERY_TOO_COMPLEX, *this );
245 
246     return sColumnName;
247 }
248 
249 // -------------------------------------------------------------------------
250 void OCommonStatement::orderByAnalysis( const OSQLParseNode* _pOrderByClause, SortDescriptor& _out_rSort )
251 {
252     ENSURE_OR_THROW( _pOrderByClause, "NULL node" );
253     ENSURE_OR_THROW( SQL_ISRULE( _pOrderByClause, opt_order_by_clause ), "wrong node type" );
254 
255     _out_rSort.clear();
256 
257     const OSQLParseNode* pOrderList = _pOrderByClause->getByRule( OSQLParseNode::ordering_spec_commalist );
258     ENSURE_OR_THROW( pOrderList, "unexpected parse tree structure" );
259 
260     for ( sal_uInt32 i=0; i<pOrderList->count(); ++i )
261     {
262         const OSQLParseNode* pOrderBy = pOrderList->getChild(i);
263         if ( !pOrderBy || !SQL_ISRULE( pOrderBy, ordering_spec ) )
264             continue;
265         const OSQLParseNode* pColumnRef = pOrderBy->count() == 2 ? pOrderBy->getChild(0) : NULL;
266         const OSQLParseNode* pAscDesc = pOrderBy->count() == 2 ? pOrderBy->getChild(1) : NULL;
267         ENSURE_OR_THROW(
268                 ( pColumnRef != NULL )
269             &&  ( pAscDesc != NULL )
270             &&  SQL_ISRULE( pAscDesc, opt_asc_desc )
271             &&  ( pAscDesc->count() < 2 ),
272             "ordering_spec structure error" );
273 
274         // column name -> column field
275         if ( !SQL_ISRULE( pColumnRef, column_ref ) )
276             m_pConnection->throwGenericSQLException( STR_SORT_BY_COL_ONLY, *this );
277         const ::rtl::OUString sColumnName( impl_getColumnRefColumnName_throw( *pColumnRef ) );
278         guint nField = evoab::findEvoabField( sColumnName );
279         // ascending/descending?
280         bool bAscending = true;
281         if  (   ( pAscDesc->count() == 1 )
282             &&  SQL_ISTOKEN( pAscDesc->getChild( 0 ), DESC )
283             )
284             bAscending = false;
285 
286         _out_rSort.push_back( FieldSort( nField, bAscending ) );
287     }
288 }
289 
290 // -------------------------------------------------------------------------
291 EBookQuery *OCommonStatement::whereAnalysis( const OSQLParseNode* parseTree )
292 {
293 	EBookQuery *pResult = NULL;
294 
295     ENSURE_OR_THROW( parseTree, "invalid parse tree" );
296 
297 	// Nested brackets
298 	if( parseTree->count() == 3 &&
299 		SQL_ISPUNCTUATION( parseTree->getChild( 0 ), "(" ) &&
300 		SQL_ISPUNCTUATION( parseTree->getChild( 2 ), ")" ) )
301 	{
302 		pResult = whereAnalysis( parseTree->getChild( 1 ) );
303 	}
304 
305 	// SQL AND, OR
306     else if( ( SQL_ISRULE( parseTree, search_condition ) ||
307 		  SQL_ISRULE( parseTree, boolean_term ) ) &&
308 		parseTree->count() == 3 )
309     {
310         ENSURE_OR_THROW(    SQL_ISTOKEN( parseTree->getChild( 1 ), OR )
311                         ||  SQL_ISTOKEN( parseTree->getChild( 1 ), AND ),
312                         "unexpected search_condition structure" );
313 
314         EBookQuery *pArgs[2];
315 		pArgs[0] = whereAnalysis( parseTree->getChild( 0 ) );
316 		pArgs[1] = whereAnalysis( parseTree->getChild( 2 ) );
317 
318         if( SQL_ISTOKEN( parseTree->getChild( 1 ), OR ) )
319             pResult = e_book_query_or( 2, pArgs, TRUE );
320         else
321 			pResult = e_book_query_and( 2, pArgs, TRUE );
322     }
323 	// SQL =, !=
324 	else if( SQL_ISRULE( parseTree, comparison_predicate ) )
325 	{
326         OSQLParseNode *pPrec = parseTree->getChild( 1 );
327 
328         ENSURE_OR_THROW( parseTree->count() == 3, "unexpected comparison_predicate structure" );
329 
330         OSQLParseNode* pLHS = parseTree->getChild( 0 );
331         OSQLParseNode* pRHS = parseTree->getChild( 2 );
332 
333         if  (   (   !( SQL_ISRULE( pLHS, column_ref ) )         // on the LHS, we accept a column or a constant int value
334                 &&  ( pLHS->getNodeType() != SQL_NODE_INTNUM )
335                 )
336             ||  (   ( pRHS->getNodeType() != SQL_NODE_STRING )  // on the RHS, certain literals are acceptable
337                 &&  ( pRHS->getNodeType() != SQL_NODE_INTNUM )
338                 &&  ( pRHS->getNodeType() != SQL_NODE_APPROXNUM )
339                 &&  !( SQL_ISTOKEN( pRHS, TRUE ) )
340                 &&  !( SQL_ISTOKEN( pRHS, FALSE ) )
341                 )
342             ||  (   ( pLHS->getNodeType() == SQL_NODE_INTNUM )  // an int on LHS requires an int on RHS
343                 &&  ( pRHS->getNodeType() != SQL_NODE_INTNUM )
344                 )
345             )
346         {
347             m_pConnection->throwGenericSQLException( STR_QUERY_TOO_COMPLEX, *this );
348         }
349 
350         if  (   ( pPrec->getNodeType() != SQL_NODE_EQUAL )
351             &&  ( pPrec->getNodeType() != SQL_NODE_NOTEQUAL )
352             )
353         {
354             m_pConnection->throwGenericSQLException( STR_OPERATOR_TOO_COMPLEX, *this );
355         }
356 
357         // recognize the special "0 = 1" condition
358         if  (   ( pLHS->getNodeType() == SQL_NODE_INTNUM )
359             &&  ( pRHS->getNodeType() == SQL_NODE_INTNUM )
360             &&  ( pPrec->getNodeType() == SQL_NODE_EQUAL )
361             )
362         {
363             const sal_Int32 nLHS = pLHS->getTokenValue().toInt64();
364             const sal_Int32 nRHS = pRHS->getTokenValue().toInt64();
365             return ( nLHS == nRHS ) ? createTrue() : NULL;
366         }
367 
368         ::rtl::OUString aColumnName( impl_getColumnRefColumnName_throw( *pLHS ) );
369 
370         ::rtl::OUString aMatchString;
371 		if ( pRHS->isToken() )
372 			aMatchString = pRHS->getTokenValue();
373 		else
374 			aMatchString = pRHS->getChild( 0 )->getTokenValue();
375 
376 	    pResult = createTest( aColumnName, E_BOOK_QUERY_IS, aMatchString );
377 
378         if ( pResult && ( pPrec->getNodeType() == SQL_NODE_NOTEQUAL ) )
379 		    pResult = e_book_query_not( pResult, TRUE );
380 	}
381 	// SQL like
382 	else if( SQL_ISRULE( parseTree, like_predicate ) )
383     {
384         ENSURE_OR_THROW( parseTree->count() == 2, "unexpected like_predicate structure" );
385         const OSQLParseNode* pPart2 = parseTree->getChild(1);
386 
387         if( ! SQL_ISRULE( parseTree->getChild( 0 ), column_ref) )
388             m_pConnection->throwGenericSQLException(STR_QUERY_INVALID_LIKE_COLUMN,*this);
389 
390         ::rtl::OUString aColumnName( impl_getColumnRefColumnName_throw( *parseTree->getChild( 0 ) ) );
391 
392         OSQLParseNode *pAtom      = pPart2->getChild( pPart2->count() - 2 );     // Match String
393 		bool bNotLike             = pPart2->getChild(0)->isToken();
394 
395         if( !( pAtom->getNodeType() == SQL_NODE_STRING ||
396 			   pAtom->getNodeType() == SQL_NODE_NAME ||
397 			   SQL_ISRULE( pAtom,parameter ) ||
398 			   ( pAtom->getChild( 0 ) && pAtom->getChild( 0 )->getNodeType() == SQL_NODE_NAME ) ||
399 			   ( pAtom->getChild( 0 ) && pAtom->getChild( 0 )->getNodeType() == SQL_NODE_STRING ) ) )
400         {
401             OSL_TRACE( "analyseSQL : pAtom->count() = %d\n", pAtom->count() );
402             m_pConnection->throwGenericSQLException(STR_QUERY_INVALID_LIKE_STRING,*this);
403         }
404 
405         const sal_Unicode WILDCARD = '%';
406 
407 		rtl::OUString aMatchString;
408 		aMatchString = pAtom->getTokenValue();
409 
410 		// Determine where '%' character is...
411 		if( aMatchString.equals( ::rtl::OUString::valueOf( WILDCARD ) ) )
412 		{
413 		    // String containing only a '%' and nothing else matches everything
414 			pResult = createTest( aColumnName, E_BOOK_QUERY_CONTAINS,
415 								  rtl::OUString::createFromAscii( "" ) );
416 		}
417 		else if( aMatchString.indexOf( WILDCARD ) == -1 )
418 		{   // Simple string , eg. "to match" "contains in evo"
419 			EVO_TRACE_STRING( "Plain contains '%s'", aMatchString );
420 			pResult = createTest( aColumnName, E_BOOK_QUERY_CONTAINS, aMatchString );
421 			if( pResult && bNotLike )
422 				pResult = e_book_query_not( pResult, TRUE );
423 		}
424 		else if( bNotLike )
425 		{
426 			// We currently can't handle a 'NOT LIKE' when there are '%'
427             m_pConnection->throwGenericSQLException(STR_QUERY_NOT_LIKE_TOO_COMPLEX,*this);
428 		}
429 		else if( (aMatchString.indexOf ( WILDCARD ) == aMatchString.lastIndexOf ( WILDCARD ) ) )
430 		{   // One occurance of '%'  matches...
431             if ( aMatchString.indexOf ( WILDCARD ) == 0 )
432 				pResult = createTest( aColumnName, E_BOOK_QUERY_ENDS_WITH, aMatchString.copy( 1 ) );
433             else if ( aMatchString.indexOf ( WILDCARD ) == aMatchString.getLength() - 1 )
434 				pResult = createTest( aColumnName, E_BOOK_QUERY_BEGINS_WITH, aMatchString.copy( 0, aMatchString.getLength() - 1 ) );
435 			else
436                 m_pConnection->throwGenericSQLException(STR_QUERY_LIKE_WILDCARD,*this);
437 
438 			if( pResult && bNotLike )
439 				pResult = e_book_query_not( pResult, TRUE );
440         }
441 		else if( aMatchString.getLength() >= 3 &&
442 				 aMatchString.indexOf ( WILDCARD ) == 0 &&
443 				 aMatchString.indexOf ( WILDCARD, 1) == aMatchString.getLength() - 1 ) {
444 			// one '%' at the start and another at the end
445 			pResult = createTest( aColumnName, E_BOOK_QUERY_CONTAINS, aMatchString.copy (1, aMatchString.getLength() - 2) );
446 		}
447 		else
448             m_pConnection->throwGenericSQLException(STR_QUERY_LIKE_WILDCARD_MANY,*this);
449 	}
450 
451 	return pResult;
452 }
453 
454 rtl::OUString OCommonStatement::getTableName()
455 {
456 	::rtl::OUString aTableName;
457 
458 	if( m_pParseTree && m_aSQLIterator.getStatementType() == SQL_STATEMENT_SELECT )
459 	{
460 		Any aCatalog;
461 		::rtl::OUString aSchema, aComposedName;
462 		const OSQLParseNode *pSelectStmnt = m_aSQLIterator.getParseTree();
463 		const OSQLParseNode *pAllTableNames = pSelectStmnt->getChild( 3 )->getChild( 0 )->getChild( 1 );
464 
465 		if( m_aSQLIterator.isTableNode( pAllTableNames->getChild( 0 ) ) )
466 			OSQLParseNode::getTableComponents( pAllTableNames->getChild( 0 ),
467 											   aCatalog,aSchema, aTableName,NULL );
468 
469 		else if( SQL_ISRULE( pAllTableNames->getChild( 0 ), table_ref ) )
470 		{
471 			OSQLParseNode *pNodeForTableName = pAllTableNames->getChild( 0 )->getChild( 0 );
472 			if( m_aSQLIterator.isTableNode( pNodeForTableName ) )
473 			{
474                 aTableName = OSQLParseNode::getTableRange(pAllTableNames->getChild( 0 ));
475 				if( !aTableName.getLength() )
476         			OSQLParseNode::getTableComponents( pNodeForTableName, aCatalog, aSchema, aTableName,NULL);
477 			}
478 			else
479 				OSL_ENSURE( false,  "odd table layout" );
480 		}
481 		else
482 				OSL_ENSURE( false,  "unusual table layout" );
483 	}
484 	return aTableName;
485 }
486 
487 void OCommonStatement::parseSql( const rtl::OUString& sql, QueryData& _out_rQueryData )
488 {
489     EVO_TRACE_STRING( "parsing %s", sql );
490 
491     _out_rQueryData.eFilterType = eFilterOther;
492 
493     ::rtl::OUString aErr;
494 	m_pParseTree = m_aParser.parseTree( aErr, sql );
495 	m_aSQLIterator.setParseTree( m_pParseTree );
496 	m_aSQLIterator.traverseAll();
497 
498     _out_rQueryData.sTable = getTableName();
499 
500     // to be sorted?
501 	const OSQLParseNode* pOrderByClause = m_aSQLIterator.getOrderTree();
502     if ( pOrderByClause )
503     {
504     #if OSL_DEBUG_LEVEL > 0
505         ::rtl::OUString sTreeDebug;
506         pOrderByClause->showParseTree( sTreeDebug );
507         EVO_TRACE_STRING( "found order-by tree:\n%s", sTreeDebug );
508     #endif
509         orderByAnalysis( pOrderByClause, _out_rQueryData.aSortOrder );
510     }
511 
512 	const OSQLParseNode* pWhereClause = m_aSQLIterator.getWhereTree();
513 	if ( pWhereClause && SQL_ISRULE( pWhereClause, where_clause ) )
514     {
515     #if OSL_DEBUG_LEVEL > 0
516         ::rtl::OUString sTreeDebug;
517         pWhereClause->showParseTree( sTreeDebug );
518         EVO_TRACE_STRING( "found where tree:\n%s", sTreeDebug );
519     #endif
520         EBookQuery* pQuery = whereAnalysis( pWhereClause->getChild( 1 ) );
521         if ( !pQuery )
522         {
523 		    _out_rQueryData.eFilterType = eFilterAlwaysFalse;
524             pQuery = createTrue();
525         }
526         _out_rQueryData.setQuery( pQuery );
527     }
528 	else
529 	{
530 		_out_rQueryData.eFilterType = eFilterNone;
531 		_out_rQueryData.setQuery( createTrue() );
532 	}
533 }
534 
535 // -------------------------------------------------------------------------
536 
537 Reference< XConnection > SAL_CALL OStatement::getConnection(  ) throw(SQLException, RuntimeException)
538 {
539 	::osl::MutexGuard aGuard( m_aMutex );
540 	checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed);
541 
542 	// just return our connection here
543 	return impl_getConnection();
544 }
545 
546 // -------------------------------------------------------------------------
547 Any SAL_CALL OCommonStatement::getWarnings(  ) throw(SQLException, RuntimeException)
548 {
549 	::osl::MutexGuard aGuard( m_aMutex );
550 	checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed);
551 
552 
553 	return makeAny(SQLWarning());
554 }
555 
556 // -------------------------------------------------------------------------
557 void SAL_CALL OCommonStatement::clearWarnings(  ) throw(SQLException, RuntimeException)
558 {
559 	::osl::MutexGuard aGuard( m_aMutex );
560 	checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed);
561 
562 }
563 // -------------------------------------------------------------------------
564 ::cppu::IPropertyArrayHelper* OCommonStatement::createArrayHelper( ) const
565 {
566     Sequence< Property > aProps;
567     describeProperties( aProps );
568     return new ::cppu::OPropertyArrayHelper( aProps );
569 }
570 // -------------------------------------------------------------------------
571 ::cppu::IPropertyArrayHelper & OCommonStatement::getInfoHelper()
572 {
573 	return *const_cast< OCommonStatement* >( this )->getArrayHelper();
574 }
575 
576 // -----------------------------------------------------------------------------
577 void SAL_CALL OCommonStatement::acquire() throw()
578 {
579 	OCommonStatement_IBase::acquire();
580 }
581 // -----------------------------------------------------------------------------
582 void SAL_CALL OCommonStatement::release() throw()
583 {
584     relase_ChildImpl();
585 }
586 
587 // -------------------------------------------------------------------------
588 QueryData OCommonStatement::impl_getEBookQuery_throw( const ::rtl::OUString& _rSql )
589 {
590     QueryData aData;
591 	parseSql( _rSql, aData );
592 
593 #ifdef DEBUG
594 	char *pSexpr = aData.getQuery() ? e_book_query_to_string( aData.getQuery() ) : g_strdup( "<map failed>" );
595 	g_message( "Parsed SQL to sexpr '%s'\n", pSexpr );
596 	g_free( pSexpr );
597 #endif
598 
599     if ( !aData.getQuery() )
600         m_pConnection->throwGenericSQLException( STR_QUERY_TOO_COMPLEX, *this );
601 
602     // a postcondition of this method is that we properly determined the SELECT columns
603     aData.xSelectColumns = m_aSQLIterator.getSelectColumns();
604 	if ( !aData.xSelectColumns.isValid() )
605         m_pConnection->throwGenericSQLException( STR_QUERY_TOO_COMPLEX, *this );
606 
607     return aData;
608 }
609 
610 // -------------------------------------------------------------------------
611 Reference< XResultSet > OCommonStatement::impl_executeQuery_throw( const QueryData& _rQueryData )
612 {
613     // create result set
614 	OEvoabResultSet* pResult = new OEvoabResultSet( this, m_pConnection );
615 	Reference< XResultSet > xRS = pResult;
616     pResult->construct( _rQueryData );
617 
618     // done
619     m_xResultSet = xRS;
620 	return xRS;
621 }
622 
623 // -------------------------------------------------------------------------
624 Reference< XResultSet > OCommonStatement::impl_executeQuery_throw( const ::rtl::OUString& _rSql )
625 {
626 	EVO_TRACE_STRING( "OCommonStatement::impl_executeQuery_throw(%s)\n", _rSql );
627 
628 #ifdef DEBUG
629 	g_message( "Parse SQL '%s'\n",
630 			   (const sal_Char *)OUStringToOString( _rSql, RTL_TEXTENCODING_UTF8 ) );
631 #endif
632 
633     return impl_executeQuery_throw( impl_getEBookQuery_throw( _rSql ) );
634 }
635 
636 // -----------------------------------------------------------------------------
637 Reference< XPropertySetInfo > SAL_CALL OCommonStatement::getPropertySetInfo(  ) throw(RuntimeException)
638 {
639 	return ::cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() );
640 }
641 
642 // =============================================================================
643 // = OStatement
644 // =============================================================================
645 // -----------------------------------------------------------------------------
646 IMPLEMENT_SERVICE_INFO( OStatement, "com.sun.star.comp.sdbcx.evoab.OStatement", "com.sun.star.sdbc.Statement" );
647 
648 // -----------------------------------------------------------------------------
649 IMPLEMENT_FORWARD_XINTERFACE2( OStatement, OCommonStatement, OStatement_IBase )
650 
651 // -----------------------------------------------------------------------------
652 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OStatement, OCommonStatement, OStatement_IBase )
653 
654 // -------------------------------------------------------------------------
655 sal_Bool SAL_CALL OStatement::execute( const ::rtl::OUString& _sql ) throw(SQLException, RuntimeException)
656 {
657 	::osl::MutexGuard aGuard( m_aMutex );
658 	checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed);
659 
660     Reference< XResultSet > xRS = impl_executeQuery_throw( _sql );
661 	return xRS.is();
662 }
663 
664 // -------------------------------------------------------------------------
665 Reference< XResultSet > SAL_CALL OStatement::executeQuery( const ::rtl::OUString& _sql ) throw(SQLException, RuntimeException)
666 {
667 	::osl::MutexGuard aGuard( m_aMutex );
668 	checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed);
669 
670     return impl_executeQuery_throw( _sql );
671 }
672 
673 // -----------------------------------------------------------------------------
674 sal_Int32 SAL_CALL OStatement::executeUpdate( const ::rtl::OUString& /*sql*/ ) throw(SQLException, RuntimeException)
675 {
676 	::osl::MutexGuard aGuard( m_aMutex );
677 	checkDisposed(OCommonStatement_IBase::rBHelper.bDisposed);
678     ::dbtools::throwFeatureNotImplementedException( "XStatement::executeUpdate", *this );
679 	return 0;
680 }
681 
682 } } // namespace ::connectivity::evoab
683