sequel 3.37.0 → 3.38.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.
- data/CHANGELOG +56 -0
- data/README.rdoc +82 -58
- data/Rakefile +6 -5
- data/bin/sequel +1 -1
- data/doc/active_record.rdoc +67 -52
- data/doc/advanced_associations.rdoc +33 -48
- data/doc/association_basics.rdoc +41 -51
- data/doc/cheat_sheet.rdoc +21 -21
- data/doc/core_extensions.rdoc +374 -0
- data/doc/dataset_basics.rdoc +5 -5
- data/doc/dataset_filtering.rdoc +47 -43
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +4 -5
- data/doc/model_hooks.rdoc +3 -3
- data/doc/object_model.rdoc +31 -25
- data/doc/opening_databases.rdoc +19 -5
- data/doc/prepared_statements.rdoc +2 -2
- data/doc/querying.rdoc +109 -52
- data/doc/reflection.rdoc +6 -6
- data/doc/release_notes/3.38.0.txt +234 -0
- data/doc/schema_modification.rdoc +22 -13
- data/doc/sharding.rdoc +8 -9
- data/doc/sql.rdoc +154 -112
- data/doc/testing.rdoc +47 -7
- data/doc/thread_safety.rdoc +1 -1
- data/doc/transactions.rdoc +1 -1
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +29 -43
- data/lib/sequel/adapters/do/postgres.rb +1 -4
- data/lib/sequel/adapters/jdbc.rb +14 -3
- data/lib/sequel/adapters/jdbc/db2.rb +9 -0
- data/lib/sequel/adapters/jdbc/derby.rb +41 -4
- data/lib/sequel/adapters/jdbc/jtds.rb +11 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -6
- data/lib/sequel/adapters/mock.rb +10 -4
- data/lib/sequel/adapters/postgres.rb +1 -28
- data/lib/sequel/adapters/shared/mssql.rb +23 -13
- data/lib/sequel/adapters/shared/postgres.rb +46 -0
- data/lib/sequel/adapters/swift.rb +21 -13
- data/lib/sequel/adapters/swift/mysql.rb +1 -0
- data/lib/sequel/adapters/swift/postgres.rb +4 -5
- data/lib/sequel/adapters/swift/sqlite.rb +2 -1
- data/lib/sequel/adapters/tinytds.rb +14 -2
- data/lib/sequel/adapters/utils/pg_types.rb +5 -0
- data/lib/sequel/core.rb +29 -17
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +3 -0
- data/lib/sequel/dataset/actions.rb +5 -6
- data/lib/sequel/dataset/query.rb +7 -7
- data/lib/sequel/dataset/sql.rb +5 -18
- data/lib/sequel/extensions/core_extensions.rb +8 -12
- data/lib/sequel/extensions/pg_array.rb +59 -33
- data/lib/sequel/extensions/pg_array_ops.rb +32 -4
- data/lib/sequel/extensions/pg_auto_parameterize.rb +1 -1
- data/lib/sequel/extensions/pg_hstore.rb +32 -17
- data/lib/sequel/extensions/pg_hstore_ops.rb +32 -3
- data/lib/sequel/extensions/pg_inet.rb +1 -2
- data/lib/sequel/extensions/pg_interval.rb +0 -1
- data/lib/sequel/extensions/pg_json.rb +41 -23
- data/lib/sequel/extensions/pg_range.rb +36 -11
- data/lib/sequel/extensions/pg_range_ops.rb +32 -4
- data/lib/sequel/extensions/pg_row.rb +572 -0
- data/lib/sequel/extensions/pg_row_ops.rb +164 -0
- data/lib/sequel/extensions/query.rb +3 -3
- data/lib/sequel/extensions/schema_dumper.rb +7 -8
- data/lib/sequel/extensions/select_remove.rb +1 -1
- data/lib/sequel/model/base.rb +1 -0
- data/lib/sequel/no_core_ext.rb +1 -1
- data/lib/sequel/plugins/pg_row.rb +121 -0
- data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
- data/lib/sequel/plugins/validation_helpers.rb +31 -0
- data/lib/sequel/sql.rb +64 -44
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +37 -12
- data/spec/adapters/mysql_spec.rb +39 -75
- data/spec/adapters/oracle_spec.rb +11 -11
- data/spec/adapters/postgres_spec.rb +414 -237
- data/spec/adapters/spec_helper.rb +1 -1
- data/spec/adapters/sqlite_spec.rb +14 -14
- data/spec/core/database_spec.rb +6 -6
- data/spec/core/dataset_spec.rb +169 -205
- data/spec/core/expression_filters_spec.rb +182 -295
- data/spec/core/object_graph_spec.rb +6 -6
- data/spec/core/schema_spec.rb +14 -14
- data/spec/core/spec_helper.rb +1 -0
- data/spec/{extensions/core_extensions_spec.rb → core_extensions_spec.rb} +208 -14
- data/spec/extensions/columns_introspection_spec.rb +5 -5
- data/spec/extensions/hook_class_methods_spec.rb +28 -36
- data/spec/extensions/many_through_many_spec.rb +4 -4
- data/spec/extensions/pg_array_ops_spec.rb +15 -7
- data/spec/extensions/pg_array_spec.rb +81 -48
- data/spec/extensions/pg_auto_parameterize_spec.rb +2 -2
- data/spec/extensions/pg_hstore_ops_spec.rb +13 -9
- data/spec/extensions/pg_hstore_spec.rb +66 -65
- data/spec/extensions/pg_inet_spec.rb +2 -4
- data/spec/extensions/pg_interval_spec.rb +2 -3
- data/spec/extensions/pg_json_spec.rb +20 -18
- data/spec/extensions/pg_range_ops_spec.rb +11 -4
- data/spec/extensions/pg_range_spec.rb +30 -7
- data/spec/extensions/pg_row_ops_spec.rb +48 -0
- data/spec/extensions/pg_row_plugin_spec.rb +45 -0
- data/spec/extensions/pg_row_spec.rb +323 -0
- data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
- data/spec/extensions/query_literals_spec.rb +11 -11
- data/spec/extensions/query_spec.rb +3 -3
- data/spec/extensions/schema_dumper_spec.rb +20 -4
- data/spec/extensions/schema_spec.rb +18 -41
- data/spec/extensions/select_remove_spec.rb +4 -4
- data/spec/extensions/spec_helper.rb +4 -8
- data/spec/extensions/to_dot_spec.rb +5 -5
- data/spec/extensions/validation_class_methods_spec.rb +28 -16
- data/spec/integration/associations_test.rb +20 -20
- data/spec/integration/dataset_test.rb +98 -98
- data/spec/integration/eager_loader_test.rb +13 -27
- data/spec/integration/plugin_test.rb +5 -5
- data/spec/integration/prepared_statement_test.rb +22 -13
- data/spec/integration/schema_test.rb +28 -18
- data/spec/integration/spec_helper.rb +1 -1
- data/spec/integration/timezone_test.rb +2 -2
- data/spec/integration/type_test.rb +15 -6
- data/spec/model/association_reflection_spec.rb +1 -1
- data/spec/model/associations_spec.rb +4 -4
- data/spec/model/base_spec.rb +5 -5
- data/spec/model/eager_loading_spec.rb +15 -15
- data/spec/model/model_spec.rb +32 -32
- data/spec/model/record_spec.rb +16 -0
- data/spec/model/spec_helper.rb +2 -6
- data/spec/model/validations_spec.rb +1 -1
- 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
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
825
|
-
|
|
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
|
data/lib/sequel/dataset/query.rb
CHANGED
|
@@ -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
|
-
# (
|
|
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'
|
|
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
|
|
663
|
-
# DB[:items].order(
|
|
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
|
|
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
|
|
1123
|
-
# DB[:items].invert_order(:category, :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 = []
|
data/lib/sequel/dataset/sql.rb
CHANGED
|
@@ -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 =
|
|
204
|
-
COLUMN_REF_RE2 =
|
|
205
|
-
COLUMN_REF_RE3 =
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
#
|
|
215
|
-
#
|
|
216
|
-
#
|
|
217
|
-
#
|
|
218
|
-
#
|
|
219
|
-
|
|
220
|
-
|
|
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]
|
|
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}
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
250
|
+
def get_conversion_procs
|
|
243
251
|
procs = super
|
|
244
252
|
|
|
245
|
-
|
|
246
|
-
procs[
|
|
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
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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
|
|
9
|
-
#
|
|
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
|
-
|
|
224
|
-
|
|
249
|
+
if Sequel.core_extensions?
|
|
250
|
+
class Symbol
|
|
251
|
+
include Sequel::Postgres::ArrayOpMethods
|
|
252
|
+
end
|
|
225
253
|
end
|