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 .
22 #include <osl/diagnose.h>
23 #include <comphelper/property.hxx>
24 #include <comphelper/uno3.hxx>
25 #include <osl/thread.h>
26 #include <tools/diagnose_ex.h>
27 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
28 #include <com/sun/star/sdbc/ResultSetType.hpp>
29 #include <com/sun/star/sdbc/FetchDirection.hpp>
30 #include <com/sun/star/lang/DisposedException.hpp>
31 #include <comphelper/sequence.hxx>
32 #include <cppuhelper/typeprovider.hxx>
33 #include <comphelper/processfactory.hxx>
34 #include <comphelper/extract.hxx>
35 #include <comphelper/types.hxx>
36 #include <connectivity/dbexception.hxx>
37 #include <com/sun/star/container/XIndexAccess.hpp>
41 #include "diagnose_ex.h"
42 #include "MDriver.hxx"
43 #include "MStatement.hxx"
44 #include "MConnection.hxx"
45 #include "MResultSet.hxx"
46 #include "MDatabaseMetaData.hxx"
48 #include "resource/mork_res.hrc"
49 #include "resource/common_res.hrc"
51 #if OSL_DEBUG_LEVEL > 0
52 # define OUtoCStr( x ) ( OUStringToOString ( (x), RTL_TEXTENCODING_ASCII_US).getStr())
53 #else /* OSL_DEBUG_LEVEL */
54 # define OUtoCStr( x ) ("dummy")
55 #endif /* OSL_DEBUG_LEVEL */
57 static ::osl::Mutex m_ThreadMutex
;
59 using namespace ::comphelper
;
60 using namespace connectivity::mork
;
61 using namespace connectivity
;
62 //------------------------------------------------------------------------------
63 using namespace com::sun::star::uno
;
64 using namespace com::sun::star::lang
;
65 using namespace com::sun::star::beans
;
66 using namespace com::sun::star::sdbc
;
67 using namespace com::sun::star::container
;
68 using namespace com::sun::star::io
;
69 using namespace com::sun::star::util
;
70 //------------------------------------------------------------------------------
72 OStatement::OStatement( OConnection
* _pConnection
) : OCommonStatement( _pConnection
)
74 SAL_INFO("connectivity.mork", "=> OStatement::OStatement()" );
77 OCommonStatement::OCommonStatement(OConnection
* _pConnection
)
78 :OCommonStatement_IBASE(m_aMutex
)
79 ,OPropertySetHelper(OCommonStatement_IBASE::rBHelper
)
80 ,OCommonStatement_SBASE((::cppu::OWeakObject
*)_pConnection
, this)
82 ,m_pConnection(_pConnection
)
83 ,m_aParser( comphelper::getComponentContext(_pConnection
->getDriver()->getFactory()) )
84 ,m_pSQLIterator( new OSQLParseTreeIterator( _pConnection
, _pConnection
->createCatalog()->getTables(), m_aParser
, NULL
) )
85 ,rBHelper(OCommonStatement_IBASE::rBHelper
)
87 SAL_INFO("connectivity.mork", "=> OCommonStatement::OCommonStatement()" );
88 m_xDBMetaData
= _pConnection
->getMetaData();
90 m_pConnection
->acquire();
93 // -----------------------------------------------------------------------------
94 OCommonStatement::~OCommonStatement()
98 //------------------------------------------------------------------------------
99 void OCommonStatement::disposing()
101 ::osl::MutexGuard
aGuard(m_aMutex
);
104 clearCachedResultSet();
107 m_pConnection
->release();
108 m_pConnection
= NULL
;
110 m_pSQLIterator
->dispose();
113 OCommonStatement_IBASE::disposing();
115 //-----------------------------------------------------------------------------
116 Any SAL_CALL
OCommonStatement::queryInterface( const Type
& rType
) throw(RuntimeException
)
118 Any aRet
= OCommonStatement_IBASE::queryInterface(rType
);
120 aRet
= OPropertySetHelper::queryInterface(rType
);
123 // -------------------------------------------------------------------------
124 Sequence
< Type
> SAL_CALL
OCommonStatement::getTypes( ) throw(RuntimeException
)
126 ::cppu::OTypeCollection
aTypes( ::getCppuType( (const Reference
< XMultiPropertySet
> *)0 ),
127 ::getCppuType( (const Reference
< XFastPropertySet
> *)0 ),
128 ::getCppuType( (const Reference
< XPropertySet
> *)0 ));
130 return ::comphelper::concatSequences(aTypes
.getTypes(),OCommonStatement_IBASE::getTypes());
132 // -------------------------------------------------------------------------
133 void SAL_CALL
OCommonStatement::close( ) throw(SQLException
, RuntimeException
)
136 ::osl::MutexGuard
aGuard( m_aMutex
);
137 checkDisposed(OCommonStatement_IBASE::rBHelper
.bDisposed
);
143 // -------------------------------------------------------------------------
144 void OCommonStatement::createTable( ) throw ( SQLException
, RuntimeException
)
146 SAL_INFO("connectivity.mork", "=> OCommonStatement::createTable()" );
151 ::rtl::Reference<connectivity::OSQLColumns> xCreateColumn;
152 if (m_pSQLIterator->getStatementType() == SQL_STATEMENT_CREATE_TABLE)
154 const OSQLTables& xTabs = m_pSQLIterator->getTables();
155 OSL_ENSURE( !xTabs.empty(), "Need a Table");
156 OUString ouTableName=xTabs.begin()->first;
157 xCreateColumn = m_pSQLIterator->getCreateColumns();
158 OSL_ENSURE(xCreateColumn.is(), "Need the Columns!!");
160 const OColumnAlias& aColumnAlias = m_pConnection->getColumnAlias();
162 OSQLColumns::Vector::const_iterator aIter = xCreateColumn->get().begin();
163 const OUString sProprtyName = OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_NAME);
165 for (sal_Int32 i = 1; aIter != xCreateColumn->get().end();++aIter, i++)
167 (*aIter)->getPropertyValue(sProprtyName) >>= sName;
168 if ( !aColumnAlias.hasAlias( sName ) )
171 const OUString sError( getOwnConnection()->getResources().getResourceStringWithSubstitution(
172 STR_INVALID_COLUMNNAME,
173 "$columnname$", sName
175 ::dbtools::throwGenericSQLException(sError,*this);
178 MDatabaseMetaDataHelper _aDbHelper;
179 if (!_aDbHelper.NewAddressBook(m_pConnection,ouTableName))
181 getOwnConnection()->throwSQLException( _aDbHelper.getError(), *this );
183 m_pSQLIterator.reset( new ::connectivity::OSQLParseTreeIterator(
184 m_pConnection, m_pConnection->createCatalog()->getTables(), m_aParser, NULL ) );
189 getOwnConnection()->throwSQLException( STR_QUERY_TOO_COMPLEX, *this );
192 // -------------------------------------------------------------------------
193 OCommonStatement::StatementType
OCommonStatement::parseSql( const OUString
& sql
, sal_Bool bAdjusted
)
194 throw ( SQLException
, RuntimeException
)
196 SAL_INFO("connectivity.mork", "=> OCommonStatement::parseSql()" );
200 m_pParseTree
= m_aParser
.parseTree(aErr
,sql
);
202 #if OSL_DEBUG_LEVEL > 0
204 const char* str
= OUtoCStr(sql
);
206 OSL_TRACE("ParseSQL: %s", OUtoCStr( sql
) );
208 #endif // OSL_DEBUG_LEVEL
212 m_pSQLIterator
->setParseTree(m_pParseTree
);
213 m_pSQLIterator
->traverseAll();
214 const OSQLTables
& xTabs
= m_pSQLIterator
->getTables();
218 getOwnConnection()->throwSQLException( STR_QUERY_AT_LEAST_ONE_TABLES
, *this );
221 #if OSL_DEBUG_LEVEL > 0
222 OSQLTables::const_iterator citer
;
223 for( citer
= xTabs
.begin(); citer
!= xTabs
.end(); ++citer
) {
224 OSL_TRACE("SELECT Table : %s", OUtoCStr(citer
->first
) );
228 Reference
<XIndexAccess
> xNames
;
229 switch(m_pSQLIterator
->getStatementType())
231 case SQL_STATEMENT_SELECT
:
233 // at this moment we support only one table per select statement
235 OSL_ENSURE( xTabs
.begin() != xTabs
.end(), "Need a Table");
237 m_pTable
= static_cast< OTable
* > (xTabs
.begin()->second
.get());
238 m_xColNames
= m_pTable
->getColumns();
239 xNames
= Reference
<XIndexAccess
>(m_xColNames
,UNO_QUERY
);
240 // set the binding of the resultrow
241 m_aRow
= new OValueVector(xNames
->getCount());
242 (m_aRow
->get())[0].setBound(sal_True
);
243 ::std::for_each(m_aRow
->get().begin()+1,m_aRow
->get().end(),TSetBound(sal_False
));
244 // create the column mapping
245 createColumnMapping();
250 case SQL_STATEMENT_CREATE_TABLE
:
258 else if(!bAdjusted
) //Our sql parser does not support a statement like "create table foo"
259 // So we append ("E-mail" varchar) to the last of it to make it work
261 return parseSql(sql
+ OUString( "(""E-mail"" character)"),sal_True
);
264 getOwnConnection()->throwSQLException( STR_QUERY_TOO_COMPLEX
, *this );
265 OSL_FAIL( "OCommonStatement::parseSql: unreachable!" );
269 // -------------------------------------------------------------------------
270 Reference
< XResultSet
> OCommonStatement::impl_executeCurrentQuery()
272 SAL_INFO("connectivity.mork", "=> OCommonStatement::impl_executeCurrentQuery()" );
274 clearCachedResultSet();
276 ::rtl::Reference
< OResultSet
> pResult( new OResultSet( this, m_pSQLIterator
) );
277 initializeResultSet( pResult
.get() );
279 pResult
->executeQuery();
280 cacheResultSet( pResult
); // only cache if we survived the execution
282 return pResult
.get();
286 // -------------------------------------------------------------------------
287 void OCommonStatement::initializeResultSet( OResultSet
* _pResult
)
289 SAL_INFO("connectivity.mork", "=> OCommonStatement::initializeResultSet()" );
291 ENSURE_OR_THROW( _pResult
, "invalid result set" );
293 _pResult
->setColumnMapping(m_aColMapping
);
294 _pResult
->setOrderByColumns(m_aOrderbyColumnNumber
);
295 _pResult
->setOrderByAscending(m_aOrderbyAscending
);
296 _pResult
->setBindingRow(m_aRow
);
297 _pResult
->setTable(m_pTable
);
300 // -------------------------------------------------------------------------
301 void OCommonStatement::clearCachedResultSet()
303 SAL_INFO("connectivity.mork", "=> OCommonStatement::clearCachedResultSet()" );
305 Reference
< XResultSet
> xResultSet( m_xResultSet
.get(), UNO_QUERY
);
306 if ( !xResultSet
.is() )
309 Reference
< XCloseable
>( xResultSet
, UNO_QUERY_THROW
)->close();
311 m_xResultSet
= Reference
< XResultSet
>();
314 // -------------------------------------------------------------------------
315 void OCommonStatement::cacheResultSet( const ::rtl::Reference
< OResultSet
>& _pResult
)
317 ENSURE_OR_THROW( _pResult
.is(), "invalid result set" );
318 m_xResultSet
= Reference
< XResultSet
>( _pResult
.get() );
321 // -------------------------------------------------------------------------
322 sal_Bool SAL_CALL
OCommonStatement::execute( const OUString
& sql
) throw(SQLException
, RuntimeException
)
324 SAL_INFO("connectivity.mork", "=> OCommonStatement::execute()" );
326 ::osl::MutexGuard
aGuard( m_aMutex
);
327 checkDisposed(OCommonStatement_IBASE::rBHelper
.bDisposed
);
329 OSL_TRACE("Statement::execute( %s )", OUtoCStr( sql
) );
331 Reference
< XResultSet
> xRS
= executeQuery( sql
);
332 // returns true when a resultset is available
335 // -------------------------------------------------------------------------
337 Reference
< XResultSet
> SAL_CALL
OCommonStatement::executeQuery( const OUString
& sql
) throw(SQLException
, RuntimeException
)
339 SAL_INFO("connectivity.mork", "=> OCommonStatement::executeQuery()" );
341 ::osl::MutexGuard
aGuard( m_ThreadMutex
);
342 checkDisposed(OCommonStatement_IBASE::rBHelper
.bDisposed
);
344 OSL_TRACE("Statement::executeQuery( %s )", OUtoCStr( sql
) );
346 // parse the statement
347 StatementType eStatementType
= parseSql( sql
);
348 if ( eStatementType
!= eSelect
)
351 return impl_executeCurrentQuery();
353 // -------------------------------------------------------------------------
355 Reference
< XConnection
> SAL_CALL
OCommonStatement::getConnection( ) throw(SQLException
, RuntimeException
)
357 SAL_INFO("connectivity.mork", "=> OCommonStatement::getConnection()" );
359 ::osl::MutexGuard
aGuard( m_aMutex
);
360 checkDisposed(OCommonStatement_IBASE::rBHelper
.bDisposed
);
362 // just return our connection here
363 return (Reference
< XConnection
>)m_pConnection
;
365 // -----------------------------------------------------------------------------
366 Any SAL_CALL
OStatement::queryInterface( const Type
& rType
) throw(RuntimeException
)
368 SAL_INFO("connectivity.mork", "=> OCommonStatement::queryInterface()" );
370 Any aRet
= ::cppu::queryInterface(rType
,static_cast< XServiceInfo
*> (this));
372 aRet
= OCommonStatement::queryInterface(rType
);
375 // -------------------------------------------------------------------------
376 sal_Int32 SAL_CALL
OCommonStatement::executeUpdate( const OUString
& /*sql*/ ) throw(SQLException
, RuntimeException
)
378 ::dbtools::throwFeatureNotImplementedException( "XStatement::executeUpdate", *this );
382 // -------------------------------------------------------------------------
383 Any SAL_CALL
OCommonStatement::getWarnings( ) throw(SQLException
, RuntimeException
)
385 ::osl::MutexGuard
aGuard( m_aMutex
);
386 checkDisposed(OCommonStatement_IBASE::rBHelper
.bDisposed
);
388 return makeAny(m_aLastWarning
);
390 // -------------------------------------------------------------------------
392 // -------------------------------------------------------------------------
393 void SAL_CALL
OCommonStatement::clearWarnings( ) throw(SQLException
, RuntimeException
)
395 ::osl::MutexGuard
aGuard( m_aMutex
);
396 checkDisposed(OCommonStatement_IBASE::rBHelper
.bDisposed
);
399 m_aLastWarning
= SQLWarning();
401 // -------------------------------------------------------------------------
402 ::cppu::IPropertyArrayHelper
* OCommonStatement::createArrayHelper( ) const
404 // this properties are define by the service resultset
405 // they must in alphabetic order
406 Sequence
< Property
> aProps(9);
407 Property
* pProperties
= aProps
.getArray();
409 DECL_PROP0(CURSORNAME
, OUString
);
410 DECL_BOOL_PROP0(ESCAPEPROCESSING
);
411 DECL_PROP0(FETCHDIRECTION
,sal_Int32
);
412 DECL_PROP0(FETCHSIZE
, sal_Int32
);
413 DECL_PROP0(MAXFIELDSIZE
,sal_Int32
);
414 DECL_PROP0(MAXROWS
, sal_Int32
);
415 DECL_PROP0(QUERYTIMEOUT
,sal_Int32
);
416 DECL_PROP0(RESULTSETCONCURRENCY
,sal_Int32
);
417 DECL_PROP0(RESULTSETTYPE
,sal_Int32
);
419 return new ::cppu::OPropertyArrayHelper(aProps
);
422 // -------------------------------------------------------------------------
423 ::cppu::IPropertyArrayHelper
& OCommonStatement::getInfoHelper()
425 return *const_cast<OCommonStatement
*>(this)->getArrayHelper();
427 // -------------------------------------------------------------------------
428 sal_Bool
OCommonStatement::convertFastPropertyValue(
429 Any
& /*rConvertedValue*/,
431 sal_Int32
/*nHandle*/,
432 const Any
& /*rValue*/ )
433 throw (::com::sun::star::lang::IllegalArgumentException
)
435 sal_Bool bConverted
= sal_False
;
436 // here we have to try to convert
439 // -------------------------------------------------------------------------
440 void OCommonStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle
,const Any
& /*rValue*/) throw (Exception
)
442 // set the value to whatever is necessary
445 case PROPERTY_ID_QUERYTIMEOUT
:
446 case PROPERTY_ID_MAXFIELDSIZE
:
447 case PROPERTY_ID_MAXROWS
:
448 case PROPERTY_ID_RESULTSETCONCURRENCY
:
449 case PROPERTY_ID_RESULTSETTYPE
:
450 case PROPERTY_ID_FETCHDIRECTION
:
451 case PROPERTY_ID_FETCHSIZE
:
452 case PROPERTY_ID_ESCAPEPROCESSING
:
457 // -------------------------------------------------------------------------
458 void OCommonStatement::getFastPropertyValue(Any
& /*rValue*/,sal_Int32 nHandle
) const
462 case PROPERTY_ID_QUERYTIMEOUT
:
463 case PROPERTY_ID_MAXFIELDSIZE
:
464 case PROPERTY_ID_MAXROWS
:
465 case PROPERTY_ID_RESULTSETCONCURRENCY
:
466 case PROPERTY_ID_RESULTSETTYPE
:
467 case PROPERTY_ID_FETCHDIRECTION
:
468 case PROPERTY_ID_FETCHSIZE
:
469 case PROPERTY_ID_ESCAPEPROCESSING
:
474 // -------------------------------------------------------------------------
475 IMPLEMENT_SERVICE_INFO(OStatement
,"com.sun.star.sdbcx.OStatement","com.sun.star.sdbc.Statement");
476 // -----------------------------------------------------------------------------
477 void SAL_CALL
OCommonStatement::acquire() throw()
479 OCommonStatement_IBASE::acquire();
481 // -----------------------------------------------------------------------------
482 void SAL_CALL
OCommonStatement::release() throw()
486 // -----------------------------------------------------------------------------
487 void SAL_CALL
OStatement::acquire() throw()
489 OCommonStatement::acquire();
491 // -----------------------------------------------------------------------------
492 void SAL_CALL
OStatement::release() throw()
494 OCommonStatement::release();
496 // -----------------------------------------------------------------------------
497 Reference
< ::com::sun::star::beans::XPropertySetInfo
> SAL_CALL
OCommonStatement::getPropertySetInfo( ) throw(RuntimeException
)
499 return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
501 // -----------------------------------------------------------------------------
502 void OCommonStatement::createColumnMapping()
504 SAL_INFO("connectivity.mork", "=> OCommonStatement::createColumnMapping()" );
508 // initialize the column index map (mapping select columns to table columns)
509 ::rtl::Reference
<connectivity::OSQLColumns
> xColumns
= m_pSQLIterator
->getSelectColumns();
510 m_aColMapping
.resize(xColumns
->get().size() + 1);
511 for (i
=0; i
<m_aColMapping
.size(); ++i
)
512 m_aColMapping
[i
] = static_cast<sal_Int32
>(i
);
514 Reference
<XIndexAccess
> xNames(m_xColNames
,UNO_QUERY
);
515 // now check which columns are bound
516 #if OSL_DEBUG_LEVEL > 0
517 for ( i
= 0; i
< m_aColMapping
.size(); i
++ )
518 OSL_TRACE("BEFORE Mapped: %d -> %d", i
, m_aColMapping
[i
] );
520 OResultSet::setBoundedColumns(m_aRow
,xColumns
,xNames
,sal_True
,m_xDBMetaData
,m_aColMapping
);
521 #if OSL_DEBUG_LEVEL > 0
522 for ( i
= 0; i
< m_aColMapping
.size(); i
++ )
523 OSL_TRACE("AFTER Mapped: %d -> %d", i
, m_aColMapping
[i
] );
526 // -----------------------------------------------------------------------------
528 void OCommonStatement::analyseSQL()
530 const OSQLParseNode
* pOrderbyClause
= m_pSQLIterator
->getOrderTree();
533 OSQLParseNode
* pOrderingSpecCommalist
= pOrderbyClause
->getChild(2);
534 OSL_ENSURE(SQL_ISRULE(pOrderingSpecCommalist
,ordering_spec_commalist
),"OResultSet: Fehler im Parse Tree");
536 for (sal_uInt32 m
= 0; m
< pOrderingSpecCommalist
->count(); m
++)
538 OSQLParseNode
* pOrderingSpec
= pOrderingSpecCommalist
->getChild(m
);
539 OSL_ENSURE(SQL_ISRULE(pOrderingSpec
,ordering_spec
),"OResultSet: Fehler im Parse Tree");
540 OSL_ENSURE(pOrderingSpec
->count() == 2,"OResultSet: Fehler im Parse Tree");
542 OSQLParseNode
* pColumnRef
= pOrderingSpec
->getChild(0);
543 if(!SQL_ISRULE(pColumnRef
,column_ref
))
545 throw SQLException();
547 OSQLParseNode
* pAscendingDescending
= pOrderingSpec
->getChild(1);
548 setOrderbyColumn(pColumnRef
,pAscendingDescending
);
552 //------------------------------------------------------------------
553 void OCommonStatement::setOrderbyColumn( OSQLParseNode
* pColumnRef
,
554 OSQLParseNode
* pAscendingDescending
)
556 SAL_INFO("connectivity.mork", "=> OCommonStatement::setOrderbyColumn()" );
558 OUString aColumnName
;
559 if (pColumnRef
->count() == 1)
560 aColumnName
= pColumnRef
->getChild(0)->getTokenValue();
561 else if (pColumnRef
->count() == 3)
563 pColumnRef
->getChild(2)->parseNodeToStr( aColumnName
, getOwnConnection(), NULL
, sal_False
, sal_False
);
567 throw SQLException();
570 Reference
<XColumnLocate
> xColLocate(m_xColNames
,UNO_QUERY
);
574 m_aOrderbyColumnNumber
.push_back(xColLocate
->findColumn(aColumnName
));
576 // Ascending or Descending?
577 m_aOrderbyAscending
.push_back((SQL_ISTOKEN(pAscendingDescending
,DESC
)) ? SQL_DESC
: SQL_ASC
);
579 // -----------------------------------------------------------------------------
581 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */