File indexing completed on 2024-04-28 15:58:51

0001 /* This file is part of the KDE project
0002    Copyright (C) 2006-2012 Jarosław Staniek <staniek@kde.org>
0003 
0004    This program is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This program is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this program; see the file COPYING.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "MysqlPreparedStatement.h"
0021 #include "KDbConnection.h"
0022 
0023 //#include <mysql/errmsg.h>
0024 // For example prepared MySQL statement code see:
0025 // https://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-execute.html
0026 
0027 MysqlPreparedStatement::MysqlPreparedStatement(MysqlConnectionInternal* conn)
0028         : KDbPreparedStatementInterface()
0029         , MysqlConnectionInternal(conn->connection)
0030 #ifdef KDB_USE_MYSQL_STMT
0031         , m_statement(0)
0032         , m_mysqlBind(0)
0033 #endif
0034         , m_resetRequired(false)
0035 {
0036 // mysqlDebug();
0037     mysql_owned = false;
0038     mysql = conn->mysql;
0039     if (!init()) {
0040         done();
0041     }
0042 }
0043 
0044 bool MysqlPreparedStatement::init()
0045 {
0046 #ifdef KDB_USE_MYSQL_STMT
0047     m_statement = mysql_stmt_init(mysql);
0048     if (!m_statement) {
0049 //! @todo err 'out of memory'
0050         return false;
0051     }
0052     res = mysql_stmt_prepare(m_statement,
0053                              (const char*)m_tempStatementString, m_tempStatementString.length());
0054     if (0 != res) {
0055 //! @todo use mysql_stmt_error(stmt); to show error
0056         return false;
0057     }
0058 
0059     m_realParamCount = mysql_stmt_param_count(m_statement);
0060     if (m_realParamCount <= 0) {
0061 //! @todo err
0062         return false;
0063     }
0064     m_mysqlBind = new MYSQL_BIND[ m_realParamCount ];
0065     memset(m_mysqlBind, 0, sizeof(MYSQL_BIND)*m_realParamCount); //safe?
0066 #endif
0067     return true;
0068 }
0069 
0070 MysqlPreparedStatement::~MysqlPreparedStatement()
0071 {
0072     done();
0073 }
0074 
0075 void MysqlPreparedStatement::done()
0076 {
0077 #ifdef KDB_USE_MYSQL_STMT
0078     if (m_statement) {
0079 //! @todo handle errors of mysql_stmt_close()?
0080         mysql_stmt_close(m_statement);
0081         m_statement = 0;
0082     }
0083     delete m_mysqlBind;
0084     m_mysqlBind = 0;
0085 #endif
0086 }
0087 
0088 bool MysqlPreparedStatement::prepare(const KDbEscapedString& sql)
0089 {
0090     Q_UNUSED(sql);
0091     return true;
0092 }
0093 
0094 #ifdef KDB_USE_MYSQL_STMT
0095 #define BIND_NULL { \
0096         m_mysqlBind[arg].buffer_type = MYSQL_TYPE_NULL; \
0097         m_mysqlBind[arg].buffer = 0; \
0098         m_mysqlBind[arg].buffer_length = 0; \
0099         m_mysqlBind[arg].is_null = &dummyNull; \
0100         m_mysqlBind[arg].length = &str_length; }
0101 
0102 bool MysqlPreparedStatement::bindValue(KDbField *field, const QVariant& value, int arg)
0103 {
0104     if (value.isNull()) {
0105         // no value to bind or the value is null: bind NULL
0106         BIND_NULL;
0107         return true;
0108     }
0109 
0110     if (field->isTextType()) {
0111 //! @todo optimize
0112         m_stringBuffer[ 1024 ]; ? ? ?
0113         char *str = qstrncpy(m_stringBuffer, (const char*)value.toString().toUtf8(), 1024);
0114         m_mysqlBind[arg].buffer_type = MYSQL_TYPE_STRING;
0115         m_mysqlBind[arg].buffer = m_stringBuffer;
0116         m_mysqlBind[arg].is_null = (my_bool*)0;
0117         m_mysqlBind[arg].buffer_length = 1024; //?
0118         m_mysqlBind[arg].length = &str_length;
0119         return true;
0120     }
0121 
0122     switch (field->type()) {
0123     case KDbField::Byte:
0124     case KDbField::ShortInteger:
0125     case KDbField::Integer: {
0126         //! @todo what about unsigned > INT_MAX ?
0127         bool ok;
0128         const int intValue = value.toInt(&ok);
0129         if (ok) {
0130             if (field->type() == KDbField::Byte)
0131                 m_mysqlBind[arg].buffer_type = MYSQL_TYPE_TINY;
0132             else if (field->type() == KDbField::ShortInteger)
0133                 m_mysqlBind[arg].buffer_type = MYSQL_TYPE_SHORT;
0134             else if (field->type() == KDbField::Integer)
0135                 m_mysqlBind[arg].buffer_type = MYSQL_TYPE_LONG;
0136 
0137             m_mysqlBind[arg].is_null = (my_bool*)0;
0138             m_mysqlBind[arg].length = 0;
0139 
0140             res = sqlite3_bind_int(prepared_st_handle, arg, intValue);
0141             if (SQLITE_OK != res) {
0142                 //! @todo msg?
0143                 return false;
0144             }
0145         } else
0146             BIND_NULL;
0147         break;
0148     }
0149     case KDbField::Float:
0150     case KDbField::Double:
0151         res = sqlite3_bind_double(prepared_st_handle, arg, value.toDouble());
0152         if (SQLITE_OK != res) {
0153             //! @todo msg?
0154             return false;
0155         }
0156         break;
0157     case KDbField::BigInteger: {
0158         //! @todo what about unsigned > LLONG_MAX ?
0159         bool ok;
0160         qint64 int64Value = value.toLongLong(&ok);
0161         if (ok) {
0162             res = sqlite3_bind_int64(prepared_st_handle, arg, value);
0163             if (SQLITE_OK != res) {
0164                 //! @todo msg?
0165                 return false;
0166             }
0167         } else {
0168             res = sqlite3_bind_null(prepared_st_handle, arg);
0169             if (SQLITE_OK != res) {
0170                 //! @todo msg?
0171                 return false;
0172             }
0173         }
0174         break;
0175     }
0176     case KDbField::Boolean:
0177         res = sqlite3_bind_text(prepared_st_handle, arg,
0178                                 QString::number(value.toBool() ? 1 : 0).toLatin1(),
0179                                 1, SQLITE_TRANSIENT /*??*/);
0180         if (SQLITE_OK != res) {
0181             //! @todo msg?
0182             return false;
0183         }
0184         break;
0185     case KDbField::Time:
0186         res = sqlite3_bind_text(prepared_st_handle, arg,
0187                                 value.toTime().toString(Qt::ISODate).toLatin1(),
0188                                 sizeof("HH:MM:SS"), SQLITE_TRANSIENT /*??*/);
0189         if (SQLITE_OK != res) {
0190             //! @todo msg?
0191             return false;
0192         }
0193         break;
0194     case KDbField::Date:
0195         res = sqlite3_bind_text(prepared_st_handle, arg,
0196                                 value.toDate().toString(Qt::ISODate).toLatin1(),
0197                                 sizeof("YYYY-MM-DD"), SQLITE_TRANSIENT /*??*/);
0198         if (SQLITE_OK != res) {
0199             //! @todo msg?
0200             return false;
0201         }
0202         break;
0203     case KDbField::DateTime:
0204         res = sqlite3_bind_text(prepared_st_handle, arg,
0205                                 value.toDateTime().toString(Qt::ISODate).toLatin1(),
0206                                 sizeof("YYYY-MM-DDTHH:MM:SS"), SQLITE_TRANSIENT /*??*/);
0207         if (SQLITE_OK != res) {
0208             //! @todo msg?
0209             return false;
0210         }
0211         break;
0212     case KDbField::BLOB: {
0213         const QByteArray byteArray(value.toByteArray());
0214         res = sqlite3_bind_blob(prepared_st_handle, arg,
0215                                 (const char*)byteArray, byteArray.size(), SQLITE_TRANSIENT /*??*/);
0216         if (SQLITE_OK != res) {
0217             //! @todo msg?
0218             return false;
0219         }
0220         break;
0221     }
0222     default:
0223         mysqlWarning() << "unsupported field type:"
0224             << field->type() << "- NULL value bound to column #" << arg;
0225         res = sqlite3_bind_null(prepared_st_handle, arg);
0226         if (SQLITE_OK != res) {
0227             //! @todo msg?
0228             return false;
0229         }
0230     } //switch
0231     return true;
0232 }
0233 #endif
0234 
0235 QSharedPointer<KDbSqlResult> MysqlPreparedStatement::execute(KDbPreparedStatement::Type type,
0236                                 const KDbField::List &selectFieldList,
0237                                 KDbFieldList *insertFieldList,
0238                                 const KDbPreparedStatementParameters &parameters)
0239 {
0240     Q_UNUSED(selectFieldList);
0241     QSharedPointer<KDbSqlResult> result;
0242 #ifdef KDB_USE_MYSQL_STMT
0243     if (!m_statement || m_realParamCount <= 0)
0244         return false;
0245     if (mysql_stmt_errno(m_statement) == CR_SERVER_LOST) {
0246         //sanity: connection lost: reconnect
0247 //! @todo KDbConnection should be reconnected as well!
0248         done();
0249         if (!init()) {
0250             done();
0251             return false;
0252         }
0253     }
0254 
0255     if (m_resetRequired) {
0256         mysql_stmt_reset(m_statement);
0257         res = sqlite3_reset(prepared_st_handle);
0258         if (SQLITE_OK != res) {
0259             //! @todo msg?
0260             return false;
0261         }
0262         m_resetRequired = false;
0263     }
0264 
0265     int par = 0;
0266     bool dummyNull = true;
0267     unsigned long str_length;
0268 
0269     //for INSERT, we're iterating over inserting values
0270     //for SELECT, we're iterating over WHERE conditions
0271     KDbField::List *fieldList = 0;
0272     if (m_type == SelectStatement)
0273         fieldList = m_whereFields;
0274     else if (m_type == InsertStatement)
0275         fieldList = m_fields->fields();
0276     else
0277         Q_ASSERT(0); //impl. error
0278 
0279     KDbField::ListIterator itFields(fieldList->constBegin());
0280     for (QList<QVariant>::ConstIterator it(parameters.constBegin());
0281             itFields != fieldList->constEnd() && arg < m_realParamCount; ++it, ++itFields, par++) {
0282         if (!bindValue(*itFields, it == parameters.constEnd() ? QVariant() : *it, par))
0283             return false;
0284     }//for
0285 
0286     //real execution
0287     res = sqlite3_step(prepared_st_handle);
0288     m_resetRequired = true;
0289     if (m_type == InsertStatement && res == SQLITE_DONE) {
0290         return true;
0291     }
0292     if (m_type == SelectStatement) {
0293         //fetch result
0294 
0295 //! @todo
0296     }
0297 #else
0298     m_resetRequired = true;
0299     if (type == KDbPreparedStatement::InsertStatement) {
0300         const int missingValues = insertFieldList->fieldCount() - parameters.count();
0301         KDbPreparedStatementParameters myParameters(parameters);
0302         if (missingValues > 0) {
0303     //! @todo can be more efficient
0304             for (int i = 0; i < missingValues; i++) {
0305                 myParameters.append(QVariant());
0306             }
0307         }
0308         result = connection->insertRecord(insertFieldList, myParameters);
0309     }
0310 //! @todo support select
0311 #endif // !KDB_USE_MYSQL_STMT
0312     return result;
0313 }