/*      execute(arg,...)     */
static VALUE stmt_execute(int argc, VALUE *argv, VALUE obj)
{
    struct mysql_stmt *s = DATA_PTR(obj);
    MYSQL_STMT *stmt = s->stmt;
    int i;

    check_stmt_closed(obj);
    free_execute_memory(s);
    if (s->param.n != argc)
        rb_raise(eMysql, "execute: param_count(%d) != number of argument(%d)", s->param.n, argc);
    if (argc > 0) {
        memset(s->param.bind, 0, sizeof(*(s->param.bind))*argc);
        for (i = 0; i < argc; i++) {
            switch (TYPE(argv[i])) {
            case T_NIL:
                s->param.bind[i].buffer_type = MYSQL_TYPE_NULL;
                break;
            case T_FIXNUM:
#if SIZEOF_INT < SIZEOF_LONG
                s->param.bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
                s->param.bind[i].buffer = &(s->param.buffer[i]);
                *(LONG_LONG*)(s->param.bind[i].buffer) = FIX2LONG(argv[i]);
#else
                s->param.bind[i].buffer_type = MYSQL_TYPE_LONG;
                s->param.bind[i].buffer = &(s->param.buffer[i]);
                *(int*)(s->param.bind[i].buffer) = FIX2INT(argv[i]);
#endif
                break;
            case T_BIGNUM:
                s->param.bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
                s->param.bind[i].buffer = &(s->param.buffer[i]);
                *(LONG_LONG*)(s->param.bind[i].buffer) = rb_big2ll(argv[i]);
                break;
            case T_FLOAT:
                s->param.bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
                s->param.bind[i].buffer = &(s->param.buffer[i]);
                *(double*)(s->param.bind[i].buffer) = NUM2DBL(argv[i]);
                break;
            case T_STRING:
                s->param.bind[i].buffer_type = MYSQL_TYPE_STRING;
                s->param.bind[i].buffer = RSTRING_PTR(argv[i]);
                s->param.bind[i].buffer_length = RSTRING_LEN(argv[i]);
                s->param.length[i] = RSTRING_LEN(argv[i]);
                s->param.bind[i].length = &(s->param.length[i]);
                break;
            default:
                if (CLASS_OF(argv[i]) == rb_cTime) {
                    MYSQL_TIME t;
                    VALUE a = rb_funcall(argv[i], rb_intern("to_a"), 0);
                    s->param.bind[i].buffer_type = MYSQL_TYPE_DATETIME;
                    s->param.bind[i].buffer = &(s->param.buffer[i]);
                    memset(&t, 0, sizeof(t));    /* avoid warning */
                    t.second_part = 0;
                    t.neg = 0;
                    t.second = FIX2INT(RARRAY_PTR(a)[0]);
                    t.minute = FIX2INT(RARRAY_PTR(a)[1]);
                    t.hour = FIX2INT(RARRAY_PTR(a)[2]);
                    t.day = FIX2INT(RARRAY_PTR(a)[3]);
                    t.month = FIX2INT(RARRAY_PTR(a)[4]);
                    t.year = FIX2INT(RARRAY_PTR(a)[5]);
                    *(MYSQL_TIME*)&(s->param.buffer[i]) = t;
                } else if (CLASS_OF(argv[i]) == cMysqlTime) {
                    MYSQL_TIME t;
                    s->param.bind[i].buffer_type = MYSQL_TYPE_DATETIME;
                    s->param.bind[i].buffer = &(s->param.buffer[i]);
                    memset(&t, 0, sizeof(t));    /* avoid warning */
                    t.second_part = 0;
                    t.neg = 0;
                    t.second = NUM2INT(rb_iv_get(argv[i], "second"));
                    t.minute = NUM2INT(rb_iv_get(argv[i], "minute"));
                    t.hour = NUM2INT(rb_iv_get(argv[i], "hour"));
                    t.day = NUM2INT(rb_iv_get(argv[i], "day"));
                    t.month = NUM2INT(rb_iv_get(argv[i], "month"));
                    t.year = NUM2INT(rb_iv_get(argv[i], "year"));
                    *(MYSQL_TIME*)&(s->param.buffer[i]) = t;
                } else
                    rb_raise(rb_eTypeError, "unsupported type: %d", TYPE(argv[i]));
            }
        }
        if (mysql_stmt_bind_param(stmt, s->param.bind))
            mysql_stmt_raise(stmt);
    }

    if (mysql_stmt_execute(stmt))
        mysql_stmt_raise(stmt);
    if (s->res) {
        MYSQL_FIELD *field;
        if (mysql_stmt_store_result(stmt))
            mysql_stmt_raise(stmt);
        field = mysql_fetch_fields(s->res);
        for (i = 0; i < s->result.n; i++) {
            switch(s->result.bind[i].buffer_type) {
            case MYSQL_TYPE_NULL:
                break;
            case MYSQL_TYPE_TINY:
            case MYSQL_TYPE_SHORT:
            case MYSQL_TYPE_YEAR:
            case MYSQL_TYPE_INT24:
            case MYSQL_TYPE_LONG:
            case MYSQL_TYPE_LONGLONG:
            case MYSQL_TYPE_FLOAT:
            case MYSQL_TYPE_DOUBLE:
                s->result.bind[i].buffer = xmalloc(8);
                s->result.bind[i].buffer_length = 8;
                memset(s->result.bind[i].buffer, 0, 8);
                break;
            case MYSQL_TYPE_DECIMAL:
            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:
#if MYSQL_VERSION_ID >= 50003
            case MYSQL_TYPE_NEWDECIMAL:
            case MYSQL_TYPE_BIT:
#endif
                s->result.bind[i].buffer = xmalloc(field[i].max_length);
                memset(s->result.bind[i].buffer, 0, field[i].max_length);
                s->result.bind[i].buffer_length = field[i].max_length;
                break;
            case MYSQL_TYPE_TIME:
            case MYSQL_TYPE_DATE:
            case MYSQL_TYPE_DATETIME:
            case MYSQL_TYPE_TIMESTAMP:
                s->result.bind[i].buffer = xmalloc(sizeof(MYSQL_TIME));
                s->result.bind[i].buffer_length = sizeof(MYSQL_TIME);
                memset(s->result.bind[i].buffer, 0, sizeof(MYSQL_TIME));
                break;
            default:
                rb_raise(rb_eTypeError, "unknown buffer_type: %d", s->result.bind[i].buffer_type);
            }
        }
        if (mysql_stmt_bind_result(s->stmt, s->result.bind))
            mysql_stmt_raise(s->stmt);
    }
    return obj;
}