/** @file
Parser MySQL driver.
Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)
Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)
$Id: parser3mysql.C,v 1.15 2001/04/26 15:09:08 paf Exp $
*/
#include "config_includes.h"
#include "pa_sql_driver.h"
#define NO_CLIENT_LONG_LONG
#include "mysql.h"
#include "ltdl.h"
#define MAX_STRING 0x400
#define MAX_NUMBER 20
#if _MSC_VER
# define snprintf _snprintf
#endif
static char *lsplit(char *string, char delim) {
if(string) {
char *v=strchr(string, delim);
if(v) {
*v=0;
return v+1;
}
}
return 0;
}
/**
MySQL server driver
@todo figure out about memory for errors:
static=add multithread locks;
dynamic=who should free it up?
*/
class MySQL_Driver : public SQL_Driver {
public:
MySQL_Driver() : SQL_Driver() {
}
/// get api version
int api_version() { return SQL_DRIVER_API_VERSION; }
/// initialize driver by loading sql dynamic link library
const char *initialize(const char *dlopen_file_spec) {
return dlink(dlopen_file_spec);
}
/// connect
void connect(
char *url, /**< @b user:pass@host[:port]/database/charset
3.23.22b
Currently the only option for character_set_name is cp1251_koi8 */
void **connection ///< output: MYSQL *
) {
char *user=url;
char *host=lsplit(user, '@');
char *db=lsplit(host, '/');
char *pwd=lsplit(user, ':');
char *error_pos=0;
char *port_cstr=lsplit(host, ':');
int port=port_cstr?strtol(port_cstr, &error_pos, 0):0;
char *charset=lsplit(db, '/');
MYSQL *mysql=mysql_init(NULL);
if(!mysql_real_connect(mysql,
host, user, pwd, db, port?port:MYSQL_PORT, NULL, 0))
services->_throw(mysql_error(mysql));
if(charset) {
// set charset
char statement[MAX_STRING]="set CHARACTER SET "; // cp1251_koi8
strncat(statement, charset, MAX_STRING);
if(mysql_query(mysql, statement))
services->_throw(mysql_error(mysql));
(*mysql_store_result)(mysql); // throw out the result [don't need but must call]
}
*(MYSQL **)connection=mysql;
}
void disconnect(void *connection) {
mysql_close((MYSQL *)connection);
}
void commit(void *connection) {}
void rollback(void *connection) {}
bool ping(void *connection) {
return mysql_ping((MYSQL *)connection)==0;
}
unsigned int quote(void *connection,
char *to, const char *from, unsigned int length) {
/*
3.23.22b
You must allocate the to buffer to be at least length*2+1 bytes long.
(In the worse case, each character may need to be encoded as using two bytes,
and you need room for the terminating null byte.)
it's already UNTAINT_TIMES_BIGGER
*/
return (*mysql_escape_string)(to, from, length);
}
void query(void *connection,
const char *astatement, unsigned long offset, unsigned long limit,
unsigned int *column_count, Cell **columns,
unsigned long *row_count, Cell ***rows) {
MYSQL *mysql=(MYSQL *)connection;
MYSQL_RES *res=NULL;
const char *statement;
if(offset || limit) {
size_t statement_size=strlen(astatement);
char *statement_limited=(char *)services->malloc(
statement_size+MAX_NUMBER*2+8/* limit #,#*/+1);
char *cur=statement_limited;
memcpy(cur, astatement, statement_size); cur+=statement_size;
cur+=sprintf(cur, " limit ");
if(offset)
cur+=snprintf(cur, MAX_NUMBER+1, "%u,", offset);
if(limit)
cur+=snprintf(cur, MAX_NUMBER, "%u", limit);
statement=statement_limited;
} else
statement=astatement;
if(mysql_query(mysql, statement))
services->_throw(mysql_error(mysql));
if(!(res=mysql_store_result(mysql)) && mysql_field_count(mysql))
services->_throw(mysql_error(mysql));
if(!res) {
// empty result
*row_count=0;
*column_count=0;
return;
}
*column_count=mysql_num_fields(res);
if(!*column_count) // old client
*column_count=mysql_field_count(mysql);
*columns=(Cell *)services->malloc(sizeof(Cell)*(*column_count));
*row_count=(unsigned long)mysql_num_rows(res);
*rows=(Cell **)services->malloc(sizeof(Cell *)*(*row_count));
for(unsigned int i=0; i<(*column_count); i++){
MYSQL_FIELD *field=mysql_fetch_field(res);
size_t size=strlen(field->name);
(*columns)[i].size=size;
(*columns)[i].ptr=services->malloc(size);
memcpy((*columns)[i].ptr, field->name, size);
}
for(unsigned long r=0; r<(*row_count); r++)
if(MYSQL_ROW mysql_row=mysql_fetch_row(res)) { // never false..
unsigned long *lengths=mysql_fetch_lengths(res);
Cell *row=(Cell *)malloc(sizeof(Cell)*(*column_count));
(*rows)[r]=row;
for(unsigned int i=0; i<(*column_count); i++){
size_t size=(size_t)lengths[i];
row[i].size=size;
row[i].ptr=services->malloc(size);
memcpy(row[i].ptr, mysql_row[i], size);
}
}
mysql_free_result(res);
}
private: // mysql client library funcs
typedef MYSQL* (STDCALL *t_mysql_init)(MYSQL *); t_mysql_init mysql_init;
typedef MYSQL_RES* (STDCALL *t_mysql_store_result)(MYSQL *); t_mysql_store_result mysql_store_result;
typedef int (STDCALL *t_mysql_query)(MYSQL *, const char *q); t_mysql_query mysql_query;
typedef char * (STDCALL *t_mysql_error)(MYSQL *); t_mysql_error mysql_error;
static char* STDCALL subst_mysql_error(MYSQL *mysql) { return (mysql)->net.last_error; }
typedef MYSQL* (STDCALL *t_mysql_real_connect)(MYSQL *, const char *host,
const char *user,
const char *passwd,
const char *db,
unsigned int port,
const char *unix_socket,
unsigned int clientflag); t_mysql_real_connect mysql_real_connect;
typedef void (STDCALL *t_mysql_close)(MYSQL *); t_mysql_close mysql_close;
typedef int (STDCALL *t_mysql_ping)(MYSQL *); t_mysql_ping mysql_ping;
typedef unsigned long (STDCALL *t_mysql_escape_string)(char *to,const char *from,
unsigned long from_length); t_mysql_escape_string mysql_escape_string;
typedef void (STDCALL *t_mysql_free_result)(MYSQL_RES *result); t_mysql_free_result mysql_free_result;
typedef unsigned long* (STDCALL *t_mysql_fetch_lengths)(MYSQL_RES *result); t_mysql_fetch_lengths mysql_fetch_lengths;
typedef MYSQL_ROW (STDCALL *t_mysql_fetch_row)(MYSQL_RES *result); t_mysql_fetch_row mysql_fetch_row;
typedef MYSQL_FIELD* (STDCALL *t_mysql_fetch_field)(MYSQL_RES *result); t_mysql_fetch_field mysql_fetch_field;
typedef my_ulonglong (STDCALL *t_mysql_num_rows)(MYSQL_RES *); t_mysql_num_rows mysql_num_rows;
static my_ulonglong STDCALL subst_mysql_num_rows(MYSQL_RES *res) { return res->row_count; }
typedef unsigned int (STDCALL *t_mysql_num_fields)(MYSQL_RES *); t_mysql_num_fields mysql_num_fields;
static unsigned int STDCALL subst_mysql_num_fields(MYSQL_RES *res) { return res->field_count; }
typedef unsigned int (STDCALL *t_mysql_field_count)(MYSQL *); t_mysql_field_count mysql_field_count;
static unsigned int STDCALL subst_mysql_field_count(MYSQL *mysql) { return mysql->field_count; }
private: // mysql client library funcs linking
const char *dlink(const char *dlopen_file_spec) {
lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
if (!handle)
return "can not open the dynamic link module";
#define DSLINK(name, action) \
name=(t_##name)lt_dlsym(handle, #name); \
if(!name) \
action;
#define DLINK(name) DSLINK(name, return "function " #name " was not found")
#define SLINK(name) DSLINK(name, name=subst_##name)
DLINK(mysql_init);
DLINK(mysql_store_result);
DLINK(mysql_query);
SLINK(mysql_error);
DLINK(mysql_real_connect);
DLINK(mysql_close);
DLINK(mysql_ping);
DLINK(mysql_escape_string);
DLINK(mysql_free_result);
DLINK(mysql_fetch_lengths);
DLINK(mysql_fetch_row);
DLINK(mysql_fetch_field);
SLINK(mysql_num_rows);
SLINK(mysql_num_fields);
SLINK(mysql_field_count);
return 0;
}
};
extern "C" SQL_Driver *create() {
return new MySQL_Driver();
}
E-mail: