sequel 4.9.0 → 4.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +79 -1
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/Rakefile +2 -12
  6. data/bin/sequel +1 -0
  7. data/doc/advanced_associations.rdoc +82 -25
  8. data/doc/association_basics.rdoc +21 -22
  9. data/doc/core_extensions.rdoc +1 -1
  10. data/doc/opening_databases.rdoc +7 -0
  11. data/doc/release_notes/4.10.0.txt +226 -0
  12. data/doc/security.rdoc +1 -0
  13. data/doc/testing.rdoc +7 -7
  14. data/doc/transactions.rdoc +8 -0
  15. data/lib/sequel/adapters/jdbc.rb +160 -168
  16. data/lib/sequel/adapters/jdbc/db2.rb +17 -18
  17. data/lib/sequel/adapters/jdbc/derby.rb +5 -28
  18. data/lib/sequel/adapters/jdbc/h2.rb +11 -22
  19. data/lib/sequel/adapters/jdbc/hsqldb.rb +31 -18
  20. data/lib/sequel/adapters/jdbc/jtds.rb +0 -15
  21. data/lib/sequel/adapters/jdbc/oracle.rb +36 -35
  22. data/lib/sequel/adapters/jdbc/postgresql.rb +72 -90
  23. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +18 -16
  24. data/lib/sequel/adapters/jdbc/sqlite.rb +7 -0
  25. data/lib/sequel/adapters/jdbc/sqlserver.rb +10 -30
  26. data/lib/sequel/adapters/jdbc/transactions.rb +5 -6
  27. data/lib/sequel/adapters/openbase.rb +1 -7
  28. data/lib/sequel/adapters/postgres.rb +1 -1
  29. data/lib/sequel/adapters/shared/access.rb +3 -6
  30. data/lib/sequel/adapters/shared/cubrid.rb +24 -9
  31. data/lib/sequel/adapters/shared/db2.rb +13 -5
  32. data/lib/sequel/adapters/shared/firebird.rb +16 -16
  33. data/lib/sequel/adapters/shared/informix.rb +2 -5
  34. data/lib/sequel/adapters/shared/mssql.rb +72 -63
  35. data/lib/sequel/adapters/shared/mysql.rb +72 -40
  36. data/lib/sequel/adapters/shared/oracle.rb +27 -15
  37. data/lib/sequel/adapters/shared/postgres.rb +24 -44
  38. data/lib/sequel/adapters/shared/progress.rb +1 -5
  39. data/lib/sequel/adapters/shared/sqlanywhere.rb +26 -18
  40. data/lib/sequel/adapters/shared/sqlite.rb +21 -6
  41. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +8 -1
  42. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -2
  43. data/lib/sequel/adapters/utils/split_alter_table.rb +8 -0
  44. data/lib/sequel/core.rb +14 -9
  45. data/lib/sequel/database/dataset_defaults.rb +1 -0
  46. data/lib/sequel/database/misc.rb +12 -0
  47. data/lib/sequel/database/query.rb +4 -1
  48. data/lib/sequel/database/schema_methods.rb +3 -2
  49. data/lib/sequel/database/transactions.rb +47 -17
  50. data/lib/sequel/dataset/features.rb +12 -2
  51. data/lib/sequel/dataset/mutation.rb +2 -0
  52. data/lib/sequel/dataset/placeholder_literalizer.rb +12 -4
  53. data/lib/sequel/dataset/prepared_statements.rb +6 -0
  54. data/lib/sequel/dataset/query.rb +1 -1
  55. data/lib/sequel/dataset/sql.rb +132 -70
  56. data/lib/sequel/extensions/columns_introspection.rb +1 -1
  57. data/lib/sequel/extensions/null_dataset.rb +8 -4
  58. data/lib/sequel/extensions/pg_array.rb +4 -4
  59. data/lib/sequel/extensions/pg_row.rb +1 -0
  60. data/lib/sequel/model/associations.rb +468 -188
  61. data/lib/sequel/model/base.rb +88 -13
  62. data/lib/sequel/plugins/association_pks.rb +23 -64
  63. data/lib/sequel/plugins/auto_validations.rb +3 -2
  64. data/lib/sequel/plugins/dataset_associations.rb +1 -3
  65. data/lib/sequel/plugins/many_through_many.rb +18 -65
  66. data/lib/sequel/plugins/pg_array_associations.rb +97 -86
  67. data/lib/sequel/plugins/prepared_statements.rb +2 -1
  68. data/lib/sequel/plugins/prepared_statements_associations.rb +36 -27
  69. data/lib/sequel/plugins/rcte_tree.rb +12 -16
  70. data/lib/sequel/plugins/sharding.rb +21 -3
  71. data/lib/sequel/plugins/single_table_inheritance.rb +2 -1
  72. data/lib/sequel/plugins/subclasses.rb +1 -9
  73. data/lib/sequel/plugins/tactical_eager_loading.rb +9 -0
  74. data/lib/sequel/plugins/tree.rb +2 -2
  75. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  76. data/lib/sequel/version.rb +1 -1
  77. data/spec/adapters/mssql_spec.rb +57 -15
  78. data/spec/adapters/mysql_spec.rb +11 -0
  79. data/spec/bin_spec.rb +2 -2
  80. data/spec/core/database_spec.rb +38 -4
  81. data/spec/core/dataset_spec.rb +45 -7
  82. data/spec/core/placeholder_literalizer_spec.rb +17 -0
  83. data/spec/core/schema_spec.rb +6 -1
  84. data/spec/extensions/active_model_spec.rb +18 -9
  85. data/spec/extensions/association_pks_spec.rb +20 -18
  86. data/spec/extensions/association_proxies_spec.rb +9 -9
  87. data/spec/extensions/auto_validations_spec.rb +6 -0
  88. data/spec/extensions/columns_introspection_spec.rb +1 -0
  89. data/spec/extensions/constraint_validations_spec.rb +3 -1
  90. data/spec/extensions/many_through_many_spec.rb +191 -111
  91. data/spec/extensions/pg_array_associations_spec.rb +133 -103
  92. data/spec/extensions/prepared_statements_associations_spec.rb +23 -4
  93. data/spec/extensions/rcte_tree_spec.rb +35 -27
  94. data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -1
  95. data/spec/extensions/sharding_spec.rb +2 -2
  96. data/spec/extensions/tactical_eager_loading_spec.rb +4 -0
  97. data/spec/extensions/to_dot_spec.rb +1 -0
  98. data/spec/extensions/touch_spec.rb +2 -2
  99. data/spec/integration/associations_test.rb +130 -37
  100. data/spec/integration/dataset_test.rb +17 -0
  101. data/spec/integration/model_test.rb +17 -0
  102. data/spec/integration/schema_test.rb +14 -0
  103. data/spec/integration/transaction_test.rb +25 -1
  104. data/spec/model/association_reflection_spec.rb +63 -24
  105. data/spec/model/associations_spec.rb +104 -57
  106. data/spec/model/base_spec.rb +14 -1
  107. data/spec/model/class_dataset_methods_spec.rb +1 -0
  108. data/spec/model/eager_loading_spec.rb +221 -74
  109. data/spec/model/model_spec.rb +119 -1
  110. metadata +4 -2
data/doc/security.rdoc CHANGED
@@ -18,6 +18,7 @@ could conceivably be abused to do so:
18
18
 
19
19
  * Sequel::Schema::CreateTableGenerator.add_type_method
20
20
  * Sequel::Dataset.def_mutation_method
21
+ * Sequel::Dataset.def_sql_method
21
22
  * Sequel::Model::Plugins.def_dataset_methods
22
23
  * Sequel.def_adapter_method (private)
23
24
  * Sequel::SQL::Expression.to_s_method (private)
data/doc/testing.rdoc CHANGED
@@ -11,7 +11,7 @@ These run each test in its own transaction, the recommended way to test.
11
11
  class Spec::Example::ExampleGroup
12
12
  def execute(*args, &block)
13
13
  result = nil
14
- Sequel::Model.db.transaction(:rollback=>:always){result = super(*args, &block)}
14
+ Sequel::Model.db.transaction(:rollback=>:always, :auto_savepoint=>true){result = super(*args, &block)}
15
15
  result
16
16
  end
17
17
  end
@@ -24,7 +24,7 @@ These run each test in its own transaction, the recommended way to test.
24
24
  def self.inherited(subclass)
25
25
  super
26
26
  subclass.around do |example|
27
- Sequel::Model.db.transaction(:rollback=>:always){example.call}
27
+ Sequel::Model.db.transaction(:rollback=>:always, :auto_savepoint=>true){example.call}
28
28
  end
29
29
  end
30
30
  end
@@ -34,7 +34,7 @@ These run each test in its own transaction, the recommended way to test.
34
34
  # Global around filters should work
35
35
  RSpec.configure do |c|
36
36
  c.around(:each) do |example|
37
- DB.transaction(:rollback=>:always){example.run}
37
+ DB.transaction(:rollback=>:always, :auto_savepoint=>true){example.run}
38
38
  end
39
39
  end
40
40
 
@@ -44,7 +44,7 @@ These run each test in its own transaction, the recommended way to test.
44
44
  class SequelTestCase < Test::Unit::TestCase
45
45
  def run(*args, &block)
46
46
  result = nil
47
- Sequel::Model.db.transaction(:rollback=>:always){result = super}
47
+ Sequel::Model.db.transaction(:rollback=>:always, :auto_savepoint=>true){result = super}
48
48
  result
49
49
  end
50
50
  end
@@ -55,7 +55,7 @@ These run each test in its own transaction, the recommended way to test.
55
55
 
56
56
  def run(*args, &block)
57
57
  result = nil
58
- Sequel::Model.db.transaction(:rollback => :always) do
58
+ Sequel::Model.db.transaction(:rollback => :always, :auto_savepoint=>true) do
59
59
  result = _original_run(*args, &block)
60
60
  end
61
61
  result
@@ -69,7 +69,7 @@ These run each test in its own transaction, the recommended way to test.
69
69
  class SequelTestCase < MiniTest::Unit::TestCase
70
70
  def run(*args, &block)
71
71
  result = nil
72
- Sequel::Model.db.transaction(:rollback=>:always){result = super}
72
+ Sequel::Model.db.transaction(:rollback=>:always, :auto_savepoint=>true){result = super}
73
73
  result
74
74
  end
75
75
  end
@@ -80,7 +80,7 @@ These run each test in its own transaction, the recommended way to test.
80
80
 
81
81
  def run(*args, &block)
82
82
  result = nil
83
- Sequel::Model.db.transaction(:rollback => :always) do
83
+ Sequel::Model.db.transaction(:rollback => :always, :auto_savepoint=>true) do
84
84
  result = _original_run(*args, &block)
85
85
  end
86
86
  result
@@ -91,6 +91,14 @@ You can use the <tt>:savepoint => true</tt> option in the inner transaction to e
91
91
  end # RELEASE SAVEPOINT
92
92
  end # COMMIT
93
93
 
94
+ You can use the <tt>:auto_savepoint => true</tt> option in the outer transaction to explicitly use a savepoint in the inner transaction (if the database supports it):
95
+
96
+ DB.transaction(:auto_savepoint => true) do # BEGIN
97
+ DB.transaction do # SAVEPOINT
98
+ DB[:foo].insert(1) # INSERT
99
+ end # RELEASE SAVEPOINT
100
+ end # COMMIT
101
+
94
102
  If a Sequel::Rollback exception is raised inside the savepoint block, it will only rollback to the savepoint:
95
103
 
96
104
  DB.transaction do # BEGIN
@@ -180,6 +180,86 @@ module Sequel
180
180
  end
181
181
  end
182
182
 
183
+ class TypeConvertor
184
+ %w'Boolean Float Double Int Long Short'.each do |meth|
185
+ class_eval("def #{meth}(r, i) v = r.get#{meth}(i); v unless r.wasNull end", __FILE__, __LINE__)
186
+ end
187
+ %w'Object Array String Time Date Timestamp BigDecimal Blob Bytes Clob'.each do |meth|
188
+ class_eval("def #{meth}(r, i) r.get#{meth}(i) end", __FILE__, __LINE__)
189
+ end
190
+ def RubyTime(r, i)
191
+ if v = r.getTime(i)
192
+ Sequel.string_to_time("#{v.to_string}.#{sprintf('%03i', v.getTime.divmod(1000).last)}")
193
+ end
194
+ end
195
+ def RubyDate(r, i)
196
+ if v = r.getDate(i)
197
+ Date.civil(v.getYear + 1900, v.getMonth + 1, v.getDate)
198
+ end
199
+ end
200
+ def RubyTimestamp(r, i)
201
+ if v = r.getTimestamp(i)
202
+ Sequel.database_to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos])
203
+ end
204
+ end
205
+ def RubyBigDecimal(r, i)
206
+ if v = r.getBigDecimal(i)
207
+ BigDecimal.new(v.to_string)
208
+ end
209
+ end
210
+ def RubyBlob(r, i)
211
+ if v = r.getBytes(i)
212
+ Sequel::SQL::Blob.new(String.from_java_bytes(v))
213
+ end
214
+ end
215
+ def RubyClob(r, i)
216
+ if v = r.getClob(i)
217
+ v.getSubString(1, v.length)
218
+ end
219
+ end
220
+
221
+ INSTANCE = new
222
+ o = INSTANCE
223
+ MAP = Hash.new(o.method(:Object))
224
+ types = Java::JavaSQL::Types
225
+
226
+ {
227
+ :ARRAY => :Array,
228
+ :BOOLEAN => :Boolean,
229
+ :CHAR => :String,
230
+ :DOUBLE => :Double,
231
+ :FLOAT => :Double,
232
+ :INTEGER => :Int,
233
+ :LONGNVARCHAR => :String,
234
+ :LONGVARCHAR => :String,
235
+ :NCHAR => :String,
236
+ :REAL => :Float,
237
+ :SMALLINT => :Short,
238
+ :TINYINT => :Short,
239
+ :VARCHAR => :String,
240
+ }.each do |type, meth|
241
+ MAP[types.const_get(type)] = o.method(meth)
242
+ end
243
+ BASIC_MAP = MAP.dup
244
+
245
+ {
246
+ :BINARY => :Blob,
247
+ :BLOB => :Blob,
248
+ :CLOB => :Clob,
249
+ :DATE => :Date,
250
+ :DECIMAL => :BigDecimal,
251
+ :LONGVARBINARY => :Blob,
252
+ :NCLOB => :Clob,
253
+ :NUMERIC => :BigDecimal,
254
+ :TIME => :Time,
255
+ :TIMESTAMP => :Timestamp,
256
+ :VARBINARY => :Blob,
257
+ }.each do |type, meth|
258
+ BASIC_MAP[types.const_get(type)] = o.method(meth)
259
+ MAP[types.const_get(type)] = o.method(:"Ruby#{meth}")
260
+ end
261
+ end
262
+
183
263
  # JDBC Databases offer a fairly uniform interface that does not change
184
264
  # much based on the sub adapter.
185
265
  class Database < Sequel::Database
@@ -196,6 +276,16 @@ module Sequel
196
276
  # fetching rows.
197
277
  attr_accessor :convert_types
198
278
 
279
+ # The fetch size to use for JDBC Statement objects created by this database.
280
+ # By default, this is nil so a fetch size is not set explicitly.
281
+ attr_accessor :fetch_size
282
+
283
+ # Map of JDBC type ids to callable objects that return appropriate ruby values.
284
+ attr_reader :type_convertor_map
285
+
286
+ # Map of JDBC type ids to callable objects that return appropriate ruby or java values.
287
+ attr_reader :basic_type_convertor_map
288
+
199
289
  # Execute the given stored procedure with the give name. If a block is
200
290
  # given, the stored procedure should return rows.
201
291
  def call_sproc(name, opts = OPTS)
@@ -277,6 +367,9 @@ module Sequel
277
367
  synchronize(opts[:server]) do |conn|
278
368
  statement(conn) do |stmt|
279
369
  if block
370
+ if size = fetch_size
371
+ stmt.setFetchSize(size)
372
+ end
280
373
  yield log_yield(sql){stmt.executeQuery(sql)}
281
374
  else
282
375
  case opts[:type]
@@ -374,6 +467,7 @@ module Sequel
374
467
  def adapter_initialize
375
468
  @connection_prepared_statements = {}
376
469
  @connection_prepared_statements_mutex = Mutex.new
470
+ @fetch_size = @opts[:fetch_size] ? typecast_value_integer(@opts[:fetch_size]) : default_fetch_size
377
471
  @convert_types = typecast_value_boolean(@opts.fetch(:convert_types, true))
378
472
  raise(Error, "No connection string specified") unless uri
379
473
 
@@ -384,6 +478,8 @@ module Sequel
384
478
  else
385
479
  @opts[:driver]
386
480
  end
481
+
482
+ setup_type_convertor_map
387
483
  end
388
484
 
389
485
  # Yield the native prepared statements hash for the given connection
@@ -439,6 +535,9 @@ module Sequel
439
535
  else
440
536
  log_yield("CLOSE #{name}"){cps[1].close} if cps
441
537
  cps = log_yield("PREPARE#{" #{name}:" if name} #{sql}"){prepare_jdbc_statement(conn, sql, opts)}
538
+ if size = fetch_size
539
+ cps.setFetchSize(size)
540
+ end
442
541
  cps_sync(conn){|cpsh| cpsh[name] = [sql, cps]} if name
443
542
  end
444
543
  i = 0
@@ -480,6 +579,12 @@ module Sequel
480
579
  def execute_statement_insert(stmt, sql)
481
580
  stmt.executeUpdate(sql)
482
581
  end
582
+
583
+ # The default fetch size to use for statements. Nil by default, so that the
584
+ # default for the JDBC driver is used.
585
+ def default_fetch_size
586
+ nil
587
+ end
483
588
 
484
589
  # Gets the connection from JNDI.
485
590
  def get_connection_from_jndi
@@ -642,6 +747,11 @@ module Sequel
642
747
  h[:table_schem] == 'INFORMATION_SCHEMA'
643
748
  end
644
749
 
750
+ def setup_type_convertor_map
751
+ @type_convertor_map = TypeConvertor::MAP.merge(Java::JavaSQL::Types::TIMESTAMP=>timestamp_convertor)
752
+ @basic_type_convertor_map = TypeConvertor::BASIC_MAP
753
+ end
754
+
645
755
  # Yield a new statement object, and ensure that it is closed before returning.
646
756
  def statement(conn)
647
757
  stmt = conn.createStatement
@@ -651,6 +761,16 @@ module Sequel
651
761
  ensure
652
762
  stmt.close if stmt
653
763
  end
764
+
765
+ # A conversion proc for timestamp columns. This is used to make sure timestamps are converted using the
766
+ # correct timezone.
767
+ def timestamp_convertor
768
+ lambda do |r, i|
769
+ if v = r.getTimestamp(i)
770
+ to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos])
771
+ end
772
+ end
773
+ end
654
774
  end
655
775
 
656
776
  class Dataset < Sequel::Dataset
@@ -729,195 +849,67 @@ module Sequel
729
849
  end
730
850
  ps
731
851
  end
732
-
733
- private
734
852
 
735
- # Cache Java class constants to speed up lookups
736
- JAVA_SQL_TIMESTAMP = Java::JavaSQL::Timestamp
737
- JAVA_SQL_TIME = Java::JavaSQL::Time
738
- JAVA_SQL_DATE = Java::JavaSQL::Date
739
- JAVA_SQL_BLOB = Java::JavaSQL::Blob
740
- JAVA_SQL_CLOB = Java::JavaSQL::Clob
741
- JAVA_BUFFERED_READER = Java::JavaIo::BufferedReader
742
- JAVA_BIG_DECIMAL = Java::JavaMath::BigDecimal
743
- JAVA_BYTE_ARRAY = Java::byte[]
744
- JAVA_UUID = Java::JavaUtil::UUID
745
- JAVA_HASH_MAP = Java::JavaUtil::HashMap
746
-
747
- # Handle type conversions for common Java types.
748
- class TYPE_TRANSLATOR
749
- LF = "\n".freeze
750
- def time(v) Sequel.string_to_time("#{v.to_string}.#{sprintf('%03i', v.getTime.divmod(1000).last)}") end
751
- def date(v) Date.civil(v.getYear + 1900, v.getMonth + 1, v.getDate) end
752
- def decimal(v) BigDecimal.new(v.to_string) end
753
- def byte_array(v) Sequel::SQL::Blob.new(String.from_java_bytes(v)) end
754
- def blob(v) Sequel::SQL::Blob.new(String.from_java_bytes(v.getBytes(1, v.length))) end
755
- def clob(v) v.getSubString(1, v.length) end
756
- def buffered_reader(v)
757
- lines = ""
758
- c = false
759
- while(line = v.read_line) do
760
- lines << LF if c
761
- lines << line
762
- c ||= true
763
- end
764
- lines
765
- end
766
- def uuid(v) v.to_string end
767
- def hash_map(v) v.to_hash end
768
- end
769
- TYPE_TRANSLATOR_INSTANCE = tt = TYPE_TRANSLATOR.new
770
-
771
- # Cache type translator methods so that duplicate Method
772
- # objects are not created.
773
- DECIMAL_METHOD = tt.method(:decimal)
774
- TIME_METHOD = tt.method(:time)
775
- DATE_METHOD = tt.method(:date)
776
- BUFFERED_READER_METHOD = tt.method(:buffered_reader)
777
- BYTE_ARRAY_METHOD = tt.method(:byte_array)
778
- BLOB_METHOD = tt.method(:blob)
779
- CLOB_METHOD = tt.method(:clob)
780
- UUID_METHOD = tt.method(:uuid)
781
- HASH_MAP_METHOD = tt.method(:hash_map)
782
-
783
- # Convert the given Java timestamp to an instance of Sequel.datetime_class.
784
- def convert_type_timestamp(v)
785
- db.to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos])
786
- end
787
-
788
- # Return a callable object that will convert any value of <tt>v</tt>'s
789
- # class to a ruby object. If no callable object can handle <tt>v</tt>'s
790
- # class, return false so that the negative lookup is cached.
791
- def convert_type_proc(v, ctn=nil)
792
- case v
793
- when JAVA_BIG_DECIMAL
794
- DECIMAL_METHOD
795
- when JAVA_SQL_TIMESTAMP
796
- method(:convert_type_timestamp)
797
- when JAVA_SQL_TIME
798
- TIME_METHOD
799
- when JAVA_SQL_DATE
800
- DATE_METHOD
801
- when JAVA_BUFFERED_READER
802
- BUFFERED_READER_METHOD
803
- when JAVA_BYTE_ARRAY
804
- BYTE_ARRAY_METHOD
805
- when JAVA_SQL_BLOB
806
- BLOB_METHOD
807
- when JAVA_SQL_CLOB
808
- CLOB_METHOD
809
- when JAVA_UUID
810
- UUID_METHOD
811
- when JAVA_HASH_MAP
812
- HASH_MAP_METHOD
813
- else
814
- false
815
- end
853
+ # Set the fetch size on JDBC ResultSets created from this dataset.
854
+ def with_fetch_size(size)
855
+ clone(:fetch_size=>size)
816
856
  end
857
+
858
+ private
817
859
 
818
- # The generic JDBC support does not use column info when deciding on conversion procs.
819
- def convert_type_proc_uses_column_info?
820
- false
860
+ # Whether we should convert Java types to ruby types for this dataset.
861
+ def convert_types?
862
+ ct = @convert_types
863
+ ct.nil? ? db.convert_types : ct
821
864
  end
822
865
 
823
- # By default, if using column info, assume the info needed is the column's type name.
824
- def convert_type_proc_column_info(meta, i)
825
- meta.column_type_name(i)
826
- end
827
-
828
866
  # Extend the dataset with the JDBC stored procedure methods.
829
867
  def prepare_extend_sproc(ds)
830
868
  ds.extend(StoredProcedureMethods)
831
869
  end
832
-
870
+
871
+ # The type conversion proc to use for the given column number i,
872
+ # given the type conversion map and the ResultSetMetaData.
873
+ def type_convertor(map, meta, type, i)
874
+ map[type]
875
+ end
876
+
877
+ # The basic type conversion proc to use for the given column number i,
878
+ # given the type conversion map and the ResultSetMetaData.
879
+ #
880
+ # This is implemented as a separate method so that subclasses can
881
+ # override the methods separately.
882
+ def basic_type_convertor(map, meta, type, i)
883
+ map[type]
884
+ end
885
+
833
886
  # Split out from fetch rows to allow processing of JDBC result sets
834
887
  # that don't come from issuing an SQL string.
835
- def process_result_set(result, &block)
836
- # get column names
888
+ def process_result_set(result)
837
889
  meta = result.getMetaData
890
+ if fetch_size = opts[:fetch_size]
891
+ result.setFetchSize(fetch_size)
892
+ end
838
893
  cols = []
839
894
  i = 0
840
- ct = @convert_types
841
- ct = db.convert_types if ct.nil?
842
-
843
- if ct
844
- use_column_info = convert_type_proc_uses_column_info?
845
- # When converting types, four values are associated with every column:
846
- # 1) column name symbol
847
- # 2) index (starting at 1, as JDBC does)
848
- # 3) database-specific value usable by convert_type_proc to determine the conversion proc to use for column
849
- # 4) nil (later updated with the actual conversion proc during the lookup process)
850
- meta.getColumnCount.times do
851
- i += 1
852
- cols << [output_identifier(meta.getColumnLabel(i)), i, (convert_type_proc_column_info(meta, i) if use_column_info), nil]
853
- end
854
- @columns = cols.map{|c| c.at(0)}
855
- process_result_set_convert(cols, result, &block)
856
- else
857
- # When not converting types, only the type name and the index are used.
858
- meta.getColumnCount.times do
859
- i += 1
860
- cols << [output_identifier(meta.getColumnLabel(i)), i]
861
- end
862
- @columns = cols.map{|c| c.at(0)}
863
- process_result_set_no_convert(cols, result, &block)
864
- end
865
- ensure
866
- result.close
867
- end
895
+ convert = convert_types?
896
+ map = convert ? db.type_convertor_map : db.basic_type_convertor_map
868
897
 
869
- # Use conversion procs to convert data retrieved
870
- # from the database. This has been optimized, the algorithm it uses
871
- # is roughly, for each column value in each row:
872
- # * check if the value is truthy (not false/nil)
873
- # * if not truthy, return object
874
- # * otherwise, see if a conversion method exists for
875
- # the column. All columns start with a nil conversion proc,
876
- # since unlike other adapters, Sequel doesn't get the type of
877
- # the column when parsing the column metadata.
878
- # * if a conversion proc is not false/nil, call it with the object
879
- # and return the result.
880
- # * if a conversion proc has already been looked up and doesn't
881
- # exist (false value), return object.
882
- # * if a conversion proc hasn't been looked up yet (nil value),
883
- # call convert_type_proc to get the conversion method. Cache
884
- # the result of as the column's conversion proc to speed up
885
- # later processing. If the conversion proc exists, call it
886
- # and return the result, otherwise, return the object.
887
- def process_result_set_convert(cols, result)
888
- while result.next
889
- row = {}
890
- cols.each do |n, i, ctn, p|
891
- v = result.getObject(i)
892
- row[n] = if v
893
- if p
894
- p.call(v)
895
- elsif p.nil?
896
- cols[i-1][3] = p = convert_type_proc(v, ctn)
897
- if p
898
- p.call(v)
899
- else
900
- v
901
- end
902
- else
903
- v
904
- end
905
- else
906
- v
907
- end
908
- end
909
- yield row
898
+ meta.getColumnCount.times do
899
+ i += 1
900
+ cols << [output_identifier(meta.getColumnLabel(i)), i, convert ? type_convertor(map, meta, meta.getColumnType(i), i) : basic_type_convertor(map, meta, meta.getColumnType(i), i)]
910
901
  end
911
- end
902
+ @columns = cols.map{|c| c.at(0)}
912
903
 
913
- # Yield rows without calling any conversion procs. This
914
- # may yield Java values and not ruby values.
915
- def process_result_set_no_convert(cols, result)
916
904
  while result.next
917
905
  row = {}
918
- cols.each{|n, i| row[n] = result.getObject(i)}
906
+ cols.each do |n, i, pr|
907
+ row[n] = pr.call(result, i)
908
+ end
919
909
  yield row
920
910
  end
911
+ ensure
912
+ result.close
921
913
  end
922
914
  end
923
915
  end