sqlite3 1.3.13 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,5 +11,7 @@ typedef struct _sqlite3Ruby sqlite3Ruby;
11
11
  typedef sqlite3Ruby * sqlite3RubyPtr;
12
12
 
13
13
  void init_sqlite3_database();
14
+ void set_sqlite3_func_result(sqlite3_context * ctx, VALUE result);
15
+ VALUE sqlite3val2rb(sqlite3_value * val);
14
16
 
15
17
  #endif
@@ -4,7 +4,9 @@ void rb_sqlite3_raise(sqlite3 * db, int status)
4
4
  {
5
5
  VALUE klass = Qnil;
6
6
 
7
- switch(status) {
7
+ /* Consider only lower 8 bits, to work correctly when
8
+ extended result codes are enabled. */
9
+ switch(status & 0xff) {
8
10
  case SQLITE_OK:
9
11
  return;
10
12
  break;
@@ -90,5 +92,7 @@ void rb_sqlite3_raise(sqlite3 * db, int status)
90
92
  klass = rb_eRuntimeError;
91
93
  }
92
94
 
93
- rb_raise(klass, "%s", sqlite3_errmsg(db));
95
+ klass = rb_exc_new2(klass, sqlite3_errmsg(db));
96
+ rb_iv_set(klass, "@code", INT2FIX(status));
97
+ rb_exc_raise(klass);
94
98
  }
@@ -6,15 +6,20 @@ require 'mkmf'
6
6
 
7
7
  RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
8
8
 
9
-
10
-
11
9
  ldflags = cppflags = nil
12
10
  if RbConfig::CONFIG["host_os"] =~ /darwin/
13
11
  begin
14
- brew_prefix = `brew --prefix sqlite3`.chomp
15
- ldflags = "#{brew_prefix}/lib"
16
- cppflags = "#{brew_prefix}/include"
17
- pkg_conf = "#{brew_prefix}/lib/pkgconfig"
12
+ if with_config('sqlcipher')
13
+ brew_prefix = `brew --prefix sqlcipher`.chomp
14
+ ldflags = "#{brew_prefix}/lib"
15
+ cppflags = "#{brew_prefix}/include/sqlcipher"
16
+ pkg_conf = "#{brew_prefix}/lib/pkgconfig"
17
+ else
18
+ brew_prefix = `brew --prefix sqlite3`.chomp
19
+ ldflags = "#{brew_prefix}/lib"
20
+ cppflags = "#{brew_prefix}/include"
21
+ pkg_conf = "#{brew_prefix}/lib/pkgconfig"
22
+ end
18
23
 
19
24
  # pkg_config should be less error prone than parsing compiler
20
25
  # commandline options, but we need to set default ldflags and cpp flags
@@ -24,10 +29,19 @@ if RbConfig::CONFIG["host_os"] =~ /darwin/
24
29
  end
25
30
  end
26
31
 
27
- pkg_config("sqlite3")
32
+ if with_config('sqlcipher')
33
+ pkg_config("sqlcipher")
34
+ else
35
+ pkg_config("sqlite3")
36
+ end
28
37
 
29
38
  # --with-sqlite3-{dir,include,lib}
30
- dir_config("sqlite3", cppflags, ldflags)
39
+ if with_config('sqlcipher')
40
+ $CFLAGS << ' -DUSING_SQLCIPHER'
41
+ dir_config("sqlcipher", cppflags, ldflags)
42
+ else
43
+ dir_config("sqlite3", cppflags, ldflags)
44
+ end
31
45
 
32
46
  if RbConfig::CONFIG["host_os"] =~ /mswin/
33
47
  $CFLAGS << ' -W3'
@@ -49,7 +63,18 @@ end
49
63
 
50
64
  asplode('sqlite3.h') unless find_header 'sqlite3.h'
51
65
  find_library 'pthread', 'pthread_create' # 1.8 support. *shrug*
52
- asplode('sqlite3') unless find_library 'sqlite3', 'sqlite3_libversion_number'
66
+
67
+ have_library 'dl'
68
+
69
+ %w{ dlopen dlclose dlsym }.each do |func|
70
+ abort "missing function #{func}" unless have_func(func)
71
+ end
72
+
73
+ if with_config('sqlcipher')
74
+ asplode('sqlcipher') unless find_library 'sqlcipher', 'sqlite3_libversion_number'
75
+ else
76
+ asplode('sqlite3') unless find_library 'sqlite3', 'sqlite3_libversion_number'
77
+ end
53
78
 
54
79
  # Functions defined in 1.9 but not 1.8
55
80
  have_func('rb_proc_arity')
@@ -63,7 +88,11 @@ have_func('sqlite3_backup_init')
63
88
  have_func('sqlite3_column_database_name')
64
89
  have_func('sqlite3_enable_load_extension')
65
90
  have_func('sqlite3_load_extension')
66
- have_func('sqlite3_open_v2')
91
+
92
+ unless have_func('sqlite3_open_v2')
93
+ abort "Please use a newer version of SQLite3"
94
+ end
95
+
67
96
  have_func('sqlite3_prepare_v2')
68
97
  have_type('sqlite3_int64', 'sqlite3.h')
69
98
  have_type('sqlite3_uint64', 'sqlite3.h')
@@ -65,6 +65,15 @@ static VALUE libversion(VALUE UNUSED(klass))
65
65
  return INT2NUM(sqlite3_libversion_number());
66
66
  }
67
67
 
68
+ static VALUE using_sqlcipher(VALUE UNUSED(klass))
69
+ {
70
+ #ifdef USING_SQLCIPHER
71
+ return Qtrue;
72
+ #else
73
+ return Qfalse;
74
+ #endif
75
+ }
76
+
68
77
  /* Returns the compile time setting of the SQLITE_THREADSAFE flag.
69
78
  * See: https://www.sqlite.org/c3ref/threadsafe.html
70
79
  */
@@ -144,7 +153,7 @@ void Init_sqlite3_native()
144
153
  #ifdef HAVE_SQLITE3_BACKUP_INIT
145
154
  init_sqlite3_backup();
146
155
  #endif
147
-
156
+ rb_define_singleton_method(mSqlite3, "sqlcipher?", using_sqlcipher, 0);
148
157
  rb_define_singleton_method(mSqlite3, "libversion", libversion, 0);
149
158
  rb_define_singleton_method(mSqlite3, "threadsafe", threadsafe_p, 0);
150
159
  rb_define_const(mSqlite3, "SQLITE_VERSION", rb_str_new2(SQLITE_VERSION));
@@ -12,7 +12,6 @@
12
12
  # define UNUSED(x) x
13
13
  #endif
14
14
 
15
- #ifdef HAVE_RUBY_ENCODING_H
16
15
  #include <ruby/encoding.h>
17
16
 
18
17
  #define USASCII_P(_obj) (rb_enc_get_index(_obj) == rb_usascii_encindex())
@@ -22,12 +21,6 @@
22
21
  #define SQLITE3_UTF8_STR_NEW2(_obj) \
23
22
  (rb_enc_associate_index(rb_str_new2(_obj), rb_utf8_encindex()))
24
23
 
25
- #else
26
-
27
- #define SQLITE3_UTF8_STR_NEW2(_obj) (rb_str_new2(_obj))
28
-
29
- #endif
30
-
31
24
 
32
25
  #include <sqlite3.h>
33
26
 
@@ -43,11 +43,9 @@ static VALUE initialize(VALUE self, VALUE db, VALUE sql)
43
43
  if(!db_ctx->db)
44
44
  rb_raise(rb_eArgError, "prepare called on a closed database");
45
45
 
46
- #ifdef HAVE_RUBY_ENCODING_H
47
46
  if(!UTF8_P(sql)) {
48
47
  sql = rb_str_export_to_enc(sql, rb_utf8_encoding());
49
48
  }
50
- #endif
51
49
 
52
50
  #ifdef HAVE_SQLITE3_PREPARE_V2
53
51
  status = sqlite3_prepare_v2(
@@ -110,9 +108,7 @@ static VALUE step(VALUE self)
110
108
  sqlite3_stmt *stmt;
111
109
  int value, length;
112
110
  VALUE list;
113
- #ifdef HAVE_RUBY_ENCODING_H
114
111
  rb_encoding * internal_encoding;
115
- #endif
116
112
 
117
113
  Data_Get_Struct(self, sqlite3StmtRuby, ctx);
118
114
 
@@ -120,17 +116,24 @@ static VALUE step(VALUE self)
120
116
 
121
117
  if(ctx->done_p) return Qnil;
122
118
 
123
- #ifdef HAVE_RUBY_ENCODING_H
124
119
  {
125
120
  VALUE db = rb_iv_get(self, "@connection");
126
121
  rb_funcall(db, rb_intern("encoding"), 0);
127
122
  internal_encoding = rb_default_internal_encoding();
128
123
  }
129
- #endif
130
124
 
131
125
  stmt = ctx->st;
132
126
 
133
127
  value = sqlite3_step(stmt);
128
+ if (rb_errinfo() != Qnil) {
129
+ /* some user defined function was invoked as a callback during step and
130
+ * it raised an exception that has been suppressed until step returns.
131
+ * Now re-raise it. */
132
+ VALUE exception = rb_errinfo();
133
+ rb_set_errinfo(Qnil);
134
+ rb_exc_raise(exception);
135
+ }
136
+
134
137
  length = sqlite3_column_count(stmt);
135
138
  list = rb_ary_new2((long)length);
136
139
 
@@ -152,11 +155,9 @@ static VALUE step(VALUE self)
152
155
  (const char *)sqlite3_column_text(stmt, i),
153
156
  (long)sqlite3_column_bytes(stmt, i)
154
157
  );
155
- #ifdef HAVE_RUBY_ENCODING_H
156
158
  rb_enc_associate_index(str, rb_utf8_encindex());
157
159
  if(internal_encoding)
158
160
  str = rb_str_export_to_enc(str, internal_encoding);
159
- #endif
160
161
  rb_ary_push(list, str);
161
162
  }
162
163
  break;
@@ -225,9 +226,7 @@ static VALUE bind_param(VALUE self, VALUE key, VALUE value)
225
226
  switch(TYPE(value)) {
226
227
  case T_STRING:
227
228
  if(CLASS_OF(value) == cSqlite3Blob
228
- #ifdef HAVE_RUBY_ENCODING_H
229
229
  || rb_enc_get_index(value) == rb_ascii8bit_encindex()
230
- #endif
231
230
  ) {
232
231
  status = sqlite3_bind_blob(
233
232
  ctx->st,
@@ -239,7 +238,6 @@ static VALUE bind_param(VALUE self, VALUE key, VALUE value)
239
238
  } else {
240
239
 
241
240
 
242
- #ifdef HAVE_RUBY_ENCODING_H
243
241
  if (UTF16_LE_P(value) || UTF16_BE_P(value)) {
244
242
  status = sqlite3_bind_text16(
245
243
  ctx->st,
@@ -252,7 +250,6 @@ static VALUE bind_param(VALUE self, VALUE key, VALUE value)
252
250
  if (!UTF8_P(value) || !USASCII_P(value)) {
253
251
  value = rb_str_encode(value, rb_enc_from_encoding(rb_utf8_encoding()), 0, Qnil);
254
252
  }
255
- #endif
256
253
  status = sqlite3_bind_text(
257
254
  ctx->st,
258
255
  index,
@@ -260,9 +257,7 @@ static VALUE bind_param(VALUE self, VALUE key, VALUE value)
260
257
  (int)RSTRING_LEN(value),
261
258
  SQLITE_TRANSIENT
262
259
  );
263
- #ifdef HAVE_RUBY_ENCODING_H
264
260
  }
265
- #endif
266
261
  }
267
262
  break;
268
263
  case T_BIGNUM: {
@@ -316,7 +311,7 @@ static VALUE reset_bang(VALUE self)
316
311
  * Resets the statement. This is typically done internally, though it might
317
312
  * occassionally be necessary to manually reset the statement.
318
313
  */
319
- static VALUE clear_bindings(VALUE self)
314
+ static VALUE clear_bindings_bang(VALUE self)
320
315
  {
321
316
  sqlite3StmtRubyPtr ctx;
322
317
 
@@ -433,7 +428,7 @@ void init_sqlite3_statement()
433
428
  rb_define_method(cSqlite3Statement, "closed?", closed_p, 0);
434
429
  rb_define_method(cSqlite3Statement, "bind_param", bind_param, 2);
435
430
  rb_define_method(cSqlite3Statement, "reset!", reset_bang, 0);
436
- rb_define_method(cSqlite3Statement, "clear_bindings!", clear_bindings, 0);
431
+ rb_define_method(cSqlite3Statement, "clear_bindings!", clear_bindings_bang, 0);
437
432
  rb_define_method(cSqlite3Statement, "step", step, 0);
438
433
  rb_define_method(cSqlite3Statement, "done?", done_p, 0);
439
434
  rb_define_method(cSqlite3Statement, "column_count", column_count, 0);
@@ -6,6 +6,7 @@ module SQLite3 ; module Constants
6
6
  UTF16BE = 3
7
7
  UTF16 = 4
8
8
  ANY = 5
9
+ DETERMINISTIC = 0x800
9
10
  end
10
11
 
11
12
  module ColumnType
@@ -54,12 +54,68 @@ module SQLite3
54
54
  # as hashes or not. By default, rows are returned as arrays.
55
55
  attr_accessor :results_as_hash
56
56
 
57
+ # call-seq: SQLite3::Database.new(file, options = {})
58
+ #
59
+ # Create a new Database object that opens the given file. If utf16
60
+ # is +true+, the filename is interpreted as a UTF-16 encoded string.
61
+ #
62
+ # By default, the new database will return result rows as arrays
63
+ # (#results_as_hash) and has type translation disabled (#type_translation=).
64
+
65
+ def initialize file, options = {}, zvfs = nil
66
+ mode = Constants::Open::READWRITE | Constants::Open::CREATE
67
+
68
+ if file.encoding == ::Encoding::UTF_16LE || file.encoding == ::Encoding::UTF_16BE || options[:utf16]
69
+ open16 file
70
+ else
71
+ # The three primary flag values for sqlite3_open_v2 are:
72
+ # SQLITE_OPEN_READONLY
73
+ # SQLITE_OPEN_READWRITE
74
+ # SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE -- always used for sqlite3_open and sqlite3_open16
75
+ mode = Constants::Open::READONLY if options[:readonly]
76
+
77
+ if options[:readwrite]
78
+ raise "conflicting options: readonly and readwrite" if options[:readonly]
79
+ mode = Constants::Open::READWRITE
80
+ end
81
+
82
+ if options[:flags]
83
+ if options[:readonly] || options[:readwrite]
84
+ raise "conflicting options: flags with readonly and/or readwrite"
85
+ end
86
+ mode = options[:flags]
87
+ end
88
+
89
+ open_v2 file.encode("utf-8"), mode, zvfs
90
+ end
91
+
92
+ @tracefunc = nil
93
+ @authorizer = nil
94
+ @encoding = nil
95
+ @busy_handler = nil
96
+ @collations = {}
97
+ @functions = {}
98
+ @results_as_hash = options[:results_as_hash]
99
+ @type_translation = options[:type_translation]
100
+ @type_translator = make_type_translator @type_translation
101
+ @readonly = mode & Constants::Open::READONLY != 0
102
+
103
+ if block_given?
104
+ begin
105
+ yield self
106
+ ensure
107
+ close
108
+ end
109
+ end
110
+ end
111
+
57
112
  def type_translation= value # :nodoc:
58
113
  warn(<<-eowarn) if $VERBOSE
59
114
  #{caller[0]} is calling SQLite3::Database#type_translation=
60
115
  SQLite3::Database#type_translation= is deprecated and will be removed
61
116
  in version 2.0.0.
62
117
  eowarn
118
+ @type_translator = make_type_translator value
63
119
  @type_translation = value
64
120
  end
65
121
  attr_reader :type_translation # :nodoc:
@@ -136,25 +192,14 @@ Support for bind parameters as *args will be removed in 2.0.0.
136
192
 
137
193
  prepare( sql ) do |stmt|
138
194
  stmt.bind_params(bind_vars)
139
- columns = stmt.columns
140
- stmt = ResultSet.new(self, stmt).to_a if type_translation
195
+ stmt = ResultSet.new self, stmt
141
196
 
142
197
  if block_given?
143
198
  stmt.each do |row|
144
- if @results_as_hash
145
- yield type_translation ? row : ordered_map_for(columns, row)
146
- else
147
- yield row
148
- end
199
+ yield row
149
200
  end
150
201
  else
151
- if @results_as_hash
152
- stmt.map { |row|
153
- type_translation ? row : ordered_map_for(columns, row)
154
- }
155
- else
156
- stmt.to_a
157
- end
202
+ stmt.to_a
158
203
  end
159
204
  end
160
205
  end
@@ -190,6 +235,9 @@ Support for bind parameters as *args will be removed in 2.0.0.
190
235
  #
191
236
  # This always returns +nil+, making it unsuitable for queries that return
192
237
  # rows.
238
+ #
239
+ # See also #execute_batch2 for additional ways of
240
+ # executing statments.
193
241
  def execute_batch( sql, bind_vars = [], *args )
194
242
  # FIXME: remove this stuff later
195
243
  unless [Array, Hash].include?(bind_vars.class)
@@ -234,6 +282,30 @@ Support for this behavior will be removed in version 2.0.0.
234
282
  nil
235
283
  end
236
284
 
285
+ # Executes all SQL statements in the given string. By contrast, the other
286
+ # means of executing queries will only execute the first statement in the
287
+ # string, ignoring all subsequent statements. This will execute each one
288
+ # in turn. Bind parameters cannot be passed to #execute_batch2.
289
+ #
290
+ # If a query is made, all values will be returned as strings.
291
+ # If no query is made, an empty array will be returned.
292
+ #
293
+ # Because all values except for 'NULL' are returned as strings,
294
+ # a block can be passed to parse the values accordingly.
295
+ #
296
+ # See also #execute_batch for additional ways of
297
+ # executing statments.
298
+ def execute_batch2(sql, &block)
299
+ if block_given?
300
+ result = exec_batch(sql, @results_as_hash)
301
+ result.map do |val|
302
+ yield val
303
+ end
304
+ else
305
+ exec_batch(sql, @results_as_hash)
306
+ end
307
+ end
308
+
237
309
  # This is a convenience method for creating a statement, binding
238
310
  # paramters to it, and calling execute:
239
311
  #
@@ -287,7 +359,11 @@ Support for this will be removed in version 2.0.0.
287
359
  #
288
360
  # See also #get_first_row.
289
361
  def get_first_value( sql, *bind_vars )
290
- execute( sql, *bind_vars ) { |row| return row[0] }
362
+ query( sql, bind_vars ) do |rs|
363
+ if (row = rs.next)
364
+ return @results_as_hash ? row[rs.columns[0]] : row[0]
365
+ end
366
+ end
291
367
  nil
292
368
  end
293
369
 
@@ -316,8 +392,8 @@ Support for this will be removed in version 2.0.0.
316
392
  # end
317
393
  #
318
394
  # puts db.get_first_value( "select maim(name) from table" )
319
- def create_function name, arity, text_rep=Constants::TextRep::ANY, &block
320
- define_function(name) do |*args|
395
+ def create_function name, arity, text_rep=Constants::TextRep::UTF8, &block
396
+ define_function_with_flags(name, text_rep) do |*args|
321
397
  fp = FunctionProxy.new
322
398
  block.call(fp, *args)
323
399
  fp.result
@@ -364,42 +440,52 @@ Support for this will be removed in version 2.0.0.
364
440
  def create_aggregate( name, arity, step=nil, finalize=nil,
365
441
  text_rep=Constants::TextRep::ANY, &block )
366
442
 
367
- factory = Class.new do
443
+ proxy = Class.new do
368
444
  def self.step( &block )
369
- define_method(:step, &block)
445
+ define_method(:step_with_ctx, &block)
370
446
  end
371
447
 
372
448
  def self.finalize( &block )
373
- define_method(:finalize, &block)
449
+ define_method(:finalize_with_ctx, &block)
374
450
  end
375
451
  end
376
452
 
377
453
  if block_given?
378
- factory.instance_eval(&block)
454
+ proxy.instance_eval(&block)
379
455
  else
380
- factory.class_eval do
381
- define_method(:step, step)
382
- define_method(:finalize, finalize)
456
+ proxy.class_eval do
457
+ define_method(:step_with_ctx, step)
458
+ define_method(:finalize_with_ctx, finalize)
383
459
  end
384
460
  end
385
461
 
386
- proxy = factory.new
387
- proxy.extend(Module.new {
388
- attr_accessor :ctx
462
+ proxy.class_eval do
463
+ # class instance variables
464
+ @name = name
465
+ @arity = arity
466
+
467
+ def self.name
468
+ @name
469
+ end
470
+
471
+ def self.arity
472
+ @arity
473
+ end
474
+
475
+ def initialize
476
+ @ctx = FunctionProxy.new
477
+ end
389
478
 
390
479
  def step( *args )
391
- super(@ctx, *args)
480
+ step_with_ctx(@ctx, *args)
392
481
  end
393
482
 
394
483
  def finalize
395
- super(@ctx)
396
- result = @ctx.result
397
- @ctx = FunctionProxy.new
398
- result
484
+ finalize_with_ctx(@ctx)
485
+ @ctx.result
399
486
  end
400
- })
401
- proxy.ctx = FunctionProxy.new
402
- define_aggregator(name, proxy)
487
+ end
488
+ define_aggregator2(proxy, name)
403
489
  end
404
490
 
405
491
  # This is another approach to creating an aggregate function (see
@@ -450,29 +536,75 @@ Support for this will be removed in version 2.0.0.
450
536
  # db.create_aggregate_handler( LengthsAggregateHandler )
451
537
  # puts db.get_first_value( "select lengths(name) from A" )
452
538
  def create_aggregate_handler( handler )
453
- proxy = Class.new do
454
- def initialize klass
455
- @klass = klass
456
- @fp = FunctionProxy.new
539
+ # This is a compatiblity shim so the (basically pointless) FunctionProxy
540
+ # "ctx" object is passed as first argument to both step() and finalize().
541
+ # Now its up to the library user whether he prefers to store his
542
+ # temporaries as instance varibales or fields in the FunctionProxy.
543
+ # The library user still must set the result value with
544
+ # FunctionProxy.result= as there is no backwards compatible way to
545
+ # change this.
546
+ proxy = Class.new(handler) do
547
+ def initialize
548
+ super
549
+ @fp = FunctionProxy.new
457
550
  end
458
551
 
459
552
  def step( *args )
460
- instance.step(@fp, *args)
553
+ super(@fp, *args)
461
554
  end
462
555
 
463
556
  def finalize
464
- instance.finalize @fp
465
- @instance = nil
557
+ super(@fp)
466
558
  @fp.result
467
559
  end
560
+ end
561
+ define_aggregator2(proxy, proxy.name)
562
+ self
563
+ end
468
564
 
469
- private
565
+ # Define an aggregate function named +name+ using a object template
566
+ # object +aggregator+. +aggregator+ must respond to +step+ and +finalize+.
567
+ # +step+ will be called with row information and +finalize+ must return the
568
+ # return value for the aggregator function.
569
+ #
570
+ # _API Change:_ +aggregator+ must also implement +clone+. The provided
571
+ # +aggregator+ object will serve as template that is cloned to provide the
572
+ # individual instances of the aggregate function. Regular ruby objects
573
+ # already provide a suitable +clone+.
574
+ # The functions arity is the arity of the +step+ method.
575
+ def define_aggregator( name, aggregator )
576
+ # Previously, this has been implemented in C. Now this is just yet
577
+ # another compatiblity shim
578
+ proxy = Class.new do
579
+ @template = aggregator
580
+ @name = name
470
581
 
471
- def instance
472
- @instance ||= @klass.new
582
+ def self.template
583
+ @template
584
+ end
585
+
586
+ def self.name
587
+ @name
588
+ end
589
+
590
+ def self.arity
591
+ # this is what sqlite3_obj_method_arity did before
592
+ @template.method(:step).arity
593
+ end
594
+
595
+ def initialize
596
+ @klass = self.class.template.clone
597
+ end
598
+
599
+ def step(*args)
600
+ @klass.step(*args)
601
+ end
602
+
603
+ def finalize
604
+ @klass.finalize
473
605
  end
474
606
  end
475
- define_aggregator(handler.name, proxy.new(handler))
607
+ define_aggregator2(proxy, name)
476
608
  self
477
609
  end
478
610
 
@@ -499,7 +631,7 @@ Support for this will be removed in version 2.0.0.
499
631
  abort = false
500
632
  begin
501
633
  yield self
502
- rescue ::Object
634
+ rescue
503
635
  abort = true
504
636
  raise
505
637
  ensure
@@ -580,12 +712,25 @@ Support for this will be removed in version 2.0.0.
580
712
  end
581
713
  end
582
714
 
715
+ # Translates a +row+ of data from the database with the given +types+
716
+ def translate_from_db types, row
717
+ @type_translator.call types, row
718
+ end
719
+
583
720
  private
584
721
 
585
- def ordered_map_for columns, row
586
- h = Hash[*columns.zip(row).flatten]
587
- row.each_with_index { |r, i| h[i] = r }
588
- h
722
+ NULL_TRANSLATOR = lambda { |_, row| row }
723
+
724
+ def make_type_translator should_translate
725
+ if should_translate
726
+ lambda { |types, row|
727
+ types.zip(row).map do |type, value|
728
+ translator.translate( type, value )
729
+ end
730
+ }
731
+ else
732
+ NULL_TRANSLATOR
733
+ end
589
734
  end
590
735
  end
591
736
  end