sequel 3.36.1 → 3.37.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|