Home modules.gotpike.org
Username: Password: [Create Account]
[Forgot Password?]

Modules

ADT
Database
GTK2
GUI
IP
PiJAX
Public
Sql
Stdio
Subversion
System
Tools
Xosd
lua
v4l2
wx

Recent Changes

Public.Parser.XML2 1.50
Public.ZeroMQ 1.1
Public.Template.Mustache 1.0
Public.Protocols.XMPP 1.4
Sql.Provider.jdbc 1.0

Popular Downloads

Public.Parser.JSON2 1.0
Public.Parser.JSON 0.2
GTK2 2.23
Public.Web.FCGI 1.8
Public.Parser.XML2 1.48


Module Information
Sql.Provider.MySQL
Viewing contents of Sql_Provider_MySQL-0.2/MySQL.cmod

/* =================================================================
 * Interface for MySQL 5.0 with support for prepared statements
 *
 * Johan Björklund 
 */

#include "global.h"
#include "interpret.h"
#include "module_support.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "array.h"
#include "mapping.h"
#include "multiset.h"
#include "svalue.h"
#include "stralloc.h"

#define THISOBJ (Pike_fp->current_object)
#define CHECK_PING(m) if(mysql_ping((m)->m_connection) != 0) re_init(m)
#define ERROR_OK_NOW(m) m->giveup = 0

struct mysql_result {
  MYSQL_RES                 *m_resultset;       /* MySQL result set */
  MYSQL_BIND                *m_cols;            /* columns of the result set */
  MYSQL_FIELD               *m_fields;
  unsigned long             *m_col_len;         /* lengths of the columns */
  my_bool                   *m_col_is_null;
  struct pike_string       **m_col_names;       /* cache these */
  void                     **buffers;
  INT16                     *ptypes;            /* pike types */
  unsigned long              m_numcols;         /* number of columns in result set */
  unsigned long              m_rowsaffected;
};

struct mysql_con {
  MYSQL                 *m_connection;       /* MySQL connection */
  struct svalue         *init_fun;
  struct statement_list *stmt_list;
  unsigned char          giveup;             /* reconnect tries */
};

struct statement_list;

struct mysql_stmt {
  MYSQL_STMT               *m_stmt;            /* MySQL prepared statement */
  MYSQL_BIND               *m_bindparams;      /* prepared statement parameters */
  unsigned long             m_parcnt;          /* number of prepared statement parameters */
  unsigned long            *m_par_len;         /* lengths of the columns */
  my_bool                  *m_par_is_null;
  struct mysql_result       m_result;          /* result set for prepared statement (if query) */
  char                     *str_stmt;
  unsigned int              str_stmt_len;
  struct statement_list    *list_ref;
  struct statement_list   **list_start;
  struct mysql_con         *con;
  struct svalue            *params;
  int                       args;
};

struct statement_list {
  struct mysql_stmt     *stmt;
  struct statement_list *prev;
  struct statement_list *next;
};


int catch_error(struct mysql_con *con, struct mysql_stmt *stmt, int errno);

void throw_error(struct mysql_stmt *o, const char *error_msg) {
  Pike_error(error_msg);
}

struct svalue str_year, str_month, str_day, str_hour, str_minute, str_second;  /* initialized in INIT {} */

#define GET_INT(m, key, to) ((to = low_mapping_lookup(m, key)) && (to->type == PIKE_T_INT)) ? to->u.integer : 0

MYSQL_TIME *get_ts_from_mapping(struct mapping *m)
{
  MYSQL_TIME *ts = calloc(1, sizeof(MYSQL_TIME));
  struct svalue *to;
  ts->year   = GET_INT(m, &str_year,  to);
  ts->month  = GET_INT(m, &str_month, to);
  ts->day    = GET_INT(m, &str_day,   to);
  ts->hour   = GET_INT(m, &str_hour,  to);
  ts->minute = GET_INT(m, &str_minute, to);
  ts->second = GET_INT(m, &str_second, to);
  return ts;
}

struct mapping *push_mapping_from_ts(MYSQL_TIME *ts)
{
  push_svalue(&str_year);    push_int(ts->year);
  push_svalue(&str_month);   push_int(ts->month);
  push_svalue(&str_day);     push_int(ts->day);
  push_svalue(&str_hour);    push_int(ts->hour);
  push_svalue(&str_minute);  push_int(ts->minute);
  push_svalue(&str_second);  push_int(ts->second);
  f_aggregate_mapping(12);
}

void stmt_prepare_parameter(MYSQL_BIND *m_param,
			    struct svalue *sval)
{
  *m_param->is_null = 0;

  switch(sval->type) {
  case PIKE_T_INT:
    m_param->buffer_length = sizeof(INT_TYPE);
    m_param->buffer = &sval->u.integer;
    switch(sizeof(INT_TYPE)) {
    case 8: m_param->buffer_type = MYSQL_TYPE_LONGLONG; break;
    case 4: m_param->buffer_type = MYSQL_TYPE_LONG;
    }
    m_param->is_unsigned = 0;
    break;
  case PIKE_T_STRING:
    m_param->buffer_length = sval->u.string->len * (sval->u.string->size_shift+1);
    m_param->buffer_type = MYSQL_TYPE_VAR_STRING;
    switch(sval->u.string->size_shift) {
    case 2: m_param->buffer = STR2(sval->u.string); break;
    case 1: m_param->buffer = STR1(sval->u.string); break;
    case 0: m_param->buffer = STR0(sval->u.string);
    }
    break;
  case PIKE_T_FLOAT:
    m_param->buffer_length = sizeof(FLOAT_TYPE);
    m_param->buffer_type = MYSQL_TYPE_DOUBLE;
    m_param->is_unsigned = 0;
    m_param->buffer = &(sval->u.float_number);
    break;
  case PIKE_T_MAPPING:  /* assume date/time mapping */
    m_param->buffer_type = MYSQL_TYPE_DATETIME;
    m_param->buffer = (void*) get_ts_from_mapping(sval->u.mapping);
    break;
  case PIKE_T_OBJECT:
  case PIKE_T_ZERO:
  default:
    m_param->buffer_type = MYSQL_TYPE_NULL;
    *m_param->is_null = 1;
    break;
  }
}

void stmt_bind_parameters(struct mysql_stmt *o, struct svalue *params, int args)
{
  if(args != o->m_parcnt)
    Pike_error("Incorrect number of parameters");

  int i;
  for(i=0; i < o->m_parcnt; i++)
    stmt_prepare_parameter(&o->m_bindparams[i], ¶ms[i]);

 try_mysql_stmt_bind_param:
  if(mysql_stmt_bind_param(o->m_stmt, o->m_bindparams)) {
    if( catch_error(o->con, o, mysql_stmt_errno(o->m_stmt)) )
      goto try_mysql_stmt_bind_param;
  } else {
    ERROR_OK_NOW(o->con);
  }
}

int prepare_statement(struct mysql_stmt *o)
{
  if(o->m_stmt)
    mysql_stmt_close(o->m_stmt);

 try_mysql_stmt_init:
  o->m_stmt = mysql_stmt_init(o->con->m_connection);
  if(!o->m_stmt) {
    if( catch_error(o->con, o, mysql_stmt_errno(o->m_stmt)) )
      goto try_mysql_stmt_init;
  } else {
    ERROR_OK_NOW(o->con);
  }

 try_mysql_stmt_prepare:
  if( mysql_stmt_prepare(o->m_stmt, o->str_stmt, o->str_stmt_len) ) {
    if( catch_error(o->con, o, mysql_stmt_errno(o->m_stmt)) )
      goto try_mysql_stmt_prepare;
  } else {
    ERROR_OK_NOW(o->con);
  }

  my_bool t = 1;
  mysql_stmt_attr_set(o->m_stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &t);

  o->m_parcnt = mysql_stmt_param_count(o->m_stmt);
  o->m_bindparams = calloc(o->m_parcnt, sizeof(MYSQL_BIND));
  o->m_par_is_null = calloc(o->m_parcnt, sizeof(my_bool));

  int i;
  for (i=0; i < o->m_parcnt; i++) {
    o->m_bindparams[i].length = &o->m_bindparams[i].buffer_length;
    o->m_bindparams[i].is_null = &o->m_par_is_null[i];
  }
}

void re_init(struct mysql_con *con) {
  struct statement_list *finder = con->stmt_list;
  
  for( ; finder != NULL; finder = finder->next) {
    prepare_statement(finder->stmt);
  }

  if(con->init_fun)
    apply_svalue(con->init_fun, 0);
}

int catch_error(struct mysql_con *con, struct mysql_stmt *stmt, int errno) {

  if(con->giveup++ > 5) {
    fprintf(stderr, "MySQL.cmod catch_error() unhandled errno: %d, giveup: %d\n", errno, con->giveup);
    Pike_error( mysql_stmt_error(stmt->m_stmt) );
  }

  switch(errno) {
  case CR_CONNECTION_ERROR:
  case CR_SERVER_GONE_ERROR:
    sleep(2);  // allow server some time to come back
  case ER_UNKNOWN_STMT_HANDLER:
  case CR_SERVER_LOST:
    {
      unsigned char tries = 0;
      int d;
      while( (d = mysql_ping(con->m_connection)) != 0 ) {
	switch(mysql_errno(con->m_connection)) {
	case CR_COMMANDS_OUT_OF_SYNC:
	case CR_UNKNOWN_ERROR:
	case CR_SERVER_GONE_ERROR:
	  Pike_error(mysql_error(con->m_connection));	  
	}
	if(tries++ > 5) {
	  Pike_error( mysql_stmt_error(stmt->m_stmt) );
	  break;
	}
      }

      if(d == 0) {
	re_init(con);
	return 0;
      }
    }

  default:
    fprintf(stderr, "MySQL.cmod catch_error() unhandled errno: %d, giveup: %d\n", errno, con->giveup);
    Pike_error( mysql_stmt_error(stmt->m_stmt) );
  }
}

void free_statement(struct mysql_stmt *o)
{
  free(o->m_par_is_null);
  free(o->m_bindparams);

  if(o->m_stmt) {
    mysql_stmt_close(o->m_stmt);
    o->m_stmt = NULL;
  }

  if(o->str_stmt)
    free(o->str_stmt);

  { // Tie list together 
    struct statement_list *this = o->list_ref;
    
    if(this->prev != NULL)
      this->prev->next = this->next;
    else
      *o->list_start = this->next;
    
    if(this->next != NULL)
      this->next->prev = this->prev;
    
    free(o->list_ref);
  }

  free(o);
}

void result_map_type(MYSQL_BIND *v, INT16 *ptype, MYSQL_FIELD *field)
{
  v->buffer_length = field->max_length;
  v->buffer_type = field->type;
  switch (field->type) {
  case MYSQL_TYPE_TINY          :
  case MYSQL_TYPE_SHORT         :
  case MYSQL_TYPE_LONG          :
  case MYSQL_TYPE_LONGLONG      : *ptype = PIKE_T_INT;   break;
  case MYSQL_TYPE_FLOAT         :
  case MYSQL_TYPE_DOUBLE        : *ptype = PIKE_T_FLOAT; break;
  case MYSQL_TYPE_TIME          :
  case MYSQL_TYPE_DATE          :
  case MYSQL_TYPE_DATETIME      :
  case MYSQL_TYPE_TIMESTAMP     : *ptype = PIKE_T_MAPPING;
    v->buffer_length = sizeof(MYSQL_TIME);
    break;
  case MYSQL_TYPE_STRING        : 
  case MYSQL_TYPE_VAR_STRING    :
  case MYSQL_TYPE_TINY_BLOB     :
  case MYSQL_TYPE_BLOB          :
  case MYSQL_TYPE_MEDIUM_BLOB   :
  case MYSQL_TYPE_LONG_BLOB     :
  default                       : *ptype = PIKE_T_STRING;
  }
}

void register_resultset(struct mysql_stmt *stmt)
{
  unsigned int i;
  struct mysql_result *o = &stmt->m_result;

  o->m_fields      = mysql_fetch_fields(o->m_resultset);
  o->m_numcols     = mysql_num_fields(o->m_resultset);
  o->m_cols        = calloc(o->m_numcols, sizeof(MYSQL_BIND));
  o->m_col_is_null = calloc(o->m_numcols, sizeof(my_bool));
  o->m_col_names   = calloc(o->m_numcols, sizeof(void*));
  o->buffers       = calloc(o->m_numcols, sizeof(void*));
  o->ptypes        = calloc(o->m_numcols, sizeof(INT16));

  for(i = 0; i < o->m_numcols; i++) {
    MYSQL_FIELD *field = &o->m_fields[i];
    o->m_col_names[i] = make_shared_string(field->name);

    MYSQL_BIND *m_col = &o->m_cols[i];
    m_col->is_null = &o->m_col_is_null[i];

    result_map_type(m_col, &o->ptypes[i], field);
    m_col->buffer = (void *) malloc(m_col->buffer_length);
    if(o->ptypes[i] == PIKE_T_STRING)
      m_col->length = malloc(sizeof(unsigned long));
  }

  if(mysql_stmt_bind_result(stmt->m_stmt, o->m_cols))
    throw_error(stmt, mysql_stmt_error(stmt->m_stmt)); 
}

void free_result(struct mysql_result *r)
{
  int i;
  if(r->m_cols) {

    for(i=0; im_numcols; i++) {
      free_string(r->m_col_names[i]);
      free(r->m_cols[i].buffer);
      if(r->ptypes[i] == PIKE_T_STRING)
	free(r->m_cols[i].length);
    }

    free(r->m_col_is_null);
    free(r->m_col_names);
    free(r->m_cols);
    free(r->ptypes);
  }
}

void execute_statement(struct mysql_stmt *o, struct svalue *params, int args)
{
  // fprintf(stderr, "\texecute_statement\n");

 try_mysql_stmt_execute:
  if(args)
    stmt_bind_parameters(o, params, args);
  if(mysql_stmt_execute(o->m_stmt)) {
    if(catch_error(o->con, o, mysql_stmt_errno(o->m_stmt)) == 0) {
      goto try_mysql_stmt_execute;
    }
  } else {
    ERROR_OK_NOW(o->con);
  }

  o->m_result.m_rowsaffected = mysql_stmt_affected_rows(o->m_stmt);
  o->m_result.m_resultset = mysql_stmt_result_metadata(o->m_stmt);

  if(o->m_result.m_resultset) {
    if(mysql_stmt_store_result(o->m_stmt))
      Pike_error( mysql_stmt_error(o->m_stmt) );
    register_resultset(o);
  }
}

void push_bind_value(MYSQL_BIND *res, INT16 ptype)
{
  if(*res->is_null) {
    push_int(0);
  } else {
    switch(ptype) {
    case PIKE_T_INT     :
      switch(res->buffer_type) {
      case MYSQL_TYPE_TINY     : push_int( (INT_TYPE) *((char*) res->buffer)          ); break;
      case MYSQL_TYPE_SHORT    : push_int( (INT_TYPE) *((short int*) res->buffer)     ); break;
      case MYSQL_TYPE_LONG     : push_int( (INT_TYPE) *((int*) res->buffer)           ); break;
      case MYSQL_TYPE_LONGLONG : push_int( (INT_TYPE) *((long long int*) res->buffer) ); break;
      } break;
    case PIKE_T_FLOAT   : push_float  ( *((FLOAT_TYPE*) res->buffer) );                  break;
    case PIKE_T_MAPPING : push_mapping_from_ts((MYSQL_TIME*) res->buffer);               break;
    case PIKE_T_STRING  : push_string ( make_shared_binary_string(res->buffer, *res->length) ); break;
    default             : push_int(0);
    }
  }
}

void push_result_array(struct mysql_stmt *stmt) {
  int rows=0;
  int rc;
  int i;
  struct mysql_result *result = &stmt->m_result;

  while((rc = mysql_stmt_fetch(stmt->m_stmt)) == 0) {

    for(i=0; i < result->m_numcols; i++) {
      ref_push_string(result->m_col_names[i]);
      push_bind_value(&result->m_cols[i], result->ptypes[i]);
    }

    f_aggregate_mapping(result->m_numcols*2);
    rows++;
  }
  
  if(rc != MYSQL_NO_DATA) {
    fprintf(stderr, "\t\tpush_result_array error rc = %d\n", rc);
    throw_error(stmt, mysql_stmt_error(stmt->m_stmt));
  }

  debug_f_aggregate(rows);
}

PIKECLASS PreparedStatement {
  CVAR struct mysql_con  *con;
  CVAR struct mysql_stmt *stmt;
  CVAR struct object     *connection;
  
  PIKEFUN void create() {
    Pike_error("Must be created by MySQL.MySQL()->prepare_statement()\n");
  }

  PIKEFUN int get_last_inserted_key() {
    CHECK_PING(THIS->con);
    RETURN mysql_stmt_insert_id(THIS->stmt->m_stmt);
  }
  
  PIKEFUN array `()(mixed ... params) {

    execute_statement(THIS->stmt, params, args);

    pop_n_elems(args);

    if(THIS->stmt->m_result.m_resultset) {
      push_result_array(THIS->stmt);
      free_result(&THIS->stmt->m_result);
    }

    mysql_stmt_reset(THIS->stmt->m_stmt);
  }
  
  PIKEFUN object get_connection() {
    RETURN THIS->connection;
  }

  INIT {
    THIS->stmt = NULL;
    THIS->con = NULL;
    THIS->connection = NULL;
  }

  EXIT {
    // fprintf(stderr, "exit from MySQL.PreparedStatement\n");
    if(THIS->stmt)
      free_statement(THIS->stmt);
    if(THIS->connection)
      free_object(THIS->connection);
  }
}

/* ===============================================
 * Resultset class
 *
 * =============================================== */

PIKECLASS MysqlResult {
  CVAR struct object     *connection;
  CVAR struct mysql_stmt *stmt;

  /* Get specification of all remaining fields.
   * Returns an array with one mapping for every remaining field in the result table.
   */
  PIKEFUN array(int|mapping(string:mixed)) fetch_fields () {
    int column;

    for (column = 0; column < THIS->stmt->m_result.m_numcols; column++) {
      push_text("name");
      push_string(THIS->stmt->m_result.m_col_names[column]);

      push_text("type");
      switch (THIS->stmt->m_result.m_fields[column].type) {
      case MYSQL_TYPE_TINY          :
      case MYSQL_TYPE_SHORT         :
      case MYSQL_TYPE_LONG          :
      case MYSQL_TYPE_LONGLONG      : push_text("int");	      break;
      case MYSQL_TYPE_FLOAT         :
      case MYSQL_TYPE_DOUBLE        : push_text("float");     break;
      case MYSQL_TYPE_STRING        : 
      case MYSQL_TYPE_VAR_STRING    :
      case MYSQL_TYPE_TINY_BLOB     :
      case MYSQL_TYPE_BLOB          :
      case MYSQL_TYPE_MEDIUM_BLOB   :
      case MYSQL_TYPE_LONG_BLOB     : push_text("string");   break;
      case MYSQL_TYPE_TIME          :
      case MYSQL_TYPE_DATE          :
      case MYSQL_TYPE_DATETIME      :
      case MYSQL_TYPE_TIMESTAMP     : push_text("datetime"); break;
      default                       : push_text("weird");    break;
      }
      f_aggregate_mapping(2);
    }
    RETURN aggregate_array(column);
  }

  /* Fetch the next row from the result.
   * Returns an array with the contents of the next row in the result.
   * Advances the row cursor to the next now.
   * Returns 0 (zero) at the end of the table.
   */
  PIKEFUN array(mixed) fetch_row () {
    int rc;
    /*
    if ((rc = MySQL_ResultSet_next (THIS->result->m_resultset)) == MySQL_NO_DATA_FOUND)
      RETURN NULL;

    MYSQL_ROW *rowset = NULL;
    if(mysql_stmt_fetch_row(THIS->result->m_resultset))
      throw_error(THIS->stmt, mysql_stmt_error(THIS->stmt));

    int column;
    struct svalue *sval = calloc(1, sizeof(struct svalue));
    for (column=0; column < THIS->result->m_numcols; column++) {
      push_bind_value ( ... );
    }
    free(sval);
    */

    RETURN aggregate_array (THIS->stmt->m_result.m_numcols);
  }
  
  /* Skip to specified field.
   * Places the field cursor at the specified position.
   * This affects which field mysql_THIS->resultfetch_field() will return next.
   * Fields are numbered starting with 0.
   */
  PIKEFUN void field_seek (int field_no) {
    /*
    if (MySQL_ResultSet_absolute(THIS->result->m_resultset, field_no) != MySQL_OK) {
      mysql_error (MySQL_ResultSet_getError(THIS->result->m_resultset));
    }
    */
  }

  /* Number of fields in the result. */
  PIKEFUN int num_fields () {
    RETURN THIS->stmt->m_result.m_numcols;
  }

  /* Number of rows in the result. */
  PIKEFUN int num_rows () {
    RETURN mysql_num_rows(THIS->stmt->m_result.m_resultset);
  }

  /* Skip ahead rows rows. */
  PIKEFUN void seek (int rows) {
    mysql_stmt_data_seek (THIS->stmt->m_stmt, rows);
    /* FIXME: if(error) */
  }

  PIKEFUN int affected_rows () {
    RETURN THIS->stmt->m_result.m_rowsaffected;
  }

  PIKEFUN void create () {
    Pike_error("Must be created by Mysql.Mysql()->big_query()\n");
  }

  EXIT {
    free_result(&THIS->stmt->m_result);
    free_statement(THIS->stmt);
    sub_ref(THIS->connection);
  }
}

/* ===============================================
 * Connection class
 *
 * =============================================== */

PIKECLASS MySQL {
  CVAR struct mysql_con con;
  CVAR unsigned long    m_client_flag;      /* connection properties */

  CVAR struct pike_string *hostname;
  CVAR struct pike_string *database;
  CVAR struct pike_string *username;
  CVAR struct pike_string *password;


  PIKEFUN void create(void|string hname,
		      string db,
		      string uname,
		      string pw,
		      void|mapping options) {
    
    unsigned char *hostname;
    unsigned char *database;
    unsigned char *username;
    unsigned char *password;
    unsigned int port;

    THIS->con.m_connection = mysql_init(NULL);
    
    if(!THIS->con.m_connection)
      Pike_error("Cannot get MYSQL structure");

    if( (hname) &&
	(hname->len > 0) &&
	(hname->size_shift == 0) ) {
      THIS->hostname = hname;
      add_ref(THIS->hostname);
      hostname = STR0(hname);
    } else {
      hostname = (unsigned char*) "localhost";
    }

    if(db->len > 0) {
      if( (db->size_shift == 0) ) {
	THIS->database = db;
	add_ref(THIS->database);
	database = (unsigned char*) STR0(db);
      } else
	Pike_error("Wide character database name not supported\n");
    } else {
      database = NULL;
    }

    switch(uname->size_shift) {
    case 0: username = STR0(uname); break;
    case 1: username = (unsigned char*) STR1(uname); break;
    case 2: username = (unsigned char*) STR2(uname); break;
    }
    THIS->username = uname;
    add_ref(THIS->username);

    switch(pw->size_shift) {
    case 0: password = STR0(pw); break;
    case 1: password = (unsigned char*) STR1(THIS->password); break;
    case 2: password = (unsigned char*) STR2(THIS->password); break;
    }
    THIS->password = pw;
    add_ref(THIS->password);

    if(!mysql_real_connect(THIS->con.m_connection,
			   (char*) hostname,
			   (char*) username,
			   (char*) password,
			   (char*) database,
			   port,
			   NULL,
			   THIS->m_client_flag))
      throw_error(NULL, mysql_error(THIS->con.m_connection));

    my_bool t = 1;
    mysql_options(THIS->con.m_connection, MYSQL_OPT_RECONNECT, &t);
  }

  PIKEFUN int(0..1) ping() {
    RETURN mysql_ping(THIS->con.m_connection);
  }

  PIKEFUN void autocommit(int(0..1) mode) {
    if(mysql_autocommit(THIS->con.m_connection, mode))
      throw_error(NULL, mysql_error(THIS->con.m_connection));
  }

  PIKEFUN void commit() {
    if(mysql_commit(THIS->con.m_connection))
      throw_error(NULL, mysql_error(THIS->con.m_connection));
  }

  PIKEFUN void rollback() {
    if(mysql_rollback(THIS->con.m_connection))
      throw_error(NULL, mysql_error(THIS->con.m_connection));
  }

  PIKEFUN void create_db() {
    
  }

  PIKEFUN void drop_db() {
    
  }

  PIKEFUN void set_init(mixed fun) {
    if(!THIS->con.init_fun) {
      THIS->con.init_fun = xalloc(sizeof(struct svalue));
      assign_svalue_no_free_unlocked(THIS->con.init_fun, fun);
    } else {
      assign_svalue(THIS->con.init_fun, fun);
    }
  }

  void add_stmt(struct mysql_stmt *stmt) {
    struct statement_list *prev = NULL;
    struct statement_list *finder = THIS->con.stmt_list;
    while(finder != NULL) {
      prev = finder;
      finder = finder->next;
    }
    {
      finder = xalloc(sizeof(struct statement_list));
      finder->prev = prev;
      finder->next = NULL;
      if(!THIS->con.stmt_list)
	THIS->con.stmt_list = finder;
      else
	prev->next = finder;
    }
    finder->stmt = stmt;
    finder->stmt->list_start = &THIS->con.stmt_list;
    finder->stmt->list_ref = finder;
  }

  /* ===============================================
   * Queries
   */
  struct mysql_stmt *create_statement(struct pike_string *statement) {
    struct mysql_stmt *stmt = xalloc(sizeof(struct mysql_stmt));

    stmt->m_stmt = NULL;
    stmt->m_result.m_resultset = NULL;

    char *char_statement;

    switch(statement->size_shift) {
    case 2: char_statement = (char *) STR2(statement); break;
    case 1: char_statement = (char *) STR1(statement); break;
    case 0: char_statement = (char *) STR0(statement);
    }

    stmt->str_stmt_len = statement->len * ( 1 + statement->size_shift * 2);
    stmt->str_stmt = xalloc(stmt->str_stmt_len);
    memcpy(stmt->str_stmt, char_statement, stmt->str_stmt_len);

    stmt->con = &THIS->con;
    prepare_statement(stmt);
    add_stmt(stmt);

    return stmt;
  }

  PIKEFUN object(PreparedStatement) prepare_statement(string q) {
    struct object *stmt = low_clone(PreparedStatement_program);
    call_c_initializers(stmt);

    OBJ2_PREPAREDSTATEMENT(stmt)->stmt = create_statement(q);
    OBJ2_PREPAREDSTATEMENT(stmt)->connection = THISOBJ;
    OBJ2_PREPAREDSTATEMENT(stmt)->con = &THIS->con;
    add_ref(THISOBJ);

    RETURN stmt;
  }

  PIKEFUN mixed query(string|object q, void|mixed ... rest) {
    struct mysql_stmt *stmt = NULL;

    if(q->type == PIKE_T_OBJECT) {
      if(q->u.object->prog != PreparedStatement_program)
	Pike_error("Query object must be created by Mysql.Mysql->compile_query()\n");
      stmt = OBJ2_PREPAREDSTATEMENT(q->u.object)->stmt;
    } else 
      stmt = create_statement(q->u.string);

    execute_statement(stmt, rest, args-1);

    pop_n_elems(args);

    /* make resultmapping */
    if(stmt->m_result.m_resultset) {
      push_result_array(stmt);
      free_result(&stmt->m_result);
    }

    if (q->type == PIKE_T_STRING)
      free_statement(stmt);
    else
      mysql_stmt_reset(stmt->m_stmt);
  }

  /* Returns a resultset object */
  PIKEFUN object(MysqlResult) big_query(string|object q, void|mixed ... rest)  {

    struct mysql_stmt *stmt = NULL;
    struct array      *bindings = NULL;

    if(q->type == PIKE_T_OBJECT) {
      if(q->u.object->prog != PreparedStatement_program)
	Pike_error("Query object must be created by Mysql.Mysql->compile_query()\n");
      stmt = OBJ2_PREPAREDSTATEMENT(q->u.object)->stmt;
    } else 
      stmt = create_statement(q->u.string);

    execute_statement(stmt, rest, args-1);

    pop_n_elems(args);

    struct svalue *ret = malloc(sizeof(struct svalue));
    ret->subtype = 0;

    if (stmt->m_result.m_resultset) {
      ret->u.object = low_clone(MysqlResult_program);
      call_c_initializers(ret->u.object);
      ret->type = PIKE_T_OBJECT;
      OBJ2_MYSQLRESULT(ret->u.object)->stmt = stmt;
      OBJ2_MYSQLRESULT(ret->u.object)->connection = THISOBJ;
      add_ref(THISOBJ);
    } else {
      ret->type = PIKE_T_ZERO;
      free_result(&stmt->m_result);
    }

    if (q->type == PIKE_T_STRING)
      free_statement(stmt);

    push_svalue(ret);
    free(ret);
  }
  
  INIT {
    if (mysql_thread_init())
      Pike_error("mysql_thread_init() failed\n");

    THIS->hostname = NULL;
    THIS->database = NULL;
    THIS->username = NULL;
    THIS->password = NULL;

    THIS->con.m_connection = NULL;
    THIS->con.stmt_list    = NULL;
    THIS->con.init_fun     = NULL;
    THIS->con.giveup       = 0;

    THIS->m_client_flag = CLIENT_MULTI_STATEMENTS;
  }

  EXIT {
    if(THIS->con.m_connection)
      mysql_close(THIS->con.m_connection);

    if(THIS->con.init_fun) {
      free_svalue(THIS->con.init_fun);
      free(THIS->con.init_fun);
    }

    if(THIS->hostname)  free_string(THIS->hostname);
    if(THIS->database)  free_string(THIS->database);
    if(THIS->username)  free_string(THIS->username);
    if(THIS->password)  free_string(THIS->password);

    mysql_thread_end();
  }
}

INIT
{
  str_year.u.string = make_shared_string("year");
  str_month.u.string  = make_shared_string("month");
  str_day.u.string = make_shared_string("day");
  str_hour.u.string = make_shared_string("hour");
  str_minute.u.string  = make_shared_string("minute");
  str_second.u.string  = make_shared_string("second");

  str_year.type = str_month.type = str_day.type =
    str_hour.type = str_minute.type = str_second.type = PIKE_T_STRING;

  mysql_library_init(0, NULL, NULL);
}

EXIT
{
  mysql_library_end();

  free_string(str_year.u.string);
  free_string(str_month.u.string);
  free_string(str_day.u.string);
  free_string(str_hour.u.string);
  free_string(str_minute.u.string);
  free_string(str_second.u.string);
}

EXTRA {
  add_string_constant("__author", "Johan Björklund ", 0);
  add_string_constant("__version", "0.4", 0);
}


gotpike.org | Copyright © 2004 - 2019 | Pike is a trademark of Department of Computer and Information Science, Linköping University