sequel 4.9.0 → 4.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +79 -1
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/Rakefile +2 -12
- data/bin/sequel +1 -0
- data/doc/advanced_associations.rdoc +82 -25
- data/doc/association_basics.rdoc +21 -22
- data/doc/core_extensions.rdoc +1 -1
- data/doc/opening_databases.rdoc +7 -0
- data/doc/release_notes/4.10.0.txt +226 -0
- data/doc/security.rdoc +1 -0
- data/doc/testing.rdoc +7 -7
- data/doc/transactions.rdoc +8 -0
- data/lib/sequel/adapters/jdbc.rb +160 -168
- data/lib/sequel/adapters/jdbc/db2.rb +17 -18
- data/lib/sequel/adapters/jdbc/derby.rb +5 -28
- data/lib/sequel/adapters/jdbc/h2.rb +11 -22
- data/lib/sequel/adapters/jdbc/hsqldb.rb +31 -18
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -15
- data/lib/sequel/adapters/jdbc/oracle.rb +36 -35
- data/lib/sequel/adapters/jdbc/postgresql.rb +72 -90
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +18 -16
- data/lib/sequel/adapters/jdbc/sqlite.rb +7 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +10 -30
- data/lib/sequel/adapters/jdbc/transactions.rb +5 -6
- data/lib/sequel/adapters/openbase.rb +1 -7
- data/lib/sequel/adapters/postgres.rb +1 -1
- data/lib/sequel/adapters/shared/access.rb +3 -6
- data/lib/sequel/adapters/shared/cubrid.rb +24 -9
- data/lib/sequel/adapters/shared/db2.rb +13 -5
- data/lib/sequel/adapters/shared/firebird.rb +16 -16
- data/lib/sequel/adapters/shared/informix.rb +2 -5
- data/lib/sequel/adapters/shared/mssql.rb +72 -63
- data/lib/sequel/adapters/shared/mysql.rb +72 -40
- data/lib/sequel/adapters/shared/oracle.rb +27 -15
- data/lib/sequel/adapters/shared/postgres.rb +24 -44
- data/lib/sequel/adapters/shared/progress.rb +1 -5
- data/lib/sequel/adapters/shared/sqlanywhere.rb +26 -18
- data/lib/sequel/adapters/shared/sqlite.rb +21 -6
- data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +8 -1
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -2
- data/lib/sequel/adapters/utils/split_alter_table.rb +8 -0
- data/lib/sequel/core.rb +14 -9
- data/lib/sequel/database/dataset_defaults.rb +1 -0
- data/lib/sequel/database/misc.rb +12 -0
- data/lib/sequel/database/query.rb +4 -1
- data/lib/sequel/database/schema_methods.rb +3 -2
- data/lib/sequel/database/transactions.rb +47 -17
- data/lib/sequel/dataset/features.rb +12 -2
- data/lib/sequel/dataset/mutation.rb +2 -0
- data/lib/sequel/dataset/placeholder_literalizer.rb +12 -4
- data/lib/sequel/dataset/prepared_statements.rb +6 -0
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/dataset/sql.rb +132 -70
- data/lib/sequel/extensions/columns_introspection.rb +1 -1
- data/lib/sequel/extensions/null_dataset.rb +8 -4
- data/lib/sequel/extensions/pg_array.rb +4 -4
- data/lib/sequel/extensions/pg_row.rb +1 -0
- data/lib/sequel/model/associations.rb +468 -188
- data/lib/sequel/model/base.rb +88 -13
- data/lib/sequel/plugins/association_pks.rb +23 -64
- data/lib/sequel/plugins/auto_validations.rb +3 -2
- data/lib/sequel/plugins/dataset_associations.rb +1 -3
- data/lib/sequel/plugins/many_through_many.rb +18 -65
- data/lib/sequel/plugins/pg_array_associations.rb +97 -86
- data/lib/sequel/plugins/prepared_statements.rb +2 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +36 -27
- data/lib/sequel/plugins/rcte_tree.rb +12 -16
- data/lib/sequel/plugins/sharding.rb +21 -3
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -1
- data/lib/sequel/plugins/subclasses.rb +1 -9
- data/lib/sequel/plugins/tactical_eager_loading.rb +9 -0
- data/lib/sequel/plugins/tree.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +57 -15
- data/spec/adapters/mysql_spec.rb +11 -0
- data/spec/bin_spec.rb +2 -2
- data/spec/core/database_spec.rb +38 -4
- data/spec/core/dataset_spec.rb +45 -7
- data/spec/core/placeholder_literalizer_spec.rb +17 -0
- data/spec/core/schema_spec.rb +6 -1
- data/spec/extensions/active_model_spec.rb +18 -9
- data/spec/extensions/association_pks_spec.rb +20 -18
- data/spec/extensions/association_proxies_spec.rb +9 -9
- data/spec/extensions/auto_validations_spec.rb +6 -0
- data/spec/extensions/columns_introspection_spec.rb +1 -0
- data/spec/extensions/constraint_validations_spec.rb +3 -1
- data/spec/extensions/many_through_many_spec.rb +191 -111
- data/spec/extensions/pg_array_associations_spec.rb +133 -103
- data/spec/extensions/prepared_statements_associations_spec.rb +23 -4
- data/spec/extensions/rcte_tree_spec.rb +35 -27
- data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -1
- data/spec/extensions/sharding_spec.rb +2 -2
- data/spec/extensions/tactical_eager_loading_spec.rb +4 -0
- data/spec/extensions/to_dot_spec.rb +1 -0
- data/spec/extensions/touch_spec.rb +2 -2
- data/spec/integration/associations_test.rb +130 -37
- data/spec/integration/dataset_test.rb +17 -0
- data/spec/integration/model_test.rb +17 -0
- data/spec/integration/schema_test.rb +14 -0
- data/spec/integration/transaction_test.rb +25 -1
- data/spec/model/association_reflection_spec.rb +63 -24
- data/spec/model/associations_spec.rb +104 -57
- data/spec/model/base_spec.rb +14 -1
- data/spec/model/class_dataset_methods_spec.rb +1 -0
- data/spec/model/eager_loading_spec.rb +221 -74
- data/spec/model/model_spec.rb +119 -1
- 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
|
data/doc/transactions.rdoc
CHANGED
@@ -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
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -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
|
-
#
|
736
|
-
|
737
|
-
|
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
|
-
#
|
819
|
-
def
|
820
|
-
|
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
|
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
|
-
|
841
|
-
|
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
|
-
|
870
|
-
|
871
|
-
|
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
|
-
|
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
|
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
|