sequel 5.40.0 → 5.45.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +52 -0
  3. data/MIT-LICENSE +1 -1
  4. data/doc/release_notes/5.41.0.txt +25 -0
  5. data/doc/release_notes/5.42.0.txt +136 -0
  6. data/doc/release_notes/5.43.0.txt +98 -0
  7. data/doc/release_notes/5.44.0.txt +32 -0
  8. data/doc/release_notes/5.45.0.txt +34 -0
  9. data/doc/sql.rdoc +1 -1
  10. data/doc/testing.rdoc +3 -0
  11. data/doc/virtual_rows.rdoc +1 -1
  12. data/lib/sequel/adapters/ado.rb +16 -16
  13. data/lib/sequel/adapters/odbc.rb +5 -1
  14. data/lib/sequel/adapters/shared/postgres.rb +4 -14
  15. data/lib/sequel/adapters/shared/sqlite.rb +8 -4
  16. data/lib/sequel/core.rb +11 -0
  17. data/lib/sequel/database/misc.rb +1 -2
  18. data/lib/sequel/database/schema_generator.rb +35 -47
  19. data/lib/sequel/database/schema_methods.rb +4 -0
  20. data/lib/sequel/dataset/query.rb +1 -3
  21. data/lib/sequel/dataset/sql.rb +7 -0
  22. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  23. data/lib/sequel/extensions/blank.rb +2 -0
  24. data/lib/sequel/extensions/date_arithmetic.rb +32 -23
  25. data/lib/sequel/extensions/inflector.rb +2 -0
  26. data/lib/sequel/extensions/named_timezones.rb +5 -1
  27. data/lib/sequel/extensions/pg_enum.rb +1 -1
  28. data/lib/sequel/extensions/pg_interval.rb +12 -2
  29. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  30. data/lib/sequel/model/associations.rb +70 -14
  31. data/lib/sequel/model/base.rb +2 -2
  32. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  33. data/lib/sequel/plugins/auto_validations.rb +15 -1
  34. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  35. data/lib/sequel/plugins/column_encryption.rb +728 -0
  36. data/lib/sequel/plugins/composition.rb +2 -1
  37. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  38. data/lib/sequel/plugins/json_serializer.rb +37 -22
  39. data/lib/sequel/plugins/nested_attributes.rb +5 -2
  40. data/lib/sequel/plugins/pg_array_associations.rb +6 -4
  41. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  42. data/lib/sequel/plugins/serialization.rb +8 -3
  43. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
  44. data/lib/sequel/plugins/validation_helpers.rb +6 -2
  45. data/lib/sequel/version.rb +1 -1
  46. metadata +18 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3391bf9fe9e592892ad8ba6b39f7016fe94524908243e7c85fdf5b1cbf9a62a
4
- data.tar.gz: 3b3c946fefa8c16b8f657c191debf49f8db84cb9cadb5a91ecdb01739c2ec1e9
3
+ metadata.gz: b37b49ac4a53cefbee674e57e1b64f3733ee7c330d548c59e370efe6b557cc5a
4
+ data.tar.gz: 0f7de640673c6dff3affc0c494609d96502f62d36d7c6b639c4e3dba2d90a89f
5
5
  SHA512:
6
- metadata.gz: 1e7812b8851cdcc9e374bcd8439cc4e3dbbedfd550c72b54aa9d6aa16e3bad1e5f59eed824bdef026d784b67450d2f861be8776ddb762be58406cca75f7a0ea9
7
- data.tar.gz: a60bcc72355a1916a0639c5a99ec806f157d60b515112ec24c1757f9a43efc587593a0b11a1e5312d3f40431b313f8f6fc241eee7e7a37c5362565db7c4c8d3c
6
+ metadata.gz: 342e7413e9a93cea694f1d1062803d4729b28f3d8adfc2f537ecd24dc988c448754c13046709d0a6505a78c0b21fb3383a1e5d55f73ee5e8364dbd0efb84fa7d
7
+ data.tar.gz: 9ea9cd35c75b670053804399f27475e38e28ea7b21062cf736e9bbea641c9d0862ef77edab6007d224a3a669a7469cab0c94658c4477e4d97a93c1c0355f031f
data/CHANGELOG CHANGED
@@ -1,3 +1,55 @@
1
+ === 5.45.0 (2021-06-01)
2
+
3
+ * Fix handling of NULL values in boolean columns in the ODBC adapter (jeremyevans) (#1765)
4
+
5
+ * Add auto_validations_constraint_validations_presence_message plugin for auto_validations/constraint_validations presence message integration (jeremyevans)
6
+
7
+ * Support Dataset#with :materialized option on SQLite 3.35+ for [NOT] MATERIALIZED (jeremyevans)
8
+
9
+ * Use ALTER TABLE DROP COLUMN for dropping columns on SQLite 3.35+ (jeremyevans)
10
+
11
+ === 5.44.0 (2021-05-01)
12
+
13
+ * Add concurrent_eager_loading plugin, for eager loading multiple associations concurrently using separate threads (jeremyevans)
14
+
15
+ * Support :weeks as a interval unit in the date_arithmetic extension (jeremyevans) (#1759)
16
+
17
+ * Raise an exception if an interval hash with an unsupported key is passed in the date_arithmetic extension (jeremyevans) (#1759)
18
+
19
+ * Support dropping non-composite unique constraints on SQLite (jeremyevans) (#1755)
20
+
21
+ === 5.43.0 (2021-04-01)
22
+
23
+ * Add column_encryption plugin, for encrypting column values (jeremyevans)
24
+
25
+ === 5.42.0 (2021-03-01)
26
+
27
+ * Make the ado timestamp conversion proc a normal conversion proc that can be overridden similar to other conversion procs (jeremyevans)
28
+
29
+ * Add :reject_nil option to the nested_attributes method, to ignore calls where nil is passed as the associated object data (jeremyevans)
30
+
31
+ * Add async_thread_pool plugin for easier async usage with model classes and support for async destroy, with_pk, and with_pk! methods (jeremyevans)
32
+
33
+ * Add async_thread_pool Database extension for executing queries asynchronously using a thread pool (jeremyevans)
34
+
35
+ * Fix possible thread safety issue in Database#extension that could allow Module#extended to be called twice with the same Database instance (jeremyevans)
36
+
37
+ * Support cases where validations make modifications beyond setting errors in Model#freeze (jeremyevans)
38
+
39
+ * Add Model#to_json_data to the json_serializer plugin, returning a JSON data structure (jeremyevans)
40
+
41
+ === 5.41.0 (2021-02-01)
42
+
43
+ * Have explicit :text option for a String column take priority over :size option on PostgreSQL (jeremyevans) (#1750)
44
+
45
+ * Support a :skip_invalid option in auto_validations plugin for not adding errors to a column that already has an error (jeremyevans)
46
+
47
+ * Support a :skip_invalid option in validation_helpers for not adding an error to a column that already has an error (jeremyevans)
48
+
49
+ * Support :adder, :remover, and :clearer association options that use keyword arguments in Ruby 2.7+ (jeremyevans)
50
+
51
+ * Make pg_interval use the same number of seconds per year and per month as ActiveSupport::Duration when using ActiveSupport 5.1+ (jeremyevans)
52
+
1
53
  === 5.40.0 (2021-01-01)
2
54
 
3
55
  * Support UPDATE FROM syntax in SQLite 3.33.0+ (jeremyevans)
data/MIT-LICENSE CHANGED
@@ -1,5 +1,5 @@
1
1
  Copyright (c) 2007-2008 Sharon Rosner
2
- Copyright (c) 2008-2020 Jeremy Evans
2
+ Copyright (c) 2008-2021 Jeremy Evans
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to
@@ -0,0 +1,25 @@
1
+ = New Features
2
+
3
+ * The validation methods added by the validation_helpers plugin now
4
+ support the :skip_invalid option, which will not add a validation
5
+ error on a column if it already has a validation error. This can
6
+ be useful if you want to avoid having duplicate errors.
7
+
8
+ * The auto_validations plugin now supports a :skip_invalid plugin
9
+ option, which will pass the :skip_invalid option when calling
10
+ validation methods.
11
+
12
+ = Other Improvements
13
+
14
+ * The :adder, :remover, and :clearer association options now
15
+ support keyword arguments in Ruby 2.7+.
16
+
17
+ * In the pg_interval extension, Sequel now uses the same number of
18
+ seconds per month and seconds per year as active_support. It
19
+ originally used the same number, but active_support changed the
20
+ values in active_support 5.1. Sequel now uses the active_support
21
+ values if they are available.
22
+
23
+ * When adding a String column on PostgreSQL, an explicit text: true
24
+ option now takes precedence over an explicit :size option, as it
25
+ does in Sequel's default behavior.
@@ -0,0 +1,136 @@
1
+ = New Features
2
+
3
+ * An async_thread_pool Database extension has been added, which
4
+ executes queries and processes results using a separate thread
5
+ pool. This allows you do do things like:
6
+
7
+ foos = DB[:foos].async.all
8
+ bars = DB[:bars].async.select_map(:name)
9
+ foo_bars = DB[:foo_bars].async.each{|x| p x}
10
+
11
+ and have the three method calls (all, select_map, and each)
12
+ execute concurrently. On Ruby implementations without a global
13
+ VM lock, such as JRuby, it will allow for parallel execution of
14
+ the method calls. On CRuby, the main benefit will be for cases
15
+ where query execution takes a long time or there is significant
16
+ latency between the application and the database.
17
+
18
+ When you call a method on foos, bars, or foo_bars, if the thread
19
+ pool hasn't finished processing the method, the calling code will
20
+ block until the method call has finished.
21
+
22
+ By default, for consistency, calling code will not preempt the
23
+ async thread pool. For example, if you do:
24
+
25
+ DB[:foos].async.all.size
26
+
27
+ The calling code will always wait for the async thread pool to
28
+ run the all method, and then the calling code will call size on
29
+ the result. This ensures that async queries will not use the
30
+ same connection as the the calling thread, even if calling thread
31
+ has a connection checked out.
32
+
33
+ In some cases, such as when the async thread pool is very busy,
34
+ preemption is desired for performance reasons. If you set the
35
+ :preempt_async_thread Database option before loading the
36
+ async_thread_pool extension, preemption will be allowed. With
37
+ preemption allowed, if the async thread pool has not started the
38
+ processing of the method at the time the calling code needs the
39
+ results of the method, the calling code will preempt the async
40
+ thread pool, and run the method on the current thread.
41
+
42
+ By default, the async thread pool uses the same number of threads as
43
+ the Database objects :max_connections attribute (the default for
44
+ that is 4). You can modify the number of async threads by setting
45
+ the :num_async_threads Database option before loading the Database
46
+ async_thread_pool extension.
47
+
48
+ Most Dataset methods that execute queries on the database and return
49
+ results will operate asynchronously if the the dataset is set to be
50
+ asynchronous via the Dataset#async method. This includes most
51
+ methods available due to the inclusion in Enumerable, even if not
52
+ defined by Dataset itself.
53
+
54
+ There are multiple caveats when using the async_thread_pool
55
+ extension:
56
+
57
+ * Asynchronous behavior is harder to understand and harder to
58
+ debug. It would be wise to only use this support in cases where
59
+ it provides is significant performance benefit.
60
+
61
+ * Dataset methods executed asynchronously will use a separate
62
+ database connection than the calling thread, so they will not
63
+ respect transactions in the calling thread, or other cases where
64
+ the calling thread checks out a connection directly using
65
+ Database#synchronize. They will also not respect the use of
66
+ Database#with_server (from the server_block extension) in the
67
+ calling thread.
68
+
69
+ * Dataset methods executed asynchronously should never ignore their
70
+ return value. Code such as:
71
+
72
+ DB[:table].async.insert(1)
73
+
74
+ is probablematic because without storing the return value, you
75
+ have no way to block until the insert has been completed.
76
+
77
+ * The returned object for Dataset methods executed asynchronously is
78
+ a proxy object (promise). So you should never do:
79
+
80
+ row = DB[:table].async.first
81
+ # ...
82
+ if row
83
+ end
84
+
85
+ # or:
86
+
87
+ bool = DB[:table].async.get(:boolean_column)
88
+ # ...
89
+ if bool
90
+ end
91
+
92
+ because the if branches will always be taken as row and bool will
93
+ never be nil or false. If you want to get the underlying value,
94
+ call itself on the proxy object (or __value if using Ruby <2.2).
95
+
96
+ For the same reason, you should not use the proxy objects directly
97
+ in case expressions or as arguments to Class#===. Use itself or
98
+ __value in those cases.
99
+
100
+ * Dataset methods executed asynchronously that include blocks have the
101
+ block executed asynchronously as well, assuming that the method
102
+ calls the block. Because these blocks are executed in a separate
103
+ thread, you cannot use control flow modifiers such as break or
104
+ return in them.
105
+
106
+ * An async_thread_pool model plugin has been added. This requires the
107
+ async_thread_pool extension has been loaded into the model's Database
108
+ object, and allows you to call Model.async instead of
109
+ Model.dataset.async. It also adds async support to the destroy,
110
+ with_pk, and with_pk! model dataset methods.
111
+
112
+ * Model#to_json_data has been added to the json_serializer plugin, for
113
+ returning a hash of data that can be converted to JSON, instead of
114
+ a JSON string.
115
+
116
+ * A :reject_nil option has been added to the nested_attributes method
117
+ in the nested_attributes plugin. This will ignore calls to the
118
+ nested attributes setter method where nil is passed as the setter
119
+ method argument.
120
+
121
+ = Other Improvements
122
+
123
+ * Model#freeze now works in case where model validation modifies the
124
+ object beyond adding errors.
125
+
126
+ * Model#freeze in the composition, serialization, and
127
+ serialization_modification_detection plugins now works in cases
128
+ where validation would end up loading the composed or
129
+ serialized values.
130
+
131
+ * Database#extension now avoids a possible thread safety issue that
132
+ could result in the extension being loaded into the Database twice.
133
+
134
+ * The ado adapter now supports overriding the timestamp conversion
135
+ proc. Previously, unlike other conversion procs, the timestamp
136
+ conversion proc was hard coded and could not be overridden.
@@ -0,0 +1,98 @@
1
+ = New Features
2
+
3
+ * A column_encryption plugin has been added to support encrypting the
4
+ content of individual columns in a table.
5
+
6
+ Column values are encrypted with AES-256-GCM using a per-value
7
+ cipher key derived from a key provided in the configuration using
8
+ HMAC-SHA256.
9
+
10
+ If you would like to support encryption of columns in more than one
11
+ model, you should probably load the plugin into the parent class of
12
+ your models and specify the keys:
13
+
14
+ Sequel::Model.plugin :column_encryption do |enc|
15
+ enc.key 0, ENV["SEQUEL_COLUMN_ENCRYPTION_KEY"]
16
+ end
17
+
18
+ This specifies a single master encryption key. Unless you are
19
+ actively rotating keys, it is best to use a single master key.
20
+
21
+ In the above call, 0 is the id of the key, and
22
+ ENV["SEQUEL_COLUMN_ENCRYPTION_KEY"] is the content of the key, which
23
+ must be a string with exactly 32 bytes. As indicated, this key
24
+ should not be hardcoded or otherwise committed to the source control
25
+ repository.
26
+
27
+ For models that need encrypted columns, you load the plugin again,
28
+ but specify the columns to encrypt:
29
+
30
+ ConfidentialModel.plugin :column_encryption do |enc|
31
+ enc.column :encrypted_column_name
32
+ enc.column :searchable_column_name, searchable: true
33
+ enc.column :ci_searchable_column_name, searchable: :case_insensitive
34
+ end
35
+
36
+ With this, all three specified columns (encrypted_column_name,
37
+ searchable_column_name, and ci_searchable_column_name) will be
38
+ marked as encrypted columns. When you run the following code:
39
+
40
+ ConfidentialModel.create(
41
+ encrypted_column_name: 'These',
42
+ searchable_column_name: 'will be',
43
+ ci_searchable_column_name: 'Encrypted'
44
+ )
45
+
46
+ It will save encrypted versions to the database.
47
+ encrypted_column_name will not be searchable, searchable_column_name
48
+ will be searchable with an exact match, and
49
+ ci_searchable_column_name will be searchable with a case insensitive
50
+ match.
51
+
52
+ To search searchable encrypted columns, use with_encrypted_value.
53
+ This example code will return the model instance created in the code
54
+ example in the previous section:
55
+
56
+ ConfidentialModel.
57
+ with_encrypted_value(:searchable_column_name, "will be")
58
+ with_encrypted_value(:ci_searchable_column_name, "encrypted").
59
+ first
60
+
61
+ To rotate encryption keys, add a new key above the existing key,
62
+ with a new key ID:
63
+
64
+ Sequel::Model.plugin :column_encryption do |enc|
65
+ enc.key 1, ENV["SEQUEL_COLUMN_ENCRYPTION_KEY"]
66
+ enc.key 0, ENV["SEQUEL_OLD_COLUMN_ENCRYPTION_KEY"]
67
+ end
68
+
69
+ Newly encrypted data will then use the new key. Records encrypted
70
+ with the older key will still be decrypted correctly.
71
+
72
+ To force reencryption for existing records that are using the older
73
+ key, you can use the needing_reencryption dataset method and the
74
+ reencrypt instance method. For a small number of records, you can
75
+ probably do:
76
+
77
+ ConfidentialModel.needing_reencryption.all(&:reencrypt)
78
+
79
+ With more than a small number of records, you'll want to do this in
80
+ batches. It's possible you could use an approach such as:
81
+
82
+ ds = ConfidentialModel.needing_reencryption.limit(100)
83
+ true until ds.all(&:reencrypt).empty?
84
+
85
+ After all values have been reencrypted for all models, and no models
86
+ use the older encryption key, you can remove it from the
87
+ configuration:
88
+
89
+ Sequel::Model.plugin :column_encryption do |enc|
90
+ enc.key 1, ENV["SEQUEL_COLUMN_ENCRYPTION_KEY"]
91
+ end
92
+
93
+ The column_encryption plugin supports encrypting serialized data,
94
+ as well as enforcing uniquenss of searchable encrypted columns
95
+ (in the absence of key rotation). By design, it does not support
96
+ compression, mixing encrypted and unencrypted data in the same
97
+ column, or support arbitrary encryption ciphers. See the plugin
98
+ documentation for more details.
@@ -0,0 +1,32 @@
1
+ = New Features
2
+
3
+ * A concurrent_eager_loading plugin has been added. This plugin
4
+ builds on top of the async_thread_pool Database extension and
5
+ allows eager loading multiple associations concurrently in
6
+ separate threads. With this plugin, you can mark datasets for
7
+ concurrent eager loading using eager_load_concurrently:
8
+
9
+ Album.eager_load_concurrently.eager(:artist, :genre, :tracks).all
10
+
11
+ Datasets that are marked for concurrent eager loading will use
12
+ concurrent eager loading if they are eager loading more than one
13
+ association. If you would like to make concurrent eager loading
14
+ the default, you can load the plugin with the :always option.
15
+
16
+ All of the association types that ship with Sequel now support
17
+ concurrent eager loading when using this plugin. For custom eager
18
+ loaders using the :eager_loader association option, please see the
19
+ documentation for the plugin for how to enable custom eager loading
20
+ for them.
21
+
22
+ = Other Improvements
23
+
24
+ * The date_arithmetic extension now handles ActiveSupport::Duration
25
+ values with weeks, as well as :weeks as a key in a hash value. Weeks
26
+ are converted into 7 days internally.
27
+
28
+ * The shared SQLite adapter now emulates the dropping of non-composite
29
+ unique constraints. Non-composite unique constraints are now
30
+ treated similarly to composite unique constraints, in that dropping
31
+ any unique constraints on a table will drop all unique constraints
32
+ on that table.
@@ -0,0 +1,34 @@
1
+ = New Features
2
+
3
+ * A auto_validations_constraint_validations_presence_message plugin
4
+ has been added that provides integration for the auto_validations
5
+ and constraint_validations plugin in the following conditions:
6
+
7
+ * The column has a NOT NULL constraint
8
+ * The column has a presence constraint validation with both
9
+ the :message and :allow_nil options used.
10
+
11
+ In this case, when saving a nil value in the column, the plugin
12
+ will make it so the more specific message from the presence
13
+ constraint validation is used, instead of the generic message
14
+ from auto_validations.
15
+
16
+ = Other Improvements
17
+
18
+ * On SQLite 3.35.0+, Sequel now uses ALTER TABLE DROP COLUMN for
19
+ dropping columns, instead of emulating the dropped column by
20
+ recreating the table.
21
+
22
+ * The Dataset#with :materialized option is now supported on SQLite
23
+ 3.35.0+ for specifying whether common table expressions should be
24
+ materialized.
25
+
26
+ * The odbc adapter now correct handles boolean columns with NULL
27
+ values. Previously, such values were returned as false instead
28
+ of nil.
29
+
30
+ = Backwards Compatibility
31
+
32
+ * The change to use ALTER TABLE DROP COLUMN on SQLite 3.35.0+ can
33
+ cause backwards compatibility issues if SQLite 3.35.0+ does
34
+ not allow dropping the column.
data/doc/sql.rdoc CHANGED
@@ -544,7 +544,7 @@ On some databases, you can specify null ordering:
544
544
 
545
545
  === All Columns (.*)
546
546
 
547
- To select all columns in a table, Sequel supports the * method on identifiers and qualified without an argument:
547
+ To select all columns in a table, Sequel supports the * method on identifiers and qualified identifiers without an argument:
548
548
 
549
549
  Sequel[:table].* # "table".*
550
550
  Sequel[:schema][:table].* # "schema"."table".*
data/doc/testing.rdoc CHANGED
@@ -157,9 +157,12 @@ The SEQUEL_INTEGRATION_URL environment variable specifies the Database connectio
157
157
 
158
158
  === Other
159
159
 
160
+ SEQUEL_ASYNC_THREAD_POOL :: Use the async_thread_pool extension when running the specs
161
+ SEQUEL_ASYNC_THREAD_POOL_PREEMPT :: Use the async_thread_pool extension when running the specs, with the :preempt_async_thread option
160
162
  SEQUEL_COLUMNS_INTROSPECTION :: Use the columns_introspection extension when running the specs
161
163
  SEQUEL_CONNECTION_VALIDATOR :: Use the connection validator extension when running the specs
162
164
  SEQUEL_DUPLICATE_COLUMNS_HANDLER :: Use the duplicate columns handler extension with value given when running the specs
165
+ SEQUEL_CONCURRENT_EAGER_LOADING :: Use the async_thread_pool extension and concurrent_eager_loading plugin when running the specs
163
166
  SEQUEL_ERROR_SQL :: Use the error_sql extension when running the specs
164
167
  SEQUEL_INDEX_CACHING :: Use the index_caching extension when running the specs
165
168
  SEQUEL_FIBER_CONCURRENCY :: Use the fiber_concurrency extension when running the adapter and integration specs
@@ -54,7 +54,7 @@ methods in the surrounding scope. For example:
54
54
 
55
55
  # Regular block
56
56
  ds.where{|o| o.c > a - b + @d}
57
- # WHERE (c > 100)
57
+ # WHERE (c > 110)
58
58
 
59
59
  # Instance-evaled block
60
60
  ds.where{c > a - b + @d}
@@ -195,10 +195,25 @@ module Sequel
195
195
  end
196
196
 
197
197
  @conversion_procs = CONVERSION_PROCS.dup
198
+ @conversion_procs[AdDBTimeStamp] = method(:adb_timestamp_to_application_timestamp)
198
199
 
199
200
  super
200
201
  end
201
202
 
203
+ def adb_timestamp_to_application_timestamp(v)
204
+ # This hard codes a timestamp_precision of 6 when converting.
205
+ # That is the default timestamp_precision, but the ado/mssql adapter uses a timestamp_precision
206
+ # of 3. However, timestamps returned by ado/mssql have nsec values that end up rounding to a
207
+ # the same value as if a timestamp_precision of 3 was hard coded (either xxx999yzz, where y is
208
+ # 5-9 or xxx000yzz where y is 0-4).
209
+ #
210
+ # ADO subadapters should override this they would like a different timestamp precision and the
211
+ # this code does not work for them (for example, if they provide full nsec precision).
212
+ #
213
+ # Note that fractional second handling for WIN32OLE objects is not correct on ruby <2.2
214
+ to_application_timestamp([v.year, v.month, v.day, v.hour, v.min, v.sec, (v.nsec/1000.0).round * 1000])
215
+ end
216
+
202
217
  def dataset_class_default
203
218
  Dataset
204
219
  end
@@ -233,23 +248,8 @@ module Sequel
233
248
  cols = []
234
249
  conversion_procs = db.conversion_procs
235
250
 
236
- ts_cp = nil
237
251
  recordset.Fields.each do |field|
238
- type = field.Type
239
- cp = if type == AdDBTimeStamp
240
- ts_cp ||= begin
241
- nsec_div = 1000000000.0/(10**(timestamp_precision))
242
- nsec_mul = 10**(timestamp_precision+3)
243
- meth = db.method(:to_application_timestamp)
244
- lambda do |v|
245
- # Fractional second handling is not correct on ruby <2.2
246
- meth.call([v.year, v.month, v.day, v.hour, v.min, v.sec, (v.nsec/nsec_div).round * nsec_mul])
247
- end
248
- end
249
- else
250
- conversion_procs[type]
251
- end
252
- cols << [output_identifier(field.Name), cp]
252
+ cols << [output_identifier(field.Name), conversion_procs[field.Type]]
253
253
  end
254
254
 
255
255
  self.columns = cols.map(&:first)