/* This file is part of the KDE project Copyright (C) 2006 Jaroslaw Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "mysqlpreparedstatement.h" #include #include using namespace KexiDB; // For example prepared MySQL statement code see: // http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-execute.html MySqlPreparedStatement::MySqlPreparedStatement(StatementType type, ConnectionInternal& conn, FieldList& fields) : KexiDB::PreparedStatement(type, conn, fields) , MySqlConnectionInternal(conn.connection) #ifdef KEXI_USE_MYSQL_STMT , m_statement(0) , m_mysqlBind(0) #endif , m_resetRequired(false) { // KexiDBDrvDbg << "MySqlPreparedStatement: Construction" << endl; mysql_owned = false; mysql = dynamic_cast(conn).mysql; //copy m_tempStatementString = generateStatementString(); if (!init()) done(); } bool MySqlPreparedStatement::init() { if (m_tempStatementString.isEmpty()) return false; #ifdef KEXI_USE_MYSQL_STMT m_statement = mysql_stmt_init(mysql); if (!m_statement) { //! @todo err 'out of memory' return false; } res = mysql_stmt_prepare(m_statement, (const char*)m_tempStatementString, m_tempStatementString.length()); if (0 != res) { //! @todo use mysql_stmt_error(stmt); to show error return false; } m_realParamCount = mysql_stmt_param_count(m_statement); if (m_realParamCount<=0) { //! @todo err return false; } m_mysqlBind = new MYSQL_BIND[ m_realParamCount ]; memset(m_mysqlBind, 0, sizeof(MYSQL_BIND)*m_realParamCount); //safe? #endif return true; } MySqlPreparedStatement::~MySqlPreparedStatement() { done(); } void MySqlPreparedStatement::done() { #ifdef KEXI_USE_MYSQL_STMT if (m_statement) { //! @todo handle errors of mysql_stmt_close()? mysql_stmt_close(m_statement); m_statement = 0; } delete m_mysqlBind; m_mysqlBind = 0; #endif } #ifdef KEXI_USE_MYSQL_STMT #define BIND_NULL { \ m_mysqlBind[arg].buffer_type = MYSQL_TYPE_NULL; \ m_mysqlBind[arg].buffer = 0; \ m_mysqlBind[arg].buffer_length = 0; \ m_mysqlBind[arg].is_null = &dummyNull; \ m_mysqlBind[arg].length = &str_length; } #endif bool MySqlPreparedStatement::execute() { #ifdef KEXI_USE_MYSQL_STMT if (!m_statement || m_realParamCount<=0) return false; if ( mysql_stmt_errno(m_statement) == CR_SERVER_LOST ) { //sanity: connection lost: reconnect //! @todo KexiDB::Connection should be reconnected as well! done(); if (!init()) { done(); return false; } } if (m_resetRequired) { mysql_stmt_reset(m_statement); res = sqlite3_reset(prepared_st_handle); if (SQLITE_OK != res) { //! @todo msg? return false; } m_resetRequired = false; } int arg = 0; bool dummyNull = true; unsigned long str_length; KexiDB::Field *field; Field::List _dummy; Field::ListIterator itFields(_dummy); //for INSERT, we're iterating over inserting values //for SELECT, we're iterating over WHERE conditions if (m_type == SelectStatement) itFields = *m_whereFields; else if (m_type == InsertStatement) itFields = m_fields->fieldsIterator(); else assert(0); //impl. error for (TQValueListConstIterator it = m_args.constBegin(); (field = itFields.current()) && arg < m_realParamCount; ++it, ++itFields, arg++) { if (it==m_args.constEnd() || (*it).isNull()) {//no value to bind or the value is null: bind NULL BIND_NULL; continue; } if (field->isTextType()) { //! @todo optimize m_stringBuffer[ 1024 ]; ??? char *str = tqstrncpy(m_stringBuffer, (const char*)(*it).toString().utf8(), 1024); m_mysqlBind[arg].buffer_type = MYSQL_TYPE_STRING; m_mysqlBind[arg].buffer = m_stringBuffer; m_mysqlBind[arg].is_null = (my_bool*)0; m_mysqlBind[arg].buffer_length = 1024; //? m_mysqlBind[arg].length = &str_length; } else switch (field->type()) { case KexiDB::Field::Byte: case KexiDB::Field::ShortInteger: case KexiDB::Field::Integer: { //! @todo what about unsigned > INT_MAX ? bool ok; const int value = (*it).toInt(&ok); if (ok) { if (field->type()==KexiDB::Field::Byte) m_mysqlBind[arg].buffer_type = MYSQL_TYPE_TINY; else if (field->type()==KexiDB::Field::ShortInteger) m_mysqlBind[arg].buffer_type = MYSQL_TYPE_SHORT; else if (field->type()==KexiDB::Field::Integer) m_mysqlBind[arg].buffer_type = MYSQL_TYPE_LONG; m_mysqlBind[arg].is_null = (my_bool*)0; m_mysqlBind[arg].length = 0; res = sqlite3_bind_int(prepared_st_handle, arg, value); if (SQLITE_OK != res) { //! @todo msg? return false; } } else BIND_NULL; break; } case KexiDB::Field::Float: case KexiDB::Field::Double: res = sqlite3_bind_double(prepared_st_handle, arg, (*it).toDouble()); if (SQLITE_OK != res) { //! @todo msg? return false; } break; case KexiDB::Field::BigInteger: { //! @todo what about unsigned > LLONG_MAX ? bool ok; TQ_LLONG value = (*it).toLongLong(&ok); if (ok) { res = sqlite3_bind_int64(prepared_st_handle, arg, value); if (SQLITE_OK != res) { //! @todo msg? return false; } } else { res = sqlite3_bind_null(prepared_st_handle, arg); if (SQLITE_OK != res) { //! @todo msg? return false; } } break; } case KexiDB::Field::Boolean: res = sqlite3_bind_text(prepared_st_handle, arg, TQString::number((*it).toBool() ? 1 : 0).latin1(), 1, SQLITE_TRANSIENT /*??*/); if (SQLITE_OK != res) { //! @todo msg? return false; } break; case KexiDB::Field::Time: res = sqlite3_bind_text(prepared_st_handle, arg, (*it).toTime().toString(Qt::ISODate).latin1(), sizeof("HH:MM:SS"), SQLITE_TRANSIENT /*??*/); if (SQLITE_OK != res) { //! @todo msg? return false; } break; case KexiDB::Field::Date: res = sqlite3_bind_text(prepared_st_handle, arg, (*it).toDate().toString(Qt::ISODate).latin1(), sizeof("YYYY-MM-DD"), SQLITE_TRANSIENT /*??*/); if (SQLITE_OK != res) { //! @todo msg? return false; } break; case KexiDB::Field::DateTime: res = sqlite3_bind_text(prepared_st_handle, arg, (*it).toDateTime().toString(Qt::ISODate).latin1(), sizeof("YYYY-MM-DDTHH:MM:SS"), SQLITE_TRANSIENT /*??*/); if (SQLITE_OK != res) { //! @todo msg? return false; } break; case KexiDB::Field::BLOB: { const TQByteArray byteArray((*it).toByteArray()); res = sqlite3_bind_blob(prepared_st_handle, arg, (const char*)byteArray, byteArray.size(), SQLITE_TRANSIENT /*??*/); if (SQLITE_OK != res) { //! @todo msg? return false; } break; } default: KexiDBWarn << "PreparedStatement::execute(): unsupported field type: " << field->type() << " - NULL value bound to column #" << arg << endl; res = sqlite3_bind_null(prepared_st_handle, arg); if (SQLITE_OK != res) { //! @todo msg? return false; } } //switch } //real execution res = sqlite3_step(prepared_st_handle); m_resetRequired = true; if (m_type == InsertStatement && res == SQLITE_DONE) { return true; } if (m_type == SelectStatement) { //fetch result //todo } #else m_resetRequired = true; if (connection->insertRecord(*m_fields, m_args)) { return true; } #endif //KEXI_USE_MYSQL_STMT return false; }