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
@@ -76,7 +76,7 @@
|
|
76
76
|
# DB.extension :pg_hstore
|
77
77
|
#
|
78
78
|
# If you are not using the native postgres adapter, you probably
|
79
|
-
# also want to use the
|
79
|
+
# also want to use the pg_typecast_on_load plugin in the model, and
|
80
80
|
# set it to typecast the hstore column(s) on load.
|
81
81
|
#
|
82
82
|
# This extension requires the delegate and strscan libraries.
|
@@ -136,6 +136,13 @@ module Sequel
|
|
136
136
|
end
|
137
137
|
|
138
138
|
module DatabaseMethods
|
139
|
+
def self.extended(db)
|
140
|
+
db.instance_eval do
|
141
|
+
add_named_conversion_procs(conversion_procs, :hstore=>PG_NAMED_TYPES[:hstore])
|
142
|
+
@schema_type_classes[:hstore] = HStore
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
139
146
|
# Handle hstores in bound variables
|
140
147
|
def bound_variable_arg(arg, conn)
|
141
148
|
case arg
|
@@ -29,7 +29,7 @@
|
|
29
29
|
# This creates a Sequel::Postgres::HStoreOp object that can be used
|
30
30
|
# for easier querying:
|
31
31
|
#
|
32
|
-
# h - 'a' # hstore_column - 'a'
|
32
|
+
# h - 'a' # hstore_column - CAST('a' AS text)
|
33
33
|
# h['a'] # hstore_column -> 'a'
|
34
34
|
#
|
35
35
|
# h.concat(:other_hstore_column) # ||
|
@@ -82,6 +82,9 @@ module Sequel
|
|
82
82
|
#
|
83
83
|
# hstore_op - 'a' # (hstore - 'a')
|
84
84
|
def -(other)
|
85
|
+
if other.is_a?(String) && !other.is_a?(Sequel::LiteralString)
|
86
|
+
other = Sequel.cast_string(other)
|
87
|
+
end
|
85
88
|
HStoreOp.new(super)
|
86
89
|
end
|
87
90
|
|
@@ -11,7 +11,7 @@
|
|
11
11
|
# DB.extension :pg_inet
|
12
12
|
#
|
13
13
|
# If you are not using the native postgres adapter, you probably
|
14
|
-
# also want to use the
|
14
|
+
# also want to use the pg_typecast_on_load plugin in the model, and
|
15
15
|
# set it to typecast the inet/cidr column(s) on load.
|
16
16
|
#
|
17
17
|
# This extension integrates with the pg_array extension. If you plan
|
@@ -33,12 +33,15 @@ module Sequel
|
|
33
33
|
module Postgres
|
34
34
|
# Methods enabling Database object integration with the inet/cidr types.
|
35
35
|
module InetDatabaseMethods
|
36
|
-
|
37
36
|
# Reset the conversion procs when extending the Database object, so
|
38
37
|
# it will pick up the inet/cidr converter. Also, extend the datasets
|
39
38
|
# with support for literalizing the IPAddr types.
|
40
39
|
def self.extended(db)
|
41
|
-
db.
|
40
|
+
db.instance_eval do
|
41
|
+
extend_datasets(InetDatasetMethods)
|
42
|
+
copy_conversion_procs([869, 650, 1041, 651, 1040])
|
43
|
+
@schema_type_classes[:ipaddr] = IPAddr
|
44
|
+
end
|
42
45
|
end
|
43
46
|
|
44
47
|
# Convert an IPAddr arg to a string. Probably not necessary, but done
|
@@ -52,16 +55,6 @@ module Sequel
|
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
55
|
-
# Make the column type detection recognize the inet and cidr types.
|
56
|
-
def schema_column_type(db_type)
|
57
|
-
case db_type
|
58
|
-
when 'inet', 'cidr'
|
59
|
-
:ipaddr
|
60
|
-
else
|
61
|
-
super
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
58
|
private
|
66
59
|
|
67
60
|
# Handle inet[]/cidr[] types in bound variables.
|
@@ -74,6 +67,16 @@ module Sequel
|
|
74
67
|
end
|
75
68
|
end
|
76
69
|
|
70
|
+
# Make the column type detection recognize the inet and cidr types.
|
71
|
+
def schema_column_type(db_type)
|
72
|
+
case db_type
|
73
|
+
when 'inet', 'cidr'
|
74
|
+
:ipaddr
|
75
|
+
else
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
77
80
|
# Typecast the given value to an IPAddr object.
|
78
81
|
def typecast_value_ipaddr(value)
|
79
82
|
case value
|
@@ -16,7 +16,7 @@
|
|
16
16
|
# DB.extension :pg_interval
|
17
17
|
#
|
18
18
|
# If you are not using the native postgres adapter, you probably
|
19
|
-
# also want to use the
|
19
|
+
# also want to use the pg_typecast_on_load plugin in the model, and
|
20
20
|
# set it to typecast the interval type column(s) on load.
|
21
21
|
#
|
22
22
|
# This extension integrates with the pg_array extension. If you plan
|
@@ -119,7 +119,11 @@ module Sequel
|
|
119
119
|
# Reset the conversion procs if using the native postgres adapter,
|
120
120
|
# and extend the datasets to correctly literalize ActiveSupport::Duration values.
|
121
121
|
def self.extended(db)
|
122
|
-
db.
|
122
|
+
db.instance_eval do
|
123
|
+
extend_datasets(IntervalDatasetMethods)
|
124
|
+
copy_conversion_procs([1186, 1187])
|
125
|
+
@schema_type_classes[:interval] = ActiveSupport::Duration
|
126
|
+
end
|
123
127
|
end
|
124
128
|
|
125
129
|
# Handle ActiveSupport::Duration values in bound variables.
|
@@ -43,7 +43,7 @@
|
|
43
43
|
# DB.extension :pg_json
|
44
44
|
#
|
45
45
|
# If you are not using the native postgres adapter, you probably
|
46
|
-
# also want to use the
|
46
|
+
# also want to use the pg_typecast_on_load plugin in the model, and
|
47
47
|
# set it to typecast the json column(s) on load.
|
48
48
|
#
|
49
49
|
# This extension integrates with the pg_array extension. If you plan
|
@@ -93,6 +93,13 @@ module Sequel
|
|
93
93
|
|
94
94
|
# Methods enabling Database object integration with the json type.
|
95
95
|
module JSONDatabaseMethods
|
96
|
+
def self.extended(db)
|
97
|
+
db.instance_eval do
|
98
|
+
copy_conversion_procs([114, 199])
|
99
|
+
@schema_type_classes[:json] = [JSONHash, JSONArray]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
96
103
|
# Parse the given string as json, returning either a JSONArray
|
97
104
|
# or JSONHash instance, and raising an error if the JSON
|
98
105
|
# parsing does not yield an array or hash.
|
@@ -123,16 +130,6 @@ module Sequel
|
|
123
130
|
end
|
124
131
|
end
|
125
132
|
|
126
|
-
# Make the column type detection recognize the json type.
|
127
|
-
def schema_column_type(db_type)
|
128
|
-
case db_type
|
129
|
-
when 'json'
|
130
|
-
:json
|
131
|
-
else
|
132
|
-
super
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
133
|
private
|
137
134
|
|
138
135
|
# Handle json[] types in bound variables.
|
@@ -145,6 +142,16 @@ module Sequel
|
|
145
142
|
end
|
146
143
|
end
|
147
144
|
|
145
|
+
# Make the column type detection recognize the json type.
|
146
|
+
def schema_column_type(db_type)
|
147
|
+
case db_type
|
148
|
+
when 'json'
|
149
|
+
:json
|
150
|
+
else
|
151
|
+
super
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
148
155
|
# Given a value to typecast to the json column
|
149
156
|
# * If given a JSONArray or JSONHash, just return the value
|
150
157
|
# * If given an Array, return a JSONArray
|
@@ -46,7 +46,7 @@
|
|
46
46
|
# DB.extension :pg_range
|
47
47
|
#
|
48
48
|
# If you are not using the native postgres adapter, you probably
|
49
|
-
# also want to use the
|
49
|
+
# also want to use the pg_typecast_on_load plugin in the model, and
|
50
50
|
# set it to typecast the range type column(s) on load.
|
51
51
|
#
|
52
52
|
# This extension integrates with the pg_array extension. If you plan
|
@@ -185,7 +185,22 @@ module Sequel
|
|
185
185
|
# Reset the conversion procs if using the native postgres adapter,
|
186
186
|
# and extend the datasets to correctly literalize ruby Range values.
|
187
187
|
def self.extended(db)
|
188
|
-
db.
|
188
|
+
db.instance_eval do
|
189
|
+
extend_datasets(DatasetMethods)
|
190
|
+
copy_conversion_procs([3904, 3906, 3912, 3926, 3905, 3907, 3913, 3927])
|
191
|
+
[:int4range, :numrange, :tsrange, :tstzrange, :daterange, :int8range].each do |v|
|
192
|
+
@schema_type_classes[v] = PGRange
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
procs = db.conversion_procs
|
197
|
+
procs[3908] = Parser.new("tsrange", procs[1114])
|
198
|
+
procs[3910] = Parser.new("tstzrange", procs[1184])
|
199
|
+
if defined?(PGArray::Creator)
|
200
|
+
procs[3909] = PGArray::Creator.new("tsrange", procs[3908])
|
201
|
+
procs[3911] = PGArray::Creator.new("tstzrange", procs[3910])
|
202
|
+
end
|
203
|
+
|
189
204
|
end
|
190
205
|
|
191
206
|
# Define a private range typecasting method for the given type that uses
|
@@ -29,12 +29,12 @@
|
|
29
29
|
# for easier querying:
|
30
30
|
#
|
31
31
|
# r.contains(:other) # range @> other
|
32
|
-
# r.contained_by(:other) # range <@ other
|
32
|
+
# r.contained_by(:other) # range <@ other
|
33
33
|
# r.overlaps(:other) # range && other
|
34
34
|
# r.left_of(:other) # range << other
|
35
35
|
# r.right_of(:other) # range >> other
|
36
|
-
# r.
|
37
|
-
# r.
|
36
|
+
# r.starts_after(:other) # range &> other
|
37
|
+
# r.ends_before(:other) # range &< other
|
38
38
|
# r.adjacent_to(:other) # range -|- other
|
39
39
|
#
|
40
40
|
# r.lower # lower(range)
|
@@ -66,8 +66,8 @@ module Sequel
|
|
66
66
|
:contained_by => ["(".freeze, " <@ ".freeze, ")".freeze].freeze,
|
67
67
|
:left_of => ["(".freeze, " << ".freeze, ")".freeze].freeze,
|
68
68
|
:right_of => ["(".freeze, " >> ".freeze, ")".freeze].freeze,
|
69
|
-
:
|
70
|
-
:
|
69
|
+
:ends_before => ["(".freeze, " &< ".freeze, ")".freeze].freeze,
|
70
|
+
:starts_after => ["(".freeze, " &> ".freeze, ")".freeze].freeze,
|
71
71
|
:adjacent_to => ["(".freeze, " -|- ".freeze, ")".freeze].freeze,
|
72
72
|
:overlaps => ["(".freeze, " && ".freeze, ")".freeze].freeze,
|
73
73
|
}
|
@@ -79,6 +79,8 @@ module Sequel
|
|
79
79
|
OPERATORS.keys.each do |f|
|
80
80
|
class_eval("def #{f}(v); operator(:#{f}, v) end", __FILE__, __LINE__)
|
81
81
|
end
|
82
|
+
alias starts_before ends_before
|
83
|
+
alias ends_after starts_after
|
82
84
|
|
83
85
|
# These operators are already supported by the wrapper, but for ranges they
|
84
86
|
# return ranges, so wrap the results in another RangeOp.
|
@@ -66,6 +66,18 @@
|
|
66
66
|
#
|
67
67
|
# DB[:table].insert(:address=>DB.row_type(:address, :street=>'123 Sesame St.', :city=>'Some City', :zip=>'12345'))
|
68
68
|
#
|
69
|
+
# Note that registering row types without providing an explicit :converter option
|
70
|
+
# creates anonymous classes. This results in ruby being unable to Marshal such
|
71
|
+
# objects. You can work around this by assigning the anonymous class to a constant.
|
72
|
+
# To get a list of such anonymous classes, you can use the following code:
|
73
|
+
#
|
74
|
+
# DB.conversion_procs.select{|k,v| v.is_a?(Sequel::Postgres::PGRow::Parser) && \
|
75
|
+
# v.converter && (v.converter.name.nil? || v.converter.name == '') }.map{|k,v| v}
|
76
|
+
#
|
77
|
+
# If you are not using the native postgres adapter, you probably
|
78
|
+
# also want to use the pg_typecast_on_load plugin in the model, and
|
79
|
+
# set it to typecast the composite type column(s) on load.
|
80
|
+
#
|
69
81
|
# This extension requires both the strscan and delegate libraries.
|
70
82
|
|
71
83
|
require 'delegate'
|
@@ -98,7 +110,7 @@ module Sequel
|
|
98
110
|
# This is done so that instances of this subclass are
|
99
111
|
# automatically casted to the database type when literalizing.
|
100
112
|
def self.subclass(db_type)
|
101
|
-
|
113
|
+
Class.new(self) do
|
102
114
|
@db_type = db_type
|
103
115
|
end
|
104
116
|
end
|
@@ -146,7 +158,7 @@ module Sequel
|
|
146
158
|
# Create a new subclass of this class with the given database
|
147
159
|
# type and columns.
|
148
160
|
def self.subclass(db_type, columns)
|
149
|
-
|
161
|
+
Class.new(self) do
|
150
162
|
@db_type = db_type
|
151
163
|
@columns = columns
|
152
164
|
end
|
@@ -196,6 +208,8 @@ module Sequel
|
|
196
208
|
end
|
197
209
|
end
|
198
210
|
|
211
|
+
ROW_TYPE_CLASSES = [HashRow, ArrayRow]
|
212
|
+
|
199
213
|
# This parser-like class splits the PostgreSQL
|
200
214
|
# row-valued/composite type output string format
|
201
215
|
# into an array of strings. Note this class makes
|
@@ -377,6 +391,7 @@ module Sequel
|
|
377
391
|
@row_types = {}
|
378
392
|
@row_schema_types = {}
|
379
393
|
extend(@row_type_method_module = Module.new)
|
394
|
+
copy_conversion_procs([2249, 2287])
|
380
395
|
end
|
381
396
|
end
|
382
397
|
|
@@ -431,11 +446,12 @@ module Sequel
|
|
431
446
|
|
432
447
|
# Get column names and oids for each of the members of the composite type.
|
433
448
|
res = from(:pg_attribute).
|
449
|
+
join(:pg_type, :oid=>:atttypid).
|
434
450
|
where(:attrelid=>rel_oid).
|
435
451
|
where{attnum > 0}.
|
436
452
|
exclude(:attisdropped).
|
437
453
|
order(:attnum).
|
438
|
-
select_map([:attname, :atttypid])
|
454
|
+
select_map([:attname, Sequel.case({0=>:atttypid}, :pg_type__typbasetype, :pg_type__typbasetype).as(:atttypid)])
|
439
455
|
if res.empty?
|
440
456
|
raise Error, "no columns for row type #{db_type.inspect} in database"
|
441
457
|
end
|
@@ -471,6 +487,7 @@ module Sequel
|
|
471
487
|
|
472
488
|
@row_types[db_type] = opts.merge(:parser=>parser)
|
473
489
|
@row_schema_types[schema_type_string] = schema_type_symbol
|
490
|
+
@schema_type_classes[schema_type_symbol] = ROW_TYPE_CLASSES
|
474
491
|
@row_type_method_module.class_eval do
|
475
492
|
meth = :"typecast_value_#{schema_type_symbol}"
|
476
493
|
define_method(meth) do |v|
|
@@ -523,15 +540,6 @@ module Sequel
|
|
523
540
|
end
|
524
541
|
end
|
525
542
|
|
526
|
-
# Make the column type detection handle registered row types.
|
527
|
-
def schema_column_type(db_type)
|
528
|
-
if type = @row_schema_types[db_type]
|
529
|
-
type
|
530
|
-
else
|
531
|
-
super
|
532
|
-
end
|
533
|
-
end
|
534
|
-
|
535
543
|
private
|
536
544
|
|
537
545
|
# Format composite types used in bound variable arrays.
|
@@ -546,6 +554,15 @@ module Sequel
|
|
546
554
|
super
|
547
555
|
end
|
548
556
|
end
|
557
|
+
|
558
|
+
# Make the column type detection handle registered row types.
|
559
|
+
def schema_column_type(db_type)
|
560
|
+
if type = @row_schema_types[db_type]
|
561
|
+
type
|
562
|
+
else
|
563
|
+
super
|
564
|
+
end
|
565
|
+
end
|
549
566
|
end
|
550
567
|
end
|
551
568
|
|
@@ -149,7 +149,7 @@ END_MIG
|
|
149
149
|
# be :type. The other options added should modify that type (e.g. :size). If a
|
150
150
|
# database type is not recognized, return it as a String type.
|
151
151
|
def column_schema_to_ruby_type(schema)
|
152
|
-
case
|
152
|
+
case schema[:db_type].downcase
|
153
153
|
when /\A(medium|small)?int(?:eger)?(?:\((\d+)\))?( unsigned)?\z/o
|
154
154
|
if !$1 && $2 && $2.to_i >= 10 && $3
|
155
155
|
# Unsigned integer type with 10 digits can potentially contain values which
|
@@ -483,4 +483,6 @@ END_MIG
|
|
483
483
|
end
|
484
484
|
end
|
485
485
|
end
|
486
|
+
|
487
|
+
Database.register_extension(:schema_dumper){}
|
486
488
|
end
|
data/lib/sequel/model.rb
CHANGED
@@ -72,7 +72,7 @@ module Sequel
|
|
72
72
|
|
73
73
|
# Class methods added to model that call the method of the same name on the dataset
|
74
74
|
DATASET_METHODS = (Dataset::ACTION_METHODS + Dataset::QUERY_METHODS +
|
75
|
-
[:each_page, :each_server, :print]) - [:and, :or, :[], :[]=, :columns, :columns!]
|
75
|
+
[:each_page, :each_server, :print, :destroy, :with_pk, :with_pk!]) - [:and, :or, :[], :[]=, :columns, :columns!]
|
76
76
|
|
77
77
|
# Class instance variables to set to nil when a subclass is created, for -w compliance
|
78
78
|
EMPTY_INSTANCE_VARIABLES = [:@overridable_methods_module, :@db]
|
@@ -114,7 +114,8 @@ module Sequel
|
|
114
114
|
:@typecast_empty_string_to_nil=>nil, :@typecast_on_assignment=>nil,
|
115
115
|
:@raise_on_typecast_failure=>nil, :@plugins=>:dup, :@setter_methods=>nil,
|
116
116
|
:@use_after_commit_rollback=>nil, :@fast_pk_lookup_sql=>nil,
|
117
|
-
:@fast_instance_delete_sql=>nil
|
117
|
+
:@fast_instance_delete_sql=>nil, :@default_eager_limit_strategy=>nil,
|
118
|
+
:@db=>nil, :@default_set_fields_options=>:dup}
|
118
119
|
|
119
120
|
# Regular expression that determines if a method name is normal in the sense that
|
120
121
|
# it could be used literally in ruby code without using send. Used to
|
@@ -128,8 +129,13 @@ module Sequel
|
|
128
129
|
@allowed_columns = nil
|
129
130
|
@db = nil
|
130
131
|
@db_schema = nil
|
132
|
+
@dataset = nil
|
131
133
|
@dataset_method_modules = []
|
134
|
+
@default_eager_limit_strategy = nil
|
135
|
+
@default_set_fields_options = {}
|
132
136
|
@overridable_methods_module = nil
|
137
|
+
@fast_pk_lookup_sql = nil
|
138
|
+
@fast_instance_delete_sql = nil
|
133
139
|
@plugins = []
|
134
140
|
@primary_key = :id
|
135
141
|
@raise_on_save_failure = true
|
@@ -663,10 +663,8 @@ module Sequel
|
|
663
663
|
# milestones, allowing for further filtering/limiting/etc.
|
664
664
|
#
|
665
665
|
# If you want to override the behavior of the add_/remove_/remove_all_/ methods
|
666
|
-
# or the association setter method,
|
667
|
-
#
|
668
|
-
# easily overridden, but you shouldn't override the public instance methods without
|
669
|
-
# calling super, as they deal with callbacks and caching.
|
666
|
+
# or the association setter method, use the :adder, :remover, :clearer, and/or :setter
|
667
|
+
# options. These options override the default behavior.
|
670
668
|
#
|
671
669
|
# By default the classes for the associations are inferred from the association
|
672
670
|
# name, so for example the Project#portfolio will return an instance of
|
@@ -739,7 +737,9 @@ module Sequel
|
|
739
737
|
# model object can be associated with many current model objects.
|
740
738
|
#
|
741
739
|
# The following options can be supplied:
|
742
|
-
# ===
|
740
|
+
# === Multiple Types
|
741
|
+
# :adder :: Proc used to define the private _add_* method for doing the database work
|
742
|
+
# to associate the given object to the current object (*_to_many assocations).
|
743
743
|
# :after_add :: Symbol, Proc, or array of both/either specifying a callback to call
|
744
744
|
# after a new item is added to the association.
|
745
745
|
# :after_load :: Symbol, Proc, or array of both/either specifying a callback to call
|
@@ -764,6 +764,8 @@ module Sequel
|
|
764
764
|
# given, uses the association's name, which is camelized (and
|
765
765
|
# singularized unless the type is :many_to_one or :one_to_one). If this is specified
|
766
766
|
# as a string or symbol, you must specify the full class name (e.g. "SomeModule::MyModel").
|
767
|
+
# :clearer :: Proc used to define the private _remove_all_* method for doing the database work
|
768
|
+
# to remove all objects associated to the current object (*_to_many assocations).
|
767
769
|
# :clone :: Merge the current options and block into the options and block used in defining
|
768
770
|
# the given association. Can be used to DRY up a bunch of similar associations that
|
769
771
|
# all share the same options such as :class and :key, while changing the order and block used.
|
@@ -837,12 +839,16 @@ module Sequel
|
|
837
839
|
# if it exists. By default, Sequel will try to determine it by looking at the
|
838
840
|
# associated model's assocations for a association that matches
|
839
841
|
# the current association's key(s). Set to nil to not use a reciprocal.
|
842
|
+
# :remover :: Proc used to define the private _remove_* method for doing the database work
|
843
|
+
# to remove the association between the given object and the current object (*_to_many assocations).
|
840
844
|
# :select :: the columns to select. Defaults to the associated class's
|
841
845
|
# table_name.* in a many_to_many association, which means it doesn't include the attributes from the
|
842
846
|
# join table. If you want to include the join table attributes, you can
|
843
847
|
# use this option, but beware that the join table attributes can clash with
|
844
848
|
# attributes from the model table, so you should alias any attributes that have
|
845
849
|
# the same name in both the join table and the associated table.
|
850
|
+
# :setter :: Proc used to define the private _*= method for doing the work to setup the assocation
|
851
|
+
# between the given object and the current object (*_to_one associations).
|
846
852
|
# :validate :: Set to false to not validate when implicitly saving any associated object.
|
847
853
|
# === :many_to_one
|
848
854
|
# :key :: foreign key in current model's table that references
|
@@ -990,13 +996,6 @@ module Sequel
|
|
990
996
|
ds
|
991
997
|
end
|
992
998
|
|
993
|
-
# Copy the association reflections to the subclass
|
994
|
-
def inherited(subclass)
|
995
|
-
super
|
996
|
-
subclass.instance_variable_set(:@association_reflections, association_reflections.dup)
|
997
|
-
subclass.default_eager_limit_strategy = default_eager_limit_strategy
|
998
|
-
end
|
999
|
-
|
1000
999
|
# Shortcut for adding a many_to_many association, see #associate
|
1001
1000
|
def many_to_many(name, opts={}, &block)
|
1002
1001
|
associate(:many_to_many, name, opts, &block)
|
@@ -1016,6 +1015,9 @@ module Sequel
|
|
1016
1015
|
def one_to_one(name, opts={}, &block)
|
1017
1016
|
associate(:one_to_one, name, opts, &block)
|
1018
1017
|
end
|
1018
|
+
|
1019
|
+
Plugins.inherited_instance_variables(self, :@association_reflections=>:dup, :@default_eager_limit_strategy=>nil)
|
1020
|
+
Plugins.def_dataset_methods(self, [:eager, :eager_graph])
|
1019
1021
|
|
1020
1022
|
private
|
1021
1023
|
|
@@ -1034,7 +1036,7 @@ module Sequel
|
|
1034
1036
|
oproc = lambda do |x|
|
1035
1037
|
case x
|
1036
1038
|
when Symbol
|
1037
|
-
t, c,
|
1039
|
+
t, c, _ = ds.send(:split_symbol, x)
|
1038
1040
|
if t && t.to_sym == table
|
1039
1041
|
SQL::QualifiedIdentifier.new(dsa, c)
|
1040
1042
|
else
|
@@ -1140,7 +1142,7 @@ module Sequel
|
|
1140
1142
|
raise(Error, "mismatched number of right keys: #{rcks.inspect} vs #{rcpks.inspect}") unless rcks.length == rcpks.length
|
1141
1143
|
end
|
1142
1144
|
uses_lcks = opts[:uses_left_composite_keys] = lcks.length > 1
|
1143
|
-
|
1145
|
+
opts[:uses_right_composite_keys] = rcks.length > 1
|
1144
1146
|
opts[:cartesian_product_number] ||= 1
|
1145
1147
|
join_table = (opts[:join_table] ||= opts.default_join_table)
|
1146
1148
|
left_key_alias = opts[:left_key_alias] ||= opts.default_associated_key_alias
|
@@ -1203,18 +1205,23 @@ module Sequel
|
|
1203
1205
|
|
1204
1206
|
return if opts[:read_only]
|
1205
1207
|
|
1206
|
-
|
1208
|
+
adder = opts[:adder] || proc do |o|
|
1207
1209
|
h = {}
|
1208
1210
|
lcks.zip(lcpks).each{|k, pk| h[k] = send(pk)}
|
1209
1211
|
rcks.zip(opts.right_primary_key_methods).each{|k, pk| h[k] = o.send(pk)}
|
1210
1212
|
_join_table_dataset(opts).insert(h)
|
1211
1213
|
end
|
1212
|
-
association_module_private_def(opts.
|
1214
|
+
association_module_private_def(opts._add_method, opts, &adder)
|
1215
|
+
|
1216
|
+
remover = opts[:remover] || proc do |o|
|
1213
1217
|
_join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| send(k)}) + rcks.zip(opts.right_primary_key_methods.map{|k| o.send(k)})).delete
|
1214
1218
|
end
|
1215
|
-
association_module_private_def(opts.
|
1219
|
+
association_module_private_def(opts._remove_method, opts, &remover)
|
1220
|
+
|
1221
|
+
clearer = opts[:clearer] || proc do
|
1216
1222
|
_join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| send(k)})).delete
|
1217
1223
|
end
|
1224
|
+
association_module_private_def(opts._remove_all_method, opts, &clearer)
|
1218
1225
|
|
1219
1226
|
def_add_method(opts)
|
1220
1227
|
def_remove_methods(opts)
|
@@ -1236,7 +1243,6 @@ module Sequel
|
|
1236
1243
|
raise(Error, "mismatched number of keys: #{cks.inspect} vs #{cpks.inspect}") unless cks.length == cpks.length
|
1237
1244
|
end
|
1238
1245
|
uses_cks = opts[:uses_composite_keys] = cks.length > 1
|
1239
|
-
qualify = opts[:qualify] != false
|
1240
1246
|
opts[:cartesian_product_number] ||= 0
|
1241
1247
|
opts[:dataset] ||= proc do
|
1242
1248
|
opts.associated_dataset.where(opts.predicate_keys.zip(cks.map{|k| send(k)}))
|
@@ -1274,7 +1280,8 @@ module Sequel
|
|
1274
1280
|
|
1275
1281
|
return if opts[:read_only]
|
1276
1282
|
|
1277
|
-
|
1283
|
+
setter = opts[:setter] || proc{|o| cks.zip(opts.primary_key_methods).each{|k, pk| send(:"#{k}=", (o.send(pk) if o))}}
|
1284
|
+
association_module_private_def(opts._setter_method, opts, &setter)
|
1278
1285
|
association_module_def(opts.setter_method, opts){|o| set_associated_object(opts, o)}
|
1279
1286
|
end
|
1280
1287
|
|
@@ -1369,7 +1376,7 @@ module Sequel
|
|
1369
1376
|
validate = opts[:validate]
|
1370
1377
|
|
1371
1378
|
if one_to_one
|
1372
|
-
|
1379
|
+
setter = opts[:setter] || proc do |o|
|
1373
1380
|
up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| send(k)})))
|
1374
1381
|
if o
|
1375
1382
|
up_ds = up_ds.exclude(o.pk_hash) unless o.new?
|
@@ -1380,21 +1387,27 @@ module Sequel
|
|
1380
1387
|
o.save(:validate=>validate) || raise(Sequel::Error, "invalid associated object, cannot save") if o
|
1381
1388
|
end
|
1382
1389
|
end
|
1390
|
+
association_module_private_def(opts._setter_method, opts, &setter)
|
1383
1391
|
association_module_def(opts.setter_method, opts){|o| set_one_to_one_associated_object(opts, o)}
|
1384
1392
|
else
|
1385
|
-
|
1393
|
+
adder = opts[:adder] || proc do |o|
|
1386
1394
|
cks.zip(cpks).each{|k, pk| o.send(:"#{k}=", send(pk))}
|
1387
1395
|
o.save(:validate=>validate) || raise(Sequel::Error, "invalid associated object, cannot save")
|
1388
1396
|
end
|
1389
|
-
|
1397
|
+
association_module_private_def(opts._add_method, opts, &adder)
|
1390
1398
|
|
1391
|
-
|
1399
|
+
remover = opts[:remover] || proc do |o|
|
1392
1400
|
cks.each{|k| o.send(:"#{k}=", nil)}
|
1393
1401
|
o.save(:validate=>validate) || raise(Sequel::Error, "invalid associated object, cannot save")
|
1394
1402
|
end
|
1395
|
-
association_module_private_def(opts.
|
1403
|
+
association_module_private_def(opts._remove_method, opts, &remover)
|
1404
|
+
|
1405
|
+
clearer = opts[:clearer] || proc do
|
1396
1406
|
_apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| send(k)}))).update(ck_nil_hash)
|
1397
1407
|
end
|
1408
|
+
association_module_private_def(opts._remove_all_method, opts, &clearer)
|
1409
|
+
|
1410
|
+
def_add_method(opts)
|
1398
1411
|
def_remove_methods(opts)
|
1399
1412
|
end
|
1400
1413
|
end
|
@@ -1511,8 +1524,8 @@ module Sequel
|
|
1511
1524
|
else
|
1512
1525
|
_load_associated_object(opts, dynamic_opts)
|
1513
1526
|
end
|
1514
|
-
|
1515
|
-
[]
|
1527
|
+
elsif opts.returns_array?
|
1528
|
+
[]
|
1516
1529
|
end
|
1517
1530
|
end
|
1518
1531
|
|
@@ -1911,7 +1924,6 @@ module Sequel
|
|
1911
1924
|
else
|
1912
1925
|
alias_base = r[:graph_alias_base]
|
1913
1926
|
end
|
1914
|
-
assoc_name = r[:name]
|
1915
1927
|
assoc_table_alias = ds.unused_table_alias(alias_base)
|
1916
1928
|
loader = r[:eager_grapher]
|
1917
1929
|
if !associations.empty?
|