sequel 3.37.0 → 3.38.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. data/CHANGELOG +56 -0
  2. data/README.rdoc +82 -58
  3. data/Rakefile +6 -5
  4. data/bin/sequel +1 -1
  5. data/doc/active_record.rdoc +67 -52
  6. data/doc/advanced_associations.rdoc +33 -48
  7. data/doc/association_basics.rdoc +41 -51
  8. data/doc/cheat_sheet.rdoc +21 -21
  9. data/doc/core_extensions.rdoc +374 -0
  10. data/doc/dataset_basics.rdoc +5 -5
  11. data/doc/dataset_filtering.rdoc +47 -43
  12. data/doc/mass_assignment.rdoc +1 -1
  13. data/doc/migration.rdoc +4 -5
  14. data/doc/model_hooks.rdoc +3 -3
  15. data/doc/object_model.rdoc +31 -25
  16. data/doc/opening_databases.rdoc +19 -5
  17. data/doc/prepared_statements.rdoc +2 -2
  18. data/doc/querying.rdoc +109 -52
  19. data/doc/reflection.rdoc +6 -6
  20. data/doc/release_notes/3.38.0.txt +234 -0
  21. data/doc/schema_modification.rdoc +22 -13
  22. data/doc/sharding.rdoc +8 -9
  23. data/doc/sql.rdoc +154 -112
  24. data/doc/testing.rdoc +47 -7
  25. data/doc/thread_safety.rdoc +1 -1
  26. data/doc/transactions.rdoc +1 -1
  27. data/doc/validations.rdoc +1 -1
  28. data/doc/virtual_rows.rdoc +29 -43
  29. data/lib/sequel/adapters/do/postgres.rb +1 -4
  30. data/lib/sequel/adapters/jdbc.rb +14 -3
  31. data/lib/sequel/adapters/jdbc/db2.rb +9 -0
  32. data/lib/sequel/adapters/jdbc/derby.rb +41 -4
  33. data/lib/sequel/adapters/jdbc/jtds.rb +11 -0
  34. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -6
  35. data/lib/sequel/adapters/mock.rb +10 -4
  36. data/lib/sequel/adapters/postgres.rb +1 -28
  37. data/lib/sequel/adapters/shared/mssql.rb +23 -13
  38. data/lib/sequel/adapters/shared/postgres.rb +46 -0
  39. data/lib/sequel/adapters/swift.rb +21 -13
  40. data/lib/sequel/adapters/swift/mysql.rb +1 -0
  41. data/lib/sequel/adapters/swift/postgres.rb +4 -5
  42. data/lib/sequel/adapters/swift/sqlite.rb +2 -1
  43. data/lib/sequel/adapters/tinytds.rb +14 -2
  44. data/lib/sequel/adapters/utils/pg_types.rb +5 -0
  45. data/lib/sequel/core.rb +29 -17
  46. data/lib/sequel/database/query.rb +1 -1
  47. data/lib/sequel/database/schema_generator.rb +3 -0
  48. data/lib/sequel/dataset/actions.rb +5 -6
  49. data/lib/sequel/dataset/query.rb +7 -7
  50. data/lib/sequel/dataset/sql.rb +5 -18
  51. data/lib/sequel/extensions/core_extensions.rb +8 -12
  52. data/lib/sequel/extensions/pg_array.rb +59 -33
  53. data/lib/sequel/extensions/pg_array_ops.rb +32 -4
  54. data/lib/sequel/extensions/pg_auto_parameterize.rb +1 -1
  55. data/lib/sequel/extensions/pg_hstore.rb +32 -17
  56. data/lib/sequel/extensions/pg_hstore_ops.rb +32 -3
  57. data/lib/sequel/extensions/pg_inet.rb +1 -2
  58. data/lib/sequel/extensions/pg_interval.rb +0 -1
  59. data/lib/sequel/extensions/pg_json.rb +41 -23
  60. data/lib/sequel/extensions/pg_range.rb +36 -11
  61. data/lib/sequel/extensions/pg_range_ops.rb +32 -4
  62. data/lib/sequel/extensions/pg_row.rb +572 -0
  63. data/lib/sequel/extensions/pg_row_ops.rb +164 -0
  64. data/lib/sequel/extensions/query.rb +3 -3
  65. data/lib/sequel/extensions/schema_dumper.rb +7 -8
  66. data/lib/sequel/extensions/select_remove.rb +1 -1
  67. data/lib/sequel/model/base.rb +1 -0
  68. data/lib/sequel/no_core_ext.rb +1 -1
  69. data/lib/sequel/plugins/pg_row.rb +121 -0
  70. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  71. data/lib/sequel/plugins/validation_helpers.rb +31 -0
  72. data/lib/sequel/sql.rb +64 -44
  73. data/lib/sequel/version.rb +1 -1
  74. data/spec/adapters/mssql_spec.rb +37 -12
  75. data/spec/adapters/mysql_spec.rb +39 -75
  76. data/spec/adapters/oracle_spec.rb +11 -11
  77. data/spec/adapters/postgres_spec.rb +414 -237
  78. data/spec/adapters/spec_helper.rb +1 -1
  79. data/spec/adapters/sqlite_spec.rb +14 -14
  80. data/spec/core/database_spec.rb +6 -6
  81. data/spec/core/dataset_spec.rb +169 -205
  82. data/spec/core/expression_filters_spec.rb +182 -295
  83. data/spec/core/object_graph_spec.rb +6 -6
  84. data/spec/core/schema_spec.rb +14 -14
  85. data/spec/core/spec_helper.rb +1 -0
  86. data/spec/{extensions/core_extensions_spec.rb → core_extensions_spec.rb} +208 -14
  87. data/spec/extensions/columns_introspection_spec.rb +5 -5
  88. data/spec/extensions/hook_class_methods_spec.rb +28 -36
  89. data/spec/extensions/many_through_many_spec.rb +4 -4
  90. data/spec/extensions/pg_array_ops_spec.rb +15 -7
  91. data/spec/extensions/pg_array_spec.rb +81 -48
  92. data/spec/extensions/pg_auto_parameterize_spec.rb +2 -2
  93. data/spec/extensions/pg_hstore_ops_spec.rb +13 -9
  94. data/spec/extensions/pg_hstore_spec.rb +66 -65
  95. data/spec/extensions/pg_inet_spec.rb +2 -4
  96. data/spec/extensions/pg_interval_spec.rb +2 -3
  97. data/spec/extensions/pg_json_spec.rb +20 -18
  98. data/spec/extensions/pg_range_ops_spec.rb +11 -4
  99. data/spec/extensions/pg_range_spec.rb +30 -7
  100. data/spec/extensions/pg_row_ops_spec.rb +48 -0
  101. data/spec/extensions/pg_row_plugin_spec.rb +45 -0
  102. data/spec/extensions/pg_row_spec.rb +323 -0
  103. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  104. data/spec/extensions/query_literals_spec.rb +11 -11
  105. data/spec/extensions/query_spec.rb +3 -3
  106. data/spec/extensions/schema_dumper_spec.rb +20 -4
  107. data/spec/extensions/schema_spec.rb +18 -41
  108. data/spec/extensions/select_remove_spec.rb +4 -4
  109. data/spec/extensions/spec_helper.rb +4 -8
  110. data/spec/extensions/to_dot_spec.rb +5 -5
  111. data/spec/extensions/validation_class_methods_spec.rb +28 -16
  112. data/spec/integration/associations_test.rb +20 -20
  113. data/spec/integration/dataset_test.rb +98 -98
  114. data/spec/integration/eager_loader_test.rb +13 -27
  115. data/spec/integration/plugin_test.rb +5 -5
  116. data/spec/integration/prepared_statement_test.rb +22 -13
  117. data/spec/integration/schema_test.rb +28 -18
  118. data/spec/integration/spec_helper.rb +1 -1
  119. data/spec/integration/timezone_test.rb +2 -2
  120. data/spec/integration/type_test.rb +15 -6
  121. data/spec/model/association_reflection_spec.rb +1 -1
  122. data/spec/model/associations_spec.rb +4 -4
  123. data/spec/model/base_spec.rb +5 -5
  124. data/spec/model/eager_loading_spec.rb +15 -15
  125. data/spec/model/model_spec.rb +32 -32
  126. data/spec/model/record_spec.rb +16 -0
  127. data/spec/model/spec_helper.rb +2 -6
  128. data/spec/model/validations_spec.rb +1 -1
  129. metadata +16 -4
@@ -39,6 +39,11 @@ module Sequel
39
39
  end
40
40
  end.new
41
41
 
42
+ # Type OIDs for string types used by PostgreSQL. These types don't
43
+ # have conversion procs associated with them (since the data is
44
+ # already in the form of a string).
45
+ STRING_TYPES = [18, 19, 25, 1042, 1043]
46
+
42
47
  # Hash with type name strings/symbols and callable values for converting PostgreSQL types.
43
48
  # Non-builtin types that don't have fixed numbers should use this to register
44
49
  # conversion procs.
data/lib/sequel/core.rb CHANGED
@@ -147,23 +147,11 @@ module Sequel
147
147
  Database.connect(*args, &block)
148
148
  end
149
149
 
150
- if !defined?(::SEQUEL_NO_CORE_EXTENSIONS) && !ENV.has_key?('SEQUEL_NO_CORE_EXTENSIONS')
151
- # Whether the core extensions are enabled. The core extensions are enabled by
152
- # default for backwards compatibility, but can be disabled using the SEQUEL_NO_CORE_EXTENSIONS
153
- # constant or environment variable.
154
- def self.core_extensions?
155
- # We override this method to return true inside the core_extensions.rb file,
156
- # but we also set it here because that file is not loaded until most of Sequel
157
- # is finished loading, and parts of Sequel check whether the core extensions
158
- # are loaded.
159
- true
160
- end
161
- else
162
- def self.core_extensions?
163
- false
164
- end
150
+ # Assume the core extensions are not loaded by default, if the core_extensions
151
+ # extension is loaded, this will be overridden.
152
+ def self.core_extensions?
153
+ false
165
154
  end
166
-
167
155
 
168
156
  # Convert the +exception+ to the given class. The given class should be
169
157
  # <tt>Sequel::Error</tt> or a subclass. Returns an instance of +klass+ with
@@ -262,6 +250,28 @@ module Sequel
262
250
  Database.single_threaded = value
263
251
  end
264
252
 
253
+ COLUMN_REF_RE1 = /\A((?:(?!__).)+)__((?:(?!___).)+)___(.+)\z/.freeze
254
+ COLUMN_REF_RE2 = /\A((?:(?!___).)+)___(.+)\z/.freeze
255
+ COLUMN_REF_RE3 = /\A((?:(?!__).)+)__(.+)\z/.freeze
256
+
257
+ # Splits the symbol into three parts. Each part will
258
+ # either be a string or nil.
259
+ #
260
+ # For columns, these parts are the table, column, and alias.
261
+ # For tables, these parts are the schema, table, and alias.
262
+ def self.split_symbol(sym)
263
+ case s = sym.to_s
264
+ when COLUMN_REF_RE1
265
+ [$1, $2, $3]
266
+ when COLUMN_REF_RE2
267
+ [nil, $1, $2]
268
+ when COLUMN_REF_RE3
269
+ [$1, $2, nil]
270
+ else
271
+ [nil, s, nil]
272
+ end
273
+ end
274
+
265
275
  # Converts the given +string+ into a +Date+ object.
266
276
  #
267
277
  # Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)
@@ -413,7 +423,9 @@ module Sequel
413
423
  private_class_method :adapter_method, :def_adapter_method
414
424
 
415
425
  require(%w"metaprogramming sql connection_pool exceptions dataset database timezones ast_transformer version")
416
- extension(:core_extensions) if Sequel.core_extensions?
426
+ if !defined?(::SEQUEL_NO_CORE_EXTENSIONS) && !ENV.has_key?('SEQUEL_NO_CORE_EXTENSIONS')
427
+ extension(:core_extensions)
428
+ end
417
429
 
418
430
  # Add the database adapter class methods to Sequel via metaprogramming
419
431
  def_adapter_method(*Database::ADAPTERS)
@@ -582,7 +582,7 @@ module Sequel
582
582
  :time
583
583
  when /\A(bool(ean)?)\z/io
584
584
  :boolean
585
- when /\A(real|float|double( precision)?)\z/io
585
+ when /\A(real|float|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/io
586
586
  :float
587
587
  when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(\d+|false|true)\))?)|(?:small)?money)\z/io
588
588
  $1 && ['0', 'false'].include?($1) ? :integer : :decimal
@@ -98,6 +98,8 @@ module Sequel
98
98
  # (:restrict, :cascade, :set_null, :set_default, :no_action).
99
99
  # :primary_key :: Make the column as a single primary key column. This should only
100
100
  # be used if you have a single, nonautoincrementing primary key column.
101
+ # :type :: Overrides the type given as the argument. Generally not used by column
102
+ # itself, but can be passed as an option to other methods that call column.
101
103
  # :unique :: Mark the column as unique, generally has the same effect as
102
104
  # creating a unique index on the column.
103
105
  def column(name, type, opts = {})
@@ -122,6 +124,7 @@ module Sequel
122
124
  # foreign_key(:artist_id) # artist_id INTEGER
123
125
  # foreign_key(:artist_id, :artists) # artist_id INTEGER REFERENCES artists
124
126
  # foreign_key(:artist_id, :artists, :key=>:id) # artist_id INTEGER REFERENCES artists(id)
127
+ # foreign_key(:artist_id, :artists, :type=>String) # artist_id varchar(255) REFERENCES artists(id)
125
128
  #
126
129
  # If you want a foreign key constraint without adding a column (usually because it is a
127
130
  # composite foreign key), you can provide an array of columns as the first argument, and
@@ -340,7 +340,7 @@ module Sequel
340
340
  # DB[:table].order(:id).last # SELECT * FROM table ORDER BY id DESC LIMIT 1
341
341
  # # => {:id=>10}
342
342
  #
343
- # DB[:table].order(:id.desc).last(2) # SELECT * FROM table ORDER BY id ASC LIMIT 2
343
+ # DB[:table].order(Sequel.desc(:id)).last(2) # SELECT * FROM table ORDER BY id ASC LIMIT 2
344
344
  # # => [{:id=>1}, {:id=>2}]
345
345
  def last(*args, &block)
346
346
  raise(Error, 'No order specified') unless @opts[:order]
@@ -768,7 +768,7 @@ module Sequel
768
768
  when Symbol
769
769
  _, c, a = split_symbol(s)
770
770
  (a || c).to_sym
771
- when SQL::Identifier
771
+ when SQL::Identifier, SQL::Wrapper
772
772
  hash_key_symbol(s.value)
773
773
  when SQL::QualifiedIdentifier
774
774
  hash_key_symbol(s.column)
@@ -820,10 +820,9 @@ module Sequel
820
820
  when SQL::AliasedExpression
821
821
  c.expression
822
822
  when SQL::OrderedExpression
823
- expr = c.expression
824
- if expr.is_a?(Symbol)
825
- expr = unaliased_identifier(expr)
826
- SQL::OrderedExpression.new(unaliased_identifier(c.expression), c.descending, :nulls=>c.nulls)
823
+ case expr = c.expression
824
+ when Symbol, SQL::AliasedExpression
825
+ SQL::OrderedExpression.new(unaliased_identifier(expr), c.descending, :nulls=>c.nulls)
827
826
  else
828
827
  c
829
828
  end
@@ -500,7 +500,7 @@ module Sequel
500
500
  # # SELECT * FROM a LEFT JOIN b AS c USING (d)
501
501
  #
502
502
  # DB[:a].natural_join(:b).join_table(:inner, :c) do |ta, jta, js|
503
- # (:d.qualify(ta) > :e.qualify(jta)) & {:f.qualify(ta)=>DB.from(js.first.table).select(:g)}
503
+ # (Sequel.qualify(ta, :d) > Sequel.qualify(jta, :e)) & {Sequel.qualify(ta, :f)=>DB.from(js.first.table).select(:g)}
504
504
  # end
505
505
  # # SELECT * FROM a NATURAL JOIN b INNER JOIN c
506
506
  # # ON ((c.d > b.e) AND (c.f IN (SELECT g FROM b)))
@@ -657,10 +657,10 @@ module Sequel
657
657
  #
658
658
  # DB[:items].order(:name) # SELECT * FROM items ORDER BY name
659
659
  # DB[:items].order(:a, :b) # SELECT * FROM items ORDER BY a, b
660
- # DB[:items].order('a + b'.lit) # SELECT * FROM items ORDER BY a + b
660
+ # DB[:items].order(Sequel.lit('a + b')) # SELECT * FROM items ORDER BY a + b
661
661
  # DB[:items].order(:a + :b) # SELECT * FROM items ORDER BY (a + b)
662
- # DB[:items].order(:name.desc) # SELECT * FROM items ORDER BY name DESC
663
- # DB[:items].order(:name.asc(:nulls=>:last)) # SELECT * FROM items ORDER BY name ASC NULLS LAST
662
+ # DB[:items].order(Sequel.desc(:name)) # SELECT * FROM items ORDER BY name DESC
663
+ # DB[:items].order(Sequel.asc(:name, :nulls=>:last)) # SELECT * FROM items ORDER BY name ASC NULLS LAST
664
664
  # DB[:items].order{sum(name).desc} # SELECT * FROM items ORDER BY sum(name) DESC
665
665
  # DB[:items].order(nil) # SELECT * FROM items
666
666
  def order(*columns, &block)
@@ -756,7 +756,7 @@ module Sequel
756
756
  #
757
757
  # DB[:items].reverse(:id) # SELECT * FROM items ORDER BY id DESC
758
758
  # DB[:items].order(:id).reverse # SELECT * FROM items ORDER BY id DESC
759
- # DB[:items].order(:id).reverse(:name.asc) # SELECT * FROM items ORDER BY name ASC
759
+ # DB[:items].order(:id).reverse(Sequel.desc(:name)) # SELECT * FROM items ORDER BY name ASC
760
760
  def reverse(*order)
761
761
  order(*invert_order(order.empty? ? @opts[:order] : order))
762
762
  end
@@ -1119,8 +1119,8 @@ module Sequel
1119
1119
  # Inverts the given order by breaking it into a list of column references
1120
1120
  # and inverting them.
1121
1121
  #
1122
- # DB[:items].invert_order([:id.desc]]) #=> [:id]
1123
- # DB[:items].invert_order(:category, :price.desc]) #=> [:category.desc, :price]
1122
+ # DB[:items].invert_order([Sequel.desc(:id)]]) #=> [Sequel.asc(:id)]
1123
+ # DB[:items].invert_order([:category, Sequel.desc(:price)]) #=> [Sequel.desc(:category), Sequel.asc(:price)]
1124
1124
  def invert_order(order)
1125
1125
  return nil unless order
1126
1126
  new_order = []
@@ -200,9 +200,9 @@ module Sequel
200
200
  CASE_THEN = " THEN ".freeze
201
201
  CASE_WHEN = " WHEN ".freeze
202
202
  CAST_OPEN = 'CAST('.freeze
203
- COLUMN_REF_RE1 = /\A((?:(?!__).)+)__((?:(?!___).)+)___(.+)\z/.freeze
204
- COLUMN_REF_RE2 = /\A((?:(?!___).)+)___(.+)\z/.freeze
205
- COLUMN_REF_RE3 = /\A((?:(?!__).)+)__(.+)\z/.freeze
203
+ COLUMN_REF_RE1 = Sequel::COLUMN_REF_RE1
204
+ COLUMN_REF_RE2 = Sequel::COLUMN_REF_RE2
205
+ COLUMN_REF_RE3 = Sequel::COLUMN_REF_RE3
206
206
  COMMA = ', '.freeze
207
207
  COMMA_SEPARATOR = COMMA
208
208
  CONDITION_FALSE = '(1 = 0)'.freeze
@@ -1320,22 +1320,9 @@ module Sequel
1320
1320
  end
1321
1321
  end
1322
1322
 
1323
- # Splits the symbol into three parts. Each part will
1324
- # either be a string or nil.
1325
- #
1326
- # For columns, these parts are the table, column, and alias.
1327
- # For tables, these parts are the schema, table, and alias.
1323
+ # Delegate to Sequel.split_symbol.
1328
1324
  def split_symbol(sym)
1329
- case s = sym.to_s
1330
- when COLUMN_REF_RE1
1331
- [$1, $2, $3]
1332
- when COLUMN_REF_RE2
1333
- [nil, $1, $2]
1334
- when COLUMN_REF_RE3
1335
- [$1, $2, nil]
1336
- else
1337
- [nil, s, nil]
1338
- end
1325
+ Sequel.split_symbol(sym)
1339
1326
  end
1340
1327
 
1341
1328
  # The string that is appended to to create the SQL query, the empty
@@ -199,28 +199,24 @@ end
199
199
 
200
200
  # Sequel extends +Symbol+ to add methods to implement the SQL DSL.
201
201
  class Symbol
202
- include Sequel::SQL::QualifyingMethods
203
- include Sequel::SQL::IdentifierMethods
204
202
  include Sequel::SQL::AliasMethods
205
203
  include Sequel::SQL::CastMethods
206
204
  include Sequel::SQL::OrderMethods
207
205
  include Sequel::SQL::BooleanMethods
208
206
  include Sequel::SQL::NumericMethods
207
+ include Sequel::SQL::QualifyingMethods
209
208
  include Sequel::SQL::StringMethods
210
209
  include Sequel::SQL::SubscriptMethods
211
210
  include Sequel::SQL::ComplexExpressionMethods
212
211
  include Sequel::SQL::InequalityMethods if RUBY_VERSION < '1.9.0'
213
212
 
214
- # If no argument is given, returns a <tt>Sequel::SQL::ColumnAll</tt> object specifying all
215
- # columns for this table.
216
- # If an argument is given, returns a <tt>Sequel::SQL::NumericExpression</tt> using the *
217
- # (multiplication) operator with this and the given argument.
218
- #
219
- # :table.* # SQL: table.*
220
- # :column * 2 # SQL: column * 2
221
- def *(ce=(arg=false;nil))
222
- return super(ce) unless arg == false
223
- Sequel::SQL::ColumnAll.new(self);
213
+ # Returns receiver wrapped in an <tt>Sequel::SQL::Identifier</tt>. Usually used to
214
+ # prevent splitting the symbol.
215
+ #
216
+ # :a__b # SQL: "a"."b"
217
+ # :a__b.identifier # SQL: "a__b"
218
+ def identifier
219
+ Sequel::SQL::Identifier.new(self)
224
220
  end
225
221
 
226
222
  # Returns a <tt>Sequel::SQL::Function</tt> with this as the function name,
@@ -15,15 +15,21 @@
15
15
  #
16
16
  # To turn an existing Array into a PGArray:
17
17
  #
18
+ # Sequel.pg_array(array)
19
+ #
20
+ # If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]),
21
+ # you can also use Array#pg_array:
22
+ #
18
23
  # array.pg_array
19
24
  #
20
25
  # You can also provide a type, though it many cases it isn't necessary:
21
26
  #
27
+ # Sequel.pg_array(array, :varchar) # or :integer, :"double precision", etc.
22
28
  # array.pg_array(:varchar) # or :integer, :"double precision", etc.
23
29
  #
24
30
  # So if you want to insert an array into an integer[] database column:
25
31
  #
26
- # DB[:table].insert(:column=>[1, 2, 3].pg_array)
32
+ # DB[:table].insert(:column=>Sequel.pg_array([1, 2, 3]))
27
33
  #
28
34
  # If you would like to use PostgreSQL arrays in your model objects, you
29
35
  # probably want to modify the schema parsing/typecasting so that it
@@ -135,6 +141,8 @@ module Sequel
135
141
  # typecast method. For example, for an array of integers, this could be set to
136
142
  # :integer, so that the typecast_value_integer method is called on all of the
137
143
  # array elements. Defaults to :type_symbol option.
144
+ # :type_procs :: A hash mapping oids to conversion procs, used for looking up the :scalar_oid and
145
+ # value and setting the :oid value. Defaults to the global Sequel::Postgres::PG_TYPES.
138
146
  # :type_symbol :: The base of the schema type symbol for this type. For example, if you provide
139
147
  # :integer, Sequel will recognize this type as :integer_array during schema parsing.
140
148
  # Defaults to the db_type argument.
@@ -142,12 +150,16 @@ module Sequel
142
150
  # typecasting method to be created in the database. This should only be used
143
151
  # to alias existing array types. For example, if there is an array type that can be
144
152
  # treated just like an integer array, you can do :typecast_method=>:integer.
153
+ # :typecast_methods_module :: If given, a module object to add the typecasting method to. Defaults
154
+ # to DatabaseMethods.
145
155
  #
146
156
  # If a block is given, it is treated as the :converter option.
147
157
  def self.register(db_type, opts={}, &block)
148
158
  db_type = db_type.to_s
149
159
  typecast_method = opts[:typecast_method]
150
160
  type = (typecast_method || opts[:type_symbol] || db_type).to_sym
161
+ type_procs = opts[:type_procs] || PG_TYPES
162
+ mod = opts[:typecast_methods_module] || DatabaseMethods
151
163
 
152
164
  if converter = opts[:converter]
153
165
  raise Error, "can't provide both a block and :converter option to register" if block
@@ -157,7 +169,7 @@ module Sequel
157
169
 
158
170
  if soid = opts[:scalar_oid]
159
171
  raise Error, "can't provide both a converter and :scalar_oid option to register" if converter
160
- raise Error, "no conversion proc for :scalar_oid=>#{soid.inspect} in PG_TYPES" unless converter = PG_TYPES[soid]
172
+ raise Error, "no conversion proc for :scalar_oid=>#{soid.inspect}" unless converter = type_procs[soid]
161
173
  end
162
174
 
163
175
  array_type = (opts[:array_type] || db_type).to_s.dup.freeze
@@ -165,15 +177,27 @@ module Sequel
165
177
 
166
178
  ARRAY_TYPES[db_type] = :"#{type}_array"
167
179
 
168
- DatabaseMethods.define_array_typecast_method(type, creator, opts.fetch(:scalar_typecast, type)) unless typecast_method
180
+ define_array_typecast_method(mod, type, creator, opts.fetch(:scalar_typecast, type)) unless typecast_method
169
181
 
170
182
  if oid = opts[:oid]
171
- Sequel::Postgres::PG_TYPES[oid] = creator
183
+ type_procs[oid] = creator
172
184
  end
173
185
 
174
186
  nil
175
187
  end
176
188
 
189
+ # Define a private array typecasting method in the given module for the given type that uses
190
+ # the creator argument to do the type conversion.
191
+ def self.define_array_typecast_method(mod, type, creator, scalar_typecast)
192
+ mod.class_eval do
193
+ meth = :"typecast_value_#{type}_array"
194
+ scalar_typecast_method = :"typecast_value_#{scalar_typecast}"
195
+ define_method(meth){|v| typecast_value_pg_array(v, creator, scalar_typecast_method)}
196
+ private meth
197
+ end
198
+ end
199
+ private_class_method :define_array_typecast_method
200
+
177
201
  module DatabaseMethods
178
202
  APOS = "'".freeze
179
203
  DOUBLE_APOS = "''".freeze
@@ -181,22 +205,6 @@ module Sequel
181
205
  ESCAPE_REPLACEMENT = '\\\\\1'.freeze
182
206
  BLOB_RANGE = 1...-1
183
207
 
184
- # Reset the conversion procs when extending the Database object, so
185
- # it will pick up the array convertors. This is only done for the native
186
- # postgres adapter.
187
- def self.extended(db)
188
- db.reset_conversion_procs if db.respond_to?(:reset_conversion_procs)
189
- end
190
-
191
- # Define a private array typecasting method for the given type that uses
192
- # the creator argument to do the type conversion.
193
- def self.define_array_typecast_method(type, creator, scalar_typecast)
194
- meth = :"typecast_value_#{type}_array"
195
- scalar_typecast_method = :"typecast_value_#{scalar_typecast}"
196
- define_method(meth){|v| typecast_value_pg_array(v, creator, scalar_typecast_method)}
197
- private meth
198
- end
199
-
200
208
  # Handle arrays in bound variables
201
209
  def bound_variable_arg(arg, conn)
202
210
  case arg
@@ -239,12 +247,11 @@ module Sequel
239
247
  # Manually override the typecasting for timestamp array types so that
240
248
  # they use the database's timezone instead of the global Sequel
241
249
  # timezone.
242
- def get_conversion_procs(conn)
250
+ def get_conversion_procs
243
251
  procs = super
244
252
 
245
- converter = method(:to_application_timestamp)
246
- procs[1115] = Creator.new("timestamp without time zone", converter)
247
- procs[1185] = Creator.new("timestamp with time zone", converter)
253
+ procs[1115] = Creator.new("timestamp without time zone", procs[1114])
254
+ procs[1185] = Creator.new("timestamp with time zone", procs[1184])
248
255
 
249
256
  procs
250
257
  end
@@ -271,8 +278,6 @@ module Sequel
271
278
  value = Sequel.recursive_map(value, method(scalar_typecast_method))
272
279
  end
273
280
  PGArray.new(value, creator.type)
274
- when String
275
- creator.call(value)
276
281
  else
277
282
  raise Sequel::InvalidValue, "invalid value for array type: #{value.inspect}"
278
283
  end
@@ -497,15 +502,36 @@ module Sequel
497
502
  end
498
503
  end
499
504
 
505
+ module SQL::Builders
506
+ # Return a Postgres::PGArray proxy for the given array and database array type.
507
+ def pg_array(v, array_type=nil)
508
+ case v
509
+ when Postgres::PGArray
510
+ if array_type.nil? || v.array_type == array_type
511
+ v
512
+ else
513
+ Postgres::PGArray.new(v.to_a, array_type)
514
+ end
515
+ when Array
516
+ Postgres::PGArray.new(v, array_type)
517
+ else
518
+ # May not be defined unless the pg_array_ops extension is used
519
+ pg_array_op(v)
520
+ end
521
+ end
522
+ end
523
+
500
524
  Database.register_extension(:pg_array, Postgres::PGArray::DatabaseMethods)
501
525
  end
502
526
 
503
- class Array
504
- # Return a PGArray proxy to the receiver, using a
505
- # specific database type if given. This is mostly useful
506
- # as a short cut for creating PGArray objects that didn't
507
- # come from the database.
508
- def pg_array(type=nil)
509
- Sequel::Postgres::PGArray.new(self, type)
527
+ if Sequel.core_extensions?
528
+ class Array
529
+ # Return a PGArray proxy to the receiver, using a
530
+ # specific database type if given. This is mostly useful
531
+ # as a short cut for creating PGArray objects that didn't
532
+ # come from the database.
533
+ def pg_array(type=nil)
534
+ Sequel::Postgres::PGArray.new(self, type)
535
+ end
510
536
  end
511
537
  end
@@ -5,8 +5,22 @@
5
5
  #
6
6
  # Sequel.extension :pg_array_ops
7
7
  #
8
- # The most common usage is taking an object that represents an SQL
9
- # identifier (such as a :symbol), and calling #pg_array on it:
8
+ # The most common usage is passing an expression to Sequel.pg_array_op:
9
+ #
10
+ # ia = Sequel.pg_array_op(:int_array_column)
11
+ #
12
+ # If you have also loaded the pg_array extension, you can use
13
+ # Sequel.pg_array as well:
14
+ #
15
+ # ia = Sequel.pg_array(:int_array_column)
16
+ #
17
+ # Also, on most Sequel expression objects, you can call the pg_array
18
+ # method:
19
+ #
20
+ # ia = Sequel.expr(:int_array_column).pg_array
21
+ #
22
+ # If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]),
23
+ # you can also call Symbol#pg_array:
10
24
  #
11
25
  # ia = :int_array_column.pg_array
12
26
  #
@@ -211,6 +225,18 @@ module Sequel
211
225
  end
212
226
  end
213
227
 
228
+ module SQL::Builders
229
+ # Return the object wrapped in an Postgres::ArrayOp.
230
+ def pg_array_op(v)
231
+ case v
232
+ when Postgres::ArrayOp
233
+ v
234
+ else
235
+ Postgres::ArrayOp.new(v)
236
+ end
237
+ end
238
+ end
239
+
214
240
  class SQL::GenericExpression
215
241
  include Sequel::Postgres::ArrayOpMethods
216
242
  end
@@ -220,6 +246,8 @@ module Sequel
220
246
  end
221
247
  end
222
248
 
223
- class Symbol
224
- include Sequel::Postgres::ArrayOpMethods
249
+ if Sequel.core_extensions?
250
+ class Symbol
251
+ include Sequel::Postgres::ArrayOpMethods
252
+ end
225
253
  end