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 ¶meters) 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 }