summaryrefslogtreecommitdiffstats
path: root/kexi/kexidb/drivers/mySQL/mysqlpreparedstatement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kexi/kexidb/drivers/mySQL/mysqlpreparedstatement.cpp')
-rw-r--r--kexi/kexidb/drivers/mySQL/mysqlpreparedstatement.cpp298
1 files changed, 298 insertions, 0 deletions
diff --git a/kexi/kexidb/drivers/mySQL/mysqlpreparedstatement.cpp b/kexi/kexidb/drivers/mySQL/mysqlpreparedstatement.cpp
new file mode 100644
index 000000000..2702626a1
--- /dev/null
+++ b/kexi/kexidb/drivers/mySQL/mysqlpreparedstatement.cpp
@@ -0,0 +1,298 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ 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 <kdebug.h>
+#include <errmsg.h>
+
+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<KexiDB::MySqlConnectionInternal&>(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 (QValueListConstIterator<QVariant> 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 = qstrncpy(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;
+ Q_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,
+ QString::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 QByteArray 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;
+}