1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <tools/diagnose_ex.h>
22 #include <cppuhelper/queryinterface.hxx>
23 #include <comphelper/processfactory.hxx>
24 #include <connectivity/dbexception.hxx>
28 #include "MDriver.hxx"
29 #include "MStatement.hxx"
30 #include "sqlbison.hxx"
31 #include "MResultSet.hxx"
33 #include "resource/mork_res.hrc"
34 #include "resource/common_res.hrc"
36 #if OSL_DEBUG_LEVEL > 0
37 # define OUtoCStr( x ) ( OUStringToOString ( (x), RTL_TEXTENCODING_ASCII_US).getStr())
38 #else /* OSL_DEBUG_LEVEL */
39 # define OUtoCStr( x ) ("dummy")
40 #endif /* OSL_DEBUG_LEVEL */
42 static ::osl::Mutex m_ThreadMutex
;
44 using namespace ::comphelper
;
45 using namespace connectivity::mork
;
46 using namespace connectivity
;
48 using namespace com::sun::star::uno
;
49 using namespace com::sun::star::lang
;
50 using namespace com::sun::star::beans
;
51 using namespace com::sun::star::sdbc
;
52 using namespace com::sun::star::container
;
53 using namespace com::sun::star::io
;
54 using namespace com::sun::star::util
;
57 OStatement::OStatement( OConnection
* _pConnection
) : OCommonStatement( _pConnection
)
61 OCommonStatement::OCommonStatement(OConnection
* _pConnection
)
62 :OCommonStatement_IBASE(m_aMutex
)
63 ,OPropertySetHelper(OCommonStatement_IBASE::rBHelper
)
64 ,OCommonStatement_SBASE(static_cast<cppu::OWeakObject
*>(_pConnection
), this)
66 ,m_pConnection(_pConnection
)
67 ,m_aParser( comphelper::getComponentContext(_pConnection
->getDriver()->getFactory()) )
68 ,m_pSQLIterator( new OSQLParseTreeIterator( _pConnection
, _pConnection
->createCatalog()->getTables(), m_aParser
) )
70 m_xDBMetaData
= _pConnection
->getMetaData();
71 m_pParseTree
= nullptr;
75 OCommonStatement::~OCommonStatement()
80 void OCommonStatement::disposing()
82 ::osl::MutexGuard
aGuard(m_aMutex
);
85 clearCachedResultSet();
87 m_pConnection
.clear();
89 m_pSQLIterator
->dispose();
93 OCommonStatement_IBASE::disposing();
96 Any SAL_CALL
OCommonStatement::queryInterface( const Type
& rType
) throw(RuntimeException
, std::exception
)
98 Any aRet
= OCommonStatement_IBASE::queryInterface(rType
);
100 aRet
= OPropertySetHelper::queryInterface(rType
);
104 Sequence
< Type
> SAL_CALL
OCommonStatement::getTypes( ) throw(RuntimeException
, std::exception
)
106 ::cppu::OTypeCollection
aTypes( cppu::UnoType
<XMultiPropertySet
>::get(),
107 cppu::UnoType
<XFastPropertySet
>::get(),
108 cppu::UnoType
<XPropertySet
>::get());
110 return ::comphelper::concatSequences(aTypes
.getTypes(),OCommonStatement_IBASE::getTypes());
113 void SAL_CALL
OCommonStatement::close( ) throw(SQLException
, RuntimeException
, std::exception
)
116 ::osl::MutexGuard
aGuard( m_aMutex
);
117 checkDisposed(OCommonStatement_IBASE::rBHelper
.bDisposed
);
122 OCommonStatement::StatementType
OCommonStatement::parseSql( const OUString
& sql
, bool bAdjusted
)
123 throw ( SQLException
, RuntimeException
, std::exception
)
127 m_pParseTree
= m_aParser
.parseTree(aErr
,sql
);
129 #if OSL_DEBUG_LEVEL > 0
131 OSL_TRACE("ParseSQL: %s", OUtoCStr( sql
) );
133 #endif // OSL_DEBUG_LEVEL
137 m_pSQLIterator
->setParseTree(m_pParseTree
);
138 m_pSQLIterator
->traverseAll();
139 const OSQLTables
& rTabs
= m_pSQLIterator
->getTables();
143 getOwnConnection()->throwSQLException( STR_QUERY_AT_LEAST_ONE_TABLES
, *this );
146 #if OSL_DEBUG_LEVEL > 0
147 OSQLTables::const_iterator citer
;
148 for( citer
= rTabs
.begin(); citer
!= rTabs
.end(); ++citer
) {
149 OSL_TRACE("SELECT Table : %s", OUtoCStr(citer
->first
) );
153 Reference
<XIndexAccess
> xNames
;
154 switch(m_pSQLIterator
->getStatementType())
156 case OSQLStatementType::Select
:
158 // at this moment we support only one table per select statement
160 OSL_ENSURE( rTabs
.begin() != rTabs
.end(), "Need a Table");
162 m_pTable
= static_cast< OTable
* > (rTabs
.begin()->second
.get());
163 m_xColNames
= m_pTable
->getColumns();
164 xNames
.set(m_xColNames
,UNO_QUERY
);
165 // set the binding of the resultrow
166 m_aRow
= new OValueVector(xNames
->getCount());
167 (m_aRow
->get())[0].setBound(true);
168 ::std::for_each(m_aRow
->get().begin()+1,m_aRow
->get().end(),TSetBound(false));
169 // create the column mapping
170 createColumnMapping();
175 case OSQLStatementType::CreateTable
:
182 else if(!bAdjusted
) //Our sql parser does not support a statement like "create table foo"
183 // So we append ("E-mail" varchar) to the last of it to make it work
185 return parseSql(sql
+ "(""E-mail"" character)", true);
188 getOwnConnection()->throwSQLException( STR_QUERY_TOO_COMPLEX
, *this );
189 OSL_FAIL( "OCommonStatement::parseSql: unreachable!" );
194 Reference
< XResultSet
> OCommonStatement::impl_executeCurrentQuery()
196 clearCachedResultSet();
198 ::rtl::Reference
< OResultSet
> pResult( new OResultSet( this, m_pSQLIterator
) );
199 initializeResultSet( pResult
.get() );
201 pResult
->executeQuery();
202 cacheResultSet( pResult
); // only cache if we survived the execution
204 return pResult
.get();
209 void OCommonStatement::initializeResultSet( OResultSet
* _pResult
)
211 ENSURE_OR_THROW( _pResult
, "invalid result set" );
213 _pResult
->setColumnMapping(m_aColMapping
);
214 _pResult
->setOrderByColumns(m_aOrderbyColumnNumber
);
215 _pResult
->setOrderByAscending(m_aOrderbyAscending
);
216 _pResult
->setBindingRow(m_aRow
);
217 _pResult
->setTable(m_pTable
);
221 void OCommonStatement::clearCachedResultSet()
223 Reference
< XResultSet
> xResultSet( m_xResultSet
.get(), UNO_QUERY
);
224 if ( !xResultSet
.is() )
227 Reference
< XCloseable
>( xResultSet
, UNO_QUERY_THROW
)->close();
229 m_xResultSet
.clear();
233 void OCommonStatement::cacheResultSet( const ::rtl::Reference
< OResultSet
>& _pResult
)
235 ENSURE_OR_THROW( _pResult
.is(), "invalid result set" );
236 m_xResultSet
= Reference
< XResultSet
>( _pResult
.get() );
240 sal_Bool SAL_CALL
OCommonStatement::execute( const OUString
& sql
) throw(SQLException
, RuntimeException
, std::exception
)
242 ::osl::MutexGuard
aGuard( m_aMutex
);
243 checkDisposed(OCommonStatement_IBASE::rBHelper
.bDisposed
);
245 OSL_TRACE("Statement::execute( %s )", OUtoCStr( sql
) );
247 Reference
< XResultSet
> xRS
= executeQuery( sql
);
248 // returns true when a resultset is available
253 Reference
< XResultSet
> SAL_CALL
OCommonStatement::executeQuery( const OUString
& sql
) throw(SQLException
, RuntimeException
, std::exception
)
255 ::osl::MutexGuard
aGuard( m_ThreadMutex
);
256 checkDisposed(OCommonStatement_IBASE::rBHelper
.bDisposed
);
258 OSL_TRACE("Statement::executeQuery( %s )", OUtoCStr( sql
) );
260 // parse the statement
261 StatementType eStatementType
= parseSql( sql
);
262 if ( eStatementType
!= eSelect
)
265 return impl_executeCurrentQuery();
269 Reference
< XConnection
> SAL_CALL
OCommonStatement::getConnection( ) throw(SQLException
, RuntimeException
, std::exception
)
271 ::osl::MutexGuard
aGuard( m_aMutex
);
272 checkDisposed(OCommonStatement_IBASE::rBHelper
.bDisposed
);
274 // just return our connection here
275 return Reference
< XConnection
>(m_pConnection
.get());
278 Any SAL_CALL
OStatement::queryInterface( const Type
& rType
) throw(RuntimeException
, std::exception
)
280 Any aRet
= ::cppu::queryInterface(rType
,static_cast< XServiceInfo
*> (this));
282 aRet
= OCommonStatement::queryInterface(rType
);
286 sal_Int32 SAL_CALL
OCommonStatement::executeUpdate( const OUString
& /*sql*/ ) throw(SQLException
, RuntimeException
, std::exception
)
288 ::dbtools::throwFeatureNotImplementedSQLException( "XStatement::executeUpdate", *this );
293 Any SAL_CALL
OCommonStatement::getWarnings( ) throw(SQLException
, RuntimeException
, std::exception
)
295 ::osl::MutexGuard
aGuard( m_aMutex
);
296 checkDisposed(OCommonStatement_IBASE::rBHelper
.bDisposed
);
298 return makeAny(m_aLastWarning
);
302 void SAL_CALL
OCommonStatement::clearWarnings( ) throw(SQLException
, RuntimeException
, std::exception
)
304 ::osl::MutexGuard
aGuard( m_aMutex
);
305 checkDisposed(OCommonStatement_IBASE::rBHelper
.bDisposed
);
308 m_aLastWarning
= SQLWarning();
311 ::cppu::IPropertyArrayHelper
* OCommonStatement::createArrayHelper( ) const
313 // this properties are define by the service resultset
314 // they must in alphabetic order
315 Sequence
< Property
> aProps(9);
316 Property
* pProperties
= aProps
.getArray();
318 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME
),
319 PROPERTY_ID_CURSORNAME
, cppu::UnoType
<OUString
>::get(), 0);
320 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ESCAPEPROCESSING
),
321 PROPERTY_ID_ESCAPEPROCESSING
, cppu::UnoType
<bool>::get(), 0);
322 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION
),
323 PROPERTY_ID_FETCHDIRECTION
, cppu::UnoType
<sal_Int32
>::get(), 0);
324 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE
),
325 PROPERTY_ID_FETCHSIZE
, cppu::UnoType
<sal_Int32
>::get(), 0);
326 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXFIELDSIZE
),
327 PROPERTY_ID_MAXFIELDSIZE
, cppu::UnoType
<sal_Int32
>::get(), 0);
328 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXROWS
),
329 PROPERTY_ID_MAXROWS
, cppu::UnoType
<sal_Int32
>::get(), 0);
330 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_QUERYTIMEOUT
),
331 PROPERTY_ID_QUERYTIMEOUT
, cppu::UnoType
<sal_Int32
>::get(), 0);
332 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY
),
333 PROPERTY_ID_RESULTSETCONCURRENCY
, cppu::UnoType
<sal_Int32
>::get(), 0);
334 pProperties
[nPos
++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE
),
335 PROPERTY_ID_RESULTSETTYPE
, cppu::UnoType
<sal_Int32
>::get(), 0);
337 return new ::cppu::OPropertyArrayHelper(aProps
);
341 ::cppu::IPropertyArrayHelper
& OCommonStatement::getInfoHelper()
343 return *getArrayHelper();
346 sal_Bool
OCommonStatement::convertFastPropertyValue(
347 Any
& /*rConvertedValue*/,
349 sal_Int32
/*nHandle*/,
350 const Any
& /*rValue*/ )
351 throw (css::lang::IllegalArgumentException
)
353 bool bConverted
= false;
354 // here we have to try to convert
358 void OCommonStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle
,const Any
& /*rValue*/) throw (Exception
, std::exception
)
360 // set the value to whatever is necessary
363 case PROPERTY_ID_QUERYTIMEOUT
:
364 case PROPERTY_ID_MAXFIELDSIZE
:
365 case PROPERTY_ID_MAXROWS
:
366 case PROPERTY_ID_RESULTSETCONCURRENCY
:
367 case PROPERTY_ID_RESULTSETTYPE
:
368 case PROPERTY_ID_FETCHDIRECTION
:
369 case PROPERTY_ID_FETCHSIZE
:
370 case PROPERTY_ID_ESCAPEPROCESSING
:
376 void OCommonStatement::getFastPropertyValue(Any
& /*rValue*/,sal_Int32 nHandle
) const
380 case PROPERTY_ID_QUERYTIMEOUT
:
381 case PROPERTY_ID_MAXFIELDSIZE
:
382 case PROPERTY_ID_MAXROWS
:
383 case PROPERTY_ID_RESULTSETCONCURRENCY
:
384 case PROPERTY_ID_RESULTSETTYPE
:
385 case PROPERTY_ID_FETCHDIRECTION
:
386 case PROPERTY_ID_FETCHSIZE
:
387 case PROPERTY_ID_ESCAPEPROCESSING
:
393 IMPLEMENT_SERVICE_INFO(OStatement
,"com.sun.star.sdbcx.OStatement","com.sun.star.sdbc.Statement");
395 void SAL_CALL
OCommonStatement::acquire() throw()
397 OCommonStatement_IBASE::acquire();
400 void SAL_CALL
OCommonStatement::release() throw()
405 void SAL_CALL
OStatement::acquire() throw()
407 OCommonStatement::acquire();
410 void SAL_CALL
OStatement::release() throw()
412 OCommonStatement::release();
415 Reference
< css::beans::XPropertySetInfo
> SAL_CALL
OCommonStatement::getPropertySetInfo( ) throw(RuntimeException
, std::exception
)
417 return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
420 void OCommonStatement::createColumnMapping()
424 // initialize the column index map (mapping select columns to table columns)
425 ::rtl::Reference
<connectivity::OSQLColumns
> xColumns
= m_pSQLIterator
->getSelectColumns();
426 m_aColMapping
.resize(xColumns
->get().size() + 1);
427 for (i
=0; i
<m_aColMapping
.size(); ++i
)
428 m_aColMapping
[i
] = static_cast<sal_Int32
>(i
);
430 Reference
<XIndexAccess
> xNames(m_xColNames
,UNO_QUERY
);
431 // now check which columns are bound
432 #if OSL_DEBUG_LEVEL > 0
433 for ( i
= 0; i
< m_aColMapping
.size(); i
++ )
436 "BEFORE Mapped: " << i
<< " -> " << m_aColMapping
[i
]);
438 OResultSet::setBoundedColumns(m_aRow
,xColumns
,xNames
,true,m_xDBMetaData
,m_aColMapping
);
439 #if OSL_DEBUG_LEVEL > 0
440 for ( i
= 0; i
< m_aColMapping
.size(); i
++ )
443 "AFTER Mapped: " << i
<< " -> " << m_aColMapping
[i
]);
448 void OCommonStatement::analyseSQL()
450 const OSQLParseNode
* pOrderbyClause
= m_pSQLIterator
->getOrderTree();
453 OSQLParseNode
* pOrderingSpecCommalist
= pOrderbyClause
->getChild(2);
454 OSL_ENSURE(SQL_ISRULE(pOrderingSpecCommalist
,ordering_spec_commalist
),"OResultSet: Fehler im Parse Tree");
456 for (size_t m
= 0; m
< pOrderingSpecCommalist
->count(); m
++)
458 OSQLParseNode
* pOrderingSpec
= pOrderingSpecCommalist
->getChild(m
);
459 OSL_ENSURE(SQL_ISRULE(pOrderingSpec
,ordering_spec
),"OResultSet: Fehler im Parse Tree");
460 OSL_ENSURE(pOrderingSpec
->count() == 2,"OResultSet: Fehler im Parse Tree");
462 OSQLParseNode
* pColumnRef
= pOrderingSpec
->getChild(0);
463 if(!SQL_ISRULE(pColumnRef
,column_ref
))
465 throw SQLException();
467 OSQLParseNode
* pAscendingDescending
= pOrderingSpec
->getChild(1);
468 setOrderbyColumn(pColumnRef
,pAscendingDescending
);
473 void OCommonStatement::setOrderbyColumn( OSQLParseNode
* pColumnRef
,
474 OSQLParseNode
* pAscendingDescending
)
476 OUString aColumnName
;
477 if (pColumnRef
->count() == 1)
478 aColumnName
= pColumnRef
->getChild(0)->getTokenValue();
479 else if (pColumnRef
->count() == 3)
481 pColumnRef
->getChild(2)->parseNodeToStr( aColumnName
, getOwnConnection(), nullptr, false, false );
485 throw SQLException();
488 Reference
<XColumnLocate
> xColLocate(m_xColNames
,UNO_QUERY
);
492 m_aOrderbyColumnNumber
.push_back(xColLocate
->findColumn(aColumnName
));
494 // Ascending or Descending?
495 m_aOrderbyAscending
.push_back((SQL_ISTOKEN(pAscendingDescending
,DESC
)) ? TAscendingOrder::DESC
: TAscendingOrder::ASC
);
499 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */