1
0
Fork 0
libreoffice/connectivity/source/drivers/mysqlc/mysqlc_preparedstatement.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

578 lines
19 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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 "mysqlc_general.hxx"
#include "mysqlc_prepared_resultset.hxx"
#include "mysqlc_preparedstatement.hxx"
#include "mysqlc_propertyids.hxx"
#include "mysqlc_resultsetmetadata.hxx"
#include <o3tl/safeint.hxx>
#include <sal/log.hxx>
#include <com/sun/star/sdbc/DataType.hpp>
#include <stdio.h>
using namespace connectivity::mysqlc;
using namespace com::sun::star::uno;
using namespace com::sun::star::sdbc;
using namespace com::sun::star::container;
using namespace com::sun::star::io;
using namespace com::sun::star::util;
using ::osl::MutexGuard;
OUString OPreparedStatement::getImplementationName()
{
return u"com.sun.star.sdbcx.mysqlc.PreparedStatement"_ustr;
}
css::uno::Sequence<OUString> OPreparedStatement::getSupportedServiceNames()
{
return { u"com.sun.star.sdbc.PreparedStatement"_ustr };
}
sal_Bool OPreparedStatement::supportsService(OUString const& ServiceName)
{
return cppu::supportsService(this, ServiceName);
}
OPreparedStatement::OPreparedStatement(OConnection* _pConnection, MYSQL_STMT* pStmt)
: OCommonStatement(_pConnection)
, m_pStmt(pStmt)
{
m_paramCount = mysql_stmt_param_count(m_pStmt);
m_binds.reserve(m_paramCount);
m_bindMetas.reserve(m_paramCount);
for (unsigned i = 0; i < m_paramCount; ++i)
{
m_binds.push_back(MYSQL_BIND{});
m_bindMetas.push_back(BindMetaData{});
m_binds.back().is_null = &m_bindMetas.back().is_null;
m_binds.back().length = &m_bindMetas.back().length;
m_binds.back().buffer = nullptr;
}
}
OPreparedStatement::~OPreparedStatement() {}
void SAL_CALL OPreparedStatement::acquire() noexcept { OCommonStatement::acquire(); }
void SAL_CALL OPreparedStatement::release() noexcept { OCommonStatement::release(); }
Any SAL_CALL OPreparedStatement::queryInterface(const Type& rType)
{
Any aRet = OCommonStatement::queryInterface(rType);
if (!aRet.hasValue())
{
aRet = OPreparedStatement_BASE::queryInterface(rType);
}
return aRet;
}
Sequence<Type> SAL_CALL OPreparedStatement::getTypes()
{
return concatSequences(OPreparedStatement_BASE::getTypes(), OCommonStatement::getTypes());
}
Reference<XResultSetMetaData> SAL_CALL OPreparedStatement::getMetaData()
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
if (!m_xMetaData.is())
{
MYSQL_RES* pRes = mysql_stmt_result_metadata(m_pStmt);
// TODO warning or error if no meta data.
m_xMetaData = new OResultSetMetaData(*m_xConnection, pRes);
}
return m_xMetaData;
}
void SAL_CALL OPreparedStatement::close()
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
if (mysql_stmt_close(m_pStmt))
{
SAL_WARN("connectivity.mysqlc", "failed to close mysql prepared statement");
}
m_pStmt = nullptr; // it's deallocated already
clearWarnings();
clearParameters();
OCommonStatement::close();
}
sal_Bool SAL_CALL OPreparedStatement::execute()
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
if (!m_binds.empty() && mysql_stmt_bind_param(m_pStmt, m_binds.data()))
{
MYSQL* pMysql = m_xConnection->getMysqlConnection();
mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_stmt_error(m_pStmt),
mysql_sqlstate(pMysql), mysql_errno(pMysql),
*this, m_xConnection->getConnectionEncoding());
}
int nFail = mysql_stmt_execute(m_pStmt);
if (nFail != 0)
{
MYSQL* pMysql = m_xConnection->getMysqlConnection();
mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_stmt_error(m_pStmt),
mysql_sqlstate(pMysql), mysql_errno(pMysql),
*this, m_xConnection->getConnectionEncoding());
}
return !nFail;
}
sal_Int32 SAL_CALL OPreparedStatement::executeUpdate()
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
if (!m_binds.empty() && mysql_stmt_bind_param(m_pStmt, m_binds.data()))
{
MYSQL* pMysql = m_xConnection->getMysqlConnection();
mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_stmt_error(m_pStmt),
mysql_sqlstate(pMysql), mysql_errno(pMysql),
*this, m_xConnection->getConnectionEncoding());
}
int nFail = mysql_stmt_execute(m_pStmt);
if (nFail != 0)
{
MYSQL* pMysql = m_xConnection->getMysqlConnection();
mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_stmt_error(m_pStmt),
mysql_sqlstate(pMysql), mysql_errno(pMysql),
*this, m_xConnection->getConnectionEncoding());
}
sal_Int32 affectedRows = mysql_stmt_affected_rows(m_pStmt);
return affectedRows;
}
void SAL_CALL OPreparedStatement::setString(sal_Int32 parameter, const OUString& x)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
OString stringie(OUStringToOString(x, m_xConnection->getConnectionEncoding()));
const sal_Int32 nIndex = parameter - 1;
m_binds[nIndex].buffer_type = MYSQL_TYPE_STRING;
mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, stringie.getStr(), MYSQL_TYPE_STRING,
stringie.getLength());
m_bindMetas[nIndex].is_null = false;
m_bindMetas[nIndex].length = stringie.getLength();
}
Reference<XConnection> SAL_CALL OPreparedStatement::getConnection()
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
return m_xConnection;
}
Reference<XResultSet> SAL_CALL OPreparedStatement::executeQuery()
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
if (!m_binds.empty() && mysql_stmt_bind_param(m_pStmt, m_binds.data()))
{
MYSQL* pMysql = m_xConnection->getMysqlConnection();
mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_stmt_error(m_pStmt),
mysql_sqlstate(pMysql), mysql_errno(pMysql),
*this, m_xConnection->getConnectionEncoding());
}
int nFail = mysql_stmt_execute(m_pStmt);
if (nFail != 0)
{
MYSQL* pMysql = m_xConnection->getMysqlConnection();
mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_stmt_error(m_pStmt),
mysql_sqlstate(pMysql), mysql_errno(pMysql),
*this, m_xConnection->getConnectionEncoding());
}
Reference<XResultSet> xResultSet = new OPreparedResultSet(*m_xConnection, this, m_pStmt);
return xResultSet;
}
void SAL_CALL OPreparedStatement::setBoolean(sal_Int32 parameter, sal_Bool x)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
const sal_Int32 nIndex = parameter - 1;
m_binds[nIndex].buffer_type = MYSQL_TYPE_TINY;
mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x, MYSQL_TYPE_TINY);
m_bindMetas[nIndex].is_null = false;
}
void SAL_CALL OPreparedStatement::setByte(sal_Int32 parameter, sal_Int8 x)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
const sal_Int32 nIndex = parameter - 1;
m_binds[nIndex].buffer_type = MYSQL_TYPE_TINY;
mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x, MYSQL_TYPE_TINY);
m_bindMetas[nIndex].is_null = false;
}
void SAL_CALL OPreparedStatement::setDate(sal_Int32 parameter, const Date& aData)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
MYSQL_TIME my_time = {};
my_time.year = aData.Year;
my_time.month = aData.Month;
my_time.day = aData.Day;
const sal_Int32 nIndex = parameter - 1;
m_binds[nIndex].buffer_type = MYSQL_TYPE_DATE;
mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &my_time, MYSQL_TYPE_DATE);
m_bindMetas[nIndex].is_null = false;
}
void SAL_CALL OPreparedStatement::setTime(sal_Int32 parameter, const Time& aVal)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
MYSQL_TIME my_time = {};
my_time.hour = aVal.Hours;
my_time.minute = aVal.Minutes;
my_time.second = aVal.Seconds;
const sal_Int32 nIndex = parameter - 1;
m_binds[nIndex].buffer_type = MYSQL_TYPE_TIME;
mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &my_time, MYSQL_TYPE_TIME);
m_bindMetas[nIndex].is_null = false;
}
void SAL_CALL OPreparedStatement::setTimestamp(sal_Int32 parameter, const DateTime& aVal)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
MYSQL_TIME my_time = {};
my_time.hour = aVal.Hours;
my_time.minute = aVal.Minutes;
my_time.second = aVal.Seconds;
my_time.year = aVal.Year;
my_time.month = aVal.Month;
my_time.day = aVal.Day;
const sal_Int32 nIndex = parameter - 1;
m_binds[nIndex].buffer_type = MYSQL_TYPE_DATETIME;
mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &my_time, MYSQL_TYPE_DATETIME);
m_bindMetas[nIndex].is_null = false;
}
void SAL_CALL OPreparedStatement::setDouble(sal_Int32 parameter, double x)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
const sal_Int32 nIndex = parameter - 1;
m_binds[nIndex].buffer_type = MYSQL_TYPE_DOUBLE;
mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x, MYSQL_TYPE_DOUBLE);
m_bindMetas[nIndex].is_null = false;
}
void SAL_CALL OPreparedStatement::setFloat(sal_Int32 parameter, float x)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
const sal_Int32 nIndex = parameter - 1;
m_binds[nIndex].buffer_type = MYSQL_TYPE_FLOAT;
mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x, MYSQL_TYPE_FLOAT);
m_bindMetas[nIndex].is_null = false;
}
void SAL_CALL OPreparedStatement::setInt(sal_Int32 parameter, sal_Int32 x)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
const sal_Int32 nIndex = parameter - 1;
m_binds[nIndex].buffer_type = MYSQL_TYPE_LONG;
mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x, MYSQL_TYPE_LONG);
m_bindMetas[nIndex].is_null = false;
}
void SAL_CALL OPreparedStatement::setLong(sal_Int32 parameter, sal_Int64 aVal)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
const sal_Int32 nIndex = parameter - 1;
m_binds[nIndex].buffer_type = MYSQL_TYPE_LONGLONG;
mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &aVal, MYSQL_TYPE_LONGLONG);
m_bindMetas[nIndex].is_null = false;
}
void SAL_CALL OPreparedStatement::setNull(sal_Int32 parameter, sal_Int32 /*sqlType*/)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
const sal_Int32 nIndex = parameter - 1;
m_bindMetas[nIndex].is_null = true;
free(m_binds[nIndex].buffer);
m_binds[nIndex].buffer = nullptr;
}
void SAL_CALL OPreparedStatement::setClob(sal_Int32 parameter, const Reference<XClob>& /* x */)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setClob", *this);
}
void SAL_CALL OPreparedStatement::setBlob(sal_Int32 parameter, const Reference<XBlob>& /* x */)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setBlob", *this);
}
void SAL_CALL OPreparedStatement::setArray(sal_Int32 parameter, const Reference<XArray>& /* x */)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setArray", *this);
}
void SAL_CALL OPreparedStatement::setRef(sal_Int32 parameter, const Reference<XRef>& /* x */)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setRef", *this);
}
void SAL_CALL OPreparedStatement::setObjectWithInfo(sal_Int32 parameterIndex, const Any& value,
sal_Int32 targetSqlType, sal_Int32 /* scale */)
{
checkDisposed(rBHelper.bDisposed);
MutexGuard aGuard(m_aMutex);
checkParameterIndex(parameterIndex);
const sal_Int32 nIndex = parameterIndex - 1;
if (!value.hasValue())
{
free(m_binds[nIndex].buffer);
m_binds[nIndex].buffer = nullptr;
m_bindMetas[parameterIndex - 1].is_null = true;
return;
}
switch (targetSqlType)
{
case DataType::DECIMAL:
case DataType::NUMERIC:
{
double nValue(0.0);
OUString sValue;
if (value >>= nValue)
{
setDouble(parameterIndex, nValue);
break;
}
else if (value >>= sValue)
{
OString sAscii
= OUStringToOString(sValue, getOwnConnection()->getConnectionEncoding());
std::stringstream sStream{ std::string(sAscii) };
sStream >> nValue;
m_binds[nIndex].buffer_type = MYSQL_TYPE_DOUBLE;
mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &nValue, MYSQL_TYPE_DOUBLE,
sValue.getLength());
m_bindMetas[nIndex].is_null = false;
break;
}
[[fallthrough]];
}
// TODO other types
default:
mysqlc_sdbc_driver::throwInvalidArgumentException(
"OPreparedStatement::setObjectWithInfo", *this);
break;
}
}
void SAL_CALL OPreparedStatement::setObjectNull(sal_Int32 parameter, sal_Int32 /* sqlType */,
const OUString& /* typeName */)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setObjectNull",
*this);
}
void SAL_CALL OPreparedStatement::setObject(sal_Int32 parameter, const Any& /* x */)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setObject", *this);
}
void SAL_CALL OPreparedStatement::setShort(sal_Int32 parameter, sal_Int16 x)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
const sal_Int32 nIndex = parameter - 1;
m_binds[nIndex].buffer_type = MYSQL_TYPE_SHORT;
mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x, MYSQL_TYPE_SHORT);
m_bindMetas[nIndex].is_null = false;
}
void SAL_CALL OPreparedStatement::setBytes(sal_Int32 parameter, const Sequence<sal_Int8>& x)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
const sal_Int32 nIndex = parameter - 1;
m_binds[nIndex].buffer_type = MYSQL_TYPE_BLOB;
mysqlc_sdbc_driver::resetSqlVar(&m_binds[nIndex].buffer, &x[0], MYSQL_TYPE_BLOB, x.getLength());
m_bindMetas[nIndex].is_null = false;
m_bindMetas[nIndex].length = x.getLength();
}
void SAL_CALL OPreparedStatement::setCharacterStream(sal_Int32 parameter,
const Reference<XInputStream>& /* x */,
sal_Int32 /* length */)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
mysqlc_sdbc_driver::throwFeatureNotImplementedException(
"OPreparedStatement::setCharacterStream", *this);
}
void SAL_CALL OPreparedStatement::setBinaryStream(sal_Int32 parameter,
const Reference<XInputStream>& /* x */,
sal_Int32 /* length */)
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
checkParameterIndex(parameter);
mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::setBinaryStream",
*this);
}
void SAL_CALL OPreparedStatement::clearParameters()
{
MutexGuard aGuard(m_aMutex);
checkDisposed(rBHelper.bDisposed);
for (size_t i = 0; i < m_binds.size(); ++i)
{
m_bindMetas[i].is_null = true;
free(m_binds[i].buffer);
m_binds[i].buffer = nullptr;
}
}
// void SAL_CALL OPreparedStatement::clearBatch()
// {
// mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::clearBatch",
// *this);
// }
// void SAL_CALL OPreparedStatement::addBatch()
// {
// mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::addBatch", *this);
// }
// Sequence<sal_Int32> SAL_CALL OPreparedStatement::executeBatch() {
// mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedStatement::executeBatch", *this);
// }
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:
/* XXX: Recursion ?? */
OPreparedStatement::setFastPropertyValue_NoBroadcast(nHandle, rValue);
}
}
void OPreparedStatement::checkParameterIndex(sal_Int32 column)
{
if (column < 1 || o3tl::make_unsigned(column) > m_paramCount)
{
throw SQLException(u"Parameter index out of range"_ustr, *this, OUString(), 1, Any());
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */