sequel 3.35.0 → 3.36.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +78 -0
- data/Rakefile +3 -3
- data/bin/sequel +3 -1
- data/doc/advanced_associations.rdoc +154 -11
- data/doc/migration.rdoc +18 -0
- data/doc/object_model.rdoc +541 -0
- data/doc/opening_databases.rdoc +4 -1
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/schema_modification.rdoc +0 -6
- data/lib/sequel/adapters/do/mysql.rb +7 -0
- data/lib/sequel/adapters/jdbc.rb +11 -3
- data/lib/sequel/adapters/jdbc/mysql.rb +3 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +10 -8
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/mock.rb +2 -6
- data/lib/sequel/adapters/mysql.rb +3 -9
- data/lib/sequel/adapters/mysql2.rb +12 -11
- data/lib/sequel/adapters/postgres.rb +32 -40
- data/lib/sequel/adapters/shared/mssql.rb +15 -11
- data/lib/sequel/adapters/shared/mysql.rb +28 -3
- data/lib/sequel/adapters/shared/oracle.rb +5 -0
- data/lib/sequel/adapters/shared/postgres.rb +59 -5
- data/lib/sequel/adapters/shared/sqlite.rb +3 -13
- data/lib/sequel/adapters/sqlite.rb +0 -7
- data/lib/sequel/adapters/swift/mysql.rb +2 -5
- data/lib/sequel/adapters/tinytds.rb +1 -2
- data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
- data/lib/sequel/connection_pool/threaded.rb +9 -1
- data/lib/sequel/database/dataset_defaults.rb +3 -1
- data/lib/sequel/database/misc.rb +7 -1
- data/lib/sequel/database/query.rb +11 -3
- data/lib/sequel/database/schema_generator.rb +40 -9
- data/lib/sequel/database/schema_methods.rb +6 -1
- data/lib/sequel/dataset/actions.rb +5 -5
- data/lib/sequel/dataset/prepared_statements.rb +3 -1
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/extensions/migration.rb +28 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +0 -9
- data/lib/sequel/extensions/pg_inet.rb +89 -0
- data/lib/sequel/extensions/pg_json.rb +178 -0
- data/lib/sequel/extensions/schema_dumper.rb +24 -6
- data/lib/sequel/model/associations.rb +19 -15
- data/lib/sequel/model/base.rb +11 -12
- data/lib/sequel/plugins/composition.rb +1 -2
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/json_serializer.rb +41 -4
- data/lib/sequel/plugins/nested_attributes.rb +72 -52
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +271 -1
- data/spec/adapters/sqlite_spec.rb +11 -0
- data/spec/core/connection_pool_spec.rb +26 -1
- data/spec/core/database_spec.rb +19 -0
- data/spec/core/dataset_spec.rb +45 -5
- data/spec/core/expression_filters_spec.rb +31 -67
- data/spec/core/mock_adapter_spec.rb +4 -0
- data/spec/extensions/core_extensions_spec.rb +83 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/json_serializer_spec.rb +32 -1
- data/spec/extensions/migration_spec.rb +28 -0
- data/spec/extensions/nested_attributes_spec.rb +134 -1
- data/spec/extensions/optimistic_locking_spec.rb +15 -1
- data/spec/extensions/pg_hstore_spec.rb +1 -1
- data/spec/extensions/pg_inet_spec.rb +44 -0
- data/spec/extensions/pg_json_spec.rb +101 -0
- data/spec/extensions/prepared_statements_spec.rb +30 -0
- data/spec/extensions/rcte_tree_spec.rb +9 -0
- data/spec/extensions/schema_dumper_spec.rb +195 -7
- data/spec/extensions/serialization_spec.rb +4 -0
- data/spec/extensions/spec_helper.rb +9 -1
- data/spec/extensions/tactical_eager_loading_spec.rb +8 -0
- data/spec/integration/database_test.rb +5 -1
- data/spec/integration/prepared_statement_test.rb +20 -2
- data/spec/model/associations_spec.rb +27 -0
- data/spec/model/base_spec.rb +54 -0
- data/spec/model/model_spec.rb +6 -0
- data/spec/model/record_spec.rb +18 -0
- data/spec/rcov.opts +2 -0
- metadata +14 -3
@@ -21,13 +21,10 @@ module Sequel
|
|
21
21
|
db_type == 'tinyint(1)' ? :boolean : super
|
22
22
|
end
|
23
23
|
|
24
|
-
#
|
25
|
-
# Turn that off unless explicitly enabled.
|
24
|
+
# Apply the connectiong setting SQLs for every new connection.
|
26
25
|
def setup_connection(conn)
|
26
|
+
mysql_connection_setting_sqls.each{|sql| log_yield(sql){conn.execute(sql)}}
|
27
27
|
super
|
28
|
-
sql = "SET SQL_AUTO_IS_NULL=0"
|
29
|
-
log_yield(sql){conn.execute(sql)} unless opts[:auto_is_null]
|
30
|
-
conn
|
31
28
|
end
|
32
29
|
end
|
33
30
|
|
@@ -7,8 +7,7 @@ module Sequel
|
|
7
7
|
include Sequel::MSSQL::DatabaseMethods
|
8
8
|
set_adapter_scheme :tinytds
|
9
9
|
|
10
|
-
# Transfer the :
|
11
|
-
# :dataserver and :username options.
|
10
|
+
# Transfer the :user option to the :username option.
|
12
11
|
def connect(server)
|
13
12
|
opts = server_opts(server)
|
14
13
|
opts[:username] = opts[:user]
|
@@ -221,7 +221,11 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
221
221
|
if @connections_to_remove.include?(conn)
|
222
222
|
remove(thread, conn, server)
|
223
223
|
else
|
224
|
-
|
224
|
+
if @queue
|
225
|
+
available_connections(server).unshift(allocated(server).delete(thread))
|
226
|
+
else
|
227
|
+
available_connections(server) << allocated(server).delete(thread)
|
228
|
+
end
|
225
229
|
end
|
226
230
|
end
|
227
231
|
|
@@ -13,6 +13,9 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
13
13
|
attr_reader :allocated
|
14
14
|
|
15
15
|
# The following additional options are respected:
|
16
|
+
# * :connection_handling - Set how to handle available connections. By default,
|
17
|
+
# uses a a stack for performance. Can be set to :queue to use a queue, which reduces
|
18
|
+
# the chances of connections becoming stale.
|
16
19
|
# * :max_connections - The maximum number of connections the connection pool
|
17
20
|
# will open (default 4)
|
18
21
|
# * :pool_sleep_time - The amount of time to sleep before attempting to acquire
|
@@ -24,6 +27,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
24
27
|
@max_size = Integer(opts[:max_connections] || 4)
|
25
28
|
raise(Sequel::Error, ':max_connections must be positive') if @max_size < 1
|
26
29
|
@mutex = Mutex.new
|
30
|
+
@queue = opts[:connection_handling] == :queue
|
27
31
|
@available_connections = []
|
28
32
|
@allocated = {}
|
29
33
|
@timeout = Integer(opts[:pool_timeout] || 5)
|
@@ -153,7 +157,11 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
153
157
|
# Releases the connection assigned to the supplied thread back to the pool.
|
154
158
|
# The calling code should already have the mutex before calling this.
|
155
159
|
def release(thread)
|
156
|
-
|
160
|
+
if @queue
|
161
|
+
@available_connections.unshift(@allocated.delete(thread))
|
162
|
+
else
|
163
|
+
@available_connections << @allocated.delete(thread)
|
164
|
+
end
|
157
165
|
end
|
158
166
|
|
159
167
|
# Yield to the block while inside the mutex. The calling code should NOT
|
@@ -50,7 +50,9 @@ module Sequel
|
|
50
50
|
# an optional options hash.
|
51
51
|
attr_reader :dataset_class
|
52
52
|
|
53
|
-
# The default schema to use, generally should be nil.
|
53
|
+
# The default schema to use, generally should be nil. This sets
|
54
|
+
# the default schema used for some schema modification and
|
55
|
+
# introspection queries, but does not effect most dataset code.
|
54
56
|
attr_accessor :default_schema
|
55
57
|
|
56
58
|
# If the database has any dataset modules associated with it,
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -26,7 +26,7 @@ module Sequel
|
|
26
26
|
# options hash.
|
27
27
|
#
|
28
28
|
# Accepts the following options:
|
29
|
-
# :default_schema :: The default schema to use,
|
29
|
+
# :default_schema :: The default schema to use, see #default_schema.
|
30
30
|
# :disconnection_proc :: A proc used to disconnect the connection
|
31
31
|
# :identifier_input_method :: A string method symbol to call on identifiers going into the database
|
32
32
|
# :identifier_output_method :: A string method symbol to call on identifiers coming from the database
|
@@ -114,6 +114,12 @@ module Sequel
|
|
114
114
|
Sequel.convert_output_timestamp(v, timezone)
|
115
115
|
end
|
116
116
|
|
117
|
+
# Whether the database uses a global namespace for the index. If
|
118
|
+
# false, the indexes are going to be namespaced per table.
|
119
|
+
def global_index_namespace?
|
120
|
+
true
|
121
|
+
end
|
122
|
+
|
117
123
|
# Return true if already in a transaction given the options,
|
118
124
|
# false otherwise. Respects the :server option for selecting
|
119
125
|
# a shard.
|
@@ -55,8 +55,8 @@ module Sequel
|
|
55
55
|
#
|
56
56
|
# DB[:items].filter(:id=>1).prepare(:first, :sa)
|
57
57
|
# DB.call(:sa) # SELECT * FROM items WHERE id = 1
|
58
|
-
def call(ps_name, hash={})
|
59
|
-
prepared_statement(ps_name).call(hash)
|
58
|
+
def call(ps_name, hash={}, &block)
|
59
|
+
prepared_statement(ps_name).call(hash, &block)
|
60
60
|
end
|
61
61
|
|
62
62
|
# Executes the given SQL on the database. This method should be overridden in descendants.
|
@@ -232,7 +232,7 @@ module Sequel
|
|
232
232
|
# either all statements are successful or none of the statements are
|
233
233
|
# successful. Note that MySQL MyISAM tabels do not support transactions.
|
234
234
|
#
|
235
|
-
# The following options are respected:
|
235
|
+
# The following general options are respected:
|
236
236
|
#
|
237
237
|
# :isolation :: The transaction isolation level to use for this transaction,
|
238
238
|
# should be :uncommitted, :committed, :repeatable, or :serializable,
|
@@ -249,6 +249,14 @@ module Sequel
|
|
249
249
|
# only respected if the database/adapter supports savepoints. By
|
250
250
|
# default Sequel will reuse an existing transaction, so if you want to
|
251
251
|
# use a savepoint you must use this option.
|
252
|
+
#
|
253
|
+
# PostgreSQL specific options:
|
254
|
+
#
|
255
|
+
# :deferrable :: (9.1+) If present, set to DEFERRABLE if true or NOT DEFERRABLE if false.
|
256
|
+
# :read_only :: If present, set to READ ONLY if true or READ WRITE if false.
|
257
|
+
# :synchronous :: if non-nil, set synchronous_commit
|
258
|
+
# appropriately. Valid values true, :on, false, :off, :local (9.1+),
|
259
|
+
# and :remote_write (9.2+).
|
252
260
|
def transaction(opts={}, &block)
|
253
261
|
synchronize(opts[:server]) do |conn|
|
254
262
|
return yield(conn) if already_in_transaction?(conn, opts)
|
@@ -98,14 +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
|
-
# :size :: The size of the column, generally used with string
|
102
|
-
# columns to specify the maximum number of characters the column will hold.
|
103
|
-
# An array of two integers can be provided to set the size and the
|
104
|
-
# precision, respectively, of decimal columns.
|
105
101
|
# :unique :: Mark the column as unique, generally has the same effect as
|
106
102
|
# creating a unique index on the column.
|
107
|
-
# :unsigned :: Make the column type unsigned, only useful for integer
|
108
|
-
# columns.
|
109
103
|
def column(name, type, opts = {})
|
110
104
|
columns << {:name => name, :type => type}.merge(opts)
|
111
105
|
if index_opts = opts[:index]
|
@@ -151,6 +145,12 @@ module Sequel
|
|
151
145
|
end
|
152
146
|
|
153
147
|
# Add a full text index on the given columns to the DDL.
|
148
|
+
#
|
149
|
+
# PostgreSQL specific options:
|
150
|
+
# :language :: Set a language to use for the index (default: simple).
|
151
|
+
#
|
152
|
+
# Microsoft SQL Server specific options:
|
153
|
+
# :key_index :: The KEY INDEX to use for the full text index.
|
154
154
|
def full_text_index(columns, opts = {})
|
155
155
|
index(columns, opts.merge(:type => :full_text))
|
156
156
|
end
|
@@ -161,12 +161,26 @@ module Sequel
|
|
161
161
|
end
|
162
162
|
|
163
163
|
# Add an index on the given column(s) with the given options to the DDL.
|
164
|
-
#
|
164
|
+
# General options:
|
165
165
|
#
|
166
|
+
# :name :: The name to use for the index. If not given, a default name
|
167
|
+
# based on the table and columns is used.
|
166
168
|
# :type :: The type of index to use (only supported by some databases)
|
167
169
|
# :unique :: Make the index unique, so duplicate values are not allowed.
|
168
170
|
# :where :: Create a partial index (only supported by some databases)
|
169
171
|
#
|
172
|
+
# PostgreSQL specific options:
|
173
|
+
#
|
174
|
+
# :concurrently :: Create the index concurrently, so it doesn't block
|
175
|
+
# operations on the table while the index is being
|
176
|
+
# built.
|
177
|
+
# :op_class :: Use a specific operator class in the index.
|
178
|
+
#
|
179
|
+
# Microsoft SQL Server specific options:
|
180
|
+
#
|
181
|
+
# :include :: Include additional column values in the index, without
|
182
|
+
# actually indexing on those values.
|
183
|
+
#
|
170
184
|
# index :name
|
171
185
|
# # CREATE INDEX table_name_index ON table (name)
|
172
186
|
#
|
@@ -345,7 +359,10 @@ module Sequel
|
|
345
359
|
@operations << {:op => :drop_column, :name => name}.merge(opts)
|
346
360
|
end
|
347
361
|
|
348
|
-
# Remove a constraint from the DDL for the table.
|
362
|
+
# Remove a constraint from the DDL for the table. MySQL/SQLite specific options:
|
363
|
+
#
|
364
|
+
# :type :: Set the type of constraint to drop, either :primary_key, :foreign_key,
|
365
|
+
# or :unique.
|
349
366
|
#
|
350
367
|
# drop_constraint(:unique_name) # DROP CONSTRAINT unique_name
|
351
368
|
# drop_constraint(:unique_name, :cascade=>true) # DROP CONSTRAINT unique_name CASCADE
|
@@ -353,7 +370,17 @@ module Sequel
|
|
353
370
|
@operations << {:op => :drop_constraint, :name => name}.merge(opts)
|
354
371
|
end
|
355
372
|
|
356
|
-
# Remove an index from the DDL for the table.
|
373
|
+
# Remove an index from the DDL for the table. General options:
|
374
|
+
#
|
375
|
+
# :name :: The name of the index to drop. If not given, uses the same name
|
376
|
+
# that would be used by add_index with the same columns.
|
377
|
+
#
|
378
|
+
# PostgreSQL specific options:
|
379
|
+
#
|
380
|
+
# :cascade :: Cascade the index drop to dependent objects.
|
381
|
+
# :concurrently :: Drop the index using CONCURRENTLY, which doesn't block
|
382
|
+
# operations on the table. Supported in PostgreSQL 9.2+.
|
383
|
+
# :if_exists :: Only drop the index if it already exists.
|
357
384
|
#
|
358
385
|
# drop_index(:artist_id) # DROP INDEX table_artist_id_index
|
359
386
|
# drop_index([:a, :b]) # DROP INDEX table_a_b_index
|
@@ -379,6 +406,10 @@ module Sequel
|
|
379
406
|
# Modify a column's type in the DDL for the table.
|
380
407
|
#
|
381
408
|
# set_column_type(:artist_name, 'char(10)') # ALTER COLUMN artist_name TYPE char(10)
|
409
|
+
#
|
410
|
+
# PostgreSQL specific options:
|
411
|
+
#
|
412
|
+
# :using :: Add a USING clause that specifies how to convert existing values to new values.
|
382
413
|
def set_column_type(name, type, opts={})
|
383
414
|
@operations << {:op => :set_column_type, :name => name, :type => type}.merge(opts)
|
384
415
|
end
|
@@ -131,13 +131,18 @@ module Sequel
|
|
131
131
|
# index :title
|
132
132
|
# end
|
133
133
|
#
|
134
|
-
#
|
134
|
+
# General options:
|
135
135
|
# :as :: Create the table using the value, which should be either a
|
136
136
|
# dataset or a literal SQL string. If this option is used,
|
137
137
|
# a block should not be given to the method.
|
138
138
|
# :ignore_index_errors :: Ignore any errors when creating indexes.
|
139
139
|
# :temp :: Create the table as a temporary table.
|
140
140
|
#
|
141
|
+
# MySQL specific options:
|
142
|
+
# :charset :: The character set to use for the table.
|
143
|
+
# :collate :: The collation to use for the table.
|
144
|
+
# :engine :: The table engine to use for the table.
|
145
|
+
#
|
141
146
|
# See <tt>Schema::Generator</tt> and the {"Schema Modification" guide}[link:files/doc/schema_modification_rdoc.html].
|
142
147
|
def create_table(name, options={}, &block)
|
143
148
|
remove_cached_schema(name)
|
@@ -204,12 +204,12 @@ module Sequel
|
|
204
204
|
#
|
205
205
|
# ds.get{sum(id)} # SELECT sum(id) FROM table LIMIT 1
|
206
206
|
# # => 6
|
207
|
-
def get(column=nil, &block)
|
208
|
-
if
|
209
|
-
raise(Error, ARG_BLOCK_ERROR_MSG)
|
210
|
-
select(column).single_value
|
211
|
-
else
|
207
|
+
def get(column=(no_arg=true; nil), &block)
|
208
|
+
if block
|
209
|
+
raise(Error, ARG_BLOCK_ERROR_MSG) unless no_arg
|
212
210
|
select(&block).single_value
|
211
|
+
else
|
212
|
+
select(column).single_value
|
213
213
|
end
|
214
214
|
end
|
215
215
|
|
@@ -80,7 +80,7 @@ module Sequel
|
|
80
80
|
# the type of the statement and the prepared_modify_values.
|
81
81
|
def prepared_sql
|
82
82
|
case @prepared_type
|
83
|
-
when :select, :all
|
83
|
+
when :select, :all, :each
|
84
84
|
# Most common scenario, so listed first.
|
85
85
|
select_sql
|
86
86
|
when :first
|
@@ -131,6 +131,8 @@ module Sequel
|
|
131
131
|
when :select, :all
|
132
132
|
# Most common scenario, so listed first
|
133
133
|
all(&block)
|
134
|
+
when :each
|
135
|
+
each(&block)
|
134
136
|
when :insert_select
|
135
137
|
with_sql(prepared_sql).first
|
136
138
|
when :first
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -164,7 +164,7 @@ module Sequel
|
|
164
164
|
# DB[:items].filter('price < ?', 100)
|
165
165
|
# # SELECT * FROM items WHERE price < 100
|
166
166
|
#
|
167
|
-
# DB[:items].filter([[:id,
|
167
|
+
# DB[:items].filter([[:id, [1,2,3]], [:id, 0..10]])
|
168
168
|
# # SELECT * FROM items WHERE ((id IN (1, 2, 3)) AND ((id >= 0) AND (id <= 10)))
|
169
169
|
#
|
170
170
|
# DB[:items].filter('price < 100')
|
@@ -340,11 +340,28 @@ module Sequel
|
|
340
340
|
class Error < Sequel::Error
|
341
341
|
end
|
342
342
|
|
343
|
+
# Exception class raised when Migrator.check_current signals that it is
|
344
|
+
# not current.
|
345
|
+
class NotCurrentError < Error
|
346
|
+
end
|
347
|
+
|
343
348
|
# Wrapper for +run+, maintaining backwards API compatibility
|
344
349
|
def self.apply(db, directory, target = nil, current = nil)
|
345
350
|
run(db, directory, :target => target, :current => current)
|
346
351
|
end
|
347
352
|
|
353
|
+
# Raise a NotCurrentError unless the migrator is current, takes the same
|
354
|
+
# arguments as #run.
|
355
|
+
def self.check_current(*args)
|
356
|
+
raise(NotCurrentError, 'migrator is not current') unless is_current?(*args)
|
357
|
+
end
|
358
|
+
|
359
|
+
# Return whether the migrator is current (i.e. it does not need to make
|
360
|
+
# any changes). Takes the same arguments as #run.
|
361
|
+
def self.is_current?(db, directory, opts={})
|
362
|
+
migrator_class(directory).new(db, directory, opts).is_current?
|
363
|
+
end
|
364
|
+
|
348
365
|
# Migrates the supplied database using the migration files in the the specified directory. Options:
|
349
366
|
# * :column :: The column in the :table argument storing the migration version (default: :version).
|
350
367
|
# * :current :: The current version of the database. If not given, it is retrieved from the database
|
@@ -484,6 +501,11 @@ module Sequel
|
|
484
501
|
@migrations = get_migrations
|
485
502
|
end
|
486
503
|
|
504
|
+
# The integer migrator is current if the current version is the same as the target version.
|
505
|
+
def is_current?
|
506
|
+
current_migration_version == target
|
507
|
+
end
|
508
|
+
|
487
509
|
# Apply all migrations on the database
|
488
510
|
def run
|
489
511
|
migrations.zip(version_numbers).each do |m, v|
|
@@ -601,6 +623,12 @@ module Sequel
|
|
601
623
|
@migration_tuples = get_migration_tuples
|
602
624
|
end
|
603
625
|
|
626
|
+
# The timestamp migrator is current if there are no migrations to apply
|
627
|
+
# in either direction.
|
628
|
+
def is_current?
|
629
|
+
migration_tuples.empty?
|
630
|
+
end
|
631
|
+
|
604
632
|
# Apply all migration tuples on the database
|
605
633
|
def run
|
606
634
|
migration_tuples.each do |m, f, direction|
|
@@ -98,15 +98,6 @@ module Sequel
|
|
98
98
|
end
|
99
99
|
super
|
100
100
|
end
|
101
|
-
|
102
|
-
# If the sql string has an embedded parameter array,
|
103
|
-
# extract the arguments from that.
|
104
|
-
def execute_insert(sql, opts={})
|
105
|
-
if sql.is_a?(StringWithArray) && (args = sql.args)
|
106
|
-
opts = opts.merge(:arguments=>args)
|
107
|
-
end
|
108
|
-
super
|
109
|
-
end
|
110
101
|
end
|
111
102
|
|
112
103
|
module DatasetMethods
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# The pg_inet extension adds support for Sequel to handle
|
2
|
+
# PostgreSQL's inet and cidr types using ruby's IPAddr class.
|
3
|
+
#
|
4
|
+
# This extension integrates with Sequel's native postgres adapter, so
|
5
|
+
# that when inet/cidr fields are retrieved, they are returned as
|
6
|
+
# IPAddr instances
|
7
|
+
#
|
8
|
+
# After loading the extension, you should extend your dataset
|
9
|
+
# with a module so that it correctly handles the inet/cidr type:
|
10
|
+
#
|
11
|
+
# DB.extend Sequel::Postgres::InetDatabaseMethods
|
12
|
+
#
|
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
|
15
|
+
# set it to typecast the inet/cidr column(s) on load.
|
16
|
+
#
|
17
|
+
# This extension does not add special support for the macaddr
|
18
|
+
# type. Ruby doesn't have a stdlib class that represents mac
|
19
|
+
# addresses, so these will still be returned as strings.
|
20
|
+
|
21
|
+
require 'ipaddr'
|
22
|
+
|
23
|
+
module Sequel
|
24
|
+
module Postgres
|
25
|
+
# Methods enabling Database object integration with the inet/cidr types.
|
26
|
+
module InetDatabaseMethods
|
27
|
+
|
28
|
+
# Reset the conversion procs when extending the Database object, so
|
29
|
+
# it will pick up the inet/cidr convertor. Also, extend the datasets
|
30
|
+
# with support for literalizing the IPAddr types.
|
31
|
+
def self.extended(db)
|
32
|
+
db.reset_conversion_procs if db.respond_to?(:reset_conversion_procs)
|
33
|
+
db.extend_datasets(InetDatasetMethods)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Convert an IPAddr arg to a string. Probably not necessary, but done
|
37
|
+
# for safety.
|
38
|
+
def bound_variable_arg(arg, conn)
|
39
|
+
case arg
|
40
|
+
when IPAddr
|
41
|
+
"#{arg.to_s}/#{arg.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Make the column type detection recognize the inet and cidr types.
|
48
|
+
def schema_column_type(db_type)
|
49
|
+
case db_type
|
50
|
+
when 'inet', 'cidr'
|
51
|
+
:ipaddr
|
52
|
+
else
|
53
|
+
super
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Typecast the given value to an IPAddr object.
|
60
|
+
def typecast_value_ipaddr(value)
|
61
|
+
case value
|
62
|
+
when IPAddr
|
63
|
+
value
|
64
|
+
when String
|
65
|
+
IPAddr.new(value)
|
66
|
+
else
|
67
|
+
raise Sequel::InvalidValue, "invalid value for inet/cidr: #{value.inspect}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
module InetDatasetMethods
|
73
|
+
private
|
74
|
+
|
75
|
+
# Convert IPAddr value to a string and append a literal version
|
76
|
+
# of the string to the sql.
|
77
|
+
def literal_other_append(sql, value)
|
78
|
+
if value.is_a?(IPAddr)
|
79
|
+
literal_string_append(sql, "#{value.to_s}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}")
|
80
|
+
else
|
81
|
+
super
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
PG_TYPES = {} unless defined?(PG_TYPES)
|
87
|
+
PG_TYPES[869] = PG_TYPES[650] = IPAddr.method(:new)
|
88
|
+
end
|
89
|
+
end
|