sequel 4.9.0 → 4.10.0

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.
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