summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorFlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec>2007-09-20 11:09:36 +0000
committerFlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec>2007-09-20 11:09:36 +0000
commit5245e666a09df5f401c1329bf5ee1fc1b09b1d16 (patch)
treedcf032743e890fddd400b268b75a0868976b0a0b /src/common
parentd23c508bcc38520970156e5e25f14b03714878eb (diff)
downloadhercules-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.c1
-rw-r--r--src/common/ers.c3
-rw-r--r--src/common/ers.h2
-rw-r--r--src/common/md5calc.c2
-rw-r--r--src/common/mmo.h7
-rw-r--r--src/common/plugins.c4
-rw-r--r--src/common/showmsg.c21
-rw-r--r--src/common/socket.c13
-rw-r--r--src/common/socket.h4
-rw-r--r--src/common/sql.c851
-rw-r--r--src/common/sql.h343
-rw-r--r--src/common/strlib.c134
-rw-r--r--src/common/strlib.h22
-rw-r--r--src/common/utils.c154
-rw-r--r--src/common/utils.h28
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);