kexi

mysqlpreparedstatement.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this program; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "mysqlpreparedstatement.h"
21#include <kdebug.h>
22#include <errmsg.h>
23
24using namespace KexiDB;
25
26// For example prepared MySQL statement code see:
27// http://dev.mysql.com/doc/refman/4.1/en/mysql-stmt-execute.html
28
29MySqlPreparedStatement::MySqlPreparedStatement(StatementType type, ConnectionInternal& conn,
30 FieldList& fields)
31 : KexiDB::PreparedStatement(type, conn, fields)
32 , MySqlConnectionInternal(conn.connection)
33#ifdef KEXI_USE_MYSQL_STMT
34 , m_statement(0)
35 , m_mysqlBind(0)
36#endif
37 , m_resetRequired(false)
38{
39// KexiDBDrvDbg << "MySqlPreparedStatement: Construction" << endl;
40
41 mysql_owned = false;
42 mysql = dynamic_cast<KexiDB::MySqlConnectionInternal&>(conn).mysql; //copy
43 m_tempStatementString = generateStatementString();
44
45 if (!init())
46 done();
47}
48
49bool MySqlPreparedStatement::init()
50{
51 if (m_tempStatementString.isEmpty())
52 return false;
53#ifdef KEXI_USE_MYSQL_STMT
54 m_statement = mysql_stmt_init(mysql);
55 if (!m_statement) {
57 return false;
58 }
59 res = mysql_stmt_prepare(m_statement,
60 (const char*)m_tempStatementString, m_tempStatementString.length());
61 if (0 != res) {
63 return false;
64 }
65
66 m_realParamCount = mysql_stmt_param_count(m_statement);
67 if (m_realParamCount<=0) {
69 return false;
70 }
71 m_mysqlBind = new MYSQL_BIND[ m_realParamCount ];
72 memset(m_mysqlBind, 0, sizeof(MYSQL_BIND)*m_realParamCount); //safe?
73#endif
74 return true;
75}
76
77
78MySqlPreparedStatement::~MySqlPreparedStatement()
79{
80 done();
81}
82
83void MySqlPreparedStatement::done()
84{
85#ifdef KEXI_USE_MYSQL_STMT
86 if (m_statement) {
88 mysql_stmt_close(m_statement);
89 m_statement = 0;
90 }
91 delete m_mysqlBind;
92 m_mysqlBind = 0;
93#endif
94}
95
96#ifdef KEXI_USE_MYSQL_STMT
97#define BIND_NULL { \
98 m_mysqlBind[arg].buffer_type = MYSQL_TYPE_NULL; \
99 m_mysqlBind[arg].buffer = 0; \
100 m_mysqlBind[arg].buffer_length = 0; \
101 m_mysqlBind[arg].is_null = &dummyNull; \
102 m_mysqlBind[arg].length = &str_length; }
103#endif
104
106{
107#ifdef KEXI_USE_MYSQL_STMT
108 if (!m_statement || m_realParamCount<=0)
109 return false;
110 if ( mysql_stmt_errno(m_statement) == CR_SERVER_LOST ) {
111 //sanity: connection lost: reconnect
113 done();
114 if (!init()) {
115 done();
116 return false;
117 }
118 }
119
120 if (m_resetRequired) {
121 mysql_stmt_reset(m_statement);
122 res = sqlite3_reset(prepared_st_handle);
123 if (SQLITE_OK != res) {
125 return false;
126 }
127 m_resetRequired = false;
128 }
129
130 int arg = 0;
131 bool dummyNull = true;
132 unsigned long str_length;
133 KexiDB::Field *field;
134
136 Field::ListIterator itFields(_dummy);
137 //for INSERT, we're iterating over inserting values
138 //for SELECT, we're iterating over WHERE conditions
139 if (m_type == SelectStatement)
140 itFields = *m_whereFields;
141 else if (m_type == InsertStatement)
142 itFields = m_fields->fieldsIterator();
143 else
144 assert(0); //impl. error
145
146 for (TQValueListConstIterator<TQVariant> it = m_args.constBegin();
147 (field = itFields.current()) && arg < m_realParamCount; ++it, ++itFields, arg++)
148 {
149 if (it==m_args.constEnd() || (*it).isNull()) {//no value to bind or the value is null: bind NULL
150 BIND_NULL;
151 continue;
152 }
153 if (field->isTextType()) {
155m_stringBuffer[ 1024 ]; ???
156 char *str = tqstrncpy(m_stringBuffer, (const char*)(*it).toString().utf8(), 1024);
157 m_mysqlBind[arg].buffer_type = MYSQL_TYPE_STRING;
158 m_mysqlBind[arg].buffer = m_stringBuffer;
159 m_mysqlBind[arg].is_null = (my_bool*)0;
160 m_mysqlBind[arg].buffer_length = 1024; //?
161 m_mysqlBind[arg].length = &str_length;
162 }
163 else switch (field->type()) {
167 {
169 bool ok;
170 const int value = (*it).toInt(&ok);
171 if (ok) {
172 if (field->type()==KexiDB::Field::Byte)
173 m_mysqlBind[arg].buffer_type = MYSQL_TYPE_TINY;
174 else if (field->type()==KexiDB::Field::ShortInteger)
175 m_mysqlBind[arg].buffer_type = MYSQL_TYPE_SHORT;
176 else if (field->type()==KexiDB::Field::Integer)
177 m_mysqlBind[arg].buffer_type = MYSQL_TYPE_LONG;
178
179 m_mysqlBind[arg].is_null = (my_bool*)0;
180 m_mysqlBind[arg].length = 0;
181
182 res = sqlite3_bind_int(prepared_st_handle, arg, value);
183 if (SQLITE_OK != res) {
185 return false;
186 }
187 }
188 else
189 BIND_NULL;
190 break;
191 }
194 res = sqlite3_bind_double(prepared_st_handle, arg, (*it).toDouble());
195 if (SQLITE_OK != res) {
197 return false;
198 }
199 break;
201 {
203 bool ok;
204 TQ_LLONG value = (*it).toLongLong(&ok);
205 if (ok) {
206 res = sqlite3_bind_int64(prepared_st_handle, arg, value);
207 if (SQLITE_OK != res) {
209 return false;
210 }
211 }
212 else {
213 res = sqlite3_bind_null(prepared_st_handle, arg);
214 if (SQLITE_OK != res) {
216 return false;
217 }
218 }
219 break;
220 }
222 res = sqlite3_bind_text(prepared_st_handle, arg,
223 TQString::number((*it).toBool() ? 1 : 0).latin1(),
224 1, SQLITE_TRANSIENT /*??*/);
225 if (SQLITE_OK != res) {
227 return false;
228 }
229 break;
230 case KexiDB::Field::Time:
231 res = sqlite3_bind_text(prepared_st_handle, arg,
232 (*it).toTime().toString(TQt::ISODate).latin1(),
233 sizeof("HH:MM:SS"), SQLITE_TRANSIENT /*??*/);
234 if (SQLITE_OK != res) {
236 return false;
237 }
238 break;
239 case KexiDB::Field::Date:
240 res = sqlite3_bind_text(prepared_st_handle, arg,
241 (*it).toDate().toString(TQt::ISODate).latin1(),
242 sizeof("YYYY-MM-DD"), SQLITE_TRANSIENT /*??*/);
243 if (SQLITE_OK != res) {
245 return false;
246 }
247 break;
248 case KexiDB::Field::DateTime:
249 res = sqlite3_bind_text(prepared_st_handle, arg,
250 (*it).toDateTime().toString(TQt::ISODate).latin1(),
251 sizeof("YYYY-MM-DDTHH:MM:SS"), SQLITE_TRANSIENT /*??*/);
252 if (SQLITE_OK != res) {
254 return false;
255 }
256 break;
258 {
259 const TQByteArray byteArray((*it).toByteArray());
260 res = sqlite3_bind_blob(prepared_st_handle, arg,
261 (const char*)byteArray, byteArray.size(), SQLITE_TRANSIENT /*??*/);
262 if (SQLITE_OK != res) {
264 return false;
265 }
266 break;
267 }
268 default:
269 KexiDBWarn << "PreparedStatement::execute(): unsupported field type: "
270 << field->type() << " - NULL value bound to column #" << arg << endl;
271 res = sqlite3_bind_null(prepared_st_handle, arg);
272 if (SQLITE_OK != res) {
274 return false;
275 }
276 } //switch
277 }
278
279 //real execution
280 res = sqlite3_step(prepared_st_handle);
281 m_resetRequired = true;
282 if (m_type == InsertStatement && res == SQLITE_DONE) {
283 return true;
284 }
285 if (m_type == SelectStatement) {
286 //fetch result
287
288 //todo
289 }
290#else
291 m_resetRequired = true;
292 if (connection->insertRecord(*m_fields, m_args)) {
293 return true;
294 }
295
296#endif //KEXI_USE_MYSQL_STMT
297 return false;
298}
Meta-data for a field.
Definition field.h:73
@ ShortInteger
Definition field.h:86
TQPtrList< Field > List
list of fields
Definition field.h:75
TQPtrListIterator< Field > ListIterator
iterator for list of fields
Definition field.h:77
Prepared database command for optimizing sequences of multiple database actions.
@ InsertStatement
INSERT statement will be prepared end executed.
@ SelectStatement
SELECT statement will be prepared end executed.
High-level database connectivity library with database backend drivers.
static bool _dummy
Definition driver_p.cpp:34