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.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +96 -0
  3. data/Rakefile +7 -1
  4. data/bin/sequel +6 -4
  5. data/doc/active_record.rdoc +1 -1
  6. data/doc/advanced_associations.rdoc +14 -35
  7. data/doc/association_basics.rdoc +66 -4
  8. data/doc/migration.rdoc +4 -0
  9. data/doc/opening_databases.rdoc +6 -0
  10. data/doc/postgresql.rdoc +302 -0
  11. data/doc/release_notes/3.47.0.txt +270 -0
  12. data/doc/security.rdoc +6 -0
  13. data/lib/sequel/adapters/ibmdb.rb +9 -9
  14. data/lib/sequel/adapters/jdbc.rb +22 -7
  15. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -2
  16. data/lib/sequel/adapters/mock.rb +2 -0
  17. data/lib/sequel/adapters/postgres.rb +44 -13
  18. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  19. data/lib/sequel/adapters/shared/mysql.rb +2 -2
  20. data/lib/sequel/adapters/shared/postgres.rb +94 -55
  21. data/lib/sequel/adapters/shared/sqlite.rb +3 -1
  22. data/lib/sequel/adapters/sqlite.rb +2 -2
  23. data/lib/sequel/adapters/utils/pg_types.rb +1 -14
  24. data/lib/sequel/adapters/utils/split_alter_table.rb +3 -3
  25. data/lib/sequel/connection_pool/threaded.rb +1 -1
  26. data/lib/sequel/core.rb +1 -1
  27. data/lib/sequel/database/connecting.rb +2 -2
  28. data/lib/sequel/database/features.rb +5 -0
  29. data/lib/sequel/database/misc.rb +47 -5
  30. data/lib/sequel/database/query.rb +2 -2
  31. data/lib/sequel/dataset/actions.rb +4 -2
  32. data/lib/sequel/dataset/misc.rb +1 -1
  33. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  34. data/lib/sequel/dataset/query.rb +8 -6
  35. data/lib/sequel/dataset/sql.rb +8 -6
  36. data/lib/sequel/extensions/constraint_validations.rb +5 -2
  37. data/lib/sequel/extensions/migration.rb +10 -8
  38. data/lib/sequel/extensions/pagination.rb +3 -0
  39. data/lib/sequel/extensions/pg_array.rb +85 -25
  40. data/lib/sequel/extensions/pg_hstore.rb +8 -1
  41. data/lib/sequel/extensions/pg_hstore_ops.rb +4 -1
  42. data/lib/sequel/extensions/pg_inet.rb +16 -13
  43. data/lib/sequel/extensions/pg_interval.rb +6 -2
  44. data/lib/sequel/extensions/pg_json.rb +18 -11
  45. data/lib/sequel/extensions/pg_range.rb +17 -2
  46. data/lib/sequel/extensions/pg_range_ops.rb +7 -5
  47. data/lib/sequel/extensions/pg_row.rb +29 -12
  48. data/lib/sequel/extensions/pretty_table.rb +3 -0
  49. data/lib/sequel/extensions/query.rb +3 -0
  50. data/lib/sequel/extensions/schema_caching.rb +2 -0
  51. data/lib/sequel/extensions/schema_dumper.rb +3 -1
  52. data/lib/sequel/extensions/select_remove.rb +3 -0
  53. data/lib/sequel/model.rb +8 -2
  54. data/lib/sequel/model/associations.rb +39 -27
  55. data/lib/sequel/model/base.rb +99 -38
  56. data/lib/sequel/model/plugins.rb +25 -0
  57. data/lib/sequel/plugins/association_autoreloading.rb +27 -22
  58. data/lib/sequel/plugins/association_dependencies.rb +1 -7
  59. data/lib/sequel/plugins/auto_validations.rb +110 -0
  60. data/lib/sequel/plugins/boolean_readers.rb +1 -6
  61. data/lib/sequel/plugins/caching.rb +6 -13
  62. data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
  63. data/lib/sequel/plugins/composition.rb +14 -7
  64. data/lib/sequel/plugins/constraint_validations.rb +2 -13
  65. data/lib/sequel/plugins/defaults_setter.rb +1 -6
  66. data/lib/sequel/plugins/dirty.rb +8 -0
  67. data/lib/sequel/plugins/error_splitter.rb +54 -0
  68. data/lib/sequel/plugins/force_encoding.rb +1 -5
  69. data/lib/sequel/plugins/hook_class_methods.rb +1 -6
  70. data/lib/sequel/plugins/input_transformer.rb +79 -0
  71. data/lib/sequel/plugins/instance_filters.rb +7 -1
  72. data/lib/sequel/plugins/instance_hooks.rb +7 -1
  73. data/lib/sequel/plugins/json_serializer.rb +5 -10
  74. data/lib/sequel/plugins/lazy_attributes.rb +20 -7
  75. data/lib/sequel/plugins/list.rb +1 -6
  76. data/lib/sequel/plugins/many_through_many.rb +1 -2
  77. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +23 -39
  78. data/lib/sequel/plugins/optimistic_locking.rb +1 -5
  79. data/lib/sequel/plugins/pg_row.rb +4 -2
  80. data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -7
  81. data/lib/sequel/plugins/prepared_statements.rb +1 -5
  82. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -11
  83. data/lib/sequel/plugins/rcte_tree.rb +2 -2
  84. data/lib/sequel/plugins/serialization.rb +11 -13
  85. data/lib/sequel/plugins/serialization_modification_detection.rb +13 -1
  86. data/lib/sequel/plugins/single_table_inheritance.rb +4 -4
  87. data/lib/sequel/plugins/static_cache.rb +67 -19
  88. data/lib/sequel/plugins/string_stripper.rb +7 -27
  89. data/lib/sequel/plugins/subclasses.rb +3 -5
  90. data/lib/sequel/plugins/tactical_eager_loading.rb +2 -2
  91. data/lib/sequel/plugins/timestamps.rb +2 -7
  92. data/lib/sequel/plugins/touch.rb +5 -8
  93. data/lib/sequel/plugins/tree.rb +1 -6
  94. data/lib/sequel/plugins/typecast_on_load.rb +1 -5
  95. data/lib/sequel/plugins/update_primary_key.rb +26 -14
  96. data/lib/sequel/plugins/validation_class_methods.rb +31 -16
  97. data/lib/sequel/plugins/validation_helpers.rb +50 -26
  98. data/lib/sequel/plugins/xml_serializer.rb +3 -6
  99. data/lib/sequel/sql.rb +1 -1
  100. data/lib/sequel/version.rb +1 -1
  101. data/spec/adapters/postgres_spec.rb +131 -15
  102. data/spec/adapters/sqlite_spec.rb +1 -1
  103. data/spec/core/connection_pool_spec.rb +16 -17
  104. data/spec/core/database_spec.rb +111 -40
  105. data/spec/core/dataset_spec.rb +65 -74
  106. data/spec/core/expression_filters_spec.rb +6 -5
  107. data/spec/core/object_graph_spec.rb +0 -1
  108. data/spec/core/schema_spec.rb +23 -23
  109. data/spec/core/spec_helper.rb +5 -1
  110. data/spec/extensions/association_dependencies_spec.rb +1 -1
  111. data/spec/extensions/association_proxies_spec.rb +1 -1
  112. data/spec/extensions/auto_validations_spec.rb +90 -0
  113. data/spec/extensions/caching_spec.rb +6 -0
  114. data/spec/extensions/class_table_inheritance_spec.rb +8 -1
  115. data/spec/extensions/composition_spec.rb +12 -5
  116. data/spec/extensions/constraint_validations_spec.rb +4 -4
  117. data/spec/extensions/core_refinements_spec.rb +29 -79
  118. data/spec/extensions/dirty_spec.rb +14 -0
  119. data/spec/extensions/error_splitter_spec.rb +18 -0
  120. data/spec/extensions/identity_map_spec.rb +0 -1
  121. data/spec/extensions/input_transformer_spec.rb +54 -0
  122. data/spec/extensions/instance_filters_spec.rb +6 -0
  123. data/spec/extensions/instance_hooks_spec.rb +12 -1
  124. data/spec/extensions/json_serializer_spec.rb +0 -1
  125. data/spec/extensions/lazy_attributes_spec.rb +64 -55
  126. data/spec/extensions/looser_typecasting_spec.rb +1 -1
  127. data/spec/extensions/many_through_many_spec.rb +3 -4
  128. data/spec/extensions/many_to_one_pk_lookup_spec.rb +53 -15
  129. data/spec/extensions/migration_spec.rb +16 -0
  130. data/spec/extensions/null_dataset_spec.rb +1 -1
  131. data/spec/extensions/pg_array_spec.rb +48 -1
  132. data/spec/extensions/pg_hstore_ops_spec.rb +10 -2
  133. data/spec/extensions/pg_hstore_spec.rb +5 -0
  134. data/spec/extensions/pg_inet_spec.rb +5 -0
  135. data/spec/extensions/pg_interval_spec.rb +7 -3
  136. data/spec/extensions/pg_json_spec.rb +6 -1
  137. data/spec/extensions/pg_range_ops_spec.rb +4 -1
  138. data/spec/extensions/pg_range_spec.rb +5 -0
  139. data/spec/extensions/pg_row_plugin_spec.rb +13 -0
  140. data/spec/extensions/pg_row_spec.rb +28 -19
  141. data/spec/extensions/pg_typecast_on_load_spec.rb +6 -1
  142. data/spec/extensions/prepared_statements_associations_spec.rb +1 -1
  143. data/spec/extensions/query_literals_spec.rb +1 -1
  144. data/spec/extensions/rcte_tree_spec.rb +2 -2
  145. data/spec/extensions/schema_spec.rb +2 -2
  146. data/spec/extensions/serialization_modification_detection_spec.rb +8 -0
  147. data/spec/extensions/serialization_spec.rb +15 -1
  148. data/spec/extensions/sharding_spec.rb +1 -1
  149. data/spec/extensions/single_table_inheritance_spec.rb +1 -1
  150. data/spec/extensions/static_cache_spec.rb +59 -9
  151. data/spec/extensions/tactical_eager_loading_spec.rb +19 -4
  152. data/spec/extensions/update_primary_key_spec.rb +17 -1
  153. data/spec/extensions/validation_class_methods_spec.rb +25 -0
  154. data/spec/extensions/validation_helpers_spec.rb +59 -3
  155. data/spec/integration/associations_test.rb +5 -5
  156. data/spec/integration/eager_loader_test.rb +32 -63
  157. data/spec/integration/model_test.rb +2 -2
  158. data/spec/integration/plugin_test.rb +88 -56
  159. data/spec/integration/prepared_statement_test.rb +1 -1
  160. data/spec/integration/schema_test.rb +1 -1
  161. data/spec/integration/timezone_test.rb +0 -1
  162. data/spec/integration/transaction_test.rb +0 -1
  163. data/spec/model/association_reflection_spec.rb +1 -1
  164. data/spec/model/associations_spec.rb +106 -84
  165. data/spec/model/base_spec.rb +4 -4
  166. data/spec/model/eager_loading_spec.rb +8 -8
  167. data/spec/model/model_spec.rb +27 -9
  168. data/spec/model/plugins_spec.rb +71 -0
  169. data/spec/model/record_spec.rb +99 -13
  170. 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 typecast_on_load plugin in the model, and
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 typecast_on_load plugin in the model, and
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.extend_datasets(InetDatasetMethods)
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 typecast_on_load plugin in the model, and
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.extend_datasets(IntervalDatasetMethods)
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 typecast_on_load plugin in the model, and
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 typecast_on_load plugin in the model, and
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.extend_datasets(DatasetMethods)
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.starts_before(:other) # range &< other
37
- # r.ends_after(:other) # range &> other
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
- :starts_before => ["(".freeze, " &< ".freeze, ")".freeze].freeze,
70
- :ends_after => ["(".freeze, " &> ".freeze, ")".freeze].freeze,
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
- sc = Class.new(self) do
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
- sc = Class.new(self) do
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
 
@@ -24,4 +24,7 @@ module Sequel
24
24
  Sequel::PrettyTable.print(rows, cols.empty? ? ds.columns : cols)
25
25
  end
26
26
  end
27
+
28
+ Database.register_extension(:pretty_table){}
29
+ Dataset.register_extension(:pretty_table){}
27
30
  end
@@ -51,4 +51,7 @@ module Sequel
51
51
  end
52
52
  end
53
53
  end
54
+
55
+ Dataset.register_extension(:query){}
56
+ Database.register_extension(:query){}
54
57
  end
@@ -73,4 +73,6 @@ module Sequel
73
73
  load_schema_cache(file) if File.exist?(file)
74
74
  end
75
75
  end
76
+
77
+ Database.register_extension(:schema_caching){}
76
78
  end
@@ -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 t = schema[:db_type].downcase
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
@@ -36,4 +36,7 @@ module Sequel
36
36
  end
37
37
  end
38
38
  end
39
+
40
+ Database.register_extension(:select_remove){}
41
+ Dataset.register_extension(:select_remove){}
39
42
  end
@@ -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, there are private instance methods created that are prepended
667
- # with an underscore (e.g. _add_milestone or _portfolio=). The private instance methods can be
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
- # === All Types
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, a = ds.send(:split_symbol, x)
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
- uses_rcks = opts[:uses_right_composite_keys] = rcks.length > 1
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
- association_module_private_def(opts._add_method, opts) do |o|
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._remove_method, opts) do |o|
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._remove_all_method, opts) do
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
- association_module_private_def(opts._setter_method, opts){|o| cks.zip(opts.primary_key_methods).each{|k, pk| send(:"#{k}=", (o.send(pk) if o))}}
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
- association_module_private_def(opts._setter_method, opts) do |o|
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
- association_module_private_def(opts._add_method, opts) do |o|
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
- def_add_method(opts)
1397
+ association_module_private_def(opts._add_method, opts, &adder)
1390
1398
 
1391
- association_module_private_def(opts._remove_method, opts) do |o|
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._remove_all_method, opts) do
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
- else
1515
- [] if opts.returns_array?
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?