root/src/common/sql.c @ 5

Revision 1, 21.7 kB (checked in by jinshiro, 17 years ago)
Line 
1// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
2// For more information, see LICENCE in the main folder
3
4#include "../common/cbasetypes.h"
5#include "../common/malloc.h"
6#include "../common/showmsg.h"
7#include "../common/strlib.h"
8#include "sql.h"
9
10#ifdef WIN32
11#include <winsock2.h>
12#endif
13#include <mysql.h>
14#include <string.h>// strlen/strnlen/memcpy/memset
15#include <stdlib.h>// strtoul
16
17
18
19/// Sql handle
20struct Sql
21{
22        StringBuf buf;
23        MYSQL handle;
24        MYSQL_RES* result;
25        MYSQL_ROW row;
26        unsigned long* lengths;
27};
28
29
30
31// Column length receiver.
32// Takes care of the possible size missmatch between uint32 and unsigned long.
33struct s_column_length
34{
35        uint32* out_length;
36        unsigned long length;
37};
38typedef struct s_column_length s_column_length;
39
40
41
42/// Sql statement
43struct SqlStmt
44{
45        StringBuf buf;
46        MYSQL_STMT* stmt;
47        MYSQL_BIND* params;
48        MYSQL_BIND* columns;
49        s_column_length* column_lengths;
50        size_t max_params;
51        size_t max_columns;
52        bool bind_params;
53        bool bind_columns;
54};
55
56
57
58///////////////////////////////////////////////////////////////////////////////
59// Sql Handle
60///////////////////////////////////////////////////////////////////////////////
61
62
63
64/// Allocates and initializes a new Sql handle.
65Sql* Sql_Malloc(void)
66{
67        Sql* self;
68        CREATE(self, Sql, 1);
69        mysql_init(&self->handle);
70        StringBuf_Init(&self->buf);
71        return self;
72}
73
74
75
76/// Establishes a connection.
77int Sql_Connect(Sql* self, const char* user, const char* passwd, const char* host, uint16 port, const char* db)
78{
79        if( self == NULL )
80                return SQL_ERROR;
81
82        StringBuf_Clear(&self->buf);
83        if( !mysql_real_connect(&self->handle, host, user, passwd, db, (unsigned int)port, NULL/*unix_socket*/, 0/*clientflag*/) )
84        {
85                ShowSQL("%s\n", mysql_error(&self->handle));
86                return SQL_ERROR;
87        }
88        return SQL_SUCCESS;
89}
90
91
92
93/// Retrieves the timeout of the connection.
94int Sql_GetTimeout(Sql* self, uint32* out_timeout)
95{
96        if( self && out_timeout && SQL_SUCCESS == Sql_Query(self, "SHOW VARIABLES LIKE 'wait_timeout'") )
97        {
98                char* data;
99                size_t len;
100                if( SQL_SUCCESS == Sql_NextRow(self) &&
101                        SQL_SUCCESS == Sql_GetData(self, 1, &data, &len) )
102                {
103                        *out_timeout = (uint32)strtoul(data, NULL, 10);
104                        Sql_FreeResult(self);
105                        return SQL_SUCCESS;
106                }
107                Sql_FreeResult(self);
108        }
109        return SQL_ERROR;
110}
111
112
113
114/// Retrieves the name of the columns of a table into out_buf, with the separator after each name.
115int Sql_GetColumnNames(Sql* self, const char* table, char* out_buf, size_t buf_len, char sep)
116{
117        char* data;
118        size_t len;
119        size_t off = 0;
120
121        if( self == NULL || SQL_ERROR == Sql_Query(self, "EXPLAIN `%s`", table) )
122                return SQL_ERROR;
123
124        out_buf[off] = '\0';
125        while( SQL_SUCCESS == Sql_NextRow(self) && SQL_SUCCESS == Sql_GetData(self, 0, &data, &len) )
126        {
127                len = strnlen(data, len);
128                if( off + len + 2 > buf_len )
129                {
130                        ShowDebug("Sql_GetColumns: output buffer is too small\n");
131                        *out_buf = '\0';
132                        return SQL_ERROR;
133                }
134                memcpy(out_buf+off, data, len);
135                off += len;
136                out_buf[off++] = sep;
137        }
138        out_buf[off] = '\0';
139        Sql_FreeResult(self);
140        return SQL_SUCCESS;
141}
142
143
144
145/// Changes the encoding of the connection.
146int Sql_SetEncoding(Sql* self, const char* encoding)
147{
148        if( self && mysql_set_character_set(&self->handle, encoding) )
149                return SQL_SUCCESS;
150        return SQL_ERROR;
151}
152
153
154
155/// Pings the connection.
156int Sql_Ping(Sql* self)
157{
158        if( self && mysql_ping(&self->handle) == 0 )
159                return SQL_SUCCESS;
160        return SQL_ERROR;
161}
162
163
164
165/// Escapes a string.
166size_t Sql_EscapeString(Sql* self, char *out_to, const char *from)
167{
168        if( self )
169                return (size_t)mysql_real_escape_string(&self->handle, out_to, from, (unsigned long)strlen(from));
170        else
171                return (size_t)mysql_escape_string(out_to, from, (unsigned long)strlen(from));
172}
173
174
175
176/// Escapes a string.
177size_t Sql_EscapeStringLen(Sql* self, char *out_to, const char *from, size_t from_len)
178{
179        if( self )
180                return (size_t)mysql_real_escape_string(&self->handle, out_to, from, (unsigned long)from_len);
181        else
182                return (size_t)mysql_escape_string(out_to, from, (unsigned long)from_len);
183}
184
185
186
187/// Executes a query.
188int Sql_Query(Sql* self, const char* query, ...)
189{
190        int res;
191        va_list args;
192
193        va_start(args, query);
194        res = Sql_QueryV(self, query, args);
195        va_end(args);
196
197        return res;
198}
199
200
201
202/// Executes a query.
203int Sql_QueryV(Sql* self, const char* query, va_list args)
204{
205        if( self == NULL )
206                return SQL_ERROR;
207
208        Sql_FreeResult(self);
209        StringBuf_Clear(&self->buf);
210        StringBuf_Vprintf(&self->buf, query, args);
211        if( mysql_real_query(&self->handle, StringBuf_Value(&self->buf), (unsigned long)StringBuf_Length(&self->buf)) )
212        {
213                ShowSQL("DB error - %s\n", mysql_error(&self->handle));
214                return SQL_ERROR;
215        }
216        self->result = mysql_store_result(&self->handle);
217        if( mysql_errno(&self->handle) != 0 )
218        {
219                ShowSQL("DB error - %s\n", mysql_error(&self->handle));
220                return SQL_ERROR;
221        }
222        return SQL_SUCCESS;
223}
224
225
226
227/// Executes a query.
228int Sql_QueryStr(Sql* self, const char* query)
229{
230        if( self == NULL )
231                return SQL_ERROR;
232
233        Sql_FreeResult(self);
234        StringBuf_Clear(&self->buf);
235        StringBuf_AppendStr(&self->buf, query);
236        if( mysql_real_query(&self->handle, StringBuf_Value(&self->buf), (unsigned long)StringBuf_Length(&self->buf)) )
237        {
238                ShowSQL("DB error - %s\n", mysql_error(&self->handle));
239                return SQL_ERROR;
240        }
241        self->result = mysql_store_result(&self->handle);
242        if( mysql_errno(&self->handle) != 0 )
243        {
244                ShowSQL("DB error - %s\n", mysql_error(&self->handle));
245                return SQL_ERROR;
246        }
247        return SQL_SUCCESS;
248}
249
250
251
252/// Returns the number of the AUTO_INCREMENT column of the last INSERT/UPDATE query.
253uint64 Sql_LastInsertId(Sql* self)
254{
255        if( self )
256                return (uint64)mysql_insert_id(&self->handle);
257        else
258                return 0;
259}
260
261
262
263/// Returns the number of columns in each row of the result.
264uint32 Sql_NumColumns(Sql* self)
265{
266        if( self && self->result )
267                return (uint32)mysql_num_fields(self->result);
268        return 0;
269}
270
271
272
273/// Returns the number of rows in the result.
274uint64 Sql_NumRows(Sql* self)
275{
276        if( self && self->result )
277                return (uint64)mysql_num_rows(self->result);
278        return 0;
279}
280
281
282
283/// Fetches the next row.
284int Sql_NextRow(Sql* self)
285{
286        if( self && self->result )
287        {
288                self->row = mysql_fetch_row(self->result);
289                if( self->row )
290                {
291                        self->lengths = mysql_fetch_lengths(self->result);
292                        return SQL_SUCCESS;
293                }
294                self->lengths = NULL;
295                if( mysql_errno(&self->handle) == 0 )
296                        return SQL_NO_DATA;
297        }
298        return SQL_ERROR;
299}
300
301
302
303/// Gets the data of a column.
304int Sql_GetData(Sql* self, size_t col, char** out_buf, size_t* out_len)
305{
306        if( self && self->row )
307        {
308                if( col < Sql_NumColumns(self) )
309                {
310                        if( out_buf ) *out_buf = self->row[col];
311                        if( out_len ) *out_len = (size_t)self->lengths[col];
312                }
313                else
314                {// out of range - ignore
315                        if( out_buf ) *out_buf = NULL;
316                        if( out_len ) *out_len = 0;
317                }
318                return SQL_SUCCESS;
319        }
320        return SQL_ERROR;
321}
322
323
324
325/// Frees the result of the query.
326void Sql_FreeResult(Sql* self)
327{
328        if( self && self->result )
329        {
330                mysql_free_result(self->result);
331                self->result = NULL;
332                self->row = NULL;
333                self->lengths = NULL;
334        }
335}
336
337
338
339/// Shows debug information (last query).
340void Sql_ShowDebug_(Sql* self, const char* debug_file, const unsigned long debug_line)
341{
342        if( self == NULL )
343                ShowDebug("at %s:%lu - self is NULL\n", debug_file, debug_line);
344        else if( StringBuf_Length(&self->buf) > 0 )
345                ShowDebug("at %s:%lu - %s\n", debug_file, debug_line, StringBuf_Value(&self->buf));
346        else
347                ShowDebug("at %s:%lu\n", debug_file, debug_line);
348}
349
350
351
352/// Frees a Sql handle returned by Sql_Malloc.
353void Sql_Free(Sql* self) 
354{
355        if( self )
356        {
357                Sql_FreeResult(self);
358                StringBuf_Destroy(&self->buf);
359                aFree(self);
360        }
361}
362
363
364
365///////////////////////////////////////////////////////////////////////////////
366// Prepared Statements
367///////////////////////////////////////////////////////////////////////////////
368
369
370
371/// Returns the mysql integer type for the target size.
372///
373/// @private
374static enum enum_field_types Sql_P_SizeToMysqlIntType(int sz)
375{
376        switch( sz )
377        {
378        case 1: return MYSQL_TYPE_TINY;
379        case 2: return MYSQL_TYPE_SHORT;
380        case 4: return MYSQL_TYPE_LONG;
381        case 8: return MYSQL_TYPE_LONGLONG;
382        default:
383                ShowDebug("SizeToMysqlIntType: unsupported size (%d)\n", sz);
384                return MYSQL_TYPE_NULL;
385        }
386}
387
388
389
390/// Binds a parameter/result.
391///
392/// @private
393static int Sql_P_BindSqlDataType(MYSQL_BIND* bind, enum SqlDataType buffer_type, void* buffer, size_t buffer_len, unsigned long* out_length, int8* out_is_null)
394{
395        memset(bind, 0, sizeof(MYSQL_BIND));
396        switch( buffer_type )
397        {
398        case SQLDT_NULL: bind->buffer_type = MYSQL_TYPE_NULL;
399                buffer_len = 0;// FIXME length = ? [FlavioJS]
400                break;
401        // fixed size
402        case SQLDT_UINT8: bind->is_unsigned = 1;
403        case SQLDT_INT8: bind->buffer_type = MYSQL_TYPE_TINY;
404                buffer_len = 1;
405                break;
406        case SQLDT_UINT16: bind->is_unsigned = 1;
407        case SQLDT_INT16: bind->buffer_type = MYSQL_TYPE_SHORT;
408                buffer_len = 2;
409                break;
410        case SQLDT_UINT32: bind->is_unsigned = 1;
411        case SQLDT_INT32: bind->buffer_type = MYSQL_TYPE_LONG;
412                buffer_len = 4;
413                break;
414        case SQLDT_UINT64: bind->is_unsigned = 1;
415        case SQLDT_INT64: bind->buffer_type = MYSQL_TYPE_LONGLONG;
416                buffer_len = 8;
417                break;
418        // platform dependent size
419        case SQLDT_UCHAR: bind->is_unsigned = 1;
420        case SQLDT_CHAR: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(char));
421                buffer_len = sizeof(char);
422                break;
423        case SQLDT_USHORT: bind->is_unsigned = 1;
424        case SQLDT_SHORT: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(short));
425                buffer_len = sizeof(short);
426                break;
427        case SQLDT_UINT: bind->is_unsigned = 1;
428        case SQLDT_INT: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(int));
429                buffer_len = sizeof(int);
430                break;
431        case SQLDT_ULONG: bind->is_unsigned = 1;
432        case SQLDT_LONG: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(long));
433                buffer_len = sizeof(long);
434                break;
435        case SQLDT_ULONGLONG: bind->is_unsigned = 1;
436        case SQLDT_LONGLONG: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(long long));
437                buffer_len = sizeof(long long);
438                break;
439        // floating point
440        case SQLDT_FLOAT: bind->buffer_type = MYSQL_TYPE_FLOAT;
441                buffer_len = 4;
442                break;
443        case SQLDT_DOUBLE: bind->buffer_type = MYSQL_TYPE_DOUBLE;
444                buffer_len = 8;
445                break;
446        // other
447        case SQLDT_STRING:
448        case SQLDT_ENUM: bind->buffer_type = MYSQL_TYPE_STRING;
449                break;
450        case SQLDT_BLOB: bind->buffer_type = MYSQL_TYPE_BLOB;
451                break;
452        default:
453                ShowDebug("Sql_P_BindSqlDataType: unsupported buffer type (%d)\n", buffer_type);
454                return SQL_ERROR;
455        }
456        bind->buffer = buffer;
457        bind->buffer_length = (unsigned long)buffer_len;
458        bind->length = out_length;
459        bind->is_null = (my_bool*)out_is_null;
460        return SQL_SUCCESS;
461}
462
463
464
465/// Prints debug information about a field (type and length).
466///
467/// @private
468static void Sql_P_ShowDebugMysqlFieldInfo(const char* prefix, enum enum_field_types type, int is_unsigned, unsigned long length, const char* length_postfix)
469{
470        const char* sign = (is_unsigned ? "UNSIGNED " : "");
471        const char* type_string;
472        switch( type )
473        {
474        default:
475                ShowDebug("%stype=%s%u, length=%d\n", prefix, sign, type, length);
476                return;
477#define SHOW_DEBUG_OF(x) case x: type_string = #x; break
478        SHOW_DEBUG_OF(MYSQL_TYPE_TINY);
479        SHOW_DEBUG_OF(MYSQL_TYPE_SHORT);
480        SHOW_DEBUG_OF(MYSQL_TYPE_LONG);
481        SHOW_DEBUG_OF(MYSQL_TYPE_INT24);
482        SHOW_DEBUG_OF(MYSQL_TYPE_LONGLONG);
483        SHOW_DEBUG_OF(MYSQL_TYPE_DECIMAL);
484        SHOW_DEBUG_OF(MYSQL_TYPE_FLOAT);
485        SHOW_DEBUG_OF(MYSQL_TYPE_DOUBLE);
486        SHOW_DEBUG_OF(MYSQL_TYPE_TIMESTAMP);
487        SHOW_DEBUG_OF(MYSQL_TYPE_DATE);
488        SHOW_DEBUG_OF(MYSQL_TYPE_TIME);
489        SHOW_DEBUG_OF(MYSQL_TYPE_DATETIME);
490        SHOW_DEBUG_OF(MYSQL_TYPE_YEAR);
491        SHOW_DEBUG_OF(MYSQL_TYPE_STRING);
492        SHOW_DEBUG_OF(MYSQL_TYPE_VAR_STRING);
493        SHOW_DEBUG_OF(MYSQL_TYPE_BLOB);
494        SHOW_DEBUG_OF(MYSQL_TYPE_SET);
495        SHOW_DEBUG_OF(MYSQL_TYPE_ENUM);
496        SHOW_DEBUG_OF(MYSQL_TYPE_NULL);
497#undef SHOW_DEBUG_TYPE_OF
498        }
499        ShowDebug("%stype=%s%s, length=%d%s\n", prefix, sign, type_string, length, length_postfix); 
500}
501
502
503
504/// Reports debug information about a truncated column.
505///
506/// @private
507static void SqlStmt_P_ShowDebugTruncatedColumn(SqlStmt* self, size_t i)
508{
509        MYSQL_RES* meta;
510        MYSQL_FIELD* field;
511        MYSQL_BIND* column;
512
513        meta = mysql_stmt_result_metadata(self->stmt);
514        field = mysql_fetch_field_direct(meta, (unsigned int)i);
515        ShowSQL("DB error - data of field '%s' was truncated.\n", field->name);
516        ShowDebug("column - %lu\n", (unsigned long)i);
517        Sql_P_ShowDebugMysqlFieldInfo("data   - ", field->type, field->flags&UNSIGNED_FLAG, self->column_lengths[i].length, "");
518        column = &self->columns[i];
519        if( column->buffer_type == MYSQL_TYPE_STRING )
520                Sql_P_ShowDebugMysqlFieldInfo("buffer - ", column->buffer_type, column->is_unsigned, column->buffer_length, "+1(nul-terminator)");
521        else
522                Sql_P_ShowDebugMysqlFieldInfo("buffer - ", column->buffer_type, column->is_unsigned, column->buffer_length, "");
523        mysql_free_result(meta);
524}
525
526
527
528/// Allocates and initializes a new SqlStmt handle.
529SqlStmt* SqlStmt_Malloc(Sql* sql)
530{
531        SqlStmt* self;
532        MYSQL_STMT* stmt;
533
534        if( sql == NULL )
535                return NULL;
536
537        stmt = mysql_stmt_init(&sql->handle);
538        if( stmt == NULL )
539        {
540                ShowSQL("DB error - %s\n", mysql_error(&sql->handle));
541                return NULL;
542        }
543        CREATE(self, SqlStmt, 1);
544        StringBuf_Init(&self->buf);
545        self->stmt = stmt;
546        self->params = NULL;
547        self->columns = NULL;
548        self->column_lengths = NULL;
549        self->max_params = 0;
550        self->max_columns = 0;
551        self->bind_params = false;
552        self->bind_columns = false;
553
554        return self;
555}
556
557
558
559/// Prepares the statement.
560int SqlStmt_Prepare(SqlStmt* self, const char* query, ...)
561{
562        int res;
563        va_list args;
564
565        va_start(args, query);
566        res = SqlStmt_PrepareV(self, query, args);
567        va_end(args);
568
569        return res;
570}
571
572
573
574/// Prepares the statement.
575int SqlStmt_PrepareV(SqlStmt* self, const char* query, va_list args)
576{
577        if( self == NULL )
578                return SQL_ERROR;
579
580        SqlStmt_FreeResult(self);
581        StringBuf_Clear(&self->buf);
582        StringBuf_Vprintf(&self->buf, query, args);
583        if( mysql_stmt_prepare(self->stmt, StringBuf_Value(&self->buf), (unsigned long)StringBuf_Length(&self->buf)) )
584        {
585                ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt));
586                return SQL_ERROR;
587        }
588        self->bind_params = false;
589
590        return SQL_SUCCESS;
591}
592
593
594
595/// Prepares the statement.
596int SqlStmt_PrepareStr(SqlStmt* self, const char* query)
597{
598        if( self == NULL )
599                return SQL_ERROR;
600
601        SqlStmt_FreeResult(self);
602        StringBuf_Clear(&self->buf);
603        StringBuf_AppendStr(&self->buf, query);
604        if( mysql_stmt_prepare(self->stmt, StringBuf_Value(&self->buf), (unsigned long)StringBuf_Length(&self->buf)) )
605        {
606                ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt));
607                return SQL_ERROR;
608        }
609        self->bind_params = false;
610
611        return SQL_SUCCESS;
612}
613
614
615
616/// Returns the number of parameters in the prepared statement.
617size_t SqlStmt_NumParams(SqlStmt* self)
618{
619        if( self )
620                return (size_t)mysql_stmt_param_count(self->stmt);
621        else
622                return 0;
623}
624
625
626
627/// Binds a parameter to a buffer.
628int SqlStmt_BindParam(SqlStmt* self, size_t idx, enum SqlDataType buffer_type, void* buffer, size_t buffer_len)
629{
630        if( self == NULL )
631                return SQL_ERROR;
632
633        if( !self->bind_params )
634        {// initialize the bindings
635                size_t i;
636                size_t count;
637
638                count = SqlStmt_NumParams(self);
639                if( self->max_params < count )
640                {
641                        self->max_params = count;
642                        RECREATE(self->params, MYSQL_BIND, count);
643                }
644                memset(self->params, 0, count*sizeof(MYSQL_BIND));
645                for( i = 0; i < count; ++i )
646                        self->params[i].buffer_type = MYSQL_TYPE_NULL;
647                self->bind_params = true;
648        }
649        if( idx < self->max_params )
650                return Sql_P_BindSqlDataType(self->params+idx, buffer_type, buffer, buffer_len, NULL, NULL);
651        else
652                return SQL_SUCCESS;// out of range - ignore
653}
654
655
656
657/// Executes the prepared statement.
658int SqlStmt_Execute(SqlStmt* self)
659{
660        if( self == NULL )
661                return SQL_ERROR;
662
663        SqlStmt_FreeResult(self);
664        if( (self->bind_params && mysql_stmt_bind_param(self->stmt, self->params)) ||
665                mysql_stmt_execute(self->stmt) )
666        {
667                ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt));
668                return SQL_ERROR;
669        }
670        self->bind_columns = false;
671        if( mysql_stmt_store_result(self->stmt) )// store all the data
672        {
673                ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt));
674                return SQL_ERROR;
675        }
676
677        return SQL_SUCCESS;
678}
679
680
681
682/// Returns the number of the AUTO_INCREMENT column of the last INSERT/UPDATE statement.
683uint64 SqlStmt_LastInsertId(SqlStmt* self)
684{
685        if( self )
686                return (uint64)mysql_stmt_insert_id(self->stmt);
687        else
688                return 0;
689}
690
691
692
693/// Returns the number of columns in each row of the result.
694size_t SqlStmt_NumColumns(SqlStmt* self)
695{
696        if( self )
697                return (size_t)mysql_stmt_field_count(self->stmt);
698        else
699                return 0;
700}
701
702
703
704/// Binds the result of a column to a buffer.
705int SqlStmt_BindColumn(SqlStmt* self, size_t idx, enum SqlDataType buffer_type, void* buffer, size_t buffer_len, uint32* out_length, int8* out_is_null)
706{
707        if( self == NULL )
708                return SQL_ERROR;
709
710        if( buffer_type == SQLDT_STRING || buffer_type == SQLDT_ENUM )
711        {
712                if( buffer_len < 1 )
713                {
714                        ShowDebug("SqlStmt_BindColumn: buffer_len(%d) is too small, no room for the nul-terminator\n", buffer_len);
715                        return SQL_ERROR;
716                }
717                --buffer_len;// nul-terminator
718        }
719        if( !self->bind_columns )
720        {// initialize the bindings
721                size_t i;
722                size_t cols;
723
724                cols = SqlStmt_NumColumns(self);
725                if( self->max_columns < cols )
726                {
727                        self->max_columns = cols;
728                        RECREATE(self->columns, MYSQL_BIND, cols);
729                        RECREATE(self->column_lengths, s_column_length, cols);
730                }
731                memset(self->columns, 0, cols*sizeof(MYSQL_BIND));
732                memset(self->column_lengths, 0, cols*sizeof(s_column_length));
733                for( i = 0; i < cols; ++i )
734                        self->columns[i].buffer_type = MYSQL_TYPE_NULL;
735                self->bind_columns = true;
736        }
737        if( idx < self->max_columns )
738        {
739                self->column_lengths[idx].out_length = out_length;
740                return Sql_P_BindSqlDataType(self->columns+idx, buffer_type, buffer, buffer_len, &self->column_lengths[idx].length, out_is_null);
741        }
742        else
743        {
744                return SQL_SUCCESS;// out of range - ignore
745        }
746}
747
748
749
750/// Returns the number of rows in the result.
751uint64 SqlStmt_NumRows(SqlStmt* self)
752{
753        if( self )
754                return (uint64)mysql_stmt_num_rows(self->stmt);
755        else
756                return 0;
757}
758
759
760
761/// Fetches the next row.
762int SqlStmt_NextRow(SqlStmt* self)
763{
764        int err;
765        size_t i;
766        size_t cols;
767        MYSQL_BIND* column;
768        unsigned long length;
769
770        if( self == NULL )
771                return SQL_ERROR;
772
773        // bind columns
774        if( self->bind_columns && mysql_stmt_bind_result(self->stmt, self->columns) )
775                err = 1;// error binding columns
776        else
777                err = mysql_stmt_fetch(self->stmt);// fetch row
778
779        // check for errors
780        if( err == MYSQL_NO_DATA )
781                return SQL_NO_DATA;
782#if defined(MYSQL_DATA_TRUNCATED)
783        // MySQL 5.0/5.1 defines and returns MYSQL_DATA_TRUNCATED [FlavioJS]
784        if( err == MYSQL_DATA_TRUNCATED )
785        {
786                my_bool truncated;
787
788                if( !self->bind_columns )
789                {
790                        ShowSQL("DB error - data truncated (unknown source, columns are not bound)\n");
791                        return SQL_ERROR;
792                }
793
794                // find truncated column
795                cols = SqlStmt_NumColumns(self);
796                for( i = 0; i < cols; ++i )
797                {
798                        column = &self->columns[i];
799                        column->error = &truncated;
800                        mysql_stmt_fetch_column(self->stmt, column, (unsigned int)i, 0);
801                        column->error = NULL;
802                        if( truncated )
803                        {// report truncated column
804                                SqlStmt_P_ShowDebugTruncatedColumn(self, i);
805                                return SQL_ERROR;
806                        }
807                }
808                ShowSQL("DB error - data truncated (unknown source)\n");
809                return SQL_ERROR;
810        }
811#endif
812        if( err )
813        {
814                ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt));
815                return SQL_ERROR;
816        }
817
818        // propagate column lengths and clear unused parts of string/enum/blob buffers
819        cols = SqlStmt_NumColumns(self);
820        for( i = 0; i < cols; ++i )
821        {
822                length = self->column_lengths[i].length;
823                column = &self->columns[i];
824#if !defined(MYSQL_DATA_TRUNCATED)
825                // MySQL 4.1/(below?) returns success even if data is truncated, so we test truncation manually [FlavioJS]
826                if( column->buffer_length < length )
827                {// report truncated column
828                        if( column->buffer_type == MYSQL_TYPE_STRING || column->buffer_type == MYSQL_TYPE_BLOB )
829                        {// string/enum/blob column
830                                SqlStmt_P_ShowDebugTruncatedColumn(self, i);
831                                return SQL_ERROR;
832                        }
833                        // FIXME numeric types and null [FlavioJS]
834                }
835#endif
836                if( self->column_lengths[i].out_length )
837                        *self->column_lengths[i].out_length = (uint32)length;
838                if( column->buffer_type == MYSQL_TYPE_STRING )
839                {// clear unused part of the string/enum buffer (and nul-terminate)
840                        memset((char*)column->buffer + length, 0, column->buffer_length - length + 1);
841                }
842                else if( column->buffer_type == MYSQL_TYPE_BLOB && length < column->buffer_length )
843                {// clear unused part of the blob buffer
844                        memset((char*)column->buffer + length, 0, column->buffer_length - length);
845                }
846        }
847
848        return SQL_SUCCESS;
849}
850
851
852
853/// Frees the result of the statement execution.
854void SqlStmt_FreeResult(SqlStmt* self)
855{
856        if( self )
857                mysql_stmt_free_result(self->stmt);
858}
859
860
861
862/// Shows debug information (with statement).
863void SqlStmt_ShowDebug_(SqlStmt* self, const char* debug_file, const unsigned long debug_line)
864{
865        if( self == NULL )
866                ShowDebug("at %s:%lu -  self is NULL\n", debug_file, debug_line);
867        if( StringBuf_Length(&self->buf) > 0 )
868                ShowDebug("at %s:%lu - %s\n", debug_file, debug_line, StringBuf_Value(&self->buf));
869        else
870                ShowDebug("at %s:%lu\n", debug_file, debug_line);
871}
872
873
874
875/// Frees a SqlStmt returned by SqlStmt_Malloc.
876void SqlStmt_Free(SqlStmt* self)
877{
878        if( self )
879        {
880                SqlStmt_FreeResult(self);
881                StringBuf_Destroy(&self->buf);
882                mysql_stmt_close(self->stmt);
883                if( self->params )
884                        aFree(self->params);
885                if( self->columns )
886                {
887                        aFree(self->columns);
888                        aFree(self->column_lengths);
889                }
890                aFree(self);
891        }
892}
Note: See TracBrowser for help on using the browser.