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 .
20 #include "Connection.hxx"
21 #include "PreparedStatement.hxx"
22 #include "ResultSet.hxx"
23 #include "ResultSetMetaData.hxx"
26 #include <comphelper/sequence.hxx>
27 #include <connectivity/dbexception.hxx>
28 #include <cppuhelper/typeprovider.hxx>
29 #include <osl/diagnose.h>
30 #include <propertyids.hxx>
33 #include <com/sun/star/sdbc/DataType.hpp>
34 #include <com/sun/star/lang/DisposedException.hpp>
36 using namespace connectivity::firebird
;
38 using namespace ::comphelper
;
39 using namespace ::osl
;
41 using namespace com::sun::star
;
42 using namespace com::sun::star::uno
;
43 using namespace com::sun::star::lang
;
44 using namespace com::sun::star::beans
;
45 using namespace com::sun::star::sdbc
;
46 using namespace com::sun::star::container
;
47 using namespace com::sun::star::io
;
48 using namespace com::sun::star::util
;
50 IMPLEMENT_SERVICE_INFO(OPreparedStatement
,"com.sun.star.sdbcx.firebird.PreparedStatement","com.sun.star.sdbc.PreparedStatement");
53 OPreparedStatement::OPreparedStatement( Connection
* _pConnection
,
54 const TTypeInfoVector
& _TypeInfo
,
56 :OStatementCommonBase(_pConnection
)
57 ,m_aTypeInfo(_TypeInfo
)
62 SAL_INFO("connectivity.firebird", "OPreparedStatement(). "
66 void OPreparedStatement::ensurePrepared()
67 throw (SQLException
, RuntimeException
)
69 MutexGuard
aGuard(m_aMutex
);
70 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
72 if (m_aStatementHandle
)
79 m_pInSqlda
= (XSQLDA
*) malloc(XSQLDA_LENGTH(10));
80 m_pInSqlda
->version
= SQLDA_VERSION1
;
81 m_pInSqlda
->sqln
= 10;
84 prepareAndDescribeStatement(m_sSqlStatement
,
89 aErr
= isc_dsql_describe_bind(m_statusVector
,
96 SAL_WARN("connectivity.firebird", "isc_dsql_describe_bind failed");
98 else if (m_pInSqlda
->sqld
> m_pInSqlda
->sqln
) // Not large enough
100 short nItems
= m_pInSqlda
->sqld
;
102 m_pInSqlda
= (XSQLDA
*) malloc(XSQLDA_LENGTH(nItems
));
103 m_pInSqlda
->version
= SQLDA_VERSION1
;
104 m_pInSqlda
->sqln
= nItems
;
105 isc_dsql_describe_bind(m_statusVector
,
112 mallocSQLVAR(m_pInSqlda
);
114 evaluateStatusVector(m_statusVector
, m_sSqlStatement
, *this);
117 OPreparedStatement::~OPreparedStatement()
121 void SAL_CALL
OPreparedStatement::acquire() throw()
123 OStatementCommonBase::acquire();
126 void SAL_CALL
OPreparedStatement::release() throw()
128 OStatementCommonBase::release();
131 Any SAL_CALL
OPreparedStatement::queryInterface(const Type
& rType
)
132 throw(RuntimeException
, std::exception
)
134 Any aRet
= OStatementCommonBase::queryInterface(rType
);
136 aRet
= OPreparedStatement_Base::queryInterface(rType
);
140 uno::Sequence
< Type
> SAL_CALL
OPreparedStatement::getTypes()
141 throw(RuntimeException
, std::exception
)
143 return concatSequences(OPreparedStatement_Base::getTypes(),
144 OStatementCommonBase::getTypes());
147 Reference
< XResultSetMetaData
> SAL_CALL
OPreparedStatement::getMetaData()
148 throw(SQLException
, RuntimeException
, std::exception
)
150 ::osl::MutexGuard
aGuard( m_aMutex
);
151 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
154 if(!m_xMetaData
.is())
155 m_xMetaData
= new OResultSetMetaData(m_pConnection
, m_pOutSqlda
);
160 void SAL_CALL
OPreparedStatement::close() throw(SQLException
, RuntimeException
, std::exception
)
162 MutexGuard
aGuard( m_aMutex
);
163 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
165 OStatementCommonBase::close();
168 freeSQLVAR(m_pInSqlda
);
174 freeSQLVAR(m_pOutSqlda
);
180 void SAL_CALL
OPreparedStatement::disposing()
185 void SAL_CALL
OPreparedStatement::setString(sal_Int32 nParameterIndex
,
187 throw(SQLException
, RuntimeException
, std::exception
)
189 SAL_INFO("connectivity.firebird",
190 "setString(" << nParameterIndex
<< " , " << x
<< ")");
192 MutexGuard
aGuard( m_aMutex
);
193 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
196 checkParameterIndex(nParameterIndex
);
197 setParameterNull(nParameterIndex
, false);
199 OString str
= OUStringToOString(x
, RTL_TEXTENCODING_UTF8
);
201 XSQLVAR
* pVar
= m_pInSqlda
->sqlvar
+ (nParameterIndex
- 1);
203 int dtype
= (pVar
->sqltype
& ~1); // drop flag bit for now
205 if (str
.getLength() > pVar
->sqllen
)
206 str
= str
.copy(0, pVar
->sqllen
);
211 const sal_Int32 max_varchar_len
= 0xFFFF;
212 // First 2 bytes indicate string size
213 if (str
.getLength() > max_varchar_len
)
215 str
= str
.copy(0, max_varchar_len
);
217 const short nLength
= str
.getLength();
218 memcpy(pVar
->sqldata
, &nLength
, 2);
220 memcpy(pVar
->sqldata
+ 2, str
.getStr(), str
.getLength());
224 memcpy(pVar
->sqldata
, str
.getStr(), str
.getLength());
225 // Fill remainder with spaces
226 memset(pVar
->sqldata
+ str
.getLength(), ' ', pVar
->sqllen
- str
.getLength());
229 ::dbtools::throwSQLException(
230 "Incorrect type for setString",
231 ::dbtools::SQL_INVALID_SQL_DATA_TYPE
,
236 Reference
< XConnection
> SAL_CALL
OPreparedStatement::getConnection()
237 throw(SQLException
, RuntimeException
, std::exception
)
239 MutexGuard
aGuard( m_aMutex
);
240 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
242 return Reference
< XConnection
>(m_pConnection
);
245 sal_Bool SAL_CALL
OPreparedStatement::execute()
246 throw(SQLException
, RuntimeException
, std::exception
)
248 SAL_INFO("connectivity.firebird", "executeQuery(). "
249 "Got called with sql: " << m_sSqlStatement
);
251 MutexGuard
aGuard( m_aMutex
);
252 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
258 if (m_xResultSet
.is()) // Checks whether we have already run the statement.
261 // Closes the cursor from the last run.
262 // This doesn't actually free the statement -- using DSQL_close closes
263 // the cursor and keeps the statement, using DSQL_drop frees the statement
264 // (and associated cursors).
265 aErr
= isc_dsql_free_statement(m_statusVector
,
269 evaluateStatusVector(m_statusVector
,
270 "isc_dsql_free_statement: close cursor",
274 aErr
= isc_dsql_execute(m_statusVector
,
275 &m_pConnection
->getTransaction(),
281 SAL_WARN("connectivity.firebird", "isc_dsql_execute failed" );
282 evaluateStatusVector(m_statusVector
, "isc_dsql_execute", *this);
285 m_xResultSet
= new OResultSet(m_pConnection
,
287 uno::Reference
< XInterface
>(*this),
291 if (getStatementChangeCount() > 0)
292 m_pConnection
->notifyDatabaseModified();
294 return m_xResultSet
.is();
295 // TODO: implement handling of multiple ResultSets.
298 sal_Int32 SAL_CALL
OPreparedStatement::executeUpdate()
299 throw(SQLException
, RuntimeException
, std::exception
)
302 return getStatementChangeCount();
305 Reference
< XResultSet
> SAL_CALL
OPreparedStatement::executeQuery()
306 throw(SQLException
, RuntimeException
, std::exception
)
312 //----- XParameters -----------------------------------------------------------
313 void SAL_CALL
OPreparedStatement::setNull(sal_Int32 nIndex
, sal_Int32
/*nSqlType*/)
314 throw(SQLException
, RuntimeException
, std::exception
)
316 MutexGuard
aGuard( m_aMutex
);
317 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
320 setParameterNull(nIndex
, true);
323 void SAL_CALL
OPreparedStatement::setBoolean(sal_Int32
/*nIndex*/, sal_Bool
/*bValue*/)
324 throw(SQLException
, RuntimeException
, std::exception
)
326 // FIREBIRD3: will need to be implemented.
327 ::dbtools::throwFunctionNotSupportedException("XParameters::setBoolean", *this);
330 template <typename T
>
331 void OPreparedStatement::setValue(sal_Int32 nIndex
, T
& nValue
, ISC_SHORT nType
)
332 throw(SQLException
, RuntimeException
)
334 MutexGuard
aGuard( m_aMutex
);
335 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
338 checkParameterIndex(nIndex
);
339 setParameterNull(nIndex
, false);
341 XSQLVAR
* pVar
= m_pInSqlda
->sqlvar
+ (nIndex
- 1);
343 if ((pVar
->sqltype
& ~1) != nType
)
345 ::dbtools::throwSQLException(
346 "Incorrect type for setString",
347 ::dbtools::SQL_INVALID_SQL_DATA_TYPE
,
351 memcpy(pVar
->sqldata
, &nValue
, sizeof(nValue
));
354 void SAL_CALL
OPreparedStatement::setByte(sal_Int32
/*nIndex*/, sal_Int8
/*nValue*/)
355 throw(SQLException
, RuntimeException
, std::exception
)
357 ::dbtools::throwFunctionNotSupportedException("XParameters::setByte", *this);
360 void SAL_CALL
OPreparedStatement::setShort(sal_Int32 nIndex
, sal_Int16 nValue
)
361 throw(SQLException
, RuntimeException
, std::exception
)
363 setValue
< sal_Int16
>(nIndex
, nValue
, SQL_SHORT
);
366 void SAL_CALL
OPreparedStatement::setInt(sal_Int32 nIndex
, sal_Int32 nValue
)
367 throw(SQLException
, RuntimeException
, std::exception
)
369 setValue
< sal_Int32
>(nIndex
, nValue
, SQL_LONG
);
372 void SAL_CALL
OPreparedStatement::setLong(sal_Int32 nIndex
, sal_Int64 nValue
)
373 throw(SQLException
, RuntimeException
, std::exception
)
375 setValue
< sal_Int64
>(nIndex
, nValue
, SQL_INT64
);
378 void SAL_CALL
OPreparedStatement::setFloat(sal_Int32 nIndex
, float nValue
)
379 throw(SQLException
, RuntimeException
, std::exception
)
381 setValue
< float >(nIndex
, nValue
, SQL_FLOAT
);
384 void SAL_CALL
OPreparedStatement::setDouble(sal_Int32 nIndex
, double nValue
)
385 throw(SQLException
, RuntimeException
, std::exception
)
387 setValue
< double >(nIndex
, nValue
, SQL_DOUBLE
); // TODO: SQL_D_FLOAT?
390 void SAL_CALL
OPreparedStatement::setDate(sal_Int32 nIndex
, const Date
& rDate
)
391 throw(SQLException
, RuntimeException
, std::exception
)
394 aCTime
.tm_mday
= rDate
.Day
;
395 aCTime
.tm_mon
= rDate
.Month
;
396 aCTime
.tm_year
= rDate
.Year
;
399 isc_encode_sql_date(&aCTime
, &aISCDate
);
401 setValue
< ISC_DATE
>(nIndex
, aISCDate
, SQL_TYPE_DATE
);
404 void SAL_CALL
OPreparedStatement::setTime( sal_Int32 nIndex
, const Time
& rTime
)
405 throw(SQLException
, RuntimeException
, std::exception
)
408 aCTime
.tm_sec
= rTime
.Seconds
;
409 aCTime
.tm_min
= rTime
.Minutes
;
410 aCTime
.tm_hour
= rTime
.Hours
;
413 isc_encode_sql_time(&aCTime
, &aISCTime
);
415 setValue
< ISC_TIME
>(nIndex
, aISCTime
, SQL_TYPE_TIME
);
418 void SAL_CALL
OPreparedStatement::setTimestamp(sal_Int32 nIndex
, const DateTime
& rTimestamp
)
419 throw(SQLException
, RuntimeException
, std::exception
)
422 aCTime
.tm_sec
= rTimestamp
.Seconds
;
423 aCTime
.tm_min
= rTimestamp
.Minutes
;
424 aCTime
.tm_hour
= rTimestamp
.Hours
;
425 aCTime
.tm_mday
= rTimestamp
.Day
;
426 aCTime
.tm_mon
= rTimestamp
.Month
;
427 aCTime
.tm_year
= rTimestamp
.Year
;
429 ISC_TIMESTAMP aISCTimestamp
;
430 isc_encode_timestamp(&aCTime
, &aISCTimestamp
);
432 setValue
< ISC_TIMESTAMP
>(nIndex
, aISCTimestamp
, SQL_TIMESTAMP
);
436 // void OPreaparedStatement::set
437 void OPreparedStatement::openBlobForWriting(isc_blob_handle
& rBlobHandle
, ISC_QUAD
& rBlobId
)
441 aErr
= isc_create_blob2(m_statusVector
,
442 &m_pConnection
->getDBHandle(),
443 &m_pConnection
->getTransaction(),
446 0, // Blob parameter buffer length
447 0); // Blob parameter buffer handle
451 evaluateStatusVector(m_statusVector
,
452 "setBlob failed on " + m_sSqlStatement
,
458 void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle
& rBlobHandle
)
462 aErr
= isc_close_blob(m_statusVector
,
466 evaluateStatusVector(m_statusVector
,
467 "isc_close_blob failed",
473 void SAL_CALL
OPreparedStatement::setClob( sal_Int32 parameterIndex
, const Reference
< XClob
>& x
) throw(SQLException
, RuntimeException
, std::exception
)
475 (void) parameterIndex
;
477 ::osl::MutexGuard
aGuard( m_aMutex
);
478 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
482 void SAL_CALL
OPreparedStatement::setBlob(sal_Int32 nParameterIndex
,
483 const Reference
< XBlob
>& xBlob
)
484 throw (SQLException
, RuntimeException
, std::exception
)
486 ::osl::MutexGuard
aGuard(m_aMutex
);
487 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
489 isc_blob_handle aBlobHandle
= 0;
492 openBlobForWriting(aBlobHandle
, aBlobId
);
494 // Max segment size is 2^16 == SAL_MAX_UINT16
495 // LEM TODO: SAL_MAX_UINT16 is 2^16-1; this mixup is probably innocuous; to be checked
496 sal_uInt64 nDataWritten
= 0;
498 while (xBlob
->length() - nDataWritten
> 0)
500 sal_uInt64 nDataRemaining
= xBlob
->length() - nDataWritten
;
501 sal_uInt16 nWriteSize
= (nDataRemaining
> SAL_MAX_UINT16
) ? SAL_MAX_UINT16
: nDataRemaining
;
502 aErr
= isc_put_segment(m_statusVector
,
505 (const char*) xBlob
->getBytes(nDataWritten
, nWriteSize
).getConstArray());
506 nDataWritten
+= nWriteSize
;
514 // We need to make sure we close the Blob even if their are errors, hence evaluate
515 // errors after closing.
516 closeBlobAfterWriting(aBlobHandle
);
520 evaluateStatusVector(m_statusVector
,
521 "isc_put_segment failed",
526 setValue
< ISC_QUAD
>(nParameterIndex
, aBlobId
, SQL_BLOB
);
531 void SAL_CALL
OPreparedStatement::setArray( sal_Int32 parameterIndex
, const Reference
< XArray
>& x
) throw(SQLException
, RuntimeException
, std::exception
)
533 (void) parameterIndex
;
535 ::osl::MutexGuard
aGuard( m_aMutex
);
536 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
541 void SAL_CALL
OPreparedStatement::setRef( sal_Int32 parameterIndex
, const Reference
< XRef
>& x
) throw(SQLException
, RuntimeException
, std::exception
)
543 (void) parameterIndex
;
545 ::osl::MutexGuard
aGuard( m_aMutex
);
546 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
551 void SAL_CALL
OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex
, const Any
& x
, sal_Int32 sqlType
, sal_Int32 scale
) throw(SQLException
, RuntimeException
, std::exception
)
553 (void) parameterIndex
;
557 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
558 ::osl::MutexGuard
aGuard( m_aMutex
);
563 void SAL_CALL
OPreparedStatement::setObjectNull( sal_Int32 parameterIndex
, sal_Int32 sqlType
, const ::rtl::OUString
& typeName
) throw(SQLException
, RuntimeException
, std::exception
)
565 (void) parameterIndex
;
568 ::osl::MutexGuard
aGuard( m_aMutex
);
569 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
574 void SAL_CALL
OPreparedStatement::setObject( sal_Int32 parameterIndex
, const Any
& x
) throw(SQLException
, RuntimeException
, std::exception
)
576 (void) parameterIndex
;
578 ::osl::MutexGuard
aGuard( m_aMutex
);
579 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
583 void SAL_CALL
OPreparedStatement::setBytes(sal_Int32 nParameterIndex
,
584 const Sequence
< sal_Int8
>& xBytes
)
585 throw (SQLException
, RuntimeException
, std::exception
)
587 ::osl::MutexGuard
aGuard(m_aMutex
);
588 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
590 isc_blob_handle aBlobHandle
= 0;
593 openBlobForWriting(aBlobHandle
, aBlobId
);
595 // Max segment size is 2^16 == SAL_MAX_UINT16
596 sal_uInt64 nDataWritten
= 0;
598 while (xBytes
.getLength() - nDataWritten
> 0)
600 sal_uInt64 nDataRemaining
= xBytes
.getLength() - nDataWritten
;
601 sal_uInt16 nWriteSize
= (nDataRemaining
> SAL_MAX_UINT16
) ? SAL_MAX_UINT16
: nDataRemaining
;
602 aErr
= isc_put_segment(m_statusVector
,
605 (const char*) xBytes
.getConstArray() + nDataWritten
);
606 nDataWritten
+= nWriteSize
;
612 // We need to make sure we close the Blob even if their are errors, hence evaluate
613 // errors after closing.
614 closeBlobAfterWriting(aBlobHandle
);
618 evaluateStatusVector(m_statusVector
,
619 "isc_put_segment failed",
624 setValue
< ISC_QUAD
>(nParameterIndex
, aBlobId
, SQL_BLOB
);
629 void SAL_CALL
OPreparedStatement::setCharacterStream( sal_Int32 parameterIndex
, const Reference
< ::com::sun::star::io::XInputStream
>& x
, sal_Int32 length
) throw(SQLException
, RuntimeException
, std::exception
)
631 (void) parameterIndex
;
634 ::osl::MutexGuard
aGuard( m_aMutex
);
635 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
640 void SAL_CALL
OPreparedStatement::setBinaryStream( sal_Int32 parameterIndex
, const Reference
< ::com::sun::star::io::XInputStream
>& x
, sal_Int32 length
) throw(SQLException
, RuntimeException
, std::exception
)
642 (void) parameterIndex
;
645 ::osl::MutexGuard
aGuard( m_aMutex
);
646 checkDisposed(OStatementCommonBase_Base::rBHelper
.bDisposed
);
651 void SAL_CALL
OPreparedStatement::clearParameters( ) throw(SQLException
, RuntimeException
, std::exception
)
655 // ---- Batch methods -- unsupported -----------------------------------------
656 void SAL_CALL
OPreparedStatement::clearBatch()
657 throw(SQLException
, RuntimeException
, std::exception
)
662 void SAL_CALL
OPreparedStatement::addBatch()
663 throw(SQLException
, RuntimeException
, std::exception
)
665 // Unsupported by firebird
668 Sequence
< sal_Int32
> SAL_CALL
OPreparedStatement::executeBatch()
669 throw(SQLException
, RuntimeException
, std::exception
)
671 // Unsupported by firebird
672 return Sequence
< sal_Int32
>();
675 void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle
,const Any
& rValue
) throw (Exception
, std::exception
)
679 case PROPERTY_ID_RESULTSETCONCURRENCY
:
681 case PROPERTY_ID_RESULTSETTYPE
:
683 case PROPERTY_ID_FETCHDIRECTION
:
685 case PROPERTY_ID_USEBOOKMARKS
:
688 OStatementCommonBase::setFastPropertyValue_NoBroadcast(nHandle
,rValue
);
692 void OPreparedStatement::checkParameterIndex(sal_Int32 nParameterIndex
)
693 throw(SQLException
, RuntimeException
)
696 if ((nParameterIndex
== 0) || (nParameterIndex
> m_pInSqlda
->sqld
))
698 ::dbtools::throwSQLException(
699 "No column " + OUString::number(nParameterIndex
),
700 ::dbtools::SQL_COLUMN_NOT_FOUND
,
705 void OPreparedStatement::setParameterNull(sal_Int32 nParameterIndex
,
708 XSQLVAR
* pVar
= m_pInSqlda
->sqlvar
+ (nParameterIndex
- 1);
709 if (pVar
->sqltype
& 1)
718 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */