summaryrefslogtreecommitdiffstats
path: root/connectivity/source/drivers/firebird
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--connectivity/source/drivers/firebird/Blob.cxx391
-rw-r--r--connectivity/source/drivers/firebird/Blob.hxx100
-rw-r--r--connectivity/source/drivers/firebird/Catalog.cxx105
-rw-r--r--connectivity/source/drivers/firebird/Catalog.hxx40
-rw-r--r--connectivity/source/drivers/firebird/Clob.cxx141
-rw-r--r--connectivity/source/drivers/firebird/Clob.hxx64
-rw-r--r--connectivity/source/drivers/firebird/Column.cxx51
-rw-r--r--connectivity/source/drivers/firebird/Column.hxx32
-rw-r--r--connectivity/source/drivers/firebird/Columns.cxx41
-rw-r--r--connectivity/source/drivers/firebird/Columns.hxx30
-rw-r--r--connectivity/source/drivers/firebird/Connection.cxx979
-rw-r--r--connectivity/source/drivers/firebird/Connection.hxx247
-rw-r--r--connectivity/source/drivers/firebird/DatabaseMetaData.cxx1803
-rw-r--r--connectivity/source/drivers/firebird/DatabaseMetaData.hxx205
-rw-r--r--connectivity/source/drivers/firebird/Driver.cxx228
-rw-r--r--connectivity/source/drivers/firebird/Driver.hxx90
-rw-r--r--connectivity/source/drivers/firebird/Indexes.cxx33
-rw-r--r--connectivity/source/drivers/firebird/Indexes.hxx39
-rw-r--r--connectivity/source/drivers/firebird/Keys.cxx54
-rw-r--r--connectivity/source/drivers/firebird/Keys.hxx36
-rw-r--r--connectivity/source/drivers/firebird/PreparedStatement.cxx1058
-rw-r--r--connectivity/source/drivers/firebird/PreparedStatement.hxx152
-rw-r--r--connectivity/source/drivers/firebird/ResultSet.cxx926
-rw-r--r--connectivity/source/drivers/firebird/ResultSet.hxx216
-rw-r--r--connectivity/source/drivers/firebird/ResultSetMetaData.cxx301
-rw-r--r--connectivity/source/drivers/firebird/ResultSetMetaData.hxx80
-rw-r--r--connectivity/source/drivers/firebird/Statement.cxx176
-rw-r--r--connectivity/source/drivers/firebird/Statement.hxx84
-rw-r--r--connectivity/source/drivers/firebird/StatementCommonBase.cxx486
-rw-r--r--connectivity/source/drivers/firebird/StatementCommonBase.hxx134
-rw-r--r--connectivity/source/drivers/firebird/SubComponent.hxx111
-rw-r--r--connectivity/source/drivers/firebird/Table.cxx232
-rw-r--r--connectivity/source/drivers/firebird/Table.hxx81
-rw-r--r--connectivity/source/drivers/firebird/Tables.cxx226
-rw-r--r--connectivity/source/drivers/firebird/Tables.hxx61
-rw-r--r--connectivity/source/drivers/firebird/User.cxx53
-rw-r--r--connectivity/source/drivers/firebird/User.hxx46
-rw-r--r--connectivity/source/drivers/firebird/Users.cxx78
-rw-r--r--connectivity/source/drivers/firebird/Users.hxx53
-rw-r--r--connectivity/source/drivers/firebird/Util.cxx424
-rw-r--r--connectivity/source/drivers/firebird/Util.hxx126
-rw-r--r--connectivity/source/drivers/firebird/View.cxx85
-rw-r--r--connectivity/source/drivers/firebird/View.hxx60
-rw-r--r--connectivity/source/drivers/firebird/Views.cxx112
-rw-r--r--connectivity/source/drivers/firebird/Views.hxx42
-rw-r--r--connectivity/source/drivers/firebird/firebird_sdbc.component19
46 files changed, 10131 insertions, 0 deletions
diff --git a/connectivity/source/drivers/firebird/Blob.cxx b/connectivity/source/drivers/firebird/Blob.cxx
new file mode 100644
index 0000000000..26a5deaca0
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Blob.cxx
@@ -0,0 +1,391 @@
+/* -*- 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/.
+ */
+
+#include "Blob.hxx"
+#include "Util.hxx"
+
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <connectivity/CommonTools.hxx>
+#include <connectivity/dbexception.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace ::connectivity::firebird;
+
+using namespace ::cppu;
+using namespace ::osl;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::uno;
+
+Blob::Blob(isc_db_handle* pDatabaseHandle,
+ isc_tr_handle* pTransactionHandle,
+ ISC_QUAD const & aBlobID):
+ Blob_BASE(m_aMutex),
+ m_pDatabaseHandle(pDatabaseHandle),
+ m_pTransactionHandle(pTransactionHandle),
+ m_blobID(aBlobID),
+#if SAL_TYPES_SIZEOFPOINTER == 8
+ m_blobHandle(0),
+#else
+ m_blobHandle(nullptr),
+#endif
+ m_bBlobOpened(false),
+ m_nBlobLength(0),
+ m_nMaxSegmentSize(0),
+ m_nBlobPosition(0)
+{
+}
+
+void Blob::ensureBlobIsOpened()
+{
+ MutexGuard aGuard(m_aMutex);
+
+ if (m_bBlobOpened)
+ return;
+
+ ISC_STATUS aErr;
+ aErr = isc_open_blob2(m_statusVector,
+ m_pDatabaseHandle,
+ m_pTransactionHandle,
+ &m_blobHandle,
+ &m_blobID,
+ 0,
+ nullptr);
+
+ if (aErr)
+ evaluateStatusVector(m_statusVector, u"isc_open_blob2", *this);
+
+ m_bBlobOpened = true;
+ m_nBlobPosition = 0;
+
+ char aBlobItems[] = {
+ isc_info_blob_total_length,
+ isc_info_blob_max_segment
+ };
+
+ // Assuming a data (e.g. length of blob) is maximum 64 bit.
+ // That means we need 8 bytes for data + 2 for length of data + 1 for item
+ // identifier for each item.
+ char aResultBuffer[11 + 11];
+
+ aErr = isc_blob_info(m_statusVector,
+ &m_blobHandle,
+ sizeof(aBlobItems),
+ aBlobItems,
+ sizeof(aResultBuffer),
+ aResultBuffer);
+
+ if (aErr)
+ evaluateStatusVector(m_statusVector, u"isc_blob_info", *this);
+
+ char* pIt = aResultBuffer;
+ while( *pIt != isc_info_end ) // info is in clusters
+ {
+ char item = *pIt++;
+ short aResultLength = static_cast<short>(isc_vax_integer(pIt, 2));
+
+ pIt += 2;
+ switch(item)
+ {
+ case isc_info_blob_total_length:
+ m_nBlobLength = isc_vax_integer(pIt, aResultLength);
+ break;
+ case isc_info_blob_max_segment:
+ m_nMaxSegmentSize = isc_vax_integer(pIt, aResultLength);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ pIt += aResultLength;
+ }
+}
+
+sal_uInt16 Blob::getMaximumSegmentSize()
+{
+ ensureBlobIsOpened();
+
+ return m_nMaxSegmentSize;
+}
+
+bool Blob::readOneSegment(std::vector<char>& rDataOut)
+{
+ checkDisposed(Blob_BASE::rBHelper.bDisposed);
+ ensureBlobIsOpened();
+
+ sal_uInt16 nMaxSize = getMaximumSegmentSize();
+
+ if(rDataOut.size() < nMaxSize)
+ rDataOut.resize(nMaxSize);
+
+ sal_uInt16 nActualSize = 0;
+ ISC_STATUS aRet = isc_get_segment(m_statusVector,
+ &m_blobHandle,
+ &nActualSize,
+ nMaxSize,
+ rDataOut.data() );
+
+ if (aRet && aRet != isc_segstr_eof && IndicatesError(m_statusVector))
+ {
+ OUString sError(StatusVectorToString(m_statusVector, u"isc_get_segment"));
+ throw IOException(sError, *this);
+ }
+
+ if (rDataOut.size() > nActualSize)
+ rDataOut.resize(nActualSize);
+ m_nBlobPosition += nActualSize;
+ return aRet == isc_segstr_eof; // last segment read
+}
+
+
+void Blob::closeBlob()
+{
+ MutexGuard aGuard(m_aMutex);
+
+ if (!m_bBlobOpened)
+ return;
+
+ ISC_STATUS aErr;
+ aErr = isc_close_blob(m_statusVector,
+ &m_blobHandle);
+ if (aErr)
+ evaluateStatusVector(m_statusVector, u"isc_close_blob", *this);
+
+ m_bBlobOpened = false;
+#if SAL_TYPES_SIZEOFPOINTER == 8
+ m_blobHandle = 0;
+#else
+ m_blobHandle = nullptr;
+#endif
+}
+
+void SAL_CALL Blob::disposing()
+{
+ try
+ {
+ closeBlob();
+ }
+ catch (const SQLException &)
+ {
+ // we cannot throw any exceptions here...
+ TOOLS_WARN_EXCEPTION("connectivity.firebird", "isc_close_blob failed");
+ assert(false);
+ }
+ Blob_BASE::disposing();
+}
+
+sal_Int64 SAL_CALL Blob::length()
+{
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(Blob_BASE::rBHelper.bDisposed);
+ ensureBlobIsOpened();
+
+ return m_nBlobLength;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL Blob::getBytes(sal_Int64 nPosition,
+ sal_Int32 nBytes)
+{
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(Blob_BASE::rBHelper.bDisposed);
+ ensureBlobIsOpened();
+
+ if (nPosition > m_nBlobLength || nPosition < 1)
+ throw lang::IllegalArgumentException("nPosition out of range", *this, 0);
+ // We only have to read as many bytes as are available, i.e. nPosition+nBytes
+ // can legally be greater than the total length, hence we don't bother to check.
+
+ if (nPosition -1 < m_nBlobPosition)
+ {
+ // Resets to the beginning (we can't seek these blobs)
+ closeBlob();
+ ensureBlobIsOpened();
+ }
+
+ // nPosition is indexed from 1.
+ skipBytes(nPosition - m_nBlobPosition -1 );
+
+ // Don't bother preallocating: readBytes does the appropriate calculations
+ // and reallocates for us.
+ uno::Sequence< sal_Int8 > aBytes;
+ readBytes(aBytes, nBytes);
+ return aBytes;
+}
+
+uno::Reference< XInputStream > SAL_CALL Blob::getBinaryStream()
+{
+ return this;
+}
+
+sal_Int64 SAL_CALL Blob::position(const uno::Sequence< sal_Int8 >& /*rPattern*/,
+ sal_Int64 /*nStart*/)
+{
+ ::dbtools::throwFeatureNotImplementedSQLException("Blob::position", *this);
+ return 0;
+}
+
+sal_Int64 SAL_CALL Blob::positionOfBlob(const uno::Reference< XBlob >& /*rPattern*/,
+ sal_Int64 /*aStart*/)
+{
+ ::dbtools::throwFeatureNotImplementedSQLException("Blob::positionOfBlob", *this);
+ return 0;
+}
+
+// ---- XInputStream ----------------------------------------------------------
+
+sal_Int32 SAL_CALL Blob::readBytes(uno::Sequence< sal_Int8 >& rDataOut,
+ sal_Int32 nBytes)
+{
+ MutexGuard aGuard(m_aMutex);
+
+ try
+ {
+ checkDisposed(Blob_BASE::rBHelper.bDisposed);
+ ensureBlobIsOpened();
+ }
+ catch (const NotConnectedException&)
+ {
+ throw;
+ }
+ catch (const BufferSizeExceededException&)
+ {
+ throw;
+ }
+ catch (const IOException&)
+ {
+ throw;
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception& e)
+ {
+ css::uno::Any a(cppu::getCaughtException());
+ throw css::lang::WrappedTargetRuntimeException(
+ "wrapped Exception " + e.Message,
+ css::uno::Reference<css::uno::XInterface>(), a);
+ }
+
+ // Ensure we have enough space for the amount of data we can actually read.
+ const sal_Int64 nBytesAvailable = m_nBlobLength - m_nBlobPosition;
+ const sal_Int32 nBytesToRead = std::min<sal_Int64>(nBytes, nBytesAvailable);
+
+ if (rDataOut.getLength() < nBytesToRead)
+ rDataOut.realloc(nBytesToRead);
+
+ sal_Int32 nTotalBytesRead = 0;
+ ISC_STATUS aErr;
+ while (nTotalBytesRead < nBytesToRead)
+ {
+ sal_uInt16 nBytesRead = 0;
+ sal_uInt64 nDataRemaining = nBytesToRead - nTotalBytesRead;
+ sal_uInt16 nReadSize = std::min<sal_uInt64>(nDataRemaining, SAL_MAX_UINT16);
+ aErr = isc_get_segment(m_statusVector,
+ &m_blobHandle,
+ &nBytesRead,
+ nReadSize,
+ reinterpret_cast<char*>(rDataOut.getArray()) + nTotalBytesRead);
+ if (aErr && IndicatesError(m_statusVector))
+ {
+ OUString sError(StatusVectorToString(m_statusVector, u"isc_get_segment"));
+ throw IOException(sError, *this);
+ }
+ nTotalBytesRead += nBytesRead;
+ m_nBlobPosition += nBytesRead;
+ }
+
+ return nTotalBytesRead;
+}
+
+sal_Int32 SAL_CALL Blob::readSomeBytes(uno::Sequence< sal_Int8 >& rDataOut,
+ sal_Int32 nMaximumBytes)
+{
+ // We don't have any way of verifying how many bytes are immediately available,
+ // hence we just pass through direct to readBytes
+ // (Spec: "reads the available number of bytes, at maximum nMaxBytesToRead.")
+ return readBytes(rDataOut, nMaximumBytes);
+}
+
+void SAL_CALL Blob::skipBytes(sal_Int32 nBytesToSkip)
+{
+ // There is no way of directly skipping, hence we have to pretend to skip
+ // by reading & discarding the data.
+ uno::Sequence< sal_Int8 > aBytes;
+ readBytes(aBytes, nBytesToSkip);
+}
+
+sal_Int32 SAL_CALL Blob::available()
+{
+ MutexGuard aGuard(m_aMutex);
+
+ try
+ {
+ checkDisposed(Blob_BASE::rBHelper.bDisposed);
+ ensureBlobIsOpened();
+ }
+ catch (const NotConnectedException&)
+ {
+ throw;
+ }
+ catch (const IOException&)
+ {
+ throw;
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception& e)
+ {
+ css::uno::Any a(cppu::getCaughtException());
+ throw css::lang::WrappedTargetRuntimeException(
+ "wrapped Exception " + e.Message,
+ css::uno::Reference<css::uno::XInterface>(), a);
+ }
+
+ return m_nBlobLength - m_nBlobPosition;
+}
+
+void SAL_CALL Blob::closeInput()
+{
+ try
+ {
+ closeBlob();
+ }
+ catch (const NotConnectedException&)
+ {
+ throw;
+ }
+ catch (const IOException&)
+ {
+ throw;
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception& e)
+ {
+ css::uno::Any a(cppu::getCaughtException());
+ throw css::lang::WrappedTargetRuntimeException(
+ "wrapped Exception " + e.Message,
+ css::uno::Reference<css::uno::XInterface>(), a);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Blob.hxx b/connectivity/source/drivers/firebird/Blob.hxx
new file mode 100644
index 0000000000..990108934b
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Blob.hxx
@@ -0,0 +1,100 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <ibase.h>
+
+#include <cppuhelper/compbase.hxx>
+
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/sdbc/XBlob.hpp>
+
+#include <vector>
+
+namespace connectivity::firebird
+ {
+ typedef ::cppu::WeakComponentImplHelper< css::sdbc::XBlob,
+ css::io::XInputStream >
+ Blob_BASE;
+
+ class Blob :
+ public Blob_BASE
+ {
+ protected:
+ ::osl::Mutex m_aMutex;
+
+ isc_db_handle* m_pDatabaseHandle;
+ isc_tr_handle* m_pTransactionHandle;
+ // We store our own copy of the blob id as typically the statement
+ // manages its own blob id, and blobs are independent of a statement
+ // in firebird.
+ ISC_QUAD m_blobID;
+ isc_blob_handle m_blobHandle;
+
+ bool m_bBlobOpened;
+ sal_Int64 m_nBlobLength;
+ sal_uInt16 m_nMaxSegmentSize;
+ sal_Int64 m_nBlobPosition;
+
+ ISC_STATUS_ARRAY m_statusVector;
+
+ /// @throws css::sdbc::SQLException
+ void ensureBlobIsOpened();
+ /**
+ * Closes the blob and cleans up resources -- can be used to reset
+ * the blob if we e.g. want to read from the beginning again.
+ *
+ * @throws css::sdbc::SQLException
+ */
+ void closeBlob();
+ sal_uInt16 getMaximumSegmentSize();
+
+ public:
+ Blob(isc_db_handle* pDatabaseHandle,
+ isc_tr_handle* pTransactionHandle,
+ ISC_QUAD const & aBlobID);
+
+ bool readOneSegment(std::vector<char>& rDataOut);
+
+ // ---- XBlob ----------------------------------------------------
+ virtual sal_Int64 SAL_CALL
+ length() override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL
+ getBytes(sal_Int64 aPosition, sal_Int32 aLength) override;
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL
+ getBinaryStream() override;
+ virtual sal_Int64 SAL_CALL
+ position(const css::uno::Sequence< sal_Int8 >& rPattern,
+ sal_Int64 aStart) override;
+ virtual sal_Int64 SAL_CALL
+ positionOfBlob(const css::uno::Reference< css::sdbc::XBlob >& rPattern,
+ sal_Int64 aStart) override;
+
+ // ---- XInputStream ----------------------------------------------
+ virtual sal_Int32 SAL_CALL
+ readBytes(css::uno::Sequence< sal_Int8 >& rDataOut,
+ sal_Int32 nBytes) override;
+ virtual sal_Int32 SAL_CALL
+ readSomeBytes(css::uno::Sequence< sal_Int8 >& rDataOut,
+ sal_Int32 nMaximumBytes) override;
+ virtual void SAL_CALL
+ skipBytes(sal_Int32 nBytes) override;
+ virtual sal_Int32 SAL_CALL
+ available() override;
+ virtual void SAL_CALL
+ closeInput() override;
+
+ // ---- OComponentHelper ------------------------------------------
+ virtual void SAL_CALL disposing() override;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Catalog.cxx b/connectivity/source/drivers/firebird/Catalog.cxx
new file mode 100644
index 0000000000..2ef4f514b1
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Catalog.cxx
@@ -0,0 +1,105 @@
+/* -*- 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/.
+ */
+
+#include "Catalog.hxx"
+#include "Tables.hxx"
+#include "Users.hxx"
+#include "Views.hxx"
+
+#include <com/sun/star/sdbc/XRow.hpp>
+
+using namespace ::connectivity::firebird;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::uno;
+
+Catalog::Catalog(const uno::Reference< XConnection >& rConnection):
+ OCatalog(rConnection),
+ m_xConnection(rConnection)
+{
+}
+
+//----- OCatalog -------------------------------------------------------------
+void Catalog::refreshTables()
+{
+ Sequence< OUString > aTypes {"TABLE", "VIEW"};
+
+ uno::Reference< XResultSet > xTables = m_xMetaData->getTables(Any(),
+ "%",
+ "%",
+ aTypes);
+
+ if (!xTables.is())
+ return;
+
+ ::std::vector< OUString> aTableNames;
+
+ fillNames(xTables, aTableNames);
+
+ if (!m_pTables)
+ m_pTables.reset( new Tables(m_xConnection->getMetaData(),
+ *this,
+ m_aMutex,
+ aTableNames) );
+ else
+ m_pTables->reFill(aTableNames);
+
+}
+
+void Catalog::refreshViews()
+{
+ css::uno::Reference<css::sdbc::XResultSet> xViews
+ = m_xMetaData->getTables(css::uno::Any(), "%", "%", { "VIEW" });
+
+ if (!xViews.is())
+ return;
+
+ ::std::vector<OUString> aViewNames;
+
+ fillNames(xViews, aViewNames);
+
+ if (!m_pViews)
+ m_pViews.reset(new Views(m_xConnection, *this, m_aMutex, aViewNames));
+ else
+ m_pViews->reFill(aViewNames);
+}
+
+//----- IRefreshableGroups ---------------------------------------------------
+void Catalog::refreshGroups()
+{
+ // TODO: implement me
+}
+
+//----- IRefreshableUsers ----------------------------------------------------
+void Catalog::refreshUsers()
+{
+ Reference<XStatement> xStmt= m_xMetaData->getConnection()->createStatement();
+ uno::Reference< XResultSet > xUsers = xStmt->executeQuery("SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES");
+
+ if (!xUsers.is())
+ return;
+
+ ::std::vector< OUString> aUserNames;
+
+ uno::Reference< XRow > xRow(xUsers,UNO_QUERY);
+ while (xUsers->next())
+ {
+ aUserNames.push_back(xRow->getString(1));
+ }
+
+ if (!m_pUsers)
+ m_pUsers.reset( new Users(m_xConnection->getMetaData(),
+ *this,
+ m_aMutex,
+ aUserNames) );
+ else
+ m_pUsers->reFill(aUserNames);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Catalog.hxx b/connectivity/source/drivers/firebird/Catalog.hxx
new file mode 100644
index 0000000000..3ffb9238ed
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Catalog.hxx
@@ -0,0 +1,40 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sdbcx/VCatalog.hxx>
+
+namespace connectivity::firebird
+ {
+ class Catalog: public ::connectivity::sdbcx::OCatalog
+ {
+ css::uno::Reference< css::sdbc::XConnection >
+ m_xConnection;
+
+ public:
+ explicit Catalog(const css::uno::Reference< css::sdbc::XConnection >& rConnection);
+
+ // OCatalog
+ virtual void refreshTables() override;
+ virtual void refreshViews() override;
+
+ // IRefreshableGroups
+ virtual void refreshGroups() override;
+
+ // IRefreshableUsers
+ virtual void refreshUsers() override;
+
+ sdbcx::OCollection* getPrivateTables() const { return m_pTables.get(); }
+ sdbcx::OCollection* getPrivateViews() const { return m_pViews.get(); }
+ };
+
+} // namespace connectivity::firebird
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Clob.cxx b/connectivity/source/drivers/firebird/Clob.cxx
new file mode 100644
index 0000000000..dde050edee
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Clob.cxx
@@ -0,0 +1,141 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include "Clob.hxx"
+#include "Blob.hxx"
+
+#include <connectivity/CommonTools.hxx>
+#include <connectivity/dbexception.hxx>
+
+using namespace ::connectivity::firebird;
+
+using namespace ::osl;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::uno;
+
+Clob::Clob(isc_db_handle* pDatabaseHandle,
+ isc_tr_handle* pTransactionHandle,
+ ISC_QUAD const & aBlobID):
+ Clob_BASE(m_aMutex),
+ m_aBlob(new connectivity::firebird::Blob(pDatabaseHandle, pTransactionHandle, aBlobID)),
+ m_nCharCount(-1)
+{
+}
+
+void SAL_CALL Clob::disposing()
+{
+ m_aBlob->dispose();
+ m_aBlob.clear();
+ Clob_BASE::disposing();
+}
+
+sal_Int64 SAL_CALL Clob::length()
+{
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(Clob_BASE::rBHelper.bDisposed);
+
+ if( m_nCharCount >= 0 )
+ return m_nCharCount;
+ m_nCharCount = 0;
+
+ // Read each segment, and calculate it's size by interpreting it as a
+ // character stream. Assume that no characters are split by the segments.
+ bool bLastSegmRead = false;
+ std::vector<char> aSegmentBytes;
+ do
+ {
+ bLastSegmRead = m_aBlob->readOneSegment( aSegmentBytes );
+ OUString sSegment(aSegmentBytes.data(), aSegmentBytes.size(), RTL_TEXTENCODING_UTF8);
+
+ if( !bLastSegmRead)
+ m_nCharCount += sSegment.getLength();
+ }while( !bLastSegmRead );
+
+ m_aBlob->closeInput(); // reset position
+ return m_nCharCount;
+}
+
+OUString SAL_CALL Clob::getSubString(sal_Int64 nPosition,
+ sal_Int32 nLength)
+{
+ if (nPosition < 1) // XClob is indexed from 1
+ throw lang::IllegalArgumentException("nPosition < 1", *this, 0);
+ --nPosition; // make 0-based
+
+ if (nLength < 0)
+ throw lang::IllegalArgumentException("nLength < 0", *this, 0);
+
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(Clob_BASE::rBHelper.bDisposed);
+ // TODO do not reset position if it is not necessary
+ m_aBlob->closeInput(); // reset position
+
+ OUStringBuffer sSegmentBuffer;
+ std::vector<char> aSegmentBytes;
+
+ for (;;)
+ {
+ bool bLastRead = m_aBlob->readOneSegment( aSegmentBytes );
+ // TODO: handle possible case of split UTF-8 character
+ OUString sSegment(aSegmentBytes.data(), aSegmentBytes.size(), RTL_TEXTENCODING_UTF8);
+
+ // skip irrelevant parts
+ if (sSegment.getLength() < nPosition)
+ {
+ if (bLastRead)
+ throw lang::IllegalArgumentException("nPosition out of range", *this, 0);
+ nPosition -= sSegment.getLength();
+ continue;
+ }
+
+ // Getting here for the first time, nPosition may be > 0, meaning copy start offset.
+ // This also handles sSegment.getLength() == nPosition case, including nLength == 0.
+ const sal_Int32 nCharsToCopy = std::min<sal_Int32>(sSegment.getLength() - nPosition,
+ nLength - sSegmentBuffer.getLength());
+ sSegmentBuffer.append(sSegment.subView(nPosition, nCharsToCopy));
+ if (sSegmentBuffer.getLength() == nLength)
+ return sSegmentBuffer.makeStringAndClear();
+
+ assert(sSegmentBuffer.getLength() < nLength);
+
+ if (bLastRead)
+ throw lang::IllegalArgumentException("out of range", *this, 0);
+
+ nPosition = 0; // No offset after first append
+ }
+}
+
+uno::Reference< XInputStream > SAL_CALL Clob::getCharacterStream()
+{
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(Clob_BASE::rBHelper.bDisposed);
+
+ return m_aBlob->getBinaryStream();
+}
+
+sal_Int64 SAL_CALL Clob::position(const OUString& /*rPattern*/,
+ sal_Int32 /*nStart*/)
+{
+ ::dbtools::throwFeatureNotImplementedSQLException("Clob::position", *this);
+ return 0;
+}
+
+sal_Int64 SAL_CALL Clob::positionOfClob(const Reference <XClob >& /*rPattern*/,
+ sal_Int64 /*aStart*/)
+{
+ ::dbtools::throwFeatureNotImplementedSQLException("Clob::positionOfClob", *this);
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Clob.hxx b/connectivity/source/drivers/firebird/Clob.hxx
new file mode 100644
index 0000000000..7fc5459d5d
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Clob.hxx
@@ -0,0 +1,64 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include "Blob.hxx"
+
+#include <cppuhelper/compbase.hxx>
+
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/sdbc/XClob.hpp>
+#include <rtl/ref.hxx>
+
+namespace connectivity::firebird
+ {
+ typedef ::cppu::WeakComponentImplHelper< css::sdbc::XClob >
+ Clob_BASE;
+
+ class Clob :
+ public Clob_BASE
+ {
+ protected:
+ ::osl::Mutex m_aMutex;
+
+ /*
+ * In Firebird Clob (textual Blob) is a subtype of blob,
+ * hence we store the data in a Blob, and the Clob class is
+ * a wrapper around that.
+ */
+ rtl::Reference<connectivity::firebird::Blob> m_aBlob;
+
+ sal_Int64 m_nCharCount;
+
+ public:
+ Clob(isc_db_handle* pDatabaseHandle,
+ isc_tr_handle* pTransactionHandle,
+ ISC_QUAD const & aBlobID);
+
+ // ---- XClob ----------------------------------------------------
+ virtual sal_Int64 SAL_CALL
+ length() override;
+ virtual OUString SAL_CALL
+ getSubString(sal_Int64 aPosition, sal_Int32 aLength) override;
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL
+ getCharacterStream() override;
+ virtual sal_Int64 SAL_CALL
+ position(const OUString& rPattern,
+ sal_Int32 aStart) override;
+ virtual sal_Int64 SAL_CALL
+ positionOfClob(const ::css::uno::Reference< ::css::sdbc::XClob >& rPattern,
+ sal_Int64 aStart) override;
+ // ---- OComponentHelper ------------------------------------------
+ virtual void SAL_CALL disposing() override;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Column.cxx b/connectivity/source/drivers/firebird/Column.cxx
new file mode 100644
index 0000000000..0a18ebe5b4
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Column.cxx
@@ -0,0 +1,51 @@
+/* -*- 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/.
+ */
+
+#include "Column.hxx"
+
+#include <TConnection.hxx>
+
+using namespace connectivity;
+using namespace connectivity::firebird;
+using namespace connectivity::sdbcx;
+
+Column::Column()
+ : OColumn( true ) // case sensitive
+{
+ construct();
+}
+
+void Column::construct()
+{
+ m_sAutoIncrement = "GENERATED BY DEFAULT AS IDENTITY";
+ registerProperty(OMetaConnection::getPropMap().getNameByIndex(
+ PROPERTY_ID_AUTOINCREMENTCREATION),
+ PROPERTY_ID_AUTOINCREMENTCREATION,
+ 0,
+ &m_sAutoIncrement,
+ cppu::UnoType<decltype(m_sAutoIncrement)>::get()
+ );
+}
+
+::cppu::IPropertyArrayHelper* Column::createArrayHelper( sal_Int32 /*_nId*/ ) const
+{
+ return doCreateArrayHelper();
+}
+
+::cppu::IPropertyArrayHelper & SAL_CALL Column::getInfoHelper()
+{
+ return *Column_PROP::getArrayHelper(isNew() ? 1 : 0);
+}
+
+css::uno::Sequence< OUString > SAL_CALL Column::getSupportedServiceNames( )
+{
+ return { "com.sun.star.sdbc.Firebird" };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Column.hxx b/connectivity/source/drivers/firebird/Column.hxx
new file mode 100644
index 0000000000..c66287ce56
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Column.hxx
@@ -0,0 +1,32 @@
+/* -*- 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/.
+ */
+#pragma once
+
+#include <connectivity/sdbcx/VColumn.hxx>
+
+namespace connectivity::firebird
+ {
+ class Column;
+ typedef ::comphelper::OIdPropertyArrayUsageHelper<Column> Column_PROP;
+ class Column : public sdbcx::OColumn,
+ public Column_PROP
+ {
+ OUString m_sAutoIncrement;
+ protected:
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( sal_Int32 _nId) const override;
+ virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override;
+ public:
+ Column();
+ virtual void construct() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Columns.cxx b/connectivity/source/drivers/firebird/Columns.cxx
new file mode 100644
index 0000000000..200eec1fb5
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Columns.cxx
@@ -0,0 +1,41 @@
+/* -*- 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/.
+ */
+
+#include "Columns.hxx"
+#include "Column.hxx"
+
+using namespace ::connectivity;
+using namespace ::connectivity::firebird;
+using namespace ::connectivity::sdbcx;
+
+using namespace ::cppu;
+using namespace ::osl;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::uno;
+
+Columns::Columns(Table& rTable,
+ Mutex& rMutex,
+ const ::std::vector< OUString>& rVector):
+ OColumnsHelper(rTable,
+ true, // TODO: is this case sensitivity?
+ rMutex,
+ rVector,
+ /*bUseHardRef*/true)
+{
+ OColumnsHelper::setParent(&rTable);
+}
+
+Reference< css::beans::XPropertySet > Columns::createDescriptor()
+{
+ return new Column;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Columns.hxx b/connectivity/source/drivers/firebird/Columns.hxx
new file mode 100644
index 0000000000..a211f70d18
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Columns.hxx
@@ -0,0 +1,30 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include "Table.hxx"
+
+#include <connectivity/TColumnsHelper.hxx>
+
+namespace connectivity::firebird
+{
+ class Columns: public ::connectivity::OColumnsHelper
+ {
+ protected:
+ virtual css::uno::Reference< css::beans::XPropertySet > createDescriptor() override;
+ public:
+ Columns(Table& rTable,
+ ::osl::Mutex& rMutex,
+ const ::std::vector< OUString> &_rVector);
+ };
+
+} // namespace connectivity::firebird
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Connection.cxx b/connectivity/source/drivers/firebird/Connection.cxx
new file mode 100644
index 0000000000..d0008272e1
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Connection.cxx
@@ -0,0 +1,979 @@
+/* -*- 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 "Blob.hxx"
+#include "Catalog.hxx"
+#include "Clob.hxx"
+#include "Connection.hxx"
+#include "DatabaseMetaData.hxx"
+#include "PreparedStatement.hxx"
+#include "Statement.hxx"
+#include "Util.hxx"
+
+#include <stdexcept>
+
+#include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/sdbc/TransactionIsolation.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/ucb/XSimpleFileAccess2.hpp>
+
+#include <connectivity/dbexception.hxx>
+#include <strings.hrc>
+#include <resource/sharedresources.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <unotools/tempfile.hxx>
+
+#include <osl/file.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+
+using namespace connectivity::firebird;
+using namespace connectivity;
+
+using namespace ::osl;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::embed;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::uno;
+
+/**
+ * Location within the .odb that an embedded .fdb will be stored.
+ * Only relevant for embedded dbs.
+ */
+constexpr OUString our_sFDBLocation( u"firebird.fdb"_ustr );
+/**
+ * Older version of LO may store the database in a .fdb file
+ */
+constexpr OUString our_sFBKLocation( u"firebird.fbk"_ustr );
+
+Connection::Connection()
+ : Connection_BASE(m_aMutex)
+ , m_bIsEmbedded(false)
+ , m_bIsFile(false)
+ , m_bIsAutoCommit(true)
+ , m_bIsReadOnly(false)
+ , m_aTransactionIsolation(TransactionIsolation::REPEATABLE_READ)
+#if SAL_TYPES_SIZEOFPOINTER == 8
+ , m_aDBHandle(0)
+ , m_aTransactionHandle(0)
+#else
+ , m_aDBHandle(nullptr)
+ , m_aTransactionHandle(nullptr)
+#endif
+ , m_xCatalog(nullptr)
+ , m_xMetaData(nullptr)
+{
+}
+
+Connection::~Connection()
+{
+ if(!isClosed())
+ close();
+}
+
+namespace {
+
+struct ConnectionGuard
+{
+ oslInterlockedCount& m_refCount;
+ explicit ConnectionGuard(oslInterlockedCount& refCount)
+ : m_refCount(refCount)
+ {
+ osl_atomic_increment(&m_refCount);
+ }
+ ~ConnectionGuard()
+ {
+ osl_atomic_decrement(&m_refCount);
+ }
+};
+
+}
+
+void Connection::construct(const OUString& url, const Sequence< PropertyValue >& info)
+{
+ ConnectionGuard aGuard(m_refCount);
+
+ try
+ {
+ m_sConnectionURL = url;
+
+ bool bIsNewDatabase = false;
+ // the database may be stored as an
+ // fdb file in older versions
+ bool bIsFdbStored = false;
+ if (url == "sdbc:embedded:firebird")
+ {
+ m_bIsEmbedded = true;
+
+ const PropertyValue* pIter = info.getConstArray();
+ const PropertyValue* pEnd = pIter + info.getLength();
+
+ for (;pIter != pEnd; ++pIter)
+ {
+ if ( pIter->Name == "Storage" )
+ {
+ m_xEmbeddedStorage.set(pIter->Value,UNO_QUERY);
+ }
+ else if ( pIter->Name == "Document" )
+ {
+ pIter->Value >>= m_xParentDocument;
+ }
+ }
+
+ if ( !m_xEmbeddedStorage.is() )
+ {
+ ::connectivity::SharedResources aResources;
+ const OUString sMessage = aResources.getResourceString(STR_NO_STORAGE);
+ ::dbtools::throwGenericSQLException(sMessage ,*this);
+ }
+
+ bIsNewDatabase = !m_xEmbeddedStorage->hasElements();
+
+ m_pDatabaseFileDir.reset(new ::utl::TempFileNamed(nullptr, true));
+ m_pDatabaseFileDir->EnableKillingFile();
+ m_sFirebirdURL = m_pDatabaseFileDir->GetFileName() + "/firebird.fdb";
+ m_sFBKPath = m_pDatabaseFileDir->GetFileName() + "/firebird.fbk";
+
+ SAL_INFO("connectivity.firebird", "Temporary .fdb location: " << m_sFirebirdURL);
+
+ if (!bIsNewDatabase)
+ {
+ if (m_xEmbeddedStorage->hasByName(our_sFBKLocation) &&
+ m_xEmbeddedStorage->isStreamElement(our_sFBKLocation))
+ {
+ SAL_INFO("connectivity.firebird", "Extracting* .fbk from .odb" );
+ loadDatabaseFile(our_sFBKLocation, m_sFBKPath);
+ }
+ else if(m_xEmbeddedStorage->hasByName(our_sFDBLocation) &&
+ m_xEmbeddedStorage->isStreamElement(our_sFDBLocation))
+ {
+ SAL_INFO("connectivity.firebird", "Found .fdb instead of .fbk");
+ bIsFdbStored = true;
+ loadDatabaseFile(our_sFDBLocation, m_sFirebirdURL);
+ }
+ else
+ {
+ // There might be files which are not firebird databases.
+ // This is not a problem.
+ bIsNewDatabase = true;
+ }
+ }
+ // TODO: Get DB properties from XML
+
+ }
+ // External file AND/OR remote connection
+ else if (url.startsWith("sdbc:firebird:"))
+ {
+ m_sFirebirdURL = url.copy(strlen("sdbc:firebird:"));
+ if (m_sFirebirdURL.startsWith("file://"))
+ {
+ m_bIsFile = true;
+ uno::Reference< ucb::XSimpleFileAccess > xFileAccess =
+ ucb::SimpleFileAccess::create(comphelper::getProcessComponentContext());
+ if (!xFileAccess->exists(m_sFirebirdURL))
+ bIsNewDatabase = true;
+
+ osl::FileBase::getSystemPathFromFileURL(m_sFirebirdURL, m_sFirebirdURL);
+ }
+ }
+
+ std::string dpbBuffer;
+ {
+ OString userName;
+ OString userPassword;
+
+ dpbBuffer.push_back(isc_dpb_version1);
+ dpbBuffer.push_back(isc_dpb_sql_dialect);
+ dpbBuffer.push_back(1); // 1 byte long
+ dpbBuffer.push_back(SQL_DIALECT_CURRENT);
+
+ // set UTF8 as default character set of the database
+ const char sCharset[] = "UTF8";
+ dpbBuffer.push_back(isc_dpb_set_db_charset);
+ dpbBuffer.push_back(sizeof(sCharset) - 1);
+ dpbBuffer.append(sCharset);
+ // set UTF8 as default character set of the connection
+ dpbBuffer.push_back(isc_dpb_lc_ctype);
+ dpbBuffer.push_back(sizeof(sCharset) - 1);
+ dpbBuffer.append(sCharset);
+
+ // Do any more dpbBuffer additions here
+
+ if (m_bIsEmbedded || m_bIsFile)
+ {
+ userName = "sysdba"_ostr;
+ userPassword = "masterkey"_ostr;
+ }
+ else
+ {
+ for (const auto& rIter : info)
+ {
+ if (rIter.Name == "user")
+ {
+ if (OUString value; rIter.Value >>= value)
+ userName = OUStringToOString(value, RTL_TEXTENCODING_UTF8);
+ }
+ else if (rIter.Name == "password")
+ {
+ if (OUString value; rIter.Value >>= value)
+ userPassword = OUStringToOString(value, RTL_TEXTENCODING_UTF8);
+ }
+ }
+ }
+
+ if (!userName.isEmpty())
+ {
+ const sal_Int32 nMaxUsername = 255; //max size
+ int nUsernameLength = std::min(userName.getLength(), nMaxUsername);
+ dpbBuffer.push_back(isc_dpb_user_name);
+ dpbBuffer.push_back(nUsernameLength);
+ dpbBuffer.append(userName.getStr(), nUsernameLength);
+ }
+
+ if (!userPassword.isEmpty())
+ {
+ const sal_Int32 nMaxPassword = 255; //max size
+ int nPasswordLength = std::min(userPassword.getLength(), nMaxPassword);
+ dpbBuffer.push_back(isc_dpb_password);
+ dpbBuffer.push_back(nPasswordLength);
+ dpbBuffer.append(userPassword.getStr(), nPasswordLength);
+ }
+ }
+
+ // use isc_dpb_utf8_filename to identify encoding of filenames
+ dpbBuffer.push_back(isc_dpb_utf8_filename);
+ dpbBuffer.push_back(0); // no filename here, it is passed to functions directly
+
+ ISC_STATUS_ARRAY status; /* status vector */
+ ISC_STATUS aErr;
+ const OString sFirebirdURL = OUStringToOString(m_sFirebirdURL, RTL_TEXTENCODING_UTF8);
+ if (bIsNewDatabase)
+ {
+ aErr = isc_create_database(status,
+ sFirebirdURL.getLength(),
+ sFirebirdURL.getStr(),
+ &m_aDBHandle,
+ dpbBuffer.size(),
+ dpbBuffer.c_str(),
+ 0);
+ if (aErr)
+ {
+ evaluateStatusVector(status, u"isc_create_database", *this);
+ }
+ }
+ else
+ {
+ if (m_bIsEmbedded && !bIsFdbStored) // We need to restore the .fbk first
+ {
+ runBackupService(isc_action_svc_restore);
+ }
+
+ aErr = isc_attach_database(status,
+ sFirebirdURL.getLength(),
+ sFirebirdURL.getStr(),
+ &m_aDBHandle,
+ dpbBuffer.size(),
+ dpbBuffer.c_str());
+ if (aErr)
+ {
+ evaluateStatusVector(status, u"isc_attach_database", *this);
+ }
+ }
+
+ if (m_bIsEmbedded) // Add DocumentEventListener to save the .fdb as needed
+ {
+ // We need to attach as a document listener in order to be able to store
+ // the temporary db back into the .odb when saving
+ uno::Reference<XDocumentEventBroadcaster> xBroadcaster(m_xParentDocument, UNO_QUERY);
+
+ if (xBroadcaster.is())
+ xBroadcaster->addDocumentEventListener(this);
+ else
+ assert(false);
+ }
+ }
+ catch (const Exception&)
+ {
+ throw;
+ }
+ catch (const std::exception&)
+ {
+ throw;
+ }
+ catch (...) // const Firebird::Exception& firebird throws this, but doesn't install the fb_exception.h that declares it
+
+ {
+ throw std::runtime_error("Generic Firebird::Exception");
+ }
+}
+
+void Connection::notifyDatabaseModified()
+{
+ if (m_xParentDocument.is()) // Only true in embedded mode
+ m_xParentDocument->setModified(true);
+}
+
+//----- XServiceInfo ---------------------------------------------------------
+IMPLEMENT_SERVICE_INFO(Connection, "com.sun.star.sdbc.drivers.firebird.Connection",
+ "com.sun.star.sdbc.Connection")
+
+Reference< XBlob> Connection::createBlob(ISC_QUAD const * pBlobId)
+{
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ Reference< XBlob > xReturn = new Blob(&m_aDBHandle,
+ &m_aTransactionHandle,
+ *pBlobId);
+
+ m_aStatements.push_back(WeakReferenceHelper(xReturn));
+ return xReturn;
+}
+
+Reference< XClob> Connection::createClob(ISC_QUAD const * pBlobId)
+{
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ Reference< XClob > xReturn = new Clob(&m_aDBHandle,
+ &m_aTransactionHandle,
+ *pBlobId);
+
+ m_aStatements.push_back(WeakReferenceHelper(xReturn));
+ return xReturn;
+}
+
+//----- XUnoTunnel ----------------------------------------------------------
+// virtual
+sal_Int64 SAL_CALL Connection::getSomething(const css::uno::Sequence<sal_Int8>& rId)
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+// static
+const css::uno::Sequence<sal_Int8> & Connection::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit implId;
+ return implId.getSeq();
+}
+
+//----- XConnection ----------------------------------------------------------
+Reference< XStatement > SAL_CALL Connection::createStatement( )
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ // the pre
+ if(m_aTypeInfo.empty())
+ buildTypeInfo();
+
+ // create a statement
+ // the statement can only be executed once
+ Reference< XStatement > xReturn = new OStatement(this);
+ m_aStatements.push_back(WeakReferenceHelper(xReturn));
+ return xReturn;
+}
+
+Reference< XPreparedStatement > SAL_CALL Connection::prepareStatement(
+ const OUString& _sSql)
+{
+ SAL_INFO("connectivity.firebird", "prepareStatement() "
+ "called with sql: " << _sSql);
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ if(m_aTypeInfo.empty())
+ buildTypeInfo();
+
+ Reference< XPreparedStatement > xReturn = new OPreparedStatement(this, _sSql);
+ m_aStatements.push_back(WeakReferenceHelper(xReturn));
+
+ return xReturn;
+}
+
+Reference< XPreparedStatement > SAL_CALL Connection::prepareCall(
+ const OUString& _sSql )
+{
+ SAL_INFO("connectivity.firebird", "prepareCall(). "
+ "_sSql: " << _sSql);
+
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ // OUString sSqlStatement (transformPreparedStatement( _sSql ));
+
+ // not implemented yet :-) a task to do
+ return nullptr;
+}
+
+OUString SAL_CALL Connection::nativeSQL( const OUString& _sSql )
+{
+ // We do not need to adapt the SQL for Firebird atm.
+ return _sSql;
+}
+
+void SAL_CALL Connection::setAutoCommit( sal_Bool autoCommit )
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ m_bIsAutoCommit = autoCommit;
+
+ if (m_aTransactionHandle)
+ {
+ setupTransaction();
+ }
+}
+
+sal_Bool SAL_CALL Connection::getAutoCommit()
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ return m_bIsAutoCommit;
+}
+
+void Connection::setupTransaction()
+{
+ MutexGuard aGuard( m_aMutex );
+ ISC_STATUS status_vector[20];
+
+ // TODO: is this sensible? If we have changed parameters then transaction
+ // is lost...
+ if (m_aTransactionHandle)
+ {
+ disposeStatements();
+ isc_rollback_transaction(status_vector, &m_aTransactionHandle);
+ }
+
+ char aTransactionIsolation = 0;
+ switch (m_aTransactionIsolation)
+ {
+ // TODO: confirm that these are correct.
+ case TransactionIsolation::READ_UNCOMMITTED:
+ aTransactionIsolation = isc_tpb_concurrency;
+ break;
+ case TransactionIsolation::READ_COMMITTED:
+ aTransactionIsolation = isc_tpb_read_committed;
+ break;
+ case TransactionIsolation::REPEATABLE_READ:
+ aTransactionIsolation = isc_tpb_consistency;
+ break;
+ case TransactionIsolation::SERIALIZABLE:
+ aTransactionIsolation = isc_tpb_consistency;
+ break;
+ default:
+ assert( false ); // We must have a valid TransactionIsolation.
+ }
+
+ // You cannot pass an empty tpb parameter so we have to do some pointer
+ // arithmetic to avoid problems. (i.e. aTPB[x] = 0 is invalid)
+ char aTPB[5];
+ char* pTPB = aTPB;
+
+ *pTPB++ = isc_tpb_version3;
+ if (m_bIsAutoCommit)
+ *pTPB++ = isc_tpb_autocommit;
+ *pTPB++ = (!m_bIsReadOnly ? isc_tpb_write : isc_tpb_read);
+ *pTPB++ = aTransactionIsolation;
+ *pTPB++ = isc_tpb_wait;
+
+ isc_start_transaction(status_vector,
+ &m_aTransactionHandle,
+ 1,
+ &m_aDBHandle,
+ pTPB - aTPB, // bytes used in TPB
+ aTPB);
+
+ evaluateStatusVector(status_vector,
+ u"isc_start_transaction",
+ *this);
+}
+
+isc_tr_handle& Connection::getTransaction()
+{
+ MutexGuard aGuard( m_aMutex );
+ if (!m_aTransactionHandle)
+ {
+ setupTransaction();
+ }
+ return m_aTransactionHandle;
+}
+
+void SAL_CALL Connection::commit()
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ ISC_STATUS status_vector[20];
+
+ if (!m_bIsAutoCommit && m_aTransactionHandle)
+ {
+ disposeStatements();
+ isc_commit_transaction(status_vector, &m_aTransactionHandle);
+ evaluateStatusVector(status_vector,
+ u"isc_commit_transaction",
+ *this);
+ }
+}
+
+void Connection::loadDatabaseFile(const OUString& srcLocation, const OUString& tmpLocation)
+{
+ Reference< XStream > xDBStream(m_xEmbeddedStorage->openStreamElement(srcLocation,
+ ElementModes::READ));
+
+ uno::Reference< ucb::XSimpleFileAccess2 > xFileAccess =
+ ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() );
+ if ( !xFileAccess.is() )
+ {
+ ::connectivity::SharedResources aResources;
+ // TODO FIXME: this does _not_ look like the right error message
+ const OUString sMessage = aResources.getResourceString(STR_ERROR_NEW_VERSION);
+ ::dbtools::throwGenericSQLException(sMessage ,*this);
+ }
+ xFileAccess->writeFile(tmpLocation,xDBStream->getInputStream());
+}
+
+isc_svc_handle Connection::attachServiceManager()
+{
+ ISC_STATUS_ARRAY aStatusVector;
+#if SAL_TYPES_SIZEOFPOINTER == 8
+ isc_svc_handle aServiceHandle = 0;
+#else
+ isc_svc_handle aServiceHandle = nullptr;
+#endif
+
+ char aSPBBuffer[256];
+ char* pSPB = aSPBBuffer;
+ *pSPB++ = isc_spb_version;
+ *pSPB++ = isc_spb_current_version;
+ *pSPB++ = isc_spb_user_name;
+ OUString sUserName("SYSDBA");
+ char aLength = static_cast<char>(sUserName.getLength());
+ *pSPB++ = aLength;
+ strncpy(pSPB,
+ OUStringToOString(sUserName,
+ RTL_TEXTENCODING_UTF8).getStr(),
+ aLength);
+ pSPB += aLength;
+ // TODO: do we need ", isc_dpb_trusted_auth, 1, 1" -- probably not but ...
+ if (isc_service_attach(aStatusVector,
+ 0, // Denotes null-terminated string next
+ "service_mgr",
+ &aServiceHandle,
+ pSPB - aSPBBuffer,
+ aSPBBuffer))
+ {
+ evaluateStatusVector(aStatusVector,
+ u"isc_service_attach",
+ *this);
+ }
+
+ return aServiceHandle;
+}
+
+void Connection::detachServiceManager(isc_svc_handle aServiceHandle)
+{
+ ISC_STATUS_ARRAY aStatusVector;
+ if (isc_service_detach(aStatusVector,
+ &aServiceHandle))
+ {
+ evaluateStatusVector(aStatusVector,
+ u"isc_service_detach",
+ *this);
+ }
+}
+
+void Connection::runBackupService(const short nAction)
+{
+ assert(nAction == isc_action_svc_backup
+ || nAction == isc_action_svc_restore);
+
+ ISC_STATUS_ARRAY aStatusVector;
+
+ // convert paths to 8-Bit strings
+ OString sFDBPath = OUStringToOString(m_sFirebirdURL, RTL_TEXTENCODING_UTF8);
+ OString sFBKPath = OUStringToOString(m_sFBKPath, RTL_TEXTENCODING_UTF8);
+
+
+ sal_uInt16 nFDBLength = sFDBPath.getLength();
+ sal_uInt16 nFBKLength = sFBKPath.getLength();
+ OStringBuffer aRequest( // byte array
+ OStringChar(static_cast<char>(nAction))
+ + OStringChar(char(isc_spb_dbname)) // .fdb
+ + OStringChar(static_cast<char>(nFDBLength & 0xFF)) // least significant byte first
+ + OStringChar(static_cast<char>((nFDBLength >> 8) & 0xFF))
+ + sFDBPath
+ + OStringChar(char(isc_spb_bkp_file)) // .fbk
+ + OStringChar(static_cast<char>(nFBKLength & 0xFF))
+ + OStringChar(static_cast<char>((nFBKLength >> 8) & 0xFF))
+ + sFBKPath);
+
+ if (nAction == isc_action_svc_restore)
+ {
+ aRequest.append(char(isc_spb_options)); // 4-Byte bitmask
+ char sOptions[4];
+ char * pOptions = sOptions;
+#ifdef _WIN32
+#pragma warning(push)
+#pragma warning(disable: 4310) // cast truncates data
+#endif
+ ADD_SPB_NUMERIC(pOptions, isc_spb_res_create);
+#ifdef _WIN32
+#pragma warning(pop)
+#endif
+ aRequest.append(sOptions, 4);
+ }
+
+ isc_svc_handle aServiceHandle;
+ aServiceHandle = attachServiceManager();
+
+ if (isc_service_start(aStatusVector,
+ &aServiceHandle,
+ nullptr,
+ aRequest.getLength(),
+ aRequest.getStr()))
+ {
+ evaluateStatusVector(aStatusVector, u"isc_service_start", *this);
+ }
+
+ char aInfoSPB = isc_info_svc_line;
+ char aResults[256];
+
+ // query blocks until success or error
+ if(isc_service_query(aStatusVector,
+ &aServiceHandle,
+ nullptr, // Reserved null
+ 0,nullptr, // "send" spb -- size and spb -- not needed?
+ 1,
+ &aInfoSPB,
+ sizeof(aResults),
+ aResults))
+ {
+ evaluateStatusVector(aStatusVector, u"isc_service_query", *this);
+ }
+
+ detachServiceManager(aServiceHandle);
+}
+
+
+void SAL_CALL Connection::rollback()
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ ISC_STATUS status_vector[20];
+
+ if (!m_bIsAutoCommit && m_aTransactionHandle)
+ {
+ isc_rollback_transaction(status_vector, &m_aTransactionHandle);
+ }
+}
+
+sal_Bool SAL_CALL Connection::isClosed( )
+{
+ MutexGuard aGuard( m_aMutex );
+
+ // just simple -> we are close when we are disposed that means someone called dispose(); (XComponent)
+ return Connection_BASE::rBHelper.bDisposed;
+}
+
+Reference< XDatabaseMetaData > SAL_CALL Connection::getMetaData( )
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ // here we have to create the class with biggest interface
+ // The answer is 42 :-)
+ Reference< XDatabaseMetaData > xMetaData = m_xMetaData;
+ if(!xMetaData.is())
+ {
+ xMetaData = new ODatabaseMetaData(this); // need the connection because it can return it
+ m_xMetaData = xMetaData;
+ }
+
+ return xMetaData;
+}
+
+void SAL_CALL Connection::setReadOnly(sal_Bool readOnly)
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ m_bIsReadOnly = readOnly;
+ setupTransaction();
+}
+
+sal_Bool SAL_CALL Connection::isReadOnly()
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ return m_bIsReadOnly;
+}
+
+void SAL_CALL Connection::setCatalog(const OUString& /*catalog*/)
+{
+ ::dbtools::throwFunctionNotSupportedSQLException("setCatalog", *this);
+}
+
+OUString SAL_CALL Connection::getCatalog()
+{
+ ::dbtools::throwFunctionNotSupportedSQLException("getCatalog", *this);
+ return OUString();
+}
+
+void SAL_CALL Connection::setTransactionIsolation( sal_Int32 level )
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ m_aTransactionIsolation = level;
+ setupTransaction();
+}
+
+sal_Int32 SAL_CALL Connection::getTransactionIsolation( )
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ return m_aTransactionIsolation;
+}
+
+Reference< XNameAccess > SAL_CALL Connection::getTypeMap()
+{
+ ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::getTypeMap", *this );
+ return nullptr;
+}
+
+void SAL_CALL Connection::setTypeMap(const Reference< XNameAccess >&)
+{
+ ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTypeMap", *this );
+}
+
+//----- XCloseable -----------------------------------------------------------
+void SAL_CALL Connection::close( )
+{
+ // we just dispose us
+ {
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(Connection_BASE::rBHelper.bDisposed);
+
+ }
+ dispose();
+}
+
+// XWarningsSupplier
+Any SAL_CALL Connection::getWarnings( )
+{
+ // when you collected some warnings -> return it
+ return Any();
+}
+
+void SAL_CALL Connection::clearWarnings( )
+{
+ // you should clear your collected warnings here
+}
+
+// XDocumentEventListener
+void SAL_CALL Connection::documentEventOccured( const DocumentEvent& Event )
+{
+ MutexGuard aGuard(m_aMutex);
+
+ if (!m_bIsEmbedded)
+ return;
+
+ if (Event.EventName != "OnSave" && Event.EventName != "OnSaveAs")
+ return;
+
+ commit(); // Commit and close transaction
+ if ( !(m_bIsEmbedded && m_xEmbeddedStorage.is()) )
+ return;
+
+ SAL_INFO("connectivity.firebird", "Writing .fbk from running db");
+ try
+ {
+ runBackupService(isc_action_svc_backup);
+ }
+ catch (const SQLException& e)
+ {
+ auto a = cppu::getCaughtException();
+ throw WrappedTargetRuntimeException(e.Message, e.Context, a);
+ }
+
+
+ Reference< XStream > xDBStream(m_xEmbeddedStorage->openStreamElement(our_sFBKLocation,
+ ElementModes::WRITE));
+
+ // TODO: verify the backup actually exists -- the backup service
+ // can fail without giving any sane error messages / telling us
+ // that it failed.
+ using namespace ::comphelper;
+ Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
+ Reference< XInputStream > xInputStream;
+ if (!xContext.is())
+ return;
+
+ xInputStream =
+ OStorageHelper::GetInputStreamFromURL(m_sFBKPath, xContext);
+ if (xInputStream.is())
+ OStorageHelper::CopyInputToOutput( xInputStream,
+ xDBStream->getOutputStream());
+
+ // remove old fdb file if exists
+ uno::Reference< ucb::XSimpleFileAccess > xFileAccess =
+ ucb::SimpleFileAccess::create(xContext);
+ if (xFileAccess->exists(m_sFirebirdURL))
+ xFileAccess->kill(m_sFirebirdURL);
+}
+// XEventListener
+void SAL_CALL Connection::disposing(const EventObject& /*rSource*/)
+{
+ MutexGuard aGuard( m_aMutex );
+
+ m_xEmbeddedStorage.clear();
+}
+
+void Connection::buildTypeInfo()
+{
+ MutexGuard aGuard( m_aMutex );
+
+ Reference< XResultSet> xRs = getMetaData ()->getTypeInfo ();
+ Reference< XRow> xRow(xRs,UNO_QUERY);
+ // Information for a single SQL type
+
+ // Loop on the result set until we reach end of file
+
+ while (xRs->next ())
+ {
+ OTypeInfo aInfo;
+ aInfo.aTypeName = xRow->getString (1);
+ aInfo.nType = xRow->getShort (2);
+ aInfo.nPrecision = xRow->getInt (3);
+ // aLiteralPrefix = xRow->getString (4);
+ // aLiteralSuffix = xRow->getString (5);
+ // aCreateParams = xRow->getString (6);
+ // bNullable = xRow->getBoolean (7);
+ // bCaseSensitive = xRow->getBoolean (8);
+ // nSearchType = xRow->getShort (9);
+ // bUnsigned = xRow->getBoolean (10);
+ // bCurrency = xRow->getBoolean (11);
+ // bAutoIncrement = xRow->getBoolean (12);
+ aInfo.aLocalTypeName = xRow->getString (13);
+ // nMinimumScale = xRow->getShort (14);
+ aInfo.nMaximumScale = xRow->getShort (15);
+ // nNumPrecRadix = (sal_Int16)xRow->getInt(18);
+
+
+ // Now that we have the type info, save it
+ // in the Hashtable if we don't already have an
+ // entry for this SQL type.
+
+ m_aTypeInfo.push_back(aInfo);
+ }
+
+ SAL_INFO("connectivity.firebird", "buildTypeInfo(). "
+ "Type info built.");
+
+ // Close the result set/statement.
+
+ Reference< XCloseable> xClose(xRs,UNO_QUERY);
+ xClose->close();
+
+ SAL_INFO("connectivity.firebird", "buildTypeInfo(). "
+ "Closed.");
+}
+
+void Connection::disposing()
+{
+ MutexGuard aGuard(m_aMutex);
+
+ disposeStatements();
+
+ m_xMetaData = css::uno::WeakReference< css::sdbc::XDatabaseMetaData>();
+
+ ISC_STATUS_ARRAY status; /* status vector */
+ if (m_aTransactionHandle)
+ {
+ // TODO: confirm whether we need to ask the user here.
+ isc_rollback_transaction(status, &m_aTransactionHandle);
+ }
+
+ if (m_aDBHandle)
+ {
+ if (isc_detach_database(status, &m_aDBHandle))
+ {
+ evaluateStatusVector(status, u"isc_detach_database", *this);
+ }
+ }
+ // TODO: write to storage again?
+
+ cppu::WeakComponentImplHelperBase::disposing();
+
+ m_pDatabaseFileDir.reset();
+}
+
+void Connection::disposeStatements()
+{
+ MutexGuard aGuard(m_aMutex);
+ for (auto const& statement : m_aStatements)
+ {
+ Reference< XComponent > xComp(statement.get(), UNO_QUERY);
+ if (xComp.is())
+ xComp->dispose();
+ }
+ m_aStatements.clear();
+}
+
+uno::Reference< XTablesSupplier > Connection::createCatalog()
+{
+ MutexGuard aGuard(m_aMutex);
+
+ // m_xCatalog is a weak reference. Reuse it if it still exists.
+ Reference< XTablesSupplier > xCatalog = m_xCatalog;
+ if (xCatalog.is())
+ {
+ return xCatalog;
+ }
+ else
+ {
+ xCatalog = new Catalog(this);
+ m_xCatalog = xCatalog;
+ return m_xCatalog;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Connection.hxx b/connectivity/source/drivers/firebird/Connection.hxx
new file mode 100644
index 0000000000..fa896439c9
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Connection.hxx
@@ -0,0 +1,247 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <ibase.h>
+
+#include <connectivity/CommonTools.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <memory>
+#include <OTypeInfo.hxx>
+#include <unotools/tempfile.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/document/DocumentEvent.hpp>
+#include <com/sun/star/document/XDocumentEventListener.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/sdbc/XBlob.hpp>
+#include <com/sun/star/sdbc/XClob.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbc/XWarningsSupplier.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+
+namespace connectivity::firebird
+ {
+
+ typedef ::cppu::WeakComponentImplHelper< css::document::XDocumentEventListener,
+ css::lang::XServiceInfo,
+ css::lang::XUnoTunnel,
+ css::sdbc::XConnection,
+ css::sdbc::XWarningsSupplier
+ > Connection_BASE;
+
+ class OStatementCommonBase;
+ class FirebirdDriver;
+ class ODatabaseMetaData;
+
+
+ typedef std::vector< ::connectivity::OTypeInfo> TTypeInfoVector;
+ typedef std::vector< css::uno::WeakReferenceHelper > OWeakRefArray;
+
+ class Connection final : public Connection_BASE
+ {
+ ::osl::Mutex m_aMutex;
+
+ TTypeInfoVector m_aTypeInfo; // vector containing an entry
+ // for each row returned by
+ // DatabaseMetaData.getTypeInfo.
+
+ /** The URL passed to us when opening, i.e. of the form sdbc:* */
+ OUString m_sConnectionURL;
+ /**
+ * The URL passed to firebird, i.e. either a local file (for a
+ * temporary .fdb extracted from a .odb or a normal local file) or
+ * a remote url.
+ */
+ OUString m_sFirebirdURL;
+
+ /* EMBEDDED MODE DATA */
+ /** Denotes that we have a database stored within a .odb file. */
+ bool m_bIsEmbedded;
+
+ /**
+ * Handle for the parent DatabaseDocument. We need to notify this
+ * whenever any data is written to our temporary database so that
+ * the user is able to save this back to the .odb file.
+ *
+ * Note that this is ONLY set in embedded mode.
+ */
+ css::uno::Reference< css::util::XModifiable >
+ m_xParentDocument;
+
+ /**
+ * Handle for the folder within the .odb where we store our .fbk
+ * (Only used if m_bIsEmbedded is true).
+ */
+ css::uno::Reference< css::embed::XStorage >
+ m_xEmbeddedStorage;
+ /**
+ * The temporary folder where we extract the .fbk from a .odb,
+ * and also store the temporary .fdb
+ * It is only valid if m_bIsEmbedded is true.
+ *
+ * The extracted .fbk is written in firebird.fbk, the temporary
+ * .fdb is stored as firebird.fdb.
+ */
+ std::unique_ptr< ::utl::TempFileNamed > m_pDatabaseFileDir;
+ /**
+ * Path for our extracted .fbk file.
+ *
+ * (The temporary .fdb is our m_sFirebirdURL.)
+ */
+ OUString m_sFBKPath;
+
+ void loadDatabaseFile(const OUString& pSrcLocation, const OUString& pTmpLocation);
+
+ /**
+ * Run the backup service, use nAction =
+ * isc_action_svc_backup to backup, nAction = isc_action_svc_restore
+ * to restore.
+ */
+ void runBackupService(const short nAction);
+
+ isc_svc_handle attachServiceManager();
+
+ void detachServiceManager(isc_svc_handle pServiceHandle);
+
+ /** We are using an external (local) file */
+ bool m_bIsFile;
+
+ /* CONNECTION PROPERTIES */
+ bool m_bIsAutoCommit;
+ bool m_bIsReadOnly;
+ sal_Int32 m_aTransactionIsolation;
+
+ isc_db_handle m_aDBHandle;
+ isc_tr_handle m_aTransactionHandle;
+
+ css::uno::WeakReference< css::sdbcx::XTablesSupplier>
+ m_xCatalog;
+ css::uno::WeakReference< css::sdbc::XDatabaseMetaData >
+ m_xMetaData;
+ /** Statements owned by this connection. */
+ OWeakRefArray m_aStatements;
+
+ /// @throws css::sdbc::SQLException
+ void buildTypeInfo();
+
+ /**
+ * Creates a new transaction with the desired parameters, if
+ * necessary discarding an existing transaction. This has to be done
+ * anytime we change the transaction isolation, or autocommitting.
+ *
+ * @throws css::sdbc::SQLException
+ */
+ void setupTransaction();
+ void disposeStatements();
+
+ public:
+ explicit Connection();
+ virtual ~Connection() override;
+
+ /// @throws css::sdbc::SQLException
+ /// @throws css::uno::RuntimeException
+ void construct( const OUString& url,
+ const css::uno::Sequence< css::beans::PropertyValue >& info);
+
+ const OUString& getConnectionURL() const {return m_sConnectionURL;}
+ bool isEmbedded() const {return m_bIsEmbedded;}
+ isc_db_handle& getDBHandle() {return m_aDBHandle;}
+ /// @throws css::sdbc::SQLException
+ isc_tr_handle& getTransaction();
+
+ /**
+ * Must be called anytime the underlying database is likely to have
+ * changed.
+ *
+ * This is used to notify the database document of any changes, so
+ * that the user is informed of any pending changes needing to be
+ * saved.
+ */
+ void notifyDatabaseModified();
+
+ /**
+ * Create a new Blob tied to this connection. Blobs are tied to a
+ * transaction and not to a statement, hence the connection should
+ * deal with their management.
+ *
+ * @throws css::sdbc::SQLException
+ * @throws css::uno::RuntimeException
+ */
+ css::uno::Reference< css::sdbc::XBlob>
+ createBlob(ISC_QUAD const * pBlobID);
+ /// @throws css::sdbc::SQLException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::sdbc::XClob>
+ createClob(ISC_QUAD const * pBlobID);
+
+ /**
+ * Create and/or connect to the sdbcx Catalog. This is completely
+ * unrelated to the SQL "Catalog".
+ */
+ css::uno::Reference< css::sdbcx::XTablesSupplier >
+ createCatalog();
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // XServiceInfo
+ DECLARE_SERVICE_INFO();
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(const css::uno::Sequence<sal_Int8>& rId) override;
+ static const css::uno::Sequence<sal_Int8> & getUnoTunnelId();
+ // XConnection
+ virtual css::uno::Reference< css::sdbc::XStatement > SAL_CALL createStatement( ) override;
+ virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareStatement( const OUString& sql ) override;
+ virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareCall( const OUString& sql ) override;
+ virtual OUString SAL_CALL nativeSQL( const OUString& sql ) override;
+ virtual void SAL_CALL setAutoCommit( sal_Bool autoCommit ) override;
+ virtual sal_Bool SAL_CALL getAutoCommit( ) override;
+ virtual void SAL_CALL commit( ) override;
+ virtual void SAL_CALL rollback( ) override;
+ virtual sal_Bool SAL_CALL isClosed( ) override;
+ virtual css::uno::Reference< css::sdbc::XDatabaseMetaData > SAL_CALL getMetaData( ) override;
+ virtual void SAL_CALL setReadOnly( sal_Bool readOnly ) override;
+ virtual sal_Bool SAL_CALL isReadOnly( ) override;
+ virtual void SAL_CALL setCatalog( const OUString& catalog ) override;
+ virtual OUString SAL_CALL getCatalog( ) override;
+ virtual void SAL_CALL setTransactionIsolation( sal_Int32 level ) override;
+ virtual sal_Int32 SAL_CALL getTransactionIsolation( ) override;
+ virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTypeMap( ) override;
+ virtual void SAL_CALL setTypeMap( const css::uno::Reference< css::container::XNameAccess >& typeMap ) override;
+ // XCloseable
+ virtual void SAL_CALL close( ) override;
+ // XWarningsSupplier
+ virtual css::uno::Any SAL_CALL getWarnings( ) override;
+ virtual void SAL_CALL clearWarnings( ) override;
+ // XDocumentEventListener
+ virtual void SAL_CALL documentEventOccured( const css::document::DocumentEvent& Event ) override;
+ // css.lang.XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/DatabaseMetaData.cxx b/connectivity/source/drivers/firebird/DatabaseMetaData.cxx
new file mode 100644
index 0000000000..37c2ffe72c
--- /dev/null
+++ b/connectivity/source/drivers/firebird/DatabaseMetaData.cxx
@@ -0,0 +1,1803 @@
+/* -*- 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 "DatabaseMetaData.hxx"
+#include "Util.hxx"
+
+#include <ibase.h>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <FDatabaseMetaDataResultSet.hxx>
+
+#include <com/sun/star/sdbc/ColumnSearch.hpp>
+#include <com/sun/star/sdbc/ColumnValue.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/IndexType.hpp>
+#include <com/sun/star/sdbc/ResultSetType.hpp>
+#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/TransactionIsolation.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/sdbc/KeyRule.hpp>
+#include <com/sun/star/sdbc/Deferrability.hpp>
+
+using namespace connectivity::firebird;
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::sdbc;
+
+ODatabaseMetaData::ODatabaseMetaData(Connection* _pCon)
+: m_pConnection(_pCon)
+{
+ SAL_WARN_IF(!m_pConnection.is(), "connectivity.firebird",
+ "ODatabaseMetaData::ODatabaseMetaData: No connection set!");
+}
+
+ODatabaseMetaData::~ODatabaseMetaData()
+{
+}
+
+//----- Catalog Info -- UNSUPPORTED -------------------------------------------
+OUString SAL_CALL ODatabaseMetaData::getCatalogSeparator()
+{
+ return OUString();
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCatalogNameLength()
+{
+ return -1;
+}
+
+OUString SAL_CALL ODatabaseMetaData::getCatalogTerm()
+{
+ return OUString();
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::isCatalogAtStart()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInTableDefinitions()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInIndexDefinitions()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInDataManipulation( )
+{
+ return false;
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getCatalogs()
+{
+ OSL_FAIL("Not implemented yet!");
+ // TODO implement
+ return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eCatalogs);
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInProcedureCalls()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInPrivilegeDefinitions()
+{
+ return false;
+}
+
+//----- Schema Info -- UNSUPPORTED --------------------------------------------
+sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInProcedureCalls()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInPrivilegeDefinitions()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInDataManipulation()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInIndexDefinitions()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInTableDefinitions()
+{
+ return false;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxSchemaNameLength()
+{
+ return -1;
+}
+
+OUString SAL_CALL ODatabaseMetaData::getSchemaTerm()
+{
+ return OUString();
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getSchemas()
+{
+ OSL_FAIL("Not implemented yet!");
+ // TODO implement
+ return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eSchemas);
+}
+
+//----- Max Sizes/Lengths -----------------------------------------------------
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxBinaryLiteralLength()
+{
+ return 32767;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxRowSize()
+{
+ return 32767;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCharLiteralLength()
+{
+ return 32767;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnNameLength()
+{
+ return 31;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInIndex()
+{
+ // TODO: No idea.
+ // See: http://www.firebirdsql.org/en/firebird-technical-specifications/
+ return 16;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCursorNameLength()
+{
+ return 32;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxConnections()
+{
+ return 100; // Arbitrary
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInTable()
+{
+ // May however be smaller.
+ // See: http://www.firebirdsql.org/en/firebird-technical-specifications/
+ return 32767;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxStatementLength()
+{
+ return 32767;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxTableNameLength()
+{
+ return 31;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxTablesInSelect( )
+{
+ return 0; // 0 means no limit
+}
+
+
+sal_Bool SAL_CALL ODatabaseMetaData::doesMaxRowSizeIncludeBlobs( )
+{
+ return false;
+}
+
+// ---- Identifiers -----------------------------------------------------------
+// Only quoted identifiers are case sensitive, unquoted are case insensitive
+OUString SAL_CALL ODatabaseMetaData::getIdentifierQuoteString()
+{
+ return "\"";
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsMixedCaseQuotedIdentifiers( )
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseQuotedIdentifiers()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::storesMixedCaseQuotedIdentifiers()
+{
+ // TODO: confirm this -- the documentation is highly ambiguous
+ // However it seems this should be true as quoted identifiers ARE
+ // stored mixed case.
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseQuotedIdentifiers()
+{
+ return false;
+}
+
+// ---- Unquoted Identifiers -------------------------------------------------
+// All unquoted identifiers are stored upper case.
+sal_Bool SAL_CALL ODatabaseMetaData::supportsMixedCaseIdentifiers()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseIdentifiers()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::storesMixedCaseIdentifiers()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseIdentifiers()
+{
+ return true;
+}
+
+// ---- SQL Feature Support ---------------------------------------------------
+sal_Bool SAL_CALL ODatabaseMetaData::supportsCoreSQLGrammar()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsMinimumSQLGrammar()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsAlterTableWithAddColumn()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsAlterTableWithDropColumn()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsPositionedDelete()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsPositionedUpdate()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsOuterJoins()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsSelectForUpdate()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::allTablesAreSelectable()
+{
+ // TODO: true if embedded, but unsure about remote server
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsConvert(sal_Int32,
+ sal_Int32)
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsTypeConversion()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsColumnAliasing()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsTableCorrelationNames()
+{
+ return true;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxIndexLength( )
+{
+ return 0; // 0 means no limit
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsNonNullableColumns( )
+{
+ return true;
+}
+
+OUString SAL_CALL ODatabaseMetaData::getExtraNameCharacters( )
+{
+ return OUString();
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsDifferentTableCorrelationNames( )
+{
+ return false;
+}
+// ---- Data definition stuff -------------------------------------------------
+sal_Bool SAL_CALL ODatabaseMetaData::dataDefinitionIgnoredInTransactions()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::dataDefinitionCausesTransactionCommit()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsDataManipulationTransactionsOnly()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::
+ supportsDataDefinitionAndDataManipulationTransactions()
+{
+ return false;
+}
+//----- Transaction Support --------------------------------------------------
+sal_Bool SAL_CALL ODatabaseMetaData::supportsTransactions()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenStatementsAcrossRollback()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenStatementsAcrossCommit()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenCursorsAcrossCommit()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenCursorsAcrossRollback()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsMultipleTransactions()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsTransactionIsolationLevel(
+ sal_Int32 aLevel)
+{
+ return aLevel == TransactionIsolation::READ_UNCOMMITTED
+ || aLevel == TransactionIsolation::READ_COMMITTED
+ || aLevel == TransactionIsolation::REPEATABLE_READ
+ || aLevel == TransactionIsolation::SERIALIZABLE;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getDefaultTransactionIsolation()
+{
+ return TransactionIsolation::REPEATABLE_READ;
+}
+
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92FullSQL( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92EntryLevelSQL( )
+{
+ return true; // should be supported at least
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsIntegrityEnhancementFacility( )
+{
+ return true;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxStatements( )
+{
+ return 0; // 0 means no limit
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxProcedureNameLength( )
+{
+ return 31; // TODO: confirm
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::allProceduresAreCallable( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsStoredProcedures( )
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::isReadOnly( )
+{
+ return m_pConnection->isReadOnly();
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFiles( )
+{
+ return m_pConnection->isEmbedded();
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFilePerTable( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::nullPlusNonNullIsNull( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsExpressionsInOrderBy( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupBy( )
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByBeyondSelect( )
+{
+ // Unsure
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByUnrelated( )
+{
+ // Unsure
+ return false;
+}
+
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsMultipleResultSets( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsLikeEscapeClause( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsOrderByUnrelated( )
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsUnion( )
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsUnionAll( )
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtEnd( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtStart( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedHigh( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedLow( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsCorrelatedSubqueries( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInComparisons( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInExists( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInIns( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInQuantifieds( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92IntermediateSQL( )
+{
+ return false;
+}
+
+OUString SAL_CALL ODatabaseMetaData::getURL()
+{
+ return m_pConnection->getConnectionURL();
+}
+
+OUString SAL_CALL ODatabaseMetaData::getUserName( )
+{
+ return OUString();
+}
+
+OUString SAL_CALL ODatabaseMetaData::getDriverName( )
+{
+ return OUString();
+}
+
+OUString SAL_CALL ODatabaseMetaData::getDriverVersion()
+{
+ return OUString();
+}
+
+OUString SAL_CALL ODatabaseMetaData::getDatabaseProductVersion( )
+{
+ uno::Reference< XStatement > xSelect = m_pConnection->createStatement();
+
+ uno::Reference< XResultSet > xRs = xSelect->executeQuery("SELECT rdb$get_context('SYSTEM', 'ENGINE_VERSION') as version from rdb$database");
+ (void)xRs->next(); // first and only row
+ uno::Reference< XRow > xRow( xRs, UNO_QUERY_THROW );
+ return xRow->getString(1);
+}
+
+OUString SAL_CALL ODatabaseMetaData::getDatabaseProductName( )
+{
+ return "Firebird (engine12)";
+}
+
+OUString SAL_CALL ODatabaseMetaData::getProcedureTerm( )
+{
+ return OUString();
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getDriverMajorVersion( )
+{
+ return 1;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getDriverMinorVersion( )
+{
+ return 0;
+}
+
+OUString SAL_CALL ODatabaseMetaData::getSQLKeywords( )
+{
+ return OUString();
+}
+
+OUString SAL_CALL ODatabaseMetaData::getSearchStringEscape( )
+{
+ return OUString();
+}
+
+OUString SAL_CALL ODatabaseMetaData::getStringFunctions( )
+{
+ return "ASCII_CHAR,ASCII_VAL,BIT_LENGTH,CHAR_LENGTH,CHAR_TO_UUID,CHARACTER_LENGTH,"
+ "GEN_UUID,HASH,LEFT,LOWER,LPAD,OCTET_LENGTH,OVERLAY,POSITION,REPLACE,REVERSE,"
+ "RIGHT,RPAD,SUBSTRING,TRIM,UPPER,UUID_TO_CHAR";
+}
+
+OUString SAL_CALL ODatabaseMetaData::getTimeDateFunctions( )
+{
+ return "CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP,DATEADD, DATEDIFF,"
+ "EXTRACT,'NOW','TODAY','TOMORROW','YESTERDAY'";
+}
+
+OUString SAL_CALL ODatabaseMetaData::getSystemFunctions( )
+{
+ return OUString();
+}
+
+OUString SAL_CALL ODatabaseMetaData::getNumericFunctions( )
+{
+ return "ABS,ACOS,ASIN,ATAN,ATAN2,BIN_AND,BIN_NOT,BIN_OR,BIN_SHL,"
+ "BIN_SHR,BIN_XOR,CEIL,CEILING,COS,COSH,COT,EXP,FLOOR,LN,"
+ "LOG,LOG10,MOD,PI,POWER,RAND,ROUND,SIGN,SIN,SINH,SQRT,TAN,TANH,TRUNC";
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsExtendedSQLGrammar( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsFullOuterJoins( )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsLimitedOuterJoins( )
+{
+ return false;
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInGroupBy( )
+{
+ return 0; // 0 means no limit
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInOrderBy( )
+{
+ return 0; // 0 means no limit
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInSelect( )
+{
+ return 0; // 0 means no limit
+}
+
+sal_Int32 SAL_CALL ODatabaseMetaData::getMaxUserNameLength( )
+{
+ return 31;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetType(sal_Int32 setType)
+{
+ switch (setType)
+ {
+ case ResultSetType::FORWARD_ONLY:
+ return true;
+ default:
+ return false;
+ }
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetConcurrency(
+ sal_Int32 aResultSetType,
+ sal_Int32 aConcurrency)
+{
+ if (aResultSetType == ResultSetType::FORWARD_ONLY
+ && aConcurrency == ResultSetConcurrency::READ_ONLY)
+ return true;
+ else
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::ownUpdatesAreVisible( sal_Int32 )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::ownDeletesAreVisible( sal_Int32 )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::ownInsertsAreVisible( sal_Int32 )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::othersUpdatesAreVisible( sal_Int32 )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::othersDeletesAreVisible( sal_Int32 )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::othersInsertsAreVisible( sal_Int32 )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::updatesAreDetected( sal_Int32 )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::deletesAreDetected( sal_Int32 )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::insertsAreDetected( sal_Int32 )
+{
+ return false;
+}
+
+sal_Bool SAL_CALL ODatabaseMetaData::supportsBatchUpdates()
+{
+ // No batch support in firebird
+ return false;
+}
+
+uno::Reference< XConnection > SAL_CALL ODatabaseMetaData::getConnection()
+{
+ return m_pConnection;
+}
+
+// here follow all methods which return a resultset
+// the first methods is an example implementation how to use this resultset
+// of course you could implement it on your and you should do this because
+// the general way is more memory expensive
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTableTypes( )
+{
+ rtl::Reference<ODatabaseMetaDataResultSet> pResultSet = new
+ ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTableTypes);
+
+ ODatabaseMetaDataResultSet::ORows aResults;
+ ODatabaseMetaDataResultSet::ORow aRow(2);
+
+ aRow[0] = new ORowSetValueDecorator(); // unused
+
+ // TODO Put these statics to one place
+ // like postgreSQL's Statics class.
+
+ aRow[1] = new ORowSetValueDecorator(OUString("TABLE"));
+ aResults.push_back(aRow);
+
+ aRow[1] = new ORowSetValueDecorator(OUString("VIEW"));
+ aResults.push_back(aRow);
+
+ aRow[1] = new ORowSetValueDecorator(OUString("SYSTEM TABLE"));
+ aResults.push_back(aRow);
+
+ pResultSet->setRows(std::move(aResults));
+ return pResultSet;
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTypeInfo()
+{
+ SAL_INFO("connectivity.firebird", "getTypeInfo()");
+
+ // this returns an empty resultset where the column-names are already set
+ // in special the metadata of the resultset already returns the right columns
+ rtl::Reference<ODatabaseMetaDataResultSet> pResultSet =
+ new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTypeInfo);
+ static ODatabaseMetaDataResultSet::ORows aResults = []()
+ {
+ ODatabaseMetaDataResultSet::ORows tmp;
+ ODatabaseMetaDataResultSet::ORow aRow(19);
+
+ // Common data
+ aRow[4] = ODatabaseMetaDataResultSet::getQuoteValue(); // Literal quote marks
+ aRow[5] = ODatabaseMetaDataResultSet::getQuoteValue(); // Literal quote marks
+ aRow[7] = new ORowSetValueDecorator(ORowSetValue(true)); // Nullable
+ aRow[8] = new ORowSetValueDecorator(ORowSetValue(true)); // Case sensitive
+ aRow[10] = new ORowSetValueDecorator(ORowSetValue(false)); // Is unsigned
+ // FIXED_PREC_SCALE: docs state "can it be a money value? " however
+ // in reality this causes Base to treat all numbers as money formatted
+ // by default which is wrong (and formatting as money value is still
+ // possible for all values).
+ aRow[11] = new ORowSetValueDecorator(ORowSetValue(false));
+ // Localised Type Name -- TODO: implement (but can be null):
+ aRow[13] = new ORowSetValueDecorator();
+ aRow[16] = new ORowSetValueDecorator(); // Unused
+ aRow[17] = new ORowSetValueDecorator(); // Unused
+ aRow[18] = new ORowSetValueDecorator(sal_Int16(10));// Radix
+
+ // Char
+ aRow[1] = new ORowSetValueDecorator(OUString("CHAR"));
+ aRow[2] = new ORowSetValueDecorator(DataType::CHAR);
+ aRow[3] = new ORowSetValueDecorator(sal_Int16(32765)); // Prevision = max length
+ aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params
+ aRow[9] = new ORowSetValueDecorator(
+ sal_Int16(ColumnSearch::FULL)); // Searchable
+ aRow[12] = new ORowSetValueDecorator(ORowSetValue(false)); // Autoincrement
+ aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
+ aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
+ tmp.push_back(aRow);
+
+ // Varchar
+ aRow[1] = new ORowSetValueDecorator(OUString("VARCHAR"));
+ aRow[2] = new ORowSetValueDecorator(DataType::VARCHAR);
+ aRow[3] = new ORowSetValueDecorator(sal_Int16(32765)); // Prevision = max length
+ aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params
+ aRow[9] = new ORowSetValueDecorator(
+ sal_Int16(ColumnSearch::FULL)); // Searchable
+ aRow[12] = new ORowSetValueDecorator(ORowSetValue(false)); // Autoincrement
+ aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
+ aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
+ tmp.push_back(aRow);
+
+ // Binary (CHAR); we use the Firebird synonym CHARACTER
+ // to fool LO into seeing it as different types.
+ // It is distinguished from Text type by its character set OCTETS;
+ // that will be added by Tables::createStandardColumnPart
+ aRow[1] = new ORowSetValueDecorator(OUString("CHARACTER"));
+ aRow[2] = new ORowSetValueDecorator(DataType::BINARY);
+ aRow[3] = new ORowSetValueDecorator(sal_Int16(32765)); // Prevision = max length
+ aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params
+ aRow[9] = new ORowSetValueDecorator(
+ sal_Int16(ColumnSearch::NONE)); // Searchable
+ aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
+ aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
+ tmp.push_back(aRow);
+
+ // Varbinary (VARCHAR); see comment above about BINARY
+ aRow[1] = new ORowSetValueDecorator(OUString("CHARACTER VARYING"));
+ aRow[2] = new ORowSetValueDecorator(DataType::VARBINARY);
+ aRow[3] = new ORowSetValueDecorator(sal_Int16(32765)); // Prevision = max length
+ aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params
+ aRow[9] = new ORowSetValueDecorator(
+ sal_Int16(ColumnSearch::NONE)); // Searchable
+
+ // Clob (SQL_BLOB)
+ aRow[1] = new ORowSetValueDecorator(OUString("BLOB SUB_TYPE TEXT")); // BLOB, with subtype 1
+ aRow[2] = new ORowSetValueDecorator(DataType::CLOB);
+ aRow[3] = new ORowSetValueDecorator(sal_Int32(2147483647)); // Precision = max length
+ aRow[6] = new ORowSetValueDecorator(); // Create Params
+ aRow[9] = new ORowSetValueDecorator(
+ sal_Int16(ColumnSearch::FULL)); // Searchable
+ aRow[12] = new ORowSetValueDecorator(ORowSetValue(false)); // Autoincrement
+ aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
+ aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
+ tmp.push_back(aRow);
+
+ // Longvarbinary (SQL_BLOB)
+ // Distinguished from simple blob with a user-defined subtype.
+ aRow[1] = new ORowSetValueDecorator(OUString("BLOB SUB_TYPE " + OUString::number(static_cast<short>(BlobSubtype::Image))) ); // BLOB, with subtype 0
+ aRow[2] = new ORowSetValueDecorator(DataType::LONGVARBINARY);
+ tmp.push_back(aRow);
+
+ // Integer Types common
+ {
+ aRow[6] = new ORowSetValueDecorator(); // Create Params
+ aRow[9] = new ORowSetValueDecorator(
+ sal_Int16(ColumnSearch::FULL)); // Searchable
+ aRow[12] = new ORowSetValueDecorator(ORowSetValue(true)); // Autoincrement
+ aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
+ aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
+ }
+ // Smallint (SQL_SHORT)
+ aRow[1] = new ORowSetValueDecorator(OUString("SMALLINT"));
+ aRow[2] = new ORowSetValueDecorator(DataType::SMALLINT);
+ aRow[3] = new ORowSetValueDecorator(sal_Int16(5)); // Prevision
+ tmp.push_back(aRow);
+ // Integer (SQL_LONG)
+ aRow[1] = new ORowSetValueDecorator(OUString("INTEGER"));
+ aRow[2] = new ORowSetValueDecorator(DataType::INTEGER);
+ aRow[3] = new ORowSetValueDecorator(sal_Int16(10)); // Precision
+ tmp.push_back(aRow);
+ // Bigint (SQL_INT64)
+ aRow[1] = new ORowSetValueDecorator(OUString("BIGINT"));
+ aRow[2] = new ORowSetValueDecorator(DataType::BIGINT);
+ aRow[3] = new ORowSetValueDecorator(sal_Int16(20)); // Precision
+ tmp.push_back(aRow);
+
+ // Decimal Types common
+ {
+ aRow[9] = new ORowSetValueDecorator(
+ sal_Int16(ColumnSearch::FULL)); // Searchable
+ aRow[12] = new ORowSetValueDecorator(ORowSetValue(true)); // Autoincrement
+ }
+
+ aRow[6] = new ORowSetValueDecorator(OUString("PRECISION,SCALE")); // Create params
+ // Numeric
+ aRow[1] = new ORowSetValueDecorator(OUString("NUMERIC"));
+ aRow[2] = new ORowSetValueDecorator(DataType::NUMERIC);
+ aRow[3] = new ORowSetValueDecorator(sal_Int16(18)); // Precision
+ aRow[14] = new ORowSetValueDecorator(sal_Int16(0)); // Minimum scale
+ aRow[15] = new ORowSetValueDecorator(sal_Int16(18)); // Max scale
+ tmp.push_back(aRow);
+ // Decimal
+ aRow[1] = new ORowSetValueDecorator(OUString("DECIMAL"));
+ aRow[2] = new ORowSetValueDecorator(DataType::DECIMAL);
+ aRow[3] = new ORowSetValueDecorator(sal_Int16(18)); // Precision
+ aRow[14] = new ORowSetValueDecorator(sal_Int16(0)); // Minimum scale
+ aRow[15] = new ORowSetValueDecorator(sal_Int16(18)); // Max scale
+ tmp.push_back(aRow);
+
+ aRow[6] = new ORowSetValueDecorator(); // Create Params
+ // Float (SQL_FLOAT)
+ aRow[1] = new ORowSetValueDecorator(OUString("FLOAT"));
+ aRow[2] = new ORowSetValueDecorator(DataType::FLOAT);
+ aRow[3] = new ORowSetValueDecorator(sal_Int16(7)); // Precision
+ aRow[14] = new ORowSetValueDecorator(sal_Int16(1)); // Minimum scale
+ aRow[15] = new ORowSetValueDecorator(sal_Int16(7)); // Max scale
+ tmp.push_back(aRow);
+ // Double (SQL_DOUBLE)
+ aRow[1] = new ORowSetValueDecorator(OUString("DOUBLE PRECISION"));
+ aRow[2] = new ORowSetValueDecorator(DataType::DOUBLE);
+ aRow[3] = new ORowSetValueDecorator(sal_Int16(15)); // Precision
+ aRow[14] = new ORowSetValueDecorator(sal_Int16(1)); // Minimum scale
+ aRow[15] = new ORowSetValueDecorator(sal_Int16(15)); // Max scale
+ tmp.push_back(aRow);
+
+ // TODO: no idea whether D_FLOAT corresponds to an sql type
+
+ // SQL_TIMESTAMP
+ aRow[1] = new ORowSetValueDecorator(OUString("TIMESTAMP"));
+ aRow[2] = new ORowSetValueDecorator(DataType::TIMESTAMP);
+ aRow[3] = new ORowSetValueDecorator(sal_Int32(8)); // Prevision = max length
+ aRow[6] = new ORowSetValueDecorator(); // Create Params
+ aRow[9] = new ORowSetValueDecorator(
+ sal_Int16(ColumnSearch::FULL)); // Searchable
+ aRow[12] = new ORowSetValueDecorator(ORowSetValue(false)); // Autoincrement
+ aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
+ aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
+ tmp.push_back(aRow);
+
+ // SQL_TYPE_TIME
+ aRow[1] = new ORowSetValueDecorator(OUString("TIME"));
+ aRow[2] = new ORowSetValueDecorator(DataType::TIME);
+ aRow[3] = new ORowSetValueDecorator(sal_Int32(8)); // Prevision = max length
+ aRow[6] = new ORowSetValueDecorator(); // Create Params
+ aRow[9] = new ORowSetValueDecorator(
+ sal_Int16(ColumnSearch::FULL)); // Searchable
+ aRow[12] = new ORowSetValueDecorator(ORowSetValue(false)); // Autoincrement
+ aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
+ aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
+ tmp.push_back(aRow);
+
+ // SQL_TYPE_DATE
+ aRow[1] = new ORowSetValueDecorator(OUString("DATE"));
+ aRow[2] = new ORowSetValueDecorator(DataType::DATE);
+ aRow[3] = new ORowSetValueDecorator(sal_Int32(8)); // Prevision = max length
+ aRow[6] = new ORowSetValueDecorator(); // Create Params
+ aRow[9] = new ORowSetValueDecorator(
+ sal_Int16(ColumnSearch::FULL)); // Searchable
+ aRow[12] = new ORowSetValueDecorator(ORowSetValue(false)); // Autoincrement
+ aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
+ aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
+ tmp.push_back(aRow);
+
+ // SQL_BLOB
+ aRow[1] = new ORowSetValueDecorator(OUString("BLOB SUB_TYPE BINARY"));
+ aRow[2] = new ORowSetValueDecorator(DataType::BLOB);
+ aRow[3] = new ORowSetValueDecorator(sal_Int32(0)); // Prevision = max length
+ aRow[6] = new ORowSetValueDecorator(); // Create Params
+ aRow[9] = new ORowSetValueDecorator(
+ sal_Int16(ColumnSearch::NONE)); // Searchable
+ aRow[12] = new ORowSetValueDecorator(ORowSetValue(false)); // Autoincrement
+ aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
+ aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
+ tmp.push_back(aRow);
+
+ // SQL_BOOLEAN
+ aRow[1] = new ORowSetValueDecorator(OUString("BOOLEAN"));
+ aRow[2] = new ORowSetValueDecorator(DataType::BOOLEAN);
+ aRow[3] = new ORowSetValueDecorator(sal_Int32(1)); // Prevision = max length
+ aRow[6] = new ORowSetValueDecorator(); // Create Params
+ aRow[9] = new ORowSetValueDecorator(
+ sal_Int16(ColumnSearch::BASIC)); // Searchable
+ aRow[12] = new ORowSetValueDecorator(ORowSetValue(false)); // Autoincrement
+ aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
+ aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
+ tmp.push_back(aRow);
+ return tmp;
+ }();
+ // [-loplugin:redundantfcast] false positive:
+ pResultSet->setRows(ODatabaseMetaDataResultSet::ORows(aResults));
+ return pResultSet;
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getColumnPrivileges(
+ const Any& /*aCatalog*/,
+ const OUString& /*sSchema*/,
+ const OUString& sTable,
+ const OUString& sColumnNamePattern)
+{
+ SAL_INFO("connectivity.firebird", "getColumnPrivileges() with "
+ "Table: " << sTable
+ << " & ColumnNamePattern: " << sColumnNamePattern);
+
+ rtl::Reference<ODatabaseMetaDataResultSet> pResultSet = new
+ ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eColumnPrivileges);
+ uno::Reference< XStatement > statement = m_pConnection->createStatement();
+
+ static const char wld[] = "%";
+ OUStringBuffer queryBuf(
+ "SELECT "
+ "priv.RDB$RELATION_NAME, " // 1 Table name
+ "priv.RDB$GRANTOR," // 2
+ "priv.RDB$USER, " // 3 Grantee
+ "priv.RDB$PRIVILEGE, " // 4
+ "priv.RDB$GRANT_OPTION, " // 5 is Grantable
+ "priv.RDB$FIELD_NAME " // 6 Column name
+ "FROM RDB$USER_PRIVILEGES priv ");
+
+ {
+ OUString sAppend = "WHERE priv.RDB$RELATION_NAME = '%' ";
+ queryBuf.append(sAppend.replaceAll("%", sTable));
+ }
+ if (!sColumnNamePattern.isEmpty())
+ {
+ OUString sAppend;
+ if (sColumnNamePattern.match(wld))
+ sAppend = "AND priv.RDB$FIELD_NAME LIKE '%' ";
+ else
+ sAppend = "AND priv.RDB$FIELD_NAME = '%' ";
+
+ queryBuf.append(sAppend.replaceAll(wld, sColumnNamePattern));
+ }
+
+ queryBuf.append(" ORDER BY priv.RDB$FIELD, "
+ "priv.RDB$PRIVILEGE");
+
+ OUString query = queryBuf.makeStringAndClear();
+
+ uno::Reference< XResultSet > rs = statement->executeQuery(query);
+ uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW );
+ ODatabaseMetaDataResultSet::ORows aResults;
+
+ ODatabaseMetaDataResultSet::ORow aCurrentRow(9);
+ aCurrentRow[0] = new ORowSetValueDecorator(); // Unused
+ aCurrentRow[1] = new ORowSetValueDecorator(); // 1. TABLE_CAT Unsupported
+ aCurrentRow[2] = new ORowSetValueDecorator(); // 1. TABLE_SCHEM Unsupported
+
+ while( rs->next() )
+ {
+ // 3. TABLE_NAME
+ aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1)));
+ // 4. COLUMN_NAME
+ aCurrentRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(6)));
+ aCurrentRow[5] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2))); // 5. GRANTOR
+ aCurrentRow[6] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(3))); // 6. GRANTEE
+ aCurrentRow[7] = new ORowSetValueDecorator(xRow->getString(4)); // 7. Privilege
+ aCurrentRow[8] = new ORowSetValueDecorator( ( xRow->getShort(5) == 1 ) ?
+ OUString("YES") : OUString("NO")); // 8. Grantable
+
+ aResults.push_back(aCurrentRow);
+ }
+
+ pResultSet->setRows( std::move(aResults) );
+
+ return pResultSet;
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getColumns(
+ const Any& /*catalog*/,
+ const OUString& /*schemaPattern*/,
+ const OUString& tableNamePattern,
+ const OUString& columnNamePattern)
+{
+ SAL_INFO("connectivity.firebird", "getColumns() with "
+ "TableNamePattern: " << tableNamePattern <<
+ " & ColumnNamePattern: " << columnNamePattern);
+
+ OUStringBuffer queryBuf("SELECT "
+ "relfields.RDB$RELATION_NAME, " // 1
+ "relfields.RDB$FIELD_NAME, " // 2
+ "relfields.RDB$DESCRIPTION," // 3
+ "relfields.RDB$DEFAULT_VALUE, " // 4
+ "relfields.RDB$FIELD_POSITION, "// 5
+ "fields.RDB$FIELD_TYPE, " // 6
+ "fields.RDB$FIELD_SUB_TYPE, " // 7
+ "fields.RDB$FIELD_LENGTH, " // 8
+ "fields.RDB$FIELD_PRECISION, " // 9
+ "fields.RDB$FIELD_SCALE, " // 10
+ // Specifically use relfields null flag -- the one in fields is used
+ // for domains, whether a specific field is nullable is set in relfields,
+ // this is also the one we manually fiddle when changing NULL/NOT NULL
+ // (see Table.cxx)
+ "relfields.RDB$NULL_FLAG, " // 11
+ "fields.RDB$CHARACTER_LENGTH, " // 12
+ "charset.RDB$CHARACTER_SET_NAME " // 13
+ "FROM RDB$RELATION_FIELDS relfields "
+ "JOIN RDB$FIELDS fields "
+ "on (fields.RDB$FIELD_NAME = relfields.RDB$FIELD_SOURCE) "
+ "LEFT JOIN RDB$CHARACTER_SETS charset "
+ "on (fields.RDB$CHARACTER_SET_ID = charset.RDB$CHARACTER_SET_ID) "
+ "WHERE (1 = 1) ");
+
+ if (!tableNamePattern.isEmpty())
+ {
+ OUString sAppend;
+ if (tableNamePattern.match("%"))
+ sAppend = "AND relfields.RDB$RELATION_NAME LIKE '%' ";
+ else
+ sAppend = "AND relfields.RDB$RELATION_NAME = '%' ";
+
+ queryBuf.append(sAppend.replaceAll("%", tableNamePattern));
+ }
+
+ if (!columnNamePattern.isEmpty())
+ {
+ OUString sAppend;
+ if (columnNamePattern.match("%"))
+ sAppend = "AND relfields.RDB$FIELD_NAME LIKE '%' ";
+ else
+ sAppend = "AND relfields.RDB$FIELD_NAME = '%' ";
+
+ queryBuf.append(sAppend.replaceAll("%", columnNamePattern));
+ }
+
+ OUString query = queryBuf.makeStringAndClear();
+
+ uno::Reference< XStatement > statement = m_pConnection->createStatement();
+ uno::Reference< XResultSet > rs = statement->executeQuery(query);
+ uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW );
+
+ ODatabaseMetaDataResultSet::ORows aResults;
+ ODatabaseMetaDataResultSet::ORow aCurrentRow(19);
+
+ aCurrentRow[0] = new ORowSetValueDecorator(); // Unused -- numbering starts from 0
+ aCurrentRow[1] = new ORowSetValueDecorator(); // Catalog - can be null
+ aCurrentRow[2] = new ORowSetValueDecorator(); // Schema - can be null
+ aCurrentRow[8] = new ORowSetValueDecorator(); // Unused
+ aCurrentRow[10] = new ORowSetValueDecorator(sal_Int32(10)); // Radix: fixed in FB
+ aCurrentRow[14] = new ORowSetValueDecorator(); // Unused
+ aCurrentRow[15] = new ORowSetValueDecorator(); // Unused
+
+ while( rs->next() )
+ {
+ // 3. TABLE_NAME
+ aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1)));
+ // 4. Column Name
+ aCurrentRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2)));
+ // 5. Datatype
+ short aType = getFBTypeFromBlrType(xRow->getShort(6));
+ short aScale = xRow->getShort(10);
+ OUString sCharsetName = xRow->getString(13);
+ // result field may be filled with spaces
+ sCharsetName = sCharsetName.trim();
+ ColumnTypeInfo aInfo(aType, xRow->getShort(7), aScale,
+ sCharsetName);
+
+ aCurrentRow[5] = new ORowSetValueDecorator(aInfo.getSdbcType());
+ // 6. Typename (SQL_*)
+ aCurrentRow[6] = new ORowSetValueDecorator(aInfo.getColumnTypeName());
+
+ // 7. Column Sizes
+ {
+ sal_Int32 aColumnSize = 0;
+ switch (aType)
+ {
+ case SQL_TEXT:
+ case SQL_VARYING:
+ aColumnSize = xRow->getShort(12);
+ break;
+ case SQL_SHORT:
+ case SQL_LONG:
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ case SQL_D_FLOAT:
+ case SQL_INT64:
+ case SQL_QUAD:
+ aColumnSize = xRow->getShort(9);
+ break;
+ case SQL_TIMESTAMP:
+ case SQL_BLOB:
+ case SQL_ARRAY:
+ case SQL_TYPE_TIME:
+ case SQL_TYPE_DATE:
+ case SQL_NULL:
+ // TODO: implement.
+ break;
+ }
+ aCurrentRow[7] = new ORowSetValueDecorator(aColumnSize);
+ }
+
+ // 9. Decimal digits (scale)
+ // fb stores a negative number
+ aCurrentRow[9] = new ORowSetValueDecorator( static_cast<sal_Int16>(-aScale) );
+
+ // 11. Nullable
+ if (xRow->getShort(11))
+ {
+ aCurrentRow[11] = new ORowSetValueDecorator(ColumnValue::NO_NULLS);
+ }
+ else
+ {
+ aCurrentRow[11] = new ORowSetValueDecorator(ColumnValue::NULLABLE);
+ }
+ // 12. Comments -- may be omitted
+ {
+ OUString aDescription;
+ uno::Reference< XBlob > xBlob = xRow->getBlob(3);
+ if (xBlob.is())
+ {
+ const sal_Int64 aBlobLength = xBlob->length();
+ if (aBlobLength > SAL_MAX_INT32)
+ {
+ SAL_WARN("connectivity.firebird", "getBytes can't return " << aBlobLength << " bytes but only max " << SAL_MAX_INT32);
+ aDescription = OUString(reinterpret_cast<char*>(xBlob->getBytes(1, SAL_MAX_INT32).getArray()),
+ SAL_MAX_INT32,
+ RTL_TEXTENCODING_UTF8);
+ }
+ else
+ {
+ aDescription = OUString(reinterpret_cast<char*>(xBlob->getBytes(1, static_cast<sal_Int32>(aBlobLength)).getArray()),
+ aBlobLength,
+ RTL_TEXTENCODING_UTF8);
+ }
+ }
+ aCurrentRow[12] = new ORowSetValueDecorator(aDescription);
+ }
+ // 13. Default -- may be omitted.
+ {
+ uno::Reference< XBlob > xDefaultValueBlob = xRow->getBlob(4);
+ if (xDefaultValueBlob.is())
+ {
+ // TODO: Implement
+ }
+ aCurrentRow[13] = new ORowSetValueDecorator();
+ }
+
+ // 16. Bytes in Column for char
+ if (aType == SQL_TEXT)
+ {
+ aCurrentRow[16] = new ORowSetValueDecorator(xRow->getShort(8));
+ }
+ else if (aType == SQL_VARYING)
+ {
+ aCurrentRow[16] = new ORowSetValueDecorator(sal_Int32(32767));
+ }
+ else
+ {
+ aCurrentRow[16] = new ORowSetValueDecorator(sal_Int32(0));
+ }
+ // 17. Index of column
+ {
+ short nColumnNumber = xRow->getShort(5);
+ // Firebird stores column numbers beginning with 0 internally
+ // SDBC expects column numbering to begin with 1.
+ aCurrentRow[17] = new ORowSetValueDecorator(sal_Int32(nColumnNumber + 1));
+ }
+ // 18. Is nullable
+ if (xRow->getShort(9))
+ {
+ aCurrentRow[18] = new ORowSetValueDecorator(OUString("NO"));
+ }
+ else
+ {
+ aCurrentRow[18] = new ORowSetValueDecorator(OUString("YES"));
+ }
+
+ aResults.push_back(aCurrentRow);
+ }
+ rtl::Reference<ODatabaseMetaDataResultSet> pResultSet = new
+ ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eColumns);
+ pResultSet->setRows( std::move(aResults) );
+
+ return pResultSet;
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTables(
+ const Any& /*catalog*/,
+ const OUString& /*schemaPattern*/,
+ const OUString& tableNamePattern,
+ const Sequence< OUString >& types)
+{
+ SAL_INFO("connectivity.firebird", "getTables() with "
+ "TableNamePattern: " << tableNamePattern);
+
+ rtl::Reference<ODatabaseMetaDataResultSet> pResultSet = new
+ ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTables);
+ uno::Reference< XStatement > statement = m_pConnection->createStatement();
+
+ static const char wld[] = "%";
+ OUStringBuffer queryBuf(
+ "SELECT "
+ "RDB$RELATION_NAME, "
+ "RDB$SYSTEM_FLAG, "
+ "RDB$RELATION_TYPE, "
+ "RDB$DESCRIPTION, "
+ "RDB$VIEW_BLR "
+ "FROM RDB$RELATIONS "
+ "WHERE ");
+
+ // TODO: GLOBAL TEMPORARY, LOCAL TEMPORARY, ALIAS, SYNONYM
+ if (!types.hasElements() || (types.getLength() == 1 && types[0].match(wld)))
+ {
+ // from Firebird: src/jrd/constants.h
+ // rel_persistent = 0, rel_view = 1, rel_external = 2
+ // All table types? I.e. includes system tables.
+ queryBuf.append("(RDB$RELATION_TYPE = 0 OR RDB$RELATION_TYPE = 1 OR RDB$RELATION_TYPE = 2) ");
+ }
+ else
+ {
+ queryBuf.append("( (0 = 1) ");
+ for (OUString const & t : types)
+ {
+ if (t == "SYSTEM TABLE")
+ queryBuf.append("OR (RDB$SYSTEM_FLAG = 1 AND RDB$VIEW_BLR IS NULL) ");
+ else if (t == "TABLE")
+ queryBuf.append("OR (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0 AND RDB$VIEW_BLR IS NULL) ");
+ else if (t == "VIEW")
+ queryBuf.append("OR (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0 AND RDB$VIEW_BLR IS NOT NULL) ");
+ else
+ throw SQLException(); // TODO: implement other types, see above.
+ }
+ queryBuf.append(") ");
+ }
+
+ if (!tableNamePattern.isEmpty())
+ {
+ OUString sAppend;
+ if (tableNamePattern.match(wld))
+ sAppend = "AND RDB$RELATION_NAME LIKE '%' ";
+ else
+ sAppend = "AND RDB$RELATION_NAME = '%' ";
+
+ queryBuf.append(sAppend.replaceAll(wld, tableNamePattern));
+ }
+
+ queryBuf.append(" ORDER BY RDB$RELATION_TYPE, RDB$RELATION_NAME");
+
+ OUString query = queryBuf.makeStringAndClear();
+
+ uno::Reference< XResultSet > rs = statement->executeQuery(query);
+ uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW );
+ ODatabaseMetaDataResultSet::ORows aResults;
+
+ ODatabaseMetaDataResultSet::ORow aCurrentRow(6);
+ aCurrentRow[0] = new ORowSetValueDecorator(); // 0. Unused
+ aCurrentRow[1] = new ORowSetValueDecorator(); // 1. Table_Cat Unsupported
+ aCurrentRow[2] = new ORowSetValueDecorator(); // 2. Table_Schem Unsupported
+
+ while( rs->next() )
+ {
+ // 3. TABLE_NAME
+ aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1)));
+ // 4. TABLE_TYPE
+ {
+ // TODO: check this as the docs are a bit unclear.
+ sal_Int16 nSystemFlag = xRow->getShort(2);
+ sal_Int16 nTableType = xRow->getShort(3);
+ xRow->getBlob(5); // We have to retrieve a column to verify it is null.
+ bool aIsView = !xRow->wasNull();
+ OUString sTableType;
+
+ if (nSystemFlag == 1)
+ {
+ sTableType = "SYSTEM TABLE";
+ }
+ else if (aIsView)
+ {
+ sTableType = "VIEW";
+ }
+ else
+ {
+ // see above about src/jrd/constants.h
+ if (nTableType == 0 || nTableType == 2)
+ sTableType = "TABLE";
+ }
+
+ aCurrentRow[4] = new ORowSetValueDecorator(sTableType);
+ }
+ // 5. REMARKS
+ {
+ uno::Reference< XClob > xClob = xRow->getClob(4);
+ if (xClob.is())
+ {
+ aCurrentRow[5] = new ORowSetValueDecorator(xClob->getSubString(1, xClob->length()));
+ }
+ }
+
+ aResults.push_back(aCurrentRow);
+ }
+
+ pResultSet->setRows( std::move(aResults) );
+
+ return pResultSet;
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getProcedureColumns(
+ const Any&, const OUString&,
+ const OUString&, const OUString& )
+{
+ SAL_WARN("connectivity.firebird", "Not yet implemented");
+ OSL_FAIL("Not implemented yet!");
+ // TODO implement
+ return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eProcedureColumns);
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getProcedures(
+ const Any&, const OUString&,
+ const OUString& )
+{
+ SAL_WARN("connectivity.firebird", "Not yet implemented");
+ OSL_FAIL("Not implemented yet!");
+ // TODO implement
+ return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eProcedures);
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getVersionColumns(
+ const Any&, const OUString&, const OUString& )
+{
+ SAL_WARN("connectivity.firebird", "Not yet implemented");
+ OSL_FAIL("Not implemented yet!");
+ // TODO implement
+ return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eVersionColumns);
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getExportedKeys(
+ const Any&, const OUString&, const OUString& table )
+{
+ return ODatabaseMetaData::lcl_getKeys(false, table);
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getImportedKeys(
+ const Any&, const OUString&, const OUString& table )
+{
+ return ODatabaseMetaData::lcl_getKeys(true, table);
+}
+
+uno::Reference< XResultSet > ODatabaseMetaData::lcl_getKeys(const bool bIsImport, std::u16string_view table )
+{
+ rtl::Reference<ODatabaseMetaDataResultSet> pResultSet = new
+ ODatabaseMetaDataResultSet(bIsImport?ODatabaseMetaDataResultSet::eImportedKeys:ODatabaseMetaDataResultSet::eExportedKeys);
+
+ uno::Reference< XStatement > statement = m_pConnection->createStatement();
+
+ OUString sSQL = "SELECT "
+ "RDB$REF_CONSTRAINTS.RDB$UPDATE_RULE, " // 1 update rule
+ "RDB$REF_CONSTRAINTS.RDB$DELETE_RULE, " // 2 delete rule
+ "RDB$REF_CONSTRAINTS.RDB$CONST_NAME_UQ, " // 3 primary or unique key name
+ "RDB$REF_CONSTRAINTS.RDB$CONSTRAINT_NAME, " // 4 foreign key name
+ "PRIM.RDB$DEFERRABLE, " // 5 deferrability
+ "PRIM.RDB$INITIALLY_DEFERRED, " // 6 deferrability
+ "PRIM.RDB$RELATION_NAME, " // 7 PK table name
+ "PRIMARY_INDEX.RDB$FIELD_NAME, " // 8 PK column name
+ "PRIMARY_INDEX.RDB$FIELD_POSITION, " // 9 PK sequence number
+ "FOREI.RDB$RELATION_NAME, " // 10 FK table name
+ "FOREIGN_INDEX.RDB$FIELD_NAME " // 11 FK column name
+ "FROM RDB$REF_CONSTRAINTS "
+ "INNER JOIN RDB$RELATION_CONSTRAINTS AS PRIM "
+ "ON RDB$REF_CONSTRAINTS.RDB$CONST_NAME_UQ = PRIM.RDB$CONSTRAINT_NAME "
+ "INNER JOIN RDB$RELATION_CONSTRAINTS AS FOREI "
+ "ON RDB$REF_CONSTRAINTS.RDB$CONSTRAINT_NAME = FOREI.RDB$CONSTRAINT_NAME "
+ "INNER JOIN RDB$INDEX_SEGMENTS AS PRIMARY_INDEX "
+ "ON PRIM.RDB$INDEX_NAME = PRIMARY_INDEX.RDB$INDEX_NAME "
+ "INNER JOIN RDB$INDEX_SEGMENTS AS FOREIGN_INDEX "
+ "ON FOREI.RDB$INDEX_NAME = FOREIGN_INDEX.RDB$INDEX_NAME "
+ "WHERE FOREI.RDB$CONSTRAINT_TYPE = 'FOREIGN KEY' ";
+ if (bIsImport)
+ sSQL += OUString::Concat("AND FOREI.RDB$RELATION_NAME = '")+ table +"'";
+ else
+ sSQL += OUString::Concat("AND PRIM.RDB$RELATION_NAME = '")+ table +"'";
+
+ uno::Reference< XResultSet > rs = statement->executeQuery(sSQL);
+ uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW );
+
+ ODatabaseMetaDataResultSet::ORows aResults;
+ ODatabaseMetaDataResultSet::ORow aCurrentRow(15);
+
+ // TODO is it necessary to initialize these?
+ aCurrentRow[0] = new ORowSetValueDecorator(); // Unused
+ aCurrentRow[1] = new ORowSetValueDecorator(); // PKTABLE_CAT unsupported
+ aCurrentRow[2] = new ORowSetValueDecorator(); // PKTABLE_SCHEM unsupported
+ aCurrentRow[5] = new ORowSetValueDecorator(); // FKTABLE_CAT unsupported
+ aCurrentRow[6] = new ORowSetValueDecorator(); // FKTABLE_SCHEM unsupported
+
+ std::map< OUString,sal_Int32> aRuleMap;
+ aRuleMap[ OUString("CASCADE")] = KeyRule::CASCADE;
+ aRuleMap[ OUString("RESTRICT")] = KeyRule::RESTRICT;
+ aRuleMap[ OUString("SET NULL")] = KeyRule::SET_NULL;
+ aRuleMap[ OUString("SET DEFAULT")] = KeyRule::SET_DEFAULT;
+ aRuleMap[ OUString("NO ACTION")] = KeyRule::NO_ACTION;
+
+ while(rs->next())
+ {
+ aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(7))); // PK table
+ aCurrentRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(8))); // PK column
+ aCurrentRow[7] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(10))); // FK table
+ aCurrentRow[8] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(11))); // FK column
+
+ aCurrentRow[9] = new ORowSetValueDecorator(xRow->getShort(9)); // PK sequence number
+ aCurrentRow[10] = new ORowSetValueDecorator(aRuleMap[sanitizeIdentifier(xRow->getString(1))]); // update role
+ aCurrentRow[11] = new ORowSetValueDecorator(aRuleMap[sanitizeIdentifier(xRow->getString(2))]); // delete role
+
+ aCurrentRow[12] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(4))); // FK name
+ aCurrentRow[13] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(3))); // PK name
+
+ aCurrentRow[14] = new ORowSetValueDecorator(Deferrability::NONE); // deferrability
+
+ // deferrability is currently not supported, but may be supported in the future.
+ /*
+ aCurrentRow[14] = (xRow->getString(5) == "NO" ?
+ new ORowSetValueDecorator(Deferrability::NONE)
+ : (xRow->getString(6) == "NO" ?
+ new ORowSetValueDecorator(Deferrability::INITIALLY_IMMEDIATE)
+ : new ORowSetValueDecorator(Deferrability::INITIALLY_DEFERRED));
+ */
+
+ aResults.push_back(aCurrentRow);
+ }
+
+ pResultSet->setRows( std::move(aResults) );
+ return pResultSet;
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getPrimaryKeys(
+ const Any& /*aCatalog*/,
+ const OUString& /*sSchema*/,
+ const OUString& sTable)
+{
+ SAL_INFO("connectivity.firebird", "getPrimaryKeys() with "
+ "Table: " << sTable);
+
+ OUString sAppend = "WHERE constr.RDB$RELATION_NAME = '%' ";
+ OUString sQuery = "SELECT "
+ "constr.RDB$RELATION_NAME, " // 1. Table Name
+ "inds.RDB$FIELD_NAME, " // 2. Column Name
+ "inds.RDB$FIELD_POSITION, " // 3. Sequence Number
+ "constr.RDB$CONSTRAINT_NAME " // 4 Constraint name
+ "FROM RDB$RELATION_CONSTRAINTS constr "
+ "JOIN RDB$INDEX_SEGMENTS inds "
+ "on (constr.RDB$INDEX_NAME = inds.RDB$INDEX_NAME) " +
+ sAppend.replaceAll("%", sTable) +
+ "AND constr.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' "
+ "ORDER BY inds.RDB$FIELD_NAME";
+
+ uno::Reference< XStatement > xStatement = m_pConnection->createStatement();
+ uno::Reference< XResultSet > xRs = xStatement->executeQuery(sQuery);
+ uno::Reference< XRow > xRow( xRs, UNO_QUERY_THROW );
+
+ ODatabaseMetaDataResultSet::ORows aResults;
+ ODatabaseMetaDataResultSet::ORow aCurrentRow(7);
+
+ aCurrentRow[0] = new ORowSetValueDecorator(); // Unused -- numbering starts from 0
+ aCurrentRow[1] = new ORowSetValueDecorator(); // Catalog - can be null
+ aCurrentRow[2] = new ORowSetValueDecorator(); // Schema - can be null
+
+ while(xRs->next())
+ {
+ // 3. Table Name
+ if (xRs->getRow() == 1) // Table name doesn't change, so only retrieve once
+ {
+ aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1)));
+ }
+ // 4. Column Name
+ aCurrentRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2)));
+ // 5. KEY_SEQ (which key in the sequence)
+ aCurrentRow[5] = new ORowSetValueDecorator(xRow->getShort(3));
+ // 6. Primary Key Name
+ aCurrentRow[6] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(4)));
+
+ aResults.push_back(aCurrentRow);
+ }
+ rtl::Reference<ODatabaseMetaDataResultSet> pResultSet = new
+ ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::ePrimaryKeys);
+ pResultSet->setRows( std::move(aResults) );
+
+ return pResultSet;
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getIndexInfo(
+ const Any& /*aCatalog*/,
+ const OUString& /*sSchema*/,
+ const OUString& sTable,
+ sal_Bool bIsUnique,
+ sal_Bool) // TODO: what is bIsApproximate?
+
+{
+ // Apparently this method can also return a "tableIndexStatistic"
+ // However this is only mentioned in XDatabaseMetaData.idl (whose comments
+ // are duplicated in the postgresql driver), and is otherwise undocumented.
+ SAL_INFO("connectivity.firebird", "getPrimaryKeys() with "
+ "Table: " << sTable);
+
+ OUStringBuffer aQueryBuf("SELECT "
+ "indices.RDB$RELATION_NAME, " // 1. Table Name
+ "index_segments.RDB$FIELD_NAME, " // 2. Column Name
+ "index_segments.RDB$FIELD_POSITION, " // 3. Sequence Number
+ "indices.RDB$INDEX_NAME, " // 4. Index name
+ "indices.RDB$UNIQUE_FLAG, " // 5. Unique Flag
+ "indices.RDB$INDEX_TYPE " // 6. Index Type
+ "FROM RDB$INDICES indices "
+ "JOIN RDB$INDEX_SEGMENTS index_segments "
+ "on (indices.RDB$INDEX_NAME = index_segments.RDB$INDEX_NAME) "
+ "WHERE indices.RDB$RELATION_NAME = '" + sTable + "' "
+ "AND (indices.RDB$SYSTEM_FLAG = 0) ");
+ // Not sure whether we should exclude system indices, but otoh. we never
+ // actually deal with system tables (system indices only apply to system
+ // tables) within the GUI.
+
+ // Only filter if true (according to the docs), i.e.:
+ // If false we return all indices, if true we return only unique indices
+ if (bIsUnique)
+ aQueryBuf.append("AND (indices.RDB$UNIQUE_FLAG = 1) ");
+
+ OUString sQuery = aQueryBuf.makeStringAndClear();
+
+ uno::Reference< XStatement > xStatement = m_pConnection->createStatement();
+ uno::Reference< XResultSet > xRs = xStatement->executeQuery(sQuery);
+ uno::Reference< XRow > xRow( xRs, UNO_QUERY_THROW );
+
+ ODatabaseMetaDataResultSet::ORows aResults;
+ ODatabaseMetaDataResultSet::ORow aCurrentRow(14);
+
+ aCurrentRow[0] = new ORowSetValueDecorator(); // Unused -- numbering starts from 0
+ aCurrentRow[1] = new ORowSetValueDecorator(); // Catalog - can be null
+ aCurrentRow[2] = new ORowSetValueDecorator(); // Schema - can be null
+ aCurrentRow[5] = new ORowSetValueDecorator(); // Index Catalog -- can be null
+ // Wikipedia indicates:
+ // 'Firebird makes all indices of the database behave like well-tuned "clustered indexes" used by other architectures.'
+ // but it's not "CLUSTERED", neither "STATISTIC" nor "HASHED" (the other specific types from offapi/com/sun/star/sdbc/IndexType.idl)
+ // According to https://www.ibphoenix.com/resources/documents/design/doc_18,
+ // it seems another type => OTHER
+ aCurrentRow[7] = new ORowSetValueDecorator(IndexType::OTHER); // 7. INDEX TYPE
+ aCurrentRow[13] = new ORowSetValueDecorator(); // Filter Condition -- can be null
+
+ while(xRs->next())
+ {
+ // 3. Table Name
+ if (xRs->getRow() == 1) // Table name doesn't change, so only retrieve once
+ {
+ aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1)));
+ }
+
+ // 4. NON_UNIQUE -- i.e. specifically negate here.
+ aCurrentRow[4] = new ORowSetValueDecorator(ORowSetValue(xRow->getShort(5) == 0));
+ // 6. INDEX NAME
+ aCurrentRow[6] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(4)));
+
+ // 8. ORDINAL POSITION
+ aCurrentRow[8] = new ORowSetValueDecorator(xRow->getShort(3));
+ // 9. COLUMN NAME
+ aCurrentRow[9] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2)));
+ // 10. ASC(ending)/DESC(ending)
+ if (xRow->getShort(6) == 1)
+ aCurrentRow[10] = new ORowSetValueDecorator(OUString("D"));
+ else
+ aCurrentRow[10] = new ORowSetValueDecorator(OUString("A"));
+ // TODO: double check this^^^, doesn't seem to be officially documented anywhere.
+ // 11. CARDINALITY
+ aCurrentRow[11] = new ORowSetValueDecorator(sal_Int32(0)); // TODO: determine how to do this
+ // 12. PAGES
+ aCurrentRow[12] = new ORowSetValueDecorator(sal_Int32(0)); // TODO: determine how to do this
+
+ aResults.push_back(aCurrentRow);
+ }
+ rtl::Reference<ODatabaseMetaDataResultSet> pResultSet = new
+ ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eIndexInfo);
+ pResultSet->setRows( std::move(aResults) );
+
+ return pResultSet;
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getBestRowIdentifier(
+ const Any&, const OUString&, const OUString&, sal_Int32,
+ sal_Bool )
+{
+ OSL_FAIL("Not implemented yet!");
+ // TODO implement
+ return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eBestRowIdentifier);
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTablePrivileges(
+ const Any& /*aCatalog*/,
+ const OUString& /*sSchemaPattern*/,
+ const OUString& sTableNamePattern)
+{
+ SAL_INFO("connectivity.firebird", "getTablePrivileges() with "
+ "TableNamePattern: " << sTableNamePattern);
+
+ rtl::Reference<ODatabaseMetaDataResultSet> pResultSet = new
+ ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTablePrivileges);
+ uno::Reference< XStatement > statement = m_pConnection->createStatement();
+
+ // TODO: column specific privileges are included, we may need
+ // to have WHERE RDB$FIELD_NAME = NULL or similar.
+ static const char wld[] = "%";
+ OUStringBuffer queryBuf(
+ "SELECT "
+ "priv.RDB$RELATION_NAME, " // 1
+ "priv.RDB$GRANTOR," // 2
+ "priv.RDB$USER, " // 3 Grantee
+ "priv.RDB$PRIVILEGE, " // 4
+ "priv.RDB$GRANT_OPTION " // 5 is Grantable
+ "FROM RDB$USER_PRIVILEGES priv ");
+
+ if (!sTableNamePattern.isEmpty())
+ {
+ OUString sAppend;
+ if (sTableNamePattern.match(wld))
+ sAppend = "WHERE priv.RDB$RELATION_NAME LIKE '%' ";
+ else
+ sAppend = "WHERE priv.RDB$RELATION_NAME = '%' ";
+
+ queryBuf.append(sAppend.replaceAll(wld, sTableNamePattern));
+ }
+ queryBuf.append(" ORDER BY priv.RDB$RELATION_TYPE, "
+ "priv.RDB$RELATION_NAME, "
+ "priv.RDB$PRIVILEGE");
+
+ OUString query = queryBuf.makeStringAndClear();
+
+ uno::Reference< XResultSet > rs = statement->executeQuery(query);
+ uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW );
+ ODatabaseMetaDataResultSet::ORows aResults;
+
+ ODatabaseMetaDataResultSet::ORow aRow(8);
+ aRow[0] = new ORowSetValueDecorator(); // Unused
+ aRow[1] = new ORowSetValueDecorator(); // TABLE_CAT unsupported
+ aRow[2] = new ORowSetValueDecorator(); // TABLE_SCHEM unsupported.
+
+ while( rs->next() )
+ {
+ // 3. TABLE_NAME
+ aRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1)));
+ aRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2))); // 4. GRANTOR
+ aRow[5] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(3))); // 5. GRANTEE
+ aRow[6] = new ORowSetValueDecorator(xRow->getString(4)); // 6. Privilege
+ aRow[7] = new ORowSetValueDecorator(ORowSetValue(bool(xRow->getBoolean(5)))); // 7. Is Grantable
+
+ aResults.push_back(aRow);
+ }
+
+ pResultSet->setRows( std::move(aResults) );
+
+ return pResultSet;
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getCrossReference(
+ const Any&, const OUString&,
+ const OUString&, const Any&,
+ const OUString&, const OUString& )
+{
+ OSL_FAIL("Not implemented yet!");
+ // TODO implement
+ return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eCrossReference);
+}
+
+uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getUDTs( const Any&, const OUString&, const OUString&, const Sequence< sal_Int32 >& )
+{
+ OSL_FAIL("Not implemented yet!");
+ // TODO implement
+ return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eUDTs);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/DatabaseMetaData.hxx b/connectivity/source/drivers/firebird/DatabaseMetaData.hxx
new file mode 100644
index 0000000000..c577f594d2
--- /dev/null
+++ b/connectivity/source/drivers/firebird/DatabaseMetaData.hxx
@@ -0,0 +1,205 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "Connection.hxx"
+
+#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+namespace connectivity::firebird
+ {
+
+ //************ Class: ODatabaseMetaData
+
+
+ typedef ::cppu::WeakImplHelper< css::sdbc::XDatabaseMetaData> ODatabaseMetaData_BASE;
+
+ class ODatabaseMetaData : public ODatabaseMetaData_BASE
+ {
+ ::rtl::Reference<Connection> m_pConnection;
+ private:
+ css::uno::Reference< css::sdbc::XResultSet > lcl_getKeys( bool bIsImport, std::u16string_view table );
+ public:
+
+ explicit ODatabaseMetaData(Connection* _pCon);
+ virtual ~ODatabaseMetaData() override;
+
+ // as I mentioned before this interface is really BIG
+ // XDatabaseMetaData
+ virtual sal_Bool SAL_CALL allProceduresAreCallable( ) override;
+ virtual sal_Bool SAL_CALL allTablesAreSelectable( ) override;
+ virtual OUString SAL_CALL getURL( ) override;
+ virtual OUString SAL_CALL getUserName( ) override;
+ virtual sal_Bool SAL_CALL isReadOnly( ) override;
+ virtual sal_Bool SAL_CALL nullsAreSortedHigh( ) override;
+ virtual sal_Bool SAL_CALL nullsAreSortedLow( ) override;
+ virtual sal_Bool SAL_CALL nullsAreSortedAtStart( ) override;
+ virtual sal_Bool SAL_CALL nullsAreSortedAtEnd( ) override;
+ virtual OUString SAL_CALL getDatabaseProductName( ) override;
+ virtual OUString SAL_CALL getDatabaseProductVersion( ) override;
+ virtual OUString SAL_CALL getDriverName( ) override;
+ virtual OUString SAL_CALL getDriverVersion( ) override;
+ virtual sal_Int32 SAL_CALL getDriverMajorVersion( ) override;
+ virtual sal_Int32 SAL_CALL getDriverMinorVersion( ) override;
+ virtual sal_Bool SAL_CALL usesLocalFiles( ) override;
+ virtual sal_Bool SAL_CALL usesLocalFilePerTable( ) override;
+ virtual sal_Bool SAL_CALL supportsMixedCaseIdentifiers( ) override;
+ virtual sal_Bool SAL_CALL storesUpperCaseIdentifiers( ) override;
+ virtual sal_Bool SAL_CALL storesLowerCaseIdentifiers( ) override;
+ virtual sal_Bool SAL_CALL storesMixedCaseIdentifiers( ) override;
+ virtual sal_Bool SAL_CALL supportsMixedCaseQuotedIdentifiers( ) override;
+ virtual sal_Bool SAL_CALL storesUpperCaseQuotedIdentifiers( ) override;
+ virtual sal_Bool SAL_CALL storesLowerCaseQuotedIdentifiers( ) override;
+ virtual sal_Bool SAL_CALL storesMixedCaseQuotedIdentifiers( ) override;
+ virtual OUString SAL_CALL getIdentifierQuoteString( ) override;
+ virtual OUString SAL_CALL getSQLKeywords( ) override;
+ virtual OUString SAL_CALL getNumericFunctions( ) override;
+ virtual OUString SAL_CALL getStringFunctions( ) override;
+ virtual OUString SAL_CALL getSystemFunctions( ) override;
+ virtual OUString SAL_CALL getTimeDateFunctions( ) override;
+ virtual OUString SAL_CALL getSearchStringEscape( ) override;
+ virtual OUString SAL_CALL getExtraNameCharacters( ) override;
+ virtual sal_Bool SAL_CALL supportsAlterTableWithAddColumn( ) override;
+ virtual sal_Bool SAL_CALL supportsAlterTableWithDropColumn( ) override;
+ virtual sal_Bool SAL_CALL supportsColumnAliasing( ) override;
+ virtual sal_Bool SAL_CALL nullPlusNonNullIsNull( ) override;
+ virtual sal_Bool SAL_CALL supportsTypeConversion( ) override;
+ virtual sal_Bool SAL_CALL supportsConvert( sal_Int32 fromType, sal_Int32 toType ) override;
+ virtual sal_Bool SAL_CALL supportsTableCorrelationNames( ) override;
+ virtual sal_Bool SAL_CALL supportsDifferentTableCorrelationNames( ) override;
+ virtual sal_Bool SAL_CALL supportsExpressionsInOrderBy( ) override;
+ virtual sal_Bool SAL_CALL supportsOrderByUnrelated( ) override;
+ virtual sal_Bool SAL_CALL supportsGroupBy( ) override;
+ virtual sal_Bool SAL_CALL supportsGroupByUnrelated( ) override;
+ virtual sal_Bool SAL_CALL supportsGroupByBeyondSelect( ) override;
+ virtual sal_Bool SAL_CALL supportsLikeEscapeClause( ) override;
+ virtual sal_Bool SAL_CALL supportsMultipleResultSets( ) override;
+ virtual sal_Bool SAL_CALL supportsMultipleTransactions( ) override;
+ virtual sal_Bool SAL_CALL supportsNonNullableColumns( ) override;
+ virtual sal_Bool SAL_CALL supportsMinimumSQLGrammar( ) override;
+ virtual sal_Bool SAL_CALL supportsCoreSQLGrammar( ) override;
+ virtual sal_Bool SAL_CALL supportsExtendedSQLGrammar( ) override;
+ virtual sal_Bool SAL_CALL supportsANSI92EntryLevelSQL( ) override;
+ virtual sal_Bool SAL_CALL supportsANSI92IntermediateSQL( ) override;
+ virtual sal_Bool SAL_CALL supportsANSI92FullSQL( ) override;
+ virtual sal_Bool SAL_CALL supportsIntegrityEnhancementFacility( ) override;
+ virtual sal_Bool SAL_CALL supportsOuterJoins( ) override;
+ virtual sal_Bool SAL_CALL supportsFullOuterJoins( ) override;
+ virtual sal_Bool SAL_CALL supportsLimitedOuterJoins( ) override;
+ virtual OUString SAL_CALL getSchemaTerm( ) override;
+ virtual OUString SAL_CALL getProcedureTerm( ) override;
+ virtual OUString SAL_CALL getCatalogTerm( ) override;
+ virtual sal_Bool SAL_CALL isCatalogAtStart( ) override;
+ virtual OUString SAL_CALL getCatalogSeparator( ) override;
+ virtual sal_Bool SAL_CALL supportsSchemasInDataManipulation( ) override;
+ virtual sal_Bool SAL_CALL supportsSchemasInProcedureCalls( ) override;
+ virtual sal_Bool SAL_CALL supportsSchemasInTableDefinitions( ) override;
+ virtual sal_Bool SAL_CALL supportsSchemasInIndexDefinitions( ) override;
+ virtual sal_Bool SAL_CALL supportsSchemasInPrivilegeDefinitions( ) override;
+ virtual sal_Bool SAL_CALL supportsCatalogsInDataManipulation( ) override;
+ virtual sal_Bool SAL_CALL supportsCatalogsInProcedureCalls( ) override;
+ virtual sal_Bool SAL_CALL supportsCatalogsInTableDefinitions( ) override;
+ virtual sal_Bool SAL_CALL supportsCatalogsInIndexDefinitions( ) override;
+ virtual sal_Bool SAL_CALL supportsCatalogsInPrivilegeDefinitions( ) override;
+ virtual sal_Bool SAL_CALL supportsPositionedDelete( ) override;
+ virtual sal_Bool SAL_CALL supportsPositionedUpdate( ) override;
+ virtual sal_Bool SAL_CALL supportsSelectForUpdate( ) override;
+ virtual sal_Bool SAL_CALL supportsStoredProcedures( ) override;
+ virtual sal_Bool SAL_CALL supportsSubqueriesInComparisons( ) override;
+ virtual sal_Bool SAL_CALL supportsSubqueriesInExists( ) override;
+ virtual sal_Bool SAL_CALL supportsSubqueriesInIns( ) override;
+ virtual sal_Bool SAL_CALL supportsSubqueriesInQuantifieds( ) override;
+ virtual sal_Bool SAL_CALL supportsCorrelatedSubqueries( ) override;
+ virtual sal_Bool SAL_CALL supportsUnion( ) override;
+ virtual sal_Bool SAL_CALL supportsUnionAll( ) override;
+ virtual sal_Bool SAL_CALL supportsOpenCursorsAcrossCommit( ) override;
+ virtual sal_Bool SAL_CALL supportsOpenCursorsAcrossRollback( ) override;
+ virtual sal_Bool SAL_CALL supportsOpenStatementsAcrossCommit( ) override;
+ virtual sal_Bool SAL_CALL supportsOpenStatementsAcrossRollback( ) override;
+ virtual sal_Int32 SAL_CALL getMaxBinaryLiteralLength( ) override;
+ virtual sal_Int32 SAL_CALL getMaxCharLiteralLength( ) override;
+ virtual sal_Int32 SAL_CALL getMaxColumnNameLength( ) override;
+ virtual sal_Int32 SAL_CALL getMaxColumnsInGroupBy( ) override;
+ virtual sal_Int32 SAL_CALL getMaxColumnsInIndex( ) override;
+ virtual sal_Int32 SAL_CALL getMaxColumnsInOrderBy( ) override;
+ virtual sal_Int32 SAL_CALL getMaxColumnsInSelect( ) override;
+ virtual sal_Int32 SAL_CALL getMaxColumnsInTable( ) override;
+ virtual sal_Int32 SAL_CALL getMaxConnections( ) override;
+ virtual sal_Int32 SAL_CALL getMaxCursorNameLength( ) override;
+ virtual sal_Int32 SAL_CALL getMaxIndexLength( ) override;
+ virtual sal_Int32 SAL_CALL getMaxSchemaNameLength( ) override;
+ virtual sal_Int32 SAL_CALL getMaxProcedureNameLength( ) override;
+ virtual sal_Int32 SAL_CALL getMaxCatalogNameLength( ) override;
+ virtual sal_Int32 SAL_CALL getMaxRowSize( ) override;
+ virtual sal_Bool SAL_CALL doesMaxRowSizeIncludeBlobs( ) override;
+ virtual sal_Int32 SAL_CALL getMaxStatementLength( ) override;
+ virtual sal_Int32 SAL_CALL getMaxStatements( ) override;
+ virtual sal_Int32 SAL_CALL getMaxTableNameLength( ) override;
+ virtual sal_Int32 SAL_CALL getMaxTablesInSelect( ) override;
+ virtual sal_Int32 SAL_CALL getMaxUserNameLength( ) override;
+ virtual sal_Int32 SAL_CALL getDefaultTransactionIsolation( ) override;
+ virtual sal_Bool SAL_CALL supportsTransactions( ) override;
+ virtual sal_Bool SAL_CALL supportsTransactionIsolationLevel( sal_Int32 level ) override;
+ virtual sal_Bool SAL_CALL supportsDataDefinitionAndDataManipulationTransactions( ) override;
+ virtual sal_Bool SAL_CALL supportsDataManipulationTransactionsOnly( ) override;
+ virtual sal_Bool SAL_CALL dataDefinitionCausesTransactionCommit( ) override;
+ virtual sal_Bool SAL_CALL dataDefinitionIgnoredInTransactions( ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getProcedures( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& procedureNamePattern ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getProcedureColumns( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& procedureNamePattern, const OUString& columnNamePattern ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTables( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const css::uno::Sequence< OUString >& types ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getSchemas( ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getCatalogs( ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTableTypes( ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getColumns( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern, const OUString& columnNamePattern ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getColumnPrivileges( const css::uno::Any& catalog, const OUString& schema, const OUString& table, const OUString& columnNamePattern ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTablePrivileges( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& tableNamePattern ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getBestRowIdentifier( const css::uno::Any& catalog, const OUString& schema, const OUString& table, sal_Int32 scope, sal_Bool nullable ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getVersionColumns( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getPrimaryKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getImportedKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getExportedKeys( const css::uno::Any& catalog, const OUString& schema, const OUString& table ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getCrossReference( const css::uno::Any& primaryCatalog, const OUString& primarySchema, const OUString& primaryTable, const css::uno::Any& foreignCatalog, const OUString& foreignSchema, const OUString& foreignTable ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getTypeInfo( ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getIndexInfo( const css::uno::Any& catalog, const OUString& schema, const OUString& table, sal_Bool unique, sal_Bool approximate ) override;
+ virtual sal_Bool SAL_CALL supportsResultSetType( sal_Int32 setType ) override;
+ virtual sal_Bool SAL_CALL supportsResultSetConcurrency( sal_Int32 setType, sal_Int32 concurrency ) override;
+ virtual sal_Bool SAL_CALL ownUpdatesAreVisible( sal_Int32 setType ) override;
+ virtual sal_Bool SAL_CALL ownDeletesAreVisible( sal_Int32 setType ) override;
+ virtual sal_Bool SAL_CALL ownInsertsAreVisible( sal_Int32 setType ) override;
+ virtual sal_Bool SAL_CALL othersUpdatesAreVisible( sal_Int32 setType ) override;
+ virtual sal_Bool SAL_CALL othersDeletesAreVisible( sal_Int32 setType ) override;
+ virtual sal_Bool SAL_CALL othersInsertsAreVisible( sal_Int32 setType ) override;
+ virtual sal_Bool SAL_CALL updatesAreDetected( sal_Int32 setType ) override;
+ virtual sal_Bool SAL_CALL deletesAreDetected( sal_Int32 setType ) override;
+ virtual sal_Bool SAL_CALL insertsAreDetected( sal_Int32 setType ) override;
+ virtual sal_Bool SAL_CALL supportsBatchUpdates( ) override;
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getUDTs( const css::uno::Any& catalog, const OUString& schemaPattern, const OUString& typeNamePattern, const css::uno::Sequence< sal_Int32 >& types ) override;
+ virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( ) override;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Driver.cxx b/connectivity/source/drivers/firebird/Driver.cxx
new file mode 100644
index 0000000000..b674d56c22
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Driver.cxx
@@ -0,0 +1,228 @@
+/* -*- 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 "Connection.hxx"
+#include "Driver.hxx"
+#include "SubComponent.hxx"
+
+#include <connectivity/dbexception.hxx>
+#include <strings.hrc>
+#include <resource/sharedresources.hxx>
+
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::sdbc;
+using namespace com::sun::star::sdbcx;
+
+using namespace ::osl;
+
+using namespace connectivity::firebird;
+
+// Static const variables
+namespace {
+constexpr OUString our_sFirebirdTmpVar = u"FIREBIRD_TMP"_ustr;
+constexpr OUString our_sFirebirdLockVar = u"FIREBIRD_LOCK"_ustr;
+constexpr OUString our_sFirebirdMsgVar = u"FIREBIRD_MSG"_ustr;
+#ifdef MACOSX
+constexpr OUString our_sFirebirdLibVar = u"LIBREOFFICE_FIREBIRD_LIB"_ustr;
+#endif
+};
+
+FirebirdDriver::FirebirdDriver(const css::uno::Reference< css::uno::XComponentContext >& _rxContext)
+ : ODriver_BASE(m_aMutex)
+ , m_aContext(_rxContext)
+ , m_firebirdTMPDirectory(nullptr, true)
+ , m_firebirdLockDirectory(nullptr, true)
+{
+ // ::utl::TempFile uses a unique temporary directory (subdirectory of
+ // /tmp or other user specific tmp directory) per instance in which
+ // we can create directories for firebird at will.
+ m_firebirdTMPDirectory.EnableKillingFile(true);
+ m_firebirdLockDirectory.EnableKillingFile(true);
+
+ // Overrides firebird's default of /tmp or c:\temp
+ osl_setEnvironment(our_sFirebirdTmpVar.pData, m_firebirdTMPDirectory.GetFileName().pData);
+
+ // Overrides firebird's default of /tmp/firebird or c:\temp\firebird
+ osl_setEnvironment(our_sFirebirdLockVar.pData, m_firebirdLockDirectory.GetFileName().pData);
+
+#ifndef SYSTEM_FIREBIRD
+ // Overrides firebird's hardcoded default of /usr/local/firebird on *nix,
+ // however on Windows it seems to use the current directory as a default.
+ OUString sMsgURL("$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/firebird");
+ ::rtl::Bootstrap::expandMacros(sMsgURL);
+ OUString sMsgPath;
+ ::osl::FileBase::getSystemPathFromFileURL(sMsgURL, sMsgPath);
+ osl_setEnvironment(our_sFirebirdMsgVar.pData, sMsgPath.pData);
+#ifdef MACOSX
+ // Set an env. variable to specify library location
+ // for dlopen used in fbclient.
+ OUString sLibURL("$LO_LIB_DIR");
+ ::rtl::Bootstrap::expandMacros(sLibURL);
+ OUString sLibPath;
+ ::osl::FileBase::getSystemPathFromFileURL(sLibURL, sLibPath);
+ osl_setEnvironment(our_sFirebirdLibVar.pData, sLibPath.pData);
+#endif /*MACOSX*/
+#endif /*!SYSTEM_FIREBIRD*/
+}
+
+FirebirdDriver::~FirebirdDriver() = default;
+
+void FirebirdDriver::disposing()
+{
+ MutexGuard aGuard(m_aMutex);
+
+ for (auto const& elem : m_xConnections)
+ {
+ Reference< XComponent > xComp(elem.get(), UNO_QUERY);
+ if (xComp.is())
+ xComp->dispose();
+ }
+ m_xConnections.clear();
+
+ osl_clearEnvironment(our_sFirebirdTmpVar.pData);
+ osl_clearEnvironment(our_sFirebirdLockVar.pData);
+
+#ifndef SYSTEM_FIREBIRD
+ osl_clearEnvironment(our_sFirebirdMsgVar.pData);
+#ifdef MACOSX
+ osl_clearEnvironment(our_sFirebirdLibVar.pData);
+#endif /*MACOSX*/
+#endif /*!SYSTEM_FIREBIRD*/
+
+ OSL_VERIFY(fb_shutdown(0, 1));
+
+ ODriver_BASE::disposing();
+}
+
+OUString SAL_CALL FirebirdDriver::getImplementationName()
+{
+ return "com.sun.star.comp.sdbc.firebird.Driver";
+}
+
+sal_Bool SAL_CALL FirebirdDriver::supportsService(const OUString& _rServiceName)
+{
+ return cppu::supportsService(this, _rServiceName);
+}
+
+Sequence< OUString > SAL_CALL FirebirdDriver::getSupportedServiceNames()
+{
+ return { "com.sun.star.sdbc.Driver", "com.sun.star.sdbcx.Driver" };
+}
+
+// ---- XDriver -------------------------------------------------------------
+Reference< XConnection > SAL_CALL FirebirdDriver::connect(
+ const OUString& url, const Sequence< PropertyValue >& info )
+{
+ SAL_INFO("connectivity.firebird", "connect(), URL: " << url );
+
+ MutexGuard aGuard( m_aMutex );
+ if (ODriver_BASE::rBHelper.bDisposed)
+ throw DisposedException();
+
+ if ( ! acceptsURL(url) )
+ return nullptr;
+
+ rtl::Reference<Connection> pCon = new Connection();
+ pCon->construct(url, info);
+
+ m_xConnections.push_back(WeakReferenceHelper(*pCon));
+
+ return pCon;
+}
+
+sal_Bool SAL_CALL FirebirdDriver::acceptsURL( const OUString& url )
+{
+ return (url == "sdbc:embedded:firebird" || url.startsWith("sdbc:firebird:"));
+}
+
+Sequence< DriverPropertyInfo > SAL_CALL FirebirdDriver::getPropertyInfo(
+ const OUString& url, const Sequence< PropertyValue >& )
+{
+ if ( ! acceptsURL(url) )
+ {
+ ::connectivity::SharedResources aResources;
+ const OUString sMessage = aResources.getResourceString(STR_URI_SYNTAX_ERROR);
+ ::dbtools::throwGenericSQLException(sMessage ,*this);
+ }
+
+ return Sequence< DriverPropertyInfo >();
+}
+
+sal_Int32 SAL_CALL FirebirdDriver::getMajorVersion( )
+{
+ // The major and minor version are sdbc driver specific. Must begin with 1.0
+ // as per https://api.libreoffice.org/docs/common/ref/com/sun/star/sdbc/XDriver.html
+ return 1;
+}
+
+sal_Int32 SAL_CALL FirebirdDriver::getMinorVersion( )
+{
+ return 0;
+}
+
+//----- XDataDefinitionSupplier
+uno::Reference< XTablesSupplier > SAL_CALL FirebirdDriver::getDataDefinitionByConnection(
+ const uno::Reference< XConnection >& rConnection)
+{
+ if (Connection* pConnection = comphelper::getFromUnoTunnel<Connection>(rConnection))
+ return pConnection->createCatalog();
+ return {};
+}
+
+uno::Reference< XTablesSupplier > SAL_CALL FirebirdDriver::getDataDefinitionByURL(
+ const OUString& rURL,
+ const uno::Sequence< PropertyValue >& rInfo)
+{
+ uno::Reference< XConnection > xConnection = connect(rURL, rInfo);
+ return getDataDefinitionByConnection(xConnection);
+}
+
+namespace connectivity::firebird
+{
+ void checkDisposed(bool _bThrow)
+ {
+ if (_bThrow)
+ throw DisposedException();
+
+ }
+
+} // namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+connectivity_FirebirdDriver_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ try {
+ return cppu::acquire(new FirebirdDriver(context));
+ } catch (...) {
+ return nullptr;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Driver.hxx b/connectivity/source/drivers/firebird/Driver.hxx
new file mode 100644
index 0000000000..d884b5008d
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Driver.hxx
@@ -0,0 +1,90 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include "Connection.hxx"
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/sdbc/XDriver.hpp>
+#include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <unotools/tempfile.hxx>
+
+namespace connectivity::firebird
+ {
+ // The SQL dialect in use
+ // Has to be used in various isc_* calls.
+ // 3: Is IB6 -- minimum required for delimited identifiers.
+ // SQL_DIALECT_V6 = 3, it's the last current version
+ // SQL_DIALECT_CURRENT is an alias for SQL_DIALECT_V6
+ // See src/dsql/sqlda_pub.h for full details
+
+ typedef ::cppu::WeakComponentImplHelper< css::sdbc::XDriver,
+ css::sdbcx::XDataDefinitionSupplier,
+ css::lang::XServiceInfo > ODriver_BASE;
+
+ class FirebirdDriver : public ODriver_BASE
+ {
+ private:
+ css::uno::Reference<css::uno::XComponentContext> m_aContext;
+ ::utl::TempFileNamed m_firebirdTMPDirectory;
+ ::utl::TempFileNamed m_firebirdLockDirectory;
+
+ protected:
+ ::osl::Mutex m_aMutex; // mutex is need to control member access
+ OWeakRefArray m_xConnections; // vector containing a list
+ // of all the Connection objects
+ // for this Driver
+
+ public:
+
+ explicit FirebirdDriver(const css::uno::Reference< css::uno::XComponentContext >& _rxContext);
+ virtual ~FirebirdDriver() override;
+ const css::uno::Reference<css::uno::XComponentContext>& getContext() const { return m_aContext; }
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XDriver
+ virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL connect( const OUString& url, const css::uno::Sequence< css::beans::PropertyValue >& info ) override;
+ virtual sal_Bool SAL_CALL acceptsURL( const OUString& url ) override;
+ virtual css::uno::Sequence< css::sdbc::DriverPropertyInfo > SAL_CALL getPropertyInfo( const OUString& url, const css::uno::Sequence< css::beans::PropertyValue >& info ) override;
+ virtual sal_Int32 SAL_CALL getMajorVersion( ) override;
+ virtual sal_Int32 SAL_CALL getMinorVersion( ) override;
+
+ // XDataDefinitionSupplier
+ virtual css::uno::Reference< css::sdbcx::XTablesSupplier >
+ SAL_CALL getDataDefinitionByConnection(
+ const css::uno::Reference< css::sdbc::XConnection >& rxConnection) override;
+ virtual css::uno::Reference< css::sdbcx::XTablesSupplier >
+ SAL_CALL getDataDefinitionByURL(
+ const OUString& rsURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& rInfo) override;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Indexes.cxx b/connectivity/source/drivers/firebird/Indexes.cxx
new file mode 100644
index 0000000000..7bf783c79c
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Indexes.cxx
@@ -0,0 +1,33 @@
+/* -*- 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/.
+ */
+
+#include "Indexes.hxx"
+
+using namespace ::connectivity;
+using namespace ::connectivity::firebird;
+
+using namespace ::osl;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::sdbc;
+
+Indexes::Indexes(Table* pTable, Mutex& rMutex, const std::vector<OUString>& rVector)
+ : OIndexesHelper(pTable, rMutex, rVector)
+ , m_pTable(pTable)
+{
+}
+
+// XDrop
+void Indexes::dropObject(sal_Int32 /*nPosition*/, const OUString& sIndexName)
+{
+ OUString sSql("DROP INDEX \"" + sIndexName + "\"");
+ m_pTable->getConnection()->createStatement()->execute(sSql);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Indexes.hxx b/connectivity/source/drivers/firebird/Indexes.hxx
new file mode 100644
index 0000000000..12d7dd1980
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Indexes.hxx
@@ -0,0 +1,39 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include "Table.hxx"
+
+#include <connectivity/TIndexes.hxx>
+
+namespace connectivity::firebird
+ {
+
+ /**
+ * Firebird has a non-standard DROP INDEX statement, hence we need
+ * to override OIndexesHelper::dropObject
+ */
+ class Indexes: public ::connectivity::OIndexesHelper
+ {
+ private:
+ Table* m_pTable;
+ protected:
+ // XDrop
+ virtual void dropObject(sal_Int32 nPosition,
+ const OUString& sIndexName) override;
+ public:
+ Indexes(Table* pTable,
+ ::osl::Mutex& rMutex,
+ const std::vector< OUString>& rVector);
+ };
+
+} // namespace connectivity::firebird
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Keys.cxx b/connectivity/source/drivers/firebird/Keys.cxx
new file mode 100644
index 0000000000..8de112ec6f
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Keys.cxx
@@ -0,0 +1,54 @@
+/* -*- 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/.
+ */
+
+#include "Keys.hxx"
+#include "Table.hxx"
+
+#include <connectivity/dbtools.hxx>
+
+using namespace ::connectivity;
+using namespace ::connectivity::firebird;
+
+using namespace ::dbtools;
+using namespace ::osl;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::uno;
+
+Keys::Keys(Table* pTable, Mutex& rMutex, const ::std::vector< OUString>& rNames):
+ OKeysHelper(pTable,
+ rMutex,
+ rNames),
+ m_pTable(pTable)
+{
+}
+
+//----- XDrop ----------------------------------------------------------------
+void Keys::dropObject(sal_Int32 nPosition, const OUString& sName)
+{
+ if (m_pTable->isNew())
+ return;
+
+ uno::Reference<XPropertySet> xKey(getObject(nPosition), UNO_QUERY);
+
+ if (xKey.is())
+ {
+ const OUString sQuote = m_pTable->getConnection()->getMetaData()
+ ->getIdentifierQuoteString();
+
+ OUString sSql("ALTER TABLE " + quoteName(sQuote, m_pTable->getName())
+ + " DROP CONSTRAINT " + quoteName(sQuote, sName));
+
+ m_pTable->getConnection()->createStatement()->execute(sSql);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Keys.hxx b/connectivity/source/drivers/firebird/Keys.hxx
new file mode 100644
index 0000000000..4e9ba5a7ee
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Keys.hxx
@@ -0,0 +1,36 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <connectivity/TKeys.hxx>
+
+namespace connectivity::firebird
+ {
+
+ class Table;
+
+ class Keys: public ::connectivity::OKeysHelper
+ {
+ private:
+ Table* m_pTable;
+
+ public:
+ Keys(Table* pTable,
+ ::osl::Mutex& rMutex,
+ const ::std::vector< OUString>& rNames);
+
+ // OKeysHelper / XDrop
+ void dropObject(sal_Int32 nPosition, const OUString& sName) override;
+
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/PreparedStatement.cxx b/connectivity/source/drivers/firebird/PreparedStatement.cxx
new file mode 100644
index 0000000000..608d05c274
--- /dev/null
+++ b/connectivity/source/drivers/firebird/PreparedStatement.cxx
@@ -0,0 +1,1058 @@
+/* -*- 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>
+#include <cmath>
+
+#include "Connection.hxx"
+#include "PreparedStatement.hxx"
+#include "ResultSet.hxx"
+#include "ResultSetMetaData.hxx"
+#include "Util.hxx"
+
+#include <comphelper/sequence.hxx>
+#include <connectivity/dbexception.hxx>
+#include <propertyids.hxx>
+#include <connectivity/dbtools.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/sdbc/DataType.hpp>
+
+using namespace connectivity::firebird;
+
+using namespace ::comphelper;
+using namespace ::osl;
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::sdbc;
+using namespace com::sun::star::container;
+using namespace com::sun::star::io;
+using namespace com::sun::star::util;
+
+IMPLEMENT_SERVICE_INFO(OPreparedStatement,"com.sun.star.sdbcx.firebird.PreparedStatement","com.sun.star.sdbc.PreparedStatement");
+
+constexpr size_t MAX_SIZE_SEGMENT = 65535; // max value of a segment of CLOB, if we want more than 65535 bytes, we need more segments
+
+
+OPreparedStatement::OPreparedStatement( Connection* _pConnection,
+ const OUString& sql)
+ :OStatementCommonBase(_pConnection)
+ ,m_sSqlStatement(sql)
+ ,m_pOutSqlda(nullptr)
+ ,m_pInSqlda(nullptr)
+{
+ SAL_INFO("connectivity.firebird", "OPreparedStatement(). "
+ "sql: " << sql);
+}
+
+void OPreparedStatement::ensurePrepared()
+{
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+
+ if (m_aStatementHandle)
+ return;
+
+ ISC_STATUS aErr = 0;
+
+ if (!m_pInSqlda)
+ {
+ m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(10)));
+ m_pInSqlda->version = SQLDA_VERSION1;
+ m_pInSqlda->sqln = 10;
+ }
+
+ prepareAndDescribeStatement(m_sSqlStatement, m_pOutSqlda);
+
+ aErr = isc_dsql_describe_bind(m_statusVector,
+ &m_aStatementHandle,
+ 1,
+ m_pInSqlda);
+
+ if (aErr)
+ {
+ SAL_WARN("connectivity.firebird", "isc_dsql_describe_bind failed");
+ }
+ else if (m_pInSqlda->sqld > m_pInSqlda->sqln) // Not large enough
+ {
+ short nItems = m_pInSqlda->sqld;
+ free(m_pInSqlda);
+ m_pInSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(nItems)));
+ m_pInSqlda->version = SQLDA_VERSION1;
+ m_pInSqlda->sqln = nItems;
+ aErr = isc_dsql_describe_bind(m_statusVector,
+ &m_aStatementHandle,
+ 1,
+ m_pInSqlda);
+ SAL_WARN_IF(aErr, "connectivity.firebird", "isc_dsql_describe_bind failed");
+ }
+
+ if (!aErr)
+ mallocSQLVAR(m_pInSqlda);
+ else
+ evaluateStatusVector(m_statusVector, m_sSqlStatement, *this);
+}
+
+OPreparedStatement::~OPreparedStatement()
+{
+}
+
+void SAL_CALL OPreparedStatement::acquire() noexcept
+{
+ OStatementCommonBase::acquire();
+}
+
+void SAL_CALL OPreparedStatement::release() noexcept
+{
+ OStatementCommonBase::release();
+}
+
+Any SAL_CALL OPreparedStatement::queryInterface(const Type& rType)
+{
+ Any aRet = OStatementCommonBase::queryInterface(rType);
+ if(!aRet.hasValue())
+ aRet = OPreparedStatement_Base::queryInterface(rType);
+ return aRet;
+}
+
+uno::Sequence< Type > SAL_CALL OPreparedStatement::getTypes()
+{
+ return concatSequences(OPreparedStatement_Base::getTypes(),
+ OStatementCommonBase::getTypes());
+}
+
+Reference< XResultSetMetaData > SAL_CALL OPreparedStatement::getMetaData()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ ensurePrepared();
+
+ if(!m_xMetaData.is())
+ m_xMetaData = new OResultSetMetaData(m_pConnection.get()
+ , m_pOutSqlda);
+
+ return m_xMetaData;
+}
+
+void SAL_CALL OPreparedStatement::close()
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+
+ OStatementCommonBase::close();
+ if (m_pInSqlda)
+ {
+ freeSQLVAR(m_pInSqlda);
+ free(m_pInSqlda);
+ m_pInSqlda = nullptr;
+ }
+ if (m_pOutSqlda)
+ {
+ freeSQLVAR(m_pOutSqlda);
+ free(m_pOutSqlda);
+ m_pOutSqlda = nullptr;
+ }
+}
+
+void SAL_CALL OPreparedStatement::disposing()
+{
+ close();
+}
+
+void SAL_CALL OPreparedStatement::setString(sal_Int32 nParameterIndex,
+ const OUString& sInput)
+{
+ SAL_INFO("connectivity.firebird",
+ "setString(" << nParameterIndex << " , " << sInput << ")");
+
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ ensurePrepared();
+
+ checkParameterIndex(nParameterIndex);
+ setParameterNull(nParameterIndex, false);
+
+ OString str = OUStringToOString(sInput , RTL_TEXTENCODING_UTF8 );
+
+ XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
+
+ int dtype = (pVar->sqltype & ~1); // drop flag bit for now
+
+ if (str.getLength() > pVar->sqllen)
+ str = str.copy(0, pVar->sqllen);
+
+ switch (dtype) {
+ case SQL_VARYING:
+ {
+ const sal_Int32 max_varchar_len = 0xFFFF;
+ // First 2 bytes indicate string size
+ if (str.getLength() > max_varchar_len)
+ {
+ str = str.copy(0, max_varchar_len);
+ }
+ const sal_uInt16 nLength = str.getLength();
+ static_assert(sizeof(nLength) == 2, "must match dest memcpy len");
+ memcpy(pVar->sqldata, &nLength, 2);
+ // Actual data
+ memcpy(pVar->sqldata + 2, str.getStr(), str.getLength());
+ break;
+ }
+ case SQL_TEXT:
+ memcpy(pVar->sqldata, str.getStr(), str.getLength());
+ // Fill remainder with spaces
+ memset(pVar->sqldata + str.getLength(), ' ', pVar->sqllen - str.getLength());
+ break;
+ case SQL_BLOB: // Clob
+ assert( pVar->sqlsubtype == static_cast<short>(BlobSubtype::Clob) );
+ setClob(nParameterIndex, sInput );
+ break;
+ case SQL_SHORT:
+ {
+ sal_Int32 int32Value = sInput.toInt32();
+ if ( (int32Value < std::numeric_limits<sal_Int16>::min()) ||
+ (int32Value > std::numeric_limits<sal_Int16>::max()) )
+ {
+ ::dbtools::throwSQLException(
+ "Value out of range for SQL_SHORT type",
+ ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
+ *this);
+ }
+ setShort(nParameterIndex, int32Value);
+ break;
+ }
+ case SQL_LONG:
+ {
+ sal_Int32 int32Value = sInput.toInt32();
+ setInt(nParameterIndex, int32Value);
+ break;
+ }
+ case SQL_INT64:
+ {
+ sal_Int64 int64Value = sInput.toInt64();
+ setLong(nParameterIndex, int64Value);
+ break;
+ }
+ case SQL_FLOAT:
+ {
+ float floatValue = sInput.toFloat();
+ setFloat(nParameterIndex, floatValue);
+ break;
+ }
+ case SQL_BOOLEAN:
+ {
+ bool boolValue = sInput.toBoolean();
+ setBoolean(nParameterIndex, boolValue);
+ break;
+ }
+ case SQL_NULL:
+ {
+ // See https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref25/firebird-25-language-reference.html#fblangref25-datatypes-special-sqlnull
+ pVar->sqldata = nullptr;
+ break;
+ }
+ default:
+ ::dbtools::throwSQLException(
+ "Incorrect type for setString",
+ ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
+ *this);
+ }
+}
+
+Reference< XConnection > SAL_CALL OPreparedStatement::getConnection()
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+
+ return m_pConnection;
+}
+
+sal_Bool SAL_CALL OPreparedStatement::execute()
+{
+ SAL_INFO("connectivity.firebird", "executeQuery(). "
+ "Got called with sql: " << m_sSqlStatement);
+
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+
+ ensurePrepared();
+
+ ISC_STATUS aErr;
+
+ if (m_xResultSet.is()) // Checks whether we have already run the statement.
+ {
+ disposeResultSet();
+ // Closes the cursor from the last run.
+ // This doesn't actually free the statement -- using DSQL_close closes
+ // the cursor and keeps the statement, using DSQL_drop frees the statement
+ // (and associated cursors).
+ aErr = isc_dsql_free_statement(m_statusVector,
+ &m_aStatementHandle,
+ DSQL_close);
+ if (aErr)
+ {
+ // Do not throw error. Trying to close a closed cursor is not a
+ // critical mistake.
+ OUString sErrMsg = StatusVectorToString(m_statusVector,
+ u"isc_dsql_free_statement: close cursor");
+ SAL_WARN("connectivity.firebird", sErrMsg);
+ }
+ }
+
+ aErr = isc_dsql_execute(m_statusVector,
+ &m_pConnection->getTransaction(),
+ &m_aStatementHandle,
+ 1,
+ m_pInSqlda);
+ if (aErr)
+ {
+ SAL_WARN("connectivity.firebird", "isc_dsql_execute failed" );
+ evaluateStatusVector(m_statusVector, u"isc_dsql_execute", *this);
+ }
+
+ m_xResultSet = new OResultSet(m_pConnection.get(),
+ m_aMutex,
+ uno::Reference< XInterface >(*this),
+ m_aStatementHandle,
+ m_pOutSqlda);
+
+ if (getStatementChangeCount() > 0)
+ m_pConnection->notifyDatabaseModified();
+
+ return m_xResultSet.is();
+ // TODO: implement handling of multiple ResultSets.
+}
+
+sal_Int32 SAL_CALL OPreparedStatement::executeUpdate()
+{
+ execute();
+ return getStatementChangeCount();
+}
+
+Reference< XResultSet > SAL_CALL OPreparedStatement::executeQuery()
+{
+ execute();
+ return m_xResultSet;
+}
+
+namespace {
+
+/**
+ * Take out the number part of a fix point decimal without
+ * the information of where is the fractional part from a
+ * string representation of a number. (e.g. 54.654 -> 54654)
+ */
+sal_Int64 toNumericWithoutDecimalPlace(const OUString& sSource)
+{
+ OUString sNumber(sSource);
+
+ // cut off leading 0 eventually ( eg. 0.567 -> .567)
+ (void)sSource.startsWith("0", &sNumber);
+
+ sal_Int32 nDotIndex = sNumber.indexOf('.');
+
+ if( nDotIndex < 0)
+ {
+ return sNumber.toInt64(); // no dot -> it's an integer
+ }
+ else
+ {
+ // remove dot
+ OUStringBuffer sBuffer(15);
+ if(nDotIndex > 0)
+ {
+ sBuffer.append(sNumber.subView(0, nDotIndex));
+ }
+ sBuffer.append(sNumber.subView(nDotIndex + 1));
+ return o3tl::toInt64(sBuffer);
+ }
+}
+
+}
+
+//----- XParameters -----------------------------------------------------------
+void SAL_CALL OPreparedStatement::setNull(sal_Int32 nIndex, sal_Int32 /*nSqlType*/)
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ ensurePrepared();
+
+ checkParameterIndex(nIndex);
+ setParameterNull(nIndex);
+}
+
+void SAL_CALL OPreparedStatement::setBoolean(sal_Int32 nIndex, sal_Bool bValue)
+{
+ setValue< sal_Bool >(nIndex, bValue, SQL_BOOLEAN);
+}
+
+template <typename T>
+void OPreparedStatement::setValue(sal_Int32 nIndex, const T& nValue, ISC_SHORT nType)
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ ensurePrepared();
+
+ checkParameterIndex(nIndex);
+ setParameterNull(nIndex, false);
+
+ XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1);
+
+ if ((pVar->sqltype & ~1) != nType)
+ {
+ ::dbtools::throwSQLException(
+ "Incorrect type for setValue",
+ ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
+ *this);
+ }
+
+ memcpy(pVar->sqldata, &nValue, sizeof(nValue));
+}
+
+void SAL_CALL OPreparedStatement::setByte(sal_Int32 nIndex, sal_Int8 nValue)
+{
+ // there's no TINYINT or equivalent on Firebird,
+ // so do the same as setShort
+ setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT);
+}
+
+void SAL_CALL OPreparedStatement::setShort(sal_Int32 nIndex, sal_Int16 nValue)
+{
+ setValue< sal_Int16 >(nIndex, nValue, SQL_SHORT);
+}
+
+void SAL_CALL OPreparedStatement::setInt(sal_Int32 nIndex, sal_Int32 nValue)
+{
+ setValue< sal_Int32 >(nIndex, nValue, SQL_LONG);
+}
+
+void SAL_CALL OPreparedStatement::setLong(sal_Int32 nIndex, sal_Int64 nValue)
+{
+ setValue< sal_Int64 >(nIndex, nValue, SQL_INT64);
+}
+
+void SAL_CALL OPreparedStatement::setFloat(sal_Int32 nIndex, float nValue)
+{
+ setValue< float >(nIndex, nValue, SQL_FLOAT);
+}
+
+void SAL_CALL OPreparedStatement::setDouble(sal_Int32 nIndex, double nValue)
+{
+ MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ ensurePrepared();
+
+ XSQLVAR* pVar = m_pInSqlda->sqlvar + (nIndex - 1);
+ short dType = (pVar->sqltype & ~1); // drop flag bit for now
+ short dSubType = pVar->sqlsubtype;
+ // Assume it is a sub type of a number.
+ if(dSubType < 0 || dSubType > 2)
+ {
+ ::dbtools::throwSQLException(
+ "Incorrect number sub type",
+ ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
+ *this);
+ }
+ // firebird stores scale as a negative number
+ ColumnTypeInfo columnType{ dType, dSubType,
+ static_cast<short>(-pVar->sqlscale) };
+
+ // Caller might try to set an integer type here. It makes sense to convert
+ // it instead of throwing an error.
+ switch(columnType.getSdbcType())
+ {
+ case DataType::SMALLINT:
+ setValue< sal_Int16 >(nIndex,
+ static_cast<sal_Int16>(nValue),
+ dType);
+ break;
+ case DataType::INTEGER:
+ setValue< sal_Int32 >(nIndex,
+ static_cast<sal_Int32>(nValue),
+ dType);
+ break;
+ case DataType::BIGINT:
+ setValue< sal_Int64 >(nIndex,
+ static_cast<sal_Int64>(nValue),
+ dType);
+ break;
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ // take decimal places into account, later on they are removed in makeNumericString
+ setObjectWithInfo(nIndex,Any{nValue}, columnType.getSdbcType(), columnType.getScale());
+ break;
+ default:
+ setValue< double >(nIndex, nValue, SQL_DOUBLE); // TODO: SQL_D_FLOAT?
+ }
+}
+
+void SAL_CALL OPreparedStatement::setDate(sal_Int32 nIndex, const Date& rDate)
+{
+ struct tm aCTime;
+ aCTime.tm_mday = rDate.Day;
+ aCTime.tm_mon = rDate.Month -1;
+ aCTime.tm_year = rDate.Year -1900;
+
+ ISC_DATE aISCDate;
+ isc_encode_sql_date(&aCTime, &aISCDate);
+
+ setValue< ISC_DATE >(nIndex, aISCDate, SQL_TYPE_DATE);
+}
+
+void SAL_CALL OPreparedStatement::setTime( sal_Int32 nIndex, const css::util::Time& rTime)
+{
+ struct tm aCTime;
+ aCTime.tm_sec = rTime.Seconds;
+ aCTime.tm_min = rTime.Minutes;
+ aCTime.tm_hour = rTime.Hours;
+
+ ISC_TIME aISCTime;
+ isc_encode_sql_time(&aCTime, &aISCTime);
+
+ // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION with no
+ // other funkiness, so we can simply add the fraction of a second.
+ aISCTime += rTime.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION);
+
+ setValue< ISC_TIME >(nIndex, aISCTime, SQL_TYPE_TIME);
+}
+
+void SAL_CALL OPreparedStatement::setTimestamp(sal_Int32 nIndex, const DateTime& rTimestamp)
+{
+ struct tm aCTime;
+ aCTime.tm_sec = rTimestamp.Seconds;
+ aCTime.tm_min = rTimestamp.Minutes;
+ aCTime.tm_hour = rTimestamp.Hours;
+ aCTime.tm_mday = rTimestamp.Day;
+ aCTime.tm_mon = rTimestamp.Month - 1;
+ aCTime.tm_year = rTimestamp.Year - 1900;
+
+ ISC_TIMESTAMP aISCTimestamp;
+ isc_encode_timestamp(&aCTime, &aISCTimestamp);
+
+ // As in previous function
+ aISCTimestamp.timestamp_time += rTimestamp.NanoSeconds / (1000000000 / ISC_TIME_SECONDS_PRECISION);
+
+ setValue< ISC_TIMESTAMP >(nIndex, aISCTimestamp, SQL_TIMESTAMP);
+}
+
+
+// void OPreparedStatement::set
+void OPreparedStatement::openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId)
+{
+ ISC_STATUS aErr;
+
+ aErr = isc_create_blob2(m_statusVector,
+ &m_pConnection->getDBHandle(),
+ &m_pConnection->getTransaction(),
+ &rBlobHandle,
+ &rBlobId,
+ 0, // Blob parameter buffer length
+ nullptr); // Blob parameter buffer handle
+
+ if (aErr)
+ {
+ evaluateStatusVector(m_statusVector,
+ Concat2View("setBlob failed on " + m_sSqlStatement),
+ *this);
+ assert(false);
+ }
+}
+
+void OPreparedStatement::closeBlobAfterWriting(isc_blob_handle& rBlobHandle)
+{
+ ISC_STATUS aErr;
+
+ aErr = isc_close_blob(m_statusVector,
+ &rBlobHandle);
+ if (aErr)
+ {
+ evaluateStatusVector(m_statusVector,
+ u"isc_close_blob failed",
+ *this);
+ assert(false);
+ }
+}
+
+void SAL_CALL OPreparedStatement::setClob(sal_Int32 nParameterIndex, const Reference< XClob >& xClob )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+
+#if SAL_TYPES_SIZEOFPOINTER == 8
+ isc_blob_handle aBlobHandle = 0;
+#else
+ isc_blob_handle aBlobHandle = nullptr;
+#endif
+ ISC_QUAD aBlobId;
+
+ openBlobForWriting(aBlobHandle, aBlobId);
+
+
+ // Max segment size is 2^16 == SAL_MAX_UINT16
+ // SAL_MAX_UINT16 / 4 is surely enough for UTF-8
+ // TODO apply max segment size to character encoding
+ sal_Int64 nCharWritten = 1; // XClob is indexed from 1
+ ISC_STATUS aErr = 0;
+ sal_Int64 nLen = xClob->length();
+ while ( nLen >= nCharWritten )
+ {
+ sal_Int64 nCharRemain = nLen - nCharWritten + 1;
+ constexpr sal_uInt16 MAX_SIZE = SAL_MAX_UINT16 / 4;
+ sal_uInt16 nWriteSize = std::min<sal_Int64>(nCharRemain, MAX_SIZE);
+ OString sData = OUStringToOString(
+ xClob->getSubString(nCharWritten, nWriteSize),
+ RTL_TEXTENCODING_UTF8);
+ aErr = isc_put_segment( m_statusVector,
+ &aBlobHandle,
+ sData.getLength(),
+ sData.getStr() );
+ nCharWritten += nWriteSize;
+
+ if (aErr)
+ break;
+ }
+
+ // We need to make sure we close the Blob even if there are errors, hence evaluate
+ // errors after closing.
+ closeBlobAfterWriting(aBlobHandle);
+
+ if (aErr)
+ {
+ evaluateStatusVector(m_statusVector,
+ u"isc_put_segment failed",
+ *this);
+ assert(false);
+ }
+
+ setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
+}
+
+void OPreparedStatement::setClob( sal_Int32 nParameterIndex, const OUString& rStr )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ checkParameterIndex(nParameterIndex);
+
+#if SAL_TYPES_SIZEOFPOINTER == 8
+ isc_blob_handle aBlobHandle = 0;
+#else
+ isc_blob_handle aBlobHandle = nullptr;
+#endif
+ ISC_QUAD aBlobId;
+
+ openBlobForWriting(aBlobHandle, aBlobId);
+
+ OString sData = OUStringToOString(
+ rStr,
+ RTL_TEXTENCODING_UTF8);
+ size_t nDataSize = sData.getLength();
+ ISC_STATUS aErr = 0;
+ // we can't store more than MAX_SIZE_SEGMENT in a segment
+ if (nDataSize <= MAX_SIZE_SEGMENT)
+ {
+ aErr = isc_put_segment( m_statusVector,
+ &aBlobHandle,
+ sData.getLength(),
+ sData.getStr() );
+ }
+ else
+ {
+ // if we need more, let's split the input and first let's calculate the nb of entire chunks needed
+ size_t nNbEntireChunks = nDataSize / MAX_SIZE_SEGMENT;
+ for (size_t i = 0; i < nNbEntireChunks; ++i)
+ {
+ OString strCurrentChunk = sData.copy(i * MAX_SIZE_SEGMENT, MAX_SIZE_SEGMENT);
+ aErr = isc_put_segment( m_statusVector,
+ &aBlobHandle,
+ strCurrentChunk.getLength(),
+ strCurrentChunk.getStr() );
+ if (aErr)
+ break;
+ }
+ size_t nRemainingBytes = nDataSize - (nNbEntireChunks * MAX_SIZE_SEGMENT);
+ if (nRemainingBytes && !aErr)
+ {
+ // then copy the remaining
+ OString strCurrentChunk = sData.copy(nNbEntireChunks * MAX_SIZE_SEGMENT, nRemainingBytes);
+ aErr = isc_put_segment( m_statusVector,
+ &aBlobHandle,
+ strCurrentChunk.getLength(),
+ strCurrentChunk.getStr() );
+ }
+ }
+
+ // We need to make sure we close the Blob even if there are errors, hence evaluate
+ // errors after closing.
+ closeBlobAfterWriting(aBlobHandle);
+
+ if (aErr)
+ {
+ evaluateStatusVector(m_statusVector,
+ u"isc_put_segment failed",
+ *this);
+ assert(false);
+ }
+
+ setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
+}
+
+void SAL_CALL OPreparedStatement::setBlob(sal_Int32 nParameterIndex,
+ const Reference< XBlob >& xBlob)
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ checkParameterIndex(nParameterIndex);
+
+#if SAL_TYPES_SIZEOFPOINTER == 8
+ isc_blob_handle aBlobHandle = 0;
+#else
+ isc_blob_handle aBlobHandle = nullptr;
+#endif
+ ISC_QUAD aBlobId;
+
+ openBlobForWriting(aBlobHandle, aBlobId);
+
+ ISC_STATUS aErr = 0;
+ const sal_Int64 nBlobLen = xBlob->length();
+ if (nBlobLen > 0)
+ {
+ // Max write size is 0xFFFF == SAL_MAX_UINT16
+ sal_uInt64 nDataWritten = 0;
+ while (sal::static_int_cast<sal_uInt64>(nBlobLen) > nDataWritten)
+ {
+ sal_uInt64 nDataRemaining = nBlobLen - nDataWritten;
+ sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt64(SAL_MAX_UINT16));
+ aErr = isc_put_segment(m_statusVector,
+ &aBlobHandle,
+ nWriteSize,
+ reinterpret_cast<const char*>(xBlob->getBytes(nDataWritten, nWriteSize).getConstArray()));
+ nDataWritten += nWriteSize;
+
+ if (aErr)
+ break;
+ }
+ }
+
+ // We need to make sure we close the Blob even if there are errors, hence evaluate
+ // errors after closing.
+ closeBlobAfterWriting(aBlobHandle);
+
+ if (aErr)
+ {
+ evaluateStatusVector(m_statusVector,
+ u"isc_put_segment failed",
+ *this);
+ assert(false);
+ }
+
+ setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
+}
+
+
+void SAL_CALL OPreparedStatement::setArray( sal_Int32 nIndex, const Reference< XArray >& )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ checkParameterIndex(nIndex);
+}
+
+
+void SAL_CALL OPreparedStatement::setRef( sal_Int32 nIndex, const Reference< XRef >& )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ checkParameterIndex(nIndex);
+}
+
+
+void SAL_CALL OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 sqlType, sal_Int32 scale )
+{
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ ::osl::MutexGuard aGuard( m_aMutex );
+ ensurePrepared();
+
+ checkParameterIndex(parameterIndex);
+ setParameterNull(parameterIndex, false);
+
+ XSQLVAR* pVar = m_pInSqlda->sqlvar + (parameterIndex - 1);
+ int dType = (pVar->sqltype & ~1); // drop null flag
+
+ if(sqlType == DataType::DECIMAL || sqlType == DataType::NUMERIC)
+ {
+ double dbValue =0.0;
+ OUString sValue;
+ if( x >>= dbValue )
+ {
+ // truncate and round to 'scale' number of decimal places
+ sValue = OUString::number( std::floor((dbValue * pow10Integer(scale)) + .5) / pow10Integer(scale) );
+ }
+ else
+ {
+ x >>= sValue;
+ }
+
+ // fill in the number with nulls in fractional part.
+ // We need this because e.g. 0.450 != 0.045 despite
+ // their scale is equal
+ OUStringBuffer sBuffer(15);
+ sBuffer.append(sValue);
+ if(sValue.indexOf('.') != -1) // there is a dot
+ {
+ for(sal_Int32 i=sValue.subView(sValue.indexOf('.')+1).size(); i<scale;i++)
+ {
+ sBuffer.append('0');
+ }
+ }
+ else
+ {
+ for (sal_Int32 i=0; i<scale; i++)
+ {
+ sBuffer.append('0');
+ }
+ }
+
+ sValue = sBuffer.makeStringAndClear();
+ switch(dType)
+ {
+ case SQL_SHORT:
+ setValue< sal_Int16 >(parameterIndex,
+ static_cast<sal_Int16>( toNumericWithoutDecimalPlace(sValue) ),
+ dType);
+ break;
+ case SQL_LONG:
+ case SQL_DOUBLE:
+ setValue< sal_Int32 >(parameterIndex,
+ static_cast<sal_Int32>( toNumericWithoutDecimalPlace(sValue) ),
+ dType);
+ break;
+ case SQL_INT64:
+ setValue< sal_Int64 >(parameterIndex,
+ toNumericWithoutDecimalPlace(sValue),
+ dType);
+ break;
+ default:
+ SAL_WARN("connectivity.firebird",
+ "No Firebird sql type found for numeric or decimal types");
+ ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
+ }
+ }
+ else
+ {
+ ::dbtools::setObjectWithInfo(this,parameterIndex,x,sqlType,scale);
+ }
+
+}
+
+
+void SAL_CALL OPreparedStatement::setObjectNull( sal_Int32 nIndex, sal_Int32, const OUString& )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ checkParameterIndex(nIndex);
+}
+
+
+void SAL_CALL OPreparedStatement::setObject( sal_Int32 nIndex, const Any& )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ checkParameterIndex(nIndex);
+}
+
+void SAL_CALL OPreparedStatement::setBytes(sal_Int32 nParameterIndex,
+ const Sequence< sal_Int8 >& xBytes)
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ checkParameterIndex(nParameterIndex);
+
+ XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
+ int dType = (pVar->sqltype & ~1); // drop flag bit for now
+
+ if( dType == SQL_BLOB )
+ {
+#if SAL_TYPES_SIZEOFPOINTER == 8
+ isc_blob_handle aBlobHandle = 0;
+#else
+ isc_blob_handle aBlobHandle = nullptr;
+#endif
+ ISC_QUAD aBlobId;
+
+ openBlobForWriting(aBlobHandle, aBlobId);
+
+ ISC_STATUS aErr = 0;
+ const sal_Int32 nBytesLen = xBytes.getLength();
+ if (nBytesLen > 0)
+ {
+ // Max write size is 0xFFFF == SAL_MAX_UINT16
+ sal_uInt32 nDataWritten = 0;
+ while (sal::static_int_cast<sal_uInt32>(nBytesLen) > nDataWritten)
+ {
+ sal_uInt32 nDataRemaining = nBytesLen - nDataWritten;
+ sal_uInt16 nWriteSize = std::min(nDataRemaining, sal_uInt32(SAL_MAX_UINT16));
+ aErr = isc_put_segment(m_statusVector,
+ &aBlobHandle,
+ nWriteSize,
+ reinterpret_cast<const char*>(xBytes.getConstArray()) + nDataWritten);
+ nDataWritten += nWriteSize;
+
+ if (aErr)
+ break;
+ }
+ }
+
+ // We need to make sure we close the Blob even if there are errors, hence evaluate
+ // errors after closing.
+ closeBlobAfterWriting(aBlobHandle);
+
+ if (aErr)
+ {
+ evaluateStatusVector(m_statusVector,
+ u"isc_put_segment failed",
+ *this);
+ assert(false);
+ }
+
+ setValue< ISC_QUAD >(nParameterIndex, aBlobId, SQL_BLOB);
+ }
+ else if( dType == SQL_VARYING )
+ {
+ setParameterNull(nParameterIndex, false);
+ const sal_Int32 nMaxSize = 0xFFFF;
+ Sequence<sal_Int8> xBytesCopy(xBytes);
+ if (xBytesCopy.getLength() > nMaxSize)
+ {
+ xBytesCopy.realloc( nMaxSize );
+ }
+ const sal_uInt16 nSize = xBytesCopy.getLength();
+ // 8000 corresponds to value from lcl_addDefaultParameters
+ // in dbaccess/source/filter/hsqldb/createparser.cxx
+ if (nSize > 8000)
+ {
+ free(pVar->sqldata);
+ pVar->sqldata = static_cast<char *>(malloc(sizeof(char) * nSize + 2));
+ }
+ static_assert(sizeof(nSize) == 2, "must match dest memcpy len");
+ // First 2 bytes indicate string size
+ memcpy(pVar->sqldata, &nSize, 2);
+ // Actual data
+ memcpy(pVar->sqldata + 2, xBytesCopy.getConstArray(), nSize);
+ }
+ else if( dType == SQL_TEXT )
+ {
+ if (pVar->sqllen < xBytes.getLength())
+ dbtools::throwSQLException("Data too big for this field",
+ dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE, *this);
+ setParameterNull(nParameterIndex, false);
+ memcpy(pVar->sqldata, xBytes.getConstArray(), xBytes.getLength() );
+ // Fill remainder with zeroes
+ memset(pVar->sqldata + xBytes.getLength(), 0, pVar->sqllen - xBytes.getLength());
+ }
+ else
+ {
+ ::dbtools::throwSQLException(
+ "Incorrect type for setBytes",
+ ::dbtools::StandardSQLState::INVALID_SQL_DATA_TYPE,
+ *this);
+ }
+}
+
+
+void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ checkParameterIndex(nIndex);
+}
+
+
+void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 nIndex, const Reference< css::io::XInputStream >&, sal_Int32 )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ checkParameterIndex(nIndex);
+}
+
+
+void SAL_CALL OPreparedStatement::clearParameters( )
+{
+}
+
+// ---- Batch methods -- unsupported -----------------------------------------
+void SAL_CALL OPreparedStatement::clearBatch()
+{
+ // Unsupported
+}
+
+void SAL_CALL OPreparedStatement::addBatch()
+{
+ // Unsupported by firebird
+}
+
+Sequence< sal_Int32 > SAL_CALL OPreparedStatement::executeBatch()
+{
+ // Unsupported by firebird
+ return Sequence< sal_Int32 >();
+}
+
+void OPreparedStatement::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue)
+{
+ switch(nHandle)
+ {
+ case PROPERTY_ID_RESULTSETCONCURRENCY:
+ break;
+ case PROPERTY_ID_RESULTSETTYPE:
+ break;
+ case PROPERTY_ID_FETCHDIRECTION:
+ break;
+ case PROPERTY_ID_USEBOOKMARKS:
+ break;
+ default:
+ OStatementCommonBase::setFastPropertyValue_NoBroadcast(nHandle,rValue);
+ }
+}
+
+void OPreparedStatement::checkParameterIndex(sal_Int32 nParameterIndex)
+{
+ ensurePrepared();
+ if ((nParameterIndex == 0) || (nParameterIndex > m_pInSqlda->sqld))
+ {
+ ::dbtools::throwSQLException(
+ "No column " + OUString::number(nParameterIndex),
+ ::dbtools::StandardSQLState::COLUMN_NOT_FOUND,
+ *this);
+ }
+}
+
+void OPreparedStatement::setParameterNull(sal_Int32 nParameterIndex,
+ bool bSetNull)
+{
+ XSQLVAR* pVar = m_pInSqlda->sqlvar + (nParameterIndex - 1);
+ if (bSetNull)
+ {
+ pVar->sqltype |= 1;
+ *pVar->sqlind = -1;
+ }
+ else
+ *pVar->sqlind = 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/PreparedStatement.hxx b/connectivity/source/drivers/firebird/PreparedStatement.hxx
new file mode 100644
index 0000000000..3e61436b58
--- /dev/null
+++ b/connectivity/source/drivers/firebird/PreparedStatement.hxx
@@ -0,0 +1,152 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include "StatementCommonBase.hxx"
+
+#include <cppuhelper/implbase5.hxx>
+
+#include <com/sun/star/sdbc/XPreparedStatement.hpp>
+#include <com/sun/star/sdbc/XParameters.hpp>
+#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
+#include <com/sun/star/sdbc/XPreparedBatchExecution.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+#include <ibase.h>
+
+namespace connectivity::firebird
+ {
+
+ class OBoundParam;
+ typedef ::cppu::ImplHelper5< css::sdbc::XPreparedStatement,
+ css::sdbc::XParameters,
+ css::sdbc::XPreparedBatchExecution,
+ css::sdbc::XResultSetMetaDataSupplier,
+ css::lang::XServiceInfo> OPreparedStatement_Base;
+
+ class OPreparedStatement : public OStatementCommonBase,
+ public OPreparedStatement_Base
+ {
+ protected:
+ OUString m_sSqlStatement;
+ css::uno::Reference< css::sdbc::XResultSetMetaData > m_xMetaData;
+
+ XSQLDA* m_pOutSqlda;
+ XSQLDA* m_pInSqlda;
+ /// @throws css::sdbc::SQLException
+ /// @throws css::uno::RuntimeException
+ void checkParameterIndex(sal_Int32 nParameterIndex);
+
+ /**
+ * Set a numeric value in the input SQLDA. If the destination
+ * parameter is not of nType then an Exception will be thrown.
+ *
+ * @throws css::sdbc::SQLException
+ * @throws css::uno::RuntimeException
+ */
+ template <typename T> void setValue(sal_Int32 nIndex, const T& nValue, ISC_SHORT nType);
+ void setParameterNull(sal_Int32 nParameterIndex, bool bSetNull = true);
+
+ /// @throws css::sdbc::SQLException
+ /// @throws css::uno::RuntimeException
+ void ensurePrepared();
+ /**
+ * Assumes that all necessary mutexes have been taken.
+ */
+ void openBlobForWriting(isc_blob_handle& rBlobHandle, ISC_QUAD& rBlobId);
+ /**
+ * Assumes that all necessary mutexes have been taken.
+ */
+ void closeBlobAfterWriting(isc_blob_handle& rBlobHandle);
+ void setClob(sal_Int32 nParamIndex, const OUString& rStr);
+
+ protected:
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,
+ const css::uno::Any& rValue) override;
+ virtual ~OPreparedStatement() override;
+ public:
+ DECLARE_SERVICE_INFO();
+ // a constructor, which is required for returning objects:
+ OPreparedStatement( Connection* _pConnection,
+ const OUString& sql);
+
+ //XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+ //XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+
+ // XPreparedStatement
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL
+ executeQuery() override;
+ virtual sal_Int32 SAL_CALL
+ executeUpdate() override;
+ virtual sal_Bool SAL_CALL
+ execute() override;
+ virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL
+ getConnection() override;
+
+ // XParameters
+ virtual void SAL_CALL setNull(sal_Int32 nIndex, sal_Int32 nValue) override;
+ virtual void SAL_CALL setObjectNull(sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName ) override;
+ virtual void SAL_CALL setBoolean( sal_Int32 nIndex, sal_Bool nValue) override;
+ virtual void SAL_CALL setByte(sal_Int32 nIndex, sal_Int8 nValue) override;
+ virtual void SAL_CALL setShort(sal_Int32 nIndex, sal_Int16 nValue) override;
+ virtual void SAL_CALL setInt(sal_Int32 nIndex, sal_Int32 nValue) override;
+ virtual void SAL_CALL setLong(sal_Int32 nIndex, sal_Int64 nValue) override;
+ virtual void SAL_CALL setFloat( sal_Int32 parameterIndex, float x ) override;
+ virtual void SAL_CALL setDouble( sal_Int32 parameterIndex, double x ) override;
+ virtual void SAL_CALL setString( sal_Int32 parameterIndex, const OUString& x ) override;
+ virtual void SAL_CALL setBytes( sal_Int32 parameterIndex, const css::uno::Sequence< sal_Int8 >& x ) override;
+ virtual void SAL_CALL setDate( sal_Int32 parameterIndex, const css::util::Date& x ) override;
+ virtual void SAL_CALL setTime( sal_Int32 parameterIndex, const css::util::Time& x ) override;
+ virtual void SAL_CALL setTimestamp( sal_Int32 parameterIndex, const css::util::DateTime& x ) override;
+ virtual void SAL_CALL setBinaryStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override;
+ virtual void SAL_CALL setCharacterStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override;
+ virtual void SAL_CALL setObject( sal_Int32 parameterIndex, const css::uno::Any& x ) override;
+ virtual void SAL_CALL setObjectWithInfo( sal_Int32 parameterIndex, const css::uno::Any& x, sal_Int32 targetSqlType, sal_Int32 scale ) override;
+ virtual void SAL_CALL setRef( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XRef >& x ) override;
+ virtual void SAL_CALL setBlob( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XBlob >& x ) override;
+ virtual void SAL_CALL setClob( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XClob >& x ) override;
+ virtual void SAL_CALL setArray( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XArray >& x ) override;
+ virtual void SAL_CALL clearParameters( ) override;
+
+ // XPreparedBatchExecution -- UNSUPPORTED by firebird
+ virtual void SAL_CALL
+ addBatch() override;
+ virtual void SAL_CALL
+ clearBatch() override;
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL
+ executeBatch() override;
+
+ // XCloseable
+ virtual void SAL_CALL close() override;
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // XResultSetMetaDataSupplier
+ virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override;
+
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/ResultSet.cxx b/connectivity/source/drivers/firebird/ResultSet.cxx
new file mode 100644
index 0000000000..ea3ac86ae7
--- /dev/null
+++ b/connectivity/source/drivers/firebird/ResultSet.cxx
@@ -0,0 +1,926 @@
+/* -*- 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 "ResultSet.hxx"
+#include "ResultSetMetaData.hxx"
+#include "Util.hxx"
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <connectivity/dbexception.hxx>
+#include <propertyids.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <TConnection.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/FetchDirection.hpp>
+#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
+#include <com/sun/star/sdbc/ResultSetType.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+
+using namespace ::comphelper;
+using namespace ::connectivity;
+using namespace ::connectivity::firebird;
+using namespace ::cppu;
+using namespace ::dbtools;
+using namespace ::osl;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::util;
+
+OResultSet::OResultSet(Connection* pConnection,
+ ::osl::Mutex& rMutex,
+ const uno::Reference< XInterface >& xStatement,
+ isc_stmt_handle aStatementHandle,
+ XSQLDA* pSqlda )
+ : OResultSet_BASE(rMutex)
+ , OPropertyContainer(OResultSet_BASE::rBHelper)
+ , m_bIsBookmarkable(false)
+ , m_nFetchSize(1)
+ , m_nResultSetType(css::sdbc::ResultSetType::FORWARD_ONLY)
+ , m_nFetchDirection(css::sdbc::FetchDirection::FORWARD)
+ , m_nResultSetConcurrency(css::sdbc::ResultSetConcurrency::READ_ONLY)
+ , m_pConnection(pConnection)
+ , m_rMutex(rMutex)
+ , m_xStatement(xStatement)
+ , m_pSqlda(pSqlda)
+ , m_statementHandle(aStatementHandle)
+ , m_bWasNull(false)
+ , m_currentRow(0)
+ , m_bIsAfterLastRow(false)
+ , m_fieldCount(pSqlda? pSqlda->sqld : 0)
+{
+ SAL_INFO("connectivity.firebird", "OResultSet().");
+ registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISBOOKMARKABLE),
+ PROPERTY_ID_ISBOOKMARKABLE,
+ PropertyAttribute::READONLY,
+ &m_bIsBookmarkable,
+ cppu::UnoType<decltype(m_bIsBookmarkable)>::get());
+ registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE),
+ PROPERTY_ID_FETCHSIZE,
+ PropertyAttribute::READONLY,
+ &m_nFetchSize,
+ cppu::UnoType<decltype(m_nFetchSize)>::get());
+ registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE),
+ PROPERTY_ID_RESULTSETTYPE,
+ PropertyAttribute::READONLY,
+ &m_nResultSetType,
+ cppu::UnoType<decltype(m_nResultSetType)>::get());
+ registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION),
+ PROPERTY_ID_FETCHDIRECTION,
+ PropertyAttribute::READONLY,
+ &m_nFetchDirection,
+ cppu::UnoType<decltype(m_nFetchDirection)>::get());
+ registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY),
+ PROPERTY_ID_RESULTSETCONCURRENCY,
+ PropertyAttribute::READONLY,
+ &m_nResultSetConcurrency,
+ cppu::UnoType<decltype(m_nResultSetConcurrency)>::get());
+
+ if (!pSqlda)
+ return; // TODO: what?
+
+}
+
+OResultSet::~OResultSet()
+{
+}
+
+// ---- XResultSet -- Row retrieval methods ------------------------------------
+sal_Int32 SAL_CALL OResultSet::getRow()
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ return m_currentRow;
+}
+
+sal_Bool SAL_CALL OResultSet::next()
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ m_currentRow++;
+
+ ISC_STATUS fetchStat = isc_dsql_fetch(m_statusVector,
+ &m_statementHandle,
+ 1,
+ m_pSqlda);
+ if (fetchStat == 0) // SUCCESSFUL
+ {
+ return true;
+ }
+ else if (fetchStat == 100) // END OF DATASET
+ {
+ m_bIsAfterLastRow = true;
+ return false;
+ }
+ else
+ {
+ SAL_WARN("connectivity.firebird", "Error when fetching data");
+ // Throws sql exception as appropriate
+ evaluateStatusVector(m_statusVector, u"isc_dsql_fetch", *this);
+ return false;
+ }
+}
+
+sal_Bool SAL_CALL OResultSet::previous()
+{
+ ::dbtools::throwFunctionNotSupportedSQLException("previous not supported in firebird",
+ *this);
+ return false;
+}
+
+sal_Bool SAL_CALL OResultSet::isLast()
+{
+ ::dbtools::throwFunctionNotSupportedSQLException("isLast not supported in firebird",
+ *this);
+ return false;
+}
+
+sal_Bool SAL_CALL OResultSet::isBeforeFirst()
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ return m_currentRow == 0;
+}
+
+sal_Bool SAL_CALL OResultSet::isAfterLast()
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ return m_bIsAfterLastRow;
+}
+
+sal_Bool SAL_CALL OResultSet::isFirst()
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ return m_currentRow == 1 && !m_bIsAfterLastRow;
+}
+
+void SAL_CALL OResultSet::beforeFirst()
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ if (m_currentRow != 0)
+ ::dbtools::throwFunctionNotSupportedSQLException("beforeFirst not supported in firebird",
+ *this);
+}
+
+void SAL_CALL OResultSet::afterLast()
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ if (!m_bIsAfterLastRow)
+ ::dbtools::throwFunctionNotSupportedSQLException("afterLast not supported in firebird",
+ *this);
+}
+
+sal_Bool SAL_CALL OResultSet::first()
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ if (m_currentRow == 0)
+ {
+ return next();
+ }
+ else if (m_currentRow == 1 && !m_bIsAfterLastRow)
+ {
+ return true;
+ }
+ else
+ {
+ ::dbtools::throwFunctionNotSupportedSQLException("first not supported in firebird",
+ *this);
+ return false;
+ }
+}
+
+sal_Bool SAL_CALL OResultSet::last()
+{
+ // We need to iterate past the last row to know when we've passed the last
+ // row, hence we can't actually move to last.
+ ::dbtools::throwFunctionNotSupportedSQLException("last not supported in firebird",
+ *this);
+ return false;
+}
+
+sal_Bool SAL_CALL OResultSet::absolute(sal_Int32 aRow)
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ if (aRow > m_currentRow)
+ {
+ sal_Int32 aIterations = aRow - m_currentRow;
+ return relative(aIterations);
+ }
+ else
+ {
+ ::dbtools::throwFunctionNotSupportedSQLException("absolute not supported in firebird",
+ *this);
+ return false;
+ }
+}
+
+sal_Bool SAL_CALL OResultSet::relative(sal_Int32 row)
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ if (row > 0)
+ {
+ while (row--)
+ {
+ if (!next())
+ return false;
+ }
+ return true;
+ }
+ else
+ {
+ ::dbtools::throwFunctionNotSupportedSQLException("relative not supported in firebird",
+ *this);
+ return false;
+ }
+}
+
+void OResultSet::checkColumnIndex(sal_Int32 nIndex)
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ if( nIndex < 1 || nIndex > m_fieldCount )
+ {
+ ::dbtools::throwSQLException(
+ "No column " + OUString::number(nIndex),
+ ::dbtools::StandardSQLState::COLUMN_NOT_FOUND,
+ *this);
+ }
+}
+
+void OResultSet::checkRowIndex()
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ if((m_currentRow < 1) || m_bIsAfterLastRow)
+ {
+ ::dbtools::throwSQLException(
+ "Invalid Row",
+ ::dbtools::StandardSQLState::INVALID_CURSOR_POSITION,
+ *this);
+ }
+}
+
+Any SAL_CALL OResultSet::queryInterface( const Type & rType )
+{
+ Any aRet = OPropertySetHelper::queryInterface(rType);
+ return aRet.hasValue() ? aRet : OResultSet_BASE::queryInterface(rType);
+}
+
+ Sequence< Type > SAL_CALL OResultSet::getTypes()
+{
+ return concatSequences(OPropertySetHelper::getTypes(), OResultSet_BASE::getTypes());
+}
+// ---- XColumnLocate ---------------------------------------------------------
+sal_Int32 SAL_CALL OResultSet::findColumn(const OUString& rColumnName)
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ uno::Reference< XResultSetMetaData > xMeta = getMetaData();
+ sal_Int32 nLen = xMeta->getColumnCount();
+ sal_Int32 i;
+
+ for(i = 1; i<=nLen; ++i)
+ {
+ // We assume case sensitive, otherwise you'd have to test
+ // xMeta->isCaseSensitive and use qualsIgnoreAsciiCase as needed.
+ if (rColumnName == xMeta->getColumnName(i))
+ return i;
+ }
+
+ ::dbtools::throwInvalidColumnException(rColumnName, *this);
+ assert(false);
+ return 0; // Never reached
+}
+
+uno::Reference< XInputStream > SAL_CALL OResultSet::getBinaryStream( sal_Int32 )
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ return nullptr;
+}
+
+uno::Reference< XInputStream > SAL_CALL OResultSet::getCharacterStream( sal_Int32 )
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ return nullptr;
+}
+
+// ---- Internal Utilities ---------------------------------------------------
+bool OResultSet::isNull(const sal_Int32 nColumnIndex)
+{
+ assert(nColumnIndex <= m_fieldCount);
+ XSQLVAR* pVar = m_pSqlda->sqlvar;
+
+ if (pVar[nColumnIndex-1].sqltype & 1) // Indicates column may contain null
+ {
+ if (*pVar[nColumnIndex-1].sqlind == -1)
+ return true;
+ }
+ return false;
+}
+
+template <typename T>
+OUString OResultSet::makeNumericString(const sal_Int32 nColumnIndex)
+{
+ // minus because firebird stores scale as a negative number
+ int nDecimalCount = -(m_pSqlda->sqlvar[nColumnIndex-1].sqlscale);
+ if(nDecimalCount < 0)
+ {
+ // scale should be always positive
+ assert(false);
+ return OUString();
+ }
+
+ OUStringBuffer sRetBuffer;
+ T nAllDigits = *reinterpret_cast<T*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
+ sal_Int64 nDecimalCountExp = pow10Integer(nDecimalCount);
+
+ if(nAllDigits < 0)
+ {
+ sRetBuffer.append('-');
+ nAllDigits = -nAllDigits; // abs
+ }
+
+ sRetBuffer.append(static_cast<sal_Int64>(nAllDigits / nDecimalCountExp) );
+ if( nDecimalCount > 0)
+ {
+ sRetBuffer.append('.');
+
+ sal_Int64 nFractionalPart = nAllDigits % nDecimalCountExp;
+
+ int iCount = 0; // digit count
+ sal_Int64 nFracTemp = nFractionalPart;
+ while(nFracTemp>0)
+ {
+ nFracTemp /= 10;
+ iCount++;
+ }
+
+ int nMissingNulls = nDecimalCount - iCount;
+
+ // append nulls after dot and before nFractionalPart
+ for(int i=0; i<nMissingNulls; i++)
+ {
+ sRetBuffer.append('0');
+ }
+
+ // the rest
+ sRetBuffer.append(nFractionalPart);
+ }
+
+ return sRetBuffer.makeStringAndClear();
+}
+
+template <typename T>
+T OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType)
+{
+ m_bWasNull = isNull(nColumnIndex);
+ if (m_bWasNull)
+ return T();
+
+ if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == nType)
+ return *reinterpret_cast<T*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
+ else
+ {
+ ORowSetValue row = retrieveValue< ORowSetValue >(nColumnIndex, 0);
+ if constexpr ( std::is_same_v<sal_Int64, T> )
+ return row.getLong();
+ else if constexpr ( std::is_same_v<sal_Int32, T> )
+ return row.getInt32();
+ else if constexpr ( std::is_same_v<sal_Int16, T> )
+ return row.getInt16();
+ else if constexpr ( std::is_same_v<float, T> )
+ return row.getFloat();
+ else if constexpr ( std::is_same_v<double, T> )
+ return row.getDouble();
+ else if constexpr ( std::is_same_v<bool, T> )
+ return row.getBool();
+ else
+ return row;
+ }
+}
+
+template <>
+ORowSetValue OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
+{
+ // See https://wiki.documentfoundation.org/Documentation/DevGuide/Database_Access#Using_the_getXXX_Methods
+ // (bottom of page) for a chart of possible conversions, we should allow all
+ // of these -- Blob/Clob will probably need some specialist handling especially
+ // w.r.t. to generating Strings for them.
+ //
+ // Basically we just have to map to the correct direct request and
+ // ORowSetValue does the rest for us here.
+ int nSqlSubType = m_pSqlda->sqlvar[nColumnIndex-1].sqlsubtype;
+
+ // TODO Firebird 3.0 does not set subtype (i.e. set to 0) for computed numeric/decimal value.
+ // It may change in the future.
+ // Imply numeric data type when subtype is 0 and scale is negative
+ if( nSqlSubType == 0 && m_pSqlda->sqlvar[nColumnIndex-1].sqlscale < 0 )
+ nSqlSubType = 1;
+
+ switch (m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1)
+ {
+ case SQL_TEXT:
+ case SQL_VARYING:
+ return getString(nColumnIndex);
+ case SQL_SHORT:
+ if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal
+ return getString(nColumnIndex);
+ return getShort(nColumnIndex);
+ case SQL_LONG:
+ if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal
+ return getString(nColumnIndex);
+ return getInt(nColumnIndex);
+ case SQL_FLOAT:
+ return getFloat(nColumnIndex);
+ case SQL_DOUBLE:
+ if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal
+ return getString(nColumnIndex);
+ return getDouble(nColumnIndex);
+ case SQL_D_FLOAT:
+ return getFloat(nColumnIndex);
+ case SQL_TIMESTAMP:
+ return getTimestamp(nColumnIndex);
+ case SQL_TYPE_TIME:
+ return getTime(nColumnIndex);
+ case SQL_TYPE_DATE:
+ return getDate(nColumnIndex);
+ case SQL_INT64:
+ if(nSqlSubType == 1 || nSqlSubType == 2) //numeric or decimal
+ return getString(nColumnIndex);
+ return getLong(nColumnIndex);
+ case SQL_BOOLEAN:
+ return ORowSetValue(bool(getBoolean(nColumnIndex)));
+ case SQL_BLOB:
+ case SQL_NULL:
+ case SQL_QUAD:
+ case SQL_ARRAY:
+ // TODO: these are all invalid conversions, so maybe we should
+ // throw an exception?
+ return ORowSetValue();
+ default:
+ assert(false);
+ return ORowSetValue();
+ }
+}
+
+template <>
+Date OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
+{
+ if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TYPE_DATE)
+ {
+ ISC_DATE aISCDate = *reinterpret_cast<ISC_DATE*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
+
+ struct tm aCTime;
+ isc_decode_sql_date(&aISCDate, &aCTime);
+
+ return Date(aCTime.tm_mday, aCTime.tm_mon + 1, aCTime.tm_year + 1900);
+ }
+ else
+ {
+ return retrieveValue< ORowSetValue >(nColumnIndex, 0).getDate();
+ }
+}
+
+template <>
+Time OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
+{
+ if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TYPE_TIME)
+ {
+ ISC_TIME aISCTime = *reinterpret_cast<ISC_TIME*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
+
+ struct tm aCTime;
+ isc_decode_sql_time(&aISCTime, &aCTime);
+
+ // First field is nanoseconds.
+ // last field denotes UTC (true) or unknown (false)
+ // Here we "know" that ISC_TIME is simply in units of seconds/ISC_TIME_SECONDS_PRECISION
+ // with no other funkiness, so we can get the fractional seconds easily.
+ return Time((aISCTime % ISC_TIME_SECONDS_PRECISION) * (1000000000 / ISC_TIME_SECONDS_PRECISION),
+ aCTime.tm_sec, aCTime.tm_min, aCTime.tm_hour, false);
+ }
+ else
+ {
+ return retrieveValue< ORowSetValue >(nColumnIndex, 0).getTime();
+ }
+}
+
+template <>
+DateTime OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
+{
+ if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) == SQL_TIMESTAMP)
+ {
+ ISC_TIMESTAMP aISCTimestamp = *reinterpret_cast<ISC_TIMESTAMP*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
+
+ struct tm aCTime;
+ isc_decode_timestamp(&aISCTimestamp, &aCTime);
+
+ // Ditto here, see comment in previous function about ISC_TIME and ISC_TIME_SECONDS_PRECISION.
+ return DateTime((aISCTimestamp.timestamp_time % ISC_TIME_SECONDS_PRECISION) * (1000000000 / ISC_TIME_SECONDS_PRECISION), //nanoseconds
+ aCTime.tm_sec,
+ aCTime.tm_min,
+ aCTime.tm_hour,
+ aCTime.tm_mday,
+ aCTime.tm_mon + 1, // tm is from 0 to 11
+ aCTime.tm_year + 1900, //tm_year is the years since 1900
+ false); // denotes UTC (true), or unknown (false)
+ }
+ else
+ {
+ return retrieveValue< ORowSetValue >(nColumnIndex, 0).getDateTime();
+ }
+}
+
+template <>
+OUString OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT /*nType*/)
+{
+ // &~1 to remove the "can contain NULL" indicator
+ int aSqlType = m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1;
+ int aSqlSubType = m_pSqlda->sqlvar[nColumnIndex-1].sqlsubtype;
+ if (aSqlType == SQL_TEXT )
+ {
+ return OUString(m_pSqlda->sqlvar[nColumnIndex-1].sqldata,
+ m_pSqlda->sqlvar[nColumnIndex-1].sqllen,
+ RTL_TEXTENCODING_UTF8);
+ }
+ else if (aSqlType == SQL_VARYING)
+ {
+ // First 2 bytes are a short containing the length of the string
+ // Under unclear conditions, it may be wrong and greater than sqllen.
+ sal_uInt16 aLength = *reinterpret_cast<sal_uInt16*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
+ // Use greater signed type sal_Int32 to get the minimum of two 16-bit values
+ return OUString(m_pSqlda->sqlvar[nColumnIndex-1].sqldata + 2,
+ std::min<sal_Int32>(aLength, m_pSqlda->sqlvar[nColumnIndex-1].sqllen),
+ RTL_TEXTENCODING_UTF8);
+ }
+ else if ((aSqlType == SQL_SHORT || aSqlType == SQL_LONG ||
+ aSqlType == SQL_DOUBLE || aSqlType == SQL_INT64)
+ && (aSqlSubType == 1 ||
+ aSqlSubType == 2 ||
+ (aSqlSubType == 0 && m_pSqlda->sqlvar[nColumnIndex-1].sqlscale < 0) ) )
+ {
+ // decimal and numeric types
+ switch(aSqlType)
+ {
+ case SQL_SHORT:
+ return makeNumericString<sal_Int16>(nColumnIndex);
+ case SQL_LONG:
+ return makeNumericString<sal_Int32>(nColumnIndex);
+ case SQL_DOUBLE:
+ // TODO FIXME 64 bits?
+ case SQL_INT64:
+ return makeNumericString<sal_Int64>(nColumnIndex);
+ default:
+ assert(false);
+ return OUString(); // never reached
+ }
+ }
+ else if(aSqlType == SQL_BLOB && aSqlSubType == static_cast<short>(BlobSubtype::Clob) )
+ {
+ uno::Reference<XClob> xClob = getClob(nColumnIndex);
+ return xClob->getSubString( 1, xClob->length() );
+ }
+ else
+ {
+ return retrieveValue< ORowSetValue >(nColumnIndex, 0).getString();
+ }
+}
+
+template <>
+ISC_QUAD* OResultSet::retrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType)
+{
+ // TODO: this is probably wrong
+ if ((m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1) != nType)
+ throw SQLException(); // TODO: better exception (can't convert Blob)
+
+ return reinterpret_cast<ISC_QUAD*>(m_pSqlda->sqlvar[nColumnIndex-1].sqldata);
+}
+
+template <typename T>
+T OResultSet::safelyRetrieveValue(const sal_Int32 nColumnIndex, const ISC_SHORT nType)
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ checkColumnIndex(nColumnIndex);
+ checkRowIndex();
+
+ m_bWasNull = isNull(nColumnIndex);
+ if (m_bWasNull)
+ return T();
+
+ return retrieveValue< T >(nColumnIndex, nType);
+}
+
+// ---- XRow -----------------------------------------------------------------
+sal_Bool SAL_CALL OResultSet::wasNull()
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ return m_bWasNull;
+}
+
+// ---- XRow: Simple Numerical types ------------------------------------------
+sal_Bool SAL_CALL OResultSet::getBoolean(sal_Int32 nColumnIndex)
+{
+ return safelyRetrieveValue< bool >(nColumnIndex, SQL_BOOLEAN);
+}
+
+sal_Int8 SAL_CALL OResultSet::getByte(sal_Int32 nColumnIndex)
+{
+ // Not a native firebird type hence we always have to convert.
+ return safelyRetrieveValue< ORowSetValue >(nColumnIndex).getInt8();
+}
+
+Sequence< sal_Int8 > SAL_CALL OResultSet::getBytes(sal_Int32 nColumnIndex)
+{
+ // &~1 to remove the "can contain NULL" indicator
+ int aSqlType = m_pSqlda->sqlvar[nColumnIndex-1].sqltype & ~1;
+ if ( aSqlType == SQL_BLOB )
+ {
+ Reference< XBlob> xBlob = getBlob(nColumnIndex);
+ if (xBlob.is())
+ {
+ const sal_Int64 aBlobLength = xBlob->length();
+ if (aBlobLength > SAL_MAX_INT32)
+ {
+ SAL_WARN("connectivity.firebird", "getBytes can't return " << aBlobLength << " bytes but only max " << SAL_MAX_INT32);
+ return xBlob->getBytes(1, SAL_MAX_INT32);
+ }
+ return xBlob->getBytes(1, static_cast<sal_Int32>(aBlobLength));
+ }
+ else
+ return Sequence< sal_Int8 >();
+ }
+ // TODO implement SQL_VARYING and SQL_TEXT
+ // as it's the counterpart as OPreparedStatement::setBytes
+ else
+ {
+ return Sequence< sal_Int8 >(); // TODO: implement
+ }
+}
+
+sal_Int16 SAL_CALL OResultSet::getShort(sal_Int32 columnIndex)
+{
+ return safelyRetrieveValue< sal_Int16 >(columnIndex, SQL_SHORT);
+}
+
+sal_Int32 SAL_CALL OResultSet::getInt(sal_Int32 columnIndex)
+{
+ return safelyRetrieveValue< sal_Int32 >(columnIndex, SQL_LONG);
+}
+
+sal_Int64 SAL_CALL OResultSet::getLong(sal_Int32 columnIndex)
+{
+ return safelyRetrieveValue< sal_Int64 >(columnIndex, SQL_INT64);
+}
+
+float SAL_CALL OResultSet::getFloat(sal_Int32 columnIndex)
+{
+ return safelyRetrieveValue< float >(columnIndex, SQL_FLOAT);
+}
+
+double SAL_CALL OResultSet::getDouble(sal_Int32 columnIndex)
+{
+ return safelyRetrieveValue< double >(columnIndex, SQL_DOUBLE);
+}
+
+// ---- XRow: More complex types ----------------------------------------------
+OUString SAL_CALL OResultSet::getString(sal_Int32 nIndex)
+{
+ return safelyRetrieveValue< OUString >(nIndex);
+}
+
+Date SAL_CALL OResultSet::getDate(sal_Int32 nIndex)
+{
+ return safelyRetrieveValue< Date >(nIndex, SQL_TYPE_DATE);
+}
+
+Time SAL_CALL OResultSet::getTime(sal_Int32 nIndex)
+{
+ return safelyRetrieveValue< css::util::Time >(nIndex, SQL_TYPE_TIME);
+}
+
+DateTime SAL_CALL OResultSet::getTimestamp(sal_Int32 nIndex)
+{
+ return safelyRetrieveValue< DateTime >(nIndex, SQL_TIMESTAMP);
+}
+
+
+uno::Reference< XResultSetMetaData > SAL_CALL OResultSet::getMetaData( )
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ if(!m_xMetaData.is())
+ m_xMetaData = new OResultSetMetaData(m_pConnection
+ , m_pSqlda);
+ return m_xMetaData;
+}
+
+uno::Reference< XArray > SAL_CALL OResultSet::getArray( sal_Int32 )
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ return nullptr;
+}
+
+
+uno::Reference< XClob > SAL_CALL OResultSet::getClob( sal_Int32 columnIndex )
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ int aSqlSubType = m_pSqlda->sqlvar[columnIndex-1].sqlsubtype;
+
+ SAL_WARN_IF(aSqlSubType != 1,
+ "connectivity.firebird", "wrong subtype, not a textual blob");
+
+ ISC_QUAD* pBlobID = safelyRetrieveValue< ISC_QUAD* >(columnIndex, SQL_BLOB);
+ if (!pBlobID)
+ return nullptr;
+ return m_pConnection->createClob(pBlobID);
+}
+
+uno::Reference< XBlob > SAL_CALL OResultSet::getBlob(sal_Int32 columnIndex)
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ // TODO: CLOB etc. should be valid here too, but we probably want some more
+ // cleverness around this.
+ ISC_QUAD* pBlobID = safelyRetrieveValue< ISC_QUAD* >(columnIndex, SQL_BLOB);
+ if (!pBlobID)
+ return nullptr;
+ return m_pConnection->createBlob(pBlobID);
+}
+
+
+uno::Reference< XRef > SAL_CALL OResultSet::getRef( sal_Int32 )
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ return nullptr;
+}
+
+
+Any SAL_CALL OResultSet::getObject( sal_Int32, const uno::Reference< css::container::XNameAccess >& )
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ return Any();
+}
+
+
+void SAL_CALL OResultSet::close()
+{
+ SAL_INFO("connectivity.firebird", "close().");
+
+ {
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+ }
+ dispose();
+}
+
+
+uno::Reference< XInterface > SAL_CALL OResultSet::getStatement()
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+ return m_xStatement;
+}
+//----- XResultSet: unsupported change detection methods ---------------------
+sal_Bool SAL_CALL OResultSet::rowDeleted()
+{
+ ::dbtools::throwFunctionNotSupportedSQLException("rowDeleted not supported in firebird",
+ *this);
+ return false;
+}
+sal_Bool SAL_CALL OResultSet::rowInserted()
+{
+ ::dbtools::throwFunctionNotSupportedSQLException("rowInserted not supported in firebird",
+ *this);
+ return false;
+}
+
+sal_Bool SAL_CALL OResultSet::rowUpdated()
+{
+ ::dbtools::throwFunctionNotSupportedSQLException("rowUpdated not supported in firebird",
+ *this);
+ return false;
+}
+
+void SAL_CALL OResultSet::refreshRow()
+{
+ ::dbtools::throwFunctionNotSupportedSQLException("refreshRow not supported in firebird",
+ *this);
+}
+
+
+void SAL_CALL OResultSet::cancel( )
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+
+}
+
+//----- OIdPropertyArrayUsageHelper ------------------------------------------
+IPropertyArrayHelper* OResultSet::createArrayHelper() const
+{
+ Sequence< Property > aProperties;
+ describeProperties(aProperties);
+ return new ::cppu::OPropertyArrayHelper(aProperties);
+}
+
+IPropertyArrayHelper & OResultSet::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+
+void SAL_CALL OResultSet::acquire() noexcept
+{
+ OResultSet_BASE::acquire();
+}
+
+void SAL_CALL OResultSet::release() noexcept
+{
+ OResultSet_BASE::release();
+}
+
+uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OResultSet::getPropertySetInfo( )
+{
+ return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
+}
+
+// ---- XServiceInfo -----------------------------------------------------------
+OUString SAL_CALL OResultSet::getImplementationName()
+{
+ return "com.sun.star.sdbcx.firebird.ResultSet";
+}
+
+Sequence< OUString > SAL_CALL OResultSet::getSupportedServiceNames()
+{
+ return {"com.sun.star.sdbc.ResultSet","com.sun.star.sdbcx.ResultSet"};
+}
+
+sal_Bool SAL_CALL OResultSet::supportsService(const OUString& _rServiceName)
+{
+ return cppu::supportsService(this, _rServiceName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/ResultSet.hxx b/connectivity/source/drivers/firebird/ResultSet.hxx
new file mode 100644
index 0000000000..fdae21dfba
--- /dev/null
+++ b/connectivity/source/drivers/firebird/ResultSet.hxx
@@ -0,0 +1,216 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include "Connection.hxx"
+
+#include <ibase.h>
+
+#include <connectivity/FValue.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <comphelper/propertycontainer.hxx>
+
+#include <com/sun/star/util/XCancellable.hpp>
+#include <com/sun/star/sdbc/XCloseable.hpp>
+#include <com/sun/star/sdbc/XColumnLocate.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
+
+namespace connectivity::firebird
+ {
+ /*
+ ** OResultSet
+ */
+ typedef ::cppu::WeakComponentImplHelper< css::sdbc::XResultSet,
+ css::sdbc::XRow,
+ css::sdbc::XResultSetMetaDataSupplier,
+ css::util::XCancellable,
+ css::sdbc::XCloseable,
+ css::sdbc::XColumnLocate,
+ css::lang::XServiceInfo> OResultSet_BASE;
+
+ /**
+ * This ResultSet does not deal with the management of the SQLDA
+ * it is supplied with. The owner must mange its SQLDA appropriately
+ * and ensure that the ResultSet is destroyed before disposing of the
+ * SQLDA.
+ */
+ class OResultSet: public OResultSet_BASE,
+ public ::comphelper::OPropertyContainer,
+ public ::comphelper::OPropertyArrayUsageHelper<OResultSet>
+ {
+ private:
+ bool m_bIsBookmarkable;
+ sal_Int32 m_nFetchSize;
+ sal_Int32 m_nResultSetType;
+ sal_Int32 m_nFetchDirection;
+ sal_Int32 m_nResultSetConcurrency;
+
+ protected:
+ // Connection kept alive by m_xStatement
+ Connection* m_pConnection;
+ ::osl::Mutex& m_rMutex;
+ const css::uno::Reference< css::uno::XInterface >& m_xStatement;
+
+ css::uno::Reference< css::sdbc::XResultSetMetaData> m_xMetaData;
+
+ XSQLDA* m_pSqlda;
+ isc_stmt_handle m_statementHandle;
+
+ bool m_bWasNull;
+ // Row numbering starts with 0 for "in front of first row"
+ sal_Int32 m_currentRow;
+ bool m_bIsAfterLastRow;
+
+ const sal_Int32 m_fieldCount;
+ ISC_STATUS_ARRAY m_statusVector;
+
+ bool isNull(const sal_Int32 nColumnIndex);
+
+ template <typename T> OUString makeNumericString(
+ const sal_Int32 nColumnIndex);
+
+ template <typename T> T retrieveValue(const sal_Int32 nColumnIndex,
+ const ISC_SHORT nType);
+
+ template <typename T> T safelyRetrieveValue(
+ const sal_Int32 nColumnIndex,
+ const ISC_SHORT nType = 0);
+
+ // OIdPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper() const override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ /// @throws css::sdbc::SQLException
+ /// @throws css::uno::RuntimeException
+ void checkColumnIndex( sal_Int32 index );
+ /// @throws css::sdbc::SQLException
+ /// @throws css::uno::RuntimeException
+ void checkRowIndex();
+
+ // you can't delete objects of this type
+ virtual ~OResultSet() override;
+ public:
+ DECLARE_SERVICE_INFO();
+
+ OResultSet(Connection* pConnection,
+ ::osl::Mutex& rMutex,
+ const css::uno::Reference< css::uno::XInterface >& xStatement,
+ isc_stmt_handle aStatementHandle,
+ XSQLDA* aSqlda
+ );
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface(
+ const css::uno::Type& rType) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+ //XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ // XResultSet
+ virtual sal_Bool SAL_CALL next( ) override;
+ virtual sal_Bool SAL_CALL isBeforeFirst( ) override;
+ virtual sal_Bool SAL_CALL isAfterLast( ) override;
+ virtual sal_Bool SAL_CALL isFirst( ) override;
+ virtual sal_Bool SAL_CALL isLast( ) override;
+ virtual void SAL_CALL beforeFirst( ) override;
+ virtual void SAL_CALL afterLast( ) override;
+ virtual sal_Bool SAL_CALL first( ) override;
+ virtual sal_Bool SAL_CALL last( ) override;
+ virtual sal_Int32 SAL_CALL getRow( ) override;
+ virtual sal_Bool SAL_CALL absolute( sal_Int32 row ) override;
+ virtual sal_Bool SAL_CALL relative( sal_Int32 rows ) override;
+ virtual sal_Bool SAL_CALL previous( ) override;
+ virtual void SAL_CALL refreshRow( ) override;
+ virtual sal_Bool SAL_CALL rowUpdated( ) override;
+ virtual sal_Bool SAL_CALL rowInserted( ) override;
+ virtual sal_Bool SAL_CALL rowDeleted( ) override;
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getStatement( ) override;
+ // XRow
+ virtual sal_Bool SAL_CALL wasNull( ) override;
+ virtual OUString SAL_CALL getString( sal_Int32 columnIndex ) override;
+ virtual sal_Bool SAL_CALL getBoolean( sal_Int32 columnIndex ) override;
+ virtual sal_Int8 SAL_CALL getByte( sal_Int32 columnIndex ) override;
+ virtual sal_Int16 SAL_CALL getShort( sal_Int32 columnIndex ) override;
+ virtual sal_Int32 SAL_CALL getInt( sal_Int32 columnIndex ) override;
+ virtual sal_Int64 SAL_CALL getLong( sal_Int32 columnIndex ) override;
+ virtual float SAL_CALL getFloat( sal_Int32 columnIndex ) override;
+ virtual double SAL_CALL getDouble( sal_Int32 columnIndex ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBytes( sal_Int32 columnIndex ) override;
+ virtual css::util::Date SAL_CALL getDate( sal_Int32 columnIndex ) override;
+ virtual css::util::Time SAL_CALL getTime( sal_Int32 columnIndex ) override;
+ virtual css::util::DateTime SAL_CALL getTimestamp( sal_Int32 columnIndex ) override;
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream( sal_Int32 columnIndex ) override;
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getCharacterStream( sal_Int32 columnIndex ) override;
+ virtual css::uno::Any SAL_CALL getObject( sal_Int32 columnIndex, const css::uno::Reference< css::container::XNameAccess >& typeMap ) override;
+ virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL getRef( sal_Int32 columnIndex ) override;
+ virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( sal_Int32 columnIndex ) override;
+ virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( sal_Int32 columnIndex ) override;
+ virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( sal_Int32 columnIndex ) override;
+ // XResultSetMetaDataSupplier
+ virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override;
+ // XCancellable
+ virtual void SAL_CALL cancel( ) override;
+ // XCloseable
+ virtual void SAL_CALL close( ) override;
+ // XWarningsSupplier
+
+ // XColumnLocate
+ virtual sal_Int32 SAL_CALL findColumn(const OUString& columnName) override;
+
+ };
+
+ // Specialisations have to be in the namespace and can't be within the class.
+ template <> css::util::Date
+ OResultSet::retrieveValue(
+ const sal_Int32 nColumnIndex,
+ const ISC_SHORT nType);
+ template <> ::connectivity::ORowSetValue
+ OResultSet::retrieveValue(
+ const sal_Int32 nColumnIndex,
+ const ISC_SHORT nType);
+ template <> css::util::Time
+ OResultSet::retrieveValue(
+ const sal_Int32 nColumnIndex,
+ const ISC_SHORT nType);
+ template <> css::util::DateTime
+ OResultSet::retrieveValue(
+ const sal_Int32 nColumnIndex,
+ const ISC_SHORT nType);
+ template <> ISC_QUAD*
+ OResultSet::retrieveValue(
+ const sal_Int32 nColumnIndex,
+ const ISC_SHORT nType);
+ template <> OUString
+ OResultSet::retrieveValue(
+ const sal_Int32 nColumnIndex,
+ const ISC_SHORT nType);
+ template <> ISC_QUAD*
+ OResultSet::retrieveValue(
+ const sal_Int32 nColumnIndex,
+ const ISC_SHORT nType);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/ResultSetMetaData.cxx b/connectivity/source/drivers/firebird/ResultSetMetaData.cxx
new file mode 100644
index 0000000000..31a6796f53
--- /dev/null
+++ b/connectivity/source/drivers/firebird/ResultSetMetaData.cxx
@@ -0,0 +1,301 @@
+/* -*- 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 "ResultSetMetaData.hxx"
+#include "Util.hxx"
+
+#include <com/sun/star/sdbc/ColumnValue.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+
+#include <sal/log.hxx>
+
+using namespace connectivity::firebird;
+
+using namespace com::sun::star::lang;
+using namespace com::sun::star::sdbc;
+using namespace com::sun::star::sdbcx;
+using namespace com::sun::star::uno;
+
+OResultSetMetaData::~OResultSetMetaData()
+{
+}
+
+OUString OResultSetMetaData::getCharacterSet( sal_Int32 nIndex )
+{
+ OUString sTable = getTableName( nIndex );
+ if( !sTable.isEmpty() )
+ {
+ OUString sColumnName = getColumnName( nIndex );
+
+ OUString sSql = "SELECT charset.RDB$CHARACTER_SET_NAME "
+ "FROM RDB$CHARACTER_SETS charset "
+ "JOIN RDB$FIELDS fields "
+ "ON (fields.RDB$CHARACTER_SET_ID = charset.RDB$CHARACTER_SET_ID) "
+ "JOIN RDB$RELATION_FIELDS relfields "
+ "ON (fields.RDB$FIELD_NAME = relfields.RDB$FIELD_SOURCE) "
+ "WHERE relfields.RDB$RELATION_NAME = '"
+ + sTable.replaceAll("'", "''") + "' AND "
+ "relfields.RDB$FIELD_NAME = '"+ sColumnName.replaceAll("'", "''") +"'";
+
+ Reference<XStatement> xStmt= m_pConnection->createStatement();
+
+ Reference<XResultSet> xRes =
+ xStmt->executeQuery(sSql);
+ Reference<XRow> xRow ( xRes, UNO_QUERY);
+ if(xRes->next())
+ {
+ OUString sCharset = xRow->getString(1).trim();
+ return sCharset;
+ }
+ }
+ return OUString();
+
+
+}
+
+void OResultSetMetaData::verifyValidColumn(sal_Int32 column)
+{
+ if (column>getColumnCount() || column < 1)
+ throw SQLException("Invalid column specified", *this, OUString(), 0, Any());
+}
+
+sal_Int32 SAL_CALL OResultSetMetaData::getColumnCount()
+{
+ return m_pSqlda->sqld;
+}
+
+sal_Int32 SAL_CALL OResultSetMetaData::getColumnDisplaySize( sal_Int32 column )
+{
+ verifyValidColumn(column);
+ return 32; // Hard limit for firebird
+}
+
+sal_Int32 SAL_CALL OResultSetMetaData::getColumnType(sal_Int32 column)
+{
+ verifyValidColumn(column);
+
+ short aType = m_pSqlda->sqlvar[column-1].sqltype & ~1;
+ OUString sCharset;
+
+ // do not query the character set unnecessarily
+ if(aType == SQL_TEXT || aType == SQL_VARYING)
+ {
+ sCharset = getCharacterSet(column);
+ }
+
+ ColumnTypeInfo aInfo( m_pSqlda->sqlvar[column-1].sqltype,
+ m_pSqlda->sqlvar[column-1].sqlsubtype,
+ -(m_pSqlda->sqlvar[column-1].sqlscale),
+ sCharset );
+
+ return aInfo.getSdbcType();
+}
+
+sal_Bool SAL_CALL OResultSetMetaData::isCaseSensitive(sal_Int32)
+{
+ // Firebird is generally case sensitive when using quoted identifiers.
+ // IF THIS CHANGES make ResultSet::findColumn to be case-insensitive as needed.
+ // Generally names that are entirely UPPERCASE are case insensitive, however
+ // there remains some ambiguity if there is another mixed-case-named column
+ // of the same name. For safety always assume case insensitive.
+ return true;
+}
+
+OUString SAL_CALL OResultSetMetaData::getSchemaName(sal_Int32)
+{
+ return OUString(); // Schemas supported by firebird
+}
+
+OUString SAL_CALL OResultSetMetaData::getColumnName(sal_Int32 column)
+{
+ verifyValidColumn(column);
+ char* pColumnName = m_pSqlda->sqlvar[column - 1].sqlname;
+ sal_Int32 nColumnNameLength = m_pSqlda->sqlvar[column - 1].sqlname_length;
+ // tdf#132924 - return column alias if specified
+ if (m_pSqlda->sqlvar[column - 1].aliasname_length > 0)
+ {
+ pColumnName = m_pSqlda->sqlvar[column - 1].aliasname;
+ nColumnNameLength = m_pSqlda->sqlvar[column - 1].aliasname_length;
+ }
+ OUString sRet(pColumnName, nColumnNameLength, RTL_TEXTENCODING_UTF8);
+ sanitizeIdentifier(sRet);
+ return sRet;
+}
+
+OUString SAL_CALL OResultSetMetaData::getTableName(sal_Int32 column)
+{
+ verifyValidColumn(column);
+ return OUString(m_pSqlda->sqlvar[column-1].relname,
+ m_pSqlda->sqlvar[column-1].relname_length,
+ RTL_TEXTENCODING_UTF8);
+}
+
+OUString SAL_CALL OResultSetMetaData::getCatalogName(sal_Int32)
+{
+ return OUString(); // Catalogs not supported by firebird
+}
+
+OUString SAL_CALL OResultSetMetaData::getColumnTypeName(sal_Int32 column)
+{
+ verifyValidColumn(column);
+
+ ColumnTypeInfo aInfo( m_pSqlda->sqlvar[column-1].sqltype,
+ m_pSqlda->sqlvar[column-1].sqlsubtype,
+ -(m_pSqlda->sqlvar[column-1].sqlscale) );
+
+ return aInfo.getColumnTypeName();
+}
+
+OUString SAL_CALL OResultSetMetaData::getColumnLabel(sal_Int32 column)
+{
+ // aliasname
+ verifyValidColumn(column);
+ OUString sRet(m_pSqlda->sqlvar[column-1].aliasname,
+ m_pSqlda->sqlvar[column-1].aliasname_length,
+ RTL_TEXTENCODING_UTF8);
+ sanitizeIdentifier(sRet);
+ return sRet;
+}
+
+OUString SAL_CALL OResultSetMetaData::getColumnServiceName(sal_Int32)
+{
+ // TODO: implement
+ return OUString();
+}
+
+sal_Bool SAL_CALL OResultSetMetaData::isCurrency(sal_Int32)
+{
+ return false;
+}
+
+sal_Bool SAL_CALL OResultSetMetaData::isAutoIncrement(sal_Int32 column)
+{
+ OUString sTable = getTableName(column);
+ if( sTable.isEmpty() )
+ return false;
+
+ OUString sColumnName = getColumnName( column );
+
+ OUString sSql = "SELECT RDB$IDENTITY_TYPE FROM RDB$RELATION_FIELDS "
+ "WHERE RDB$RELATION_NAME = '"
+ + sTable.replaceAll("'", "''") + "' AND "
+ "RDB$FIELD_NAME = '"+ sColumnName.replaceAll("'", "''") +"'";
+
+ Reference<XStatement> xStmt =m_pConnection ->createStatement();
+
+ Reference<XResultSet> xRes =
+ xStmt->executeQuery(sSql);
+ Reference<XRow> xRow ( xRes, UNO_QUERY);
+ if(xRes->next())
+ {
+ int iType = xRow->getShort(1);
+ if(iType == 1) // IDENTITY
+ return true;
+ }
+ else
+ {
+ SAL_WARN("connectivity.firebird","Column '"
+ << sColumnName
+ << "' not found in database");
+
+ return false;
+ }
+ return false;
+}
+
+
+sal_Bool SAL_CALL OResultSetMetaData::isSigned(sal_Int32)
+{
+ // Unsigned values aren't supported in firebird.
+ return true;
+}
+
+sal_Int32 SAL_CALL OResultSetMetaData::getPrecision(sal_Int32 column)
+{
+ sal_Int32 nType = getColumnType(column);
+ if( nType != DataType::NUMERIC && nType != DataType::DECIMAL )
+ return 0;
+
+ OUString sColumnName = getColumnName( column );
+
+ // RDB$FIELD_SOURCE is a unique name of column per database
+ OUString sSql = "SELECT RDB$FIELD_PRECISION FROM RDB$FIELDS "
+ " INNER JOIN RDB$RELATION_FIELDS "
+ " ON RDB$RELATION_FIELDS.RDB$FIELD_SOURCE = RDB$FIELDS.RDB$FIELD_NAME "
+ "WHERE RDB$RELATION_FIELDS.RDB$RELATION_NAME = '"
+ + getTableName(column).replaceAll("'", "''") + "' AND "
+ "RDB$RELATION_FIELDS.RDB$FIELD_NAME = '"
+ + sColumnName.replaceAll("'", "''") +"'";
+ Reference<XStatement> xStmt= m_pConnection->createStatement();
+
+ Reference<XResultSet> xRes =
+ xStmt->executeQuery(sSql);
+ Reference<XRow> xRow ( xRes, UNO_QUERY);
+ if(xRes->next())
+ {
+ return static_cast<sal_Int32>(xRow->getShort(1));
+ }
+ else
+ {
+ SAL_WARN("connectivity.firebird","Column '"
+ << sColumnName
+ << "' not found in database");
+ return 0;
+ }
+ return 0;
+}
+
+sal_Int32 SAL_CALL OResultSetMetaData::getScale(sal_Int32 column)
+{
+ return -(m_pSqlda->sqlvar[column-1].sqlscale); // fb stores negative number
+}
+
+sal_Int32 SAL_CALL OResultSetMetaData::isNullable(sal_Int32 column)
+{
+ if (m_pSqlda->sqlvar[column-1].sqltype & 1)
+ return ColumnValue::NULLABLE;
+ else
+ return ColumnValue::NO_NULLS;
+}
+
+sal_Bool SAL_CALL OResultSetMetaData::isSearchable(sal_Int32)
+{
+ // TODO: Can the column be used as part of a where clause? Assume yes
+ return true;
+}
+
+sal_Bool SAL_CALL OResultSetMetaData::isReadOnly(sal_Int32)
+{
+ return m_pConnection->isReadOnly(); // Readonly only available on db level
+}
+
+sal_Bool SAL_CALL OResultSetMetaData::isDefinitelyWritable(sal_Int32)
+{
+ return !m_pConnection->isReadOnly();
+}
+
+sal_Bool SAL_CALL OResultSetMetaData::isWritable( sal_Int32 )
+{
+ return !m_pConnection->isReadOnly();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/ResultSetMetaData.hxx b/connectivity/source/drivers/firebird/ResultSetMetaData.hxx
new file mode 100644
index 0000000000..a32c206213
--- /dev/null
+++ b/connectivity/source/drivers/firebird/ResultSetMetaData.hxx
@@ -0,0 +1,80 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include "Connection.hxx"
+
+#include <ibase.h>
+
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+#include <com/sun/star/sdbc/XResultSetMetaData.hpp>
+
+namespace connectivity::firebird
+ {
+ typedef ::cppu::WeakImplHelper< css::sdbc::XResultSetMetaData>
+ OResultSetMetaData_BASE;
+
+ class OResultSetMetaData : public OResultSetMetaData_BASE
+ {
+ protected:
+ ::rtl::Reference<Connection> m_pConnection;
+ XSQLDA* m_pSqlda;
+
+ virtual ~OResultSetMetaData() override;
+
+ /// @throws css::sdbc::SQLException
+ void verifyValidColumn(sal_Int32 column);
+ OUString getCharacterSet(sal_Int32 nIndex);
+ public:
+ // a constructor, which is required for returning objects:
+ OResultSetMetaData(Connection* pConnection,
+ XSQLDA* pSqlda)
+ : m_pConnection(pConnection)
+ , m_pSqlda(pSqlda)
+ {}
+
+ virtual sal_Int32 SAL_CALL getColumnCount() override;
+ virtual sal_Bool SAL_CALL isAutoIncrement(sal_Int32 column) override;
+ virtual sal_Bool SAL_CALL isCaseSensitive(sal_Int32 column) override;
+ virtual sal_Bool SAL_CALL isSearchable(sal_Int32 column) override;
+ virtual sal_Bool SAL_CALL isCurrency(sal_Int32 column) override;
+ virtual sal_Int32 SAL_CALL isNullable(sal_Int32 column) override;
+ virtual sal_Bool SAL_CALL isSigned(sal_Int32 column) override;
+ virtual sal_Int32 SAL_CALL getColumnDisplaySize(sal_Int32 column) override;
+ virtual OUString SAL_CALL getColumnLabel(sal_Int32 column) override;
+ virtual OUString SAL_CALL getColumnName(sal_Int32 column) override;
+ virtual OUString SAL_CALL getSchemaName(sal_Int32 column) override;
+ virtual sal_Int32 SAL_CALL getPrecision(sal_Int32 column) override;
+ virtual sal_Int32 SAL_CALL getScale(sal_Int32 column) override;
+ virtual OUString SAL_CALL getTableName(sal_Int32 column) override;
+ virtual OUString SAL_CALL getCatalogName(sal_Int32 column) override;
+ virtual sal_Int32 SAL_CALL getColumnType(sal_Int32 column) override;
+ virtual OUString SAL_CALL getColumnTypeName(sal_Int32 column) override;
+ virtual sal_Bool SAL_CALL isReadOnly(sal_Int32 column) override;
+ virtual sal_Bool SAL_CALL isWritable(sal_Int32 column) override;
+ virtual sal_Bool SAL_CALL isDefinitelyWritable(sal_Int32 column) override;
+ virtual OUString SAL_CALL getColumnServiceName(sal_Int32 column) override;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Statement.cxx b/connectivity/source/drivers/firebird/Statement.cxx
new file mode 100644
index 0000000000..ed56b594a3
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Statement.cxx
@@ -0,0 +1,176 @@
+/* -*- 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 "Connection.hxx"
+#include "ResultSet.hxx"
+#include "Statement.hxx"
+#include "Util.hxx"
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <sal/log.hxx>
+
+using namespace connectivity::firebird;
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::sdbc;
+using namespace com::sun::star::sdbcx;
+using namespace com::sun::star::container;
+using namespace com::sun::star::io;
+using namespace com::sun::star::util;
+
+using namespace ::comphelper;
+using namespace ::osl;
+
+// ---- XBatchExecution - UNSUPPORTED ----------------------------------------
+void SAL_CALL OStatement::addBatch(const OUString&)
+{
+}
+
+void SAL_CALL OStatement::clearBatch()
+{
+}
+
+Sequence< sal_Int32 > SAL_CALL OStatement::executeBatch()
+{
+ return Sequence< sal_Int32 >();
+}
+
+IMPLEMENT_SERVICE_INFO(OStatement,"com.sun.star.sdbcx.OStatement","com.sun.star.sdbc.Statement");
+
+void SAL_CALL OStatement::acquire() noexcept
+{
+ OStatementCommonBase::acquire();
+}
+
+void SAL_CALL OStatement::release() noexcept
+{
+ OStatementCommonBase::release();
+}
+
+void OStatement::disposeResultSet()
+{
+ MutexGuard aGuard(m_aMutex);
+ if (OStatementCommonBase_Base::rBHelper.bDisposed)
+ return;
+
+ OStatementCommonBase::disposeResultSet();
+
+ if (m_pSqlda)
+ {
+ freeSQLVAR(m_pSqlda);
+ free(m_pSqlda);
+ m_pSqlda = nullptr;
+ }
+}
+
+// ---- XStatement -----------------------------------------------------------
+sal_Int32 SAL_CALL OStatement::executeUpdate(const OUString& sql)
+{
+ execute(sql);
+ return getStatementChangeCount();
+}
+
+
+uno::Reference< XResultSet > SAL_CALL OStatement::executeQuery(const OUString& sql)
+{
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+
+ SAL_INFO("connectivity.firebird", "executeQuery(" << sql << ")");
+
+ ISC_STATUS aErr = 0;
+
+ disposeResultSet();
+
+ prepareAndDescribeStatement(sql,
+ m_pSqlda);
+
+ aErr = isc_dsql_execute(m_statusVector,
+ &m_pConnection->getTransaction(),
+ &m_aStatementHandle,
+ 1,
+ nullptr);
+ if (aErr)
+ SAL_WARN("connectivity.firebird", "isc_dsql_execute failed");
+
+ m_xResultSet = new OResultSet(m_pConnection.get(),
+ m_aMutex,
+ uno::Reference< XInterface >(*this),
+ m_aStatementHandle,
+ m_pSqlda );
+
+ // TODO: deal with cleanup
+
+ evaluateStatusVector(m_statusVector, sql, *this);
+
+ if (isDDLStatement())
+ {
+ m_pConnection->commit();
+ m_pConnection->notifyDatabaseModified();
+ }
+ else if (getStatementChangeCount() > 0)
+ {
+ m_pConnection->notifyDatabaseModified();
+ }
+
+ return m_xResultSet;
+}
+
+sal_Bool SAL_CALL OStatement::execute(const OUString& sql)
+{
+ uno::Reference< XResultSet > xResults = executeQuery(sql);
+ return xResults.is();
+ // TODO: what if we have multiple results?
+}
+
+uno::Reference< XConnection > SAL_CALL OStatement::getConnection()
+{
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+
+ return m_pConnection;
+}
+
+Any SAL_CALL OStatement::queryInterface( const Type & rType )
+{
+ Any aRet = OStatement_Base::queryInterface(rType);
+ if(!aRet.hasValue())
+ aRet = ::cppu::queryInterface(rType,static_cast< XBatchExecution*> (this));
+ if(!aRet.hasValue())
+ aRet = OStatementCommonBase::queryInterface(rType);
+ return aRet;
+}
+
+uno::Sequence< Type > SAL_CALL OStatement::getTypes()
+{
+ return concatSequences(OStatement_Base::getTypes(),
+ OStatementCommonBase::getTypes());
+}
+
+void SAL_CALL OStatement::disposing()
+{
+ disposeResultSet();
+ close();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Statement.hxx b/connectivity/source/drivers/firebird/Statement.hxx
new file mode 100644
index 0000000000..d1b967def1
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Statement.hxx
@@ -0,0 +1,84 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include "StatementCommonBase.hxx"
+
+#include <cppuhelper/implbase1.hxx>
+#include <com/sun/star/sdbc/XBatchExecution.hpp>
+
+namespace connectivity::firebird
+ {
+
+ typedef ::cppu::ImplHelper1< css::sdbc::XStatement >
+ OStatement_Base;
+
+ class OStatement : public OStatementCommonBase,
+ public OStatement_Base,
+ public css::sdbc::XBatchExecution,
+ public css::lang::XServiceInfo
+ {
+ XSQLDA* m_pSqlda;
+ protected:
+ virtual ~OStatement() override {}
+
+ public:
+ // a constructor, which is required for returning objects:
+ explicit OStatement( Connection* _pConnection)
+ : OStatementCommonBase( _pConnection),
+ m_pSqlda(nullptr)
+ {}
+
+ virtual void disposeResultSet() override;
+
+ DECLARE_SERVICE_INFO();
+
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ // XStatement
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL
+ executeQuery(const OUString& sql) override;
+ virtual sal_Int32 SAL_CALL executeUpdate(const OUString& sqlIn) override;
+ virtual sal_Bool SAL_CALL
+ execute(const OUString& sql) override;
+ virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL
+ getConnection() override;
+
+ // XBatchExecution - UNSUPPORTED
+ virtual void SAL_CALL addBatch( const OUString& sql ) override;
+ virtual void SAL_CALL clearBatch( ) override;
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL executeBatch( ) override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL
+ queryInterface(const css::uno::Type & rType) override;
+
+ //XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL
+ getTypes() override;
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/StatementCommonBase.cxx b/connectivity/source/drivers/firebird/StatementCommonBase.cxx
new file mode 100644
index 0000000000..29ef592565
--- /dev/null
+++ b/connectivity/source/drivers/firebird/StatementCommonBase.cxx
@@ -0,0 +1,486 @@
+/* -*- 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 "StatementCommonBase.hxx"
+#include "Util.hxx"
+
+#include <sal/log.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <propertyids.hxx>
+#include <vcl/svapp.hxx>
+#include <TConnection.hxx>
+
+#include <com/sun/star/sdbc/SQLException.hpp>
+
+using namespace ::connectivity::firebird;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::util;
+
+using namespace ::comphelper;
+using namespace ::osl;
+
+OStatementCommonBase::OStatementCommonBase(Connection* _pConnection)
+ : OStatementCommonBase_Base(m_aMutex),
+ OPropertySetHelper(OStatementCommonBase_Base::rBHelper),
+ m_pConnection(_pConnection),
+#if SAL_TYPES_SIZEOFPOINTER == 8
+ m_aStatementHandle(0)
+#else
+ m_aStatementHandle(nullptr)
+#endif
+{
+}
+
+OStatementCommonBase::~OStatementCommonBase()
+{
+}
+
+void OStatementCommonBase::disposeResultSet()
+{
+ uno::Reference< XComponent > xComp(m_xResultSet, UNO_QUERY);
+ if (xComp.is())
+ xComp->dispose();
+ m_xResultSet.clear();
+}
+
+void OStatementCommonBase::freeStatementHandle()
+{
+ if (m_aStatementHandle)
+ {
+ isc_dsql_free_statement(m_statusVector,
+ &m_aStatementHandle,
+ DSQL_drop);
+ evaluateStatusVector(m_statusVector,
+ u"isc_dsql_free_statement",
+ *this);
+ }
+}
+
+
+Any SAL_CALL OStatementCommonBase::queryInterface( const Type & rType )
+{
+ Any aRet = OStatementCommonBase_Base::queryInterface(rType);
+ if(!aRet.hasValue())
+ aRet = OPropertySetHelper::queryInterface(rType);
+ return aRet;
+}
+
+Sequence< Type > SAL_CALL OStatementCommonBase::getTypes( )
+{
+ ::cppu::OTypeCollection aTypes(
+ ::cppu::UnoType<XMultiPropertySet>::get(),
+ ::cppu::UnoType<XFastPropertySet>::get(),
+ ::cppu::UnoType<XPropertySet>::get());
+
+ return concatSequences(aTypes.getTypes(),OStatementCommonBase_Base::getTypes());
+}
+
+
+void SAL_CALL OStatementCommonBase::cancel( )
+{
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ // cancel the current sql statement
+}
+
+void SAL_CALL OStatementCommonBase::close()
+{
+ SAL_INFO("connectivity.firebird", "close");
+
+ {
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+ disposeResultSet();
+ freeStatementHandle();
+ }
+
+ dispose();
+}
+
+void OStatementCommonBase::prepareAndDescribeStatement(std::u16string_view sql, XSQLDA*& pOutSqlda)
+{
+ SolarMutexGuard g; // tdf#122129
+
+ freeStatementHandle();
+
+ if (!pOutSqlda)
+ {
+ pOutSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(10)));
+ pOutSqlda->version = SQLDA_VERSION1;
+ pOutSqlda->sqln = 10;
+ }
+
+ ISC_STATUS aErr = isc_dsql_allocate_statement(m_statusVector,
+ &m_pConnection->getDBHandle(),
+ &m_aStatementHandle);
+
+ if (aErr)
+ {
+ evaluateStatusVector(m_statusVector,
+ u"isc_dsql_allocate_statement",
+ *this);
+ }
+ else
+ {
+ aErr = isc_dsql_prepare(m_statusVector,
+ &m_pConnection->getTransaction(),
+ &m_aStatementHandle,
+ 0,
+ OUStringToOString(sql, RTL_TEXTENCODING_UTF8).getStr(),
+ SQL_DIALECT_CURRENT,
+ pOutSqlda);
+
+ if (aErr)
+ {
+ evaluateStatusVector(m_statusVector,
+ u"isc_dsql_prepare",
+ *this);
+ }
+ else
+ {
+ // Ensure we have enough space in pOutSqlda
+ if (pOutSqlda->sqld > pOutSqlda->sqln)
+ {
+ int n = pOutSqlda->sqld;
+ free(pOutSqlda);
+ pOutSqlda = static_cast<XSQLDA*>(calloc(1, XSQLDA_LENGTH(n)));
+ pOutSqlda->version = SQLDA_VERSION1;
+ pOutSqlda->sqln = n;
+ aErr = isc_dsql_describe(m_statusVector,
+ &m_aStatementHandle,
+ 1,
+ pOutSqlda);
+ }
+
+ // Process each XSQLVAR parameter structure in the output XSQLDA
+ if (aErr)
+ {
+ evaluateStatusVector(m_statusVector,
+ u"isc_dsql_describe",
+ *this);
+ }
+ else
+ {
+ mallocSQLVAR(pOutSqlda);
+ }
+ }
+ if(aErr)
+ {
+ freeStatementHandle();
+ }
+ }
+ if(aErr)
+ {
+ free(pOutSqlda);
+ pOutSqlda = nullptr;
+ }
+}
+
+// ---- XMultipleResults - UNSUPPORTED ----------------------------------------
+uno::Reference< XResultSet > SAL_CALL OStatementCommonBase::getResultSet()
+{
+ // TODO: verify we really can't support this
+// return uno::Reference< XResultSet >();
+ MutexGuard aGuard(m_aMutex);
+ checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+
+ return m_xResultSet;
+}
+
+sal_Bool SAL_CALL OStatementCommonBase::getMoreResults()
+{
+ // TODO: verify we really can't support this
+ return false;
+// MutexGuard aGuard( m_aMutex );
+// checkDisposed(OStatementCommonBase_Base::rBHelper.bDisposed);
+}
+
+sal_Int32 SAL_CALL OStatementCommonBase::getUpdateCount()
+{
+ // TODO: verify we really can't support this
+ return -1;
+}
+
+
+// ---- XWarningsSupplier - UNSUPPORTED ----------------------------------------
+Any SAL_CALL OStatementCommonBase::getWarnings()
+{
+ return Any();
+}
+
+void SAL_CALL OStatementCommonBase::clearWarnings()
+{
+}
+
+::cppu::IPropertyArrayHelper* OStatementCommonBase::createArrayHelper( ) const
+{
+ // this properties are define by the service statement
+ // they must in alphabetic order
+ return new ::cppu::OPropertyArrayHelper
+ {
+ {
+ {
+ ::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME),
+ PROPERTY_ID_CURSORNAME,
+ cppu::UnoType<OUString>::get(),
+ 0
+ },
+ {
+ ::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ESCAPEPROCESSING),
+ PROPERTY_ID_ESCAPEPROCESSING,
+ cppu::UnoType<bool>::get(),
+ 0
+ },
+ {
+ ::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION),
+ PROPERTY_ID_FETCHDIRECTION,
+ cppu::UnoType<sal_Int32>::get(),
+ 0
+ },
+ {
+ ::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE),
+ PROPERTY_ID_FETCHSIZE,
+ cppu::UnoType<sal_Int32>::get(),
+ 0
+ },
+ {
+ ::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXFIELDSIZE),
+ PROPERTY_ID_MAXFIELDSIZE,
+ cppu::UnoType<sal_Int32>::get(),
+ 0
+ },
+ {
+ ::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_MAXROWS),
+ PROPERTY_ID_MAXROWS,
+ cppu::UnoType<sal_Int32>::get(),
+ 0
+ },
+ {
+ ::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_QUERYTIMEOUT),
+ PROPERTY_ID_QUERYTIMEOUT,
+ cppu::UnoType<sal_Int32>::get(),
+ 0
+ },
+ {
+ ::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY),
+ PROPERTY_ID_RESULTSETCONCURRENCY,
+ cppu::UnoType<sal_Int32>::get(),
+ 0
+ },
+ {
+ ::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE),
+ PROPERTY_ID_RESULTSETTYPE,
+ cppu::UnoType<sal_Int32>::get(),
+ 0
+ },
+ {
+ ::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_USEBOOKMARKS),
+ PROPERTY_ID_USEBOOKMARKS,
+ cppu::UnoType<bool>::get(),
+ 0
+ }
+ }
+ };
+}
+
+
+::cppu::IPropertyArrayHelper & OStatementCommonBase::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+
+sal_Bool OStatementCommonBase::convertFastPropertyValue(
+ Any &,
+ Any &,
+ sal_Int32,
+ const Any& )
+{
+ // here we have to try to convert
+ return false;
+}
+
+void OStatementCommonBase::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any&)
+{
+ // set the value to whatever is necessary
+ switch(nHandle)
+ {
+ case PROPERTY_ID_QUERYTIMEOUT:
+ case PROPERTY_ID_MAXFIELDSIZE:
+ case PROPERTY_ID_MAXROWS:
+ case PROPERTY_ID_CURSORNAME:
+ case PROPERTY_ID_RESULTSETCONCURRENCY:
+ case PROPERTY_ID_RESULTSETTYPE:
+ case PROPERTY_ID_FETCHDIRECTION:
+ case PROPERTY_ID_FETCHSIZE:
+ case PROPERTY_ID_ESCAPEPROCESSING:
+ case PROPERTY_ID_USEBOOKMARKS:
+ default:
+ ;
+ }
+}
+
+void OStatementCommonBase::getFastPropertyValue(Any&,sal_Int32 nHandle) const
+{
+ switch(nHandle)
+ {
+ case PROPERTY_ID_QUERYTIMEOUT:
+ case PROPERTY_ID_MAXFIELDSIZE:
+ case PROPERTY_ID_MAXROWS:
+ case PROPERTY_ID_CURSORNAME:
+ case PROPERTY_ID_RESULTSETCONCURRENCY:
+ case PROPERTY_ID_RESULTSETTYPE:
+ case PROPERTY_ID_FETCHDIRECTION:
+ case PROPERTY_ID_FETCHSIZE:
+ case PROPERTY_ID_ESCAPEPROCESSING:
+ case PROPERTY_ID_USEBOOKMARKS:
+ default:
+ ;
+ }
+}
+
+void SAL_CALL OStatementCommonBase::acquire() noexcept
+{
+ OStatementCommonBase_Base::acquire();
+}
+
+void SAL_CALL OStatementCommonBase::release() noexcept
+{
+ OStatementCommonBase_Base::release();
+}
+
+uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OStatementCommonBase::getPropertySetInfo( )
+{
+ return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
+}
+
+short OStatementCommonBase::getSqlInfoItem(char aInfoItem)
+{
+ ISC_STATUS_ARRAY aStatusVector;
+ ISC_STATUS aErr;
+
+ char aInfoItems[] = {aInfoItem};
+ char aResultsBuffer[8];
+
+ aErr = isc_dsql_sql_info(aStatusVector,
+ &m_aStatementHandle,
+ sizeof(aInfoItems),
+ aInfoItems,
+ sizeof(aResultsBuffer),
+ aResultsBuffer);
+
+ if (!aErr && aResultsBuffer[0] == aInfoItem)
+ {
+ const short aBytes = static_cast<short>(isc_vax_integer(aResultsBuffer+1, 2));
+ return static_cast<short>(isc_vax_integer(aResultsBuffer+3, aBytes));
+ }
+
+ evaluateStatusVector(aStatusVector,
+ u"isc_dsq_sql_info",
+ *this);
+ return 0;
+}
+
+bool OStatementCommonBase::isDDLStatement()
+{
+ return getSqlInfoItem(isc_info_sql_stmt_type) == isc_info_sql_stmt_ddl;
+}
+
+sal_Int32 OStatementCommonBase::getStatementChangeCount()
+{
+ const short aStatementType = getSqlInfoItem(isc_info_sql_stmt_type);
+
+
+ ISC_STATUS_ARRAY aStatusVector;
+ ISC_STATUS aErr;
+
+ // This is somewhat undocumented so I'm just guessing and hoping for the best.
+ char aInfoItems[] = {isc_info_sql_records};
+ char aResultsBuffer[1024];
+
+ aErr = isc_dsql_sql_info(aStatusVector,
+ &m_aStatementHandle,
+ sizeof(aInfoItems),
+ aInfoItems,
+ sizeof(aResultsBuffer),
+ aResultsBuffer);
+
+ if (aErr)
+ {
+ evaluateStatusVector(aStatusVector,
+ u"isc_dsq_sql_info",
+ *this);
+ return 0;
+ }
+
+ short aDesiredInfoType = 0;
+ switch (aStatementType)
+ {
+ case isc_info_sql_stmt_select:
+ aDesiredInfoType = isc_info_req_select_count;
+ break;
+ case isc_info_sql_stmt_insert:
+ aDesiredInfoType = isc_info_req_insert_count;
+ break;
+ case isc_info_sql_stmt_update:
+ aDesiredInfoType = isc_info_req_update_count;
+ break;
+ case isc_info_sql_stmt_delete:
+ aDesiredInfoType = isc_info_req_delete_count;
+ break;
+ case isc_info_sql_stmt_exec_procedure:
+ case isc_info_sql_stmt_ddl:
+ return 0; // cannot determine
+ default:
+ throw SQLException(); // TODO: better error message?
+ }
+
+ char* pResults = aResultsBuffer;
+ if (static_cast<short>(*pResults++) != isc_info_sql_records)
+ return 0;
+
+// const short aTotalLength = (short) isc_vax_integer(pResults, 2);
+ pResults += 2;
+
+ // Seems to be of form TOKEN (1 byte), LENGTH (2 bytes), DATA (LENGTH bytes)
+ while (*pResults != isc_info_rsb_end)
+ {
+ const char aToken = *pResults;
+ const short aLength = static_cast<short>(isc_vax_integer(pResults+1, 2));
+
+ if (aToken == aDesiredInfoType)
+ {
+ return isc_vax_integer(pResults + 3, aLength);
+ }
+
+ pResults += (3 + aLength);
+ }
+
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/StatementCommonBase.hxx b/connectivity/source/drivers/firebird/StatementCommonBase.hxx
new file mode 100644
index 0000000000..fa9cd79027
--- /dev/null
+++ b/connectivity/source/drivers/firebird/StatementCommonBase.hxx
@@ -0,0 +1,134 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "Connection.hxx"
+#include "SubComponent.hxx"
+
+#include <ibase.h>
+
+#include <cppuhelper/compbase.hxx>
+#include <rtl/ref.hxx>
+
+#include <com/sun/star/sdbc/XCloseable.hpp>
+#include <com/sun/star/sdbc/XMultipleResults.hpp>
+#include <com/sun/star/sdbc/XWarningsSupplier.hpp>
+#include <com/sun/star/util/XCancellable.hpp>
+
+namespace connectivity::firebird
+ {
+
+ typedef ::cppu::WeakComponentImplHelper< css::sdbc::XWarningsSupplier,
+ css::util::XCancellable,
+ css::sdbc::XCloseable,
+ css::sdbc::XMultipleResults> OStatementCommonBase_Base;
+
+ class OStatementCommonBase : public OStatementCommonBase_Base,
+ public ::cppu::OPropertySetHelper,
+ public OPropertyArrayUsageHelper<OStatementCommonBase>
+
+ {
+ protected:
+ ::osl::Mutex m_aMutex;
+
+ css::uno::Reference< css::sdbc::XResultSet> m_xResultSet; // The last ResultSet created
+ // for this Statement
+
+ ::rtl::Reference<Connection> m_pConnection;
+
+ ISC_STATUS_ARRAY m_statusVector;
+ isc_stmt_handle m_aStatementHandle;
+
+ protected:
+ virtual void disposeResultSet();
+ /// @throws css::sdbc::SQLException
+ void freeStatementHandle();
+
+ // OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+ // OPropertySetHelper
+ using OPropertySetHelper::getFastPropertyValue;
+ virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(
+ css::uno::Any & rConvertedValue,
+ css::uno::Any & rOldValue,
+ sal_Int32 nHandle,
+ const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(
+ sal_Int32 nHandle,
+ const css::uno::Any& rValue) override;
+ virtual void SAL_CALL getFastPropertyValue(
+ css::uno::Any& rValue,
+ sal_Int32 nHandle) const override;
+ virtual ~OStatementCommonBase() override;
+
+ /// @throws css::sdbc::SQLException
+ void prepareAndDescribeStatement(std::u16string_view sqlIn, XSQLDA*& pOutSqlda);
+
+ /// @throws css::sdbc::SQLException
+ short getSqlInfoItem(char aInfoItem);
+ /// @throws css::sdbc::SQLException
+ bool isDDLStatement();
+ /// @throws css::sdbc::SQLException
+ sal_Int32 getStatementChangeCount();
+
+ public:
+
+ explicit OStatementCommonBase(Connection* _pConnection);
+ using OStatementCommonBase_Base::operator css::uno::Reference< css::uno::XInterface >;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override {
+ disposeResultSet();
+ OStatementCommonBase_Base::disposing();
+ }
+ // XInterface
+ virtual void SAL_CALL release() noexcept override;
+ virtual void SAL_CALL acquire() noexcept override;
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ //XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+
+ // XWarningsSupplier - UNSUPPORTED
+ virtual css::uno::Any SAL_CALL getWarnings( ) override;
+ virtual void SAL_CALL clearWarnings( ) override;
+ // XMultipleResults - UNSUPPORTED
+ virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getResultSet( ) override;
+ virtual sal_Int32 SAL_CALL getUpdateCount( ) override;
+ virtual sal_Bool SAL_CALL getMoreResults( ) override;
+
+ // XCancellable
+ virtual void SAL_CALL cancel( ) override;
+ // XCloseable
+ virtual void SAL_CALL close( ) override;
+
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/SubComponent.hxx b/connectivity/source/drivers/firebird/SubComponent.hxx
new file mode 100644
index 0000000000..bea5d76d42
--- /dev/null
+++ b/connectivity/source/drivers/firebird/SubComponent.hxx
@@ -0,0 +1,111 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <cppuhelper/propshlp.hxx>
+#include <osl/diagnose.h>
+#include <osl/mutex.hxx>
+
+namespace cppu { class IPropertyArrayHelper; }
+namespace com::sun::star::lang { class XComponent; }
+
+namespace connectivity::firebird
+ {
+ /// @throws css::lang::DisposedException
+ void checkDisposed(bool _bThrow);
+
+
+ template <class TYPE>
+ class OPropertyArrayUsageHelper
+ {
+ protected:
+ static sal_Int32 s_nRefCount;
+ static ::cppu::IPropertyArrayHelper* s_pProps;
+ static ::osl::Mutex s_aMutex;
+
+ public:
+ OPropertyArrayUsageHelper();
+ virtual ~OPropertyArrayUsageHelper();
+
+ /** call this in the getInfoHelper method of your derived class. The method returns the array helper of the
+ class, which is created if necessary.
+ */
+ ::cppu::IPropertyArrayHelper* getArrayHelper();
+
+ protected:
+ /** used to implement the creation of the array helper which is shared amongst all instances of the class.
+ This method needs to be implemented in derived classes.
+ <BR>
+ The method gets called with s_aMutex acquired.
+ @return a pointer to the newly created array helper. Must not be NULL.
+ */
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const = 0;
+ };
+
+ template<class TYPE>
+ sal_Int32 OPropertyArrayUsageHelper< TYPE >::s_nRefCount = 0;
+
+ template<class TYPE>
+ ::cppu::IPropertyArrayHelper* OPropertyArrayUsageHelper< TYPE >::s_pProps = nullptr;
+
+ template<class TYPE>
+ ::osl::Mutex OPropertyArrayUsageHelper< TYPE >::s_aMutex;
+
+
+ template <class TYPE>
+ OPropertyArrayUsageHelper<TYPE>::OPropertyArrayUsageHelper()
+ {
+ ::osl::MutexGuard aGuard(s_aMutex);
+ ++s_nRefCount;
+ }
+
+
+ template <class TYPE>
+ OPropertyArrayUsageHelper<TYPE>::~OPropertyArrayUsageHelper()
+ {
+ ::osl::MutexGuard aGuard(s_aMutex);
+ OSL_ENSURE(s_nRefCount > 0, "OPropertyArrayUsageHelper::~OPropertyArrayUsageHelper : suspicious call : have a refcount of 0 !");
+ if (!--s_nRefCount)
+ {
+ delete s_pProps;
+ s_pProps = nullptr;
+ }
+ }
+
+
+ template <class TYPE>
+ ::cppu::IPropertyArrayHelper* OPropertyArrayUsageHelper<TYPE>::getArrayHelper()
+ {
+ OSL_ENSURE(s_nRefCount, "OPropertyArrayUsageHelper::getArrayHelper : suspicious call : have a refcount of 0 !");
+ if (!s_pProps)
+ {
+ ::osl::MutexGuard aGuard(s_aMutex);
+ if (!s_pProps)
+ {
+ s_pProps = createArrayHelper();
+ OSL_ENSURE(s_pProps, "OPropertyArrayUsageHelper::getArrayHelper : createArrayHelper returned nonsense !");
+ }
+ }
+ return s_pProps;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Table.cxx b/connectivity/source/drivers/firebird/Table.cxx
new file mode 100644
index 0000000000..871febcf51
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Table.cxx
@@ -0,0 +1,232 @@
+/* -*- 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/.
+ */
+
+#include "Columns.hxx"
+#include "Indexes.hxx"
+#include "Keys.hxx"
+#include "Table.hxx"
+
+#include <TConnection.hxx>
+
+#include <sal/log.hxx>
+#include <comphelper/sequence.hxx>
+#include <connectivity/dbtools.hxx>
+
+#include <com/sun/star/sdbc/ColumnValue.hpp>
+#include <com/sun/star/sdbcx/Privilege.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+using namespace ::connectivity;
+using namespace ::connectivity::firebird;
+using namespace ::connectivity::sdbcx;
+
+using namespace ::osl;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::uno;
+
+Table::Table(Tables* pTables,
+ Mutex& rMutex,
+ const uno::Reference< XConnection >& rConnection):
+ OTableHelper(pTables,
+ rConnection,
+ true),
+ m_rMutex(rMutex),
+ m_nPrivileges(0)
+{
+ construct();
+}
+
+Table::Table(Tables* pTables,
+ Mutex& rMutex,
+ const uno::Reference< XConnection >& rConnection,
+ const OUString& rName,
+ const OUString& rType,
+ const OUString& rDescription):
+ OTableHelper(pTables,
+ rConnection,
+ true,
+ rName,
+ rType,
+ rDescription,
+ "",
+ ""),
+ m_rMutex(rMutex),
+ m_nPrivileges(0)
+{
+ construct();
+}
+
+void Table::construct()
+{
+ OTableHelper::construct();
+ if (isNew())
+ return;
+
+ // TODO: get privileges when in non-embedded mode.
+ m_nPrivileges = Privilege::DROP |
+ Privilege::REFERENCE |
+ Privilege::ALTER |
+ Privilege::CREATE |
+ Privilege::READ |
+ Privilege::DELETE |
+ Privilege::UPDATE |
+ Privilege::INSERT |
+ Privilege::SELECT;
+ registerProperty(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_PRIVILEGES),
+ PROPERTY_ID_PRIVILEGES,
+ PropertyAttribute::READONLY,
+ &m_nPrivileges,
+ cppu::UnoType<decltype(m_nPrivileges)>::get());
+}
+//----- OTableHelper ---------------------------------------------------------
+OCollection* Table::createColumns(const ::std::vector< OUString>& rNames)
+{
+ return new Columns(*this,
+ m_rMutex,
+ rNames);
+}
+
+OCollection* Table::createKeys(const ::std::vector< OUString>& rNames)
+{
+ return new Keys(this,
+ m_rMutex,
+ rNames);
+}
+
+OCollection* Table::createIndexes(const ::std::vector< OUString>& rNames)
+{
+ return new Indexes(this,
+ m_rMutex,
+ rNames);
+}
+
+//----- XAlterTable -----------------------------------------------------------
+void SAL_CALL Table::alterColumnByName(const OUString& rColName,
+ const uno::Reference< XPropertySet >& rDescriptor)
+{
+ MutexGuard aGuard(m_rMutex);
+ checkDisposed(WeakComponentImplHelperBase::rBHelper.bDisposed);
+
+ uno::Reference< XPropertySet > xColumn(m_xColumns->getByName(rColName), UNO_QUERY);
+
+ // sdbcx::Descriptor
+ const bool bNameChanged = xColumn->getPropertyValue("Name") != rDescriptor->getPropertyValue("Name");
+ // sdbcx::ColumnDescriptor
+ const bool bTypeChanged = xColumn->getPropertyValue("Type") != rDescriptor->getPropertyValue("Type");
+ const bool bTypeNameChanged = xColumn->getPropertyValue("TypeName") != rDescriptor->getPropertyValue("TypeName");
+ const bool bPrecisionChanged = xColumn->getPropertyValue("Precision") != rDescriptor->getPropertyValue("Precision");
+ const bool bScaleChanged = xColumn->getPropertyValue("Scale") != rDescriptor->getPropertyValue("Scale");
+ const bool bIsNullableChanged = xColumn->getPropertyValue("IsNullable") != rDescriptor->getPropertyValue("IsNullable");
+ const bool bIsAutoIncrementChanged = xColumn->getPropertyValue("IsAutoIncrement") != rDescriptor->getPropertyValue("IsAutoIncrement");
+
+ // TODO: remainder -- these are all "optional" so have to detect presence and change.
+
+ bool bDefaultChanged = xColumn->getPropertyValue("DefaultValue")
+ != rDescriptor->getPropertyValue("DefaultValue");
+
+ if (bTypeChanged || bTypeNameChanged || bPrecisionChanged || bScaleChanged)
+ {
+ // If bPrecisionChanged this will only succeed if we have increased the
+ // precision, otherwise an exception is thrown -- however the base
+ // gui then offers to delete and recreate the column.
+ OUString sSql(getAlterTableColumn(rColName) + "TYPE " +
+ ::dbtools::createStandardTypePart(rDescriptor, getConnection()));
+ getConnection()->createStatement()->execute(sSql);
+ // TODO: could cause errors e.g. if incompatible types, deal with them here as appropriate.
+ // possibly we have to wrap things in Util::evaluateStatusVector.
+ }
+
+ if (bIsNullableChanged)
+ {
+ sal_Int32 nNullable = 0;
+ rDescriptor->getPropertyValue("IsNullable") >>= nNullable;
+
+ if (nNullable != ColumnValue::NULLABLE_UNKNOWN)
+ {
+
+ OUString sSql(getAlterTableColumn(rColName));
+ if (nNullable == ColumnValue::NULLABLE)
+ {
+ sSql += "DROP NOT NULL";
+ }
+ else if (nNullable == ColumnValue::NO_NULLS)
+ {
+ sSql += "SET NOT NULL";
+ }
+ getConnection()->createStatement()->execute(sSql);
+ }
+ else
+ {
+ SAL_WARN("connectivity.firebird", "Attempting to set Nullable to NULLABLE_UNKNOWN");
+ }
+ }
+
+ if (bIsAutoIncrementChanged)
+ {
+ ::dbtools::throwSQLException(
+ "Changing autoincrement property of existing column is not supported",
+ ::dbtools::StandardSQLState::FUNCTION_NOT_SUPPORTED,
+ *this);
+
+ }
+
+ if (bDefaultChanged)
+ {
+ OUString sNewDefault;
+ rDescriptor->getPropertyValue("DefaultValue") >>= sNewDefault;
+
+ OUString sSql;
+ if (sNewDefault.isEmpty())
+ sSql = getAlterTableColumn(rColName) + "DROP DEFAULT";
+ else
+ sSql = getAlterTableColumn(rColName) + "SET DEFAULT " + sNewDefault;
+
+ getConnection()->createStatement()->execute(sSql);
+ }
+ // TODO: quote identifiers as needed.
+ if (bNameChanged)
+ {
+ OUString sNewColName;
+ rDescriptor->getPropertyValue("Name") >>= sNewColName;
+ OUString sSql(getAlterTableColumn(rColName)
+ + " TO \"" + sNewColName + "\"");
+
+ getConnection()->createStatement()->execute(sSql);
+ }
+
+
+ m_xColumns->refresh();
+}
+
+// ----- XRename --------------------------------------------------------------
+void SAL_CALL Table::rename(const OUString&)
+{
+ throw RuntimeException("Table renaming not supported by Firebird.");
+}
+
+// ----- XInterface -----------------------------------------------------------
+Any SAL_CALL Table::queryInterface(const Type& rType)
+{
+ if (rType.getTypeName() == "com.sun.star.sdbcx.XRename")
+ return Any();
+
+ return OTableHelper::queryInterface(rType);
+}
+
+OUString Table::getAlterTableColumn(std::u16string_view rColumn)
+{
+ return ("ALTER TABLE \"" + getName() + "\" ALTER COLUMN \"" + rColumn + "\" ");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Table.hxx b/connectivity/source/drivers/firebird/Table.hxx
new file mode 100644
index 0000000000..ed638a9c88
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Table.hxx
@@ -0,0 +1,81 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "Tables.hxx"
+
+#include <connectivity/TTableHelper.hxx>
+
+namespace connectivity::firebird
+ {
+
+ /**
+ * Implements sdbcx.Table. We don't support table renaming (XRename)
+ * hence the appropriate methods are overridden.
+ */
+ class Table: public OTableHelper
+ {
+ private:
+ ::osl::Mutex& m_rMutex;
+ sal_Int32 m_nPrivileges;
+
+ /**
+ * Get the ALTER TABLE [TABLE] ALTER [COLUMN] String.
+ * Includes a trailing space.
+ */
+ OUString getAlterTableColumn(std::u16string_view rColumn);
+
+ protected:
+ void construct() override;
+
+ public:
+ Table(Tables* pTables,
+ ::osl::Mutex& rMutex,
+ const css::uno::Reference< css::sdbc::XConnection >& _xConnection);
+ Table(Tables* pTables,
+ ::osl::Mutex& rMutex,
+ const css::uno::Reference< css::sdbc::XConnection >& _xConnection,
+ const OUString& rName,
+ const OUString& rType,
+ const OUString& rDescription);
+
+ // OTableHelper
+ virtual ::connectivity::sdbcx::OCollection* createColumns(
+ const ::std::vector< OUString>& rNames) override;
+ virtual ::connectivity::sdbcx::OCollection* createKeys(
+ const ::std::vector< OUString>& rNames) override;
+ virtual ::connectivity::sdbcx::OCollection* createIndexes(
+ const ::std::vector< OUString>& rNames) override;
+
+ // XAlterTable
+ /**
+ * See css::sdbcx::ColumnDescriptor for details of
+ * rDescriptor.
+ */
+ virtual void SAL_CALL alterColumnByName(
+ const OUString& rColName,
+ const css::uno::Reference< css::beans::XPropertySet >& rDescriptor) override;
+
+ // XRename -- UNSUPPORTED
+ virtual void SAL_CALL rename(const OUString& sName) override;
+
+ //XInterface
+ virtual css::uno::Any
+ SAL_CALL queryInterface(const css::uno::Type & rType) override;
+
+ };
+
+} // namespace connectivity::firebird
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Tables.cxx b/connectivity/source/drivers/firebird/Tables.cxx
new file mode 100644
index 0000000000..8b69044bef
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Tables.cxx
@@ -0,0 +1,226 @@
+/* -*- 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/.
+ */
+
+#include "Table.hxx"
+#include "Tables.hxx"
+#include "Views.hxx"
+#include "Catalog.hxx"
+
+#include <TConnection.hxx>
+
+#include <connectivity/dbtools.hxx>
+
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/sdbc/ColumnValue.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <comphelper/types.hxx>
+
+using namespace ::connectivity;
+using namespace ::connectivity::firebird;
+using namespace ::connectivity::sdbcx;
+using namespace ::cppu;
+using namespace ::osl;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::uno;
+
+
+//----- OCollection -----------------------------------------------------------
+void Tables::impl_refresh()
+{
+ static_cast<Catalog&>(m_rParent).refreshTables();
+}
+
+ObjectType Tables::createObject(const OUString& rName)
+{
+ // Only retrieving a single table, so table type is irrelevant (param 4)
+ uno::Reference< XResultSet > xTables = m_xMetaData->getTables(Any(),
+ OUString(),
+ rName,
+ uno::Sequence< OUString >());
+
+ if (!xTables.is())
+ throw RuntimeException("Could not acquire table.");
+
+ uno::Reference< XRow > xRow(xTables,UNO_QUERY_THROW);
+
+ if (!xTables->next())
+ throw RuntimeException();
+
+ ObjectType xRet(new Table(this,
+ m_rMutex,
+ m_xMetaData->getConnection(),
+ xRow->getString(3), // Name
+ xRow->getString(4), // Type
+ xRow->getString(5))); // Description / Remarks / Comments
+
+ if (xTables->next())
+ throw RuntimeException("Found more tables than expected.");
+
+ return xRet;
+}
+
+OUString Tables::createStandardColumnPart(const Reference< XPropertySet >& xColProp,const Reference< XConnection>& _xConnection)
+{
+ Reference<XDatabaseMetaData> xMetaData = _xConnection->getMetaData();
+
+ ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
+
+ bool bIsAutoIncrement = false;
+ xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_ISAUTOINCREMENT)) >>= bIsAutoIncrement;
+
+ const OUString sQuoteString = xMetaData->getIdentifierQuoteString();
+ OUStringBuffer aSql(::dbtools::quoteName(sQuoteString,::comphelper::getString(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)))));
+
+ // check if the user enter a specific string to create autoincrement values
+ OUString sAutoIncrementValue;
+ Reference<XPropertySetInfo> xPropInfo = xColProp->getPropertySetInfo();
+
+ if ( xPropInfo.is() && xPropInfo->hasPropertyByName(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) )
+ xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_AUTOINCREMENTCREATION)) >>= sAutoIncrementValue;
+
+ aSql.append(" "
+ + dbtools::createStandardTypePart(xColProp, _xConnection));
+ // Add character set for (VAR)BINARY (fix) types:
+ // (VAR) BINARY is distinguished from other CHAR types by its character set.
+ // Octets is a special character set for binary data.
+ if ( xPropInfo.is() && xPropInfo->hasPropertyByName(rPropMap.getNameByIndex(
+ PROPERTY_ID_TYPE)) )
+ {
+ sal_Int32 aType = 0;
+ xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_TYPE))
+ >>= aType;
+ if(aType == DataType::BINARY || aType == DataType::VARBINARY)
+ {
+ aSql.append(" CHARACTER SET OCTETS");
+ }
+ }
+
+ if ( bIsAutoIncrement && !sAutoIncrementValue.isEmpty())
+ {
+ aSql.append(" " + sAutoIncrementValue);
+ }
+ // AutoIncrement "IDENTITY" is implicitly "NOT NULL"
+ else if(::comphelper::getINT32(xColProp->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_ISNULLABLE))) == ColumnValue::NO_NULLS)
+ aSql.append(" NOT NULL");
+
+ return aSql.makeStringAndClear();
+}
+
+uno::Reference< XPropertySet > Tables::createDescriptor()
+{
+ // There is some internal magic so that the same class can be used as either
+ // a descriptor or as a normal table. See VTable.cxx for the details. In our
+ // case we just need to ensure we use the correct constructor.
+ return new Table(this, m_rMutex, m_xMetaData->getConnection());
+}
+
+//----- XAppend ---------------------------------------------------------------
+ObjectType Tables::appendObject(const OUString& rName,
+ const uno::Reference< XPropertySet >& rDescriptor)
+{
+ /* OUString sSql(::dbtools::createSqlCreateTableStatement(rDescriptor,
+ m_xMetaData->getConnection())); */
+ OUStringBuffer aSqlBuffer("CREATE TABLE ");
+ OUString sCatalog, sSchema, sComposedName, sTable;
+ const Reference< XConnection>& xConnection = m_xMetaData->getConnection();
+
+ ::dbtools::OPropertyMap& rPropMap = OMetaConnection::getPropMap();
+
+ rDescriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_CATALOGNAME)) >>= sCatalog;
+ rDescriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_SCHEMANAME)) >>= sSchema;
+ rDescriptor->getPropertyValue(rPropMap.getNameByIndex(PROPERTY_ID_NAME)) >>= sTable;
+
+ sComposedName = ::dbtools::composeTableName(m_xMetaData, sCatalog, sSchema, sTable, true, ::dbtools::EComposeRule::InTableDefinitions );
+ if ( sComposedName.isEmpty() )
+ ::dbtools::throwFunctionSequenceException(xConnection);
+
+ aSqlBuffer.append(sComposedName
+ + " (");
+
+ // columns
+ Reference<XColumnsSupplier> xColumnSup(rDescriptor,UNO_QUERY);
+ Reference<XIndexAccess> xColumns(xColumnSup->getColumns(),UNO_QUERY);
+ // check if there are columns
+ if(!xColumns.is() || !xColumns->getCount())
+ ::dbtools::throwFunctionSequenceException(xConnection);
+
+ Reference< XPropertySet > xColProp;
+
+ sal_Int32 nCount = xColumns->getCount();
+ for(sal_Int32 i=0;i<nCount;++i)
+ {
+ if ( (xColumns->getByIndex(i) >>= xColProp) && xColProp.is() )
+ {
+ aSqlBuffer.append(createStandardColumnPart(xColProp,xConnection)
+ + ",");
+ }
+ }
+ OUString sSql = aSqlBuffer.makeStringAndClear();
+
+ const OUString sKeyStmt = ::dbtools::createStandardKeyStatement(rDescriptor,xConnection);
+ if ( !sKeyStmt.isEmpty() )
+ sSql += sKeyStmt;
+ else
+ {
+ if ( sSql.endsWith(",") )
+ sSql = sSql.replaceAt(sSql.getLength()-1, 1, u")");
+ else
+ sSql += ")";
+ }
+
+ m_xMetaData->getConnection()->createStatement()->execute(sSql);
+
+ return createObject(rName);
+}
+
+//----- XDrop -----------------------------------------------------------------
+void Tables::dropObject(sal_Int32 nPosition, const OUString& sName)
+{
+ uno::Reference< XPropertySet > xTable(getObject(nPosition));
+
+ if (ODescriptor::isNew(xTable))
+ return;
+
+ OUString sType;
+ xTable->getPropertyValue("Type") >>= sType;
+
+ const OUString sQuoteString = m_xMetaData->getIdentifierQuoteString();
+
+ m_xMetaData->getConnection()->createStatement()->execute(
+ "DROP " + sType + " " + ::dbtools::quoteName(sQuoteString,sName));
+
+ if (sType == "VIEW")
+ {
+ Views* pViews = static_cast<Views*>(static_cast<Catalog&>(m_rParent).getPrivateViews());
+ if ( pViews && pViews->hasByName(sName) )
+ pViews->dropByNameImpl(sName);
+ }
+}
+
+void connectivity::firebird::Tables::appendNew(const OUString& _rsNewTable)
+{
+ insertElement(_rsNewTable, nullptr);
+
+ // notify our container listeners
+ css::container::ContainerEvent aEvent(static_cast<XContainer*>(this),
+ css::uno::Any(_rsNewTable), css::uno::Any(),
+ css::uno::Any());
+ comphelper::OInterfaceIteratorHelper3 aListenerLoop(m_aContainerListeners);
+ while (aListenerLoop.hasMoreElements())
+ aListenerLoop.next()->elementInserted(aEvent);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Tables.hxx b/connectivity/source/drivers/firebird/Tables.hxx
new file mode 100644
index 0000000000..ada1827097
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Tables.hxx
@@ -0,0 +1,61 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
+
+#include <connectivity/sdbcx/VCollection.hxx>
+#include <utility>
+
+namespace connectivity::firebird
+ {
+
+ /**
+ * This implements com.sun.star.sdbcx.Container, which seems to be
+ * also known by the name of Tables and Collection.
+ */
+ class Tables: public ::connectivity::sdbcx::OCollection
+ {
+ protected:
+ css::uno::Reference< css::sdbc::XDatabaseMetaData >
+ m_xMetaData;
+
+ static OUString createStandardColumnPart(const css::uno::Reference< css::beans::XPropertySet >& xColProp,const css::uno::Reference< com::sun::star::sdbc::XConnection>& _xConnection);
+
+ // OCollection
+ virtual void impl_refresh() override;
+ virtual ::connectivity::sdbcx::ObjectType createObject(
+ const OUString& rName) override;
+ virtual css::uno::Reference< css::beans::XPropertySet >
+ createDescriptor() override;
+ virtual ::connectivity::sdbcx::ObjectType appendObject(
+ const OUString& rName,
+ const css::uno::Reference< css::beans::XPropertySet >& rDescriptor) override;
+
+ public:
+ Tables(css::uno::Reference< css::sdbc::XDatabaseMetaData > xMetaData,
+ ::cppu::OWeakObject& rParent,
+ ::osl::Mutex& rMutex,
+ ::std::vector< OUString> const & rNames) : sdbcx::OCollection(rParent, true, rMutex, rNames), m_xMetaData(std::move(xMetaData)) {}
+
+ // TODO: we should also implement XDataDescriptorFactory, XRefreshable,
+ // XAppend, etc., but all are optional.
+
+ // XDrop
+ virtual void dropObject(sal_Int32 nPosition, const OUString& rName) override;
+
+ void appendNew(const OUString& _rsNewTable);
+
+ };
+
+} // namespace connectivity::firebird
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/User.cxx b/connectivity/source/drivers/firebird/User.cxx
new file mode 100644
index 0000000000..9f647713a6
--- /dev/null
+++ b/connectivity/source/drivers/firebird/User.cxx
@@ -0,0 +1,53 @@
+/* -*- 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/.
+ */
+
+#include "User.hxx"
+
+using namespace ::connectivity;
+using namespace ::connectivity::firebird;
+using namespace ::connectivity::sdbcx;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::sdbc;
+
+User::User(const css::uno::Reference< css::sdbc::XConnection >& rConnection):
+ OUser(true) // Case Sensitive
+ , m_xConnection(rConnection)
+{}
+
+User::User(const css::uno::Reference< css::sdbc::XConnection >& rConnection, const OUString& rName):
+ OUser(rName,
+ true) // Case Sensitive
+ , m_xConnection(rConnection)
+{}
+
+void User::changePassword(const OUString&, const OUString& newPassword)
+{
+ m_xConnection->createStatement()->execute("ALTER USER " + m_Name + " PASSWORD '" + newPassword + "'");
+}
+
+sal_Int32 User::getPrivileges(const OUString& , sal_Int32 )
+{
+ // TODO: implement.
+ return 0;
+}
+
+sal_Int32 User::getGrantablePrivileges(const OUString& , sal_Int32 )
+{
+ // TODO: implement.
+ return 0;
+}
+
+//----- IRefreshableGroups ----------------------------------------------------
+void User::refreshGroups()
+{
+ // TODO: implement.
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/User.hxx b/connectivity/source/drivers/firebird/User.hxx
new file mode 100644
index 0000000000..e47565f5d5
--- /dev/null
+++ b/connectivity/source/drivers/firebird/User.hxx
@@ -0,0 +1,46 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sdbcx/VUser.hxx>
+#include <com/sun/star/sdbc/XConnection.hpp>
+
+namespace connectivity::firebird
+ {
+
+ /**
+ * This implements com.sun.star.sdbcx.Container.
+ */
+ class User: public ::connectivity::sdbcx::OUser
+ {
+ css::uno::Reference< css::sdbc::XConnection > m_xConnection;
+
+ public:
+ /**
+ * Create a "new" descriptor, which isn't yet in the database.
+ */
+ User(const css::uno::Reference< css::sdbc::XConnection >& rConnection);
+ /**
+ * For a user that already exists in the db.
+ */
+ User(const css::uno::Reference< css::sdbc::XConnection >& rConnection, const OUString& rName);
+
+ // XAuthorizable
+ virtual void SAL_CALL changePassword(const OUString&, const OUString& newPassword) override;
+ virtual sal_Int32 SAL_CALL getPrivileges(const OUString&, sal_Int32) override;
+ virtual sal_Int32 SAL_CALL getGrantablePrivileges(const OUString&, sal_Int32) override;
+
+ // IRefreshableGroups::
+ virtual void refreshGroups() override;
+ };
+
+} // namespace connectivity::firebird
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Users.cxx b/connectivity/source/drivers/firebird/Users.cxx
new file mode 100644
index 0000000000..50cfef84be
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Users.cxx
@@ -0,0 +1,78 @@
+/* -*- 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/.
+ */
+
+#include "User.hxx"
+#include "Users.hxx"
+
+using namespace ::connectivity;
+using namespace ::connectivity::firebird;
+using namespace ::connectivity::sdbcx;
+using namespace ::cppu;
+using namespace ::osl;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::uno;
+
+
+Users::Users(const uno::Reference< XDatabaseMetaData >& rMetaData,
+ OWeakObject& rParent,
+ Mutex& rMutex,
+ ::std::vector< OUString> const & rNames) :
+ OCollection(rParent,
+ true,
+ rMutex,
+ rNames),
+ m_xMetaData(rMetaData)
+{
+}
+
+//----- OCollection -----------------------------------------------------------
+void Users::impl_refresh()
+{
+ // TODO: IMPLEMENT ME
+}
+
+ObjectType Users::createObject(const OUString& rName)
+{
+ return new User(m_xMetaData->getConnection(), rName);
+}
+
+uno::Reference< XPropertySet > Users::createDescriptor()
+{
+ // There is some internal magic so that the same class can be used as either
+ // a descriptor or as a normal user. See VUser.cxx for the details. In our
+ // case we just need to ensure we use the correct constructor.
+ return new User(m_xMetaData->getConnection());
+}
+
+//----- XAppend ---------------------------------------------------------------
+ObjectType Users::appendObject(const OUString& rName,
+ const uno::Reference< XPropertySet >&)
+{
+ // TODO: set sSql as appropriate
+ m_xMetaData->getConnection()->createStatement()->execute(OUString());
+
+ return createObject(rName);
+}
+
+//----- XDrop -----------------------------------------------------------------
+void Users::dropObject(sal_Int32 nPosition, const OUString&)
+{
+ uno::Reference< XPropertySet > xUser(getObject(nPosition));
+
+ if (!ODescriptor::isNew(xUser))
+ {
+ // TODO: drop me
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Users.hxx b/connectivity/source/drivers/firebird/Users.hxx
new file mode 100644
index 0000000000..7e78444d11
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Users.hxx
@@ -0,0 +1,53 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <connectivity/sdbcx/VCollection.hxx>
+#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
+
+namespace connectivity::firebird
+ {
+
+ /**
+ * This implements com.sun.star.sdbcx.Container.
+ */
+ class Users: public ::connectivity::sdbcx::OCollection
+ {
+ css::uno::Reference< css::sdbc::XDatabaseMetaData >
+ m_xMetaData;
+ protected:
+
+ // OCollection
+ virtual void impl_refresh() override;
+ virtual ::connectivity::sdbcx::ObjectType createObject(
+ const OUString& rName) override;
+ virtual css::uno::Reference< css::beans::XPropertySet >
+ createDescriptor() override;
+ virtual ::connectivity::sdbcx::ObjectType appendObject(
+ const OUString& rName,
+ const css::uno::Reference< css::beans::XPropertySet >& rDescriptor) override;
+
+ public:
+ Users(const css::uno::Reference< css::sdbc::XDatabaseMetaData >& rMetaData,
+ ::cppu::OWeakObject& rParent,
+ ::osl::Mutex& rMutex,
+ ::std::vector< OUString> const & rNames);
+
+ // TODO: we should also implement XDataDescriptorFactory, XRefreshable,
+ // XAppend, etc., but all are optional.
+
+ // XDrop
+ virtual void dropObject(sal_Int32 nPosition, const OUString& rName) override;
+
+ };
+
+} // namespace connectivity::firebird
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Util.cxx b/connectivity/source/drivers/firebird/Util.cxx
new file mode 100644
index 0000000000..3cee5dab6e
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Util.cxx
@@ -0,0 +1,424 @@
+/* -*- 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/.
+ */
+
+#include "Util.hxx"
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <o3tl/string_view.hxx>
+
+using namespace ::connectivity;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::uno;
+
+using namespace firebird;
+
+OUString firebird::sanitizeIdentifier(std::u16string_view rIdentifier)
+{
+ std::u16string_view sRet = o3tl::trim(rIdentifier);
+ assert(sRet.size() <= 31); // Firebird identifiers cannot be longer than this.
+
+ return OUString(sRet);
+}
+
+OUString firebird::StatusVectorToString(const ISC_STATUS_ARRAY& rStatusVector,
+ std::u16string_view rCause)
+{
+ OUStringBuffer buf;
+ const ISC_STATUS* pStatus = reinterpret_cast<const ISC_STATUS*>(&rStatusVector);
+
+ buf.append("firebird_sdbc error:");
+ try
+ {
+ char msg[512]; // Size is based on suggestion in docs.
+ while(fb_interpret(msg, sizeof(msg), &pStatus))
+ {
+ // TODO: verify encoding
+ buf.append("\n*"
+ + OUString(msg, strlen(msg), RTL_TEXTENCODING_UTF8));
+ }
+ }
+ catch (...)
+ {
+ SAL_WARN("connectivity.firebird", "ignore fb_interpret exception");
+ }
+ buf.append(OUString::Concat("\ncaused by\n'") + rCause + "'\n");
+
+ OUString error = buf.makeStringAndClear();
+ SAL_WARN("connectivity.firebird", error);
+ return error;
+}
+
+void firebird::evaluateStatusVector(const ISC_STATUS_ARRAY& rStatusVector,
+ std::u16string_view rCause,
+ const uno::Reference< XInterface >& _rxContext)
+{
+ if (IndicatesError(rStatusVector))
+ {
+ OUString error = StatusVectorToString(rStatusVector, rCause);
+ throw SQLException(error, _rxContext, OUString(), 1, Any());
+ }
+}
+
+static sal_Int32 lcl_getNumberType( short aType, NumberSubType aSubType )
+{
+ switch(aSubType)
+ {
+ case NumberSubType::Numeric:
+ return DataType::NUMERIC;
+ case NumberSubType::Decimal:
+ return DataType::DECIMAL;
+ default:
+ switch(aType)
+ {
+ case SQL_SHORT:
+ return DataType::SMALLINT;
+ case SQL_LONG:
+ return DataType::INTEGER;
+ case SQL_DOUBLE:
+ return DataType::DOUBLE;
+ case SQL_INT64:
+ return DataType::BIGINT;
+ default:
+ assert(false); // not a number
+ return 0;
+ }
+ }
+}
+static sal_Int32 lcl_getCharColumnType( short aType, std::u16string_view sCharset )
+{
+ switch(aType)
+ {
+ case SQL_TEXT:
+ if( sCharset == u"OCTETS")
+ return DataType::BINARY;
+ else
+ return DataType::CHAR;
+ case SQL_VARYING:
+ if( sCharset == u"OCTETS")
+ return DataType::VARBINARY;
+ else
+ return DataType::VARCHAR;
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+sal_Int32 firebird::ColumnTypeInfo::getSdbcType() const
+{
+ short aType = m_aType & ~1; // Remove last bit -- it is used to denote whether column
+ // can store Null, not needed for type determination
+ short aSubType = m_aSubType;
+ if( m_nScale > 0 )
+ {
+ // numeric / decimal
+ if(aType == SQL_SHORT || aType == SQL_LONG || aType == SQL_DOUBLE
+ || aType == SQL_INT64)
+ {
+ // if scale is set without subtype then imply numeric
+ if( static_cast<NumberSubType>(aSubType) == NumberSubType::Other )
+ aSubType = static_cast<short>(NumberSubType::Numeric);
+ }
+ }
+
+ switch (aType)
+ {
+ case SQL_TEXT:
+ case SQL_VARYING:
+ return lcl_getCharColumnType(aType, m_sCharsetName);
+ case SQL_SHORT:
+ case SQL_LONG:
+ case SQL_DOUBLE:
+ case SQL_INT64:
+ return lcl_getNumberType(aType, static_cast<NumberSubType>(aSubType) );
+ case SQL_FLOAT:
+ return DataType::FLOAT;
+ case SQL_D_FLOAT:
+ return DataType::DOUBLE;
+ case SQL_TIMESTAMP:
+ return DataType::TIMESTAMP;
+ case SQL_BLOB:
+ switch (static_cast<BlobSubtype>(aSubType))
+ {
+ case BlobSubtype::Blob:
+ return DataType::BLOB;
+ case BlobSubtype::Clob:
+ return DataType::CLOB;
+ case BlobSubtype::Image:
+ return DataType::LONGVARBINARY;
+ default:
+ SAL_WARN("connectivity.firebird", "Unknown subtype for Blob type: " << aSubType);
+ assert(!"Unknown subtype for Blob type"); // Should never happen
+ return 0;
+ }
+ case SQL_ARRAY:
+ return DataType::ARRAY;
+ case SQL_TYPE_TIME:
+ return DataType::TIME;
+ case SQL_TYPE_DATE:
+ return DataType::DATE;
+ case SQL_NULL:
+ return DataType::SQLNULL;
+ case SQL_QUAD: // Is a "Blob ID" according to the docs
+ return 0; // TODO: verify
+ case SQL_BOOLEAN:
+ return DataType::BOOLEAN;
+ default:
+ assert(false); // Should never happen
+ return 0;
+ }
+}
+
+OUString firebird::ColumnTypeInfo::getColumnTypeName() const
+{
+ sal_Int32 nDataType = this->getSdbcType();
+ switch (nDataType)
+ {
+ case DataType::BIT:
+ return "BIT";
+ case DataType::TINYINT:
+ return "TINYINT";
+ case DataType::SMALLINT:
+ return "SMALLINT";
+ case DataType::INTEGER:
+ return "INTEGER";
+ case DataType::BIGINT:
+ return "BIGINT";
+ case DataType::FLOAT:
+ return "FLOAT";
+ case DataType::REAL:
+ return "REAL";
+ case DataType::DOUBLE:
+ return "DOUBLE";
+ case DataType::NUMERIC:
+ return "NUMERIC";
+ case DataType::DECIMAL:
+ return "DECIMAL";
+ case DataType::CHAR:
+ return "CHAR";
+ case DataType::VARCHAR:
+ return "VARCHAR";
+ case DataType::LONGVARCHAR:
+ return "LONGVARCHAR";
+ case DataType::DATE:
+ return "DATE";
+ case DataType::TIME:
+ return "TIME";
+ case DataType::TIMESTAMP:
+ return "TIMESTAMP";
+ case DataType::BINARY:
+ // in Firebird, that is the same datatype "CHAR" as DataType::CHAR,
+ // only with CHARACTER SET OCTETS; we use the synonym CHARACTER
+ // to fool LO into seeing it as different types.
+ return "CHARACTER";
+ case DataType::VARBINARY:
+ // see above comment about DataType::BINARY.
+ return "CHARACTER VARYING";
+ case DataType::LONGVARBINARY:
+ return "BLOB SUB_TYPE " + OUString::number(static_cast<short>(BlobSubtype::Image));
+ case DataType::ARRAY:
+ return "ARRAY";
+ case DataType::BLOB:
+ return "BLOB SUB_TYPE BINARY";
+ case DataType::CLOB:
+ return "BLOB SUB_TYPE TEXT";
+ case DataType::BOOLEAN:
+ return "BOOLEAN";
+ case DataType::SQLNULL:
+ return "NULL";
+ default:
+ assert(false); // Should never happen
+ return OUString();
+ }
+}
+
+short firebird::getFBTypeFromBlrType(short blrType)
+{
+ switch (blrType)
+ {
+ case blr_text:
+ return SQL_TEXT;
+ case blr_text2:
+ assert(false);
+ return 0; // No idea if this should be supported
+ case blr_varying:
+ return SQL_VARYING;
+ case blr_varying2:
+ assert(false);
+ return 0; // No idea if this should be supported
+ case blr_short:
+ return SQL_SHORT;
+ case blr_long:
+ return SQL_LONG;
+ case blr_float:
+ return SQL_FLOAT;
+ case blr_double:
+ return SQL_DOUBLE;
+ case blr_d_float:
+ return SQL_D_FLOAT;
+ case blr_timestamp:
+ return SQL_TIMESTAMP;
+ case blr_blob:
+ return SQL_BLOB;
+// case blr_SQL_ARRAY:
+// return OUString("SQL_ARRAY");
+ case blr_sql_time:
+ return SQL_TYPE_TIME;
+ case blr_sql_date:
+ return SQL_TYPE_DATE;
+ case blr_int64:
+ return SQL_INT64;
+// case SQL_NULL:
+// return OUString("SQL_NULL");
+ case blr_quad:
+ return SQL_QUAD;
+ case blr_bool:
+ return SQL_BOOLEAN;
+ default:
+ // If this happens we have hit one of the extra types in ibase.h
+ // look up blr_* for a list, e.g. blr_domain_name, blr_not_nullable etc.
+ assert(false);
+ return 0;
+ }
+}
+
+void firebird::mallocSQLVAR(XSQLDA* pSqlda)
+{
+ // TODO: confirm the sizings below.
+ XSQLVAR* pVar = pSqlda->sqlvar;
+ for (int i=0; i < pSqlda->sqld; i++, pVar++)
+ {
+ int dtype = (pVar->sqltype & ~1); /* drop flag bit for now */
+ switch(dtype) {
+ case SQL_TEXT:
+ pVar->sqldata = static_cast<char *>(malloc(sizeof(char)*pVar->sqllen));
+ break;
+ case SQL_VARYING:
+ pVar->sqldata = static_cast<char *>(malloc(sizeof(char)*pVar->sqllen + 2));
+ break;
+ case SQL_SHORT:
+ pVar->sqldata = static_cast<char*>(malloc(sizeof(sal_Int16)));
+ break;
+ case SQL_LONG:
+ pVar->sqldata = static_cast<char*>(malloc(sizeof(sal_Int32)));
+ break;
+ case SQL_FLOAT:
+ pVar->sqldata = static_cast<char *>(malloc(sizeof(float)));
+ break;
+ case SQL_DOUBLE:
+ pVar->sqldata = static_cast<char *>(malloc(sizeof(double)));
+ break;
+ case SQL_D_FLOAT:
+ pVar->sqldata = static_cast<char *>(malloc(sizeof(double)));
+ break;
+ case SQL_TIMESTAMP:
+ pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_TIMESTAMP)));
+ break;
+ // an ARRAY is in fact a BLOB of a specialized type
+ // See https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-datatypes-bnrytypes.html#fblangref25-datatypes-array
+ case SQL_ARRAY:
+ case SQL_BLOB:
+ pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_QUAD)));
+ break;
+ case SQL_TYPE_TIME:
+ pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_TIME)));
+ break;
+ case SQL_TYPE_DATE:
+ pVar->sqldata = static_cast<char*>(malloc(sizeof(ISC_DATE)));
+ break;
+ case SQL_INT64:
+ pVar->sqldata = static_cast<char *>(malloc(sizeof(sal_Int64)));
+ break;
+ case SQL_BOOLEAN:
+ pVar->sqldata = static_cast<char *>(malloc(sizeof(sal_Bool)));
+ break;
+ // See https://www.firebirdsql.org/file/documentation/html/en/refdocs/fblangref25/firebird-25-language-reference.html#fblangref25-datatypes-special-sqlnull
+ case SQL_NULL:
+ pVar->sqldata = nullptr;
+ break;
+ case SQL_QUAD:
+ assert(false); // TODO: implement
+ break;
+ default:
+ SAL_WARN("connectivity.firebird", "Unknown type: " << dtype);
+ assert(false);
+ break;
+ }
+ /* allocate variable to hold NULL status */
+ pVar->sqlind = static_cast<short *>(malloc(sizeof(short)));
+ }
+}
+
+void firebird::freeSQLVAR(XSQLDA* pSqlda)
+{
+ XSQLVAR* pVar = pSqlda->sqlvar;
+ for (int i=0; i < pSqlda->sqld; i++, pVar++)
+ {
+ int dtype = (pVar->sqltype & ~1); /* drop flag bit for now */
+ switch(dtype) {
+ case SQL_TEXT:
+ case SQL_VARYING:
+ case SQL_SHORT:
+ case SQL_LONG:
+ case SQL_FLOAT:
+ case SQL_DOUBLE:
+ case SQL_D_FLOAT:
+ case SQL_TIMESTAMP:
+ // an ARRAY is in fact a BLOB of a specialized type
+ // See https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-datatypes-bnrytypes.html#fblangref25-datatypes-array
+ case SQL_ARRAY:
+ case SQL_BLOB:
+ case SQL_INT64:
+ case SQL_TYPE_TIME:
+ case SQL_TYPE_DATE:
+ case SQL_BOOLEAN:
+ if(pVar->sqldata)
+ {
+ free(pVar->sqldata);
+ pVar->sqldata = nullptr;
+ }
+ break;
+ case SQL_NULL:
+ // See SQL_NULL case in mallocSQLVAR
+ assert(pVar->sqldata == nullptr);
+ break;
+ case SQL_QUAD:
+ assert(false); // TODO: implement
+ break;
+ default:
+ SAL_WARN("connectivity.firebird", "Unknown type: " << dtype);
+// assert(false);
+ break;
+ }
+
+ if(pVar->sqlind)
+ {
+ free(pVar->sqlind);
+ pVar->sqlind = nullptr;
+ }
+ }
+}
+
+
+sal_Int64 firebird::pow10Integer(int nDecimalCount)
+{
+ sal_Int64 nRet = 1;
+ for(int i=0; i< nDecimalCount; i++)
+ {
+ nRet *= 10;
+ }
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Util.hxx b/connectivity/source/drivers/firebird/Util.hxx
new file mode 100644
index 0000000000..db407ef98b
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Util.hxx
@@ -0,0 +1,126 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <ibase.h>
+
+#include <rtl/ustring.hxx>
+
+#include <com/sun/star/uno/XInterface.hpp>
+#include <utility>
+
+namespace connectivity::firebird
+ {
+ // Type Blob has 2 subtypes values
+ // 0 for BLOB, 1 for CLOB
+ // see http://www.firebirdfaq.org/faq48/
+ // User-defined subtypes are negative.
+ // Use a number for image which is very unlikely to be defined by a
+ // user.
+ enum class BlobSubtype {
+ Blob = 0,
+ Clob = 1,
+ Image = -9546
+ };
+
+ /**
+ * Numeric and decimal types can be identified by their subtype in
+ * Firebird API. 1 for NUMERIC, 2 for DECIMAL.
+ */
+ enum class NumberSubType {
+ Other = 0,
+ Numeric = 1,
+ Decimal = 2
+ };
+
+ class ColumnTypeInfo {
+private:
+ short m_aType;
+ short m_aSubType;
+ short m_nScale;
+ OUString m_sCharsetName;
+public:
+ /**
+ * @param tType SQL type of column defined by Firebird (e.g.
+ * SQL_DOUBLE)
+ * @param aSubType SQL sub type as in firebird API. See
+ * NumberSubType.
+ * @param scale: Scale of the number. It is ignored in case it's not
+ * a number. Scale obtained from the Firebird API is negative, so
+ * that should be negated before passing to this constructor.
+ *
+ */
+ explicit ColumnTypeInfo( short aType, short aSubType = 0,
+ short nScale = 0, OUString sCharset = OUString() )
+ : m_aType(aType)
+ , m_aSubType(aSubType)
+ , m_nScale(nScale)
+ , m_sCharsetName(std::move(sCharset)) {}
+ explicit ColumnTypeInfo( short aType, OUString sCharset )
+ : m_aType(aType)
+ , m_aSubType(0)
+ , m_nScale(0)
+ , m_sCharsetName(std::move(sCharset)) {}
+ short getType() const { return m_aType; }
+ short getSubType() const { return m_aSubType; }
+ short getScale() const { return m_nScale; }
+ OUString const & getCharacterSet() const { return m_sCharsetName; }
+
+ sal_Int32 getSdbcType() const;
+ OUString getColumnTypeName() const;
+
+ };
+
+ /**
+ * Make sure an identifier is safe to use within the database. Currently
+ * firebird seems to return identifiers with 93 character (instead of
+ * 31), whereby the name is simply padded with trailing whitespace.
+ * This removes all trailing whitespace (i.e. if necessary so that
+ * the length is below 31 characters). Firebird automatically compensates
+ * for such shorter strings, however any trailing padding makes the gui
+ * editing of such names harder, hence we remove all trailing whitespace.
+ */
+ OUString sanitizeIdentifier(std::u16string_view rIdentifier);
+
+ inline bool IndicatesError(const ISC_STATUS_ARRAY& rStatusVector)
+ {
+ return rStatusVector[0]==1 && rStatusVector[1]; // indicates error;
+ }
+
+ OUString StatusVectorToString(const ISC_STATUS_ARRAY& rStatusVector,
+ std::u16string_view rCause);
+
+ /**
+ * Evaluate a firebird status vector and throw exceptions as necessary.
+ * The content of the status vector is included in the thrown exception.
+ *
+ * @throws css::sdbc::SQLException
+ */
+ void evaluateStatusVector(const ISC_STATUS_ARRAY& rStatusVector,
+ std::u16string_view aCause,
+ const css::uno::Reference< css::uno::XInterface >& _rxContext);
+
+ /**
+ * Internally (i.e. in RDB$FIELD_TYPE) firebird stores the data type
+ * for a column as defined in blr_*, however in the firebird
+ * api the SQL_* types are used, hence we need to be able to convert
+ * between the two when retrieving column metadata.
+ */
+ short getFBTypeFromBlrType(short blrType);
+
+ void mallocSQLVAR(XSQLDA* pSqlda);
+
+ void freeSQLVAR(XSQLDA* pSqlda);
+
+ sal_Int64 pow10Integer( int nDecimalCount );
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/View.cxx b/connectivity/source/drivers/firebird/View.cxx
new file mode 100644
index 0000000000..6dcbf6bce2
--- /dev/null
+++ b/connectivity/source/drivers/firebird/View.cxx
@@ -0,0 +1,85 @@
+/* -*- 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/.
+ */
+#include "View.hxx"
+
+#include <propertyids.hxx>
+
+#include <com/sun/star/sdbc/XRow.hpp>
+
+namespace connectivity::firebird
+{
+View::View(const css::uno::Reference<css::sdbc::XConnection>& _rxConnection, bool _bCaseSensitive,
+ const OUString& _rSchemaName, const OUString& _rName)
+ : View_Base(_bCaseSensitive, _rName, _rxConnection->getMetaData(), OUString(), _rSchemaName,
+ OUString())
+ , m_xConnection(_rxConnection)
+{
+}
+
+View::~View() {}
+
+void SAL_CALL View::acquire() noexcept { View_Base::acquire(); };
+void SAL_CALL View::release() noexcept { View_Base::release(); };
+css::uno::Any SAL_CALL View::queryInterface(const css::uno::Type& _rType)
+{
+ css::uno::Any aReturn = View_Base::queryInterface(_rType);
+ if (!aReturn.hasValue())
+ aReturn = View_IBASE::queryInterface(_rType);
+ return aReturn;
+}
+
+css::uno::Sequence<css::uno::Type> SAL_CALL View::getTypes()
+{
+ return ::comphelper::concatSequences(View_Base::getTypes(), View_IBASE::getTypes());
+}
+
+css::uno::Sequence<sal_Int8> SAL_CALL View::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+void SAL_CALL View::alterCommand(const OUString& _rNewCommand)
+{
+ OUString aCommand = "ALTER VIEW \"" + m_Name + "\" AS " + _rNewCommand;
+ m_xMetaData->getConnection()->createStatement()->execute(aCommand);
+}
+
+void SAL_CALL View::getFastPropertyValue(css::uno::Any& _rValue, sal_Int32 _nHandle) const
+{
+ if (_nHandle == PROPERTY_ID_COMMAND)
+ {
+ // retrieve the very current command, don't rely on the base classes cached value
+ // (which we initialized empty, anyway)
+ _rValue <<= impl_getCommand();
+ return;
+ }
+
+ View_Base::getFastPropertyValue(_rValue, _nHandle);
+}
+
+OUString View::impl_getCommand() const
+{
+ OUString aCommand("SELECT RDB$VIEW_SOURCE FROM RDB$RELATIONS WHERE RDB$RELATION_NAME = '"
+ + m_Name + "'");
+ css::uno::Reference<css::sdbc::XStatement> statement = m_xConnection->createStatement();
+ css::uno::Reference<css::sdbc::XResultSet> xResult = statement->executeQuery(aCommand);
+
+ css::uno::Reference<css::sdbc::XRow> xRow(xResult, css::uno::UNO_QUERY_THROW);
+ if (!xResult->next())
+ {
+ // hmm. There is no view the name as we know it. Can only mean some other instance
+ // dropped this view meanwhile...
+ std::abort();
+ }
+
+ return xRow->getString(1);
+}
+
+} // namespace connectivity::firebird
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/View.hxx b/connectivity/source/drivers/firebird/View.hxx
new file mode 100644
index 0000000000..2b300a8d06
--- /dev/null
+++ b/connectivity/source/drivers/firebird/View.hxx
@@ -0,0 +1,60 @@
+/* -*- 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/.
+ */
+#pragma once
+
+#include <connectivity/sdbcx/VView.hxx>
+
+#include <com/sun/star/sdbcx/XAlterView.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/implbase1.hxx>
+
+namespace connectivity::firebird
+{
+typedef ::connectivity::sdbcx::OView View_Base;
+typedef ::cppu::ImplHelper1<css::sdbcx::XAlterView> View_IBASE;
+
+class View : public View_Base, public View_IBASE
+{
+public:
+ View(const css::uno::Reference<css::sdbc::XConnection>& _rxConnection, bool _bCaseSensitive,
+ const OUString& _rSchemaName, const OUString& _rName);
+
+ // UNO
+ virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type& aType) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ virtual css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override;
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override;
+
+ // XAlterView
+ virtual void SAL_CALL alterCommand(const OUString& NewCommand) override;
+
+protected:
+ virtual ~View() override;
+
+protected:
+ // OPropertyContainer
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& _rValue,
+ sal_Int32 _nHandle) const override;
+
+private:
+ /** retrieves the current command of the View */
+ OUString impl_getCommand() const;
+
+private:
+ css::uno::Reference<css::sdbc::XConnection> m_xConnection;
+
+ using View_Base::getFastPropertyValue;
+};
+
+} // namespace connectivity::firebird
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Views.cxx b/connectivity/source/drivers/firebird/Views.cxx
new file mode 100644
index 0000000000..2e5bec42ad
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Views.cxx
@@ -0,0 +1,112 @@
+/* -*- 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/.
+ */
+#include "Tables.hxx"
+#include "Views.hxx"
+#include "View.hxx"
+#include "Catalog.hxx"
+#include <connectivity/dbtools.hxx>
+#include <comphelper/types.hxx>
+#include <TConnection.hxx>
+
+connectivity::firebird::Views::Views(
+ const css::uno::Reference<css::sdbc::XConnection>& _rxConnection, ::cppu::OWeakObject& _rParent,
+ ::osl::Mutex& _rMutex, const ::std::vector<OUString>& _rVector)
+ : sdbcx::OCollection(_rParent, true, _rMutex, _rVector)
+ , m_xConnection(_rxConnection)
+ , m_xMetaData(_rxConnection->getMetaData())
+ , m_bInDrop(false)
+{
+}
+
+connectivity::sdbcx::ObjectType connectivity::firebird::Views::createObject(const OUString& _rName)
+{
+ OUString sCatalog, sSchema, sTable;
+ ::dbtools::qualifiedNameComponents(m_xMetaData, _rName, sCatalog, sSchema, sTable,
+ ::dbtools::EComposeRule::InDataManipulation);
+ return new View(m_xConnection, isCaseSensitive(), sSchema, sTable);
+}
+
+void connectivity::firebird::Views::impl_refresh()
+{
+ static_cast<Catalog&>(m_rParent).refreshViews();
+}
+
+css::uno::Reference<css::beans::XPropertySet> connectivity::firebird::Views::createDescriptor()
+{
+ return new connectivity::sdbcx::OView(true, m_xMetaData);
+}
+
+// XAppend
+connectivity::sdbcx::ObjectType connectivity::firebird::Views::appendObject(
+ const OUString& _rForName, const css::uno::Reference<css::beans::XPropertySet>& descriptor)
+{
+ createView(descriptor);
+ return createObject(_rForName);
+}
+
+// XDrop
+void connectivity::firebird::Views::dropObject(sal_Int32 _nPos, const OUString& /*_sElementName*/)
+{
+ if (m_bInDrop)
+ return;
+
+ css::uno::Reference<XInterface> xObject(getObject(_nPos));
+ bool bIsNew = connectivity::sdbcx::ODescriptor::isNew(xObject);
+ if (!bIsNew)
+ {
+ OUString aSql("DROP VIEW");
+
+ css::uno::Reference<css::beans::XPropertySet> xProp(xObject, css::uno::UNO_QUERY);
+ aSql += ::dbtools::composeTableName(m_xMetaData, xProp,
+ ::dbtools::EComposeRule::InTableDefinitions, true);
+
+ css::uno::Reference<css::sdbc::XConnection> xConnection = m_xMetaData->getConnection();
+ css::uno::Reference<css::sdbc::XStatement> xStmt = xConnection->createStatement();
+ xStmt->execute(aSql);
+ ::comphelper::disposeComponent(xStmt);
+ }
+}
+
+void connectivity::firebird::Views::dropByNameImpl(const OUString& elementName)
+{
+ m_bInDrop = true;
+ connectivity::sdbcx::OCollection::dropByName(elementName);
+ m_bInDrop = false;
+}
+
+void connectivity::firebird::Views::createView(
+ const css::uno::Reference<css::beans::XPropertySet>& descriptor)
+{
+ css::uno::Reference<css::sdbc::XConnection> xConnection = m_xMetaData->getConnection();
+
+ OUString sCommand;
+ descriptor->getPropertyValue(OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_COMMAND))
+ >>= sCommand;
+
+ OUString aSql = "CREATE VIEW "
+ + ::dbtools::composeTableName(m_xMetaData, descriptor,
+ ::dbtools::EComposeRule::InTableDefinitions, true)
+ + " AS " + sCommand;
+
+ css::uno::Reference<css::sdbc::XStatement> xStmt = xConnection->createStatement();
+ if (xStmt.is())
+ {
+ xStmt->execute(aSql);
+ ::comphelper::disposeComponent(xStmt);
+ }
+ connectivity::firebird::Tables* pTables = static_cast<connectivity::firebird::Tables*>(
+ static_cast<connectivity::firebird::Catalog&>(m_rParent).getPrivateTables());
+ if (pTables)
+ {
+ OUString sName = ::dbtools::composeTableName(
+ m_xMetaData, descriptor, ::dbtools::EComposeRule::InDataManipulation, false);
+ pTables->appendNew(sName);
+ }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/Views.hxx b/connectivity/source/drivers/firebird/Views.hxx
new file mode 100644
index 0000000000..6887bdc66e
--- /dev/null
+++ b/connectivity/source/drivers/firebird/Views.hxx
@@ -0,0 +1,42 @@
+/* -*- 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/.
+ */
+#pragma once
+
+#include <connectivity/sdbcx/VCollection.hxx>
+#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
+namespace connectivity::firebird
+{
+class Views final : public connectivity::sdbcx::OCollection
+{
+ css::uno::Reference<css::sdbc::XConnection> m_xConnection;
+ css::uno::Reference<css::sdbc::XDatabaseMetaData> m_xMetaData;
+ bool m_bInDrop;
+
+ // OCollection
+ virtual connectivity::sdbcx::ObjectType createObject(const OUString& _rName) override;
+ virtual void impl_refresh() override;
+ virtual css::uno::Reference<css::beans::XPropertySet> createDescriptor() override;
+ virtual sdbcx::ObjectType
+ appendObject(const OUString& _rForName,
+ const css::uno::Reference<css::beans::XPropertySet>& descriptor) override;
+
+ void createView(const css::uno::Reference<css::beans::XPropertySet>& descriptor);
+
+public:
+ Views(const css::uno::Reference<css::sdbc::XConnection>& _rxConnection,
+ ::cppu::OWeakObject& _rParent, ::osl::Mutex& _rMutex,
+ const ::std::vector<OUString>& _rVector);
+
+ // XDrop
+ virtual void dropObject(sal_Int32 _nPos, const OUString& _sElementName) override;
+
+ void dropByNameImpl(const OUString& elementName);
+};
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/connectivity/source/drivers/firebird/firebird_sdbc.component b/connectivity/source/drivers/firebird/firebird_sdbc.component
new file mode 100644
index 0000000000..d80755a24d
--- /dev/null
+++ b/connectivity/source/drivers/firebird/firebird_sdbc.component
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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/.
+ *
+-->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.sdbc.firebird.Driver"
+ constructor="connectivity_FirebirdDriver_get_implementation">
+ <service name="com.sun.star.sdbc.Driver"/>
+ <service name="com.sun.star.sdbcx.Driver"/>
+ </implementation>
+</component>
+