module mysqlpl; // // mysqlpl.d // // Copyright (c) 2007 Martin Fuchs // /// \file mysqlpl.d /// MySQLPL/D implementation file /* All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ version(XMLSTORAGE) { import xmlstorage; } import std.stdio; import std.stream; import std.string; import std.c.string; // strlen() //static import std.c.stdlib; // atoll(), malloc() // C wrapper for MySQL API extern (C) { struct MYSQL; struct MYSQL_STMT; struct MYSQL_FIELD; struct MYSQL_RES; struct MYSQL_BIND; alias ulong my_ulonglong; alias char my_bool; alias char** MYSQL_ROW; alias long LONGLONG; enum MYSQL_FIELD_TYPES { // enum_field_types MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY, MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, MYSQL_TYPE_NULL, MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_LONGLONG, MYSQL_TYPE_INT24, MYSQL_TYPE_DATE, MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR, MYSQL_TYPE_NEWDATE, // since MySQL 5.0 MYSQL_TYPE_VARCHAR, // since MySQL 5.0 MYSQL_TYPE_BIT, MYSQL_TYPE_NEWDECIMAL = 246, MYSQL_TYPE_ENUM = 247, MYSQL_TYPE_SET = 248, MYSQL_TYPE_TINY_BLOB = 249, MYSQL_TYPE_MEDIUM_BLOB = 250, MYSQL_TYPE_LONG_BLOB = 251, MYSQL_TYPE_BLOB = 252, MYSQL_TYPE_VAR_STRING = 253, MYSQL_TYPE_STRING = 254, MYSQL_TYPE_GEOMETRY = 255 }; enum MYSQL_TIMESTAMP_TYPE { MYSQL_TIMESTAMP_NONE = -2, MYSQL_TIMESTAMP_ERROR = -1, MYSQL_TIMESTAMP_DATE = 0, MYSQL_TIMESTAMP_DATETIME= 1, MYSQL_TIMESTAMP_TIME = 2 }; const int MYSQL_NO_DATA = 100; const int MYSQL_DATA_TRUNCATED = 101; const int MYSQL_NOT_NULL_FLAG = 1; // MySQL field can't be null bool IS_NOT_NULL(int flags) { return (flags&MYSQL_NOT_NULL_FLAG) != 0; } const int MYSQL_BINARY_FLAG = 128; // MySQL result set field is binary // interface to mysqlpl_fwd.cpp MYSQL* mysqlpl_init(MYSQL*); MYSQL_FIELD* mysqlpl_next_field(MYSQL_FIELD*); // increment MYSQL_FIELD pointer using the correct structure size according to the mysql header files char* mysqlpl_field_get_table(in MYSQL_FIELD*); char* mysqlpl_field_get_name(in MYSQL_FIELD*); MYSQL_FIELD_TYPES mysqlpl_field_get_type(in MYSQL_FIELD*); int mysqlpl_field_get_length(in MYSQL_FIELD*); int mysqlpl_field_get_decimals(in MYSQL_FIELD*); int mysqlpl_field_get_flags(in MYSQL_FIELD*); void bindvector_free(MYSQL_BIND*); MYSQL_BIND* bindvector_resize(MYSQL_BIND*, int length); MYSQL_BIND* bindvector_next(MYSQL_BIND*); void bindvector_bind(MYSQL_BIND*, void* ptr, MYSQL_FIELD_TYPES type, int len, my_bool* pind, uint* plen); } // extern (C) extern (Windows) { MYSQL* (*mysqlpl_real_connect) (MYSQL* mysql, char* host, char* user, char* passwd, char* db, uint port, char* unix_socket, uint clientflag); void (*mysqlpl_close) (MYSQL*); char* (*mysqlpl_info) (MYSQL* db); uint (*mysqlpl_errno) (MYSQL* db); char* (*mysqlpl_error) (MYSQL* db); uint (*mysqlpl_warning_count)(MYSQL* db); MYSQL_RES* (*mysqlpl_store_result) (MYSQL* db); void (*mysqlpl_free_result) (MYSQL_RES*); my_ulonglong (*mysqlpl_affected_rows) (MYSQL* db); uint (*mysqlpl_num_fields) (MYSQL_RES* res); MYSQL_FIELD* (*mysqlpl_fetch_fields) (MYSQL_RES*); MYSQL_FIELD* (*mysqlpl_fetch_field_direct)(MYSQL_RES*, int); version(MYSQLPL_PREP_STMT) { char* (*mysqlpl_sqlstate) (MYSQL* db); uint (*mysqlpl_stmt_errno) (MYSQL_STMT* s); char* (*mysqlpl_stmt_error) (MYSQL_STMT* s); char* (*mysqlpl_stmt_sqlstate) (MYSQL_STMT* s); my_bool (*mysqlpl_autocommit) (MYSQL* db, uint); MYSQL_STMT* (*mysqlpl_stmt_init) (MYSQL*); int (*mysqlpl_stmt_prepare) (MYSQL_STMT*, char* sql, int length); void (*mysqlpl_stmt_close) (MYSQL_STMT*); int (*mysqlpl_stmt_execute) (MYSQL_STMT*); int (*mysqlpl_stmt_fetch) (MYSQL_STMT*); MYSQL_RES* (*mysqlpl_stmt_store_result) (MYSQL_STMT* s); my_bool (*mysqlpl_stmt_bind_param) (MYSQL_STMT*, MYSQL_BIND*); my_bool (*mysqlpl_stmt_bind_result) (MYSQL_STMT* s, MYSQL_BIND*); int (*mysqlpl_stmt_affected_rows) (MYSQL_STMT* s); MYSQL_RES* (*mysqlpl_stmt_result_metadata) (MYSQL_STMT* s); } my_bool (*mysqlpl_commit) (MYSQL* db); my_bool (*mysqlpl_rollback) (MYSQL* db); my_bool (*mysqlpl_query) (MYSQL* db, char* sql); MYSQL_ROW (*mysqlpl_fetch_row) (MYSQL_RES* res); my_ulonglong (*mysqlpl_insert_id) (MYSQL* db); } // extern (Windows) class vector(T) { public: this() { } this(int size) { _entries.length = size; for(int i=0; i0); return _entries[0]; } T back() { assert(_entries.length>0); return _entries[_entries.length-1]; } bool empty() { return !_entries.length; } int length() { return _entries.length; } void resize(int l) { _entries.length = l; } void clear() { foreach(entry; _entries) delete entry; _entries = null; } T opIndex(int idx) { return _entries[idx]; } void opIndexAssign(int idx, T value) { _entries[idx] = value; } int opApply(int delegate(inout T) dg) { int result = 0; foreach(e; _entries) { result = dg(e); if (result) break; } return result; } protected: T[] _entries; }; /// MySQL Errorhandling class MSqlException : public Exception { this(MYSQL* db/*, in char[] file, int line*/) { _sql_error_code = mysqlpl_errno(db); char* error_str = mysqlpl_error(db); version(MYSQLPL_PREP_STMT) { char* state_str = mysqlpl_sqlstate(db); super(format("MySQL error %d - SQLSTATE %s: %s", _sql_error_code, state_str[0..strlen(state_str)], error_str[0..strlen(error_str)])); _sqlstate = state_str[0 .. state_str?strlen(state_str):0]; } else { super(format("MySQL error %d: %s", _sql_error_code, error_str)); } // _file = file; // _line = line; } version(MYSQLPL_PREP_STMT) { this(MYSQL_STMT* s/*, in char[] file, int line*/) { _sql_error_code = mysqlpl_stmt_errno(s); char* error_str = mysqlpl_stmt_error(s); version(MYSQLPL_PREP_STMT) { char* state_str = mysqlpl_stmt_sqlstate(s); super(format("MySQL error %d - SQLSTATE %s: %s", _sql_error_code, state_str[0..strlen(state_str)], error_str[0..strlen(error_str)])); _sqlstate = state_str[0 .. state_str?strlen(state_str):0]; } else { super(format("MySQL error %d: %s", _sql_error_code, error_str)); } // _file = file; // _line = line; } this(MYSQL_STMT* s, SqlStatement stmt/*, in char[] file, int line*/) { _sql_error_code = mysqlpl_stmt_errno(s); char* error_str = mysqlpl_stmt_error(s); char* state_str = mysqlpl_stmt_sqlstate(s); char[] msg = format("MySQL error %d - SQLSTATE %s: %s", _sql_error_code, state_str[0..strlen(state_str)], error_str[0..strlen(error_str)]); _last_sql = stmt.get_last_sql(); if (_last_sql.length) msg ~= format("\nlast SQL statement:\n%s\n", _last_sql); super(msg); _sqlstate = state_str[0 .. state_str?strlen(state_str):0]; // _file = file; // _line = line; } } // MYSQLPL_PREP_STMT this(in char[] msg/*, in char* file, int line*/) { super(msg); // _file = file; // _line = line; } uint _sql_error_code; version(MYSQLPL_PREP_STMT) { char[] _sqlstate; } char[] _last_sql; // char[] _file; // int _line; }; /// error handling functions void mysqlpl_check_error(MYSQL* db) { uint error = mysqlpl_errno(db); if (error != 0) throw new MSqlException(db); } void mysqlpl_check_error(MYSQL* db, int res) { if (res) { uint error = mysqlpl_errno(db); if (error != 0) throw new MSqlException(db); } } void mysqlpl_check_error(MYSQL* db, my_bool b) { if (b) { uint error = mysqlpl_errno(db); if (error != 0) throw new MSqlException(db); } } version(MYSQLPL_PREP_STMT) { void mysqlpl_check_error(MYSQL_STMT* s) { uint error = mysqlpl_stmt_errno(s); if (error != 0) throw new MSqlException(s); } void mysqlpl_check_error(MYSQL_STMT* s, int res) { if (res) { uint error = mysqlpl_stmt_errno(s); if (error != 0) throw new MSqlException(s); } } void mysqlpl_check_error(MYSQL_STMT* s, my_bool b) { if (b) { uint error = mysqlpl_stmt_errno(s); if (error != 0) throw new MSqlException(s); } } } // version(MYSQLPL_PREP_STMT) class MSqlEnv { this(MYSQL* mysql=null) { _mysql = mysqlpl_init(null); } ~this() { mysqlpl_close(_mysql); } MYSQL* opCast() {return _mysql;} MYSQL* opCall() {return _mysql;} protected: MYSQL* _mysql; }; /// simple MySQL login to database class MSqlConnection { this(MSqlEnv env, in char[] username, in char[] password, in char[] db_name, in char[] host="localhost", uint port=3306, uint clientflag=0) { _env = env; _db = null; _connected = false; connect(username, password, db_name, host, port, clientflag); } void connect(in char[] username, in char[] password, in char[] db_name, in char[] host="localhost", uint port=3306, uint clientflag=0) { _connected = false; _db = mysqlpl_real_connect(_env(), toStringz(host), toStringz(username), toStringz(password), toStringz(db_name), port, null, clientflag); if (!_db) throw new MSqlException(_env()); _connected = true; version(MYSQLPL_PREP_STMT) { // disable auto commit mode my_bool res = mysqlpl_autocommit(_db, 0); mysqlpl_check_error(_env(), res); } else { (new SqlStatement(this)).execute("set autocommit=0"); // "set autocommit=0" is not supported before MySQL 4.1 } } ~this() { /*see MSqlEnv::~MSqlEnv() if (_connected) mysqlpl_close(_mysql); */ } version(MYSQLPL_PREP_STMT) { // get warnings uint get_warnings_count() { uint res = mysqlpl_warning_count(_db); if (res == -1) throw new MSqlException(_db); return res; } char[] get_info() { char* info = mysqlpl_info(_db); if (!info) throw new MSqlException(_db); return info[0 .. info?strlen(info):0]; } void print_warnings(Stream o) { while(get_warnings_count()) o.writeString(get_info() ~ '\n'); } } else { // version(MYSQLPL_PREP_STMT) char[] get_info() { char* ret = mysqlpl_info(_db); return ret[0 .. ret?strlen(ret):0]; } void print_warnings(Stream o) { char[] info = get_info(); if (info.length) o.writeString(info ~ '\n'); } } // get last generated insert ID my_ulonglong get_insert_id() { return mysqlpl_insert_id(_db); } void commit() { version(MYSQLPL_PREP_STMT) { my_bool res = mysqlpl_commit(_db); } else { my_bool res = mysqlpl_query(_db, "commit"); } mysqlpl_check_error(_env(), res); } void rollback() { version(MYSQLPL_PREP_STMT) {//@@ my_bool res = mysqlpl_rollback(_db); } else { my_bool res = mysqlpl_query(_db, "rollback"); } mysqlpl_check_error(_env(), res); } MYSQL* opCast() {return _db;} MYSQL* opCall() {return _db;} protected: MSqlEnv _env; MYSQL* _db; bool _connected; }; /// column description class ColumnType { char[] _table_name; char[] _name; MYSQL_FIELD_TYPES _data_type; int _width; int _decimals; int _flags; this() { _data_type = cast(MYSQL_FIELD_TYPES)-1; _width = 0; _decimals = 0; _flags = 0; } char[] get_type_str(bool show_null=false) { char[] str; switch(_data_type) { case MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING: str = format("char(%d)", _width); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_VAR_STRING: str = format("varchar(%d)", _width); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DECIMAL: if (_width == 0) str = "decimal"; else str = format("decimal(%d,%d)", _width-1, _decimals); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_NEWDECIMAL: // MYSQL_VERSION_ID>=50000 if (_width == 0) str = "newdecimal"; else str = format("newdecimal(%d,%d)", _width-1, _decimals); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_TINY: str = format("tinyint(%d)", _width); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_SHORT: str = format("shortint(%d)", _width); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG: str = format("int(%d)", _width); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG: str = format("bigint(%d)", _width); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_ENUM: str = "enum"; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_FLOAT: if (_width == 12) str = "float"; else str = format("float(%d,%d)", _width, _decimals); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE: if (_width == 22) str = "double"; else str = format("double(%d,%d)", _width, _decimals); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATE: str = "date"; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_NEWDATE: // MYSQL_VERSION_ID>=50000 str = "newdate"; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATETIME: str = "datetime"; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIME: str = "time"; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_YEAR: str = format("year(%d)", _width); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_TINY_BLOB: if (_flags & MYSQL_BINARY_FLAG) str = "tinyblob"; else str = "tinytext"; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_MEDIUM_BLOB: if (_flags & MYSQL_BINARY_FLAG) str = "mediumblob"; else str = "mediumtext"; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG_BLOB: if (_flags & MYSQL_BINARY_FLAG) str = "longblob"; else str = "longtext"; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_BLOB: if (_flags & MYSQL_BINARY_FLAG) str = "blob"; else str = "text"; break; default: str = "unknown"; } if (show_null) if (IS_NOT_NULL(_flags)) str ~= " not null"; else str ~= " null"; return str; } void init(in MYSQL_FIELD* field) { char* table = mysqlpl_field_get_table(field); char* name = mysqlpl_field_get_name(field); // get column name _table_name = table[0 .. strlen(table)]; _name = name[0 .. strlen(name)]; // get column data type _data_type = mysqlpl_field_get_type(field); _width = mysqlpl_field_get_length(field); _decimals = mysqlpl_field_get_decimals(field); // get null-ability, binary, ... flags _flags = mysqlpl_field_get_flags(field); } }; class ColumnData { this() { _type = new ColumnType; } ColumnType _type; version(MYSQLPL_PREP_STMT) { SqlVariant _column; } }; class BindVector : public vector!(BindPar) { void bind(int idx, in BindPar par) { _entries[idx] = par; } }; version(MYSQLPL_PREP_STMT) { void mysqlpl_bind_param(in BindPar par, MYSQL_BIND* bind) { bindvector_bind(bind, par._ptr, par._type, par._len, par._pind, par._plen); /* ersetzt memset(&bind, 0, sizeof(bind)); bind.buffer = par._ptr; bind.buffer_type = par._type; bind.buffer_length = par._len; bind.is_null = par._pind; bind.length = par._plen; */ } } alias vector!(ColumnData) ColumnDataVector; /// a record to hold return columns for a given statement class SqlRecord { this(SqlStatement stmt) { _stmt = stmt; int column_cnt = stmt.get_column_count(); _columns = new ColumnDataVector(column_cnt); MYSQL_FIELD* fields = mysqlpl_fetch_fields(stmt.get_meta_res()); foreach(ColumnData col; _columns) { col._type.init(fields); fields = mysqlpl_next_field(fields); version(MYSQLPL_PREP_STMT) { col._column = new SqlVariant(col._type._data_type); } } //_name_map.clear(); } int get_column_count() { return _columns.length; } version(MYSQLPL_PREP_STMT) { SqlVariant get_column(int idx) { return _columns[idx]._column; } SqlVariant get_column(in char[] col_name) { int* found = find_column(col_name); if (found) return _columns[*found]._column; else { throw new MSqlException("column not found"); return null; } } } ColumnType get_column_type(int idx) { return _columns[idx]._type; } ColumnType get_column_type(in char[] col_name) { int* found = find_column(col_name); if (found) return _columns[*found]._type; else { throw new MSqlException("column not found"); return null; } } void print_columns(Stream o) { foreach(col; _columns) { ColumnType type = col._type; if (type._table_name.length) o.writeString(type._table_name ~ '.'); o.writeString(type._name ~ ": " ~ type.get_type_str() ~ '\n'); } o.write('\n'); } void print_values(Stream o) { version(XMLSTORAGE) { foreach(col; _columns) { ColumnType type = col._type; // if (type._table_name.length) // o.writeString(type._table_name ~ "."); o.writeString(type._name ~ "=" ~ col._column.str() ~ '\n'); } } else { int i = 0; foreach(col; _columns) { ColumnType type = col._type; // if (type._table_name) // o.writeString(type._table_name ~ "."); o.writeString(type._name ~ "=" ~ _stmt().get_string(i++) ~ '\n'); } } } version(XMLSTORAGE) { void dump_values(XMLStorage.XMLPos o); } protected: SqlStatement _stmt; ColumnDataVector _columns; int[char[]] _name_map; // column index cache int* find_column(in char[] col_name) { // initialize column name map if (!_name_map) { int idx = 0; foreach(col; _columns) _name_map[toupper(col._type._name)] = idx++; } return toupper(col_name) in _name_map; } int get_column_idx(in char[] col_name) { int* found = find_column(col_name); if (found) return *found; else { throw new MSqlException("column not found"); //return -1; } } }; version(MYSQLPL_PREP_STMT) { class SqlResult : public SqlRecord { this(SqlStatement stmt) { super(stmt); version(MYSQLPL_PREP_STMT) { stmt.bind_result(_columns); } } bool is_null(int idx) { return get_column(idx).is_null(); } int is_null(in char[] col_name) { return get_column(col_name).is_null(); } bool is_not_null(int idx) { return get_column(idx).is_not_null(); } int is_not_null(in char[] col_name) { return get_column(col_name).is_not_null(); } int get_int(int idx) { return get_column(idx).get_int(); } int get_int(in char[] col_name) { return get_column(col_name).get_int(); } long get_int64(int idx) { return get_column(idx).get_int64(); } long get_int64(in char[] col_name) { return get_column(col_name).get_int64(); } double get_double(int idx) { return get_column(idx).get_double(); } double get_double(in char[] col_name) { return get_column(col_name).get_double(); } char[] get_string(int idx) { return get_column(idx).str(); } char[] get_string(in char[] col_name) { return get_column(col_name).str(); } version(MYSQLPL_PREP_STMT) { // always used in this section SqlDate get_date(int idx, MYSQL_TIMESTAMP_TYPE type=MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATE) { return get_column(idx).date(type); } SqlDate get_date(in char[] col_name, MYSQL_TIMESTAMP_TYPE type=MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATE) { return get_column(col_name).date(type); } } }; } else { // version(MYSQLPL_PREP_STMT) /// class to retrieve query result values class SqlResult : public SqlRecord { MYSQL_ROW _row; this(SqlStatement stmt) { super(stmt); _row = null; } bool is_null(int idx) { return _row[idx] == null; } int is_null(in char[] col_name) { return is_null(get_column_idx(col_name)); } bool is_not_null(int idx) { return _row[idx] != null; } int is_not_null(in char[] col_name) { return is_not_null(get_column_idx(col_name)); } int get_int(int idx) { return std.c.stdlib.atoi(_row[idx]); } int get_int(in char[] col_name) { return get_int(get_column_idx(col_name)); } long get_int64(int idx) { return std.c.stdlib.atoll(_row[idx]); } long get_int64(in char[] col_name) { return get_int64(get_column_idx(col_name)); } double get_double(int idx) { return std.c.stdlib.atof(_row[idx]); } double get_double(in char[] col_name) { return get_double(get_column_idx(col_name)); } char[] get_string(int idx) { char* str = _row[idx]; return str? str[0..strlen(str)]: ""; } char[] get_string(in char[] col_name) { return get_string(get_column_idx(col_name)); } version(MYSQLPL_PREP_STMT) { // never used SqlDate get_date(int idx, MYSQL_TIMESTAMP_TYPE type=MYSQL_TIMESTAMP_DATE) { return get_column(idx).date(type); } SqlDate get_date(in char[] col_name, MYSQL_TIMESTAMP_TYPE type=MYSQL_TIMESTAMP_DATE) { return get_date(get_column_idx(col_name)); } } }; } // MYSQLPL_PREP_STMT class BindPar { void* _ptr; int _len; MYSQL_FIELD_TYPES _type; my_bool* _pind; uint* _plen; this() { _ptr = null; _len = 0; _type = cast(MYSQL_FIELD_TYPES)-1; _pind = null; _plen = null; } this(in char[] str, my_bool* pind=null) { _ptr = str.ptr; _len = str.length; _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING; _pind = pind; _plen = null; } this(in int* var, my_bool* pind=null) { _ptr = var; _len = (*var).sizeof; _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG; _pind = pind; _plen = null; } this(in double* var, my_bool* pind=null) { _ptr = var; _len = (*var).sizeof; _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE; _pind = pind; _plen = null; } version(MYSQLPL_PREP_STMT) { this(SqlInt var) { _ptr = var.get_ref(); _len = int.sizeof; _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG; _pind = var.ind().get_ref(); _plen = null; } this(SqlInteger var) { _ptr = var.get_ref(); _len = LONGLONG.sizeof; _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG; _pind = var.ind().get_ref(); _plen = null; } this(in SqlString str) { _ptr = str.c_str().ptr; _len = str.len(); _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING; _pind = null; _plen = null; } /*@@ SqlString as output buffer this(SqlString str) { _ptr = str.str().ptr; _len = str.blen();//alen(); _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_VAR_STRING; _pind = str.ind().get_ref(); _plen = str.get_len_ref(); } */ this(SqlDate date) { _ptr = &date._value; _len = MYSQL_TIME.sizeof; _type = date.get_data_type(); _pind = date.ind().get_ref(); _plen = null; } this(SqlNumber num) { _ptr = num.get_ref(); _len = double.sizeof; _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE; _pind = num.ind().get_ref(); _plen = null; } this(MSqlBlob blob) { _ptr = blob.get_ref(); _len = blob.get_len(); _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_BLOB; _pind = blob.ind().get_ref(); _plen = cast(uint*)blob.get_len_ref(); } } // version(MYSQLPL_PREP_STMT) }; class SqlQuery { this(in char[] sql) { _param_bind_vector = new BindVector; _sql = sql; } void where(in char[] cond) { if (!_cond.length) _cond ~= "\nwhere "; else _cond ~= "\nand "; _cond ~= cond; } char[] get_sql() { return _sql ~ _cond; } void bind_param(BindPar par) { int idx = _param_bind_vector.length(); _param_bind_vector.resize(idx+1); _param_bind_vector.bind(idx, par); } BindVector get_params() { return _param_bind_vector; } protected: char[] _sql; char[] _cond; BindVector _param_bind_vector; }; /// a MySQL SQL statement class SqlStatement { this(MSqlConnection conn) { _db = conn(); _state = STATE.UNINITIALIZED; // _res = null; _stmt_type = -1; // _result = null; _param_bind_vector = new BindVector; version(MYSQLPL_PREP_STMT) { _stmt = mysqlpl_stmt_init(_db); } } this(MSqlConnection conn, in char[] sql) { _db = conn(); _state = STATE.UNINITIALIZED; // _res = null; _stmt_type = -1; // _result = null; _param_bind_vector = new BindVector; version(MYSQLPL_PREP_STMT) { _stmt = mysqlpl_stmt_init(_db); } prepare(sql); } this(MSqlConnection conn, in SqlQuery query) { _db = conn(); _state = STATE.UNINITIALIZED; // _res = null; _stmt_type = -1; // _result = null; _param_bind_vector = new BindVector; version(MYSQLPL_PREP_STMT) { _stmt = mysqlpl_stmt_init(_db); } execute(query); } ~this() { version(MYSQLPL_PREP_STMT) { if (_stmt) mysqlpl_stmt_close(_stmt); bindvector_free(_result_bind_vector); _result_bind_vector = null; } else { if (_result) mysqlpl_free_result(_result); } } protected: MYSQL* _db; version(MYSQLPL_PREP_STMT) { MYSQL_STMT* _stmt; MYSQL_BIND* _result_bind_vector; } else { char[] _sql; // create sql statement from prepared statement and integrate bind variables char[] create_bound_sql() { char[] str = _sql; char[] sql; foreach(BindPar value; _param_bind_vector) { int idx = next_bind_variable(str); if (idx == -1) throw new MSqlException("too much bind values"); sql ~= str[0 .. idx]; switch(value._type) { case MYSQL_FIELD_TYPES.MYSQL_TYPE_VAR_STRING: { sql ~= '\''; // escape quote characters in bound string constants char[] p = (cast(char*)value._ptr)[0 .. value._len]; foreach(c; p) { if (c == '\'') sql ~= "''"; else sql ~= c; } sql ~= '\''; break;} case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG: sql ~= format("%d", *cast(int*)value._ptr); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE: sql ~= format("%f", *cast(double*)value._ptr); break; default: throw new MSqlException("unsupported datatype"); } str = str[idx+1 .. length]; } if (next_bind_variable(str) != -1) throw new MSqlException("too less bind values"); sql ~= str; return sql; } static int next_bind_variable(in char[] p) { for(int i=0; i SELECT) _result_bind_vector = bindvector_resize(_result_bind_vector, get_column_count()); // define resultset variables assert(!_res); _res = new SqlResult(this); } } MYSQL_BIND* param_mbind_vector; // bind parameter values if (!_param_bind_vector.empty()) { // Umwandlung der Bindparamater von BindPar in MYSQL_BIND-Strukturelemente param_mbind_vector = bindvector_resize(param_mbind_vector, _param_bind_vector.length); MYSQL_BIND* o = param_mbind_vector; foreach(i; _param_bind_vector) { mysqlpl_bind_param(i, o); o = bindvector_next(o); } my_bool res = mysqlpl_stmt_bind_param(_stmt, param_mbind_vector); mysqlpl_check_error(_stmt, res); } int res = mysqlpl_stmt_execute(_stmt); mysqlpl_check_error(_stmt, res); } else { int res = mysqlpl_query(_db, toStringz(create_bound_sql())); mysqlpl_check_error(_db, res); if (_result) mysqlpl_free_result(_result); _result = mysqlpl_store_result(_db); if (_result) { // is_select() // define resultset variables if (_res) delete _res; _res = new SqlResult(this); } } _state = (_state|STATE.EXECUTED) & ~STATE.FETCHED; } void execute(in char[] sql) { prepare(sql); execute(); } void execute(in SqlQuery query) { prepare(query.get_sql()); bind_params(query.get_params()); execute(); } bool execute_fetch() { execute(); return fetch(); } bool execute_fetch(in char[] sql) { prepare(sql); return execute_fetch(); } bool fetch() { if (!(_state & STATE.EXECUTED)) execute(); version(MYSQLPL_PREP_STMT) { int res = mysqlpl_stmt_fetch(_stmt); if (res == MYSQL_NO_DATA) return false; mysqlpl_check_error(_stmt, res); } else { _res._row = mysqlpl_fetch_row(_result); if (!_res._row) return false; } _state |= STATE.FETCHED; return true; } MYSQL_RES* get_meta_res() { version(MYSQLPL_PREP_STMT) { if (!_result) { _result = mysqlpl_stmt_result_metadata(_stmt); if (!_result) throw new MSqlException("no result set metadata"); } } return _result; } bool is_select() { version(MYSQLPL_PREP_STMT) { if (!_result) _result = mysqlpl_stmt_result_metadata(_stmt); return _result!=null; } else { if (!(_state & STATE.EXECUTED)) throw new MSqlException("statement not yet executed"); return _result!=null; } } int get_column_count() { version(MYSQLPL_PREP_STMT) { uint count = mysqlpl_num_fields(get_meta_res()); if (count == -1u) throw new MSqlException(_stmt); return count; } else { return mysqlpl_num_fields(_result); } } void get_column_type(int idx, out ColumnType info) { MYSQL_FIELD* field = mysqlpl_fetch_field_direct(get_meta_res(), idx); if (!field) version(MYSQLPL_PREP_STMT) { throw new MSqlException(_stmt); } else { throw new MSqlException(_db); } info.init(field); } void print_results(Stream o, bool header=true) { if (!(_state & STATE.EXECUTED)) execute(); if (header) _res.print_columns(o); int row_cnt = 0; while(fetch()) { ++row_cnt; o.writeString(format("row %d:\n", row_cnt)); _res.print_values(o); o.write('\n'); } } void print_results(in char[] sql, Stream o, bool header=true) { execute(sql); print_results(o, header); } SqlResult opCall() { if (!_res) fetch(); return _res; } version(XMLSTORAGE) {//@@ void dump_results(XMLStorage.XMLPos idx, in char[] element_name); void dump_results(in char[] sql, XMLStorage.XMLPos idx, in char[] element_name) { execute(sql); dump_results(idx, element_name); } } }; // SqlStatement version(MYSQLPL_PREP_STMT) { /// indicator variable class SqlInd { this(my_bool is_null=1) { _is_null = is_null; } this(SqlInd other) { _is_null = other._is_null; } my_bool opCast() {return _is_null;} my_bool* get_ref() {return &_is_null;} bool is_null() {return _is_null!=0;} bool is_not_null() {return _is_null==0;} void clear() {_is_null = 1;} void set() {_is_null = 0;} protected: my_bool _is_null; }; class SqlValue { this() { _ind = new SqlInd(); } this(in SqlValue other) { _ind = new SqlInd(other._ind); } bool is_null() { return _ind.is_null(); } bool is_not_null() { return _ind.is_not_null(); } SqlInd ind() { return _ind; } protected: SqlInd _ind; }; /// int with indicator variable class SqlInt : public SqlValue { this() // null constructor { } this(int value) { _value = value; _ind.set(); } this(in SqlInt other) { super(other); _value = other._value; } const int INT_NULL = -1; int opCast() {return _ind.is_null()? INT_NULL: _value;} int* get_ref() {return &_value;} char[] str() { if (_ind.is_null()) return ""; return format("%d", _value); } protected: int _value; }; /// 64 bit integer with indicator variable class SqlInteger : public SqlValue { this() // null constructor { } this(int value) { _value = value; _ind.set(); } this(in SqlInteger other) { super(other); _value = other._value; } const int INT_NULL = -1; LONGLONG opCast() {return _ind.is_null()? INT_NULL: _value;} LONGLONG* get_ref() {return &_value;} char[] str() { if (_ind.is_null()) return ""; return format("%d", _value); } protected: LONGLONG _value; }; class SqlString : public SqlValue { this(int blen=2000) { _str.length = blen; _len = 0; } this(in char[] s, int blen=2000) { _str.length = blen; opAssign(s); _ind.set(); } this(in SqlString other) { super(other); _str = other._str.dup; _len = other._len; } int len() {return is_null()? 0: _len;} int blen() {return _str.length;} // int alen() {return _str.length + 1;} void resize(int l) {_str.length = l;} void clear() {_str.length = 0; _ind.clear();} char[] str() {return _str[0.._len];} // Note: _ind is not checked by this function. char[] str(int l) {resize(l); return _str[0.._len];} // Note: _ind is not changed by this function. char[] c_str() {return is_null()? "": _str[0.._len];} SqlString opAssign(in char[] s) { if (s) { _str[0 .. s.length] = s[]; _len = s.length; _ind.set(); } else { _len = 0; _ind.clear(); } return this; } Stream writeString(Stream os) { if (is_not_null()) os.writeString(_str); return os; } protected: char[] _str; size_t _len; }; struct MYSQL_TIME { uint year, month, day, hour, minute, second; uint second_part; my_bool neg; MYSQL_TIMESTAMP_TYPE time_type; }; /// wrapper for MYSQL_TIME class SqlDate : public SqlValue { this(MYSQL_TIMESTAMP_TYPE type=MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATETIME) { _value.time_type = type; } this(MYSQL_TIME time, MYSQL_TIMESTAMP_TYPE type=MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATETIME) { _value = time; _value.time_type = MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATETIME; } this(in char[] str, MYSQL_TIMESTAMP_TYPE type=MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATETIME) { while(str.length>0 && std.ctype.isspace(str[0])) str = str[1 .. length]; char* s = toStringz(str); if (s && *s) { switch(_value.time_type) { case MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATE: if (sscanf(s, "%04d-%02d-%02d", &_value.year, &_value.month, &_value.day) != 3) if (sscanf(s, "%02d.%02d.%04d", &_value.day, &_value.month, &_value.year) != 3) throw new MSqlException("date conversion error"); break; case MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_TIME: _value.second_part = 0; if (sscanf(s, "%02d:%02d:%02d.%06d", &_value.hour, &_value.minute, &_value.second, &_value.second_part) < 3) throw new MSqlException("time conversion error"); break; //case MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATETIME: default: _value.hour = 0; _value.minute = 0; _value.second = 0; _value.second_part = 0; if (sscanf(s, "%04d-%02d-%02d %02d:%02d:%02d.%06d", &_value.year, &_value.month, &_value.day, &_value.hour, &_value.minute, &_value.second, &_value.second_part) < 6) if (sscanf(s, "%02d.%02d.%04d %02d:%02d:%02d.%06d", &_value.day, &_value.month, &_value.year, &_value.hour, &_value.minute, &_value.second, &_value.second_part) < 3) throw new MSqlException("datetime conversion error"); } _value.time_type = type; _ind.set(); } else _ind.clear(); } this(in SqlDate other, MYSQL_TIMESTAMP_TYPE type) { _value = other._value; if (other._value.time_type != type) { if (type==MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_NONE || type==MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_ERROR) _value.time_type = type; else { if (other._value.time_type == MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATE) { if (type == MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_TIME) throw new MSqlException("no conversion from time to date"); _value.hour = 0; _value.minute = 0; _value.second = 0; } else if (other._value.time_type == MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_TIME) { if (type == MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATE) throw new MSqlException("no conversion from date to time"); _value.year = 0; _value.month = 0; _value.day = 0; } _value.time_type = type; } } } MYSQL_FIELD_TYPES get_data_type() { switch(_value.time_type) { case MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATE: return MYSQL_FIELD_TYPES.MYSQL_TYPE_DATE; case MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_TIME: return MYSQL_FIELD_TYPES.MYSQL_TYPE_TIME; case MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATETIME: return MYSQL_FIELD_TYPES.MYSQL_TYPE_DATETIME; default: return cast(MYSQL_FIELD_TYPES)-1; } } char[] str() { if (_ind.is_not_null()) { switch(_value.time_type) { case MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATE: return format("%04d-%02d-%02d", _value.year, _value.month, _value.day); break; case MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_TIME: return format("%02d:%02d:%02d.%06d", _value.hour, _value.minute, _value.second, _value.second_part); break; //case MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATETIME: default: return format("%04d-%02d-%02d %02d:%02d:%02d.%06d", _value.year, _value.month, _value.day, _value.hour, _value.minute, _value.second, _value.second_part); } } else return ""; } MYSQL_TIME opCast() {return _value;} MYSQL_TIME _value; }; class SqlNumber : public SqlValue { this() { } this(int i) { _value = i; _ind.set(); } this(LONGLONG l) { _value = l; _ind.set(); } this(double d) { _value = d; _ind.set(); } this(in char[] s) { if (sscanf(toStringz(s), "%lf", &_value) == 1) // %lf -> double _ind.set(); } //@@ void* get_ref() {return &_value;} char[] str() { if (_ind.is_null()) return ""; return format("%f", _value); // %f -> double } double opCast() {return _value;} double* get_ref() {return &_value;} int get_int() { return _ind.is_not_null()? cast(int)(_value+.5): 0; } long get_int64() { return _ind.is_not_null()? cast(long)(_value+.5): 0; } double get_double() { return _ind.is_not_null()? _value: 0.; } protected: double _value; }; class MSqlBlob : public SqlValue { this(int len=0x10000) { _len = len; _ptr = null; _own = true; } this(void* ptr, int len) { _len = len; _ptr = ptr; _own = false; } this(in MSqlBlob other) { super(other); _len = other._len; _own = true; if (other._ptr) memcpy(_ptr=std.c.stdlib.malloc(other._len), other._ptr, other._len); else _ptr = null; } ~this() { if (_own) std.c.stdlib.free(_ptr); } void* get_ref() { if (_own && !_ptr) // delayed buffer allocation _ptr = std.c.stdlib.malloc(_len); return _ptr; } int get_len() {return _len;} ulong* get_len_ref() {return &_len;} char[] str() { if (_ind.is_not_null()) { return (cast(char*)_ptr)[0 .. _len]; } else return ""; } protected: ulong _len; void* _ptr; bool _own; }; /// factory for call parameters for MySQL define functions class BindResultPar { void* _ptr; int _len; MYSQL_FIELD_TYPES _type; my_bool* _pind; uint* _plen; this(SqlInt var) { _ptr = var.get_ref(); _len = int.sizeof; _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG; _pind = var.ind().get_ref(); _plen = null; } this(SqlInteger var) { _ptr = var.get_ref(); _len = LONGLONG.sizeof; _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG; _pind = var.ind().get_ref(); _plen = null; } this(int* var, my_bool* pind=null) { _ptr = var; _len = int.sizeof; _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG; _pind = pind; _plen = null; } this(in char[] buffer, int len, MYSQL_FIELD_TYPES type=MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING/*MYSQL_TYPE_VAR_STRING*/, my_bool* pind=null) { _ptr = buffer.ptr; _len = len; _type = type; _pind = pind; _plen = null; } this(SqlString str) { _ptr = str.str().ptr; _len = str.blen();//alen(); _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING; _pind = str.ind().get_ref(); _plen = &str._len; } this(SqlNumber num) { _ptr = num.get_ref(); _len = double.sizeof; _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE; _pind = num.ind().get_ref(); _plen = null; } version(MYSQLPL_PREP_STMT) { this(SqlDate date) { _ptr = &date._value; _len = date._value.sizeof; _type = date.get_data_type(); _pind = date.ind().get_ref(); _plen = null; } } this(MSqlBlob blob) { _ptr = blob.get_ref(); _len = blob.get_len(); _type = MYSQL_FIELD_TYPES.MYSQL_TYPE_BLOB; _pind = blob.ind().get_ref(); _plen = cast(uint*)blob.get_len_ref(); } }; class SqlVariant { this() { _data_type = cast(MYSQL_FIELD_TYPES)-1; } this(MYSQL_FIELD_TYPES data_type) { _data_type = data_type; switch(data_type) { case MYSQL_FIELD_TYPES.MYSQL_TYPE_VAR_STRING: _data_type = MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING; // fall through case MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING: _string = new SqlString; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_TINY: case MYSQL_FIELD_TYPES.MYSQL_TYPE_SHORT: case MYSQL_FIELD_TYPES.MYSQL_TYPE_INT24: case MYSQL_FIELD_TYPES.MYSQL_TYPE_YEAR: case MYSQL_FIELD_TYPES.MYSQL_TYPE_ENUM: _data_type = MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG; // fall through case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG: _int = new SqlInt; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG: _integer = new SqlInteger; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DECIMAL: case MYSQL_FIELD_TYPES.MYSQL_TYPE_NEWDECIMAL: // MYSQL_VERSION_ID>=50000 case MYSQL_FIELD_TYPES.MYSQL_TYPE_FLOAT: _data_type = MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE; // fall through case MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE: _number = new SqlNumber; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATE: _date = new SqlDate(MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATE); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_NEWDATE: // MYSQL_VERSION_ID>=50000 _date = new SqlDate(MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATE); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATETIME: _date = new SqlDate(MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATETIME); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIMESTAMP: _date = new SqlDate(MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_TIME); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_TINY_BLOB: case MYSQL_FIELD_TYPES.MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG_BLOB: _data_type = MYSQL_FIELD_TYPES.MYSQL_TYPE_BLOB; // fall through case MYSQL_FIELD_TYPES.MYSQL_TYPE_BLOB: _blob = new MSqlBlob; break; default: throw new MSqlException("unsupported datatype"); } } this(in SqlVariant other) { _data_type = cast(MYSQL_FIELD_TYPES)-1; assign(other); } ~this() { clear(); } this(in SqlString x) { _string = new SqlString(x); _data_type = MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING; } this(in char[] x) { _string = new SqlString(x); _data_type = MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING; } this(in SqlInt x) { _int = new SqlInt(x); _data_type = MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG; } this(in SqlInteger x) { _integer = new SqlInteger(x); _data_type = MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG; } this(in SqlNumber x) { _number = new SqlNumber(cast(double)x); _data_type = MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE; } version(MYSQLPL_PREP_STMT) { this(in SqlDate x) { _date = new SqlDate(cast(MYSQL_TIME)x); _data_type = x.get_data_type(); } } void clear() { switch(_data_type) { case cast(MYSQL_FIELD_TYPES)-1: break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING: delete _string; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG: delete _int; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG: delete _integer; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE: delete _number; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATE: case MYSQL_FIELD_TYPES.MYSQL_TYPE_NEWDATE: // MYSQL_VERSION_ID>=50000 case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATETIME: case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIME: case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIMESTAMP: delete _date; break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_BLOB: delete _blob; break; default: assert(0); } _data_type = cast(MYSQL_FIELD_TYPES)-1; } void assign(in SqlVariant other) { clear(); _data_type = other._data_type; switch(other._data_type) { case cast(MYSQL_FIELD_TYPES)-1: break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING: _string = new SqlString(other._string); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG: _int = new SqlInt(other._int); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG: _integer = new SqlInteger(other._integer); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE: _number = new SqlNumber(cast(double)other._number); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATE: case MYSQL_FIELD_TYPES.MYSQL_TYPE_NEWDATE: // MYSQL_VERSION_ID>=50000 case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATETIME: case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIME: case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIMESTAMP: _date = new SqlDate(other._date._value); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_BLOB: _blob = new MSqlBlob(other._blob); break; default: assert(0); } } int get_data_type() { return _data_type; } char[] str() { switch(_data_type) { case cast(MYSQL_FIELD_TYPES)-1: return ""; case MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING: return _string.c_str(); // return *_string; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG: return _int.str(); case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG: return _integer.str(); case MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE: return _number.str(); case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATE: case MYSQL_FIELD_TYPES.MYSQL_TYPE_NEWDATE: // MYSQL_VERSION_ID>=50000 case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATETIME: case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIME: case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIMESTAMP: return _date.str(); case MYSQL_FIELD_TYPES.MYSQL_TYPE_BLOB: return _blob.str(); break; default: assert(0); return ""; } } int get_int() { switch(_data_type) { case cast(MYSQL_FIELD_TYPES)-1: return 0; case MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING: return std.c.stdlib.atoi(toStringz(_string.str())); case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG: return cast(int)_int; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG: return cast(LONGLONG)_integer; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE: return _number.get_int(); //case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATETIME: // return ... default: throw new MSqlException("no conversion to int"); } } long get_int64() { switch(_data_type) { case cast(MYSQL_FIELD_TYPES)-1: return 0; case MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING: return std.c.stdlib.atoll(toStringz(_string.str())); case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG: return cast(int)_int; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG: return cast(LONGLONG)_integer; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE: return _number.get_int64(); //case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATETIME: // return ... default: throw new MSqlException("no conversion to int64"); } } double get_double() { switch(_data_type) { case cast(MYSQL_FIELD_TYPES)-1: return 0; case MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING: return std.c.stdlib.atof(toStringz(_string.str())); case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG: return cast(int)_int; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG: return cast(LONGLONG)_integer; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE: return _number.get_double(); //case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATETIME: // return ... default: throw new MSqlException("no conversion to double"); } } SqlNumber number() { switch(_data_type) { case cast(MYSQL_FIELD_TYPES)-1: return new SqlNumber(); case MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING: return new SqlNumber(_string.str()); case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG: return new SqlNumber(cast(int)_int); case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG: return new SqlNumber(cast(LONGLONG)_integer); case MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE: return _number; //case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATETIME: // return ... default: throw new MSqlException("no conversion to number"); } } bool is_null() { switch(_data_type) { case cast(MYSQL_FIELD_TYPES)-1: return true; case MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING: return _string.is_null(); case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG: return _int.is_null(); case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG: return _integer.is_null(); case MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE: return _number.is_null(); case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATE: case MYSQL_FIELD_TYPES.MYSQL_TYPE_NEWDATE: // MYSQL_VERSION_ID>=50000 case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATETIME: case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIME: case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIMESTAMP: return _date.is_null(); case MYSQL_FIELD_TYPES.MYSQL_TYPE_BLOB: return _blob.is_null(); default: assert(0); return true; } } bool is_not_null() { return !is_null(); } version(MYSQLPL_PREP_STMT) { void bind_result_column(SqlStatement stmt, MYSQL_BIND* bind) { switch(_data_type) { case MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING: stmt.bind_result_column(new BindResultPar(_string), bind); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONG: stmt.bind_result_column(new BindResultPar(_int), bind); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_LONGLONG: stmt.bind_result_column(new BindResultPar(_integer), bind); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DOUBLE: stmt.bind_result_column(new BindResultPar(_number), bind); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATE: case MYSQL_FIELD_TYPES.MYSQL_TYPE_NEWDATE: // MYSQL_VERSION_ID>=50000 case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATETIME: case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIME: case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIMESTAMP: stmt.bind_result_column(new BindResultPar(_date), bind); break; case MYSQL_FIELD_TYPES.MYSQL_TYPE_BLOB: stmt.bind_result_column(new BindResultPar(_blob), bind); break; default: assert(0); } } } version(MYSQLPL_PREP_STMT) { SqlDate date(MYSQL_TIMESTAMP_TYPE type=MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATE) { switch(_data_type) { case cast(MYSQL_FIELD_TYPES)-1: return new SqlDate(); case MYSQL_FIELD_TYPES.MYSQL_TYPE_STRING: return new SqlDate(_string.str(), MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATE); case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATE: case MYSQL_FIELD_TYPES.MYSQL_TYPE_NEWDATE: // MYSQL_VERSION_ID>=50000 case MYSQL_FIELD_TYPES.MYSQL_TYPE_DATETIME: case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIME: case MYSQL_FIELD_TYPES.MYSQL_TYPE_TIMESTAMP: return new SqlDate(_date, type); default: throw new MSqlException("no conversion to date type"); } } SqlDate datetime() { return date(MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_DATETIME); } SqlDate time() { return date(MYSQL_TIMESTAMP_TYPE.MYSQL_TIMESTAMP_TIME); } } protected: union { SqlString _string; SqlInt _int; SqlInteger _integer; SqlNumber _number; version(MYSQLPL_PREP_STMT) { SqlDate _date; } MSqlBlob _blob; }; MYSQL_FIELD_TYPES _data_type; }; }