sequel 3.36.1 → 3.37.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.
- data/CHANGELOG +84 -0
- data/Rakefile +13 -0
- data/bin/sequel +12 -16
- data/doc/advanced_associations.rdoc +36 -67
- data/doc/association_basics.rdoc +11 -16
- data/doc/release_notes/3.37.0.txt +338 -0
- data/doc/schema_modification.rdoc +4 -0
- data/lib/sequel/adapters/jdbc/h2.rb +1 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +26 -8
- data/lib/sequel/adapters/mysql2.rb +4 -3
- data/lib/sequel/adapters/odbc/mssql.rb +2 -2
- data/lib/sequel/adapters/postgres.rb +4 -60
- data/lib/sequel/adapters/shared/mssql.rb +2 -1
- data/lib/sequel/adapters/shared/mysql.rb +0 -5
- data/lib/sequel/adapters/shared/postgres.rb +68 -2
- data/lib/sequel/adapters/shared/sqlite.rb +17 -1
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +12 -1
- data/lib/sequel/adapters/utils/pg_types.rb +76 -0
- data/lib/sequel/core.rb +13 -0
- data/lib/sequel/database/misc.rb +41 -1
- data/lib/sequel/database/schema_generator.rb +23 -10
- data/lib/sequel/database/schema_methods.rb +26 -4
- data/lib/sequel/dataset/graph.rb +2 -1
- data/lib/sequel/dataset/query.rb +62 -2
- data/lib/sequel/extensions/_pretty_table.rb +7 -3
- data/lib/sequel/extensions/arbitrary_servers.rb +5 -4
- data/lib/sequel/extensions/blank.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +13 -2
- data/lib/sequel/extensions/core_extensions.rb +6 -0
- data/lib/sequel/extensions/eval_inspect.rb +158 -0
- data/lib/sequel/extensions/inflector.rb +4 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -4
- data/lib/sequel/extensions/migration.rb +4 -1
- data/lib/sequel/extensions/named_timezones.rb +4 -0
- data/lib/sequel/extensions/null_dataset.rb +4 -0
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pg_array.rb +219 -168
- data/lib/sequel/extensions/pg_array_ops.rb +7 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -4
- data/lib/sequel/extensions/pg_hstore.rb +3 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +7 -2
- data/lib/sequel/extensions/pg_inet.rb +28 -3
- data/lib/sequel/extensions/pg_interval.rb +192 -0
- data/lib/sequel/extensions/pg_json.rb +21 -9
- data/lib/sequel/extensions/pg_range.rb +487 -0
- data/lib/sequel/extensions/pg_range_ops.rb +122 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +3 -2
- data/lib/sequel/extensions/pretty_table.rb +12 -1
- data/lib/sequel/extensions/query.rb +4 -0
- data/lib/sequel/extensions/query_literals.rb +6 -6
- data/lib/sequel/extensions/schema_dumper.rb +39 -38
- data/lib/sequel/extensions/select_remove.rb +4 -0
- data/lib/sequel/extensions/server_block.rb +3 -2
- data/lib/sequel/extensions/split_array_nil.rb +65 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -0
- data/lib/sequel/extensions/string_date_time.rb +4 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +9 -3
- data/lib/sequel/extensions/to_dot.rb +4 -0
- data/lib/sequel/model/associations.rb +150 -91
- data/lib/sequel/plugins/identity_map.rb +2 -2
- data/lib/sequel/plugins/list.rb +1 -0
- data/lib/sequel/plugins/many_through_many.rb +33 -32
- data/lib/sequel/plugins/nested_attributes.rb +11 -3
- data/lib/sequel/plugins/rcte_tree.rb +2 -2
- data/lib/sequel/plugins/schema.rb +1 -1
- data/lib/sequel/sql.rb +14 -14
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/mysql_spec.rb +25 -0
- data/spec/adapters/postgres_spec.rb +572 -28
- data/spec/adapters/sqlite_spec.rb +16 -1
- data/spec/core/database_spec.rb +61 -2
- data/spec/core/dataset_spec.rb +92 -0
- data/spec/core/expression_filters_spec.rb +12 -0
- data/spec/extensions/arbitrary_servers_spec.rb +1 -1
- data/spec/extensions/boolean_readers_spec.rb +25 -25
- data/spec/extensions/eval_inspect_spec.rb +58 -0
- data/spec/extensions/json_serializer_spec.rb +0 -6
- data/spec/extensions/list_spec.rb +1 -1
- data/spec/extensions/looser_typecasting_spec.rb +7 -7
- data/spec/extensions/many_through_many_spec.rb +81 -0
- data/spec/extensions/nested_attributes_spec.rb +21 -4
- data/spec/extensions/pg_array_ops_spec.rb +1 -11
- data/spec/extensions/pg_array_spec.rb +181 -90
- data/spec/extensions/pg_auto_parameterize_spec.rb +3 -3
- data/spec/extensions/pg_hstore_spec.rb +1 -3
- data/spec/extensions/pg_inet_spec.rb +6 -1
- data/spec/extensions/pg_interval_spec.rb +73 -0
- data/spec/extensions/pg_json_spec.rb +5 -9
- data/spec/extensions/pg_range_ops_spec.rb +49 -0
- data/spec/extensions/pg_range_spec.rb +372 -0
- data/spec/extensions/pg_statement_cache_spec.rb +1 -2
- data/spec/extensions/query_literals_spec.rb +1 -2
- data/spec/extensions/schema_dumper_spec.rb +48 -89
- data/spec/extensions/serialization_spec.rb +1 -5
- data/spec/extensions/server_block_spec.rb +2 -2
- data/spec/extensions/spec_helper.rb +12 -2
- data/spec/extensions/split_array_nil_spec.rb +24 -0
- data/spec/integration/associations_test.rb +4 -4
- data/spec/integration/database_test.rb +2 -2
- data/spec/integration/dataset_test.rb +4 -4
- data/spec/integration/eager_loader_test.rb +6 -6
- data/spec/integration/plugin_test.rb +2 -2
- data/spec/integration/spec_helper.rb +2 -2
- data/spec/model/association_reflection_spec.rb +5 -0
- data/spec/model/associations_spec.rb +156 -49
- data/spec/model/eager_loading_spec.rb +137 -2
- data/spec/model/model_spec.rb +10 -10
- metadata +15 -2
|
@@ -58,7 +58,7 @@ module Sequel
|
|
|
58
58
|
left_key_alias = opts[:left_key_alias] ||= opts.default_associated_key_alias
|
|
59
59
|
opts[:eager_loader] = lambda do |eo|
|
|
60
60
|
return el.call(eo) unless model.identity_map
|
|
61
|
-
h = eo[:
|
|
61
|
+
h = eo[:id_map]
|
|
62
62
|
eo[:rows].each{|object| object.associations[name] = []}
|
|
63
63
|
r = uses_rcks ? rcks.zip(opts.right_primary_keys) : [[right, opts.right_primary_key]]
|
|
64
64
|
l = uses_lcks ? [[lcks.map{|k| SQL::QualifiedIdentifier.new(join_table, k)}, h.keys]] : [[left, h.keys]]
|
|
@@ -107,7 +107,7 @@ module Sequel
|
|
|
107
107
|
left_key_alias = opts[:left_key_alias]
|
|
108
108
|
opts[:eager_loader] = lambda do |eo|
|
|
109
109
|
return el.call(eo) unless model.identity_map
|
|
110
|
-
h = eo[:
|
|
110
|
+
h = eo[:id_map]
|
|
111
111
|
eo[:rows].each{|object| object.associations[name] = []}
|
|
112
112
|
ds = opts.associated_class
|
|
113
113
|
opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
|
data/lib/sequel/plugins/list.rb
CHANGED
|
@@ -53,7 +53,7 @@ module Sequel
|
|
|
53
53
|
self[:uses_left_composite_keys] ? (0...self[:through].first[:left].length).map{|i| :"x_foreign_key_#{i}_x"} : :x_foreign_key_x
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
%w'associated_key_table
|
|
56
|
+
%w'associated_key_table predicate_key edges final_edge final_reverse_edge reverse_edges'.each do |meth|
|
|
57
57
|
class_eval(<<-END, __FILE__, __LINE__+1)
|
|
58
58
|
def #{meth}
|
|
59
59
|
cached_fetch(:#{meth}){calculate_edges[:#{meth}]}
|
|
@@ -87,7 +87,7 @@ module Sequel
|
|
|
87
87
|
|
|
88
88
|
# Transform the :through option into a list of edges and reverse edges to use to join tables when loading the association.
|
|
89
89
|
def calculate_edges
|
|
90
|
-
es = [{:left_table=>self[:model].table_name, :left_key=>self[:
|
|
90
|
+
es = [{:left_table=>self[:model].table_name, :left_key=>self[:left_primary_key_column]}]
|
|
91
91
|
self[:through].each do |t|
|
|
92
92
|
es.last.merge!(:right_key=>t[:left], :right_table=>t[:table], :join_type=>t[:join_type]||self[:graph_join_type], :conditions=>(t[:conditions]||[]).to_a, :block=>t[:block])
|
|
93
93
|
es.last[:only_conditions] = t[:only_conditions] if t.include?(:only_conditions)
|
|
@@ -109,7 +109,7 @@ module Sequel
|
|
|
109
109
|
:final_reverse_edge=>final_reverse_edge,
|
|
110
110
|
:edges=>edges,
|
|
111
111
|
:reverse_edges=>reverse_edges,
|
|
112
|
-
:
|
|
112
|
+
:predicate_key=>qualify(final_reverse_alias, edges.first[:right]),
|
|
113
113
|
:associated_key_table=>final_reverse_edge[:alias],
|
|
114
114
|
}
|
|
115
115
|
h.each{|k, v| cached_set(k, v)}
|
|
@@ -123,24 +123,17 @@ module Sequel
|
|
|
123
123
|
# * through - The tables and keys to join between the current table and the associated table.
|
|
124
124
|
# Must be an array, with elements that are either 3 element arrays, or hashes with keys :table, :left, and :right.
|
|
125
125
|
# The required entries in the array/hash are:
|
|
126
|
-
#
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
#
|
|
126
|
+
# :table (first array element) :: The name of the table to join.
|
|
127
|
+
# :left (middle array element) :: The key joining the table to the previous table. Can use an
|
|
128
|
+
# array of symbols for a composite key association.
|
|
129
|
+
# :right (last array element) :: The key joining the table to the next table. Can use an
|
|
130
|
+
# array of symbols for a composite key association.
|
|
131
131
|
# If a hash is provided, the following keys are respected when using eager_graph:
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
# * opts - The options for the associaion. Takes the same options as
|
|
137
|
-
# * :left_primary_key - column in current table that the first :left option in
|
|
138
|
-
# through points to, as a symbol. Defaults to primary key of current table. Can use an
|
|
139
|
-
# array of symbols for a composite key association.
|
|
140
|
-
# * :right_primary_key - column in associated table that the final :right option in
|
|
141
|
-
# through points to, as a symbol. Defaults to primary key of the associated table. Can use an
|
|
142
|
-
# array of symbols for a composite key association.
|
|
143
|
-
# * :uniq - Adds a after_load callback that makes the array of objects unique.
|
|
132
|
+
# :block :: A proc to use as the block argument to join.
|
|
133
|
+
# :conditions :: Extra conditions to add to the JOIN ON clause. Must be a hash or array of two pairs.
|
|
134
|
+
# :join_type :: The join type to use for the join, defaults to :left_outer.
|
|
135
|
+
# :only_conditions :: Conditions to use for the join instead of the ones specified by the keys.
|
|
136
|
+
# * opts - The options for the associaion. Takes the same options as many_to_many.
|
|
144
137
|
def many_through_many(name, through, opts={}, &block)
|
|
145
138
|
associate(:many_through_many, name, opts.merge(through.is_a?(Hash) ? through : {:through=>through}), &block)
|
|
146
139
|
end
|
|
@@ -171,23 +164,26 @@ module Sequel
|
|
|
171
164
|
uses_lcks = opts[:uses_left_composite_keys] = left_key.is_a?(Array)
|
|
172
165
|
left_keys = Array(left_key)
|
|
173
166
|
left_pk = (opts[:left_primary_key] ||= self.primary_key)
|
|
167
|
+
opts[:eager_loader_key] = left_pk unless opts.has_key?(:eager_loader_key)
|
|
174
168
|
left_pks = opts[:left_primary_keys] = Array(left_pk)
|
|
169
|
+
lpkc = opts[:left_primary_key_column] ||= left_pk
|
|
170
|
+
lpkcs = opts[:left_primary_key_columns] ||= Array(lpkc)
|
|
175
171
|
opts[:dataset] ||= lambda do
|
|
176
172
|
ds = opts.associated_class
|
|
177
|
-
opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
|
|
173
|
+
opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias], :qualify=>:deep)}
|
|
178
174
|
ft = opts.final_reverse_edge
|
|
179
|
-
ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) +
|
|
175
|
+
ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + opts.predicate_keys.zip(left_pks.map{|k| send(k)}), :table_alias=>ft[:alias], :qualify=>:deep)
|
|
180
176
|
end
|
|
181
177
|
|
|
182
178
|
left_key_alias = opts[:left_key_alias] ||= opts.default_associated_key_alias
|
|
183
179
|
opts[:eager_loader] ||= lambda do |eo|
|
|
184
|
-
h = eo[:
|
|
180
|
+
h = eo[:id_map]
|
|
185
181
|
rows = eo[:rows]
|
|
186
182
|
rows.each{|object| object.associations[name] = []}
|
|
187
183
|
ds = opts.associated_class
|
|
188
|
-
opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
|
|
184
|
+
opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias], :qualify=>:deep)}
|
|
189
185
|
ft = opts.final_reverse_edge
|
|
190
|
-
ds = ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + [[opts.
|
|
186
|
+
ds = ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + [[opts.predicate_key, h.keys]], :table_alias=>ft[:alias], :qualify=>:deep)
|
|
191
187
|
ds = model.eager_loading_dataset(opts, ds, nil, eo[:associations], eo)
|
|
192
188
|
case opts.eager_limit_strategy
|
|
193
189
|
when :window_function
|
|
@@ -198,7 +194,7 @@ module Sequel
|
|
|
198
194
|
ds = apply_correlated_subquery_eager_limit_strategy(ds, opts) do |xds|
|
|
199
195
|
dsa = ds.send(:dataset_alias, 2)
|
|
200
196
|
opts.reverse_edges.each{|t| xds = xds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
|
|
201
|
-
xds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + left_keys.map{|k| [k, SQL::QualifiedIdentifier.new(ft[:table], k)]}, :table_alias=>dsa)
|
|
197
|
+
xds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + left_keys.map{|k| [k, SQL::QualifiedIdentifier.new(ft[:table], k)]}, :table_alias=>dsa, :qualify=>:deep)
|
|
202
198
|
end
|
|
203
199
|
end
|
|
204
200
|
ds.all do |assoc_record|
|
|
@@ -227,11 +223,11 @@ module Sequel
|
|
|
227
223
|
ds = eo[:self]
|
|
228
224
|
iq = eo[:implicit_qualifier]
|
|
229
225
|
opts.edges.each do |t|
|
|
230
|
-
ds = ds.graph(t[:table], t.fetch(:only_conditions, (Array(t[:right]).zip(Array(t[:left])) + t[:conditions])), :select=>false, :table_alias=>ds.unused_table_alias(t[:table]), :join_type=>t[:join_type], :implicit_qualifier=>iq, &t[:block])
|
|
226
|
+
ds = ds.graph(t[:table], t.fetch(:only_conditions, (Array(t[:right]).zip(Array(t[:left])) + t[:conditions])), :select=>false, :table_alias=>ds.unused_table_alias(t[:table]), :join_type=>t[:join_type], :qualify=>:deep, :implicit_qualifier=>iq, &t[:block])
|
|
231
227
|
iq = nil
|
|
232
228
|
end
|
|
233
229
|
fe = opts.final_edge
|
|
234
|
-
ds.graph(opts.associated_class, use_only_conditions ? only_conditions : (Array(opts.right_primary_key).zip(Array(fe[:left])) + conditions), :select=>select, :table_alias=>eo[:table_alias], :join_type=>join_type, &graph_block)
|
|
230
|
+
ds.graph(opts.associated_class, use_only_conditions ? only_conditions : (Array(opts.right_primary_key).zip(Array(fe[:left])) + conditions), :select=>select, :table_alias=>eo[:table_alias], :qualify=>:deep, :join_type=>join_type, &graph_block)
|
|
235
231
|
end
|
|
236
232
|
|
|
237
233
|
def_association_dataset_methods(opts)
|
|
@@ -243,22 +239,27 @@ module Sequel
|
|
|
243
239
|
|
|
244
240
|
# Use a subquery to filter rows to those related to the given associated object
|
|
245
241
|
def many_through_many_association_filter_expression(op, ref, obj)
|
|
246
|
-
lpks = ref[:
|
|
242
|
+
lpks = ref[:left_primary_key_columns]
|
|
247
243
|
lpks = lpks.first if lpks.length == 1
|
|
248
244
|
lpks = ref.qualify(model.table_name, lpks)
|
|
249
245
|
edges = ref.edges
|
|
250
246
|
first, rest = edges.first, edges[1..-1]
|
|
251
247
|
last = edges.last
|
|
252
248
|
ds = model.db[first[:table]].select(*Array(ref.qualify(first[:table], first[:right])))
|
|
253
|
-
rest.each{|e| ds = ds.join(e[:table], e.fetch(:only_conditions, (Array(e[:right]).zip(Array(e[:left])) + e[:conditions])), :table_alias=>ds.unused_table_alias(e[:table]), &e[:block])}
|
|
249
|
+
rest.each{|e| ds = ds.join(e[:table], e.fetch(:only_conditions, (Array(e[:right]).zip(Array(e[:left])) + e[:conditions])), :table_alias=>ds.unused_table_alias(e[:table]), :qualify=>:deep, &e[:block])}
|
|
254
250
|
last_alias = if rest.empty?
|
|
255
251
|
first[:table]
|
|
256
252
|
else
|
|
257
253
|
last_join = ds.opts[:join].last
|
|
258
254
|
last_join.table_alias || last_join.table
|
|
259
255
|
end
|
|
260
|
-
|
|
261
|
-
meths =
|
|
256
|
+
|
|
257
|
+
meths = if obj.is_a?(Sequel::Dataset)
|
|
258
|
+
ref.qualify(obj.model.table_name, ref.right_primary_keys)
|
|
259
|
+
else
|
|
260
|
+
ref.right_primary_key_methods
|
|
261
|
+
end
|
|
262
|
+
|
|
262
263
|
exp = association_filter_key_expression(ref.qualify(last_alias, Array(ref.final_edge[:left])), meths, obj)
|
|
263
264
|
if exp == SQL::Constants::FALSE
|
|
264
265
|
association_filter_handle_inversion(op, exp, Array(lpks))
|
|
@@ -83,8 +83,11 @@ module Sequel
|
|
|
83
83
|
|
|
84
84
|
# Allow nested attributes to be set for the given associations. Options:
|
|
85
85
|
# * :destroy - Allow destruction of nested records.
|
|
86
|
-
# * :fields - If provided, should be an Array.
|
|
87
|
-
#
|
|
86
|
+
# * :fields - If provided, should be an Array or proc. If it is an array,
|
|
87
|
+
# restricts the fields allowed to be modified through the
|
|
88
|
+
# association_attributes= method to the specific fields given. If it is
|
|
89
|
+
# a proc, it will be called with the associated object and should return an
|
|
90
|
+
# array of the allowable fields.
|
|
88
91
|
# * :limit - For *_to_many associations, a limit on the number of records
|
|
89
92
|
# that will be processed, to prevent denial of service attacks.
|
|
90
93
|
# * :reject_if - A proc that is given each attribute hash before it is
|
|
@@ -168,7 +171,11 @@ module Sequel
|
|
|
168
171
|
|
|
169
172
|
# Don't need to validate the object twice if :validate association option is not false
|
|
170
173
|
# and don't want to validate it at all if it is false.
|
|
171
|
-
|
|
174
|
+
if reflection[:type] == :many_to_one
|
|
175
|
+
before_save_hook{send(reflection.setter_method, obj.save(:validate=>false))}
|
|
176
|
+
else
|
|
177
|
+
after_save_hook{send(reflection.setter_method, obj)}
|
|
178
|
+
end
|
|
172
179
|
end
|
|
173
180
|
obj
|
|
174
181
|
end
|
|
@@ -207,6 +214,7 @@ module Sequel
|
|
|
207
214
|
# specific :fields if configured.
|
|
208
215
|
def nested_attributes_set_attributes(reflection, obj, attributes)
|
|
209
216
|
if fields = reflection[:nested_attributes][:fields]
|
|
217
|
+
fields = fields.call(obj) if fields.respond_to?(:call)
|
|
210
218
|
obj.set_only(attributes, fields)
|
|
211
219
|
else
|
|
212
220
|
obj.set(attributes)
|
|
@@ -160,7 +160,7 @@ module Sequel
|
|
|
160
160
|
end
|
|
161
161
|
a[:after_load] ||= aal
|
|
162
162
|
a[:eager_loader] ||= proc do |eo|
|
|
163
|
-
id_map = eo[:
|
|
163
|
+
id_map = eo[:id_map]
|
|
164
164
|
parent_map = {}
|
|
165
165
|
children_map = {}
|
|
166
166
|
eo[:rows].each do |obj|
|
|
@@ -259,7 +259,7 @@ module Sequel
|
|
|
259
259
|
end
|
|
260
260
|
d[:after_load] = dal
|
|
261
261
|
d[:eager_loader] ||= proc do |eo|
|
|
262
|
-
id_map = eo[:
|
|
262
|
+
id_map = eo[:id_map]
|
|
263
263
|
associations = eo[:associations]
|
|
264
264
|
parent_map = {}
|
|
265
265
|
children_map = {}
|
|
@@ -66,7 +66,7 @@ module Sequel
|
|
|
66
66
|
# test code or simple examples.
|
|
67
67
|
def set_schema(name = nil, &block)
|
|
68
68
|
set_dataset(db[name]) if name
|
|
69
|
-
@schema =
|
|
69
|
+
@schema = db.create_table_generator(&block)
|
|
70
70
|
set_primary_key(@schema.primary_key_name) if @schema.primary_key_name
|
|
71
71
|
end
|
|
72
72
|
|
data/lib/sequel/sql.rb
CHANGED
|
@@ -67,7 +67,7 @@ module Sequel
|
|
|
67
67
|
# such methods are the values of the object's attributes.
|
|
68
68
|
def self.attr_reader(*args)
|
|
69
69
|
super
|
|
70
|
-
comparison_attrs.concat
|
|
70
|
+
comparison_attrs.concat(args)
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
# All attributes used for equality and hash methods.
|
|
@@ -166,12 +166,12 @@ module Sequel
|
|
|
166
166
|
# Custom expressions that may have different syntax on different databases
|
|
167
167
|
CUSTOM_EXPRESSIONS = [:extract]
|
|
168
168
|
|
|
169
|
-
# An array of args for this object
|
|
170
|
-
attr_reader :args
|
|
171
|
-
|
|
172
169
|
# The operator symbol for this object
|
|
173
170
|
attr_reader :op
|
|
174
171
|
|
|
172
|
+
# An array of args for this object
|
|
173
|
+
attr_reader :args
|
|
174
|
+
|
|
175
175
|
# Set the operator symbol and arguments for this object to the ones given.
|
|
176
176
|
# Convert all args that are hashes or arrays of two element arrays to +BooleanExpressions+,
|
|
177
177
|
# other than the second arg for an IN/NOT IN operator.
|
|
@@ -1075,12 +1075,12 @@ module Sequel
|
|
|
1075
1075
|
|
|
1076
1076
|
# Represents an SQL function call.
|
|
1077
1077
|
class Function < GenericExpression
|
|
1078
|
-
# The array of arguments to pass to the function (may be blank)
|
|
1079
|
-
attr_reader :args
|
|
1080
|
-
|
|
1081
1078
|
# The SQL function to call
|
|
1082
1079
|
attr_reader :f
|
|
1083
1080
|
|
|
1081
|
+
# The array of arguments to pass to the function (may be blank)
|
|
1082
|
+
attr_reader :args
|
|
1083
|
+
|
|
1084
1084
|
# Set the functions and args to the given arguments
|
|
1085
1085
|
def initialize(f, *args)
|
|
1086
1086
|
@f, @args = f, args
|
|
@@ -1173,16 +1173,16 @@ module Sequel
|
|
|
1173
1173
|
# required for the prepared statement support and for database-specific
|
|
1174
1174
|
# literalization.
|
|
1175
1175
|
class PlaceholderLiteralString < GenericExpression
|
|
1176
|
+
# The literal string containing placeholders. This can also be an array
|
|
1177
|
+
# of strings, where each arg in args goes between the string elements.
|
|
1178
|
+
attr_reader :str
|
|
1179
|
+
|
|
1176
1180
|
# The arguments that will be subsituted into the placeholders.
|
|
1177
1181
|
# Either an array of unnamed placeholders (which will be substituted in
|
|
1178
1182
|
# order for ? characters), or a hash of named placeholders (which will be
|
|
1179
1183
|
# substituted for :key phrases).
|
|
1180
1184
|
attr_reader :args
|
|
1181
1185
|
|
|
1182
|
-
# The literal string containing placeholders. This can also be an array
|
|
1183
|
-
# of strings, where each arg in args goes between the string elements.
|
|
1184
|
-
attr_reader :str
|
|
1185
|
-
|
|
1186
1186
|
# Whether to surround the expression with parantheses
|
|
1187
1187
|
attr_reader :parens
|
|
1188
1188
|
|
|
@@ -1257,12 +1257,12 @@ module Sequel
|
|
|
1257
1257
|
class QualifiedIdentifier < GenericExpression
|
|
1258
1258
|
include QualifyingMethods
|
|
1259
1259
|
|
|
1260
|
-
# The column/table referenced
|
|
1261
|
-
attr_reader :column
|
|
1262
|
-
|
|
1263
1260
|
# The table/schema qualifying the reference
|
|
1264
1261
|
attr_reader :table
|
|
1265
1262
|
|
|
1263
|
+
# The column/table referenced
|
|
1264
|
+
attr_reader :column
|
|
1265
|
+
|
|
1266
1266
|
# Set the table and column to the given arguments
|
|
1267
1267
|
def initialize(table, column)
|
|
1268
1268
|
@table, @column = table, column
|
data/lib/sequel/version.rb
CHANGED
|
@@ -3,10 +3,10 @@ module Sequel
|
|
|
3
3
|
MAJOR = 3
|
|
4
4
|
# The minor version of Sequel. Bumped for every non-patch level
|
|
5
5
|
# release, generally around once a month.
|
|
6
|
-
MINOR =
|
|
6
|
+
MINOR = 37
|
|
7
7
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
|
8
8
|
# releases that fix regressions from previous versions.
|
|
9
|
-
TINY =
|
|
9
|
+
TINY = 0
|
|
10
10
|
|
|
11
11
|
# The version of Sequel you are using, as a string (e.g. "2.11.0")
|
|
12
12
|
VERSION = [MAJOR, MINOR, TINY].join('.')
|
data/spec/adapters/mysql_spec.rb
CHANGED
|
@@ -1224,3 +1224,28 @@ if MYSQL_DB.adapter_scheme == :mysql
|
|
|
1224
1224
|
end
|
|
1225
1225
|
end
|
|
1226
1226
|
end
|
|
1227
|
+
|
|
1228
|
+
if MYSQL_DB.adapter_scheme == :mysql2
|
|
1229
|
+
describe "Mysql2 streaming" do
|
|
1230
|
+
before(:all) do
|
|
1231
|
+
MYSQL_DB.create_table!(:a){Integer :a}
|
|
1232
|
+
MYSQL_DB.transaction do
|
|
1233
|
+
1000.times do |i|
|
|
1234
|
+
MYSQL_DB[:a].insert(i)
|
|
1235
|
+
end
|
|
1236
|
+
end
|
|
1237
|
+
@ds = MYSQL_DB[:a].stream.order(:a)
|
|
1238
|
+
end
|
|
1239
|
+
after(:all) do
|
|
1240
|
+
MYSQL_DB.drop_table?(:a)
|
|
1241
|
+
end
|
|
1242
|
+
|
|
1243
|
+
specify "should correctly stream results" do
|
|
1244
|
+
@ds.map(:a).should == (0...1000).to_a
|
|
1245
|
+
end
|
|
1246
|
+
|
|
1247
|
+
specify "should correctly handle early returning when streaming results" do
|
|
1248
|
+
3.times{@ds.each{|r| break r[:a]}.should == 0}
|
|
1249
|
+
end
|
|
1250
|
+
end if false
|
|
1251
|
+
end
|
|
@@ -158,6 +158,43 @@ describe "A PostgreSQL dataset" do
|
|
|
158
158
|
@d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}
|
|
159
159
|
end
|
|
160
160
|
|
|
161
|
+
specify "should support exclusion constraints when creating or altering tables" do
|
|
162
|
+
begin
|
|
163
|
+
@db = POSTGRES_DB
|
|
164
|
+
@db.create_table!(:atest){Integer :t; exclude [[:t.desc(:nulls=>:last), '=']], :using=>:btree, :where=>proc{t > 0}}
|
|
165
|
+
@db[:atest].insert(1)
|
|
166
|
+
@db[:atest].insert(2)
|
|
167
|
+
proc{@db[:atest].insert(2)}.should raise_error(Sequel::DatabaseError)
|
|
168
|
+
|
|
169
|
+
@db.create_table!(:atest){Integer :t}
|
|
170
|
+
@db.alter_table(:atest){add_exclusion_constraint [[:t, '=']], :using=>:btree, :name=>'atest_ex'}
|
|
171
|
+
@db[:atest].insert(1)
|
|
172
|
+
@db[:atest].insert(2)
|
|
173
|
+
proc{@db[:atest].insert(2)}.should raise_error(Sequel::DatabaseError)
|
|
174
|
+
@db.alter_table(:atest){drop_constraint 'atest_ex'}
|
|
175
|
+
ensure
|
|
176
|
+
@db.drop_table?(:atest)
|
|
177
|
+
end
|
|
178
|
+
end if POSTGRES_DB.server_version >= 90000
|
|
179
|
+
|
|
180
|
+
specify "should support adding foreign key constarints that are not yet valid, and validating them later" do
|
|
181
|
+
begin
|
|
182
|
+
@db = POSTGRES_DB
|
|
183
|
+
@db.create_table!(:atest){primary_key :id; Integer :fk}
|
|
184
|
+
@db[:atest].insert(1, 5)
|
|
185
|
+
@db.alter_table(:atest){add_foreign_key [:fk], :atest, :not_valid=>true, :name=>:atest_fk}
|
|
186
|
+
@db[:atest].insert(2, 1)
|
|
187
|
+
proc{@db[:atest].insert(3, 4)}.should raise_error(Sequel::DatabaseError)
|
|
188
|
+
|
|
189
|
+
proc{@db.alter_table(:atest){validate_constraint :atest_fk}}.should raise_error(Sequel::DatabaseError)
|
|
190
|
+
@db[:atest].where(:id=>1).update(:fk=>2)
|
|
191
|
+
@db.alter_table(:atest){validate_constraint :atest_fk}
|
|
192
|
+
proc{@db.alter_table(:atest){validate_constraint :atest_fk}}.should_not raise_error
|
|
193
|
+
ensure
|
|
194
|
+
@db.drop_table?(:atest)
|
|
195
|
+
end
|
|
196
|
+
end if POSTGRES_DB.server_version >= 90200
|
|
197
|
+
|
|
161
198
|
specify "should support :using when altering a column's type" do
|
|
162
199
|
begin
|
|
163
200
|
@db = POSTGRES_DB
|
|
@@ -1349,11 +1386,12 @@ end
|
|
|
1349
1386
|
|
|
1350
1387
|
describe 'PostgreSQL array handling' do
|
|
1351
1388
|
before(:all) do
|
|
1352
|
-
Sequel.extension :pg_array
|
|
1353
1389
|
@db = POSTGRES_DB
|
|
1354
|
-
@db.
|
|
1390
|
+
@db.extension :pg_array
|
|
1355
1391
|
@ds = @db[:items]
|
|
1356
1392
|
@native = POSTGRES_DB.adapter_scheme == :postgres
|
|
1393
|
+
@jdbc = POSTGRES_DB.adapter_scheme == :jdbc
|
|
1394
|
+
@tp = lambda{@db.schema(:items).map{|a| a.last[:type]}}
|
|
1357
1395
|
end
|
|
1358
1396
|
after do
|
|
1359
1397
|
@db.drop_table?(:items)
|
|
@@ -1367,21 +1405,29 @@ describe 'PostgreSQL array handling' do
|
|
|
1367
1405
|
column :r, 'real[]'
|
|
1368
1406
|
column :dp, 'double precision[]'
|
|
1369
1407
|
end
|
|
1408
|
+
@tp.call.should == [:integer_array, :integer_array, :bigint_array, :float_array, :float_array]
|
|
1370
1409
|
@ds.insert([1].pg_array(:int2), [nil, 2].pg_array(:int4), [3, nil].pg_array(:int8), [4, nil, 4.5].pg_array(:real), [5, nil, 5.5].pg_array("double precision"))
|
|
1371
1410
|
@ds.count.should == 1
|
|
1372
|
-
|
|
1373
|
-
|
|
1411
|
+
rs = @ds.all
|
|
1412
|
+
if @jdbc || @native
|
|
1374
1413
|
rs.should == [{:i2=>[1], :i4=>[nil, 2], :i8=>[3, nil], :r=>[4.0, nil, 4.5], :dp=>[5.0, nil, 5.5]}]
|
|
1414
|
+
end
|
|
1415
|
+
if @native
|
|
1375
1416
|
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1376
1417
|
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1377
1418
|
@ds.delete
|
|
1378
1419
|
@ds.insert(rs.first)
|
|
1379
1420
|
@ds.all.should == rs
|
|
1421
|
+
end
|
|
1380
1422
|
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1423
|
+
@ds.delete
|
|
1424
|
+
@ds.insert([[1], [2]].pg_array(:int2), [[nil, 2], [3, 4]].pg_array(:int4), [[3, nil], [nil, nil]].pg_array(:int8), [[4, nil], [nil, 4.5]].pg_array(:real), [[5, nil], [nil, 5.5]].pg_array("double precision"))
|
|
1425
|
+
|
|
1426
|
+
rs = @ds.all
|
|
1427
|
+
if @jdbc || @native
|
|
1384
1428
|
rs.should == [{:i2=>[[1], [2]], :i4=>[[nil, 2], [3, 4]], :i8=>[[3, nil], [nil, nil]], :r=>[[4, nil], [nil, 4.5]], :dp=>[[5, nil], [nil, 5.5]]}]
|
|
1429
|
+
end
|
|
1430
|
+
if @native
|
|
1385
1431
|
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1386
1432
|
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1387
1433
|
@ds.delete
|
|
@@ -1394,21 +1440,28 @@ describe 'PostgreSQL array handling' do
|
|
|
1394
1440
|
@db.create_table!(:items) do
|
|
1395
1441
|
column :n, 'numeric[]'
|
|
1396
1442
|
end
|
|
1443
|
+
@tp.call.should == [:decimal_array]
|
|
1397
1444
|
@ds.insert([BigDecimal.new('1.000000000000000000001'), nil, BigDecimal.new('1')].pg_array(:numeric))
|
|
1398
1445
|
@ds.count.should == 1
|
|
1399
|
-
|
|
1400
|
-
|
|
1446
|
+
rs = @ds.all
|
|
1447
|
+
if @jdbc || @native
|
|
1401
1448
|
rs.should == [{:n=>[BigDecimal.new('1.000000000000000000001'), nil, BigDecimal.new('1')]}]
|
|
1449
|
+
end
|
|
1450
|
+
if @native
|
|
1402
1451
|
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1403
1452
|
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1404
1453
|
@ds.delete
|
|
1405
1454
|
@ds.insert(rs.first)
|
|
1406
1455
|
@ds.all.should == rs
|
|
1456
|
+
end
|
|
1407
1457
|
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1458
|
+
@ds.delete
|
|
1459
|
+
@ds.insert([[BigDecimal.new('1.0000000000000000000000000000001'), nil], [nil, BigDecimal.new('1')]].pg_array(:numeric))
|
|
1460
|
+
rs = @ds.all
|
|
1461
|
+
if @jdbc || @native
|
|
1411
1462
|
rs.should == [{:n=>[[BigDecimal.new('1.0000000000000000000000000000001'), nil], [nil, BigDecimal.new('1')]]}]
|
|
1463
|
+
end
|
|
1464
|
+
if @native
|
|
1412
1465
|
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1413
1466
|
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1414
1467
|
@ds.delete
|
|
@@ -1423,21 +1476,75 @@ describe 'PostgreSQL array handling' do
|
|
|
1423
1476
|
column :vc, 'varchar[]'
|
|
1424
1477
|
column :t, 'text[]'
|
|
1425
1478
|
end
|
|
1479
|
+
@tp.call.should == [:string_array, :string_array, :string_array]
|
|
1426
1480
|
@ds.insert(['a', nil, 'NULL', 'b"\'c'].pg_array('char(4)'), ['a', nil, 'NULL', 'b"\'c'].pg_array(:varchar), ['a', nil, 'NULL', 'b"\'c'].pg_array(:text))
|
|
1427
1481
|
@ds.count.should == 1
|
|
1428
|
-
|
|
1429
|
-
|
|
1482
|
+
rs = @ds.all
|
|
1483
|
+
if @jdbc || @native
|
|
1430
1484
|
rs.should == [{:c=>['a ', nil, 'NULL', 'b"\'c'], :vc=>['a', nil, 'NULL', 'b"\'c'], :t=>['a', nil, 'NULL', 'b"\'c']}]
|
|
1485
|
+
end
|
|
1486
|
+
if @native
|
|
1431
1487
|
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1432
1488
|
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1433
1489
|
@ds.delete
|
|
1434
1490
|
@ds.insert(rs.first)
|
|
1435
1491
|
@ds.all.should == rs
|
|
1492
|
+
end
|
|
1436
1493
|
|
|
1494
|
+
@ds.delete
|
|
1495
|
+
@ds.insert([[['a'], [nil]], [['NULL'], ['b"\'c']]].pg_array('char(4)'), [[['a'], ['']], [['NULL'], ['b"\'c']]].pg_array(:varchar), [[['a'], [nil]], [['NULL'], ['b"\'c']]].pg_array(:text))
|
|
1496
|
+
rs = @ds.all
|
|
1497
|
+
if @jdbc || @native
|
|
1498
|
+
rs.should == [{:c=>[[['a '], [nil]], [['NULL'], ['b"\'c']]], :vc=>[[['a'], ['']], [['NULL'], ['b"\'c']]], :t=>[[['a'], [nil]], [['NULL'], ['b"\'c']]]}]
|
|
1499
|
+
end
|
|
1500
|
+
if @native
|
|
1501
|
+
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1502
|
+
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1437
1503
|
@ds.delete
|
|
1438
|
-
@ds.insert(
|
|
1504
|
+
@ds.insert(rs.first)
|
|
1505
|
+
@ds.all.should == rs
|
|
1506
|
+
end
|
|
1507
|
+
end
|
|
1508
|
+
|
|
1509
|
+
specify 'insert and retrieve arrays of other types' do
|
|
1510
|
+
@db.create_table!(:items) do
|
|
1511
|
+
column :b, 'bool[]'
|
|
1512
|
+
column :d, 'date[]'
|
|
1513
|
+
column :t, 'time[]'
|
|
1514
|
+
column :ts, 'timestamp[]'
|
|
1515
|
+
column :tstz, 'timestamptz[]'
|
|
1516
|
+
end
|
|
1517
|
+
@tp.call.should == [:boolean_array, :date_array, :time_array, :datetime_array, :datetime_timezone_array]
|
|
1518
|
+
|
|
1519
|
+
d = Date.today
|
|
1520
|
+
t = Sequel::SQLTime.create(10, 20, 30)
|
|
1521
|
+
ts = Time.local(2011, 1, 2, 3, 4, 5)
|
|
1522
|
+
|
|
1523
|
+
@ds.insert([true, false].pg_array(:bool), [d, nil].pg_array(:date), [t, nil].pg_array(:time), [ts, nil].pg_array(:timestamp), [ts, nil].pg_array(:timestamptz))
|
|
1524
|
+
@ds.count.should == 1
|
|
1525
|
+
rs = @ds.all
|
|
1526
|
+
if @jdbc || @native
|
|
1527
|
+
rs.should == [{:b=>[true, false], :d=>[d, nil], :t=>[t, nil], :ts=>[ts, nil], :tstz=>[ts, nil]}]
|
|
1528
|
+
end
|
|
1529
|
+
if @native
|
|
1530
|
+
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1531
|
+
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1532
|
+
@ds.delete
|
|
1533
|
+
@ds.insert(rs.first)
|
|
1534
|
+
@ds.all.should == rs
|
|
1535
|
+
end
|
|
1536
|
+
|
|
1537
|
+
@db.create_table!(:items) do
|
|
1538
|
+
column :ba, 'bytea[]'
|
|
1539
|
+
column :tz, 'timetz[]'
|
|
1540
|
+
column :o, 'oid[]'
|
|
1541
|
+
end
|
|
1542
|
+
@tp.call.should == [:blob_array, :time_timezone_array, :integer_array]
|
|
1543
|
+
@ds.insert( [Sequel.blob("a\0"), nil].pg_array(:bytea), [t, nil].pg_array(:timetz), [1, 2, 3].pg_array(:oid))
|
|
1544
|
+
@ds.count.should == 1
|
|
1545
|
+
if @native
|
|
1439
1546
|
rs = @ds.all
|
|
1440
|
-
rs.should == [{:
|
|
1547
|
+
rs.should == [{:ba=>[Sequel.blob("a\0"), nil], :tz=>[t, nil], :o=>[1, 2, 3]}]
|
|
1441
1548
|
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1442
1549
|
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1443
1550
|
@ds.delete
|
|
@@ -1463,6 +1570,42 @@ describe 'PostgreSQL array handling' do
|
|
|
1463
1570
|
@ds.get(:i).should == a
|
|
1464
1571
|
@ds.filter(:i=>:$i).call(:first, :i=>a).should == {:i=>a}
|
|
1465
1572
|
@ds.filter(:i=>:$i).call(:first, :i=>['', nil, nil, 'a']).should == nil
|
|
1573
|
+
|
|
1574
|
+
@db.create_table!(:items) do
|
|
1575
|
+
column :i, 'date[]'
|
|
1576
|
+
end
|
|
1577
|
+
a = [Date.today]
|
|
1578
|
+
@ds.call(:insert, {:i=>:$i}, :i=>a.pg_array('date'))
|
|
1579
|
+
@ds.get(:i).should == a
|
|
1580
|
+
@ds.filter(:i=>:$i).call(:first, :i=>a).should == {:i=>a}
|
|
1581
|
+
@ds.filter(:i=>:$i).call(:first, :i=>[Date.today-1].pg_array('date')).should == nil
|
|
1582
|
+
|
|
1583
|
+
@db.create_table!(:items) do
|
|
1584
|
+
column :i, 'timestamp[]'
|
|
1585
|
+
end
|
|
1586
|
+
a = [Time.local(2011, 1, 2, 3, 4, 5)]
|
|
1587
|
+
@ds.call(:insert, {:i=>:$i}, :i=>a.pg_array('timestamp'))
|
|
1588
|
+
@ds.get(:i).should == a
|
|
1589
|
+
@ds.filter(:i=>:$i).call(:first, :i=>a).should == {:i=>a}
|
|
1590
|
+
@ds.filter(:i=>:$i).call(:first, :i=>[a.first-1].pg_array('timestamp')).should == nil
|
|
1591
|
+
|
|
1592
|
+
@db.create_table!(:items) do
|
|
1593
|
+
column :i, 'boolean[]'
|
|
1594
|
+
end
|
|
1595
|
+
a = [true, false]
|
|
1596
|
+
@ds.call(:insert, {:i=>:$i}, :i=>a.pg_array('boolean'))
|
|
1597
|
+
@ds.get(:i).should == a
|
|
1598
|
+
@ds.filter(:i=>:$i).call(:first, :i=>a).should == {:i=>a}
|
|
1599
|
+
@ds.filter(:i=>:$i).call(:first, :i=>[false, true].pg_array('boolean')).should == nil
|
|
1600
|
+
|
|
1601
|
+
@db.create_table!(:items) do
|
|
1602
|
+
column :i, 'bytea[]'
|
|
1603
|
+
end
|
|
1604
|
+
a = [Sequel.blob("a\0'\"")]
|
|
1605
|
+
@ds.call(:insert, {:i=>:$i}, :i=>a.pg_array('bytea'))
|
|
1606
|
+
@ds.get(:i).should == a
|
|
1607
|
+
@ds.filter(:i=>:$i).call(:first, :i=>a).should == {:i=>a}
|
|
1608
|
+
@ds.filter(:i=>:$i).call(:first, :i=>[Sequel.blob("b\0")].pg_array('bytea')).should == nil
|
|
1466
1609
|
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
1467
1610
|
|
|
1468
1611
|
specify 'with models' do
|
|
@@ -1541,9 +1684,8 @@ end
|
|
|
1541
1684
|
|
|
1542
1685
|
describe 'PostgreSQL hstore handling' do
|
|
1543
1686
|
before(:all) do
|
|
1544
|
-
Sequel.extension :pg_hstore
|
|
1545
1687
|
@db = POSTGRES_DB
|
|
1546
|
-
@db.
|
|
1688
|
+
@db.extension :pg_hstore
|
|
1547
1689
|
@ds = @db[:items]
|
|
1548
1690
|
@h = {'a'=>'b', 'c'=>nil, 'd'=>'NULL', 'e'=>'\\\\" \\\' ,=>'}
|
|
1549
1691
|
@native = POSTGRES_DB.adapter_scheme == :postgres
|
|
@@ -1586,20 +1728,104 @@ describe 'PostgreSQL hstore handling' do
|
|
|
1586
1728
|
@ds.filter(:i=>:$i).call(:first, :i=>{}).should == nil
|
|
1587
1729
|
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
1588
1730
|
|
|
1589
|
-
specify 'with models' do
|
|
1731
|
+
specify 'with models and associations' do
|
|
1590
1732
|
@db.create_table!(:items) do
|
|
1591
1733
|
primary_key :id
|
|
1592
1734
|
column :h, :hstore
|
|
1593
1735
|
end
|
|
1594
|
-
c = Class.new(Sequel::Model(@db[:items]))
|
|
1736
|
+
c = Class.new(Sequel::Model(@db[:items])) do
|
|
1737
|
+
def self.name
|
|
1738
|
+
'Item'
|
|
1739
|
+
end
|
|
1740
|
+
unrestrict_primary_key
|
|
1741
|
+
def item_id
|
|
1742
|
+
h['item_id'].to_i if h
|
|
1743
|
+
end
|
|
1744
|
+
def left_item_id
|
|
1745
|
+
h['left_item_id'].to_i if h
|
|
1746
|
+
end
|
|
1747
|
+
end
|
|
1748
|
+
Sequel.extension :pg_hstore_ops
|
|
1749
|
+
c.plugin :many_through_many
|
|
1595
1750
|
c.plugin :typecast_on_load, :h unless @native
|
|
1596
|
-
|
|
1751
|
+
|
|
1752
|
+
h = {'item_id'=>"2", 'left_item_id'=>"1"}
|
|
1753
|
+
o2 = c.create(:id=>2)
|
|
1754
|
+
o = c.create(:id=>1, :h=>h)
|
|
1755
|
+
o.h.should == h
|
|
1756
|
+
|
|
1757
|
+
c.many_to_one :item, :class=>c, :key_column=>Sequel.cast(:h.hstore['item_id'], Integer)
|
|
1758
|
+
c.one_to_many :items, :class=>c, :key=>Sequel.cast(:h.hstore['item_id'], Integer), :key_method=>:item_id
|
|
1759
|
+
c.many_to_many :related_items, :class=>c, :join_table=>:items___i, :left_key=>Sequel.cast(:h.hstore['left_item_id'], Integer), :right_key=>Sequel.cast(:h.hstore['item_id'], Integer)
|
|
1760
|
+
|
|
1761
|
+
c.many_to_one :other_item, :class=>c, :key=>:id, :primary_key_method=>:item_id, :primary_key=>Sequel.cast(:h.hstore['item_id'], Integer)
|
|
1762
|
+
c.one_to_many :other_items, :class=>c, :primary_key=>:item_id, :key=>:id, :primary_key_column=>Sequel.cast(:h.hstore['item_id'], Integer)
|
|
1763
|
+
c.many_to_many :other_related_items, :class=>c, :join_table=>:items___i, :left_key=>:id, :right_key=>:id,
|
|
1764
|
+
:left_primary_key_column=>Sequel.cast(:h.hstore['left_item_id'], Integer),
|
|
1765
|
+
:left_primary_key=>:left_item_id,
|
|
1766
|
+
:right_primary_key=>Sequel.cast(:h.hstore['left_item_id'], Integer),
|
|
1767
|
+
:right_primary_key_method=>:left_item_id
|
|
1768
|
+
|
|
1769
|
+
c.many_through_many :mtm_items, [
|
|
1770
|
+
[:items, Sequel.cast(:h.hstore['item_id'], Integer), Sequel.cast(:h.hstore['left_item_id'], Integer)],
|
|
1771
|
+
[:items, Sequel.cast(:h.hstore['left_item_id'], Integer), Sequel.cast(:h.hstore['left_item_id'], Integer)]
|
|
1772
|
+
],
|
|
1773
|
+
:class=>c,
|
|
1774
|
+
:left_primary_key_column=>Sequel.cast(:h.hstore['item_id'], Integer),
|
|
1775
|
+
:left_primary_key=>:item_id,
|
|
1776
|
+
:right_primary_key=>Sequel.cast(:h.hstore['left_item_id'], Integer),
|
|
1777
|
+
:right_primary_key_method=>:left_item_id
|
|
1778
|
+
|
|
1779
|
+
# Lazily Loading
|
|
1780
|
+
o.item.should == o2
|
|
1781
|
+
o2.items.should == [o]
|
|
1782
|
+
o.related_items.should == [o2]
|
|
1783
|
+
o2.other_item.should == o
|
|
1784
|
+
o.other_items.should == [o2]
|
|
1785
|
+
o.other_related_items.should == [o]
|
|
1786
|
+
o.mtm_items.should == [o]
|
|
1787
|
+
|
|
1788
|
+
# Eager Loading via eager
|
|
1789
|
+
os = c.eager(:item, :related_items, :other_items, :other_related_items, :mtm_items).where(:id=>1).all.first
|
|
1790
|
+
os.item.should == o2
|
|
1791
|
+
os.related_items.should == [o2]
|
|
1792
|
+
os.other_items.should == [o2]
|
|
1793
|
+
os.other_related_items.should == [o]
|
|
1794
|
+
os.mtm_items.should == [o]
|
|
1795
|
+
os = c.eager(:items, :other_item).where(:id=>2).all.first
|
|
1796
|
+
os.items.should == [o]
|
|
1797
|
+
os.other_item.should == o
|
|
1798
|
+
|
|
1799
|
+
# Eager Loading via eager_graph
|
|
1800
|
+
c.eager_graph(:item).where(:items__id=>1).all.first.item.should == o2
|
|
1801
|
+
c.eager_graph(:items).where(:items__id=>2).all.first.items.should == [o]
|
|
1802
|
+
c.eager_graph(:related_items).where(:items__id=>1).all.first.related_items.should == [o2]
|
|
1803
|
+
c.eager_graph(:other_item).where(:items__id=>2).all.first.other_item.should == o
|
|
1804
|
+
c.eager_graph(:other_items).where(:items__id=>1).all.first.other_items.should == [o2]
|
|
1805
|
+
c.eager_graph(:other_related_items).where(:items__id=>1).all.first.other_related_items.should == [o]
|
|
1806
|
+
c.eager_graph(:mtm_items).where(:items__id=>1).all.first.mtm_items.should == [o]
|
|
1807
|
+
|
|
1808
|
+
# Filter By Associations - Model Instances
|
|
1809
|
+
c.filter(:item=>o2).all.should == [o]
|
|
1810
|
+
c.filter(:items=>o).all.should == [o2]
|
|
1811
|
+
c.filter(:related_items=>o2).all.should == [o]
|
|
1812
|
+
c.filter(:other_item=>o).all.should == [o2]
|
|
1813
|
+
c.filter(:other_items=>o2).all.should == [o]
|
|
1814
|
+
c.filter(:other_related_items=>o).all.should == [o]
|
|
1815
|
+
c.filter(:mtm_items=>o).all.should == [o]
|
|
1816
|
+
|
|
1817
|
+
# Filter By Associations - Model Datasets
|
|
1818
|
+
c.filter(:item=>c.filter(:id=>o2.id)).all.should == [o]
|
|
1819
|
+
c.filter(:items=>c.filter(:id=>o.id)).all.should == [o2]
|
|
1820
|
+
c.filter(:related_items=>c.filter(:id=>o2.id)).all.should == [o]
|
|
1821
|
+
c.filter(:other_item=>c.filter(:id=>o.id)).all.should == [o2]
|
|
1822
|
+
c.filter(:other_items=>c.filter(:id=>o2.id)).all.should == [o]
|
|
1823
|
+
c.filter(:other_related_items=>c.filter(:id=>o.id)).all.should == [o]
|
|
1824
|
+
c.filter(:mtm_items=>c.filter(:id=>o.id)).all.should == [o]
|
|
1597
1825
|
end
|
|
1598
1826
|
|
|
1599
1827
|
specify 'operations/functions with pg_hstore_ops' do
|
|
1600
|
-
Sequel.extension :pg_hstore_ops
|
|
1601
|
-
Sequel.extension :pg_array
|
|
1602
|
-
Sequel.extension :pg_array_ops
|
|
1828
|
+
Sequel.extension :pg_hstore_ops, :pg_array, :pg_array_ops
|
|
1603
1829
|
@db.create_table!(:items){hstore :h1; hstore :h2; hstore :h3; String :t}
|
|
1604
1830
|
@ds.insert({'a'=>'b', 'c'=>nil}.hstore, {'a'=>'b'}.hstore, {'d'=>'e'}.hstore)
|
|
1605
1831
|
h1 = :h1.hstore
|
|
@@ -1684,9 +1910,8 @@ end if POSTGRES_DB.type_supported?(:hstore)
|
|
|
1684
1910
|
|
|
1685
1911
|
describe 'PostgreSQL json type' do
|
|
1686
1912
|
before(:all) do
|
|
1687
|
-
Sequel.extension :pg_json
|
|
1688
1913
|
@db = POSTGRES_DB
|
|
1689
|
-
@db.
|
|
1914
|
+
@db.extension :pg_array, :pg_json
|
|
1690
1915
|
@ds = @db[:items]
|
|
1691
1916
|
@a = [1, 2, {'a'=>'b'}, 3.0]
|
|
1692
1917
|
@h = {'a'=>'b', '1'=>[3, 4, 5]}
|
|
@@ -1728,6 +1953,24 @@ describe 'PostgreSQL json type' do
|
|
|
1728
1953
|
end
|
|
1729
1954
|
end
|
|
1730
1955
|
|
|
1956
|
+
specify 'insert and retrieve json[] values' do
|
|
1957
|
+
@db.create_table!(:items){column :j, 'json[]'}
|
|
1958
|
+
j = [{'a'=>1}.pg_json, ['b', 2].pg_json].pg_array
|
|
1959
|
+
@ds.insert(j)
|
|
1960
|
+
@ds.count.should == 1
|
|
1961
|
+
if @native
|
|
1962
|
+
rs = @ds.all
|
|
1963
|
+
v = rs.first[:j]
|
|
1964
|
+
v.should_not be_a_kind_of(Array)
|
|
1965
|
+
v.to_a.should be_a_kind_of(Array)
|
|
1966
|
+
v.should == j
|
|
1967
|
+
v.to_a.should == j
|
|
1968
|
+
@ds.delete
|
|
1969
|
+
@ds.insert(rs.first)
|
|
1970
|
+
@ds.all.should == rs
|
|
1971
|
+
end
|
|
1972
|
+
end
|
|
1973
|
+
|
|
1731
1974
|
specify 'use json in bound variables' do
|
|
1732
1975
|
@db.create_table!(:items){json :i}
|
|
1733
1976
|
@ds.call(:insert, {:i=>@h.pg_json}, {:i=>:$i})
|
|
@@ -1740,6 +1983,13 @@ describe 'PostgreSQL json type' do
|
|
|
1740
1983
|
@ds.get(:i).should == @a
|
|
1741
1984
|
@ds.filter(:i.cast(String)=>:$i).call(:first, :i=>@a.pg_json).should == {:i=>@a}
|
|
1742
1985
|
@ds.filter(:i.cast(String)=>:$i).call(:first, :i=>[].pg_json).should == nil
|
|
1986
|
+
|
|
1987
|
+
@db.create_table!(:items){column :i, 'json[]'}
|
|
1988
|
+
j = [{'a'=>1}.pg_json, ['b', 2].pg_json].pg_array(:text)
|
|
1989
|
+
@ds.call(:insert, {:i=>j}, {:i=>:$i})
|
|
1990
|
+
@ds.get(:i).should == j
|
|
1991
|
+
@ds.filter(:i.cast('text[]')=>:$i).call(:first, :i=>j).should == {:i=>j}
|
|
1992
|
+
@ds.filter(:i.cast('text[]')=>:$i).call(:first, :i=>[].pg_array).should == nil
|
|
1743
1993
|
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
1744
1994
|
|
|
1745
1995
|
specify 'with models' do
|
|
@@ -1758,9 +2008,8 @@ describe 'PostgreSQL inet/cidr types' do
|
|
|
1758
2008
|
ipv6_broken = (IPAddr.new('::1'); false) rescue true
|
|
1759
2009
|
|
|
1760
2010
|
before(:all) do
|
|
1761
|
-
Sequel.extension :pg_inet
|
|
1762
2011
|
@db = POSTGRES_DB
|
|
1763
|
-
@db.
|
|
2012
|
+
@db.extension :pg_array, :pg_inet
|
|
1764
2013
|
@ds = @db[:items]
|
|
1765
2014
|
@v4 = '127.0.0.1'
|
|
1766
2015
|
@v4nm = '127.0.0.0/8'
|
|
@@ -1811,6 +2060,24 @@ describe 'PostgreSQL inet/cidr types' do
|
|
|
1811
2060
|
end
|
|
1812
2061
|
end
|
|
1813
2062
|
|
|
2063
|
+
specify 'insert and retrieve inet/cidr/macaddr array values' do
|
|
2064
|
+
@db.create_table!(:items){column :i, 'inet[]'; column :c, 'cidr[]'; column :m, 'macaddr[]'}
|
|
2065
|
+
@ds.insert([@ipv4].pg_array('inet'), [@ipv4nm].pg_array('cidr'), ['12:34:56:78:90:ab'].pg_array('macaddr'))
|
|
2066
|
+
@ds.count.should == 1
|
|
2067
|
+
if @native
|
|
2068
|
+
rs = @ds.all
|
|
2069
|
+
rs.first.values.all?{|c| c.is_a?(Sequel::Postgres::PGArray)}.should be_true
|
|
2070
|
+
rs.first[:i].first.should == @ipv4
|
|
2071
|
+
rs.first[:c].first.should == @ipv4nm
|
|
2072
|
+
rs.first[:m].first.should == '12:34:56:78:90:ab'
|
|
2073
|
+
rs.first[:i].first.should be_a_kind_of(IPAddr)
|
|
2074
|
+
rs.first[:c].first.should be_a_kind_of(IPAddr)
|
|
2075
|
+
@ds.delete
|
|
2076
|
+
@ds.insert(rs.first)
|
|
2077
|
+
@ds.all.should == rs
|
|
2078
|
+
end
|
|
2079
|
+
end
|
|
2080
|
+
|
|
1814
2081
|
specify 'use ipaddr in bound variables' do
|
|
1815
2082
|
@db.create_table!(:items){inet :i; cidr :c}
|
|
1816
2083
|
|
|
@@ -1829,6 +2096,12 @@ describe 'PostgreSQL inet/cidr types' do
|
|
|
1829
2096
|
@ds.filter(:i=>:$i, :c=>:$c).call(:first, :i=>@ipv4, :c=>@ipv4nm).should == nil
|
|
1830
2097
|
@ds.filter(:i=>:$i, :c=>:$c).call(:delete, :i=>@ipv6, :c=>@ipv6nm).should == 1
|
|
1831
2098
|
end
|
|
2099
|
+
|
|
2100
|
+
@db.create_table!(:items){column :i, 'inet[]'; column :c, 'cidr[]'; column :m, 'macaddr[]'}
|
|
2101
|
+
@ds.call(:insert, {:i=>[@ipv4], :c=>[@ipv4nm], :m=>['12:34:56:78:90:ab']}, {:i=>:$i, :c=>:$c, :m=>:$m})
|
|
2102
|
+
@ds.filter(:i=>:$i, :c=>:$c, :m=>:$m).call(:first, :i=>[@ipv4], :c=>[@ipv4nm], :m=>['12:34:56:78:90:ab']).should == {:i=>[@ipv4], :c=>[@ipv4nm], :m=>['12:34:56:78:90:ab']}
|
|
2103
|
+
@ds.filter(:i=>:$i, :c=>:$c, :m=>:$m).call(:first, :i=>[], :c=>[], :m=>[]).should == nil
|
|
2104
|
+
@ds.filter(:i=>:$i, :c=>:$c, :m=>:$m).call(:delete, :i=>[@ipv4], :c=>[@ipv4nm], :m=>['12:34:56:78:90:ab']).should == 1
|
|
1832
2105
|
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
1833
2106
|
|
|
1834
2107
|
specify 'with models' do
|
|
@@ -1844,4 +2117,275 @@ describe 'PostgreSQL inet/cidr types' do
|
|
|
1844
2117
|
c.create(:i=>@ipv6, :c=>@ipv6nm).values.values_at(:i, :c).should == [@ipv6, @ipv6nm]
|
|
1845
2118
|
end
|
|
1846
2119
|
end
|
|
2120
|
+
end
|
|
2121
|
+
|
|
2122
|
+
describe 'PostgreSQL range types' do
|
|
2123
|
+
before(:all) do
|
|
2124
|
+
@db = POSTGRES_DB
|
|
2125
|
+
@db.extension :pg_array, :pg_range
|
|
2126
|
+
@ds = @db[:items]
|
|
2127
|
+
@map = {:i4=>'int4range', :i8=>'int8range', :n=>'numrange', :d=>'daterange', :t=>'tsrange', :tz=>'tstzrange'}
|
|
2128
|
+
@r = {:i4=>1...2, :i8=>2...3, :n=>BigDecimal.new('1.0')..BigDecimal.new('2.0'), :d=>Date.today...(Date.today+1), :t=>Time.local(2011, 1)..Time.local(2011, 2), :tz=>Time.local(2011, 1)..Time.local(2011, 2)}
|
|
2129
|
+
@ra = {}
|
|
2130
|
+
@pgr = {}
|
|
2131
|
+
@pgra = {}
|
|
2132
|
+
@r.each{|k, v| @ra[k] = [v].pg_array(@map[k])}
|
|
2133
|
+
@r.each{|k, v| @pgr[k] = v.pg_range}
|
|
2134
|
+
@r.each{|k, v| @pgra[k] = [v.pg_range].pg_array(@map[k])}
|
|
2135
|
+
@native = POSTGRES_DB.adapter_scheme == :postgres
|
|
2136
|
+
end
|
|
2137
|
+
after do
|
|
2138
|
+
@db.drop_table?(:items)
|
|
2139
|
+
end
|
|
2140
|
+
|
|
2141
|
+
specify 'insert and retrieve range type values' do
|
|
2142
|
+
@db.create_table!(:items){int4range :i4; int8range :i8; numrange :n; daterange :d; tsrange :t; tstzrange :tz}
|
|
2143
|
+
[@r, @pgr].each do |input|
|
|
2144
|
+
h = {}
|
|
2145
|
+
input.each{|k, v| h[k] = Sequel.cast(v, @map[k])}
|
|
2146
|
+
@ds.insert(h)
|
|
2147
|
+
@ds.count.should == 1
|
|
2148
|
+
if @native
|
|
2149
|
+
rs = @ds.all
|
|
2150
|
+
rs.first.each do |k, v|
|
|
2151
|
+
v.should_not be_a_kind_of(Range)
|
|
2152
|
+
v.to_range.should be_a_kind_of(Range)
|
|
2153
|
+
v.should == @r[k]
|
|
2154
|
+
v.to_range.should == @r[k]
|
|
2155
|
+
end
|
|
2156
|
+
@ds.delete
|
|
2157
|
+
@ds.insert(rs.first)
|
|
2158
|
+
@ds.all.should == rs
|
|
2159
|
+
end
|
|
2160
|
+
@ds.delete
|
|
2161
|
+
end
|
|
2162
|
+
end
|
|
2163
|
+
|
|
2164
|
+
specify 'insert and retrieve arrays of range type values' do
|
|
2165
|
+
@db.create_table!(:items){column :i4, 'int4range[]'; column :i8, 'int8range[]'; column :n, 'numrange[]'; column :d, 'daterange[]'; column :t, 'tsrange[]'; column :tz, 'tstzrange[]'}
|
|
2166
|
+
[@ra, @pgra].each do |input|
|
|
2167
|
+
@ds.insert(input)
|
|
2168
|
+
@ds.count.should == 1
|
|
2169
|
+
if @native
|
|
2170
|
+
rs = @ds.all
|
|
2171
|
+
rs.first.each do |k, v|
|
|
2172
|
+
v.should_not be_a_kind_of(Array)
|
|
2173
|
+
v.to_a.should be_a_kind_of(Array)
|
|
2174
|
+
v.first.should_not be_a_kind_of(Range)
|
|
2175
|
+
v.first.to_range.should be_a_kind_of(Range)
|
|
2176
|
+
v.should == @ra[k].to_a
|
|
2177
|
+
v.first.should == @r[k]
|
|
2178
|
+
end
|
|
2179
|
+
@ds.delete
|
|
2180
|
+
@ds.insert(rs.first)
|
|
2181
|
+
@ds.all.should == rs
|
|
2182
|
+
end
|
|
2183
|
+
@ds.delete
|
|
2184
|
+
end
|
|
2185
|
+
end
|
|
2186
|
+
|
|
2187
|
+
specify 'use range types in bound variables' do
|
|
2188
|
+
@db.create_table!(:items){int4range :i4; int8range :i8; numrange :n; daterange :d; tsrange :t; tstzrange :tz}
|
|
2189
|
+
h = {}
|
|
2190
|
+
@r.keys.each{|k| h[k] = :"$#{k}"}
|
|
2191
|
+
r2 = {}
|
|
2192
|
+
@r.each{|k, v| r2[k] = Range.new(v.begin, v.end+2)}
|
|
2193
|
+
@ds.call(:insert, @r, h)
|
|
2194
|
+
@ds.first.should == @r
|
|
2195
|
+
@ds.filter(h).call(:first, @r).should == @r
|
|
2196
|
+
@ds.filter(h).call(:first, @pgr).should == @r
|
|
2197
|
+
@ds.filter(h).call(:first, r2).should == nil
|
|
2198
|
+
@ds.filter(h).call(:delete, @r).should == 1
|
|
2199
|
+
|
|
2200
|
+
@db.create_table!(:items){column :i4, 'int4range[]'; column :i8, 'int8range[]'; column :n, 'numrange[]'; column :d, 'daterange[]'; column :t, 'tsrange[]'; column :tz, 'tstzrange[]'}
|
|
2201
|
+
@r.each{|k, v| r2[k] = [Range.new(v.begin, v.end+2)]}
|
|
2202
|
+
@ds.call(:insert, @ra, h)
|
|
2203
|
+
@ds.filter(h).call(:first, @ra).each{|k, v| v.should == @ra[k].to_a}
|
|
2204
|
+
@ds.filter(h).call(:first, @pgra).each{|k, v| v.should == @ra[k].to_a}
|
|
2205
|
+
@ds.filter(h).call(:first, r2).should == nil
|
|
2206
|
+
@ds.filter(h).call(:delete, @ra).should == 1
|
|
2207
|
+
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
2208
|
+
|
|
2209
|
+
specify 'with models' do
|
|
2210
|
+
@db.create_table!(:items){primary_key :id; int4range :i4; int8range :i8; numrange :n; daterange :d; tsrange :t; tstzrange :tz}
|
|
2211
|
+
c = Class.new(Sequel::Model(@db[:items]))
|
|
2212
|
+
c.plugin :typecast_on_load, :i4, :i8, :n, :d, :t, :tz unless @native
|
|
2213
|
+
v = c.create(@r).values
|
|
2214
|
+
v.delete(:id)
|
|
2215
|
+
v.should == @r
|
|
2216
|
+
|
|
2217
|
+
unless @db.adapter_scheme == :jdbc
|
|
2218
|
+
@db.create_table!(:items){primary_key :id; column :i4, 'int4range[]'; column :i8, 'int8range[]'; column :n, 'numrange[]'; column :d, 'daterange[]'; column :t, 'tsrange[]'; column :tz, 'tstzrange[]'}
|
|
2219
|
+
c = Class.new(Sequel::Model(@db[:items]))
|
|
2220
|
+
c.plugin :typecast_on_load, :i4, :i8, :n, :d, :t, :tz unless @native
|
|
2221
|
+
v = c.create(@ra).values
|
|
2222
|
+
v.delete(:id)
|
|
2223
|
+
v.each{|k,v| v.should == @ra[k].to_a}
|
|
2224
|
+
end
|
|
2225
|
+
end
|
|
2226
|
+
|
|
2227
|
+
specify 'operations/functions with pg_range_ops' do
|
|
2228
|
+
Sequel.extension :pg_range_ops
|
|
2229
|
+
|
|
2230
|
+
@db.get((1..5).pg_range(:int4range).op.contains(2..4)).should be_true
|
|
2231
|
+
@db.get((1..5).pg_range(:int4range).op.contains(3..6)).should be_false
|
|
2232
|
+
@db.get((1..5).pg_range(:int4range).op.contains(0..6)).should be_false
|
|
2233
|
+
|
|
2234
|
+
@db.get((1..5).pg_range(:int4range).op.contained_by(0..6)).should be_true
|
|
2235
|
+
@db.get((1..5).pg_range(:int4range).op.contained_by(3..6)).should be_false
|
|
2236
|
+
@db.get((1..5).pg_range(:int4range).op.contained_by(2..4)).should be_false
|
|
2237
|
+
|
|
2238
|
+
@db.get((1..5).pg_range(:int4range).op.overlaps(5..6)).should be_true
|
|
2239
|
+
@db.get((1...5).pg_range(:int4range).op.overlaps(5..6)).should be_false
|
|
2240
|
+
|
|
2241
|
+
@db.get((1..5).pg_range(:int4range).op.left_of(6..10)).should be_true
|
|
2242
|
+
@db.get((1..5).pg_range(:int4range).op.left_of(5..10)).should be_false
|
|
2243
|
+
@db.get((1..5).pg_range(:int4range).op.left_of(-1..0)).should be_false
|
|
2244
|
+
@db.get((1..5).pg_range(:int4range).op.left_of(-1..3)).should be_false
|
|
2245
|
+
|
|
2246
|
+
@db.get((1..5).pg_range(:int4range).op.right_of(6..10)).should be_false
|
|
2247
|
+
@db.get((1..5).pg_range(:int4range).op.right_of(5..10)).should be_false
|
|
2248
|
+
@db.get((1..5).pg_range(:int4range).op.right_of(-1..0)).should be_true
|
|
2249
|
+
@db.get((1..5).pg_range(:int4range).op.right_of(-1..3)).should be_false
|
|
2250
|
+
|
|
2251
|
+
@db.get((1..5).pg_range(:int4range).op.starts_before(6..10)).should be_true
|
|
2252
|
+
@db.get((1..5).pg_range(:int4range).op.starts_before(5..10)).should be_true
|
|
2253
|
+
@db.get((1..5).pg_range(:int4range).op.starts_before(-1..0)).should be_false
|
|
2254
|
+
@db.get((1..5).pg_range(:int4range).op.starts_before(-1..3)).should be_false
|
|
2255
|
+
|
|
2256
|
+
@db.get((1..5).pg_range(:int4range).op.ends_after(6..10)).should be_false
|
|
2257
|
+
@db.get((1..5).pg_range(:int4range).op.ends_after(5..10)).should be_false
|
|
2258
|
+
@db.get((1..5).pg_range(:int4range).op.ends_after(-1..0)).should be_true
|
|
2259
|
+
@db.get((1..5).pg_range(:int4range).op.ends_after(-1..3)).should be_true
|
|
2260
|
+
|
|
2261
|
+
@db.get((1..5).pg_range(:int4range).op.adjacent_to(6..10)).should be_true
|
|
2262
|
+
@db.get((1...5).pg_range(:int4range).op.adjacent_to(6..10)).should be_false
|
|
2263
|
+
|
|
2264
|
+
@db.get(((1..5).pg_range(:int4range).op + (6..10)).adjacent_to(6..10)).should be_false
|
|
2265
|
+
@db.get(((1..5).pg_range(:int4range).op + (6..10)).adjacent_to(11..20)).should be_true
|
|
2266
|
+
|
|
2267
|
+
@db.get(((1..5).pg_range(:int4range).op * (2..6)).adjacent_to(6..10)).should be_true
|
|
2268
|
+
@db.get(((1..4).pg_range(:int4range).op * (2..6)).adjacent_to(6..10)).should be_false
|
|
2269
|
+
|
|
2270
|
+
@db.get(((1..5).pg_range(:int4range).op - (2..6)).adjacent_to(2..10)).should be_true
|
|
2271
|
+
@db.get(((0..4).pg_range(:int4range).op - (3..6)).adjacent_to(4..10)).should be_false
|
|
2272
|
+
|
|
2273
|
+
@db.get((0..4).pg_range(:int4range).op.lower).should == 0
|
|
2274
|
+
@db.get((0..4).pg_range(:int4range).op.upper).should == 5
|
|
2275
|
+
|
|
2276
|
+
@db.get((0..4).pg_range(:int4range).op.isempty).should be_false
|
|
2277
|
+
@db.get(Sequel::Postgres::PGRange.empty(:int4range).op.isempty).should be_true
|
|
2278
|
+
|
|
2279
|
+
@db.get((1..5).pg_range(:numrange).op.lower_inc).should be_true
|
|
2280
|
+
@db.get(Sequel::Postgres::PGRange.new(1, 5, :exclude_begin=>true, :db_type=>:numrange).op.lower_inc).should be_false
|
|
2281
|
+
|
|
2282
|
+
@db.get((1..5).pg_range(:numrange).op.upper_inc).should be_true
|
|
2283
|
+
@db.get((1...5).pg_range(:numrange).op.upper_inc).should be_false
|
|
2284
|
+
|
|
2285
|
+
@db.get(Sequel::Postgres::PGRange.new(1, 5, :db_type=>:int4range).op.lower_inf).should be_false
|
|
2286
|
+
@db.get(Sequel::Postgres::PGRange.new(nil, 5, :db_type=>:int4range).op.lower_inf).should be_true
|
|
2287
|
+
|
|
2288
|
+
@db.get(Sequel::Postgres::PGRange.new(1, 5, :db_type=>:int4range).op.upper_inf).should be_false
|
|
2289
|
+
@db.get(Sequel::Postgres::PGRange.new(1, nil, :db_type=>:int4range).op.upper_inf).should be_true
|
|
2290
|
+
end
|
|
1847
2291
|
end if POSTGRES_DB.server_version >= 90200
|
|
2292
|
+
|
|
2293
|
+
describe 'PostgreSQL interval types' do
|
|
2294
|
+
before(:all) do
|
|
2295
|
+
@db = POSTGRES_DB
|
|
2296
|
+
@db.extension :pg_array, :pg_interval
|
|
2297
|
+
@ds = @db[:items]
|
|
2298
|
+
@native = POSTGRES_DB.adapter_scheme == :postgres
|
|
2299
|
+
end
|
|
2300
|
+
after(:all) do
|
|
2301
|
+
Sequel::Postgres::PG_TYPES.delete(1186)
|
|
2302
|
+
end
|
|
2303
|
+
after do
|
|
2304
|
+
@db.drop_table?(:items)
|
|
2305
|
+
end
|
|
2306
|
+
|
|
2307
|
+
specify 'insert and retrieve interval values' do
|
|
2308
|
+
@db.create_table!(:items){interval :i}
|
|
2309
|
+
[
|
|
2310
|
+
['0', '00:00:00', 0, [[:seconds, 0]]],
|
|
2311
|
+
['1 microsecond', '00:00:00.000001', 0.000001, [[:seconds, 0.000001]]],
|
|
2312
|
+
['1 millisecond', '00:00:00.001', 0.001, [[:seconds, 0.001]]],
|
|
2313
|
+
['1 second', '00:00:01', 1, [[:seconds, 1]]],
|
|
2314
|
+
['1 minute', '00:01:00', 60, [[:seconds, 60]]],
|
|
2315
|
+
['1 hour', '01:00:00', 3600, [[:seconds, 3600]]],
|
|
2316
|
+
['1 day', '1 day', 86400, [[:days, 1]]],
|
|
2317
|
+
['1 week', '7 days', 86400*7, [[:days, 7]]],
|
|
2318
|
+
['1 month', '1 mon', 86400*30, [[:months, 1]]],
|
|
2319
|
+
['1 year', '1 year', 31557600, [[:years, 1]]],
|
|
2320
|
+
['1 decade', '10 years', 31557600*10, [[:years, 10]]],
|
|
2321
|
+
['1 century', '100 years', 31557600*100, [[:years, 100]]],
|
|
2322
|
+
['1 millennium', '1000 years', 31557600*1000, [[:years, 1000]]],
|
|
2323
|
+
['1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds', '1 year 2 mons 25 days 05:06:07', 31557600 + 2*86400*30 + 3*86400*7 + 4*86400 + 5*3600 + 6*60 + 7, [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]]],
|
|
2324
|
+
['-1 year +2 months -3 weeks +4 days -5 hours +6 minutes -7 seconds', '-10 mons -17 days -04:54:07', -10*86400*30 - 3*86400*7 + 4*86400 - 5*3600 + 6*60 - 7, [[:months, -10], [:days, -17], [:seconds, -17647]]],
|
|
2325
|
+
['+2 years -1 months +3 weeks -4 days +5 hours -6 minutes +7 seconds', '1 year 11 mons 17 days 04:54:07', 31557600 + 11*86400*30 + 3*86400*7 - 4*86400 + 5*3600 - 6*60 + 7, [[:years, 1], [:months, 11], [:days, 17], [:seconds, 17647]]],
|
|
2326
|
+
].each do |instr, outstr, value, parts|
|
|
2327
|
+
@ds.insert(instr)
|
|
2328
|
+
@ds.count.should == 1
|
|
2329
|
+
if @native
|
|
2330
|
+
@ds.get(Sequel.cast(:i, String)).should == outstr
|
|
2331
|
+
rs = @ds.all
|
|
2332
|
+
rs.first[:i].is_a?(ActiveSupport::Duration).should be_true
|
|
2333
|
+
rs.first[:i].should == ActiveSupport::Duration.new(value, parts)
|
|
2334
|
+
rs.first[:i].parts.sort_by{|k,v| k.to_s}.should == parts.sort_by{|k,v| k.to_s}
|
|
2335
|
+
@ds.delete
|
|
2336
|
+
@ds.insert(rs.first)
|
|
2337
|
+
@ds.all.should == rs
|
|
2338
|
+
end
|
|
2339
|
+
@ds.delete
|
|
2340
|
+
end
|
|
2341
|
+
end
|
|
2342
|
+
|
|
2343
|
+
specify 'insert and retrieve interval array values' do
|
|
2344
|
+
@db.create_table!(:items){column :i, 'interval[]'}
|
|
2345
|
+
@ds.insert(['1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds'].pg_array('interval'))
|
|
2346
|
+
@ds.count.should == 1
|
|
2347
|
+
if @native
|
|
2348
|
+
rs = @ds.all
|
|
2349
|
+
rs.first[:i].is_a?(Sequel::Postgres::PGArray).should be_true
|
|
2350
|
+
rs.first[:i].first.is_a?(ActiveSupport::Duration).should be_true
|
|
2351
|
+
rs.first[:i].first.should == ActiveSupport::Duration.new(31557600 + 2*86400*30 + 3*86400*7 + 4*86400 + 5*3600 + 6*60 + 7, [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]])
|
|
2352
|
+
rs.first[:i].first.parts.sort_by{|k,v| k.to_s}.should == [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]].sort_by{|k,v| k.to_s}
|
|
2353
|
+
@ds.delete
|
|
2354
|
+
@ds.insert(rs.first)
|
|
2355
|
+
@ds.all.should == rs
|
|
2356
|
+
end
|
|
2357
|
+
end
|
|
2358
|
+
|
|
2359
|
+
specify 'use intervals in bound variables' do
|
|
2360
|
+
@db.create_table!(:items){interval :i}
|
|
2361
|
+
@ds.insert('1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds')
|
|
2362
|
+
d = @ds.get(:i)
|
|
2363
|
+
@ds.delete
|
|
2364
|
+
|
|
2365
|
+
@ds.call(:insert, {:i=>d}, {:i=>:$i})
|
|
2366
|
+
@ds.get(:i).should == d
|
|
2367
|
+
@ds.filter(:i=>:$i).call(:first, :i=>d).should == {:i=>d}
|
|
2368
|
+
@ds.filter(:i=>:$i).call(:first, :i=>'0').should == nil
|
|
2369
|
+
@ds.filter(:i=>:$i).call(:delete, :i=>d).should == 1
|
|
2370
|
+
|
|
2371
|
+
@db.create_table!(:items){column :i, 'interval[]'}
|
|
2372
|
+
@ds.call(:insert, {:i=>[d]}, {:i=>:$i})
|
|
2373
|
+
@ds.filter(:i=>:$i).call(:first, :i=>[d]).should == {:i=>[d]}
|
|
2374
|
+
@ds.filter(:i=>:$i).call(:first, :i=>[]).should == nil
|
|
2375
|
+
@ds.filter(:i=>:$i).call(:delete, :i=>[d]).should == 1
|
|
2376
|
+
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
2377
|
+
|
|
2378
|
+
specify 'with models' do
|
|
2379
|
+
@db.create_table!(:items) do
|
|
2380
|
+
primary_key :id
|
|
2381
|
+
interval :i
|
|
2382
|
+
end
|
|
2383
|
+
c = Class.new(Sequel::Model(@db[:items]))
|
|
2384
|
+
c.plugin :typecast_on_load, :i, :c unless @native
|
|
2385
|
+
v = c.create(:i=>'1 year 2 mons 25 days 05:06:07').i
|
|
2386
|
+
v.is_a?(ActiveSupport::Duration).should be_true
|
|
2387
|
+
v.should == ActiveSupport::Duration.new(31557600 + 2*86400*30 + 3*86400*7 + 4*86400 + 5*3600 + 6*60 + 7, [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]])
|
|
2388
|
+
v.parts.sort_by{|k,v| k.to_s}.should == [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]].sort_by{|k,v| k.to_s}
|
|
2389
|
+
end
|
|
2390
|
+
end if ((require 'active_support/duration'; require 'active_support/inflector'; require 'active_support/core_ext/string/inflections'; true) rescue false)
|
|
2391
|
+
|