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.
Files changed (81) hide show
  1. data/CHANGELOG +78 -0
  2. data/Rakefile +3 -3
  3. data/bin/sequel +3 -1
  4. data/doc/advanced_associations.rdoc +154 -11
  5. data/doc/migration.rdoc +18 -0
  6. data/doc/object_model.rdoc +541 -0
  7. data/doc/opening_databases.rdoc +4 -1
  8. data/doc/release_notes/3.36.0.txt +245 -0
  9. data/doc/schema_modification.rdoc +0 -6
  10. data/lib/sequel/adapters/do/mysql.rb +7 -0
  11. data/lib/sequel/adapters/jdbc.rb +11 -3
  12. data/lib/sequel/adapters/jdbc/mysql.rb +3 -5
  13. data/lib/sequel/adapters/jdbc/postgresql.rb +10 -8
  14. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  15. data/lib/sequel/adapters/mock.rb +2 -6
  16. data/lib/sequel/adapters/mysql.rb +3 -9
  17. data/lib/sequel/adapters/mysql2.rb +12 -11
  18. data/lib/sequel/adapters/postgres.rb +32 -40
  19. data/lib/sequel/adapters/shared/mssql.rb +15 -11
  20. data/lib/sequel/adapters/shared/mysql.rb +28 -3
  21. data/lib/sequel/adapters/shared/oracle.rb +5 -0
  22. data/lib/sequel/adapters/shared/postgres.rb +59 -5
  23. data/lib/sequel/adapters/shared/sqlite.rb +3 -13
  24. data/lib/sequel/adapters/sqlite.rb +0 -7
  25. data/lib/sequel/adapters/swift/mysql.rb +2 -5
  26. data/lib/sequel/adapters/tinytds.rb +1 -2
  27. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
  28. data/lib/sequel/connection_pool/threaded.rb +9 -1
  29. data/lib/sequel/database/dataset_defaults.rb +3 -1
  30. data/lib/sequel/database/misc.rb +7 -1
  31. data/lib/sequel/database/query.rb +11 -3
  32. data/lib/sequel/database/schema_generator.rb +40 -9
  33. data/lib/sequel/database/schema_methods.rb +6 -1
  34. data/lib/sequel/dataset/actions.rb +5 -5
  35. data/lib/sequel/dataset/prepared_statements.rb +3 -1
  36. data/lib/sequel/dataset/query.rb +1 -1
  37. data/lib/sequel/extensions/migration.rb +28 -0
  38. data/lib/sequel/extensions/pg_auto_parameterize.rb +0 -9
  39. data/lib/sequel/extensions/pg_inet.rb +89 -0
  40. data/lib/sequel/extensions/pg_json.rb +178 -0
  41. data/lib/sequel/extensions/schema_dumper.rb +24 -6
  42. data/lib/sequel/model/associations.rb +19 -15
  43. data/lib/sequel/model/base.rb +11 -12
  44. data/lib/sequel/plugins/composition.rb +1 -2
  45. data/lib/sequel/plugins/eager_each.rb +59 -0
  46. data/lib/sequel/plugins/json_serializer.rb +41 -4
  47. data/lib/sequel/plugins/nested_attributes.rb +72 -52
  48. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  49. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  50. data/lib/sequel/version.rb +1 -1
  51. data/spec/adapters/postgres_spec.rb +271 -1
  52. data/spec/adapters/sqlite_spec.rb +11 -0
  53. data/spec/core/connection_pool_spec.rb +26 -1
  54. data/spec/core/database_spec.rb +19 -0
  55. data/spec/core/dataset_spec.rb +45 -5
  56. data/spec/core/expression_filters_spec.rb +31 -67
  57. data/spec/core/mock_adapter_spec.rb +4 -0
  58. data/spec/extensions/core_extensions_spec.rb +83 -0
  59. data/spec/extensions/eager_each_spec.rb +34 -0
  60. data/spec/extensions/inflector_spec.rb +0 -4
  61. data/spec/extensions/json_serializer_spec.rb +32 -1
  62. data/spec/extensions/migration_spec.rb +28 -0
  63. data/spec/extensions/nested_attributes_spec.rb +134 -1
  64. data/spec/extensions/optimistic_locking_spec.rb +15 -1
  65. data/spec/extensions/pg_hstore_spec.rb +1 -1
  66. data/spec/extensions/pg_inet_spec.rb +44 -0
  67. data/spec/extensions/pg_json_spec.rb +101 -0
  68. data/spec/extensions/prepared_statements_spec.rb +30 -0
  69. data/spec/extensions/rcte_tree_spec.rb +9 -0
  70. data/spec/extensions/schema_dumper_spec.rb +195 -7
  71. data/spec/extensions/serialization_spec.rb +4 -0
  72. data/spec/extensions/spec_helper.rb +9 -1
  73. data/spec/extensions/tactical_eager_loading_spec.rb +8 -0
  74. data/spec/integration/database_test.rb +5 -1
  75. data/spec/integration/prepared_statement_test.rb +20 -2
  76. data/spec/model/associations_spec.rb +27 -0
  77. data/spec/model/base_spec.rb +54 -0
  78. data/spec/model/model_spec.rb +6 -0
  79. data/spec/model/record_spec.rb +18 -0
  80. data/spec/rcov.opts +2 -0
  81. metadata +14 -3
@@ -21,13 +21,10 @@ module Sequel
21
21
  db_type == 'tinyint(1)' ? :boolean : super
22
22
  end
23
23
 
24
- # By default, MySQL 'where id is null' selects the last inserted id.
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 :host and :user options to 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
- available_connections(server) << allocated(server).delete(thread)
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
- @available_connections << @allocated.delete(thread)
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,
@@ -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, should generally be nil
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
- # The available options are:
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
- # Options:
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 column
209
- raise(Error, ARG_BLOCK_ERROR_MSG) if block
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
@@ -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, (1,2,3)], [:id, 0..10]])
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