sequel 3.46.0 → 3.47.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +96 -0
- data/Rakefile +7 -1
- data/bin/sequel +6 -4
- data/doc/active_record.rdoc +1 -1
- data/doc/advanced_associations.rdoc +14 -35
- data/doc/association_basics.rdoc +66 -4
- data/doc/migration.rdoc +4 -0
- data/doc/opening_databases.rdoc +6 -0
- data/doc/postgresql.rdoc +302 -0
- data/doc/release_notes/3.47.0.txt +270 -0
- data/doc/security.rdoc +6 -0
- data/lib/sequel/adapters/ibmdb.rb +9 -9
- data/lib/sequel/adapters/jdbc.rb +22 -7
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -2
- data/lib/sequel/adapters/mock.rb +2 -0
- data/lib/sequel/adapters/postgres.rb +44 -13
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +2 -2
- data/lib/sequel/adapters/shared/postgres.rb +94 -55
- data/lib/sequel/adapters/shared/sqlite.rb +3 -1
- data/lib/sequel/adapters/sqlite.rb +2 -2
- data/lib/sequel/adapters/utils/pg_types.rb +1 -14
- data/lib/sequel/adapters/utils/split_alter_table.rb +3 -3
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/connecting.rb +2 -2
- data/lib/sequel/database/features.rb +5 -0
- data/lib/sequel/database/misc.rb +47 -5
- data/lib/sequel/database/query.rb +2 -2
- data/lib/sequel/dataset/actions.rb +4 -2
- data/lib/sequel/dataset/misc.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +1 -1
- data/lib/sequel/dataset/query.rb +8 -6
- data/lib/sequel/dataset/sql.rb +8 -6
- data/lib/sequel/extensions/constraint_validations.rb +5 -2
- data/lib/sequel/extensions/migration.rb +10 -8
- data/lib/sequel/extensions/pagination.rb +3 -0
- data/lib/sequel/extensions/pg_array.rb +85 -25
- data/lib/sequel/extensions/pg_hstore.rb +8 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +4 -1
- data/lib/sequel/extensions/pg_inet.rb +16 -13
- data/lib/sequel/extensions/pg_interval.rb +6 -2
- data/lib/sequel/extensions/pg_json.rb +18 -11
- data/lib/sequel/extensions/pg_range.rb +17 -2
- data/lib/sequel/extensions/pg_range_ops.rb +7 -5
- data/lib/sequel/extensions/pg_row.rb +29 -12
- data/lib/sequel/extensions/pretty_table.rb +3 -0
- data/lib/sequel/extensions/query.rb +3 -0
- data/lib/sequel/extensions/schema_caching.rb +2 -0
- data/lib/sequel/extensions/schema_dumper.rb +3 -1
- data/lib/sequel/extensions/select_remove.rb +3 -0
- data/lib/sequel/model.rb +8 -2
- data/lib/sequel/model/associations.rb +39 -27
- data/lib/sequel/model/base.rb +99 -38
- data/lib/sequel/model/plugins.rb +25 -0
- data/lib/sequel/plugins/association_autoreloading.rb +27 -22
- data/lib/sequel/plugins/association_dependencies.rb +1 -7
- data/lib/sequel/plugins/auto_validations.rb +110 -0
- data/lib/sequel/plugins/boolean_readers.rb +1 -6
- data/lib/sequel/plugins/caching.rb +6 -13
- data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
- data/lib/sequel/plugins/composition.rb +14 -7
- data/lib/sequel/plugins/constraint_validations.rb +2 -13
- data/lib/sequel/plugins/defaults_setter.rb +1 -6
- data/lib/sequel/plugins/dirty.rb +8 -0
- data/lib/sequel/plugins/error_splitter.rb +54 -0
- data/lib/sequel/plugins/force_encoding.rb +1 -5
- data/lib/sequel/plugins/hook_class_methods.rb +1 -6
- data/lib/sequel/plugins/input_transformer.rb +79 -0
- data/lib/sequel/plugins/instance_filters.rb +7 -1
- data/lib/sequel/plugins/instance_hooks.rb +7 -1
- data/lib/sequel/plugins/json_serializer.rb +5 -10
- data/lib/sequel/plugins/lazy_attributes.rb +20 -7
- data/lib/sequel/plugins/list.rb +1 -6
- data/lib/sequel/plugins/many_through_many.rb +1 -2
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +23 -39
- data/lib/sequel/plugins/optimistic_locking.rb +1 -5
- data/lib/sequel/plugins/pg_row.rb +4 -2
- data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -7
- data/lib/sequel/plugins/prepared_statements.rb +1 -5
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -11
- data/lib/sequel/plugins/rcte_tree.rb +2 -2
- data/lib/sequel/plugins/serialization.rb +11 -13
- data/lib/sequel/plugins/serialization_modification_detection.rb +13 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/static_cache.rb +67 -19
- data/lib/sequel/plugins/string_stripper.rb +7 -27
- data/lib/sequel/plugins/subclasses.rb +3 -5
- data/lib/sequel/plugins/tactical_eager_loading.rb +2 -2
- data/lib/sequel/plugins/timestamps.rb +2 -7
- data/lib/sequel/plugins/touch.rb +5 -8
- data/lib/sequel/plugins/tree.rb +1 -6
- data/lib/sequel/plugins/typecast_on_load.rb +1 -5
- data/lib/sequel/plugins/update_primary_key.rb +26 -14
- data/lib/sequel/plugins/validation_class_methods.rb +31 -16
- data/lib/sequel/plugins/validation_helpers.rb +50 -26
- data/lib/sequel/plugins/xml_serializer.rb +3 -6
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +131 -15
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/connection_pool_spec.rb +16 -17
- data/spec/core/database_spec.rb +111 -40
- data/spec/core/dataset_spec.rb +65 -74
- data/spec/core/expression_filters_spec.rb +6 -5
- data/spec/core/object_graph_spec.rb +0 -1
- data/spec/core/schema_spec.rb +23 -23
- data/spec/core/spec_helper.rb +5 -1
- data/spec/extensions/association_dependencies_spec.rb +1 -1
- data/spec/extensions/association_proxies_spec.rb +1 -1
- data/spec/extensions/auto_validations_spec.rb +90 -0
- data/spec/extensions/caching_spec.rb +6 -0
- data/spec/extensions/class_table_inheritance_spec.rb +8 -1
- data/spec/extensions/composition_spec.rb +12 -5
- data/spec/extensions/constraint_validations_spec.rb +4 -4
- data/spec/extensions/core_refinements_spec.rb +29 -79
- data/spec/extensions/dirty_spec.rb +14 -0
- data/spec/extensions/error_splitter_spec.rb +18 -0
- data/spec/extensions/identity_map_spec.rb +0 -1
- data/spec/extensions/input_transformer_spec.rb +54 -0
- data/spec/extensions/instance_filters_spec.rb +6 -0
- data/spec/extensions/instance_hooks_spec.rb +12 -1
- data/spec/extensions/json_serializer_spec.rb +0 -1
- data/spec/extensions/lazy_attributes_spec.rb +64 -55
- data/spec/extensions/looser_typecasting_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +3 -4
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +53 -15
- data/spec/extensions/migration_spec.rb +16 -0
- data/spec/extensions/null_dataset_spec.rb +1 -1
- data/spec/extensions/pg_array_spec.rb +48 -1
- data/spec/extensions/pg_hstore_ops_spec.rb +10 -2
- data/spec/extensions/pg_hstore_spec.rb +5 -0
- data/spec/extensions/pg_inet_spec.rb +5 -0
- data/spec/extensions/pg_interval_spec.rb +7 -3
- data/spec/extensions/pg_json_spec.rb +6 -1
- data/spec/extensions/pg_range_ops_spec.rb +4 -1
- data/spec/extensions/pg_range_spec.rb +5 -0
- data/spec/extensions/pg_row_plugin_spec.rb +13 -0
- data/spec/extensions/pg_row_spec.rb +28 -19
- data/spec/extensions/pg_typecast_on_load_spec.rb +6 -1
- data/spec/extensions/prepared_statements_associations_spec.rb +1 -1
- data/spec/extensions/query_literals_spec.rb +1 -1
- data/spec/extensions/rcte_tree_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +2 -2
- data/spec/extensions/serialization_modification_detection_spec.rb +8 -0
- data/spec/extensions/serialization_spec.rb +15 -1
- data/spec/extensions/sharding_spec.rb +1 -1
- data/spec/extensions/single_table_inheritance_spec.rb +1 -1
- data/spec/extensions/static_cache_spec.rb +59 -9
- data/spec/extensions/tactical_eager_loading_spec.rb +19 -4
- data/spec/extensions/update_primary_key_spec.rb +17 -1
- data/spec/extensions/validation_class_methods_spec.rb +25 -0
- data/spec/extensions/validation_helpers_spec.rb +59 -3
- data/spec/integration/associations_test.rb +5 -5
- data/spec/integration/eager_loader_test.rb +32 -63
- data/spec/integration/model_test.rb +2 -2
- data/spec/integration/plugin_test.rb +88 -56
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +1 -1
- data/spec/integration/timezone_test.rb +0 -1
- data/spec/integration/transaction_test.rb +0 -1
- data/spec/model/association_reflection_spec.rb +1 -1
- data/spec/model/associations_spec.rb +106 -84
- data/spec/model/base_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +8 -8
- data/spec/model/model_spec.rb +27 -9
- data/spec/model/plugins_spec.rb +71 -0
- data/spec/model/record_spec.rb +99 -13
- metadata +12 -2
@@ -45,17 +45,15 @@ module Sequel
|
|
45
45
|
Sequel.synchronize{_descendents}
|
46
46
|
end
|
47
47
|
|
48
|
+
Plugins.inherited_instance_variables(self, :@subclasses=>lambda{|v| []}, :@on_subclass=>nil)
|
49
|
+
|
48
50
|
# Add the subclass to this model's current subclasses,
|
49
51
|
# and initialize a new subclasses instance variable
|
50
52
|
# in the subclass.
|
51
53
|
def inherited(subclass)
|
52
54
|
super
|
53
55
|
Sequel.synchronize{subclasses << subclass}
|
54
|
-
|
55
|
-
if on_subclass
|
56
|
-
subclass.instance_variable_set(:@on_subclass, on_subclass)
|
57
|
-
on_subclass.call(subclass)
|
58
|
-
end
|
56
|
+
on_subclass.call(subclass) if on_subclass
|
59
57
|
end
|
60
58
|
|
61
59
|
private
|
@@ -42,9 +42,9 @@ module Sequel
|
|
42
42
|
# objects retrieved with the current object.
|
43
43
|
def load_associated_objects(opts, reload=false)
|
44
44
|
name = opts[:name]
|
45
|
-
if !associations.include?(name) && retrieved_by
|
45
|
+
if !associations.include?(name) && retrieved_by && !frozen?
|
46
46
|
begin
|
47
|
-
retrieved_by.send(:eager_load, retrieved_with, name=>{})
|
47
|
+
retrieved_by.send(:eager_load, retrieved_with.reject{|o| o.frozen?}, name=>{})
|
48
48
|
rescue Sequel::UndefinedAssociation
|
49
49
|
# This can happen if class table inheritance is used and the association
|
50
50
|
# is only defined in a subclass. This particular instance can use the
|
@@ -47,13 +47,8 @@ module Sequel
|
|
47
47
|
@create_timestamp_overwrite
|
48
48
|
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
super
|
53
|
-
[:@create_timestamp_field, :@update_timestamp_field, :@create_timestamp_overwrite, :@set_update_timestamp_on_create].each do |iv|
|
54
|
-
subclass.instance_variable_set(iv, instance_variable_get(iv))
|
55
|
-
end
|
56
|
-
end
|
50
|
+
Plugins.inherited_instance_variables(self, :@create_timestamp_field=>nil, :@update_timestamp_field=>nil,
|
51
|
+
:@create_timestamp_overwrite=>nil, :@set_update_timestamp_on_create=>nil)
|
57
52
|
|
58
53
|
# Whether to set the update timestamp to the create timestamp when creating
|
59
54
|
def set_update_timestamp_on_create?
|
data/lib/sequel/plugins/touch.rb
CHANGED
@@ -30,6 +30,10 @@ module Sequel
|
|
30
30
|
# The default column to update when touching
|
31
31
|
TOUCH_COLUMN_DEFAULT = :updated_at
|
32
32
|
|
33
|
+
def self.apply(model, opts={})
|
34
|
+
model.instance_variable_set(:@touched_associations, {})
|
35
|
+
end
|
36
|
+
|
33
37
|
# Set the touch_column and touched_associations variables for the model.
|
34
38
|
# Options:
|
35
39
|
# * :associations - The associations to touch when a model instance is
|
@@ -41,7 +45,6 @@ module Sequel
|
|
41
45
|
# * :column - The column to modify when touching a model instance.
|
42
46
|
def self.configure(model, opts={})
|
43
47
|
model.touch_column = opts[:column] || TOUCH_COLUMN_DEFAULT if opts[:column] || !model.touch_column
|
44
|
-
model.instance_variable_set(:@touched_associations, {})
|
45
48
|
model.touch_associations(opts[:associations]) if opts[:associations]
|
46
49
|
end
|
47
50
|
|
@@ -56,13 +59,7 @@ module Sequel
|
|
56
59
|
# are column name symbols.
|
57
60
|
attr_reader :touched_associations
|
58
61
|
|
59
|
-
|
60
|
-
# Also, create a copy of the touched_associations in the subclass.
|
61
|
-
def inherited(subclass)
|
62
|
-
super
|
63
|
-
subclass.touch_column = touch_column
|
64
|
-
subclass.instance_variable_set(:@touched_associations, touched_associations.dup)
|
65
|
-
end
|
62
|
+
Plugins.inherited_instance_variables(self, :@touched_associations=>:dup, :@touch_column=>nil)
|
66
63
|
|
67
64
|
# Add additional associations to be touched. See the :association option
|
68
65
|
# of the Sequel::Plugin::Touch.configure method for the format of the associations
|
data/lib/sequel/plugins/tree.rb
CHANGED
@@ -55,12 +55,7 @@ module Sequel
|
|
55
55
|
# parent of the leaf.
|
56
56
|
attr_accessor :parent_column
|
57
57
|
|
58
|
-
|
59
|
-
def inherited(subclass)
|
60
|
-
super
|
61
|
-
subclass.parent_column = parent_column
|
62
|
-
subclass.tree_order = tree_order
|
63
|
-
end
|
58
|
+
Plugins.inherited_instance_variables(self, :@parent_column=>nil, :@tree_order=>nil)
|
64
59
|
|
65
60
|
# Returns list of all root nodes (those with no parent nodes).
|
66
61
|
#
|
@@ -36,11 +36,7 @@ module Sequel
|
|
36
36
|
@typecast_on_load_columns.concat(columns)
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
def inherited(subclass)
|
41
|
-
super
|
42
|
-
subclass.instance_variable_set(:@typecast_on_load_columns, typecast_on_load_columns.dup)
|
43
|
-
end
|
39
|
+
Plugins.inherited_instance_variables(self, :@typecast_on_load_columns=>:dup)
|
44
40
|
end
|
45
41
|
|
46
42
|
module InstanceMethods
|
@@ -20,27 +20,39 @@ module Sequel
|
|
20
20
|
# # Make the Album class support primary key updates
|
21
21
|
# Album.plugin :update_primary_key
|
22
22
|
module UpdatePrimaryKey
|
23
|
-
module ClassMethods
|
24
|
-
# Cache the pk_hash when loading records
|
25
|
-
def call(h)
|
26
|
-
r = super(h)
|
27
|
-
r.pk_hash
|
28
|
-
r
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
23
|
module InstanceMethods
|
33
|
-
# Clear the
|
34
|
-
# the pk_hash
|
24
|
+
# Clear the cached primary key.
|
35
25
|
def after_update
|
36
26
|
super
|
37
27
|
@pk_hash = nil
|
38
|
-
pk_hash
|
39
28
|
end
|
40
29
|
|
41
|
-
#
|
30
|
+
# Use the cached primary key if one is present.
|
42
31
|
def pk_hash
|
43
|
-
@pk_hash
|
32
|
+
@pk_hash || super
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# If the primary key column changes, clear related associations and cache
|
38
|
+
# the previous primary key values.
|
39
|
+
def change_column_value(column, value)
|
40
|
+
pk = primary_key
|
41
|
+
if (pk.is_a?(Array) ? pk.include?(column) : pk == column)
|
42
|
+
@pk_hash ||= pk_hash unless new?
|
43
|
+
clear_associations_using_primary_key
|
44
|
+
end
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
# Clear associations that are likely to be tied to the primary key.
|
49
|
+
# Note that this currently can clear additional options that don't reference
|
50
|
+
# the primary key (such as one_to_many columns referencing a column other than the
|
51
|
+
# primary key).
|
52
|
+
def clear_associations_using_primary_key
|
53
|
+
associations.keys.each do |k|
|
54
|
+
associations.delete(k) if model.association_reflection(k)[:type] != :many_to_one
|
55
|
+
end
|
44
56
|
end
|
45
57
|
end
|
46
58
|
end
|
@@ -21,7 +21,6 @@ module Sequel
|
|
21
21
|
# Setup the validations hash for the given model.
|
22
22
|
def self.apply(model)
|
23
23
|
model.class_eval do
|
24
|
-
@validation_mutex = Mutex.new
|
25
24
|
@validations = {}
|
26
25
|
@validation_reflections = {}
|
27
26
|
end
|
@@ -63,21 +62,15 @@ module Sequel
|
|
63
62
|
!validations.empty?
|
64
63
|
end
|
65
64
|
|
66
|
-
|
67
|
-
|
68
|
-
vr = @validation_reflections
|
69
|
-
subclass.class_eval do
|
70
|
-
@validation_mutex = Mutex.new
|
71
|
-
@validations = {}
|
72
|
-
h = {}
|
73
|
-
vr.each{|k,v| h[k] = v.dup}
|
74
|
-
@validation_reflections = h
|
75
|
-
end
|
76
|
-
super
|
77
|
-
end
|
78
|
-
|
65
|
+
Plugins.inherited_instance_variables(self, :@validations=>:hash_dup, :@validation_reflections=>:hash_dup)
|
66
|
+
|
79
67
|
# Instructs the model to skip validations defined in superclasses
|
80
68
|
def skip_superclass_validations
|
69
|
+
superclass.validations.each do |att, procs|
|
70
|
+
if ps = @validations[att]
|
71
|
+
@validations[att] -= procs
|
72
|
+
end
|
73
|
+
end
|
81
74
|
@skip_superclass_validations = true
|
82
75
|
end
|
83
76
|
|
@@ -107,7 +100,6 @@ module Sequel
|
|
107
100
|
|
108
101
|
# Validates the given instance.
|
109
102
|
def validate(o)
|
110
|
-
superclass.validate(o) if superclass.respond_to?(:validate) && !skip_superclass_validations?
|
111
103
|
validations.each do |att, procs|
|
112
104
|
v = case att
|
113
105
|
when Array
|
@@ -200,7 +192,7 @@ module Sequel
|
|
200
192
|
end
|
201
193
|
tag = opts[:tag]
|
202
194
|
atts.each do |a|
|
203
|
-
a_vals =
|
195
|
+
a_vals = Sequel.synchronize{validations[a] ||= []}
|
204
196
|
if tag && (old = a_vals.find{|x| x[0] == tag})
|
205
197
|
old[1] = blk
|
206
198
|
else
|
@@ -362,6 +354,28 @@ module Sequel
|
|
362
354
|
end
|
363
355
|
end
|
364
356
|
|
357
|
+
# Validates whether an attribute has the correct ruby type for the associated
|
358
|
+
# database type. This is generally useful in conjunction with
|
359
|
+
# raise_on_typecast_failure = false, to handle typecasting errors at validation
|
360
|
+
# time instead of at setter time.
|
361
|
+
#
|
362
|
+
# Possible Options:
|
363
|
+
# * :message - The message to use (default: 'is not a valid (integer|datetime|etc.)')
|
364
|
+
def validates_schema_type(*atts)
|
365
|
+
opts = {
|
366
|
+
:tag => :schema_type,
|
367
|
+
}.merge!(extract_options!(atts))
|
368
|
+
reflect_validation(:schema_type, opts, atts)
|
369
|
+
atts << opts
|
370
|
+
validates_each(*atts) do |o, a, v|
|
371
|
+
next if v.nil? || (klass = o.send(:schema_type_class, a)).nil?
|
372
|
+
if klass.is_a?(Array) ? !klass.any?{|kls| v.is_a?(kls)} : !v.is_a?(klass)
|
373
|
+
message = opts[:message] || "is not a valid #{Array(klass).join(" or ").downcase}"
|
374
|
+
o.errors.add(a, message)
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
365
379
|
# Validates only if the fields in the model (specified by atts) are
|
366
380
|
# unique in the database. Pass an array of fields instead of multiple
|
367
381
|
# fields to specify that the combination of fields must be unique,
|
@@ -446,6 +460,7 @@ module Sequel
|
|
446
460
|
# Validates the object.
|
447
461
|
def validate
|
448
462
|
model.validate(self)
|
463
|
+
super
|
449
464
|
end
|
450
465
|
end
|
451
466
|
end
|
@@ -6,35 +6,36 @@ module Sequel
|
|
6
6
|
# Sequel::Model.plugin :validation_helpers
|
7
7
|
# class Album < Sequel::Model
|
8
8
|
# def validate
|
9
|
+
# super
|
9
10
|
# validates_min_length 1, :num_tracks
|
10
11
|
# end
|
11
12
|
# end
|
12
13
|
#
|
13
|
-
# The validates_unique
|
14
|
-
# the API explained here:
|
14
|
+
# The validates_unique and validates_schema_types methods have a unique API, but the other
|
15
|
+
# validations have the API explained here:
|
15
16
|
#
|
16
17
|
# Arguments:
|
17
|
-
#
|
18
|
-
#
|
18
|
+
# atts :: Single attribute symbol or an array of attribute symbols specifying the
|
19
|
+
# attribute(s) to validate.
|
19
20
|
# Options:
|
20
|
-
#
|
21
|
-
#
|
21
|
+
# :allow_blank :: Whether to skip the validation if the value is blank. You should
|
22
|
+
# make sure all objects respond to blank if you use this option, which you can do by:
|
22
23
|
# Sequel.extension :blank
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
24
|
+
# :allow_missing :: Whether to skip the validation if the attribute isn't a key in the
|
25
|
+
# values hash. This is different from allow_nil, because Sequel only sends the attributes
|
26
|
+
# in the values when doing an insert or update. If the attribute is not present, Sequel
|
27
|
+
# doesn't specify it, so the database will use the table's default value. This is different
|
28
|
+
# from having an attribute in values with a value of nil, which Sequel will send as NULL.
|
29
|
+
# If your database table has a non NULL default, this may be a good option to use. You
|
30
|
+
# don't want to use allow_nil, because if the attribute is in values but has a value nil,
|
31
|
+
# Sequel will attempt to insert a NULL value into the database, instead of using the
|
32
|
+
# database's default.
|
33
|
+
# :allow_nil :: Whether to skip the validation if the value is nil.
|
34
|
+
# :message :: The message to use. Can be a string which is used directly, or a
|
35
|
+
# proc which is called. If the validation method takes a argument before the array of attributes,
|
36
|
+
# that argument is passed as an argument to the proc. The exception is the
|
37
|
+
# validates_not_string method, which doesn't take an argument, but passes
|
38
|
+
# the schema type symbol as the argument to the proc.
|
38
39
|
#
|
39
40
|
# The default validation options for all models can be modified by
|
40
41
|
# changing the values of the Sequel::Plugins::ValidationHelpers::DEFAULT_OPTIONS hash. You
|
@@ -83,13 +84,14 @@ module Sequel
|
|
83
84
|
:length_range=>{:message=>lambda{|range| "is too short or too long"}},
|
84
85
|
:max_length=>{:message=>lambda{|max| "is longer than #{max} characters"}, :nil_message=>lambda{"is not present"}},
|
85
86
|
:min_length=>{:message=>lambda{|min| "is shorter than #{min} characters"}},
|
87
|
+
:not_null=>{:message=>lambda{"is not present"}},
|
86
88
|
:not_string=>{:message=>lambda{|type| type ? "is not a valid #{type}" : "is a string"}},
|
87
89
|
:numeric=>{:message=>lambda{"is not a number"}},
|
88
|
-
:type=>{:message=>lambda{|klass| "is not a #{klass}"}},
|
90
|
+
:type=>{:message=>lambda{|klass| klass.is_a?(Array) ? "is not a valid #{klass.join(" or ").downcase}" : "is not a valid #{klass.to_s.downcase}"}},
|
89
91
|
:presence=>{:message=>lambda{"is not present"}},
|
90
92
|
:unique=>{:message=>lambda{'is already taken'}}
|
91
93
|
}
|
92
|
-
|
94
|
+
|
93
95
|
module InstanceMethods
|
94
96
|
# Check that the attribute values are the given exact length.
|
95
97
|
def validates_exact_length(exact, atts, opts={})
|
@@ -136,6 +138,11 @@ module Sequel
|
|
136
138
|
validatable_attributes_for_type(:min_length, atts, opts){|a,v,m| validation_error_message(m, min) unless v && v.length >= min}
|
137
139
|
end
|
138
140
|
|
141
|
+
# Check attribute value(s) are not NULL/nil.
|
142
|
+
def validates_not_null(atts, opts={})
|
143
|
+
validatable_attributes_for_type(:not_null, atts, opts){|a,v,m| validation_error_message(m) if v.nil?}
|
144
|
+
end
|
145
|
+
|
139
146
|
# Check that the attribute value(s) is not a string. This is generally useful
|
140
147
|
# in conjunction with raise_on_typecast_failure = false, where you are
|
141
148
|
# passing in string values for non-string attributes (such as numbers and dates).
|
@@ -158,12 +165,28 @@ module Sequel
|
|
158
165
|
end
|
159
166
|
end
|
160
167
|
|
161
|
-
#
|
168
|
+
# Validates for all of the model columns (or just the given columns)
|
169
|
+
# that the column value is an instance of the expected class based on
|
170
|
+
# the column's schema type.
|
171
|
+
def validates_schema_types(atts=keys)
|
172
|
+
Array(atts).each do |k|
|
173
|
+
next unless type = schema_type_class(k)
|
174
|
+
validates_type(type, k)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Check if value is an instance of a class. If +klass+ is an array,
|
179
|
+
# the value must be an instance of one of the classes in the array.
|
162
180
|
def validates_type(klass, atts, opts={})
|
163
181
|
klass = klass.to_s.constantize if klass.is_a?(String) || klass.is_a?(Symbol)
|
164
|
-
validatable_attributes_for_type(:type, atts, opts)
|
182
|
+
validatable_attributes_for_type(:type, atts, opts) do |a,v,m|
|
183
|
+
next if v.nil?
|
184
|
+
if klass.is_a?(Array) ? !klass.any?{|kls| v.is_a?(kls)} : !v.is_a?(klass)
|
185
|
+
validation_error_message(m, klass)
|
186
|
+
end
|
187
|
+
end
|
165
188
|
end
|
166
|
-
|
189
|
+
|
167
190
|
# Check attribute value(s) is not considered blank by the database, but allow false values.
|
168
191
|
def validates_presence(atts, opts={})
|
169
192
|
validatable_attributes_for_type(:presence, atts, opts){|a,v,m| validation_error_message(m) if model.db.send(:blank_object?, v) && v != false}
|
@@ -220,6 +243,7 @@ module Sequel
|
|
220
243
|
where = opts[:where]
|
221
244
|
atts.each do |a|
|
222
245
|
arr = Array(a)
|
246
|
+
next if arr.any?{|x| errors.on(x)}
|
223
247
|
next if opts[:only_if_modified] && !new? && !arr.any?{|x| changed_columns.include?(x)}
|
224
248
|
ds = if where
|
225
249
|
where.call(model.dataset, self, arr)
|
@@ -151,11 +151,6 @@ module Sequel
|
|
151
151
|
new.from_xml_node(parent, opts)
|
152
152
|
end
|
153
153
|
|
154
|
-
# Call the dataset +to_xml+ method.
|
155
|
-
def to_xml(opts={})
|
156
|
-
dataset.to_xml(opts)
|
157
|
-
end
|
158
|
-
|
159
154
|
# Return an appropriate Nokogiri::XML::Builder instance
|
160
155
|
# used to create the XML. This should probably not be used
|
161
156
|
# directly by user code.
|
@@ -201,6 +196,8 @@ module Sequel
|
|
201
196
|
end
|
202
197
|
proc{|s| "#{pr[s]}_"}
|
203
198
|
end
|
199
|
+
|
200
|
+
Plugins.def_dataset_methods(self, :to_xml)
|
204
201
|
end
|
205
202
|
|
206
203
|
module InstanceMethods
|
@@ -282,7 +279,7 @@ module Sequel
|
|
282
279
|
parent.children.each do |node|
|
283
280
|
next if node.is_a?(Nokogiri::XML::Text)
|
284
281
|
k = name_proc[node.name]
|
285
|
-
if assocs_hash &&
|
282
|
+
if assocs_hash && assocs_hash[k]
|
286
283
|
assocs_present << [k.to_sym, node]
|
287
284
|
else
|
288
285
|
hash[k] = node.key?('nil') ? nil : node.children.first.to_s
|
data/lib/sequel/sql.rb
CHANGED
data/lib/sequel/version.rb
CHANGED
@@ -3,7 +3,7 @@ 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 = 47
|
7
7
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
8
8
|
# releases that fix regressions from previous versions.
|
9
9
|
TINY = 0
|
@@ -116,6 +116,25 @@ describe "A PostgreSQL database" do
|
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
|
+
describe "A PostgreSQL database with domain types" do
|
120
|
+
before(:all) do
|
121
|
+
@db = POSTGRES_DB
|
122
|
+
@db << "DROP DOMAIN IF EXISTS positive_number CASCADE"
|
123
|
+
@db << "CREATE DOMAIN positive_number AS numeric(10,2) CHECK (VALUE > 0)"
|
124
|
+
@db.create_table!(:testfk){positive_number :id, :primary_key=>true}
|
125
|
+
end
|
126
|
+
after(:all) do
|
127
|
+
@db.drop_table?(:testfk)
|
128
|
+
@db << "DROP DOMAIN positive_number"
|
129
|
+
end
|
130
|
+
|
131
|
+
specify "should correctly parse the schema" do
|
132
|
+
sch = @db.schema(:testfk, :reload=>true)
|
133
|
+
sch.first.last.delete(:domain_oid).should be_a_kind_of(Integer)
|
134
|
+
sch.should == [[:id, {:type=>:decimal, :ruby_default=>nil, :db_type=>"numeric(10,2)", :default=>nil, :oid=>1700, :primary_key=>true, :allow_null=>false, :db_domain_type=>'positive_number'}]]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
119
138
|
describe "A PostgreSQL dataset" do
|
120
139
|
before(:all) do
|
121
140
|
@db = POSTGRES_DB
|
@@ -482,6 +501,23 @@ describe "A PostgreSQL dataset with a timestamp field" do
|
|
482
501
|
@db[:test3].get(:time).should == 'infinity'
|
483
502
|
@db.convert_infinite_timestamps = :float
|
484
503
|
@db[:test3].get(:time).should == 1.0/0.0
|
504
|
+
@db.convert_infinite_timestamps = 'nil'
|
505
|
+
@db[:test3].get(:time).should == nil
|
506
|
+
@db.convert_infinite_timestamps = 'string'
|
507
|
+
@db[:test3].get(:time).should == 'infinity'
|
508
|
+
@db.convert_infinite_timestamps = 'float'
|
509
|
+
@db[:test3].get(:time).should == 1.0/0.0
|
510
|
+
@db.convert_infinite_timestamps = 't'
|
511
|
+
@db[:test3].get(:time).should == 1.0/0.0
|
512
|
+
if ((Time.parse('infinity'); nil) rescue true)
|
513
|
+
# Skip for loose time parsing (e.g. old rbx)
|
514
|
+
@db.convert_infinite_timestamps = 'f'
|
515
|
+
proc{@db[:test3].get(:time)}.should raise_error
|
516
|
+
@db.convert_infinite_timestamps = nil
|
517
|
+
proc{@db[:test3].get(:time)}.should raise_error
|
518
|
+
@db.convert_infinite_timestamps = false
|
519
|
+
proc{@db[:test3].get(:time)}.should raise_error
|
520
|
+
end
|
485
521
|
|
486
522
|
@d.update(:time=>Sequel.cast('-infinity', DateTime))
|
487
523
|
@db.convert_infinite_timestamps = :nil
|
@@ -1217,7 +1253,7 @@ describe "Postgres::Database functions, languages, schemas, and triggers" do
|
|
1217
1253
|
args = ['tf', 'SELECT 1', {:returns=>:integer}]
|
1218
1254
|
@d.send(:create_function_sql, *args).should =~ /\A\s*CREATE FUNCTION tf\(\)\s+RETURNS integer\s+LANGUAGE SQL\s+AS 'SELECT 1'\s*\z/
|
1219
1255
|
@d.create_function(*args)
|
1220
|
-
|
1256
|
+
@d['SELECT tf()'].all.should == [{:tf=>1}]
|
1221
1257
|
@d.send(:drop_function_sql, 'tf').should == 'DROP FUNCTION tf()'
|
1222
1258
|
@d.drop_function('tf')
|
1223
1259
|
proc{@d['SELECT tf()'].all}.should raise_error(Sequel::DatabaseError)
|
@@ -1229,7 +1265,7 @@ describe "Postgres::Database functions, languages, schemas, and triggers" do
|
|
1229
1265
|
@d.create_function(*args)
|
1230
1266
|
# Make sure replace works
|
1231
1267
|
@d.create_function(*args)
|
1232
|
-
|
1268
|
+
@d['SELECT tf(1, 2)'].all.should == [{:tf=>3}]
|
1233
1269
|
args = ['tf', {:if_exists=>true, :cascade=>true, :args=>[[:integer, :a], :integer]}]
|
1234
1270
|
@d.send(:drop_function_sql,*args).should == 'DROP FUNCTION IF EXISTS tf(a integer, integer) CASCADE'
|
1235
1271
|
@d.drop_function(*args)
|
@@ -1323,6 +1359,7 @@ if POSTGRES_DB.adapter_scheme == :postgres
|
|
1323
1359
|
before do
|
1324
1360
|
@db = POSTGRES_DB
|
1325
1361
|
Sequel::Postgres::PG_NAMED_TYPES[:interval] = lambda{|v| v.reverse}
|
1362
|
+
@db.extension :pg_array
|
1326
1363
|
@db.reset_conversion_procs
|
1327
1364
|
end
|
1328
1365
|
after do
|
@@ -1336,6 +1373,12 @@ if POSTGRES_DB.adapter_scheme == :postgres
|
|
1336
1373
|
@db[:foo].insert(Sequel.cast('21 days', :interval))
|
1337
1374
|
@db[:foo].get(:bar).should == 'syad 12'
|
1338
1375
|
end
|
1376
|
+
|
1377
|
+
specify "should handle array types of named types" do
|
1378
|
+
@db.create_table!(:foo){column :bar, 'interval[]'}
|
1379
|
+
@db[:foo].insert(Sequel.pg_array(['21 days'], :interval))
|
1380
|
+
@db[:foo].get(:bar).should == ['syad 12']
|
1381
|
+
end
|
1339
1382
|
end
|
1340
1383
|
end
|
1341
1384
|
|
@@ -1792,6 +1835,42 @@ describe 'PostgreSQL array handling' do
|
|
1792
1835
|
end
|
1793
1836
|
end
|
1794
1837
|
|
1838
|
+
specify 'insert and retrieve custom array types' do
|
1839
|
+
int2vector = Class.new do
|
1840
|
+
attr_reader :array
|
1841
|
+
def initialize(array)
|
1842
|
+
@array = array
|
1843
|
+
end
|
1844
|
+
def sql_literal_append(ds, sql)
|
1845
|
+
sql << "'#{array.join(' ')}'"
|
1846
|
+
end
|
1847
|
+
def ==(other)
|
1848
|
+
if other.is_a?(self.class)
|
1849
|
+
array == other.array
|
1850
|
+
else
|
1851
|
+
super
|
1852
|
+
end
|
1853
|
+
end
|
1854
|
+
end
|
1855
|
+
@db.register_array_type(:int2vector){|s| int2vector.new(s.split.map{|i| i.to_i})}
|
1856
|
+
@db.create_table!(:items) do
|
1857
|
+
column :b, 'int2vector[]'
|
1858
|
+
end
|
1859
|
+
@tp.call.should == [:int2vector_array]
|
1860
|
+
int2v = int2vector.new([1, 2])
|
1861
|
+
@ds.insert(Sequel.pg_array([int2v], :int2vector))
|
1862
|
+
@ds.count.should == 1
|
1863
|
+
rs = @ds.all
|
1864
|
+
if @native
|
1865
|
+
rs.should == [{:b=>[int2v]}]
|
1866
|
+
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
1867
|
+
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
1868
|
+
@ds.delete
|
1869
|
+
@ds.insert(rs.first)
|
1870
|
+
@ds.all.should == rs
|
1871
|
+
end
|
1872
|
+
end unless POSTGRES_DB.adapter_scheme == :jdbc
|
1873
|
+
|
1795
1874
|
specify 'use arrays in bound variables' do
|
1796
1875
|
@db.create_table!(:items) do
|
1797
1876
|
column :i, 'int4[]'
|
@@ -1929,7 +2008,7 @@ end
|
|
1929
2008
|
describe 'PostgreSQL hstore handling' do
|
1930
2009
|
before(:all) do
|
1931
2010
|
@db = POSTGRES_DB
|
1932
|
-
@db.extension :pg_hstore
|
2011
|
+
@db.extension :pg_array, :pg_hstore
|
1933
2012
|
@ds = @db[:items]
|
1934
2013
|
@h = {'a'=>'b', 'c'=>nil, 'd'=>'NULL', 'e'=>'\\\\" \\\' ,=>'}
|
1935
2014
|
@native = POSTGRES_DB.adapter_scheme == :postgres
|
@@ -1956,6 +2035,24 @@ describe 'PostgreSQL hstore handling' do
|
|
1956
2035
|
end
|
1957
2036
|
end
|
1958
2037
|
|
2038
|
+
specify 'insert and retrieve hstore[] values' do
|
2039
|
+
@db.create_table!(:items) do
|
2040
|
+
column :h, 'hstore[]'
|
2041
|
+
end
|
2042
|
+
@ds.insert(Sequel.pg_array([Sequel.hstore(@h)], :hstore))
|
2043
|
+
@ds.count.should == 1
|
2044
|
+
if @native
|
2045
|
+
rs = @ds.all
|
2046
|
+
v = rs.first[:h].first
|
2047
|
+
v.should_not be_a_kind_of(Hash)
|
2048
|
+
v.to_hash.should be_a_kind_of(Hash)
|
2049
|
+
v.to_hash.should == @h
|
2050
|
+
@ds.delete
|
2051
|
+
@ds.insert(rs.first)
|
2052
|
+
@ds.all.should == rs
|
2053
|
+
end
|
2054
|
+
end
|
2055
|
+
|
1959
2056
|
specify 'use hstore in bound variables' do
|
1960
2057
|
@db.create_table!(:items) do
|
1961
2058
|
column :i, :hstore
|
@@ -2136,6 +2233,7 @@ describe 'PostgreSQL hstore handling' do
|
|
2136
2233
|
@ds.from(:items___i).select(Sequel.hstore('t'=>'s').op.record_set(:i).as(:r)).from_self(:alias=>:s).select(Sequel.lit('(r).*')).from_self.select_map(:t).should == ['s']
|
2137
2234
|
|
2138
2235
|
@ds.from(Sequel.hstore('t'=>'s', 'a'=>'b').op.skeys.as(:s)).select_order_map(:s).should == %w'a t'
|
2236
|
+
@ds.from((Sequel.hstore('t'=>'s', 'a'=>'b').op - 'a').skeys.as(:s)).select_order_map(:s).should == %w't'
|
2139
2237
|
|
2140
2238
|
@ds.get(h1.slice(Sequel.pg_array(%w'a c')).keys.pg_array.length).should == 2
|
2141
2239
|
@ds.get(h1.slice(Sequel.pg_array(%w'd c')).keys.pg_array.length).should == 1
|
@@ -2300,7 +2398,7 @@ describe 'PostgreSQL inet/cidr types' do
|
|
2300
2398
|
@ds.count.should == 1
|
2301
2399
|
if @native
|
2302
2400
|
rs = @ds.all
|
2303
|
-
|
2401
|
+
rs.first[:j]
|
2304
2402
|
rs.first[:i].should == @ipv6
|
2305
2403
|
rs.first[:c].should == @ipv6nm
|
2306
2404
|
rs.first[:i].should be_a_kind_of(IPAddr)
|
@@ -2472,7 +2570,7 @@ describe 'PostgreSQL range types' do
|
|
2472
2570
|
c.plugin :pg_typecast_on_load, :i4, :i8, :n, :d, :t, :tz unless @native
|
2473
2571
|
v = c.create(@ra).values
|
2474
2572
|
v.delete(:id)
|
2475
|
-
v.each{|k,
|
2573
|
+
v.each{|k,v1| v1.should == @ra[k].to_a}
|
2476
2574
|
end
|
2477
2575
|
end
|
2478
2576
|
|
@@ -2500,15 +2598,19 @@ describe 'PostgreSQL range types' do
|
|
2500
2598
|
@db.get(Sequel.pg_range(1..5, :int4range).op.right_of(-1..0)).should be_true
|
2501
2599
|
@db.get(Sequel.pg_range(1..5, :int4range).op.right_of(-1..3)).should be_false
|
2502
2600
|
|
2503
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
2504
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
2505
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
2506
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
2601
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(6..10)).should be_true
|
2602
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(5..10)).should be_true
|
2603
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(-1..0)).should be_false
|
2604
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(-1..3)).should be_false
|
2605
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(-1..7)).should be_true
|
2507
2606
|
|
2508
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
2509
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
2510
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
2511
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
2607
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(6..10)).should be_false
|
2608
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(5..10)).should be_false
|
2609
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(3..10)).should be_false
|
2610
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(-1..10)).should be_true
|
2611
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(-1..0)).should be_true
|
2612
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(-1..3)).should be_true
|
2613
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(-5..-1)).should be_true
|
2512
2614
|
|
2513
2615
|
@db.get(Sequel.pg_range(1..5, :int4range).op.adjacent_to(6..10)).should be_true
|
2514
2616
|
@db.get(Sequel.pg_range(1...5, :int4range).op.adjacent_to(6..10)).should be_false
|
@@ -2637,7 +2739,7 @@ describe 'PostgreSQL interval types' do
|
|
2637
2739
|
v = c.create(:i=>'1 year 2 mons 25 days 05:06:07').i
|
2638
2740
|
v.is_a?(ActiveSupport::Duration).should be_true
|
2639
2741
|
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]])
|
2640
|
-
v.parts.sort_by{|k,
|
2742
|
+
v.parts.sort_by{|k,_| k.to_s}.should == [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]].sort_by{|k,_| k.to_s}
|
2641
2743
|
end
|
2642
2744
|
end if (begin require 'active_support/duration'; require 'active_support/inflector'; require 'active_support/core_ext/string/inflections'; true; rescue LoadError; false end)
|
2643
2745
|
|
@@ -2697,6 +2799,21 @@ describe 'PostgreSQL row-valued/composite types' do
|
|
2697
2799
|
end
|
2698
2800
|
end
|
2699
2801
|
|
2802
|
+
specify 'insert and retrieve row types containing domains' do
|
2803
|
+
begin
|
2804
|
+
@db << "DROP DOMAIN IF EXISTS positive_integer CASCADE"
|
2805
|
+
@db << "CREATE DOMAIN positive_integer AS integer CHECK (VALUE > 0)"
|
2806
|
+
@db.create_table!(:domain_check) do
|
2807
|
+
positive_integer :id
|
2808
|
+
end
|
2809
|
+
@db.register_row_type(:domain_check)
|
2810
|
+
@db.get(@db.row_type(:domain_check, [1])).should == {:id=>1}
|
2811
|
+
ensure
|
2812
|
+
@db.drop_table(:domain_check)
|
2813
|
+
@db << "DROP DOMAIN positive_integer"
|
2814
|
+
end
|
2815
|
+
end if POSTGRES_DB.adapter_scheme == :postgres
|
2816
|
+
|
2700
2817
|
specify 'insert and retrieve arrays of row types' do
|
2701
2818
|
@ds = @db[:company]
|
2702
2819
|
@ds.insert(:id=>1, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])])]))
|
@@ -2734,7 +2851,6 @@ describe 'PostgreSQL row-valued/composite types' do
|
|
2734
2851
|
@ds.filter(:employees=>Sequel.cast(:$employees, 'person[]')).call(:first, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])])]))[:id].should == 1
|
2735
2852
|
@ds.filter(:employees=>Sequel.cast(:$employees, 'person[]')).call(:first, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12356'])])])).should == nil
|
2736
2853
|
|
2737
|
-
|
2738
2854
|
@ds.delete
|
2739
2855
|
@ds.call(:insert, {:employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row([nil, nil, nil])])])}, {:employees=>:$employees, :id=>1})
|
2740
2856
|
@ds.get(:employees).should == [{:address=>{:city=>nil, :zip=>nil, :street=>nil}, :id=>1}]
|