/*      fetch()              */
static VALUE stmt_fetch(VALUE obj)
{
    struct mysql_stmt* s = DATA_PTR(obj);
    VALUE ret;
    int i;
    int r;

    check_stmt_closed(obj);
    r = mysql_stmt_fetch(s->stmt);
    if (r == MYSQL_NO_DATA)
        return Qnil;
#ifdef MYSQL_DATA_TRUNCATED
    if (r == MYSQL_DATA_TRUNCATED)
        rb_raise(rb_eRuntimeError, "unexpectedly data truncated");
#endif
    if (r == 1)
        mysql_stmt_raise(s->stmt);

    ret = rb_ary_new2(s->result.n);
    for (i = 0; i < s->result.n; i++) {
        if (s->result.is_null[i])
            rb_ary_push(ret, Qnil);
        else {
            VALUE v;
            MYSQL_TIME *t;
            switch (s->result.bind[i].buffer_type) {
            case MYSQL_TYPE_TINY:
                if (s->result.bind[i].is_unsigned)
                    v = UINT2NUM(*(unsigned char *)s->result.bind[i].buffer);
                else
                    v = INT2NUM(*(signed char *)s->result.bind[i].buffer);
                break;
            case MYSQL_TYPE_SHORT:
            case MYSQL_TYPE_YEAR:
                if (s->result.bind[i].is_unsigned)
                    v = UINT2NUM(*(unsigned short *)s->result.bind[i].buffer);
                else
                    v = INT2NUM(*(short *)s->result.bind[i].buffer);
                break;
            case MYSQL_TYPE_INT24:
            case MYSQL_TYPE_LONG:
                if (s->result.bind[i].is_unsigned)
                    v = UINT2NUM(*(unsigned int *)s->result.bind[i].buffer);
                else
                    v = INT2NUM(*(int *)s->result.bind[i].buffer);
                break;
            case MYSQL_TYPE_LONGLONG:
                if (s->result.bind[i].is_unsigned)
                    v = ULL2NUM(*(unsigned long long *)s->result.bind[i].buffer);
                else
                    v = LL2NUM(*(long long *)s->result.bind[i].buffer);
                break;
            case MYSQL_TYPE_FLOAT:
                v = rb_float_new((double)(*(float *)s->result.bind[i].buffer));
                break;
            case MYSQL_TYPE_DOUBLE:
                v = rb_float_new(*(double *)s->result.bind[i].buffer);
                break;
            case MYSQL_TYPE_TIME:
            case MYSQL_TYPE_DATE:
            case MYSQL_TYPE_DATETIME:
            case MYSQL_TYPE_TIMESTAMP:
                t = (MYSQL_TIME *)s->result.bind[i].buffer;
                v = rb_obj_alloc(cMysqlTime);
                rb_funcall(v, rb_intern("initialize"), 8,
                           INT2FIX(t->year), INT2FIX(t->month),
                           INT2FIX(t->day), INT2FIX(t->hour),
                           INT2FIX(t->minute), INT2FIX(t->second),
                           (t->neg ? Qtrue : Qfalse),
                           INT2FIX(t->second_part));
                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
                v = rb_tainted_str_new(s->result.bind[i].buffer, s->result.length[i]);
                break;
            default:
                rb_raise(rb_eTypeError, "unknown buffer_type: %d", s->result.bind[i].buffer_type);
            }
            rb_ary_push(ret, v);
        }
    }
    return ret;
}