sequel 3.29.0 → 3.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/CHANGELOG +35 -3
  2. data/Rakefile +2 -1
  3. data/doc/association_basics.rdoc +11 -0
  4. data/doc/opening_databases.rdoc +2 -0
  5. data/doc/release_notes/3.30.0.txt +135 -0
  6. data/doc/testing.rdoc +17 -3
  7. data/lib/sequel/adapters/amalgalite.rb +2 -2
  8. data/lib/sequel/adapters/do/mysql.rb +5 -2
  9. data/lib/sequel/adapters/ibmdb.rb +2 -2
  10. data/lib/sequel/adapters/jdbc.rb +126 -43
  11. data/lib/sequel/adapters/jdbc/as400.rb +11 -3
  12. data/lib/sequel/adapters/jdbc/db2.rb +2 -1
  13. data/lib/sequel/adapters/jdbc/derby.rb +44 -19
  14. data/lib/sequel/adapters/jdbc/h2.rb +32 -19
  15. data/lib/sequel/adapters/jdbc/hsqldb.rb +21 -17
  16. data/lib/sequel/adapters/jdbc/jtds.rb +9 -4
  17. data/lib/sequel/adapters/jdbc/mssql.rb +3 -1
  18. data/lib/sequel/adapters/jdbc/mysql.rb +2 -1
  19. data/lib/sequel/adapters/jdbc/oracle.rb +21 -7
  20. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -2
  21. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -1
  22. data/lib/sequel/adapters/jdbc/sqlserver.rb +48 -18
  23. data/lib/sequel/adapters/mock.rb +2 -1
  24. data/lib/sequel/adapters/mysql.rb +4 -2
  25. data/lib/sequel/adapters/mysql2.rb +2 -2
  26. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  27. data/lib/sequel/adapters/openbase.rb +1 -1
  28. data/lib/sequel/adapters/oracle.rb +6 -6
  29. data/lib/sequel/adapters/postgres.rb +25 -12
  30. data/lib/sequel/adapters/shared/access.rb +14 -6
  31. data/lib/sequel/adapters/shared/db2.rb +36 -13
  32. data/lib/sequel/adapters/shared/firebird.rb +12 -5
  33. data/lib/sequel/adapters/shared/informix.rb +11 -3
  34. data/lib/sequel/adapters/shared/mssql.rb +94 -47
  35. data/lib/sequel/adapters/shared/mysql.rb +107 -49
  36. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
  37. data/lib/sequel/adapters/shared/oracle.rb +54 -27
  38. data/lib/sequel/adapters/shared/postgres.rb +65 -26
  39. data/lib/sequel/adapters/shared/progress.rb +4 -1
  40. data/lib/sequel/adapters/shared/sqlite.rb +36 -20
  41. data/lib/sequel/adapters/sqlite.rb +2 -3
  42. data/lib/sequel/adapters/swift/mysql.rb +3 -2
  43. data/lib/sequel/adapters/swift/sqlite.rb +2 -2
  44. data/lib/sequel/adapters/tinytds.rb +14 -8
  45. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +7 -4
  46. data/lib/sequel/database/misc.rb +6 -2
  47. data/lib/sequel/dataset/graph.rb +33 -7
  48. data/lib/sequel/dataset/prepared_statements.rb +19 -5
  49. data/lib/sequel/dataset/sql.rb +611 -201
  50. data/lib/sequel/model/associations.rb +12 -5
  51. data/lib/sequel/model/base.rb +20 -5
  52. data/lib/sequel/plugins/sharding.rb +9 -29
  53. data/lib/sequel/sql.rb +2 -1
  54. data/lib/sequel/timezones.rb +14 -4
  55. data/lib/sequel/version.rb +1 -1
  56. data/spec/adapters/mysql_spec.rb +10 -0
  57. data/spec/adapters/oracle_spec.rb +1 -1
  58. data/spec/core/core_sql_spec.rb +3 -1
  59. data/spec/core/database_spec.rb +42 -0
  60. data/spec/core/dataset_spec.rb +10 -3
  61. data/spec/core/mock_adapter_spec.rb +4 -0
  62. data/spec/core/object_graph_spec.rb +38 -0
  63. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  64. data/spec/extensions/association_dependencies_spec.rb +2 -12
  65. data/spec/extensions/association_pks_spec.rb +35 -39
  66. data/spec/extensions/caching_spec.rb +23 -50
  67. data/spec/extensions/class_table_inheritance_spec.rb +30 -82
  68. data/spec/extensions/composition_spec.rb +18 -13
  69. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  70. data/spec/extensions/identity_map_spec.rb +33 -103
  71. data/spec/extensions/instance_filters_spec.rb +10 -21
  72. data/spec/extensions/instance_hooks_spec.rb +6 -24
  73. data/spec/extensions/json_serializer_spec.rb +4 -5
  74. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  75. data/spec/extensions/list_spec.rb +17 -39
  76. data/spec/extensions/many_through_many_spec.rb +135 -277
  77. data/spec/extensions/migration_spec.rb +18 -15
  78. data/spec/extensions/named_timezones_spec.rb +1 -1
  79. data/spec/extensions/nested_attributes_spec.rb +97 -92
  80. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  81. data/spec/extensions/prepared_statements_associations_spec.rb +22 -37
  82. data/spec/extensions/prepared_statements_safe_spec.rb +9 -27
  83. data/spec/extensions/prepared_statements_spec.rb +11 -30
  84. data/spec/extensions/prepared_statements_with_pk_spec.rb +6 -13
  85. data/spec/extensions/pretty_table_spec.rb +1 -6
  86. data/spec/extensions/rcte_tree_spec.rb +41 -43
  87. data/spec/extensions/schema_dumper_spec.rb +3 -6
  88. data/spec/extensions/serialization_spec.rb +20 -32
  89. data/spec/extensions/sharding_spec.rb +66 -140
  90. data/spec/extensions/single_table_inheritance_spec.rb +14 -36
  91. data/spec/extensions/spec_helper.rb +10 -64
  92. data/spec/extensions/sql_expr_spec.rb +20 -60
  93. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  94. data/spec/extensions/timestamps_spec.rb +6 -6
  95. data/spec/extensions/to_dot_spec.rb +1 -2
  96. data/spec/extensions/touch_spec.rb +13 -14
  97. data/spec/extensions/tree_spec.rb +11 -26
  98. data/spec/extensions/update_primary_key_spec.rb +30 -24
  99. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  100. data/spec/extensions/validation_helpers_spec.rb +16 -35
  101. data/spec/integration/dataset_test.rb +16 -4
  102. data/spec/integration/prepared_statement_test.rb +4 -2
  103. data/spec/model/eager_loading_spec.rb +16 -0
  104. data/spec/model/model_spec.rb +15 -1
  105. data/spec/model/record_spec.rb +60 -0
  106. metadata +23 -40
data/CHANGELOG CHANGED
@@ -1,3 +1,35 @@
1
+ === 3.30.0 (2011-12-01)
2
+
3
+ * Handle usage of on_duplicate_key_update in MySQL prepared statements (jeremyevans) (#404)
4
+
5
+ * Make after_commit and after_rollback respect :server option (jeremyevans) (#401)
6
+
7
+ * Respect :connect_timeout option in the postgres adapter when using pg (glebpom, jeremyevans) (#402)
8
+
9
+ * Make Dataset#destroy for model datasets respect dataset shard when using a transaction (jeremyevans)
10
+
11
+ * Make :server option to Model#save set the shard to use (jeremyevans)
12
+
13
+ * Move Model#set_server from the sharding plugin to the base plugin (jeremyevans)
14
+
15
+ * Add :graph_alias_base association option for setting base name to use for table aliases when eager graphing (jeremyevans)
16
+
17
+ * Make ILIKE work correctly on Microsoft SQL Server if database/column collation is case sensitive (jfirebaugh) (#398)
18
+
19
+ * When starting a new dataset graph, assume existing selection is the columns to select from the current table (jeremyevans)
20
+
21
+ * Allow specifying nanoseconds and offsets when converting a hash or array to a timestamp (jeremyevans, jfirebaugh) (#395)
22
+
23
+ * Improve performance when converting Java types to ruby types in the jdbc adapter (jeremyevans, jfirebaugh) (#395)
24
+
25
+ * Fix tinytds adapter if DB.identifier_output_method = nil (jeremyevans)
26
+
27
+ * Explicitly order by the row number column when emulating offsets (jfirebaugh) (#393)
28
+
29
+ * Fix Dataset#graph and #eager_graph modifying the receiver if the receiver is already graphed (jeremyevans) (#392)
30
+
31
+ * Change dataset literalization to an append-only-all-the-way-down design (jeremyevans)
32
+
1
33
  === 3.29.0 (2011-11-01)
2
34
 
3
35
  * Allow Model.dataset_module to take a Module instance (jeremyevans)
@@ -64,7 +96,7 @@
64
96
 
65
97
  * Add after_commit, after_rollback, after_destroy_commit, and after_destroy_rollback hooks to Model objects (jeremyevans)
66
98
 
67
- * Add after_commit and after_rollback hooks to Database objects (jeremyevans)
99
+ * Add after_commit and after_rollback hooks to Database objects (jeremyevans) (#383)
68
100
 
69
101
  * Support savepoints inside prepared transactions on MySQL (jeremyevans)
70
102
 
@@ -184,9 +216,9 @@
184
216
 
185
217
  * Fix the db2 adapter so it actually works (jeremyevans)
186
218
 
187
- * Add ibmdb adapter for accessing DB2 (roylez, jeremyevans)
219
+ * Add ibmdb adapter for accessing DB2 (roylez, jeremyevans) (#376)
188
220
 
189
- * Add much better support for DB2 databases (roylez, jeremyevans)
221
+ * Add much better support for DB2 databases (roylez, jeremyevans) (#376)
190
222
 
191
223
  * Handle SQL::AliasedExpressions and SQL::JoinClauses in Dataset#select_all (jeremyevans)
192
224
 
data/Rakefile CHANGED
@@ -12,7 +12,8 @@ CLEAN.include ["**/.*.sw?", "sequel-*.gem", ".config", "rdoc", "coverage", "www/
12
12
 
13
13
  desc "Packages sequel"
14
14
  task :package=>[:clean] do |p|
15
- sh %{gem build sequel.gemspec}
15
+ load './sequel.gemspec'
16
+ Gem::Builder.new(SEQUEL_GEMSPEC).build
16
17
  end
17
18
 
18
19
  desc "Install sequel gem"
@@ -1329,6 +1329,17 @@ a JOIN USING or NATURAL JOIN for the graph:
1329
1329
  Artist.one_to_many :albums, :key=>:artist_name,
1330
1330
  :graph_only_conditions=>nil, :graph_join_type=>:natural
1331
1331
 
1332
+ ==== :graph_alias_base
1333
+
1334
+ The base name to use for the table alias when eager graphing. Defaults to the name
1335
+ of the association. If the alias name has already been used in the query, Sequel will create
1336
+ a unique alias by appending a numeric suffix (e.g. alias_0, alias_1, ...) until the alias is
1337
+ unique.
1338
+
1339
+ This is mostly useful if you have associations with the same name in many models, and you want
1340
+ to be able to easily tell which table alias corresponds to which association when eagerly
1341
+ graphing multiple associations with the same name.
1342
+
1332
1343
  ==== :eager_grapher
1333
1344
 
1334
1345
  Sets up a custom grapher to use when eager loading the objects via eager_graph.
@@ -342,6 +342,8 @@ The following additional options are supported:
342
342
 
343
343
  :charset :: Same as :encoding, :encoding takes precedence
344
344
  :encoding :: Set the client_encoding to the given string
345
+ :connect_timeout :: Set the number of seconds to wait for a connection (default 20, only respected
346
+ if using the pg library).
345
347
 
346
348
  === sqlite
347
349
 
@@ -0,0 +1,135 @@
1
+ = Dataset Literalization Refactoring
2
+
3
+ * As warned about in the 3.29.0 release notes, dataset literalization
4
+ has been completely refactored. It now uses an append-only design
5
+ which is faster in all cases, about twice as fast for large objects
6
+ and deeply nested structures, and over two orders of magnitude
7
+ faster in some pathological cases.
8
+
9
+ This change should not affect applications, but may affect custom
10
+ extensions or adapters that dealt with literalization of objects.
11
+ Most literalization methods now have a method with an _append
12
+ suffix that does the actual literalization, which takes the sql
13
+ string to append to as the first argument. If you were overriding
14
+ a literalization method, you now probably need to override the
15
+ _append version instead. If you have this literalization method:
16
+
17
+ def foo_sql(bar)
18
+ "BAR #{literal(bar.baz)}"
19
+ end
20
+
21
+ You need to change the code to:
22
+
23
+ def foo_sql_append(sql, bar)
24
+ sql << "BAR "
25
+ literal_append(sql, bar.baz)
26
+ end
27
+
28
+ def foo_sql(bar)
29
+ sql = ""
30
+ foo_sql_append(sql, bar)
31
+ sql
32
+ end
33
+
34
+ If you have questions about modifying your custom adapter or
35
+ extension, please ask on the Google Group or the IRC channel.
36
+
37
+ = New Features
38
+
39
+ * Model#set_server has been added to the base support (it was
40
+ previously only in the sharding plugin), which allows you to
41
+ set the shard on which to save/delete the model instance:
42
+
43
+ foo1.set_server(:server_a).save
44
+ foo2.set_server(:server_a).destroy
45
+
46
+ * Model#save now accepts a :server option that uses set_server
47
+ to set the shard to use. Unlike most other #save options, this
48
+ option persists past the end of the save. Previously, the
49
+ :server option only affected the transaction code, it now
50
+ affects the INSERT/UPDATE statement as well.
51
+
52
+ * When initiating a new dataset graph, any existing selected
53
+ columns is assumed to be the columns to select for the graph from
54
+ the current/master table. Before, there was not a way to specify
55
+ the columns to select from the current/master table.
56
+
57
+ * A :graph_alias_base association option has been added, which is
58
+ used to set the base alias name to use when eager graphing. This
59
+ is mostly useful when cascading eager graphs to dependent
60
+ associations, where multiple associations with the same name in
61
+ different models are being graphed simultaneously.
62
+
63
+ * You can now specify nanoseconds and a timezone offset
64
+ when converting a hash or array to a timestamp. The nanoseconds
65
+ and offset are the 7th and 8th entries in the array, and the :nanos
66
+ and :offset entry in the hash.
67
+
68
+ * The postgres adapter now respects a :connect_timeout option if you
69
+ are using the pg driver.
70
+
71
+ = Other Improvements
72
+
73
+ * Type conversion of Java to Ruby types in the JDBC adapter has been
74
+ made much faster, as conversion method lookup is now
75
+ O(number of columns) instead of
76
+ O(number of columns*number of rows).
77
+
78
+ * Sequel::SQL::Blob literalization is now much faster on adapters that
79
+ use hex encoding, by switching to String#unpack('H*').
80
+
81
+ * Database#after_commit and after_rollback now respect the :server
82
+ option to set the server/shard to use.
83
+
84
+ * Symbol splitting (e.g. for table__column) is now slightly faster.
85
+
86
+ * All adapters now pass the dataset :limit/:offset value through
87
+ Dataset#literal instead of using it verbatim. Note that
88
+ Dataset#limit already called to_i on input strings, so this isn't
89
+ a security issue. However, the previous code broke if you
90
+ provided a Sequel-specific object (e.g. Sequel::SQL::Function) as
91
+ the :limit/:offset value.
92
+
93
+ * Calling graph and eager_graph on an already graphed dataset no
94
+ longer modifies the receiver.
95
+
96
+ * Model#set_server now correctly handles the case where @this is
97
+ already loaded.
98
+
99
+ * Dataset#destroy for model datasets now uses the dataset's shard
100
+ for transactions.
101
+
102
+ * When emulating offset support using ROW_NUMBER (on Microsoft SQL
103
+ Server, DB2, and Oracle), explicitly order by the ROW_NUMBER
104
+ result, as otherwise the results are not guaranteed to be ordered.
105
+
106
+ * Explicitly force a case insensitive collation when emulating ILIKE
107
+ on Microsoft SQL Server. Previously, ILIKE could be case sensitive
108
+ on Microsoft SQL Server if case sensitive collation was the
109
+ database default.
110
+
111
+ * Using on_duplicate_key_update with prepared statements on MySQL now
112
+ works correctly.
113
+
114
+ * The tinytds adapter now works correctly if the
115
+ identifier_output_method is nil.
116
+
117
+ * The plugin/extension specs were cleaned up using the mock adapter.
118
+
119
+ = Backwards Compatibility
120
+
121
+ * In addition to the previously mentioned dataset literalization
122
+ changes, any custom adapters that overrode *_clause_methods
123
+ methods need to be modified to add a method that adds the
124
+ SELECT/UPDATE/INSERT/DELETE. Previously, this was done by default,
125
+ but due to common table expressions and the dataset literalization
126
+ changes, a separate method is now needed.
127
+
128
+ * Dataset#on_duplicate_key_update_sql has been removed from the shared
129
+ mysql adapter.
130
+
131
+ * The :columns dataset option used when inserting is no longer
132
+ literalized in advance.
133
+
134
+ * Dataset#as_sql no longer takes an expression, it just takes the
135
+ alias, and only adds the alias part.
@@ -20,9 +20,14 @@ Make sure you are using Sequel 3.29.0 or above when using these examples, as old
20
20
 
21
21
  === RSpec 2
22
22
 
23
- class Spec::Example::ExampleGroup
24
- around do |example|
25
- Sequel::Model.db.transaction(:rollback=>:always){example.call}
23
+ class RSpec::Core::ExampleGroup
24
+ # Setting an around filter globally doesn't appear to work in 2.7 (and maybe other versions),
25
+ # so set one up for each subclass.
26
+ def self.inherited(subclass)
27
+ super
28
+ subclass.around do |example|
29
+ Sequel::Model.db.transaction(:rollback=>:always){example.call}
30
+ end
26
31
  end
27
32
  end
28
33
 
@@ -35,6 +40,15 @@ Make sure you are using Sequel 3.29.0 or above when using these examples, as old
35
40
  end
36
41
  end
37
42
 
43
+ === MiniTest::Unit
44
+
45
+ # Must use this class as the base class for your tests
46
+ class SequelTestCase < MiniTest::Unit::TestCase
47
+ def run(*args, &block)
48
+ Sequel::Model.db.transaction(:rollback=>:always){super}
49
+ end
50
+ end
51
+
38
52
  == Transactional testing with multiple databases
39
53
 
40
54
  You can use the Sequel.transaction method to run a transaction on multiple databases, rolling all of them back. Instead of:
@@ -176,8 +176,8 @@ module Sequel
176
176
  private
177
177
 
178
178
  # Quote the string using the adapter instance method.
179
- def literal_string(v)
180
- db.synchronize{|c| c.quote(v)}
179
+ def literal_string_append(sql, v)
180
+ db.synchronize{|c| sql << c.quote(v)}
181
181
  end
182
182
  end
183
183
  end
@@ -26,6 +26,9 @@ module Sequel
26
26
  # Dataset class for MySQL datasets accessed via DataObjects.
27
27
  class Dataset < DataObjects::Dataset
28
28
  include Sequel::MySQL::DatasetMethods
29
+ APOS = Dataset::APOS
30
+ APOS_RE = Dataset::APOS_RE
31
+ DOUBLE_APOS = Dataset::DOUBLE_APOS
29
32
 
30
33
  # Use execute_insert to execute the replace_sql.
31
34
  def replace(*args)
@@ -35,8 +38,8 @@ module Sequel
35
38
  private
36
39
 
37
40
  # do_mysql sets NO_BACKSLASH_ESCAPES, so use standard SQL string escaping
38
- def literal_string(s)
39
- "'#{s.gsub("'", "''")}'"
41
+ def literal_string_append(sql, s)
42
+ sql << APOS << s.gsub(APOS_RE, DOUBLE_APOS) << APOS
40
43
  end
41
44
  end
42
45
  end
@@ -354,8 +354,8 @@ module Sequel
354
354
  module CallableStatementMethods
355
355
  # Extend given dataset with this module so subselects inside subselects in
356
356
  # prepared statements work.
357
- def subselect_sql(ds)
358
- ps = ds.to_prepared_statement(:select)
357
+ def subselect_sql_append(sql, ds)
358
+ ps = ds.to_prepared_statement(:select).clone(:append_sql=>sql)
359
359
  ps.extend(CallableStatementMethods)
360
360
  ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
361
361
  ps.prepared_args = prepared_args
@@ -588,12 +588,6 @@ module Sequel
588
588
  # double performance when fetching rows.
589
589
  attr_accessor :convert_types
590
590
 
591
- # Use the convert_types default setting from the database.
592
- def initialize(db, opts={})
593
- @convert_types = db.convert_types
594
- super
595
- end
596
-
597
591
  # Correctly return rows from the database and return them as hashes.
598
592
  def fetch_rows(sql, &block)
599
593
  execute(sql){|result| process_result_set(result, &block)}
@@ -613,30 +607,77 @@ module Sequel
613
607
  end
614
608
 
615
609
  private
616
-
617
- # Convert the type. Used for converting Java types to ruby types.
618
- def convert_type(v)
610
+
611
+ # Cache Java class constants to speed up lookups
612
+ JAVA_SQL_TIMESTAMP = Java::JavaSQL::Timestamp
613
+ JAVA_SQL_TIME = Java::JavaSQL::Time
614
+ JAVA_SQL_DATE = Java::JavaSQL::Date
615
+ JAVA_SQL_BLOB = Java::JavaSQL::Blob
616
+ JAVA_SQL_CLOB = Java::JavaSQL::Clob
617
+ JAVA_BUFFERED_READER = Java::JavaIo::BufferedReader
618
+ JAVA_BIG_DECIMAL = Java::JavaMath::BigDecimal
619
+ JAVA_BYTE_ARRAY = Java::byte[]
620
+
621
+ # Handle type conversions for common Java types.
622
+ class TYPE_TRANSLATOR
623
+ LF = "\n".freeze
624
+ def time(v) Sequel.string_to_time(v.to_string) end
625
+ def date(v) Date.civil(v.getYear + 1900, v.getMonth + 1, v.getDate) end
626
+ def decimal(v) BigDecimal.new(v.to_string) end
627
+ def byte_array(v) Sequel::SQL::Blob.new(String.from_java_bytes(v)) end
628
+ def blob(v) Sequel::SQL::Blob.new(String.from_java_bytes(v.getBytes(1, v.length))) end
629
+ def clob(v) Sequel::SQL::Blob.new(v.getSubString(1, v.length)) end
630
+ def buffered_reader(v)
631
+ lines = ""
632
+ c = false
633
+ while(line = v.read_line) do
634
+ lines << LF if c
635
+ lines << line
636
+ c ||= true
637
+ end
638
+ lines
639
+ end
640
+ end
641
+ TYPE_TRANSLATOR_INSTANCE = tt = TYPE_TRANSLATOR.new
642
+
643
+ # Cache type translator methods so that duplicate Method
644
+ # objects are not created.
645
+ DECIMAL_METHOD = tt.method(:decimal)
646
+ TIME_METHOD = tt.method(:time)
647
+ DATE_METHOD = tt.method(:date)
648
+ BUFFERED_READER_METHOD = tt.method(:buffered_reader)
649
+ BYTE_ARRAY_METHOD = tt.method(:byte_array)
650
+ BLOB_METHOD = tt.method(:blob)
651
+ CLOB_METHOD = tt.method(:clob)
652
+
653
+ # Convert the given Java timestamp to an instance of Sequel.datetime_class.
654
+ def convert_type_timestamp(v)
655
+ db.to_application_timestamp([v.getYear + 1900, v.getMonth + 1, v.getDate, v.getHours, v.getMinutes, v.getSeconds, v.getNanos])
656
+ end
657
+
658
+ # Return a callable object that will convert any value of <tt>v</tt>'s
659
+ # class to a ruby object. If no callable object can handle <tt>v</tt>'s
660
+ # class, return false so that the negative lookup is cached.
661
+ def convert_type_proc(v)
619
662
  case v
620
- when Java::JavaSQL::Timestamp
621
- db.to_application_timestamp(v.to_string)
622
- when Java::JavaSQL::Time
623
- Sequel.string_to_time(v.to_string)
624
- when Java::JavaSQL::Date
625
- Sequel.string_to_date(v.to_string)
626
- when Java::JavaIo::BufferedReader
627
- lines = []
628
- while(line = v.read_line) do lines << line end
629
- lines.join("\n")
630
- when Java::JavaMath::BigDecimal
631
- BigDecimal.new(v.to_string)
632
- when Java::byte[]
633
- Sequel::SQL::Blob.new(String.from_java_bytes(v))
634
- when Java::JavaSQL::Blob
635
- convert_type(v.getBytes(1, v.length))
636
- when Java::JavaSQL::Clob
637
- Sequel::SQL::Blob.new(v.getSubString(1, v.length))
663
+ when JAVA_BIG_DECIMAL
664
+ DECIMAL_METHOD
665
+ when JAVA_SQL_TIMESTAMP
666
+ method(:convert_type_timestamp)
667
+ when JAVA_SQL_TIME
668
+ TIME_METHOD
669
+ when JAVA_SQL_DATE
670
+ DATE_METHOD
671
+ when JAVA_BUFFERED_READER
672
+ BUFFERED_READER_METHOD
673
+ when JAVA_BYTE_ARRAY
674
+ BYTE_ARRAY_METHOD
675
+ when JAVA_SQL_BLOB
676
+ BLOB_METHOD
677
+ when JAVA_SQL_CLOB
678
+ CLOB_METHOD
638
679
  else
639
- v
680
+ false
640
681
  end
641
682
  end
642
683
 
@@ -647,7 +688,7 @@ module Sequel
647
688
 
648
689
  # Split out from fetch rows to allow processing of JDBC result sets
649
690
  # that don't come from issuing an SQL string.
650
- def process_result_set(result)
691
+ def process_result_set(result, &block)
651
692
  # get column names
652
693
  meta = result.getMetaData
653
694
  cols = []
@@ -659,28 +700,70 @@ module Sequel
659
700
  columns.delete(rn)
660
701
  end
661
702
  @columns = columns
662
- blk = result_set_object_getter
663
- # get rows
703
+ ct = @convert_types
704
+ if (ct.nil? ? db.convert_types : ct)
705
+ cols.each{|c| c << nil}
706
+ process_result_set_convert(cols, result, rn, &block)
707
+ else
708
+ process_result_set_no_convert(cols, result, rn, &block)
709
+ end
710
+ ensure
711
+ result.close
712
+ end
713
+
714
+ # Use conversion procs to convert data retrieved
715
+ # from the database. This has been optimized, the algorithm it uses
716
+ # is roughly, for each column value in each row:
717
+ # * check if the value is truthy (not false/nil)
718
+ # * if not truthy, return object
719
+ # * otherwise, see if a conversion method exists for
720
+ # the column. All columns start with a nil conversion proc,
721
+ # since unlike other adapters, Sequel doesn't get the type of
722
+ # the column when parsing the column metadata.
723
+ # * if a conversion proc is not false/nil, call it with the object
724
+ # and return the result.
725
+ # * if a conversion proc has already been looked up and doesn't
726
+ # exist (false value), return object.
727
+ # * if a conversion proc hasn't been looked up yet (nil value),
728
+ # call convert_type_proc to get the conversion method. Cache
729
+ # the result of as the column's conversion proc to speed up
730
+ # later processing. If the conversion proc exists, call it
731
+ # and return the result, otherwise, return the object.
732
+ def process_result_set_convert(cols, result, rn)
664
733
  while result.next
665
734
  row = {}
666
- cols.each do |n, i|
667
- row[n] = blk.call(result, n, i)
735
+ cols.each do |n, i, p|
736
+ v = result.getObject(i)
737
+ row[n] = if v
738
+ if p
739
+ p.call(v)
740
+ elsif p.nil?
741
+ cols[i-1][2] = p = convert_type_proc(v)
742
+ if p
743
+ p.call(v)
744
+ else
745
+ v
746
+ end
747
+ else
748
+ v
749
+ end
750
+ else
751
+ v
752
+ end
668
753
  end
669
754
  row.delete(rn) if rn
670
755
  yield row
671
756
  end
672
- ensure
673
- result.close
674
757
  end
675
758
 
676
- # Proc that takes the ResultSet and an integer field number and returns
677
- # the object for that field. Respects the @convert_types setting to
678
- # convert Java types to ruby types.
679
- def result_set_object_getter
680
- if @convert_types
681
- lambda {|result, n, i| convert_type(result.getObject(i))}
682
- else
683
- lambda {|result, n, i| result.getObject(i)}
759
+ # Yield rows without calling any conversion procs. This
760
+ # may yield Java values and not ruby values.
761
+ def process_result_set_no_convert(cols, result, rn)
762
+ while result.next
763
+ row = {}
764
+ cols.each{|n, i| row[n] = result.getObject(i)}
765
+ row.delete(rn) if rn
766
+ yield row
684
767
  end
685
768
  end
686
769
  end