diff options
author | FlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2007-09-20 11:09:36 +0000 |
---|---|---|
committer | FlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2007-09-20 11:09:36 +0000 |
commit | 5245e666a09df5f401c1329bf5ee1fc1b09b1d16 (patch) | |
tree | dcf032743e890fddd400b268b75a0868976b0a0b /src/common | |
parent | d23c508bcc38520970156e5e25f14b03714878eb (diff) | |
download | hercules-5245e666a09df5f401c1329bf5ee1fc1b09b1d16.tar.gz hercules-5245e666a09df5f401c1329bf5ee1fc1b09b1d16.tar.bz2 hercules-5245e666a09df5f401c1329bf5ee1fc1b09b1d16.tar.xz hercules-5245e666a09df5f401c1329bf5ee1fc1b09b1d16.zip |
* Merged the tmpsql branch:
- Abstraction for the sql code (sql.c/h).
- New configure script and makefiles.
- Restored txt zeny logging code. (r10814)
- Rewrote mapserver's sql code - itemdb, mobdb, mapreg, logs. (r10814)
- Fixed a precedence issue (&& and ) in char_sql/char.c. (r10833)
- Improved db reading code a bit for consistency. (r11077)
- Added separate atcommand for mail deletion. (r11077)
- Corrected a few messages that said "new" instead of "unread". (r11077)
- Broadcast (*) messages now use "*" as the target's name (not ""). (r11077)
- Moved StringBuf code from utils.c/h to strlib.c/h. (r11084 r11117)
- Some misc login server cleanups (reformatting etc). (r11136)
- Corrected/modified some header entries. (r11141 r11147 11148)
- Adjusted VS project files. (r11147)
- Adjusted the way the sql charserver does item saving. (r11192)
- Corrected usage of reserved keyword 'friend' in mmo.h. (r11192)
git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@11245 54d463be-8e91-2dee-dedb-b68131a5f0ec
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/Makefile.in (renamed from src/common/Makefile) | 67 | ||||
-rw-r--r-- | src/common/db.c | 1 | ||||
-rw-r--r-- | src/common/ers.c | 3 | ||||
-rw-r--r-- | src/common/ers.h | 2 | ||||
-rw-r--r-- | src/common/md5calc.c | 2 | ||||
-rw-r--r-- | src/common/mmo.h | 7 | ||||
-rw-r--r-- | src/common/plugins.c | 4 | ||||
-rw-r--r-- | src/common/showmsg.c | 21 | ||||
-rw-r--r-- | src/common/socket.c | 13 | ||||
-rw-r--r-- | src/common/socket.h | 4 | ||||
-rw-r--r-- | src/common/sql.c | 851 | ||||
-rw-r--r-- | src/common/sql.h | 343 | ||||
-rw-r--r-- | src/common/strlib.c | 134 | ||||
-rw-r--r-- | src/common/strlib.h | 22 | ||||
-rw-r--r-- | src/common/utils.c | 154 | ||||
-rw-r--r-- | src/common/utils.h | 28 |
16 files changed, 1464 insertions, 192 deletions
diff --git a/src/common/Makefile b/src/common/Makefile.in index 258423776..c22cc721c 100644 --- a/src/common/Makefile +++ b/src/common/Makefile.in @@ -1,24 +1,62 @@ -txt sql all: obj common +HAVE_MYSQL=@HAVE_MYSQL@ +ifeq ($(HAVE_MYSQL),yes) + ALL_DEPENDS=txt sql + SQL_DEPENDS=txt obj_sql common_sql +else + ALL_TARGET=txt + SQL_DEPENDS=needs_mysql +endif + +##################################################################### +.PHONY : all txt sql clean help + +all: $(ALL_DEPENDS) + +txt: obj common + +sql: $(SQL_DEPENDS) + +clean: + rm -rf *.o obj obj_sql + +help: + @echo "possible targets are 'txt' 'sql' 'all' 'clean' 'help'" + @echo "'txt' - builds object files used in txt servers" + @echo "'sql' - builds object files used in sql servers" + @echo "'all' - builds all above targets" + @echo "'clean' - cleans builds and objects" + @echo "'help' - outputs this message" + +##################################################################### + +needs_mysql: + @echo "MySQL not found or disabled by the configure script" + @exit 1 obj: - mkdir obj + -mkdir obj + +obj_sql: + -mkdir obj_sql common: obj/core.o obj/socket.o obj/timer.o obj/db.o obj/plugins.o obj/lock.o \ obj/nullpo.o obj/malloc.o obj/showmsg.o obj/strlib.o obj/utils.o \ obj/grfio.o obj/minicore.o obj/minisocket.o obj/minimalloc.o \ obj/mapindex.o obj/ers.o obj/md5calc.o +common_sql: obj_sql/sql.o + obj/%.o: %.c - $(COMPILE.c) $(OUTPUT_OPTION) $< + @CC@ @CFLAGS@ @LDFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $< obj/mini%.o: %.c - $(COMPILE.c) -DMINICORE $(OUTPUT_OPTION) $< + @CC@ @CFLAGS@ -DMINICORE @LDFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $< + +obj_sql/%.o: %.c + @CC@ @CFLAGS@ @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $< -clean: - rm -rf *.o obj GNUmakefile - rm -rf svnversion.h HAVESVN = $(shell which svnversion) ifeq ($(findstring /,$(HAVESVN)), /) @@ -38,19 +76,20 @@ obj/minimalloc.o: malloc.c malloc.h # DO NOT DELETE obj/core.o: core.c core.h showmsg.h svnversion.h -obj/socket.o: socket.c socket.h mmo.h showmsg.h plugins.h -obj/timer.o: timer.c timer.h showmsg.h -obj/ers.o: ers.c ers.h cbasetypes.h obj/db.o: db.c db.h showmsg.h ers.h -obj/lock.o: lock.c lock.h showmsg.h +obj/ers.o: ers.c ers.h cbasetypes.h obj/grfio.o: grfio.c grfio.h -obj/nullpo.o: nullpo.c nullpo.h showmsg.h +obj/lock.o: lock.c lock.h showmsg.h obj/malloc.o: malloc.c malloc.h showmsg.h +obj/mapindex.o: mapindex.c mapindex.h +obj/md5calc.o: md5calc.c md5calc.h +obj/nullpo.o: nullpo.c nullpo.h showmsg.h obj/plugins.o: plugins.c plugins.h plugin.h obj/showmsg.o: showmsg.c showmsg.h +obj/socket.o: socket.c socket.h mmo.h showmsg.h plugins.h obj/strlib.o: strlib.c strlib.h utils.h -obj/mapindex.o: mapindex.c mapindex.h +obj/timer.o: timer.c timer.h showmsg.h obj/utils.o: utils.c utils.h malloc.h showmsg.h mmo.h -obj/md5calc.o: md5calc.c md5calc.h +obj_sql/sql.o: sql.c sql.h cbasetypes.h malloc.h showmsg.h utils.h mmo.h: cbasetypes.h @touch mmo.h diff --git a/src/common/db.c b/src/common/db.c index 80918d7f6..476c5a0c9 100644 --- a/src/common/db.c +++ b/src/common/db.c @@ -70,7 +70,6 @@ #include "db.h" #include "../common/mmo.h" -#include "../common/utils.h" #include "../common/malloc.h" #include "../common/showmsg.h" #include "../common/ers.h" diff --git a/src/common/ers.c b/src/common/ers.c index 53c1edc3a..ecdda4609 100644 --- a/src/common/ers.c +++ b/src/common/ers.c @@ -40,9 +40,10 @@ \*****************************************************************************/ #include <stdlib.h> -#include "ers.h" +#include "../common/cbasetypes.h" #include "../common/malloc.h" // CREATE, RECREATE, aMalloc, aFree #include "../common/showmsg.h" // ShowMessage, ShowError, ShowFatalError, CL_BOLD, CL_NORMAL +#include "ers.h" #ifndef DISABLE_ERS /*****************************************************************************\ diff --git a/src/common/ers.h b/src/common/ers.h index 51b12d8a2..a9ba50073 100644 --- a/src/common/ers.h +++ b/src/common/ers.h @@ -40,7 +40,9 @@ #ifndef _ERS_H_ #define _ERS_H_ +#ifndef _CBASETYPES_H_ #include "../common/cbasetypes.h" +#endif /*****************************************************************************\ * (1) All public parts of the Entry Reusage System. * diff --git a/src/common/md5calc.c b/src/common/md5calc.c index b70236d6b..701a9db6c 100644 --- a/src/common/md5calc.c +++ b/src/common/md5calc.c @@ -173,7 +173,7 @@ void MD5_String2binary(const char * string, char * output) //Step 1.Append Padding Bits (extension of a mark bit) //1-1 - string_byte_len = strlen(string); //The byte chief of a character sequence is acquired. + string_byte_len = (unsigned int)strlen(string); //The byte chief of a character sequence is acquired. pstring = (unsigned char *)string; //The position of the present character sequence is set. //1-2 Repeat calculation until length becomes less than 64 bytes. diff --git a/src/common/mmo.h b/src/common/mmo.h index 0fcfff137..619c75295 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -4,9 +4,8 @@ #ifndef _MMO_H_ #define _MMO_H_ -#include <time.h> #include "cbasetypes.h" -#include "utils.h" // _WIN32 +#include <time.h> #define FIFOSIZE_SERVERLINK 256*1024 @@ -179,7 +178,7 @@ struct s_homunculus { //[orn] int luk ; }; -struct friend { +struct s_friend { int account_id; int char_id; char name[NAME_LENGTH]; @@ -229,7 +228,7 @@ struct mmo_charstatus { struct item inventory[MAX_INVENTORY],cart[MAX_CART]; struct skill skill[MAX_SKILL]; - struct friend friends[MAX_FRIENDS]; //New friend system [Skotlex] + struct s_friend friends[MAX_FRIENDS]; //New friend system [Skotlex] #ifdef HOTKEY_SAVING struct hotkey hotkeys[HOTKEY_SAVING]; #endif diff --git a/src/common/plugins.c b/src/common/plugins.c index a2d4de73e..58ab60b74 100644 --- a/src/common/plugins.c +++ b/src/common/plugins.c @@ -1,15 +1,15 @@ // Copyright (c) Athena Dev Teams - Licensed under GNU GPL // For more information, see LICENCE in the main folder -#include "plugins.h" #include "../common/mmo.h" #include "../common/core.h" #include "../common/timer.h" -#include "../common/utils.h" +#include "../common/utils.h" // findfile() #include "../common/socket.h" #include "../common/malloc.h" #include "../common/version.h" #include "../common/showmsg.h" +#include "plugins.h" #include <stdio.h> #include <stdlib.h> diff --git a/src/common/showmsg.c b/src/common/showmsg.c index 615b52914..6fb8a2e6c 100644 --- a/src/common/showmsg.c +++ b/src/common/showmsg.c @@ -2,7 +2,7 @@ // For more information, see LICENCE in the main folder #include "../common/cbasetypes.h" -#include "../common/utils.h" +#include "../common/strlib.h" // StringBuf #include "showmsg.h" #include <stdio.h> @@ -59,7 +59,7 @@ int msg_silent = 0; //Specifies how silent the console is. #define NEWBUF(buf) \ struct { \ char s_[SBUF_SIZE]; \ - struct StringBuf *d_; \ + StringBuf *d_; \ char *v_; \ int l_; \ } buf ={"",NULL,NULL,0}; \ @@ -198,7 +198,7 @@ int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr) */ ///////////////////////////////////////////////////////////////// - unsigned long written; + DWORD written; char *p, *q; NEWBUF(tempbuf); // temporary buffer @@ -218,8 +218,8 @@ int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr) p = BUFVAL(tempbuf); while ((q = strchr(p, 0x1b)) != NULL) { // find the escape character - if( 0==WriteConsole(handle, p, q-p, &written, 0) ) // write up to the escape - WriteFile(handle, p, q-p, &written, 0); + if( 0==WriteConsole(handle, p, (DWORD)(q-p), &written, 0) ) // write up to the escape + WriteFile(handle, p, (DWORD)(q-p), &written, 0); if( q[1]!='[' ) { // write the escape char (whatever purpose it has) @@ -240,7 +240,7 @@ int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr) // skip escape and bracket q=q+2; - while(1) + for(;;) { if( ISDIGIT(*q) ) { // add number to number array, only accept 2digits, shift out the rest @@ -501,8 +501,8 @@ int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr) } } if (*p) // write the rest of the buffer - if( 0==WriteConsole(handle, p, strlen(p), &written, 0) ) - WriteFile(handle,p, strlen(p), &written, 0); + if( 0==WriteConsole(handle, p, (DWORD)strlen(p), &written, 0) ) + WriteFile(handle, p, (DWORD)strlen(p), &written, 0); FREEBUF(tempbuf); return 0; } @@ -689,7 +689,10 @@ int _vShowMessage(enum msg_type flag, const char *string, va_list ap) ShowError("Empty string passed to _vShowMessage().\n"); return 1; } - if ((flag == MSG_DEBUG && !SHOW_DEBUG_MSG) || + if ( +#if !defined(SHOW_DEBUG_MSG) + (flag == MSG_DEBUG) || +#endif (flag == MSG_INFORMATION && msg_silent&1) || (flag == MSG_STATUS && msg_silent&2) || (flag == MSG_NOTICE && msg_silent&4) || diff --git a/src/common/socket.c b/src/common/socket.c index 221a0289b..c70c0cbaa 100644 --- a/src/common/socket.c +++ b/src/common/socket.c @@ -111,8 +111,9 @@ void set_defaultparse(ParseFunc defaultparse) void set_nonblocking(int fd, unsigned long yes) { // TCP_NODELAY BOOL Disables the Nagle algorithm for send coalescing. - if(MODE_NODELAY) - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&yes, sizeof yes); +#if defined(MODE_NODELAY) && MODE_NODELAY == 1 + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&yes, sizeof yes); +#endif // FIONBIO Use with a nonzero argp parameter to enable the nonblocking mode of socket s. // The argp parameter is zero if nonblocking is to be disabled. @@ -169,7 +170,7 @@ int recv_to_fifo(int fd) if( !session_isActive(fd) ) return -1; - len = recv(fd, (char *) session[fd]->rdata + session[fd]->rdata_size, RFIFOSPACE(fd), 0); + len = recv(fd, (char *) session[fd]->rdata + session[fd]->rdata_size, (int)RFIFOSPACE(fd), 0); if (len == SOCKET_ERROR) { if (s_errno == S_ECONNABORTED) { @@ -203,7 +204,7 @@ int send_from_fifo(int fd) if (session[fd]->wdata_size == 0) return 0; - len = send(fd, (const char *) session[fd]->wdata, session[fd]->wdata_size, 0); + len = send(fd, (const char *) session[fd]->wdata, (int)session[fd]->wdata_size, 0); if (len == SOCKET_ERROR) { if (s_errno == S_ECONNABORTED) { @@ -457,7 +458,7 @@ int realloc_writefifo(int fd, size_t addition) return 0; } -int RFIFOSKIP(int fd, int len) +int RFIFOSKIP(int fd, size_t len) { struct socket_data *s; @@ -477,7 +478,7 @@ int RFIFOSKIP(int fd, int len) return 0; } -int WFIFOSET(int fd, int len) +int WFIFOSET(int fd, size_t len) { size_t newreserve; struct socket_data* s = session[fd]; diff --git a/src/common/socket.h b/src/common/socket.h index 469ff678d..4015fed97 100644 --- a/src/common/socket.h +++ b/src/common/socket.h @@ -104,8 +104,8 @@ int make_listen_bind(uint32 ip, uint16 port); int make_connection(uint32 ip, uint16 port); int realloc_fifo(int fd, unsigned int rfifo_size, unsigned int wfifo_size); int realloc_writefifo(int fd, size_t addition); -int WFIFOSET(int fd, int len); -int RFIFOSKIP(int fd, int len); +int WFIFOSET(int fd, size_t len); +int RFIFOSKIP(int fd, size_t len); int do_sendrecv(int next); int do_parsepacket(void); diff --git a/src/common/sql.c b/src/common/sql.c new file mode 100644 index 000000000..cc6e81009 --- /dev/null +++ b/src/common/sql.c @@ -0,0 +1,851 @@ +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include "../common/cbasetypes.h" +#include "../common/malloc.h" +#include "../common/showmsg.h" +#include "../common/strlib.h" +#include "sql.h" + +#ifdef WIN32 +#include <winsock2.h> +#endif +#include <mysql.h> +#include <string.h>// strlen/strnlen/memcpy/memset +#include <stdlib.h>// strtoul + + + +/// Sql handle +struct Sql +{ + StringBuf buf; + MYSQL handle; + MYSQL_RES* result; + MYSQL_ROW row; + unsigned long* lengths; +}; + + + +// Column length receiver. +// Takes care of the possible size missmatch between uint32 and unsigned long. +struct s_column_length +{ + uint32* out_length; + unsigned long length; +}; +typedef struct s_column_length s_column_length; + + + +/// Sql statement +struct SqlStmt +{ + StringBuf buf; + MYSQL_STMT* stmt; + MYSQL_BIND* params; + MYSQL_BIND* columns; + s_column_length* column_lengths; + size_t max_params; + size_t max_columns; + bool bind_params; + bool bind_columns; +}; + + + +/////////////////////////////////////////////////////////////////////////////// +// Sql Handle +/////////////////////////////////////////////////////////////////////////////// + + + +/// Allocates and initializes a new Sql handle. +Sql* Sql_Malloc(void) +{ + Sql* self; + CREATE(self, Sql, 1); + mysql_init(&self->handle); + StringBuf_Init(&self->buf); + return self; +} + + + +/// Establishes a connection. +int Sql_Connect(Sql* self, const char* user, const char* passwd, const char* host, uint16 port, const char* db) +{ + if( self == NULL ) + return SQL_ERROR; + + StringBuf_Clear(&self->buf); + if( !mysql_real_connect(&self->handle, host, user, passwd, db, (unsigned int)port, NULL/*unix_socket*/, 0/*clientflag*/) ) + { + ShowSQL("%s\n", mysql_error(&self->handle)); + return SQL_ERROR; + } + return SQL_SUCCESS; +} + + + +/// Retrieves the timeout of the connection. +int Sql_GetTimeout(Sql* self, uint32* out_timeout) +{ + if( self && out_timeout && SQL_SUCCESS == Sql_Query(self, "SHOW VARIABLES LIKE 'wait_timeout'") ) + { + char* data; + size_t len; + if( SQL_SUCCESS == Sql_NextRow(self) && + SQL_SUCCESS == Sql_GetData(self, 1, &data, &len) ) + { + *out_timeout = (uint32)strtoul(data, NULL, 10); + Sql_FreeResult(self); + return SQL_SUCCESS; + } + Sql_FreeResult(self); + } + return SQL_ERROR; +} + + + +/// Retrieves the name of the columns of a table into out_buf, with the separator after each name. +int Sql_GetColumnNames(Sql* self, const char* table, char* out_buf, size_t buf_len, char sep) +{ + char* data; + size_t len; + size_t off = 0; + + if( self == NULL || SQL_ERROR == Sql_Query(self, "EXPLAIN `%s`", table) ) + return SQL_ERROR; + + out_buf[off] = '\0'; + while( SQL_SUCCESS == Sql_NextRow(self) && SQL_SUCCESS == Sql_GetData(self, 0, &data, &len) ) + { + len = strnlen(data, len); + if( off + len + 2 > buf_len ) + { + ShowDebug("Sql_GetColumns: output buffer is too small\n"); + *out_buf = '\0'; + return SQL_ERROR; + } + memcpy(out_buf+off, data, len); + off += len; + out_buf[off++] = sep; + } + out_buf[off] = '\0'; + Sql_FreeResult(self); + return SQL_SUCCESS; +} + + + +/// Changes the encoding of the connection. +int Sql_SetEncoding(Sql* self, const char* encoding) +{ + return Sql_Query(self, "SET NAMES %s", encoding); +} + + + +/// Pings the connection. +int Sql_Ping(Sql* self) +{ + if( self && mysql_ping(&self->handle) == 0 ) + return SQL_SUCCESS; + return SQL_ERROR; +} + + + +/// Escapes a string. +size_t Sql_EscapeString(Sql* self, char *out_to, const char *from) +{ + if( self ) + return (size_t)mysql_real_escape_string(&self->handle, out_to, from, (unsigned long)strlen(from)); + else + return (size_t)mysql_escape_string(out_to, from, (unsigned long)strlen(from)); +} + + + +/// Escapes a string. +size_t Sql_EscapeStringLen(Sql* self, char *out_to, const char *from, size_t from_len) +{ + if( self ) + return (size_t)mysql_real_escape_string(&self->handle, out_to, from, (unsigned long)from_len); + else + return (size_t)mysql_escape_string(out_to, from, (unsigned long)from_len); +} + + + +/// Executes a query. +int Sql_Query(Sql* self, const char* query, ...) +{ + int res; + va_list args; + + va_start(args, query); + res = Sql_QueryV(self, query, args); + va_end(args); + + return res; +} + + + +/// Executes a query. +int Sql_QueryV(Sql* self, const char* query, va_list args) +{ + if( self == NULL ) + return SQL_ERROR; + + Sql_FreeResult(self); + StringBuf_Clear(&self->buf); + StringBuf_Vprintf(&self->buf, query, args); + if( mysql_real_query(&self->handle, StringBuf_Value(&self->buf), (unsigned long)StringBuf_Length(&self->buf)) ) + { + ShowSQL("DB error - %s\n", mysql_error(&self->handle)); + return SQL_ERROR; + } + self->result = mysql_store_result(&self->handle); + if( mysql_errno(&self->handle) != 0 ) + { + ShowSQL("DB error - %s\n", mysql_error(&self->handle)); + return SQL_ERROR; + } + return SQL_SUCCESS; +} + + + +/// Executes a query. +int Sql_QueryStr(Sql* self, const char* query) +{ + if( self == NULL ) + return SQL_ERROR; + + Sql_FreeResult(self); + StringBuf_Clear(&self->buf); + StringBuf_AppendStr(&self->buf, query); + if( mysql_real_query(&self->handle, StringBuf_Value(&self->buf), (unsigned long)StringBuf_Length(&self->buf)) ) + { + ShowSQL("DB error - %s\n", mysql_error(&self->handle)); + return SQL_ERROR; + } + self->result = mysql_store_result(&self->handle); + if( mysql_errno(&self->handle) != 0 ) + { + ShowSQL("DB error - %s\n", mysql_error(&self->handle)); + return SQL_ERROR; + } + return SQL_SUCCESS; +} + + + +/// Returns the number of the AUTO_INCREMENT column of the last INSERT/UPDATE query. +uint64 Sql_LastInsertId(Sql* self) +{ + if( self ) + return (uint64)mysql_insert_id(&self->handle); + else + return 0; +} + + + +/// Returns the number of columns in each row of the result. +uint32 Sql_NumColumns(Sql* self) +{ + if( self && self->result ) + return (uint32)mysql_num_fields(self->result); + return 0; +} + + + +/// Returns the number of rows in the result. +uint64 Sql_NumRows(Sql* self) +{ + if( self && self->result ) + return (uint64)mysql_num_rows(self->result); + return 0; +} + + + +/// Fetches the next row. +int Sql_NextRow(Sql* self) +{ + if( self && self->result ) + { + self->row = mysql_fetch_row(self->result); + if( self->row ) + { + self->lengths = mysql_fetch_lengths(self->result); + return SQL_SUCCESS; + } + self->lengths = NULL; + if( mysql_errno(&self->handle) == 0 ) + return SQL_NO_DATA; + } + return SQL_ERROR; +} + + + +/// Gets the data of a column. +int Sql_GetData(Sql* self, size_t col, char** out_buf, size_t* out_len) +{ + if( self && self->row ) + { + if( col < Sql_NumColumns(self) ) + { + if( out_buf ) *out_buf = self->row[col]; + if( out_len ) *out_len = (size_t)self->lengths[col]; + } + else + {// out of range - ignore + if( out_buf ) *out_buf = NULL; + if( out_len ) *out_len = 0; + } + return SQL_SUCCESS; + } + return SQL_ERROR; +} + + + +/// Frees the result of the query. +void Sql_FreeResult(Sql* self) +{ + if( self && self->result ) + { + mysql_free_result(self->result); + self->result = NULL; + self->row = NULL; + self->lengths = NULL; + } +} + + + +/// Shows debug information (last query). +void Sql_ShowDebug_(Sql* self, const char* debug_file, const unsigned long debug_line) +{ + if( self == NULL ) + ShowDebug("at %s:%lu - self is NULL\n", debug_file, debug_line); + else if( StringBuf_Length(&self->buf) > 0 ) + ShowDebug("at %s:%lu - %s\n", debug_file, debug_line, StringBuf_Value(&self->buf)); + else + ShowDebug("at %s:%lu\n", debug_file, debug_line); +} + + + +/// Frees a Sql handle returned by Sql_Malloc. +void Sql_Free(Sql* self) +{ + if( self ) + { + Sql_FreeResult(self); + StringBuf_Destroy(&self->buf); + aFree(self); + } +} + + + +/////////////////////////////////////////////////////////////////////////////// +// Prepared Statements +/////////////////////////////////////////////////////////////////////////////// + + + +/// Returns the mysql integer type for the target size. +/// +/// @private +static enum enum_field_types Sql_P_SizeToMysqlIntType(int sz) +{ + switch( sz ) + { + case 1: return MYSQL_TYPE_TINY; + case 2: return MYSQL_TYPE_SHORT; + case 4: return MYSQL_TYPE_LONG; + case 8: return MYSQL_TYPE_LONGLONG; + default: + ShowDebug("SizeToMysqlIntType: unsupported size (%d)\n", sz); + return MYSQL_TYPE_NULL; + } +} + + + +/// Binds a parameter/result. +/// +/// @private +static 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) +{ + memset(bind, 0, sizeof(MYSQL_BIND)); + switch( buffer_type ) + { + case SQLDT_NULL: bind->buffer_type = MYSQL_TYPE_NULL; + break; + // fixed size + case SQLDT_UINT8: bind->is_unsigned = 1; + case SQLDT_INT8: bind->buffer_type = MYSQL_TYPE_TINY; + break; + case SQLDT_UINT16: bind->is_unsigned = 1; + case SQLDT_INT16: bind->buffer_type = MYSQL_TYPE_SHORT; + break; + case SQLDT_UINT32: bind->is_unsigned = 1; + case SQLDT_INT32: bind->buffer_type = MYSQL_TYPE_LONG; + break; + case SQLDT_UINT64: bind->is_unsigned = 1; + case SQLDT_INT64: bind->buffer_type = MYSQL_TYPE_LONGLONG; + break; + // platform dependent size + case SQLDT_UCHAR: bind->is_unsigned = 1; + case SQLDT_CHAR: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(char)); + break; + case SQLDT_USHORT: bind->is_unsigned = 1; + case SQLDT_SHORT: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(short)); + break; + case SQLDT_UINT: bind->is_unsigned = 1; + case SQLDT_INT: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(int)); + break; + case SQLDT_ULONG: bind->is_unsigned = 1; + case SQLDT_LONG: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(long)); + break; + case SQLDT_ULONGLONG: bind->is_unsigned = 1; + case SQLDT_LONGLONG: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(long long)); + break; + // floating point + case SQLDT_FLOAT: bind->buffer_type = MYSQL_TYPE_FLOAT; + break; + case SQLDT_DOUBLE: bind->buffer_type = MYSQL_TYPE_DOUBLE; + break; + // other + case SQLDT_STRING: + case SQLDT_ENUM: bind->buffer_type = MYSQL_TYPE_STRING; + break; + case SQLDT_BLOB: bind->buffer_type = MYSQL_TYPE_BLOB; + break; + default: + ShowDebug("Sql_P_BindSqlDataType: unsupported buffer type (%d)\n", buffer_type); + return SQL_ERROR; + } + bind->buffer = buffer; + bind->buffer_length = (unsigned long)buffer_len; + bind->length = out_length; + bind->is_null = (my_bool*)out_is_null; + return SQL_SUCCESS; +} + + + +/// Prints debug information about a field (type and length). +/// +/// @private +static void Sql_P_ShowDebugMysqlFieldInfo(const char* prefix, enum enum_field_types type, int is_unsigned, unsigned long length, const char* length_postfix) +{ + const char* sign = (is_unsigned ? "UNSIGNED " : ""); + const char* type_string; + switch( type ) + { + default: + ShowDebug("%stype=%s%u, length=%d\n", prefix, sign, type, length); + return; +#define SHOW_DEBUG_OF(x) case x: type_string = #x; break + SHOW_DEBUG_OF(MYSQL_TYPE_TINY); + SHOW_DEBUG_OF(MYSQL_TYPE_SHORT); + SHOW_DEBUG_OF(MYSQL_TYPE_LONG); + SHOW_DEBUG_OF(MYSQL_TYPE_INT24); + SHOW_DEBUG_OF(MYSQL_TYPE_LONGLONG); + SHOW_DEBUG_OF(MYSQL_TYPE_DECIMAL); + SHOW_DEBUG_OF(MYSQL_TYPE_FLOAT); + SHOW_DEBUG_OF(MYSQL_TYPE_DOUBLE); + SHOW_DEBUG_OF(MYSQL_TYPE_TIMESTAMP); + SHOW_DEBUG_OF(MYSQL_TYPE_DATE); + SHOW_DEBUG_OF(MYSQL_TYPE_TIME); + SHOW_DEBUG_OF(MYSQL_TYPE_DATETIME); + SHOW_DEBUG_OF(MYSQL_TYPE_YEAR); + SHOW_DEBUG_OF(MYSQL_TYPE_STRING); + SHOW_DEBUG_OF(MYSQL_TYPE_VAR_STRING); + SHOW_DEBUG_OF(MYSQL_TYPE_BLOB); + SHOW_DEBUG_OF(MYSQL_TYPE_SET); + SHOW_DEBUG_OF(MYSQL_TYPE_ENUM); + SHOW_DEBUG_OF(MYSQL_TYPE_NULL); +#undef SHOW_DEBUG_TYPE_OF + } + ShowDebug("%stype=%s%s, length=%d%s\n", prefix, sign, type_string, length, length_postfix); +} + + + +/// Allocates and initializes a new SqlStmt handle. +SqlStmt* SqlStmt_Malloc(Sql* sql) +{ + SqlStmt* self; + MYSQL_STMT* stmt; + + if( sql == NULL ) + return NULL; + + stmt = mysql_stmt_init(&sql->handle); + if( stmt == NULL ) + { + ShowSQL("DB error - %s\n", mysql_error(&sql->handle)); + return NULL; + } + CREATE(self, SqlStmt, 1); + StringBuf_Init(&self->buf); + self->stmt = stmt; + self->params = NULL; + self->columns = NULL; + self->column_lengths = NULL; + self->max_params = 0; + self->max_columns = 0; + self->bind_params = false; + self->bind_columns = false; + + return self; +} + + + +/// Prepares the statement. +int SqlStmt_Prepare(SqlStmt* self, const char* query, ...) +{ + int res; + va_list args; + + va_start(args, query); + res = SqlStmt_PrepareV(self, query, args); + va_end(args); + + return res; +} + + + +/// Prepares the statement. +int SqlStmt_PrepareV(SqlStmt* self, const char* query, va_list args) +{ + if( self == NULL ) + return SQL_ERROR; + + SqlStmt_FreeResult(self); + StringBuf_Clear(&self->buf); + StringBuf_Vprintf(&self->buf, query, args); + if( mysql_stmt_prepare(self->stmt, StringBuf_Value(&self->buf), (unsigned long)StringBuf_Length(&self->buf)) ) + { + ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt)); + return SQL_ERROR; + } + self->bind_params = false; + + return SQL_SUCCESS; +} + + + +/// Prepares the statement. +int SqlStmt_PrepareStr(SqlStmt* self, const char* query) +{ + if( self == NULL ) + return SQL_ERROR; + + SqlStmt_FreeResult(self); + StringBuf_Clear(&self->buf); + StringBuf_AppendStr(&self->buf, query); + if( mysql_stmt_prepare(self->stmt, StringBuf_Value(&self->buf), (unsigned long)StringBuf_Length(&self->buf)) ) + { + ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt)); + return SQL_ERROR; + } + self->bind_params = false; + + return SQL_SUCCESS; +} + + + +/// Returns the number of parameters in the prepared statement. +size_t SqlStmt_NumParams(SqlStmt* self) +{ + if( self ) + return (size_t)mysql_stmt_param_count(self->stmt); + else + return 0; +} + + + +/// Binds a parameter to a buffer. +int SqlStmt_BindParam(SqlStmt* self, size_t idx, enum SqlDataType buffer_type, void* buffer, size_t buffer_len) +{ + if( self == NULL ) + return SQL_ERROR; + + if( !self->bind_params ) + {// initialize the bindings + size_t i; + size_t count; + + count = SqlStmt_NumParams(self); + if( self->max_params < count ) + { + self->max_params = count; + RECREATE(self->params, MYSQL_BIND, count); + } + memset(self->params, 0, count*sizeof(MYSQL_BIND)); + for( i = 0; i < count; ++i ) + self->params[i].buffer_type = MYSQL_TYPE_NULL; + self->bind_params = true; + } + if( idx < self->max_params ) + return Sql_P_BindSqlDataType(self->params+idx, buffer_type, buffer, buffer_len, NULL, NULL); + else + return SQL_SUCCESS;// out of range - ignore +} + + + +/// Executes the prepared statement. +int SqlStmt_Execute(SqlStmt* self) +{ + if( self == NULL ) + return SQL_ERROR; + + SqlStmt_FreeResult(self); + if( (self->bind_params && mysql_stmt_bind_param(self->stmt, self->params)) || + mysql_stmt_execute(self->stmt) ) + { + ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt)); + return SQL_ERROR; + } + self->bind_columns = false; + if( mysql_stmt_store_result(self->stmt) )// store all the data + { + ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt)); + return SQL_ERROR; + } + + return SQL_SUCCESS; +} + + + +/// Returns the number of the AUTO_INCREMENT column of the last INSERT/UPDATE statement. +uint64 SqlStmt_LastInsertId(SqlStmt* self) +{ + if( self ) + return (uint64)mysql_stmt_insert_id(self->stmt); + else + return 0; +} + + + +/// Returns the number of columns in each row of the result. +size_t SqlStmt_NumColumns(SqlStmt* self) +{ + if( self ) + return (size_t)mysql_stmt_field_count(self->stmt); + else + return 0; +} + + + +/// Binds the result of a column to a buffer. +int SqlStmt_BindColumn(SqlStmt* self, size_t idx, enum SqlDataType buffer_type, void* buffer, size_t buffer_len, uint32* out_length, int8* out_is_null) +{ + if( self == NULL ) + return SQL_ERROR; + + if( buffer_type == SQLDT_STRING || buffer_type == SQLDT_ENUM ) + { + if( buffer_len < 1 ) + { + ShowDebug("SqlStmt_BindColumn: buffer_len(%d) is too small, no room for the nul-terminator\n", buffer_len); + return SQL_ERROR; + } + --buffer_len;// nul-terminator + } + if( !self->bind_columns ) + {// initialize the bindings + size_t i; + size_t cols; + + cols = SqlStmt_NumColumns(self); + if( self->max_columns < cols ) + { + self->max_columns = cols; + RECREATE(self->columns, MYSQL_BIND, cols); + RECREATE(self->column_lengths, s_column_length, cols); + } + memset(self->columns, 0, cols*sizeof(MYSQL_BIND)); + memset(self->column_lengths, 0, cols*sizeof(s_column_length)); + for( i = 0; i < cols; ++i ) + self->columns[i].buffer_type = MYSQL_TYPE_NULL; + self->bind_columns = true; + } + if( idx < self->max_columns ) + { + self->column_lengths[idx].out_length = out_length; + return Sql_P_BindSqlDataType(self->columns+idx, buffer_type, buffer, buffer_len, &self->column_lengths[idx].length, out_is_null); + } + else + { + return SQL_SUCCESS;// out of range - ignore + } +} + + + +/// Returns the number of rows in the result. +uint64 SqlStmt_NumRows(SqlStmt* self) +{ + if( self ) + return (uint64)mysql_stmt_num_rows(self->stmt); + else + return 0; +} + + + +/// Fetches the next row. +int SqlStmt_NextRow(SqlStmt* self) +{ + int err; + size_t i; + size_t cols; + MYSQL_BIND* column; + unsigned long length; + + if( self == NULL ) + return SQL_ERROR; + + // bind columns + if( self->bind_columns && mysql_stmt_bind_result(self->stmt, self->columns) ) + err = 1;// error binding columns + else + err = mysql_stmt_fetch(self->stmt);// fetch row + + // check for errors + if( err == MYSQL_NO_DATA ) + return SQL_NO_DATA; + if( err == MYSQL_DATA_TRUNCATED ) + { + my_bool truncated; + + if( !self->bind_columns ) + { + ShowSQL("DB error - data truncated (unknown source, columns are not bound)\n"); + return SQL_ERROR; + } + + // find truncated column + cols = SqlStmt_NumColumns(self); + for( i = 0; i < cols; ++i ) + { + column = &self->columns[i]; + column->error = &truncated; + mysql_stmt_fetch_column(self->stmt, column, (unsigned int)i, 0); + column->error = NULL; + if( truncated ) + {// report truncated column + MYSQL_RES* meta; + MYSQL_FIELD* field; + + meta = mysql_stmt_result_metadata(self->stmt); + field = mysql_fetch_field_direct(meta, (unsigned int)i); + ShowSQL("DB error - data of field '%s' was truncated.\n", field->name); + ShowDebug("column - %lu\n", (unsigned long)i); + Sql_P_ShowDebugMysqlFieldInfo("data - ", field->type, field->flags&UNSIGNED_FLAG, self->column_lengths[i].length, ""); + if( column->buffer_type == MYSQL_TYPE_STRING ) + Sql_P_ShowDebugMysqlFieldInfo("buffer - ", column->buffer_type, column->is_unsigned, column->buffer_length, "+1(nul-terminator)"); + else + Sql_P_ShowDebugMysqlFieldInfo("buffer - ", column->buffer_type, column->is_unsigned, column->buffer_length, ""); + mysql_free_result(meta); + return SQL_ERROR; + } + } + ShowSQL("DB error - data truncated (unknown source)\n"); + return SQL_ERROR; + } + if( err ) + { + ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt)); + return SQL_ERROR; + } + + // propagate column lengths and clear unused parts of string/enum/blob buffers + cols = SqlStmt_NumColumns(self); + for( i = 0; i < cols; ++i ) + { + length = self->column_lengths[i].length; + if( self->column_lengths[i].out_length ) + *self->column_lengths[i].out_length = (uint32)length; + column = &self->columns[i]; + if( column->buffer_type == MYSQL_TYPE_STRING ) + {// clear unused part of the string/enum buffer (and nul-terminate) + memset((char*)column->buffer + length, 0, column->buffer_length - length + 1); + } + else if( column->buffer_type == MYSQL_TYPE_BLOB && length < column->buffer_length ) + {// clear unused part of the blob buffer + memset((char*)column->buffer + length, 0, column->buffer_length - length); + } + } + + return SQL_SUCCESS; +} + + + +/// Frees the result of the statement execution. +void SqlStmt_FreeResult(SqlStmt* self) +{ + if( self ) + mysql_stmt_free_result(self->stmt); +} + + + +/// Shows debug information (with statement). +void SqlStmt_ShowDebug_(SqlStmt* self, const char* debug_file, const unsigned long debug_line) +{ + if( self == NULL ) + ShowDebug("at %s:%lu - self is NULL\n", debug_file, debug_line); + if( StringBuf_Length(&self->buf) > 0 ) + ShowDebug("at %s:%lu - %s\n", debug_file, debug_line, StringBuf_Value(&self->buf)); + else + ShowDebug("at %s:%lu\n", debug_file, debug_line); +} + + + +/// Frees a SqlStmt returned by SqlStmt_Malloc. +void SqlStmt_Free(SqlStmt* self) +{ + if( self ) + { + SqlStmt_FreeResult(self); + StringBuf_Destroy(&self->buf); + mysql_stmt_close(self->stmt); + if( self->params ) + aFree(self->params); + if( self->columns ) + { + aFree(self->columns); + aFree(self->column_lengths); + } + aFree(self); + } +} diff --git a/src/common/sql.h b/src/common/sql.h new file mode 100644 index 000000000..ef76b2ec5 --- /dev/null +++ b/src/common/sql.h @@ -0,0 +1,343 @@ +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _COMMON_SQL_H_ +#define _COMMON_SQL_H_ + +#ifndef _CBASETYPES_H_ +#include "../common/cbasetypes.h" +#endif +#include <stdarg.h>// va_list + + + +// Return codes +#define SQL_ERROR -1 +#define SQL_SUCCESS 0 +#define SQL_NO_DATA 100 + + + +/// Data type identifier. +/// String, enum and blob data types need the buffer length specified. +enum SqlDataType +{ + SQLDT_NULL, + // fixed size + SQLDT_INT8, + SQLDT_INT16, + SQLDT_INT32, + SQLDT_INT64, + SQLDT_UINT8, + SQLDT_UINT16, + SQLDT_UINT32, + SQLDT_UINT64, + // platform dependent size + SQLDT_CHAR, + SQLDT_SHORT, + SQLDT_INT, + SQLDT_LONG, + SQLDT_LONGLONG, + SQLDT_UCHAR, + SQLDT_USHORT, + SQLDT_UINT, + SQLDT_ULONG, + SQLDT_ULONGLONG, + // floating point + SQLDT_FLOAT, + SQLDT_DOUBLE, + // other + SQLDT_STRING, + SQLDT_ENUM, + // Note: An ENUM is a string with restricted values. When an invalid value + // is inserted, it is saved as an empty string (numerical value 0). + SQLDT_BLOB, + SQLDT_LASTID +}; + +struct Sql;// Sql handle (private access) +struct SqlStmt;// Sql statement (private access) + +typedef enum SqlDataType SqlDataType; +typedef struct Sql Sql; +typedef struct SqlStmt SqlStmt; + + +/// Allocates and initializes a new Sql handle. +struct Sql* Sql_Malloc(void); + + + +/// Establishes a connection. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int Sql_Connect(Sql* self, const char* user, const char* passwd, const char* host, uint16 port, const char* db); + + + + +/// Retrieves the timeout of the connection. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int Sql_GetTimeout(Sql* self, uint32* out_timeout); + + + + +/// Retrieves the name of the columns of a table into out_buf, with the separator after each name. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int Sql_GetColumnNames(Sql* self, const char* table, char* out_buf, size_t buf_len, char sep); + + + + +/// Changes the encoding of the connection. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int Sql_SetEncoding(Sql* self, const char* encoding); + + + +/// Pings the connection. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int Sql_Ping(Sql* self); + + + +/// Escapes a string. +/// The output buffer must be at least strlen(from)*2+1 in size. +/// +/// @return The size of the escaped string +size_t Sql_EscapeString(Sql* self, char* out_to, const char* from); + + + +/// Escapes a string. +/// The output buffer must be at least from_len*2+1 in size. +/// +/// @return The size of the escaped string +size_t Sql_EscapeStringLen(Sql* self, char* out_to, const char* from, size_t from_len); + + + +/// Executes a query. +/// Any previous result is freed. +/// The query is constructed as if it was sprintf. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int Sql_Query(Sql* self, const char* query, ...); + + + +/// Executes a query. +/// Any previous result is freed. +/// The query is constructed as if it was svprintf. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int Sql_QueryV(Sql* self, const char* query, va_list args); + + + +/// Executes a query. +/// Any previous result is freed. +/// The query is used directly. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int Sql_QueryStr(Sql* self, const char* query); + + + +/// Returns the number of the AUTO_INCREMENT column of the last INSERT/UPDATE query. +/// +/// @return Value of the auto-increment column +uint64 Sql_LastInsertId(Sql* self); + + + +/// Returns the number of columns in each row of the result. +/// +/// @return Number of columns +uint32 Sql_NumColumns(Sql* self); + + + +/// Returns the number of rows in the result. +/// +/// @return Number of rows +uint64 Sql_NumRows(Sql* self); + + + +/// Fetches the next row. +/// The data of the previous row is no longer valid. +/// +/// @return SQL_SUCCESS, SQL_ERROR or SQL_NO_DATA +int Sql_NextRow(Sql* self); + + + +/// Gets the data of a column. +/// The data remains valid until the next row is fetched or the result is freed. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int Sql_GetData(Sql* self, size_t col, char** out_buf, size_t* out_len); + + + +/// Frees the result of the query. +void Sql_FreeResult(Sql* self); + + + +#if defined(SQL_REMOVE_SHOWDEBUG) +#define Sql_ShowDebug(self) (void)0 +#else +#define Sql_ShowDebug(self) Sql_ShowDebug_(self, __FILE__, __LINE__) +#endif +/// Shows debug information (last query). +void Sql_ShowDebug_(Sql* self, const char* debug_file, const unsigned long debug_line); + + + +/// Frees a Sql handle returned by Sql_Malloc. +void Sql_Free(Sql* self); + + + +/////////////////////////////////////////////////////////////////////////////// +// Prepared Statements +/////////////////////////////////////////////////////////////////////////////// +// Parameters are placed in the statement by embedding question mark ('?') +// characters into the query at the appropriate positions. +// The markers are legal only in places where they represent data. +// The markers cannot be inside quotes. Quotes will be added automatically +// when they are required. +// +// example queries with parameters: +// 1) SELECT col FROM table WHERE id=? +// 2) INSERT INTO table(col1,col2) VALUES(?,?) + + + +/// Allocates and initializes a new SqlStmt handle. +/// It uses the connection of the parent Sql handle. +/// Queries in Sql and SqlStmt are independent and don't affect each other. +/// +/// @return SqlStmt handle or NULL if an error occured +struct SqlStmt* SqlStmt_Malloc(Sql* sql); + + + +/// Prepares the statement. +/// Any previous result is freed and all parameter bindings are removed. +/// The query is constructed as if it was sprintf. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int SqlStmt_Prepare(SqlStmt* self, const char* query, ...); + + + +/// Prepares the statement. +/// Any previous result is freed and all parameter bindings are removed. +/// The query is constructed as if it was svprintf. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int SqlStmt_PrepareV(SqlStmt* self, const char* query, va_list args); + + + +/// Prepares the statement. +/// Any previous result is freed and all parameter bindings are removed. +/// The query is used directly. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int SqlStmt_PrepareStr(SqlStmt* self, const char* query); + + + +/// Returns the number of parameters in the prepared statement. +/// +/// @return Number or paramenters +size_t SqlStmt_NumParams(SqlStmt* self); + + + +/// Binds a parameter to a buffer. +/// The buffer data will be used when the statement is executed. +/// All parameters should have bindings. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int SqlStmt_BindParam(SqlStmt* self, size_t idx, SqlDataType buffer_type, void* buffer, size_t buffer_len); + + + +/// Executes the prepared statement. +/// Any previous result is freed and all column bindings are removed. +/// +/// @return SQL_SUCCESS or SQL_ERROR +int SqlStmt_Execute(SqlStmt* self); + + + +/// Returns the number of the AUTO_INCREMENT column of the last INSERT/UPDATE statement. +/// +/// @return Value of the auto-increment column +uint64 SqlStmt_LastInsertId(SqlStmt* self); + + + +/// Returns the number of columns in each row of the result. +/// +/// @return Number of columns +size_t SqlStmt_NumColumns(SqlStmt* self); + + + +/// Binds the result of a column to a buffer. +/// The buffer will be filled with data when the next row is fetched. +/// For string/enum buffer types there has to be enough space for the data +/// and the nul-terminator (an extra byte). +/// +/// @return SQL_SUCCESS or SQL_ERROR +int SqlStmt_BindColumn(SqlStmt* self, size_t idx, SqlDataType buffer_type, void* buffer, size_t buffer_len, uint32* out_length, int8* out_is_null); + + + +/// Returns the number of rows in the result. +/// +/// @return Number of rows +uint64 SqlStmt_NumRows(SqlStmt* self); + + + +/// Fetches the next row. +/// All column bindings will be filled with data. +/// +/// @return SQL_SUCCESS, SQL_ERROR or SQL_NO_DATA +int SqlStmt_NextRow(SqlStmt* self); + + + +/// Frees the result of the statement execution. +void SqlStmt_FreeResult(SqlStmt* self); + + + +#if defined(SQL_REMOVE_SHOWDEBUG) +#define SqlStmt_ShowDebug(self) (void)0 +#else +#define SqlStmt_ShowDebug(self) SqlStmt_ShowDebug_(self, __FILE__, __LINE__) +#endif +/// Shows debug information (with statement). +void SqlStmt_ShowDebug_(SqlStmt* self, const char* debug_file, const unsigned long debug_line); + + + +/// Frees a SqlStmt returned by SqlStmt_Malloc. +void SqlStmt_Free(SqlStmt* self); + + + +#endif /* _COMMON_SQL_H_ */ diff --git a/src/common/strlib.c b/src/common/strlib.c index a683dc100..4f204a768 100644 --- a/src/common/strlib.c +++ b/src/common/strlib.c @@ -3,7 +3,6 @@ #include "../common/cbasetypes.h" #include "../common/malloc.h" -#include "../common/utils.h" #include "strlib.h" #include <stdio.h> @@ -258,7 +257,7 @@ int e_mail_check(char* email) { char ch; char* last_arobas; - int len = strlen(email); + size_t len = strlen(email); // athena limits if (len < 3 || len > 39) @@ -310,3 +309,134 @@ char* safestrncpy(char* dst, const char* src, size_t n) ret[n - 1] = '\0'; return ret; } + + +///////////////////////////////////////////////////////////////////// +// StringBuf - dynamic string +// +// @author MouseJstr (original) + +/// Allocates a StringBuf +StringBuf* StringBuf_Malloc() +{ + StringBuf* self; + CREATE(self, StringBuf, 1); + StringBuf_Init(self); + return self; +} + +/// Initializes a previously allocated StringBuf +void StringBuf_Init(StringBuf* self) +{ + self->max_ = 1024; + self->ptr_ = self->buf_ = (char*)aMallocA(self->max_ + 1); +} + +/// Appends the result of printf to the StringBuf +int StringBuf_Printf(StringBuf* self, const char* fmt, ...) +{ + int len; + va_list ap; + + va_start(ap, fmt); + len = StringBuf_Vprintf(self, fmt, ap); + va_end(ap); + + return len; +} + +/// Appends the result of vprintf to the StringBuf +int StringBuf_Vprintf(StringBuf* self, const char* fmt, va_list ap) +{ + int n, size, off; + + for(;;) + { + /* Try to print in the allocated space. */ + size = self->max_ - (self->ptr_ - self->buf_); + n = vsnprintf(self->ptr_, size, fmt, ap); + /* If that worked, return the length. */ + if( n > -1 && n < size ) + { + self->ptr_ += n; + return (int)(self->ptr_ - self->buf_); + } + /* Else try again with more space. */ + self->max_ *= 2; // twice the old size + off = (int)(self->ptr_ - self->buf_); + self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1); + self->ptr_ = self->buf_ + off; + } +} + +/// Appends the contents of another StringBuf to the StringBuf +int StringBuf_Append(StringBuf* self, const StringBuf* sbuf) +{ + int available = self->max_ - (self->ptr_ - self->buf_); + int needed = (int)(sbuf->ptr_ - sbuf->buf_); + + if( needed >= available ) + { + int off = (int)(self->ptr_ - self->buf_); + self->max_ += needed; + self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1); + self->ptr_ = self->buf_ + off; + } + + memcpy(self->ptr_, sbuf->buf_, needed); + self->ptr_ += needed; + return (int)(self->ptr_ - self->buf_); +} + +// Appends str to the StringBuf +int StringBuf_AppendStr(StringBuf* self, const char* str) +{ + int available = self->max_ - (self->ptr_ - self->buf_); + int needed = (int)strlen(str); + + if( needed >= available ) + {// not enough space, expand the buffer (minimum expansion = 1024) + int off = (int)(self->ptr_ - self->buf_); + self->max_ += max(needed, 1024); + self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1); + self->ptr_ = self->buf_ + off; + } + + memcpy(self->ptr_, str, needed); + self->ptr_ += needed; + return (int)(self->ptr_ - self->buf_); +} + +// Returns the length of the data in the Stringbuf +int StringBuf_Length(StringBuf* self) +{ + return (int)(self->ptr_ - self->buf_); +} + +/// Returns the data in the StringBuf +char* StringBuf_Value(StringBuf* self) +{ + *self->ptr_ = '\0'; + return self->buf_; +} + +/// Clears the contents of the StringBuf +void StringBuf_Clear(StringBuf* self) +{ + self->ptr_ = self->buf_; +} + +/// Destroys the StringBuf +void StringBuf_Destroy(StringBuf* self) +{ + aFree(self->buf_); + self->ptr_ = self->buf_ = 0; + self->max_ = 0; +} + +// Frees a StringBuf returned by StringBuf_Malloc +void StringBuf_Free(StringBuf* self) +{ + StringBuf_Destroy(self); + aFree(self); +} diff --git a/src/common/strlib.h b/src/common/strlib.h index 42fb75ab2..ee473f466 100644 --- a/src/common/strlib.h +++ b/src/common/strlib.h @@ -7,6 +7,7 @@ #ifndef _CBASETYPES_H_ #include "../common/cbasetypes.h" #endif +#include <stdarg.h> char* jstrescape (char* pt); char* jstrescapecpy (char* pt, const char* spt); @@ -33,4 +34,25 @@ int config_switch(const char* str); /// always nul-terminates the string char* safestrncpy(char* dst, const char* src, size_t n); +/// StringBuf - dynamic string +struct StringBuf +{ + char *buf_; + char *ptr_; + unsigned int max_; +}; +typedef struct StringBuf StringBuf; + +StringBuf* StringBuf_Malloc(void); +void StringBuf_Init(StringBuf* self); +int StringBuf_Printf(StringBuf* self, const char* fmt, ...); +int StringBuf_Vprintf(StringBuf* self, const char* fmt, va_list args); +int StringBuf_Append(StringBuf* self, const StringBuf *sbuf); +int StringBuf_AppendStr(StringBuf* self, const char* str); +int StringBuf_Length(StringBuf* self); +char* StringBuf_Value(StringBuf* self); +void StringBuf_Clear(StringBuf* self); +void StringBuf_Destroy(StringBuf* self); +void StringBuf_Free(StringBuf* self); + #endif /* _STRLIB_H_ */ diff --git a/src/common/utils.c b/src/common/utils.c index 15d1d2e68..354bd3814 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -55,118 +55,6 @@ void dump(FILE* fp, const unsigned char* buffer, int length) fprintf(fp, "\n"); } -// Allocate a StringBuf [MouseJstr] -struct StringBuf * StringBuf_Malloc() -{ - struct StringBuf * ret = (struct StringBuf *) aMallocA(sizeof(struct StringBuf)); - StringBuf_Init(ret); - return ret; -} - -// Initialize a previously allocated StringBuf [MouseJstr] -void StringBuf_Init(struct StringBuf * sbuf) { - sbuf->max_ = 1024; - sbuf->ptr_ = sbuf->buf_ = (char *) aMallocA(sbuf->max_ + 1); -} - -// vprintf into a StringBuf, moving the pointer [MouseJstr] -int StringBuf_Vprintf(struct StringBuf *sbuf,const char *fmt,va_list ap) -{ - int n, size, off; - - while (1) { - /* Try to print in the allocated space. */ - size = sbuf->max_ - (sbuf->ptr_ - sbuf->buf_); - n = vsnprintf (sbuf->ptr_, size, fmt, ap); - /* If that worked, return the length. */ - if (n > -1 && n < size) { - sbuf->ptr_ += n; - return (int)(sbuf->ptr_ - sbuf->buf_); - } - /* Else try again with more space. */ - sbuf->max_ *= 2; // twice the old size - off = (int)(sbuf->ptr_ - sbuf->buf_); - sbuf->buf_ = (char *) aRealloc(sbuf->buf_, sbuf->max_ + 1); - sbuf->ptr_ = sbuf->buf_ + off; - } -} - -// printf into a StringBuf, moving the pointer [MouseJstr] -int StringBuf_Printf(struct StringBuf *sbuf,const char *fmt,...) -{ - int len; - va_list ap; - - va_start(ap,fmt); - len = StringBuf_Vprintf(sbuf,fmt,ap); - va_end(ap); - - return len; -} - -// Append buf2 onto the end of buf1 [MouseJstr] -int StringBuf_Append(struct StringBuf *buf1,const struct StringBuf *buf2) -{ - int buf1_avail = buf1->max_ - (buf1->ptr_ - buf1->buf_); - int size2 = (int)(buf2->ptr_ - buf2->buf_); - - if (size2 >= buf1_avail) { - int off = (int)(buf1->ptr_ - buf1->buf_); - buf1->max_ += size2; - buf1->buf_ = (char *) aRealloc(buf1->buf_, buf1->max_ + 1); - buf1->ptr_ = buf1->buf_ + off; - } - - memcpy(buf1->ptr_, buf2->buf_, size2); - buf1->ptr_ += size2; - return (int)(buf1->ptr_ - buf1->buf_); -} - -// Appends str onto the end of buf -int StringBuf_AppendStr(struct StringBuf* sbuf, const char* str) -{ - int available = sbuf->max_ - (sbuf->ptr_ - sbuf->buf_); - int size = (int)strlen(str); - - if( size >= available ) - {// not enough space, expand the buffer (minimum expansion = 1024) - int off = (int)(sbuf->ptr_ - sbuf->buf_); - sbuf->max_ += max(size, 1024); - sbuf->buf_ = (char *) aRealloc(sbuf->buf_, sbuf->max_ + 1); - sbuf->ptr_ = sbuf->buf_ + off; - } - - memcpy(sbuf->ptr_, str, size); - sbuf->ptr_ += size; - return (int)(sbuf->ptr_ - sbuf->buf_); -} - -// Returns the length of the data in a Stringbuf -int StringBuf_Length(struct StringBuf *sbuf) -{ - return (int)(sbuf->ptr_ - sbuf->buf_); -} - -// Destroy a StringBuf [MouseJstr] -void StringBuf_Destroy(struct StringBuf *sbuf) -{ - aFree(sbuf->buf_); - sbuf->ptr_ = sbuf->buf_ = 0; -} - -// Free a StringBuf returned by StringBuf_Malloc [MouseJstr] -void StringBuf_Free(struct StringBuf *sbuf) -{ - StringBuf_Destroy(sbuf); - aFree(sbuf); -} - -// Return the built string from the StringBuf [MouseJstr] -char * StringBuf_Value(struct StringBuf *sbuf) -{ - *sbuf->ptr_ = '\0'; - return sbuf->buf_; -} #ifdef WIN32 @@ -293,35 +181,43 @@ void findfile(const char *p, const char *pat, void (func)(const char*)) } #endif -uint8 GetByte(uint32 val, size_t num) +uint8 GetByte(uint32 val, int idx) { - switch( num ) + switch( idx ) { - case 0: return (uint8)((val & 0x000000FF) ); - case 1: return (uint8)((val & 0x0000FF00) >> 0x08); - case 2: return (uint8)((val & 0x00FF0000) >> 0x10); - case 3: return (uint8)((val & 0xFF000000) >> 0x18); - default: return 0; //better throw something here + case 0: return (uint8)( (val & 0x000000FF) ); + case 1: return (uint8)( (val & 0x0000FF00) >> 0x08 ); + case 2: return (uint8)( (val & 0x00FF0000) >> 0x10 ); + case 3: return (uint8)( (val & 0xFF000000) >> 0x18 ); + default: +#if defined(DEBUG) + ShowDebug("GetByte: invalid index (idx=%d)\n", idx); +#endif + return 0; } } -uint16 GetWord(uint32 val, size_t num) + +uint16 GetWord(uint32 val, int idx) { - switch( num ) + switch( idx ) { - case 0: return (uint16)((val & 0x0000FFFF) ); - case 1: return (uint16)((val & 0xFFFF0000) >> 0x10); - default: return 0; //better throw something here + case 0: return (uint16)( (val & 0x0000FFFF) ); + case 1: return (uint16)( (val & 0xFFFF0000) >> 0x10 ); + default: +#if defined(DEBUG) + ShowDebug("GetWord: invalid index (idx=%d)\n", idx); +#endif + return 0; } } uint16 MakeWord(uint8 byte0, uint8 byte1) { - return - ((uint16)(byte0 ))| - ((uint16)(byte1 << 0x08)); + return byte0 | (byte1 << 0x08); } + uint32 MakeDWord(uint16 word0, uint16 word1) { return - ((uint32)(word0 ))| - ((uint32)(word1 << 0x10)); + ( (uint32)(word0 ) )| + ( (uint32)(word1 << 0x10) ); } diff --git a/src/common/utils.h b/src/common/utils.h index 898a63ab4..21e8e090c 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -4,29 +4,15 @@ #ifndef _UTILS_H_ #define _UTILS_H_ -#include <stdio.h> -#include <stdarg.h> +#ifndef _CBASETYPES_H_ +#include "../common/cbasetypes.h" +#endif + +#include <stdio.h> // FILE* // generate a hex dump of the first 'length' bytes of 'buffer' void dump(FILE* fp, const unsigned char* buffer, int length); -struct StringBuf { - char *buf_; - char *ptr_; - unsigned int max_; -}; - -struct StringBuf * StringBuf_Malloc(void); -void StringBuf_Init(struct StringBuf *); -int StringBuf_Vprintf(struct StringBuf *,const char *,va_list); -int StringBuf_Printf(struct StringBuf *,const char *,...); -int StringBuf_Append(struct StringBuf *,const struct StringBuf *); -int StringBuf_AppendStr(struct StringBuf* sbuf, const char* str); -int StringBuf_Length(struct StringBuf* sbuf); -char * StringBuf_Value(struct StringBuf *); -void StringBuf_Destroy(struct StringBuf *); -void StringBuf_Free(struct StringBuf *); - void findfile(const char *p, const char *pat, void (func)(const char*)); //Caps values to min/max @@ -36,8 +22,8 @@ void findfile(const char *p, const char *pat, void (func)(const char*)); // byte word dword access [Shinomori] ////////////////////////////////////////////////////////////////////////// -extern uint8 GetByte(uint32 val, size_t num); -extern uint16 GetWord(uint32 val, size_t num); +extern uint8 GetByte(uint32 val, int idx); +extern uint16 GetWord(uint32 val, int idx); extern uint16 MakeWord(uint8 byte0, uint8 byte1); extern uint32 MakeDWord(uint16 word0, uint16 word1); |