# ------------------------------------------------------
# MySQL/ADO.NET driver for IronRuby
# Author: Brian Blackwell (brianblackwell@realemail.net)
# Based on pure Ruby MySQL driver by TOMITA Masahiro
# tommy@tmtm.org
# ------------------------------------------------------

# change to path of your MySQL Connector/NET DLL
load "C:\\brian\\mysql-ironruby\\MySql.Data.DLL"

class Mysql

  VERSION = "0.1-ironruby"

  # Client flag
  CLIENT_LONG_PASSWORD	= 1
  CLIENT_FOUND_ROWS	= 1 << 1
  CLIENT_LONG_FLAG	= 1 << 2
  CLIENT_CONNECT_WITH_DB= 1 << 3
  CLIENT_NO_SCHEMA	= 1 << 4
  CLIENT_COMPRESS	= 1 << 5
  CLIENT_ODBC		= 1 << 6
  CLIENT_LOCAL_FILES	= 1 << 7
  CLIENT_IGNORE_SPACE	= 1 << 8
  CLIENT_PROTOCOL_41	= 1 << 9
  CLIENT_INTERACTIVE	= 1 << 10
  CLIENT_SSL		= 1 << 11
  CLIENT_IGNORE_SIGPIPE	= 1 << 12
  CLIENT_TRANSACTIONS	= 1 << 13
  CLIENT_RESERVED	= 1 << 14
  CLIENT_SECURE_CONNECTION	= 1 << 15
  CLIENT_CAPABILITIES = CLIENT_LONG_PASSWORD|CLIENT_LONG_FLAG|CLIENT_TRANSACTIONS
  PROTO_AUTH41 = CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION

  # Connection Option
  OPT_CONNECT_TIMEOUT	= 0
  OPT_COMPRESS		= 1
  OPT_NAMED_PIPE	= 2
  INIT_COMMAND		= 3
  READ_DEFAULT_FILE	= 4
  READ_DEFAULT_GROUP	= 5
  SET_CHARSET_DIR	= 6
  SET_CHARSET_NAME	= 7
  OPT_LOCAL_INFILE	= 8

  # Server Status
  SERVER_STATUS_IN_TRANS	= 1
  SERVER_STATUS_AUTOCOMMIT	= 2

  # Refresh parameter
  REFRESH_GRANT		= 1
  REFRESH_LOG		= 2
  REFRESH_TABLES	= 4
  REFRESH_HOSTS		= 8
  REFRESH_STATUS	= 16
  REFRESH_THREADS	= 32
  REFRESH_SLAVE		= 64
  REFRESH_MASTER	= 128

  # methods to implement
  #        affected_rows( ); errno( ); init( ); initialize( ); insert_id( ); 
  #        options(Fixnum, String); query(String); query_with_result=(TrueClass); 
  #        quote(String); real_connect(NilClass, String, String, String, NilClass, NilClass); stat( )

  def initialize(*args)
    @query_with_result = true
    if args[0] != :INIT then
      real_connect(*args)
    end
  end

  def real_connect(host="localhost", user=nil, passwd=nil, db=nil, port=nil, socket=nil, flag=nil)
    @conn_params = Hash.new
    @conn_params["server"] = host
    @conn_params["user id"] = user
    @conn_params["password"] = passwd
    @conn_params["database"] = db
    @conn_params["port"] = port
    @conn_params["socket"] = socket
    @conn_params["flag"] = flag
    
    begin
      @conn = MySql::Data::MySqlClient::MySqlConnection.new(connection_string())
      @conn.Open
    rescue MySql::Data::MySqlClient::MySqlException => e
      raise Error.new(e.Number.to_i, e.Message)
    end
    return @conn
  end
  alias :connect :real_connect

  def handle_auth_fallback(pkt, passwd)
    raise "not implemented"
  end

  def escape_string(str)
    raise "not implemented"
  end
  alias :quote :escape_string

  def get_client_info()
  end
  alias :client_info :get_client_info

  def options(option, arg=nil)
    # FIXME: the connection is closed and re-opened when an option is set; any way around this in ADO.NET?
    # not implemented
    if @conn != nil
      if option == OPT_CONNECT_TIMEOUT
        @conn.ConnectionTimeout = arg
      elsif option == OPT_COMPRESS
        if arg
          @conn.UseCompression = true
        else
          @conn.UseCompression = false
        end
      elsif option == OPT_NAMED_PIPE
        @conn_params["pipe"] = arg
        @conn.Close
        @conn.ConnectionString = connection_string()
        @conn.Open
      elsif option == SET_CHARSET_NAME
        @conn_params["charset"] = arg
        @conn.Close
        @conn.ConnectionString = connection_string()
        @conn.Open
      else
        raise "not implemented"
      end
    end
  end

  def real_query(query)
    raise "not implemented"
  end

  def use_result()
    raise "not implemented"
  end

  def store_result()
    @result
  end

  def change_user(user=nil, passwd=nil, db=nil)
    if @conn != nil
      @conn_params["user id"] = user
      @conn_params["password"] = passwd
      @conn_params["db"] = db
      @conn.Close
      @conn.ConnectionString = connection_string()
      @conn.Open
    end
  end

  def character_set_name()
    raise "not implemented"
  end

  def close()
    if @conn != nil
      @conn.Close
    end
  end

  def create_db(db)
    begin
      if @conn != nil
        cmd = @conn.CreateCommand
        cmd.CommandText = "CREATE DATABASE " + db.to_s
        cmd.ExecuteScalar
        cmd.Dispose
      end
    rescue MySql::Data::MySqlClient::MySqlException => e
      raise Error.new(e.Number.to_i, e.Message)
    end
  end

  def drop_db(db)
    begin
      if @conn != nil
        cmd = @conn.CreateCommand
        cmd.CommandText = "DROP DATABASE " + db.to_s
        cmd.ExecuteScalar
        cmd.Dispose
      end
    rescue MySql::Data::MySqlClient::MySqlException => e
      raise Error.new(e.Number.to_i, e.Message)
    end
  end

  def dump_debug_info()
    raise "not implemented"
  end

  def get_host_info()
    raise "not implemented"
  end
  alias :host_info :get_host_info

  def get_proto_info()
    raise "not implemented"
  end
  alias :proto_info :get_proto_info

  def get_server_info()
    if @conn != nil
      return @conn.ServerVersion
    end
  end
  alias :server_info :get_server_info

  def kill(id)
    raise "not implemented"
  end

  def list_dbs(db=nil)
    begin
      if @conn != nil
        cmd = @conn.CreateCommand
        cmd.CommandText = "SHOW DATABASES #{db}"
        reader = cmd.ExecuteReader
        rows = []
        i = 0
        while reader.Read
          rows[i] = reader.GetString(0)
          i += 1
        end
        reader.Close
        return rows
      end
    rescue MySql::Data::MySqlClient::MySqlException => e
      raise Error.new(e.Number.to_i, e.Message)
    end
  end

  def list_fields(table, field=nil)
    raise "not implemented"
  end

  def list_processes()
    raise "not implemented"
  end

  def list_tables(table=nil)
    raise "not implemented"
  end

  def ping()
    if @conn != nil
      return @conn.Ping
    end
  end

  def query(query)
    begin
      if @conn != nil
        cmd = @conn.CreateCommand
        cmd.CommandText = query
        reader = cmd.ExecuteReader
        @field_count = reader.FieldCount
        @affected_rows = reader.RecordsAffected
       
        if @field_count > 0
          fields = fields_from_schema_table(reader.GetSchemaTable)
        end
        
        # if this is an insert, get insert_id
        # FIXME: does this need to be atomic? Connector/NET does not provide a last_insert_id method
        # FIXME: need a better test of when an INSERT has been executed
        if query.upcase.include?("INSERT INTO")
          reader.Close
          @insert_id = query_insert_id()
        end
        
        @result = Result.new(self, fields, @field_count, reader)

        if not @query_with_result
          # FIXME: what do we do with the data reader in this case?
          return self
        end
        
        if @field_count == 0
          return nil
        end
        
        return @result
      end
    rescue MySql::Data::MySqlClient::MySqlException => e
      raise Error.new(e.Number.to_i, e.Message)
    end
  end

  def refresh(r)
    raise "not implemented"
  end

  def reload()
    raise "not implemented"
  end

  def select_db(db)
    begin
      if @conn != nil
        @conn.ChangeDatabase(db)
      end
    rescue MySql::Data::MySqlClient::MySqlException => e
      puts e
    end
  end

  def shutdown()
    raise "not implemented"
  end

  def stat()
    raise "not implemented"
  end
  
  def thread_id()
    if @conn != nil
      return @conn.ServerThread
    end
  end

  attr_reader :info, :insert_id, :affected_rows, :field_count
  attr_accessor :query_with_result, :status

  def read_one_row(field_count)
    raise "not implemented"
  end

  def skip_result()
    raise "not implemented"
  end

  def inspect()
  end
  
  def escape_string(str)
    #str.gsub(/([\0\n\r\032\'\"\\])/) do
    #  case $1
    #  when "\0" then "\\0"
    #  when "\n" then "\\n"
    #  when "\r" then "\\r"
    #  when "\032" then "\\Z"
    #  else "\\"+$1
    #  end
    #end    
  end

  private
  
  def fields_from_schema_table(table)
    columns = table.Columns
    i = 0
    keys = []
    while i < columns.Count
      keys << columns.get_Item(i).ColumnName
      i += 1
    end
    
    rows = table.Rows
    i = 0
    fieldRows = []
    while i < rows.Count
      row = Hash.new
      j = 0
      while j < keys.length
        row[keys[j].to_s] = rows.get_Item(i).ItemArray[j].to_s
        j += 1
      end
      fieldRows << row
      i += 1
    end
    
    fields = []
    
    i = 0
    while i < fieldRows.length
      flags = 0
      flags |= Field::AUTO_INCREMENT_FLAG if fieldRows[i]["IsAutoIncrement"]
      flags |= Field::NOT_NULL_FLAG unless fieldRows[i]["AllowDBNull"]
      f = Field.new(fieldRows[i]["BaseTableName"], fieldRows[i]["BaseTableName"], fieldRows[i]["ColumnName"], fieldRows[i]["ColumnSize"], flags, nil, nil, nil, nil)      
      fields << f
      i += 1
    end
    
    return fields
  end

  def connection_string()
    connstr = ""
    
    @conn_params.each do |k, v|
      if v != nil && v != ""
        if connstr.length > 0
          connstr += ";"
        end
        connstr += "#{k}=#{v}"
      end
    end
    return connstr
  end
  
  def query_insert_id()
    insert_id_cmd = @conn.CreateCommand
    insert_id_cmd.CommandText = "SELECT last_insert_id()"
    insert_id_reader = insert_id_cmd.ExecuteReader
          
    if insert_id_reader.Read
      insert_id = insert_id_reader.GetInt32(0)
      insert_id_reader.Close
      return insert_id
    end
    
    return nil
  end
  
  def read_query_result()
    raise "not implemented"
  end

  def unpack_fields(data, long_flag_protocol)
    raise "not implemented"
  end

  def read_rows(field_count)
    raise "not implemented"
  end

  def get_length(data, longlong=nil)
    raise "not implemented"
  end

  def command(cmd, arg=nil, skip_check=nil)
    raise "not implemented"
  end

  def read()
    raise "not implemented"
  end

  def write(arg)
    raise "not implemented"
  end

  def hash_password(password)
    raise "not implemented"
  end

  def scramble(password, message, old_ver)
    raise "not implemented"
  end

  def scramble41(password, message)
    raise "not implemented"
  end

  def error(errno)
    raise "not implemented"
  end
  
  def data_type_to_mysql_type(type)
    if type.to_s == "VARCHAR"
      type = "VAR_STRING"
    end
    
    typestr = "TYPE_" + type.to_s
    
    return Field.const_get(typestr)
  end

  class Result
    def initialize(mysql, fields, field_count, data=nil)
      @handle = mysql
      @fields = fields
      @field_count = field_count
      @data = data
      @current_field = 0
      @current_row = 0
      @eof = false
      @row_count = 0
    end
    attr_accessor :eof

    def data_seek(n)
    end

    def fetch_field()
      return if @current_field >= @field_count
      f = @fields[@current_field]
      @current_field += 1
      f
    end

    def fetch_fields()
      @fields
    end

    def fetch_field_direct(n)
      @fields[n]
    end

    def fetch_lengths()
      raise "not implemented"
    end

    def fetch_row()
      if @data != nil
        if !@data.Read
          return nil
        end
        row = []
        i = 0
        while i < @field_count
          row[i] = @data.GetValue(i)
          i += 1
        end
        
        return row
      end
      
      return nil
    end

    def fetch_hash(with_table=nil)
      if @data != nil
        if !@data.Read
          return nil
        end
        hash = Hash.new
        i = 0
        while i < @field_count
          hash[@fields[i].name] = @data.GetValue(i)
          i += 1
        end
        
        return hash
      end
      
      return nil
    end

    def field_seek(n)
      raise "not implemented"
    end

    def field_tell()
      raise "not implemented"
    end

    def free()
      if @data != nil
        @data.Close
      end
    end

    def num_fields()
      @field_count
    end

    def num_rows()
      if @data != nil
        return @data.RecordsAffected
      end
      
      return 0
    end

    def row_seek(n)
      raise "not implemented"
    end

    def row_tell()
      raise "not implemented"
    end

    def each()
      while (row = fetch_row) != nil do
        yield row
      end
    end

    def each_hash(with_table=nil)
      while (hash = fetch_hash(with_table)) != nil do
        yield hash
      end
    end

    def inspect()
      "#<#{self.class}>"
    end

  end

  class Field
    # Field type
    TYPE_DECIMAL = 0
    TYPE_TINY = 1
    TYPE_SHORT = 2
    TYPE_LONG = 3
    TYPE_FLOAT = 4
    TYPE_DOUBLE = 5
    TYPE_NULL = 6
    TYPE_TIMESTAMP = 7
    TYPE_LONGLONG = 8
    TYPE_INT24 = 9
    TYPE_INT = 9
    TYPE_DATE = 10
    TYPE_TIME = 11
    TYPE_DATETIME = 12
    TYPE_YEAR = 13
    TYPE_NEWDATE = 14
    TYPE_ENUM = 247
    TYPE_SET = 248
    TYPE_TINY_BLOB = 249
    TYPE_MEDIUM_BLOB = 250
    TYPE_LONG_BLOB = 251
    TYPE_BLOB = 252
    TYPE_VAR_STRING = 253
    TYPE_STRING = 254
    TYPE_GEOMETRY = 255
    TYPE_CHAR = TYPE_TINY
    TYPE_INTERVAL = TYPE_ENUM

    # Flag
    NOT_NULL_FLAG = 1
    PRI_KEY_FLAG = 2
    UNIQUE_KEY_FLAG  = 4
    MULTIPLE_KEY_FLAG  = 8
    BLOB_FLAG = 16
    UNSIGNED_FLAG = 32
    ZEROFILL_FLAG = 64
    BINARY_FLAG = 128
    ENUM_FLAG = 256
    AUTO_INCREMENT_FLAG = 512
    TIMESTAMP_FLAG  = 1024
    SET_FLAG = 2048
    NUM_FLAG = 32768
    PART_KEY_FLAG = 16384
    GROUP_FLAG = 32768
    UNIQUE_FLAG = 65536

    def initialize(table, org_table, name, length, type, flags, decimals, def_value, max_length)
      @table = table
      @org_table = org_table
      @name = name
      @length = length
      @type = type
      @flags = flags
      @decimals = decimals
      @def = def_value
      @max_length = max_length
      if (type <= TYPE_INT24 and (type != TYPE_TIMESTAMP or length == 14 or length == 8)) or type == TYPE_YEAR then
	@flags |= NUM_FLAG
      end
    end
    attr_reader :table, :org_table, :name, :length, :type, :flags, :decimals, :def, :max_length

    def inspect()
      "#<#{self.class}:#{@name}>"
    end
  end

  class Error < StandardError
    # Server Error
    ER_HASHCHK			= 1000
    ER_NISAMCHK			= 1001
    ER_NO			= 1002
    ER_YES			= 1003
    ER_CANT_CREATE_FILE		= 1004
    ER_CANT_CREATE_TABLE	= 1005
    ER_CANT_CREATE_DB		= 1006
    ER_DB_CREATE_EXISTS		= 1007
    ER_DB_DROP_EXISTS		= 1008
    ER_DB_DROP_DELETE		= 1009
    ER_DB_DROP_RMDIR		= 1010
    ER_CANT_DELETE_FILE		= 1011
    ER_CANT_FIND_SYSTEM_REC	= 1012
    ER_CANT_GET_STAT		= 1013
    ER_CANT_GET_WD		= 1014
    ER_CANT_LOCK		= 1015
    ER_CANT_OPEN_FILE		= 1016
    ER_FILE_NOT_FOUND		= 1017
    ER_CANT_READ_DIR		= 1018
    ER_CANT_SET_WD		= 1019
    ER_CHECKREAD		= 1020
    ER_DISK_FULL		= 1021
    ER_DUP_KEY			= 1022
    ER_ERROR_ON_CLOSE		= 1023
    ER_ERROR_ON_READ		= 1024
    ER_ERROR_ON_RENAME		= 1025
    ER_ERROR_ON_WRITE		= 1026
    ER_FILE_USED		= 1027
    ER_FILSORT_ABORT		= 1028
    ER_FORM_NOT_FOUND		= 1029
    ER_GET_ERRNO		= 1030
    ER_ILLEGAL_HA		= 1031
    ER_KEY_NOT_FOUND		= 1032
    ER_NOT_FORM_FILE		= 1033
    ER_NOT_KEYFILE		= 1034
    ER_OLD_KEYFILE		= 1035
    ER_OPEN_AS_READONLY		= 1036
    ER_OUTOFMEMORY		= 1037
    ER_OUT_OF_SORTMEMORY	= 1038
    ER_UNEXPECTED_EOF		= 1039
    ER_CON_COUNT_ERROR		= 1040
    ER_OUT_OF_RESOURCES		= 1041
    ER_BAD_HOST_ERROR		= 1042
    ER_HANDSHAKE_ERROR		= 1043
    ER_DBACCESS_DENIED_ERROR	= 1044
    ER_ACCESS_DENIED_ERROR	= 1045
    ER_NO_DB_ERROR		= 1046
    ER_UNKNOWN_COM_ERROR	= 1047
    ER_BAD_NULL_ERROR		= 1048
    ER_BAD_DB_ERROR		= 1049
    ER_TABLE_EXISTS_ERROR	= 1050
    ER_BAD_TABLE_ERROR		= 1051
    ER_NON_UNIQ_ERROR		= 1052
    ER_SERVER_SHUTDOWN		= 1053
    ER_BAD_FIELD_ERROR		= 1054
    ER_WRONG_FIELD_WITH_GROUP	= 1055
    ER_WRONG_GROUP_FIELD	= 1056
    ER_WRONG_SUM_SELECT		= 1057
    ER_WRONG_VALUE_COUNT	= 1058
    ER_TOO_LONG_IDENT		= 1059
    ER_DUP_FIELDNAME		= 1060
    ER_DUP_KEYNAME		= 1061
    ER_DUP_ENTRY		= 1062
    ER_WRONG_FIELD_SPEC		= 1063
    ER_PARSE_ERROR		= 1064
    ER_EMPTY_QUERY		= 1065
    ER_NONUNIQ_TABLE		= 1066
    ER_INVALID_DEFAULT		= 1067
    ER_MULTIPLE_PRI_KEY		= 1068
    ER_TOO_MANY_KEYS		= 1069
    ER_TOO_MANY_KEY_PARTS	= 1070
    ER_TOO_LONG_KEY		= 1071
    ER_KEY_COLUMN_DOES_NOT_EXITS	= 1072
    ER_BLOB_USED_AS_KEY		= 1073
    ER_TOO_BIG_FIELDLENGTH	= 1074
    ER_WRONG_AUTO_KEY		= 1075
    ER_READY			= 1076
    ER_NORMAL_SHUTDOWN		= 1077
    ER_GOT_SIGNAL		= 1078
    ER_SHUTDOWN_COMPLETE	= 1079
    ER_FORCING_CLOSE		= 1080
    ER_IPSOCK_ERROR		= 1081
    ER_NO_SUCH_INDEX		= 1082
    ER_WRONG_FIELD_TERMINATORS	= 1083
    ER_BLOBS_AND_NO_TERMINATED	= 1084
    ER_TEXTFILE_NOT_READABLE	= 1085
    ER_FILE_EXISTS_ERROR	= 1086
    ER_LOAD_INFO		= 1087
    ER_ALTER_INFO		= 1088
    ER_WRONG_SUB_KEY		= 1089
    ER_CANT_REMOVE_ALL_FIELDS	= 1090
    ER_CANT_DROP_FIELD_OR_KEY	= 1091
    ER_INSERT_INFO		= 1092
    ER_INSERT_TABLE_USED	= 1093
    ER_NO_SUCH_THREAD		= 1094
    ER_KILL_DENIED_ERROR	= 1095
    ER_NO_TABLES_USED		= 1096
    ER_TOO_BIG_SET		= 1097
    ER_NO_UNIQUE_LOGFILE	= 1098
    ER_TABLE_NOT_LOCKED_FOR_WRITE	= 1099
    ER_TABLE_NOT_LOCKED		= 1100
    ER_BLOB_CANT_HAVE_DEFAULT	= 1101
    ER_WRONG_DB_NAME		= 1102
    ER_WRONG_TABLE_NAME		= 1103
    ER_TOO_BIG_SELECT		= 1104
    ER_UNKNOWN_ERROR		= 1105
    ER_UNKNOWN_PROCEDURE	= 1106
    ER_WRONG_PARAMCOUNT_TO_PROCEDURE	= 1107
    ER_WRONG_PARAMETERS_TO_PROCEDURE	= 1108
    ER_UNKNOWN_TABLE		= 1109
    ER_FIELD_SPECIFIED_TWICE	= 1110
    ER_INVALID_GROUP_FUNC_USE	= 1111
    ER_UNSUPPORTED_EXTENSION	= 1112
    ER_TABLE_MUST_HAVE_COLUMNS	= 1113
    ER_RECORD_FILE_FULL		= 1114
    ER_UNKNOWN_CHARACTER_SET	= 1115
    ER_TOO_MANY_TABLES		= 1116
    ER_TOO_MANY_FIELDS		= 1117
    ER_TOO_BIG_ROWSIZE		= 1118
    ER_STACK_OVERRUN		= 1119
    ER_WRONG_OUTER_JOIN		= 1120
    ER_NULL_COLUMN_IN_INDEX	= 1121
    ER_CANT_FIND_UDF		= 1122
    ER_CANT_INITIALIZE_UDF	= 1123
    ER_UDF_NO_PATHS		= 1124
    ER_UDF_EXISTS		= 1125
    ER_CANT_OPEN_LIBRARY	= 1126
    ER_CANT_FIND_DL_ENTRY	= 1127
    ER_FUNCTION_NOT_DEFINED	= 1128
    ER_HOST_IS_BLOCKED		= 1129
    ER_HOST_NOT_PRIVILEGED	= 1130
    ER_PASSWORD_ANONYMOUS_USER	= 1131
    ER_PASSWORD_NOT_ALLOWED	= 1132
    ER_PASSWORD_NO_MATCH	= 1133
    ER_UPDATE_INFO		= 1134
    ER_CANT_CREATE_THREAD	= 1135
    ER_WRONG_VALUE_COUNT_ON_ROW	= 1136
    ER_CANT_REOPEN_TABLE	= 1137
    ER_INVALID_USE_OF_NULL	= 1138
    ER_REGEXP_ERROR		= 1139
    ER_MIX_OF_GROUP_FUNC_AND_FIELDS	= 1140
    ER_NONEXISTING_GRANT	= 1141
    ER_TABLEACCESS_DENIED_ERROR	= 1142
    ER_COLUMNACCESS_DENIED_ERROR	= 1143
    ER_ILLEGAL_GRANT_FOR_TABLE	= 1144
    ER_GRANT_WRONG_HOST_OR_USER	= 1145
    ER_NO_SUCH_TABLE		= 1146
    ER_NONEXISTING_TABLE_GRANT	= 1147
    ER_NOT_ALLOWED_COMMAND	= 1148
    ER_SYNTAX_ERROR		= 1149
    ER_DELAYED_CANT_CHANGE_LOCK	= 1150
    ER_TOO_MANY_DELAYED_THREADS	= 1151
    ER_ABORTING_CONNECTION	= 1152
    ER_NET_PACKET_TOO_LARGE	= 1153
    ER_NET_READ_ERROR_FROM_PIPE	= 1154
    ER_NET_FCNTL_ERROR		= 1155
    ER_NET_PACKETS_OUT_OF_ORDER	= 1156
    ER_NET_UNCOMPRESS_ERROR	= 1157
    ER_NET_READ_ERROR		= 1158
    ER_NET_READ_INTERRUPTED	= 1159
    ER_NET_ERROR_ON_WRITE	= 1160
    ER_NET_WRITE_INTERRUPTED	= 1161
    ER_TOO_LONG_STRING		= 1162
    ER_TABLE_CANT_HANDLE_BLOB	= 1163
    ER_TABLE_CANT_HANDLE_AUTO_INCREMENT	= 1164
    ER_DELAYED_INSERT_TABLE_LOCKED	= 1165
    ER_WRONG_COLUMN_NAME	= 1166
    ER_WRONG_KEY_COLUMN		= 1167
    ER_WRONG_MRG_TABLE		= 1168
    ER_DUP_UNIQUE		= 1169
    ER_BLOB_KEY_WITHOUT_LENGTH	= 1170
    ER_PRIMARY_CANT_HAVE_NULL	= 1171
    ER_TOO_MANY_ROWS		= 1172
    ER_REQUIRES_PRIMARY_KEY	= 1173
    ER_NO_RAID_COMPILED		= 1174
    ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE	= 1175
    ER_KEY_DOES_NOT_EXITS	= 1176
    ER_CHECK_NO_SUCH_TABLE	= 1177
    ER_CHECK_NOT_IMPLEMENTED	= 1178
    ER_CANT_DO_THIS_DURING_AN_TRANSACTION	= 1179
    ER_ERROR_DURING_COMMIT	= 1180
    ER_ERROR_DURING_ROLLBACK	= 1181
    ER_ERROR_DURING_FLUSH_LOGS	= 1182
    ER_ERROR_DURING_CHECKPOINT	= 1183
    ER_NEW_ABORTING_CONNECTION	= 1184
    ER_DUMP_NOT_IMPLEMENTED   	= 1185
    ER_FLUSH_MASTER_BINLOG_CLOSED	= 1186
    ER_INDEX_REBUILD 		= 1187
    ER_MASTER			= 1188
    ER_MASTER_NET_READ		= 1189
    ER_MASTER_NET_WRITE		= 1190
    ER_FT_MATCHING_KEY_NOT_FOUND	= 1191
    ER_LOCK_OR_ACTIVE_TRANSACTION	= 1192
    ER_UNKNOWN_SYSTEM_VARIABLE	= 1193
    ER_CRASHED_ON_USAGE		= 1194
    ER_CRASHED_ON_REPAIR	= 1195
    ER_WARNING_NOT_COMPLETE_ROLLBACK	= 1196
    ER_TRANS_CACHE_FULL		= 1197
    ER_SLAVE_MUST_STOP		= 1198
    ER_SLAVE_NOT_RUNNING	= 1199
    ER_BAD_SLAVE		= 1200
    ER_MASTER_INFO		= 1201
    ER_SLAVE_THREAD		= 1202
    ER_TOO_MANY_USER_CONNECTIONS	= 1203
    ER_SET_CONSTANTS_ONLY	= 1204
    ER_LOCK_WAIT_TIMEOUT	= 1205
    ER_LOCK_TABLE_FULL		= 1206
    ER_READ_ONLY_TRANSACTION	= 1207
    ER_DROP_DB_WITH_READ_LOCK	= 1208
    ER_CREATE_DB_WITH_READ_LOCK	= 1209
    ER_WRONG_ARGUMENTS		= 1210
    ER_NO_PERMISSION_TO_CREATE_USER	= 1211
    ER_UNION_TABLES_IN_DIFFERENT_DIR	= 1212
    ER_LOCK_DEADLOCK		= 1213
    ER_TABLE_CANT_HANDLE_FULLTEXT	= 1214
    ER_CANNOT_ADD_FOREIGN	= 1215
    ER_NO_REFERENCED_ROW	= 1216
    ER_ROW_IS_REFERENCED	= 1217
    ER_CONNECT_TO_MASTER	= 1218
    ER_QUERY_ON_MASTER		= 1219
    ER_ERROR_WHEN_EXECUTING_COMMAND	= 1220
    ER_WRONG_USAGE		= 1221
    ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT	= 1222
    ER_CANT_UPDATE_WITH_READLOCK	= 1223
    ER_MIXING_NOT_ALLOWED	= 1224
    ER_DUP_ARGUMENT		= 1225
    ER_USER_LIMIT_REACHED	= 1226
    ER_SPECIFIC_ACCESS_DENIED_ERROR	= 1227
    ER_LOCAL_VARIABLE		= 1228
    ER_GLOBAL_VARIABLE		= 1229
    ER_NO_DEFAULT		= 1230
    ER_WRONG_VALUE_FOR_VAR	= 1231
    ER_WRONG_TYPE_FOR_VAR	= 1232
    ER_VAR_CANT_BE_READ		= 1233
    ER_CANT_USE_OPTION_HERE	= 1234
    ER_NOT_SUPPORTED_YET   	= 1235
    ER_MASTER_FATAL_ERROR_READING_BINLOG	= 1236
    ER_SLAVE_IGNORED_TABLE	= 1237
    ER_ERROR_MESSAGES 		= 238

    # Client Error
    CR_MIN_ERROR		= 2000
    CR_MAX_ERROR		= 2999
    CR_UNKNOWN_ERROR		= 2000
    CR_SOCKET_CREATE_ERROR	= 2001
    CR_CONNECTION_ERROR		= 2002
    CR_CONN_HOST_ERROR		= 2003
    CR_IPSOCK_ERROR		= 2004
    CR_UNKNOWN_HOST		= 2005
    CR_SERVER_GONE_ERROR	= 2006
    CR_VERSION_ERROR		= 2007
    CR_OUT_OF_MEMORY		= 2008
    CR_WRONG_HOST_INFO		= 2009
    CR_LOCALHOST_CONNECTION	= 2010
    CR_TCP_CONNECTION		= 2011
    CR_SERVER_HANDSHAKE_ERR	= 2012
    CR_SERVER_LOST		= 2013
    CR_COMMANDS_OUT_OF_SYNC	= 2014
    CR_NAMEDPIPE_CONNECTION	= 2015
    CR_NAMEDPIPEWAIT_ERROR	= 2016
    CR_NAMEDPIPEOPEN_ERROR	= 2017
    CR_NAMEDPIPESETSTATE_ERROR	= 2018
    CR_CANT_READ_CHARSET	= 2019
    CR_NET_PACKET_TOO_LARGE	= 2020
    CR_EMBEDDED_CONNECTION	= 2021
    CR_PROBE_SLAVE_STATUS	= 2022
    CR_PROBE_SLAVE_HOSTS	= 2023
    CR_PROBE_SLAVE_CONNECT	= 2024
    CR_PROBE_MASTER_CONNECT	= 2025
    CR_SSL_CONNECTION_ERROR	= 2026
    CR_MALFORMED_PACKET		= 2027

    CLIENT_ERRORS = [
      "Unknown MySQL error",
      "Can't create UNIX socket (%d)",
      "Can't connect to local MySQL server through socket '%-.64s' (%d)",
      "Can't connect to MySQL server on '%-.64s' (%d)",
      "Can't create TCP/IP socket (%d)",
      "Unknown MySQL Server Host '%-.64s' (%d)",
      "MySQL server has gone away",
      "Protocol mismatch. Server Version = %d Client Version = %d",
      "MySQL client run out of memory",
      "Wrong host info",
      "Localhost via UNIX socket",
      "%-.64s via TCP/IP",
      "Error in server handshake",
      "Lost connection to MySQL server during query",
      "Commands out of sync;  You can't run this command now",
      "%-.64s via named pipe",
      "Can't wait for named pipe to host: %-.64s  pipe: %-.32s (%lu)",
      "Can't open named pipe to host: %-.64s  pipe: %-.32s (%lu)",
      "Can't set state of named pipe to host: %-.64s  pipe: %-.32s (%lu)",
      "Can't initialize character set %-.64s (path: %-.64s)",
      "Got packet bigger than 'max_allowed_packet'",
      "Embedded server",
      "Error on SHOW SLAVE STATUS:",
      "Error on SHOW SLAVE HOSTS:",
      "Error connecting to slave:",
      "Error connecting to master:",
      "SSL connection error",
      "Malformed packet"
    ]

    def initialize(errno, error)
      @errno = errno
      @error = error
      # FIXME: why doesn't this work?
      #super error
      puts "MySql::Error: " + @error
    end
    
    attr_reader :errno, :error

    def Error::err(errno)
      CLIENT_ERRORS[errno - Error::CR_MIN_ERROR]
    end
  end
end


#
# for compatibility
#

MysqlRes = Mysql::Result
MysqlField = Mysql::Field
MysqlError = Mysql::Error
