sequel 3.35.0 → 3.36.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 +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
|