sequel 3.29.0 → 3.30.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 (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