kexi

mysqlpreparedstatement.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    This program is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this program; see the file COPYING.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "mysqlpreparedstatement.h"
00021 #include <kdebug.h>
00022 #include <errmsg.h>
00023 
00024 using namespace KexiDB;
00025 
00026 // For example prepared MySQL statement code see:
00027 // http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-execute.html
00028 
00029 MySqlPreparedStatement::MySqlPreparedStatement(StatementType type, ConnectionInternal& conn, 
00030     FieldList& fields)
00031  : KexiDB::PreparedStatement(type, conn, fields)
00032  , MySqlConnectionInternal(conn.connection)
00033 #ifdef KEXI_USE_MYSQL_STMT
00034  , m_statement(0)
00035  , m_mysqlBind(0)
00036 #endif
00037  , m_resetRequired(false)
00038 {
00039 //  KexiDBDrvDbg << "MySqlPreparedStatement: Construction" << endl;
00040 
00041     mysql_owned = false;
00042     mysql = dynamic_cast<KexiDB::MySqlConnectionInternal&>(conn).mysql; //copy
00043     m_tempStatementString = generateStatementString();
00044 
00045     if (!init())
00046         done();
00047 }
00048 
00049 bool MySqlPreparedStatement::init()
00050 {
00051     if (m_tempStatementString.isEmpty())
00052         return false;
00053 #ifdef KEXI_USE_MYSQL_STMT
00054     m_statement = mysql_stmt_init(mysql);
00055     if (!m_statement) {
00057         return false;
00058     }
00059     res = mysql_stmt_prepare(m_statement, 
00060         (const char*)m_tempStatementString, m_tempStatementString.length());
00061     if (0 != res) {
00063         return false;
00064     }
00065 
00066     m_realParamCount = mysql_stmt_param_count(m_statement);
00067     if (m_realParamCount<=0) {
00069         return false;
00070     }
00071     m_mysqlBind = new MYSQL_BIND[ m_realParamCount ];
00072     memset(m_mysqlBind, 0, sizeof(MYSQL_BIND)*m_realParamCount); //safe?
00073 #endif
00074     return true;
00075 }
00076 
00077 
00078 MySqlPreparedStatement::~MySqlPreparedStatement()
00079 {
00080     done();
00081 }
00082 
00083 void MySqlPreparedStatement::done()
00084 {
00085 #ifdef KEXI_USE_MYSQL_STMT
00086     if (m_statement) {
00088         mysql_stmt_close(m_statement);
00089         m_statement = 0;
00090     }
00091     delete m_mysqlBind;
00092     m_mysqlBind = 0;
00093 #endif
00094 }
00095 
00096 #ifdef KEXI_USE_MYSQL_STMT
00097 #define BIND_NULL { \
00098     m_mysqlBind[arg].buffer_type = MYSQL_TYPE_NULL; \
00099     m_mysqlBind[arg].buffer = 0; \
00100     m_mysqlBind[arg].buffer_length = 0; \
00101     m_mysqlBind[arg].is_null = &dummyNull; \
00102     m_mysqlBind[arg].length = &str_length; }
00103 #endif
00104 
00105 bool MySqlPreparedStatement::execute()
00106 {
00107 #ifdef KEXI_USE_MYSQL_STMT
00108     if (!m_statement || m_realParamCount<=0)
00109         return false;
00110     if ( mysql_stmt_errno(m_statement) == CR_SERVER_LOST ) {
00111         //sanity: connection lost: reconnect
00113         done();
00114         if (!init()) {
00115             done();
00116             return false;
00117         }
00118     }
00119 
00120     if (m_resetRequired) {
00121         mysql_stmt_reset(m_statement);
00122         res = sqlite3_reset(prepared_st_handle);
00123         if (SQLITE_OK != res) {
00125             return false;
00126         }
00127         m_resetRequired = false;
00128     }
00129 
00130     int arg = 0;
00131     bool dummyNull = true;
00132     unsigned long str_length;
00133     KexiDB::Field *field;
00134 
00135     Field::List _dummy;
00136     Field::ListIterator itFields(_dummy);
00137     //for INSERT, we're iterating over inserting values
00138     //for SELECT, we're iterating over WHERE conditions
00139     if (m_type == SelectStatement)
00140         itFields = *m_whereFields;
00141     else if (m_type == InsertStatement)
00142         itFields = m_fields->fieldsIterator();
00143     else
00144         assert(0); //impl. error
00145 
00146     for (QValueListConstIterator<QVariant> it = m_args.constBegin(); 
00147         (field = itFields.current()) && arg < m_realParamCount; ++it, ++itFields, arg++)
00148     {
00149         if (it==m_args.constEnd() || (*it).isNull()) {//no value to bind or the value is null: bind NULL
00150             BIND_NULL;
00151             continue;
00152         }
00153         if (field->isTextType()) {
00155 m_stringBuffer[ 1024 ]; ???
00156             char *str = qstrncpy(m_stringBuffer, (const char*)(*it).toString().utf8(), 1024);
00157             m_mysqlBind[arg].buffer_type = MYSQL_TYPE_STRING;
00158             m_mysqlBind[arg].buffer = m_stringBuffer;
00159             m_mysqlBind[arg].is_null = (my_bool*)0;
00160             m_mysqlBind[arg].buffer_length = 1024; //?
00161             m_mysqlBind[arg].length = &str_length;
00162         }
00163         else switch (field->type()) {
00164         case KexiDB::Field::Byte:
00165         case KexiDB::Field::ShortInteger:
00166         case KexiDB::Field::Integer:
00167         {
00169             bool ok;
00170             const int value = (*it).toInt(&ok);
00171             if (ok) {
00172                 if (field->type()==KexiDB::Field::Byte)
00173                     m_mysqlBind[arg].buffer_type = MYSQL_TYPE_TINY;
00174                 else if (field->type()==KexiDB::Field::ShortInteger)
00175                     m_mysqlBind[arg].buffer_type = MYSQL_TYPE_SHORT;
00176                 else if (field->type()==KexiDB::Field::Integer)
00177                     m_mysqlBind[arg].buffer_type = MYSQL_TYPE_LONG;
00178 
00179                 m_mysqlBind[arg].is_null = (my_bool*)0;
00180                 m_mysqlBind[arg].length = 0;
00181 
00182                 res = sqlite3_bind_int(prepared_st_handle, arg, value);
00183                 if (SQLITE_OK != res) {
00185                     return false;
00186                 }
00187             }
00188             else
00189                 BIND_NULL;
00190             break;
00191         }
00192         case KexiDB::Field::Float:
00193         case KexiDB::Field::Double:
00194             res = sqlite3_bind_double(prepared_st_handle, arg, (*it).toDouble());
00195             if (SQLITE_OK != res) {
00197                 return false;
00198             }
00199             break;
00200         case KexiDB::Field::BigInteger:
00201         {
00203             bool ok;
00204             Q_LLONG value = (*it).toLongLong(&ok);
00205             if (ok) {
00206                 res = sqlite3_bind_int64(prepared_st_handle, arg, value);
00207                 if (SQLITE_OK != res) {
00209                     return false;
00210                 }
00211             }
00212             else {
00213                 res = sqlite3_bind_null(prepared_st_handle, arg);
00214                 if (SQLITE_OK != res) {
00216                     return false;
00217                 }
00218             }
00219             break;
00220         }
00221         case KexiDB::Field::Boolean:
00222             res = sqlite3_bind_text(prepared_st_handle, arg, 
00223                 QString::number((*it).toBool() ? 1 : 0).latin1(), 
00224                 1, SQLITE_TRANSIENT /*??*/);
00225             if (SQLITE_OK != res) {
00227                 return false;
00228             }
00229             break;
00230         case KexiDB::Field::Time:
00231             res = sqlite3_bind_text(prepared_st_handle, arg, 
00232                 (*it).toTime().toString(Qt::ISODate).latin1(), 
00233                 sizeof("HH:MM:SS"), SQLITE_TRANSIENT /*??*/);
00234             if (SQLITE_OK != res) {
00236                 return false;
00237             }
00238             break;
00239         case KexiDB::Field::Date:
00240             res = sqlite3_bind_text(prepared_st_handle, arg, 
00241                 (*it).toDate().toString(Qt::ISODate).latin1(), 
00242                 sizeof("YYYY-MM-DD"), SQLITE_TRANSIENT /*??*/);
00243             if (SQLITE_OK != res) {
00245                 return false;
00246             }
00247             break;
00248         case KexiDB::Field::DateTime:
00249             res = sqlite3_bind_text(prepared_st_handle, arg, 
00250                 (*it).toDateTime().toString(Qt::ISODate).latin1(), 
00251                 sizeof("YYYY-MM-DDTHH:MM:SS"), SQLITE_TRANSIENT /*??*/);
00252             if (SQLITE_OK != res) {
00254                 return false;
00255             }
00256             break;
00257         case KexiDB::Field::BLOB:
00258         {
00259             const QByteArray byteArray((*it).toByteArray());
00260             res = sqlite3_bind_blob(prepared_st_handle, arg, 
00261                 (const char*)byteArray, byteArray.size(), SQLITE_TRANSIENT /*??*/);
00262             if (SQLITE_OK != res) {
00264                 return false;
00265             }
00266             break;
00267         }
00268         default:
00269             KexiDBWarn << "PreparedStatement::execute(): unsupported field type: " 
00270                 << field->type() << " - NULL value bound to column #" << arg << endl;
00271             res = sqlite3_bind_null(prepared_st_handle, arg);
00272             if (SQLITE_OK != res) {
00274                 return false;
00275             }
00276         } //switch
00277     }
00278 
00279     //real execution
00280     res = sqlite3_step(prepared_st_handle);
00281     m_resetRequired = true;
00282     if (m_type == InsertStatement && res == SQLITE_DONE) {
00283         return true;
00284     }
00285     if (m_type == SelectStatement) {
00286         //fetch result
00287 
00288         //todo
00289     }
00290 #else 
00291     m_resetRequired = true;
00292     if (connection->insertRecord(*m_fields, m_args)) {
00293         return true;
00294     }
00295     
00296 #endif //KEXI_USE_MYSQL_STMT
00297     return false;
00298 }
KDE Home | KDE Accessibility Home | Description of Access Keys