sequel 3.46.0 → 3.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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?
|