mysql2 0.3.20 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -0
- data/README.md +132 -55
- data/examples/eventmachine.rb +1 -1
- data/examples/threaded.rb +4 -6
- data/ext/mysql2/client.c +323 -140
- data/ext/mysql2/client.h +13 -3
- data/ext/mysql2/extconf.rb +111 -44
- data/ext/mysql2/infile.c +2 -2
- data/ext/mysql2/mysql2_ext.c +1 -0
- data/ext/mysql2/mysql2_ext.h +5 -10
- data/ext/mysql2/mysql_enc_name_to_ruby.h +2 -2
- data/ext/mysql2/mysql_enc_to_ruby.h +25 -22
- data/ext/mysql2/result.c +491 -97
- data/ext/mysql2/result.h +12 -4
- data/ext/mysql2/statement.c +595 -0
- data/ext/mysql2/statement.h +19 -0
- data/lib/mysql2/client.rb +82 -27
- data/lib/mysql2/console.rb +1 -1
- data/lib/mysql2/em.rb +5 -6
- data/lib/mysql2/error.rb +17 -26
- data/lib/mysql2/field.rb +3 -0
- data/lib/mysql2/statement.rb +17 -0
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +38 -18
- data/spec/configuration.yml.example +0 -6
- data/spec/em/em_spec.rb +22 -21
- data/spec/mysql2/client_spec.rb +498 -380
- data/spec/mysql2/error_spec.rb +38 -39
- data/spec/mysql2/result_spec.rb +260 -229
- data/spec/mysql2/statement_spec.rb +776 -0
- data/spec/spec_helper.rb +80 -59
- data/spec/ssl/ca-cert.pem +17 -0
- data/spec/ssl/ca-key.pem +27 -0
- data/spec/ssl/ca.cnf +22 -0
- data/spec/ssl/cert.cnf +22 -0
- data/spec/ssl/client-cert.pem +17 -0
- data/spec/ssl/client-key.pem +27 -0
- data/spec/ssl/client-req.pem +15 -0
- data/spec/ssl/gen_certs.sh +48 -0
- data/spec/ssl/pkcs8-client-key.pem +28 -0
- data/spec/ssl/pkcs8-server-key.pem +28 -0
- data/spec/ssl/server-cert.pem +17 -0
- data/spec/ssl/server-key.pem +27 -0
- data/spec/ssl/server-req.pem +15 -0
- data/support/5072E1F5.asc +432 -0
- data/support/mysql_enc_to_ruby.rb +7 -8
- data/support/ruby_enc_to_mysql.rb +1 -1
- metadata +50 -55
    
        data/ext/mysql2/result.c
    CHANGED
    
    | @@ -1,7 +1,5 @@ | |
| 1 1 | 
             
            #include <mysql2_ext.h>
         | 
| 2 2 |  | 
| 3 | 
            -
            #include <stdint.h>
         | 
| 4 | 
            -
             | 
| 5 3 | 
             
            #include "mysql_enc_to_ruby.h"
         | 
| 6 4 |  | 
| 7 5 | 
             
            #ifdef HAVE_RUBY_ENCODING_H
         | 
| @@ -50,12 +48,24 @@ static rb_encoding *binaryEncoding; | |
| 50 48 | 
             
            #define MYSQL2_MIN_TIME 62171150401ULL
         | 
| 51 49 | 
             
            #endif
         | 
| 52 50 |  | 
| 53 | 
            -
            #define GET_RESULT( | 
| 51 | 
            +
            #define GET_RESULT(self) \
         | 
| 54 52 | 
             
              mysql2_result_wrapper *wrapper; \
         | 
| 55 53 | 
             
              Data_Get_Struct(self, mysql2_result_wrapper, wrapper);
         | 
| 56 54 |  | 
| 55 | 
            +
            typedef struct {
         | 
| 56 | 
            +
              int symbolizeKeys;
         | 
| 57 | 
            +
              int asArray;
         | 
| 58 | 
            +
              int castBool;
         | 
| 59 | 
            +
              int cacheRows;
         | 
| 60 | 
            +
              int cast;
         | 
| 61 | 
            +
              int streaming;
         | 
| 62 | 
            +
              ID db_timezone;
         | 
| 63 | 
            +
              ID app_timezone;
         | 
| 64 | 
            +
              VALUE block_given;
         | 
| 65 | 
            +
            } result_each_args;
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            VALUE cBigDecimal, cDateTime, cDate;
         | 
| 57 68 | 
             
            static VALUE cMysql2Result;
         | 
| 58 | 
            -
            static VALUE cBigDecimal, cDate, cDateTime;
         | 
| 59 69 | 
             
            static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
         | 
| 60 70 | 
             
            extern VALUE mMysql2, cMysql2Client, cMysql2Error;
         | 
| 61 71 | 
             
            static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset, intern_civil, intern_new_offset;
         | 
| @@ -63,6 +73,7 @@ static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_a | |
| 63 73 | 
             
                      sym_local, sym_utc, sym_cast_booleans, sym_cache_rows, sym_cast, sym_stream, sym_name;
         | 
| 64 74 | 
             
            static ID intern_merge;
         | 
| 65 75 |  | 
| 76 | 
            +
            /* Mark any VALUEs that are only referenced in C, so the GC won't get them. */
         | 
| 66 77 | 
             
            static void rb_mysql_result_mark(void * wrapper) {
         | 
| 67 78 | 
             
              mysql2_result_wrapper * w = wrapper;
         | 
| 68 79 | 
             
              if (w) {
         | 
| @@ -70,13 +81,50 @@ static void rb_mysql_result_mark(void * wrapper) { | |
| 70 81 | 
             
                rb_gc_mark(w->rows);
         | 
| 71 82 | 
             
                rb_gc_mark(w->encoding);
         | 
| 72 83 | 
             
                rb_gc_mark(w->client);
         | 
| 84 | 
            +
                rb_gc_mark(w->statement);
         | 
| 73 85 | 
             
              }
         | 
| 74 86 | 
             
            }
         | 
| 75 87 |  | 
| 76 88 | 
             
            /* this may be called manually or during GC */
         | 
| 77 89 | 
             
            static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
         | 
| 78 | 
            -
              if (wrapper | 
| 90 | 
            +
              if (!wrapper) return;
         | 
| 91 | 
            +
             | 
| 92 | 
            +
              if (wrapper->resultFreed != 1) {
         | 
| 93 | 
            +
                if (wrapper->stmt_wrapper) {
         | 
| 94 | 
            +
                  if (!wrapper->stmt_wrapper->closed) {
         | 
| 95 | 
            +
                    mysql_stmt_free_result(wrapper->stmt_wrapper->stmt);
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                    /* MySQL BUG? If the statement handle was previously used, and so
         | 
| 98 | 
            +
                     * mysql_stmt_bind_result was called, and if that result set and bind buffers were freed,
         | 
| 99 | 
            +
                     * MySQL still thinks the result set buffer is available and will prefetch the
         | 
| 100 | 
            +
                     * first result in mysql_stmt_execute. This will corrupt or crash the program.
         | 
| 101 | 
            +
                     * By setting bind_result_done back to 0, we make MySQL think that a result set
         | 
| 102 | 
            +
                     * has never been bound to this statement handle before to prevent the prefetch.
         | 
| 103 | 
            +
                     */
         | 
| 104 | 
            +
                    wrapper->stmt_wrapper->stmt->bind_result_done = 0;
         | 
| 105 | 
            +
                  }
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  if (wrapper->statement != Qnil) {
         | 
| 108 | 
            +
                    decr_mysql2_stmt(wrapper->stmt_wrapper);
         | 
| 109 | 
            +
                  }
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  if (wrapper->result_buffers) {
         | 
| 112 | 
            +
                    unsigned int i;
         | 
| 113 | 
            +
                    for (i = 0; i < wrapper->numberOfFields; i++) {
         | 
| 114 | 
            +
                      if (wrapper->result_buffers[i].buffer) {
         | 
| 115 | 
            +
                        xfree(wrapper->result_buffers[i].buffer);
         | 
| 116 | 
            +
                      }
         | 
| 117 | 
            +
                    }
         | 
| 118 | 
            +
                    xfree(wrapper->result_buffers);
         | 
| 119 | 
            +
                    xfree(wrapper->is_null);
         | 
| 120 | 
            +
                    xfree(wrapper->error);
         | 
| 121 | 
            +
                    xfree(wrapper->length);
         | 
| 122 | 
            +
                  }
         | 
| 123 | 
            +
                  /* Clue that the next statement execute will need to allocate a new result buffer. */
         | 
| 124 | 
            +
                  wrapper->result_buffers = NULL;
         | 
| 125 | 
            +
                }
         | 
| 79 126 | 
             
                /* FIXME: this may call flush_use_result, which can hit the socket */
         | 
| 127 | 
            +
                /* For prepared statements, wrapper->result is the result metadata */
         | 
| 80 128 | 
             
                mysql_free_result(wrapper->result);
         | 
| 81 129 | 
             
                wrapper->resultFreed = 1;
         | 
| 82 130 | 
             
              }
         | 
| @@ -84,7 +132,7 @@ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) { | |
| 84 132 |  | 
| 85 133 | 
             
            /* this is called during GC */
         | 
| 86 134 | 
             
            static void rb_mysql_result_free(void *ptr) {
         | 
| 87 | 
            -
              mysql2_result_wrapper * | 
| 135 | 
            +
              mysql2_result_wrapper *wrapper = ptr;
         | 
| 88 136 | 
             
              rb_mysql_result_free_result(wrapper);
         | 
| 89 137 |  | 
| 90 138 | 
             
              // If the GC gets to client first it will be nil
         | 
| @@ -95,6 +143,12 @@ static void rb_mysql_result_free(void *ptr) { | |
| 95 143 | 
             
              xfree(wrapper);
         | 
| 96 144 | 
             
            }
         | 
| 97 145 |  | 
| 146 | 
            +
            static VALUE rb_mysql_result_free_(VALUE self) {
         | 
| 147 | 
            +
              GET_RESULT(self);
         | 
| 148 | 
            +
              rb_mysql_result_free_result(wrapper);
         | 
| 149 | 
            +
              return Qnil;
         | 
| 150 | 
            +
            }
         | 
| 151 | 
            +
             | 
| 98 152 | 
             
            /*
         | 
| 99 153 | 
             
             * for small results, this won't hit the network, but there's no
         | 
| 100 154 | 
             
             * reliable way for us to tell this so we'll always release the GVL
         | 
| @@ -106,7 +160,14 @@ static void *nogvl_fetch_row(void *ptr) { | |
| 106 160 | 
             
              return mysql_fetch_row(result);
         | 
| 107 161 | 
             
            }
         | 
| 108 162 |  | 
| 109 | 
            -
            static  | 
| 163 | 
            +
            static void *nogvl_stmt_fetch(void *ptr) {
         | 
| 164 | 
            +
              MYSQL_STMT *stmt = ptr;
         | 
| 165 | 
            +
              uintptr_t r = mysql_stmt_fetch(stmt);
         | 
| 166 | 
            +
             | 
| 167 | 
            +
              return (void *)r;
         | 
| 168 | 
            +
            }
         | 
| 169 | 
            +
             | 
| 170 | 
            +
            static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbolize_keys) {
         | 
| 110 171 | 
             
              VALUE rb_field;
         | 
| 111 172 | 
             
              GET_RESULT(self);
         | 
| 112 173 |  | 
| @@ -150,7 +211,7 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int | |
| 150 211 |  | 
| 151 212 | 
             
            #ifdef HAVE_RUBY_ENCODING_H
         | 
| 152 213 | 
             
            static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_encoding *default_internal_enc, rb_encoding *conn_enc) {
         | 
| 153 | 
            -
              /* if binary flag is set, respect  | 
| 214 | 
            +
              /* if binary flag is set, respect its wishes */
         | 
| 154 215 | 
             
              if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
         | 
| 155 216 | 
             
                rb_enc_associate(val, binaryEncoding);
         | 
| 156 217 | 
             
              } else if (!field.charsetnr) {
         | 
| @@ -185,7 +246,7 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e | |
| 185 246 | 
             
             */
         | 
| 186 247 | 
             
            static unsigned int msec_char_to_uint(char *msec_char, size_t len)
         | 
| 187 248 | 
             
            {
         | 
| 188 | 
            -
               | 
| 249 | 
            +
              size_t i;
         | 
| 189 250 | 
             
              for (i = 0; i < (len - 1); i++) {
         | 
| 190 251 | 
             
                if (msec_char[i] == '\0') {
         | 
| 191 252 | 
             
                  msec_char[i] = '0';
         | 
| @@ -194,7 +255,276 @@ static unsigned int msec_char_to_uint(char *msec_char, size_t len) | |
| 194 255 | 
             
              return (unsigned int)strtoul(msec_char, NULL, 10);
         | 
| 195 256 | 
             
            }
         | 
| 196 257 |  | 
| 197 | 
            -
            static  | 
| 258 | 
            +
            static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields) {
         | 
| 259 | 
            +
              unsigned int i;
         | 
| 260 | 
            +
              GET_RESULT(self);
         | 
| 261 | 
            +
             | 
| 262 | 
            +
              if (wrapper->result_buffers != NULL) return;
         | 
| 263 | 
            +
             | 
| 264 | 
            +
              wrapper->result_buffers = xcalloc(wrapper->numberOfFields, sizeof(MYSQL_BIND));
         | 
| 265 | 
            +
              wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(bool));
         | 
| 266 | 
            +
              wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(bool));
         | 
| 267 | 
            +
              wrapper->length = xcalloc(wrapper->numberOfFields, sizeof(unsigned long));
         | 
| 268 | 
            +
             | 
| 269 | 
            +
              for (i = 0; i < wrapper->numberOfFields; i++) {
         | 
| 270 | 
            +
                wrapper->result_buffers[i].buffer_type = fields[i].type;
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                //      mysql type    |            C type
         | 
| 273 | 
            +
                switch(fields[i].type) {
         | 
| 274 | 
            +
                  case MYSQL_TYPE_NULL:         // NULL
         | 
| 275 | 
            +
                    break;
         | 
| 276 | 
            +
                  case MYSQL_TYPE_TINY:         // signed char
         | 
| 277 | 
            +
                    wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(signed char));
         | 
| 278 | 
            +
                    wrapper->result_buffers[i].buffer_length = sizeof(signed char);
         | 
| 279 | 
            +
                    break;
         | 
| 280 | 
            +
                  case MYSQL_TYPE_SHORT:        // short int
         | 
| 281 | 
            +
                    wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(short int));
         | 
| 282 | 
            +
                    wrapper->result_buffers[i].buffer_length = sizeof(short int);
         | 
| 283 | 
            +
                    break;
         | 
| 284 | 
            +
                  case MYSQL_TYPE_INT24:        // int
         | 
| 285 | 
            +
                  case MYSQL_TYPE_LONG:         // int
         | 
| 286 | 
            +
                  case MYSQL_TYPE_YEAR:         // int
         | 
| 287 | 
            +
                    wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(int));
         | 
| 288 | 
            +
                    wrapper->result_buffers[i].buffer_length = sizeof(int);
         | 
| 289 | 
            +
                    break;
         | 
| 290 | 
            +
                  case MYSQL_TYPE_LONGLONG:     // long long int
         | 
| 291 | 
            +
                    wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(long long int));
         | 
| 292 | 
            +
                    wrapper->result_buffers[i].buffer_length = sizeof(long long int);
         | 
| 293 | 
            +
                    break;
         | 
| 294 | 
            +
                  case MYSQL_TYPE_FLOAT:        // float
         | 
| 295 | 
            +
                  case MYSQL_TYPE_DOUBLE:       // double
         | 
| 296 | 
            +
                    wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(double));
         | 
| 297 | 
            +
                    wrapper->result_buffers[i].buffer_length = sizeof(double);
         | 
| 298 | 
            +
                    break;
         | 
| 299 | 
            +
                  case MYSQL_TYPE_TIME:         // MYSQL_TIME
         | 
| 300 | 
            +
                  case MYSQL_TYPE_DATE:         // MYSQL_TIME
         | 
| 301 | 
            +
                  case MYSQL_TYPE_NEWDATE:      // MYSQL_TIME
         | 
| 302 | 
            +
                  case MYSQL_TYPE_DATETIME:     // MYSQL_TIME
         | 
| 303 | 
            +
                  case MYSQL_TYPE_TIMESTAMP:    // MYSQL_TIME
         | 
| 304 | 
            +
                    wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(MYSQL_TIME));
         | 
| 305 | 
            +
                    wrapper->result_buffers[i].buffer_length = sizeof(MYSQL_TIME);
         | 
| 306 | 
            +
                    break;
         | 
| 307 | 
            +
                  case MYSQL_TYPE_DECIMAL:      // char[]
         | 
| 308 | 
            +
                  case MYSQL_TYPE_NEWDECIMAL:   // char[]
         | 
| 309 | 
            +
                  case MYSQL_TYPE_STRING:       // char[]
         | 
| 310 | 
            +
                  case MYSQL_TYPE_VAR_STRING:   // char[]
         | 
| 311 | 
            +
                  case MYSQL_TYPE_VARCHAR:      // char[]
         | 
| 312 | 
            +
                  case MYSQL_TYPE_TINY_BLOB:    // char[]
         | 
| 313 | 
            +
                  case MYSQL_TYPE_BLOB:         // char[]
         | 
| 314 | 
            +
                  case MYSQL_TYPE_MEDIUM_BLOB:  // char[]
         | 
| 315 | 
            +
                  case MYSQL_TYPE_LONG_BLOB:    // char[]
         | 
| 316 | 
            +
                  case MYSQL_TYPE_BIT:          // char[]
         | 
| 317 | 
            +
                  case MYSQL_TYPE_SET:          // char[]
         | 
| 318 | 
            +
                  case MYSQL_TYPE_ENUM:         // char[]
         | 
| 319 | 
            +
                  case MYSQL_TYPE_GEOMETRY:     // char[]
         | 
| 320 | 
            +
                  default:
         | 
| 321 | 
            +
                    wrapper->result_buffers[i].buffer = xmalloc(fields[i].max_length);
         | 
| 322 | 
            +
                    wrapper->result_buffers[i].buffer_length = fields[i].max_length;
         | 
| 323 | 
            +
                    break;
         | 
| 324 | 
            +
                }
         | 
| 325 | 
            +
             | 
| 326 | 
            +
                wrapper->result_buffers[i].is_null = &wrapper->is_null[i];
         | 
| 327 | 
            +
                wrapper->result_buffers[i].length  = &wrapper->length[i];
         | 
| 328 | 
            +
                wrapper->result_buffers[i].error   = &wrapper->error[i];
         | 
| 329 | 
            +
                wrapper->result_buffers[i].is_unsigned = ((fields[i].flags & UNSIGNED_FLAG) != 0);
         | 
| 330 | 
            +
              }
         | 
| 331 | 
            +
            }
         | 
| 332 | 
            +
             | 
| 333 | 
            +
            static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
         | 
| 334 | 
            +
            {
         | 
| 335 | 
            +
              VALUE rowVal;
         | 
| 336 | 
            +
              unsigned int i = 0;
         | 
| 337 | 
            +
             | 
| 338 | 
            +
            #ifdef HAVE_RUBY_ENCODING_H
         | 
| 339 | 
            +
              rb_encoding *default_internal_enc;
         | 
| 340 | 
            +
              rb_encoding *conn_enc;
         | 
| 341 | 
            +
            #endif
         | 
| 342 | 
            +
              GET_RESULT(self);
         | 
| 343 | 
            +
             | 
| 344 | 
            +
            #ifdef HAVE_RUBY_ENCODING_H
         | 
| 345 | 
            +
              default_internal_enc = rb_default_internal_encoding();
         | 
| 346 | 
            +
              conn_enc = rb_to_encoding(wrapper->encoding);
         | 
| 347 | 
            +
            #endif
         | 
| 348 | 
            +
             | 
| 349 | 
            +
              if (wrapper->fields == Qnil) {
         | 
| 350 | 
            +
                wrapper->numberOfFields = mysql_num_fields(wrapper->result);
         | 
| 351 | 
            +
                wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
         | 
| 352 | 
            +
              }
         | 
| 353 | 
            +
              if (args->asArray) {
         | 
| 354 | 
            +
                rowVal = rb_ary_new2(wrapper->numberOfFields);
         | 
| 355 | 
            +
              } else {
         | 
| 356 | 
            +
                rowVal = rb_hash_new();
         | 
| 357 | 
            +
              }
         | 
| 358 | 
            +
             | 
| 359 | 
            +
              if (wrapper->result_buffers == NULL) {
         | 
| 360 | 
            +
                rb_mysql_result_alloc_result_buffers(self, fields);
         | 
| 361 | 
            +
              }
         | 
| 362 | 
            +
             | 
| 363 | 
            +
              if (mysql_stmt_bind_result(wrapper->stmt_wrapper->stmt, wrapper->result_buffers)) {
         | 
| 364 | 
            +
                rb_raise_mysql2_stmt_error(wrapper->stmt_wrapper);
         | 
| 365 | 
            +
              }
         | 
| 366 | 
            +
             | 
| 367 | 
            +
              {
         | 
| 368 | 
            +
                switch((uintptr_t)rb_thread_call_without_gvl(nogvl_stmt_fetch, wrapper->stmt_wrapper->stmt, RUBY_UBF_IO, 0)) {
         | 
| 369 | 
            +
                  case 0:
         | 
| 370 | 
            +
                    /* success */
         | 
| 371 | 
            +
                    break;
         | 
| 372 | 
            +
             | 
| 373 | 
            +
                  case 1:
         | 
| 374 | 
            +
                    /* error */
         | 
| 375 | 
            +
                    rb_raise_mysql2_stmt_error(wrapper->stmt_wrapper);
         | 
| 376 | 
            +
             | 
| 377 | 
            +
                  case MYSQL_NO_DATA:
         | 
| 378 | 
            +
                    /* no more row */
         | 
| 379 | 
            +
                    return Qnil;
         | 
| 380 | 
            +
             | 
| 381 | 
            +
                  case MYSQL_DATA_TRUNCATED:
         | 
| 382 | 
            +
                    rb_raise(cMysql2Error, "IMPLBUG: caught MYSQL_DATA_TRUNCATED. should not come here as buffer_length is set to fields[i].max_length.");
         | 
| 383 | 
            +
                }
         | 
| 384 | 
            +
              }
         | 
| 385 | 
            +
             | 
| 386 | 
            +
              for (i = 0; i < wrapper->numberOfFields; i++) {
         | 
| 387 | 
            +
                VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
         | 
| 388 | 
            +
                VALUE val = Qnil;
         | 
| 389 | 
            +
                MYSQL_TIME *ts;
         | 
| 390 | 
            +
             | 
| 391 | 
            +
                if (wrapper->is_null[i]) {
         | 
| 392 | 
            +
                  val = Qnil;
         | 
| 393 | 
            +
                } else {
         | 
| 394 | 
            +
                  const MYSQL_BIND* const result_buffer = &wrapper->result_buffers[i];
         | 
| 395 | 
            +
             | 
| 396 | 
            +
                  switch(result_buffer->buffer_type) {
         | 
| 397 | 
            +
                    case MYSQL_TYPE_TINY:         // signed char
         | 
| 398 | 
            +
                      if (args->castBool && fields[i].length == 1) {
         | 
| 399 | 
            +
                        val = (*((unsigned char*)result_buffer->buffer) != 0) ? Qtrue : Qfalse;
         | 
| 400 | 
            +
                        break;
         | 
| 401 | 
            +
                      }
         | 
| 402 | 
            +
                      if (result_buffer->is_unsigned) {
         | 
| 403 | 
            +
                        val = UINT2NUM(*((unsigned char*)result_buffer->buffer));
         | 
| 404 | 
            +
                      } else {
         | 
| 405 | 
            +
                        val = INT2NUM(*((signed char*)result_buffer->buffer));
         | 
| 406 | 
            +
                      }
         | 
| 407 | 
            +
                      break;
         | 
| 408 | 
            +
                    case MYSQL_TYPE_BIT:        /* BIT field (MySQL 5.0.3 and up) */
         | 
| 409 | 
            +
                      if (args->castBool && fields[i].length == 1) {
         | 
| 410 | 
            +
                        val = (*((unsigned char*)result_buffer->buffer) != 0) ? Qtrue : Qfalse;
         | 
| 411 | 
            +
                      }else{
         | 
| 412 | 
            +
                        val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
         | 
| 413 | 
            +
                      }
         | 
| 414 | 
            +
                      break;
         | 
| 415 | 
            +
                    case MYSQL_TYPE_SHORT:        // short int
         | 
| 416 | 
            +
                      if (result_buffer->is_unsigned) {
         | 
| 417 | 
            +
                        val = UINT2NUM(*((unsigned short int*)result_buffer->buffer));
         | 
| 418 | 
            +
                      } else  {
         | 
| 419 | 
            +
                        val = INT2NUM(*((short int*)result_buffer->buffer));
         | 
| 420 | 
            +
                      }
         | 
| 421 | 
            +
                      break;
         | 
| 422 | 
            +
                    case MYSQL_TYPE_INT24:        // int
         | 
| 423 | 
            +
                    case MYSQL_TYPE_LONG:         // int
         | 
| 424 | 
            +
                    case MYSQL_TYPE_YEAR:         // int
         | 
| 425 | 
            +
                      if (result_buffer->is_unsigned) {
         | 
| 426 | 
            +
                        val = UINT2NUM(*((unsigned int*)result_buffer->buffer));
         | 
| 427 | 
            +
                      } else {
         | 
| 428 | 
            +
                        val = INT2NUM(*((int*)result_buffer->buffer));
         | 
| 429 | 
            +
                      }
         | 
| 430 | 
            +
                      break;
         | 
| 431 | 
            +
                    case MYSQL_TYPE_LONGLONG:     // long long int
         | 
| 432 | 
            +
                      if (result_buffer->is_unsigned) {
         | 
| 433 | 
            +
                        val = ULL2NUM(*((unsigned long long int*)result_buffer->buffer));
         | 
| 434 | 
            +
                      } else {
         | 
| 435 | 
            +
                        val = LL2NUM(*((long long int*)result_buffer->buffer));
         | 
| 436 | 
            +
                      }
         | 
| 437 | 
            +
                      break;
         | 
| 438 | 
            +
                    case MYSQL_TYPE_FLOAT:        // float
         | 
| 439 | 
            +
                      val = rb_float_new((double)(*((float*)result_buffer->buffer)));
         | 
| 440 | 
            +
                      break;
         | 
| 441 | 
            +
                    case MYSQL_TYPE_DOUBLE:       // double
         | 
| 442 | 
            +
                      val = rb_float_new((double)(*((double*)result_buffer->buffer)));
         | 
| 443 | 
            +
                      break;
         | 
| 444 | 
            +
                    case MYSQL_TYPE_DATE:         // MYSQL_TIME
         | 
| 445 | 
            +
                    case MYSQL_TYPE_NEWDATE:      // MYSQL_TIME
         | 
| 446 | 
            +
                      ts = (MYSQL_TIME*)result_buffer->buffer;
         | 
| 447 | 
            +
                      val = rb_funcall(cDate, intern_new, 3, INT2NUM(ts->year), INT2NUM(ts->month), INT2NUM(ts->day));
         | 
| 448 | 
            +
                      break;
         | 
| 449 | 
            +
                    case MYSQL_TYPE_TIME:         // MYSQL_TIME
         | 
| 450 | 
            +
                      ts = (MYSQL_TIME*)result_buffer->buffer;
         | 
| 451 | 
            +
                      val = rb_funcall(rb_cTime, args->db_timezone, 7, opt_time_year, opt_time_month, opt_time_month, UINT2NUM(ts->hour), UINT2NUM(ts->minute), UINT2NUM(ts->second), ULONG2NUM(ts->second_part));
         | 
| 452 | 
            +
                      if (!NIL_P(args->app_timezone)) {
         | 
| 453 | 
            +
                        if (args->app_timezone == intern_local) {
         | 
| 454 | 
            +
                          val = rb_funcall(val, intern_localtime, 0);
         | 
| 455 | 
            +
                        } else { // utc
         | 
| 456 | 
            +
                          val = rb_funcall(val, intern_utc, 0);
         | 
| 457 | 
            +
                        }
         | 
| 458 | 
            +
                      }
         | 
| 459 | 
            +
                      break;
         | 
| 460 | 
            +
                    case MYSQL_TYPE_DATETIME:     // MYSQL_TIME
         | 
| 461 | 
            +
                    case MYSQL_TYPE_TIMESTAMP: {  // MYSQL_TIME
         | 
| 462 | 
            +
                      uint64_t seconds;
         | 
| 463 | 
            +
             | 
| 464 | 
            +
                      ts = (MYSQL_TIME*)result_buffer->buffer;
         | 
| 465 | 
            +
                      seconds = (ts->year*31557600ULL) + (ts->month*2592000ULL) + (ts->day*86400ULL) + (ts->hour*3600ULL) + (ts->minute*60ULL) + ts->second;
         | 
| 466 | 
            +
             | 
| 467 | 
            +
                      if (seconds < MYSQL2_MIN_TIME || seconds > MYSQL2_MAX_TIME) { // use DateTime instead
         | 
| 468 | 
            +
                        VALUE offset = INT2NUM(0);
         | 
| 469 | 
            +
                        if (args->db_timezone == intern_local) {
         | 
| 470 | 
            +
                          offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
         | 
| 471 | 
            +
                        }
         | 
| 472 | 
            +
                        val = rb_funcall(cDateTime, intern_civil, 7, UINT2NUM(ts->year), UINT2NUM(ts->month), UINT2NUM(ts->day), UINT2NUM(ts->hour), UINT2NUM(ts->minute), UINT2NUM(ts->second), offset);
         | 
| 473 | 
            +
                        if (!NIL_P(args->app_timezone)) {
         | 
| 474 | 
            +
                          if (args->app_timezone == intern_local) {
         | 
| 475 | 
            +
                            offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
         | 
| 476 | 
            +
                            val = rb_funcall(val, intern_new_offset, 1, offset);
         | 
| 477 | 
            +
                          } else { // utc
         | 
| 478 | 
            +
                            val = rb_funcall(val, intern_new_offset, 1, opt_utc_offset);
         | 
| 479 | 
            +
                          }
         | 
| 480 | 
            +
                        }
         | 
| 481 | 
            +
                      } else {
         | 
| 482 | 
            +
                        val = rb_funcall(rb_cTime, args->db_timezone, 7, UINT2NUM(ts->year), UINT2NUM(ts->month), UINT2NUM(ts->day), UINT2NUM(ts->hour), UINT2NUM(ts->minute), UINT2NUM(ts->second), ULONG2NUM(ts->second_part));
         | 
| 483 | 
            +
                        if (!NIL_P(args->app_timezone)) {
         | 
| 484 | 
            +
                          if (args->app_timezone == intern_local) {
         | 
| 485 | 
            +
                            val = rb_funcall(val, intern_localtime, 0);
         | 
| 486 | 
            +
                          } else { // utc
         | 
| 487 | 
            +
                            val = rb_funcall(val, intern_utc, 0);
         | 
| 488 | 
            +
                          }
         | 
| 489 | 
            +
                        }
         | 
| 490 | 
            +
                      }
         | 
| 491 | 
            +
                      break;
         | 
| 492 | 
            +
                    }
         | 
| 493 | 
            +
                    case MYSQL_TYPE_DECIMAL:      // char[]
         | 
| 494 | 
            +
                    case MYSQL_TYPE_NEWDECIMAL:   // char[]
         | 
| 495 | 
            +
                      val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
         | 
| 496 | 
            +
                      break;
         | 
| 497 | 
            +
                    case MYSQL_TYPE_STRING:       // char[]
         | 
| 498 | 
            +
                    case MYSQL_TYPE_VAR_STRING:   // char[]
         | 
| 499 | 
            +
                    case MYSQL_TYPE_VARCHAR:      // char[]
         | 
| 500 | 
            +
                    case MYSQL_TYPE_TINY_BLOB:    // char[]
         | 
| 501 | 
            +
                    case MYSQL_TYPE_BLOB:         // char[]
         | 
| 502 | 
            +
                    case MYSQL_TYPE_MEDIUM_BLOB:  // char[]
         | 
| 503 | 
            +
                    case MYSQL_TYPE_LONG_BLOB:    // char[]
         | 
| 504 | 
            +
                    case MYSQL_TYPE_SET:          // char[]
         | 
| 505 | 
            +
                    case MYSQL_TYPE_ENUM:         // char[]
         | 
| 506 | 
            +
                    case MYSQL_TYPE_GEOMETRY:     // char[]
         | 
| 507 | 
            +
                    default:
         | 
| 508 | 
            +
                      val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
         | 
| 509 | 
            +
            #ifdef HAVE_RUBY_ENCODING_H
         | 
| 510 | 
            +
                      val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
         | 
| 511 | 
            +
            #endif
         | 
| 512 | 
            +
                      break;
         | 
| 513 | 
            +
                  }
         | 
| 514 | 
            +
                }
         | 
| 515 | 
            +
             | 
| 516 | 
            +
                if (args->asArray) {
         | 
| 517 | 
            +
                  rb_ary_push(rowVal, val);
         | 
| 518 | 
            +
                } else {
         | 
| 519 | 
            +
                  rb_hash_aset(rowVal, field, val);
         | 
| 520 | 
            +
                }
         | 
| 521 | 
            +
              }
         | 
| 522 | 
            +
             | 
| 523 | 
            +
              return rowVal;
         | 
| 524 | 
            +
            }
         | 
| 525 | 
            +
             | 
| 526 | 
            +
            static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
         | 
| 527 | 
            +
            {
         | 
| 198 528 | 
             
              VALUE rowVal;
         | 
| 199 529 | 
             
              MYSQL_ROW row;
         | 
| 200 530 | 
             
              unsigned int i = 0;
         | 
| @@ -217,24 +547,24 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo | |
| 217 547 | 
             
                return Qnil;
         | 
| 218 548 | 
             
              }
         | 
| 219 549 |  | 
| 220 | 
            -
              if ( | 
| 550 | 
            +
              if (wrapper->fields == Qnil) {
         | 
| 551 | 
            +
                wrapper->numberOfFields = mysql_num_fields(wrapper->result);
         | 
| 552 | 
            +
                wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
         | 
| 553 | 
            +
              }
         | 
| 554 | 
            +
              if (args->asArray) {
         | 
| 221 555 | 
             
                rowVal = rb_ary_new2(wrapper->numberOfFields);
         | 
| 222 556 | 
             
              } else {
         | 
| 223 557 | 
             
                rowVal = rb_hash_new();
         | 
| 224 558 | 
             
              }
         | 
| 225 559 | 
             
              fieldLengths = mysql_fetch_lengths(wrapper->result);
         | 
| 226 | 
            -
              if (wrapper->fields == Qnil) {
         | 
| 227 | 
            -
                wrapper->numberOfFields = mysql_num_fields(wrapper->result);
         | 
| 228 | 
            -
                wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
         | 
| 229 | 
            -
              }
         | 
| 230 560 |  | 
| 231 561 | 
             
              for (i = 0; i < wrapper->numberOfFields; i++) {
         | 
| 232 | 
            -
                VALUE field = rb_mysql_result_fetch_field(self, i, symbolizeKeys);
         | 
| 562 | 
            +
                VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
         | 
| 233 563 | 
             
                if (row[i]) {
         | 
| 234 564 | 
             
                  VALUE val = Qnil;
         | 
| 235 565 | 
             
                  enum enum_field_types type = fields[i].type;
         | 
| 236 566 |  | 
| 237 | 
            -
                  if (!cast) {
         | 
| 567 | 
            +
                  if (!args->cast) {
         | 
| 238 568 | 
             
                    if (type == MYSQL_TYPE_NULL) {
         | 
| 239 569 | 
             
                      val = Qnil;
         | 
| 240 570 | 
             
                    } else {
         | 
| @@ -249,14 +579,14 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo | |
| 249 579 | 
             
                      val = Qnil;
         | 
| 250 580 | 
             
                      break;
         | 
| 251 581 | 
             
                    case MYSQL_TYPE_BIT:        /* BIT field (MySQL 5.0.3 and up) */
         | 
| 252 | 
            -
                      if (castBool && fields[i].length == 1) {
         | 
| 582 | 
            +
                      if (args->castBool && fields[i].length == 1) {
         | 
| 253 583 | 
             
                        val = *row[i] == 1 ? Qtrue : Qfalse;
         | 
| 254 584 | 
             
                      }else{
         | 
| 255 585 | 
             
                        val = rb_str_new(row[i], fieldLengths[i]);
         | 
| 256 586 | 
             
                      }
         | 
| 257 587 | 
             
                      break;
         | 
| 258 588 | 
             
                    case MYSQL_TYPE_TINY:       /* TINYINT field */
         | 
| 259 | 
            -
                      if (castBool && fields[i].length == 1) {
         | 
| 589 | 
            +
                      if (args->castBool && fields[i].length == 1) {
         | 
| 260 590 | 
             
                        val = *row[i] != '0' ? Qtrue : Qfalse;
         | 
| 261 591 | 
             
                        break;
         | 
| 262 592 | 
             
                      }
         | 
| @@ -299,9 +629,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo | |
| 299 629 | 
             
                        break;
         | 
| 300 630 | 
             
                      }
         | 
| 301 631 | 
             
                      msec = msec_char_to_uint(msec_char, sizeof(msec_char));
         | 
| 302 | 
            -
                      val = rb_funcall(rb_cTime, db_timezone, 7, opt_time_year, opt_time_month, opt_time_month, UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
         | 
| 303 | 
            -
                      if (!NIL_P(app_timezone)) {
         | 
| 304 | 
            -
                        if (app_timezone == intern_local) {
         | 
| 632 | 
            +
                      val = rb_funcall(rb_cTime, args->db_timezone, 7, opt_time_year, opt_time_month, opt_time_month, UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
         | 
| 633 | 
            +
                      if (!NIL_P(args->app_timezone)) {
         | 
| 634 | 
            +
                        if (args->app_timezone == intern_local) {
         | 
| 305 635 | 
             
                          val = rb_funcall(val, intern_localtime, 0);
         | 
| 306 636 | 
             
                        } else { /* utc */
         | 
| 307 637 | 
             
                          val = rb_funcall(val, intern_utc, 0);
         | 
| @@ -332,12 +662,12 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo | |
| 332 662 | 
             
                        } else {
         | 
| 333 663 | 
             
                          if (seconds < MYSQL2_MIN_TIME || seconds > MYSQL2_MAX_TIME) { /* use DateTime for larger date range, does not support microseconds */
         | 
| 334 664 | 
             
                            VALUE offset = INT2NUM(0);
         | 
| 335 | 
            -
                            if (db_timezone == intern_local) {
         | 
| 665 | 
            +
                            if (args->db_timezone == intern_local) {
         | 
| 336 666 | 
             
                              offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
         | 
| 337 667 | 
             
                            }
         | 
| 338 668 | 
             
                            val = rb_funcall(cDateTime, intern_civil, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), offset);
         | 
| 339 | 
            -
                            if (!NIL_P(app_timezone)) {
         | 
| 340 | 
            -
                              if (app_timezone == intern_local) {
         | 
| 669 | 
            +
                            if (!NIL_P(args->app_timezone)) {
         | 
| 670 | 
            +
                              if (args->app_timezone == intern_local) {
         | 
| 341 671 | 
             
                                offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
         | 
| 342 672 | 
             
                                val = rb_funcall(val, intern_new_offset, 1, offset);
         | 
| 343 673 | 
             
                              } else { /* utc */
         | 
| @@ -346,9 +676,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo | |
| 346 676 | 
             
                            }
         | 
| 347 677 | 
             
                          } else {
         | 
| 348 678 | 
             
                            msec = msec_char_to_uint(msec_char, sizeof(msec_char));
         | 
| 349 | 
            -
                            val = rb_funcall(rb_cTime, db_timezone, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
         | 
| 350 | 
            -
                            if (!NIL_P(app_timezone)) {
         | 
| 351 | 
            -
                              if (app_timezone == intern_local) {
         | 
| 679 | 
            +
                            val = rb_funcall(rb_cTime, args->db_timezone, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
         | 
| 680 | 
            +
                            if (!NIL_P(args->app_timezone)) {
         | 
| 681 | 
            +
                              if (args->app_timezone == intern_local) {
         | 
| 352 682 | 
             
                                val = rb_funcall(val, intern_localtime, 0);
         | 
| 353 683 | 
             
                              } else { /* utc */
         | 
| 354 684 | 
             
                                val = rb_funcall(val, intern_utc, 0);
         | 
| @@ -398,13 +728,13 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo | |
| 398 728 | 
             
                      break;
         | 
| 399 729 | 
             
                    }
         | 
| 400 730 | 
             
                  }
         | 
| 401 | 
            -
                  if (asArray) {
         | 
| 731 | 
            +
                  if (args->asArray) {
         | 
| 402 732 | 
             
                    rb_ary_push(rowVal, val);
         | 
| 403 733 | 
             
                  } else {
         | 
| 404 734 | 
             
                    rb_hash_aset(rowVal, field, val);
         | 
| 405 735 | 
             
                  }
         | 
| 406 736 | 
             
                } else {
         | 
| 407 | 
            -
                  if (asArray) {
         | 
| 737 | 
            +
                  if (args->asArray) {
         | 
| 408 738 | 
             
                    rb_ary_push(rowVal, Qnil);
         | 
| 409 739 | 
             
                  } else {
         | 
| 410 740 | 
             
                    rb_hash_aset(rowVal, field, Qnil);
         | 
| @@ -432,7 +762,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) { | |
| 432 762 | 
             
                wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
         | 
| 433 763 | 
             
              }
         | 
| 434 764 |  | 
| 435 | 
            -
              if (RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
         | 
| 765 | 
            +
              if ((my_ulonglong)RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
         | 
| 436 766 | 
             
                for (i=0; i<wrapper->numberOfFields; i++) {
         | 
| 437 767 | 
             
                  rb_mysql_result_fetch_field(self, i, symbolizeKeys);
         | 
| 438 768 | 
             
                }
         | 
| @@ -441,55 +771,16 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) { | |
| 441 771 | 
             
              return wrapper->fields;
         | 
| 442 772 | 
             
            }
         | 
| 443 773 |  | 
| 444 | 
            -
            static VALUE  | 
| 445 | 
            -
             | 
| 446 | 
            -
             | 
| 774 | 
            +
            static VALUE rb_mysql_result_each_(VALUE self,
         | 
| 775 | 
            +
                                               VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args),
         | 
| 776 | 
            +
                                               const result_each_args *args)
         | 
| 777 | 
            +
            {
         | 
| 447 778 | 
             
              unsigned long i;
         | 
| 448 | 
            -
              const char * | 
| 449 | 
            -
               | 
| 450 | 
            -
              MYSQL_FIELD * fields = NULL;
         | 
| 779 | 
            +
              const char *errstr;
         | 
| 780 | 
            +
              MYSQL_FIELD *fields = NULL;
         | 
| 451 781 |  | 
| 452 782 | 
             
              GET_RESULT(self);
         | 
| 453 783 |  | 
| 454 | 
            -
              defaults = rb_iv_get(self, "@query_options");
         | 
| 455 | 
            -
              Check_Type(defaults, T_HASH);
         | 
| 456 | 
            -
              if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
         | 
| 457 | 
            -
                opts = rb_funcall(defaults, intern_merge, 1, opts);
         | 
| 458 | 
            -
              } else {
         | 
| 459 | 
            -
                opts = defaults;
         | 
| 460 | 
            -
              }
         | 
| 461 | 
            -
             | 
| 462 | 
            -
              symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys));
         | 
| 463 | 
            -
              asArray       = rb_hash_aref(opts, sym_as) == sym_array;
         | 
| 464 | 
            -
              castBool      = RTEST(rb_hash_aref(opts, sym_cast_booleans));
         | 
| 465 | 
            -
              cacheRows     = RTEST(rb_hash_aref(opts, sym_cache_rows));
         | 
| 466 | 
            -
              cast          = RTEST(rb_hash_aref(opts, sym_cast));
         | 
| 467 | 
            -
             | 
| 468 | 
            -
              if (wrapper->is_streaming && cacheRows) {
         | 
| 469 | 
            -
                rb_warn(":cache_rows is ignored if :stream is true");
         | 
| 470 | 
            -
              }
         | 
| 471 | 
            -
             | 
| 472 | 
            -
              dbTz = rb_hash_aref(opts, sym_database_timezone);
         | 
| 473 | 
            -
              if (dbTz == sym_local) {
         | 
| 474 | 
            -
                db_timezone = intern_local;
         | 
| 475 | 
            -
              } else if (dbTz == sym_utc) {
         | 
| 476 | 
            -
                db_timezone = intern_utc;
         | 
| 477 | 
            -
              } else {
         | 
| 478 | 
            -
                if (!NIL_P(dbTz)) {
         | 
| 479 | 
            -
                  rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
         | 
| 480 | 
            -
                }
         | 
| 481 | 
            -
                db_timezone = intern_local;
         | 
| 482 | 
            -
              }
         | 
| 483 | 
            -
             | 
| 484 | 
            -
              appTz = rb_hash_aref(opts, sym_application_timezone);
         | 
| 485 | 
            -
              if (appTz == sym_local) {
         | 
| 486 | 
            -
                app_timezone = intern_local;
         | 
| 487 | 
            -
              } else if (appTz == sym_utc) {
         | 
| 488 | 
            -
                app_timezone = intern_utc;
         | 
| 489 | 
            -
              } else {
         | 
| 490 | 
            -
                app_timezone = Qnil;
         | 
| 491 | 
            -
              }
         | 
| 492 | 
            -
             | 
| 493 784 | 
             
              if (wrapper->is_streaming) {
         | 
| 494 785 | 
             
                /* When streaming, we will only yield rows, not return them. */
         | 
| 495 786 | 
             
                if (wrapper->rows == Qnil) {
         | 
| @@ -502,10 +793,10 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { | |
| 502 793 | 
             
                  fields = mysql_fetch_fields(wrapper->result);
         | 
| 503 794 |  | 
| 504 795 | 
             
                  do {
         | 
| 505 | 
            -
                    row =  | 
| 796 | 
            +
                    row = fetch_row_func(self, fields, args);
         | 
| 506 797 | 
             
                    if (row != Qnil) {
         | 
| 507 798 | 
             
                      wrapper->numberOfRows++;
         | 
| 508 | 
            -
                      if ( | 
| 799 | 
            +
                      if (args->block_given != Qnil) {
         | 
| 509 800 | 
             
                        rb_yield(row);
         | 
| 510 801 | 
             
                      }
         | 
| 511 802 | 
             
                    }
         | 
| @@ -524,16 +815,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { | |
| 524 815 | 
             
                  rb_raise(cMysql2Error, "You have already fetched all the rows for this query and streaming is true. (to reiterate you must requery).");
         | 
| 525 816 | 
             
                }
         | 
| 526 817 | 
             
              } else {
         | 
| 527 | 
            -
                if (wrapper->lastRowProcessed ==  | 
| 528 | 
            -
                  wrapper->numberOfRows = mysql_num_rows(wrapper->result);
         | 
| 529 | 
            -
                  if (wrapper->numberOfRows == 0) {
         | 
| 530 | 
            -
                    wrapper->rows = rb_ary_new();
         | 
| 531 | 
            -
                    return wrapper->rows;
         | 
| 532 | 
            -
                  }
         | 
| 533 | 
            -
                  wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
         | 
| 534 | 
            -
                }
         | 
| 535 | 
            -
             | 
| 536 | 
            -
                if (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
         | 
| 818 | 
            +
                if (args->cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
         | 
| 537 819 | 
             
                  /* we've already read the entire dataset from the C result into our */
         | 
| 538 820 | 
             
                  /* internal array. Lets hand that over to the user since it's ready to go */
         | 
| 539 821 | 
             
                  for (i = 0; i < wrapper->numberOfRows; i++) {
         | 
| @@ -546,11 +828,11 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { | |
| 546 828 |  | 
| 547 829 | 
             
                  for (i = 0; i < wrapper->numberOfRows; i++) {
         | 
| 548 830 | 
             
                    VALUE row;
         | 
| 549 | 
            -
                    if (cacheRows && i < rowsProcessed) {
         | 
| 831 | 
            +
                    if (args->cacheRows && i < rowsProcessed) {
         | 
| 550 832 | 
             
                      row = rb_ary_entry(wrapper->rows, i);
         | 
| 551 833 | 
             
                    } else {
         | 
| 552 | 
            -
                      row =  | 
| 553 | 
            -
                      if (cacheRows) {
         | 
| 834 | 
            +
                      row = fetch_row_func(self, fields, args);
         | 
| 835 | 
            +
                      if (args->cacheRows) {
         | 
| 554 836 | 
             
                        rb_ary_store(wrapper->rows, i, row);
         | 
| 555 837 | 
             
                      }
         | 
| 556 838 | 
             
                      wrapper->lastRowProcessed++;
         | 
| @@ -558,24 +840,119 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { | |
| 558 840 |  | 
| 559 841 | 
             
                    if (row == Qnil) {
         | 
| 560 842 | 
             
                      /* we don't need the mysql C dataset around anymore, peace it */
         | 
| 561 | 
            -
                       | 
| 843 | 
            +
                      if (args->cacheRows) {
         | 
| 844 | 
            +
                        rb_mysql_result_free_result(wrapper);
         | 
| 845 | 
            +
                      }
         | 
| 562 846 | 
             
                      return Qnil;
         | 
| 563 847 | 
             
                    }
         | 
| 564 848 |  | 
| 565 | 
            -
                    if ( | 
| 849 | 
            +
                    if (args->block_given != Qnil) {
         | 
| 566 850 | 
             
                      rb_yield(row);
         | 
| 567 851 | 
             
                    }
         | 
| 568 852 | 
             
                  }
         | 
| 569 | 
            -
                  if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
         | 
| 853 | 
            +
                  if (wrapper->lastRowProcessed == wrapper->numberOfRows && args->cacheRows) {
         | 
| 570 854 | 
             
                    /* we don't need the mysql C dataset around anymore, peace it */
         | 
| 571 855 | 
             
                    rb_mysql_result_free_result(wrapper);
         | 
| 572 856 | 
             
                  }
         | 
| 573 857 | 
             
                }
         | 
| 574 858 | 
             
              }
         | 
| 575 859 |  | 
| 860 | 
            +
              // FIXME return Enumerator instead?
         | 
| 861 | 
            +
              // return rb_ary_each(wrapper->rows);
         | 
| 576 862 | 
             
              return wrapper->rows;
         | 
| 577 863 | 
             
            }
         | 
| 578 864 |  | 
| 865 | 
            +
            static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
         | 
| 866 | 
            +
              result_each_args args;
         | 
| 867 | 
            +
              VALUE defaults, opts, block, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
         | 
| 868 | 
            +
              ID db_timezone, app_timezone, dbTz, appTz;
         | 
| 869 | 
            +
              int symbolizeKeys, asArray, castBool, cacheRows, cast;
         | 
| 870 | 
            +
             | 
| 871 | 
            +
              GET_RESULT(self);
         | 
| 872 | 
            +
             | 
| 873 | 
            +
              if (wrapper->stmt_wrapper && wrapper->stmt_wrapper->closed) {
         | 
| 874 | 
            +
                rb_raise(cMysql2Error, "Statement handle already closed");
         | 
| 875 | 
            +
              }
         | 
| 876 | 
            +
             | 
| 877 | 
            +
              defaults = rb_iv_get(self, "@query_options");
         | 
| 878 | 
            +
              Check_Type(defaults, T_HASH);
         | 
| 879 | 
            +
              if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
         | 
| 880 | 
            +
                opts = rb_funcall(defaults, intern_merge, 1, opts);
         | 
| 881 | 
            +
              } else {
         | 
| 882 | 
            +
                opts = defaults;
         | 
| 883 | 
            +
              }
         | 
| 884 | 
            +
             | 
| 885 | 
            +
              symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys));
         | 
| 886 | 
            +
              asArray       = rb_hash_aref(opts, sym_as) == sym_array;
         | 
| 887 | 
            +
              castBool      = RTEST(rb_hash_aref(opts, sym_cast_booleans));
         | 
| 888 | 
            +
              cacheRows     = RTEST(rb_hash_aref(opts, sym_cache_rows));
         | 
| 889 | 
            +
              cast          = RTEST(rb_hash_aref(opts, sym_cast));
         | 
| 890 | 
            +
             | 
| 891 | 
            +
              if (wrapper->is_streaming && cacheRows) {
         | 
| 892 | 
            +
                rb_warn(":cache_rows is ignored if :stream is true");
         | 
| 893 | 
            +
              }
         | 
| 894 | 
            +
             | 
| 895 | 
            +
              if (wrapper->stmt_wrapper && !cacheRows && !wrapper->is_streaming) {
         | 
| 896 | 
            +
                rb_warn(":cache_rows is forced for prepared statements (if not streaming)");
         | 
| 897 | 
            +
                cacheRows = 1;
         | 
| 898 | 
            +
              }
         | 
| 899 | 
            +
             | 
| 900 | 
            +
              if (wrapper->stmt_wrapper && !cast) {
         | 
| 901 | 
            +
                rb_warn(":cast is forced for prepared statements");
         | 
| 902 | 
            +
              }
         | 
| 903 | 
            +
             | 
| 904 | 
            +
              dbTz = rb_hash_aref(opts, sym_database_timezone);
         | 
| 905 | 
            +
              if (dbTz == sym_local) {
         | 
| 906 | 
            +
                db_timezone = intern_local;
         | 
| 907 | 
            +
              } else if (dbTz == sym_utc) {
         | 
| 908 | 
            +
                db_timezone = intern_utc;
         | 
| 909 | 
            +
              } else {
         | 
| 910 | 
            +
                if (!NIL_P(dbTz)) {
         | 
| 911 | 
            +
                  rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
         | 
| 912 | 
            +
                }
         | 
| 913 | 
            +
                db_timezone = intern_local;
         | 
| 914 | 
            +
              }
         | 
| 915 | 
            +
             | 
| 916 | 
            +
              appTz = rb_hash_aref(opts, sym_application_timezone);
         | 
| 917 | 
            +
              if (appTz == sym_local) {
         | 
| 918 | 
            +
                app_timezone = intern_local;
         | 
| 919 | 
            +
              } else if (appTz == sym_utc) {
         | 
| 920 | 
            +
                app_timezone = intern_utc;
         | 
| 921 | 
            +
              } else {
         | 
| 922 | 
            +
                app_timezone = Qnil;
         | 
| 923 | 
            +
              }
         | 
| 924 | 
            +
             | 
| 925 | 
            +
              if (wrapper->rows == Qnil && !wrapper->is_streaming) {
         | 
| 926 | 
            +
                wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result);
         | 
| 927 | 
            +
                wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
         | 
| 928 | 
            +
              } else if (wrapper->rows && !cacheRows) {
         | 
| 929 | 
            +
                if (wrapper->resultFreed) {
         | 
| 930 | 
            +
                  rb_raise(cMysql2Error, "Result set has already been freed");
         | 
| 931 | 
            +
                }
         | 
| 932 | 
            +
                mysql_data_seek(wrapper->result, 0);
         | 
| 933 | 
            +
                wrapper->lastRowProcessed = 0;
         | 
| 934 | 
            +
                wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
         | 
| 935 | 
            +
              }
         | 
| 936 | 
            +
             | 
| 937 | 
            +
              // Backward compat
         | 
| 938 | 
            +
              args.symbolizeKeys = symbolizeKeys;
         | 
| 939 | 
            +
              args.asArray = asArray;
         | 
| 940 | 
            +
              args.castBool = castBool;
         | 
| 941 | 
            +
              args.cacheRows = cacheRows;
         | 
| 942 | 
            +
              args.cast = cast;
         | 
| 943 | 
            +
              args.db_timezone = db_timezone;
         | 
| 944 | 
            +
              args.app_timezone = app_timezone;
         | 
| 945 | 
            +
              args.block_given = block;
         | 
| 946 | 
            +
             | 
| 947 | 
            +
              if (wrapper->stmt_wrapper) {
         | 
| 948 | 
            +
                fetch_row_func = rb_mysql_result_fetch_row_stmt;
         | 
| 949 | 
            +
              } else {
         | 
| 950 | 
            +
                fetch_row_func = rb_mysql_result_fetch_row;
         | 
| 951 | 
            +
              }
         | 
| 952 | 
            +
             | 
| 953 | 
            +
              return rb_mysql_result_each_(self, fetch_row_func, &args);
         | 
| 954 | 
            +
            }
         | 
| 955 | 
            +
             | 
| 579 956 | 
             
            static VALUE rb_mysql_result_count(VALUE self) {
         | 
| 580 957 | 
             
              GET_RESULT(self);
         | 
| 581 958 |  | 
| @@ -589,12 +966,16 @@ static VALUE rb_mysql_result_count(VALUE self) { | |
| 589 966 | 
             
                return LONG2NUM(RARRAY_LEN(wrapper->rows));
         | 
| 590 967 | 
             
              } else {
         | 
| 591 968 | 
             
                /* MySQL returns an unsigned 64-bit long here */
         | 
| 592 | 
            -
                 | 
| 969 | 
            +
                if (wrapper->stmt_wrapper) {
         | 
| 970 | 
            +
                  return ULL2NUM(mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt));
         | 
| 971 | 
            +
                } else {
         | 
| 972 | 
            +
                  return ULL2NUM(mysql_num_rows(wrapper->result));
         | 
| 973 | 
            +
                }
         | 
| 593 974 | 
             
              }
         | 
| 594 975 | 
             
            }
         | 
| 595 976 |  | 
| 596 977 | 
             
            /* Mysql2::Result */
         | 
| 597 | 
            -
            VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r) {
         | 
| 978 | 
            +
            VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r, VALUE statement) {
         | 
| 598 979 | 
             
              VALUE obj;
         | 
| 599 980 | 
             
              mysql2_result_wrapper * wrapper;
         | 
| 600 981 |  | 
| @@ -611,9 +992,21 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_ | |
| 611 992 | 
             
              wrapper->client = client;
         | 
| 612 993 | 
             
              wrapper->client_wrapper = DATA_PTR(client);
         | 
| 613 994 | 
             
              wrapper->client_wrapper->refcount++;
         | 
| 995 | 
            +
              wrapper->result_buffers = NULL;
         | 
| 996 | 
            +
              wrapper->is_null = NULL;
         | 
| 997 | 
            +
              wrapper->error = NULL;
         | 
| 998 | 
            +
              wrapper->length = NULL;
         | 
| 999 | 
            +
             | 
| 1000 | 
            +
              /* Keep a handle to the Statement to ensure it doesn't get garbage collected first */
         | 
| 1001 | 
            +
              wrapper->statement = statement;
         | 
| 1002 | 
            +
              if (statement != Qnil) {
         | 
| 1003 | 
            +
                wrapper->stmt_wrapper = DATA_PTR(statement);
         | 
| 1004 | 
            +
                wrapper->stmt_wrapper->refcount++;
         | 
| 1005 | 
            +
              } else {
         | 
| 1006 | 
            +
                wrapper->stmt_wrapper = NULL;
         | 
| 1007 | 
            +
              }
         | 
| 614 1008 |  | 
| 615 1009 | 
             
              rb_obj_call_init(obj, 0, NULL);
         | 
| 616 | 
            -
             | 
| 617 1010 | 
             
              rb_iv_set(obj, "@query_options", options);
         | 
| 618 1011 |  | 
| 619 1012 | 
             
              /* Options that cannot be changed in results.each(...) { |row| }
         | 
| @@ -631,6 +1024,7 @@ void init_mysql2_result() { | |
| 631 1024 | 
             
              cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
         | 
| 632 1025 | 
             
              rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
         | 
| 633 1026 | 
             
              rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
         | 
| 1027 | 
            +
              rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
         | 
| 634 1028 | 
             
              rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
         | 
| 635 1029 | 
             
              rb_define_alias(cMysql2Result, "size", "count");
         | 
| 636 1030 |  |