module Sequel::Postgres::PGArray::DatabaseMethods
Constants
- BLOB_RANGE
Public Class Methods
Create the local hash of database type strings to schema type symbols, used for array types local to this database.
# File lib/sequel/extensions/pg_array.rb 86 def self.extended(db) 87 db.instance_exec do 88 @pg_array_schema_types ||= {} 89 register_array_type('timestamp without time zone', :oid=>1115, :scalar_oid=>1114, :type_symbol=>:datetime) 90 register_array_type('timestamp with time zone', :oid=>1185, :scalar_oid=>1184, :type_symbol=>:datetime_timezone, :scalar_typecast=>:datetime) 91 92 register_array_type('text', :oid=>1009, :scalar_oid=>25, :type_symbol=>:string) 93 register_array_type('integer', :oid=>1007, :scalar_oid=>23) 94 register_array_type('bigint', :oid=>1016, :scalar_oid=>20, :scalar_typecast=>:integer) 95 register_array_type('numeric', :oid=>1231, :scalar_oid=>1700, :type_symbol=>:decimal) 96 register_array_type('double precision', :oid=>1022, :scalar_oid=>701, :type_symbol=>:float) 97 98 register_array_type('boolean', :oid=>1000, :scalar_oid=>16) 99 register_array_type('bytea', :oid=>1001, :scalar_oid=>17, :type_symbol=>:blob) 100 register_array_type('date', :oid=>1182, :scalar_oid=>1082) 101 register_array_type('time without time zone', :oid=>1183, :scalar_oid=>1083, :type_symbol=>:time) 102 register_array_type('time with time zone', :oid=>1270, :scalar_oid=>1266, :type_symbol=>:time_timezone, :scalar_typecast=>:time) 103 104 register_array_type('smallint', :oid=>1005, :scalar_oid=>21, :scalar_typecast=>:integer) 105 register_array_type('oid', :oid=>1028, :scalar_oid=>26, :scalar_typecast=>:integer) 106 register_array_type('real', :oid=>1021, :scalar_oid=>700, :scalar_typecast=>:float) 107 register_array_type('character', :oid=>1014, :converter=>nil, :array_type=>:text, :scalar_typecast=>:string) 108 register_array_type('character varying', :oid=>1015, :converter=>nil, :scalar_typecast=>:string, :type_symbol=>:varchar) 109 110 register_array_type('xml', :oid=>143, :scalar_oid=>142) 111 register_array_type('money', :oid=>791, :scalar_oid=>790) 112 register_array_type('bit', :oid=>1561, :scalar_oid=>1560) 113 register_array_type('bit varying', :oid=>1563, :scalar_oid=>1562, :type_symbol=>:varbit) 114 register_array_type('uuid', :oid=>2951, :scalar_oid=>2950) 115 116 register_array_type('xid', :oid=>1011, :scalar_oid=>28) 117 register_array_type('cid', :oid=>1012, :scalar_oid=>29) 118 119 register_array_type('name', :oid=>1003, :scalar_oid=>19) 120 register_array_type('tid', :oid=>1010, :scalar_oid=>27) 121 register_array_type('int2vector', :oid=>1006, :scalar_oid=>22) 122 register_array_type('oidvector', :oid=>1013, :scalar_oid=>30) 123 124 [:string_array, :integer_array, :decimal_array, :float_array, :boolean_array, :blob_array, :date_array, :time_array, :datetime_array].each do |v| 125 @schema_type_classes[v] = PGArray 126 end 127 end 128 end
Public Instance Methods
# File lib/sequel/extensions/pg_array.rb 130 def add_named_conversion_proc(name, &block) 131 ret = super 132 name = name.to_s if name.is_a?(Symbol) 133 from(:pg_type).where(:typname=>name).select_map([:oid, :typarray]).each do |scalar_oid, array_oid| 134 register_array_type(name, :oid=>array_oid.to_i, :scalar_oid=>scalar_oid.to_i) 135 end 136 ret 137 end
Handle arrays in bound variables
# File lib/sequel/extensions/pg_array.rb 140 def bound_variable_arg(arg, conn) 141 case arg 142 when PGArray 143 bound_variable_array(arg.to_a) 144 when Array 145 bound_variable_array(arg) 146 else 147 super 148 end 149 end
Freeze the pg array schema types to prevent adding new ones.
# File lib/sequel/extensions/pg_array.rb 152 def freeze 153 @pg_array_schema_types.freeze 154 super 155 end
Register a database specific array type. Options:
- :array_type
-
The type to automatically cast the array to when literalizing the array. Usually the same as db_type.
- :converter
-
A callable object (e.g. Proc), that is called with each element of the array (usually a string), and should return the appropriate typecasted object.
- :oid
-
The PostgreSQL OID for the array type. This is used by the
Sequel
postgres adapter to set up automatic type conversion on retrieval from the database. - :scalar_oid
-
Should be the PostgreSQL OID for the scalar version of this array type. If given, automatically sets the :converter option by looking for scalar conversion proc.
- :scalar_typecast
-
Should be a symbol indicating the typecast method that should be called on each element of the array, when a plain array is passed into a database typecast method. For example, for an array of integers, this could be set to :integer, so that the typecast_value_integer method is called on all of the array elements. Defaults to :type_symbol option.
- :type_symbol
-
The base of the schema type symbol for this type. For example, if you provide :integer,
Sequel
will recognize this type as :integer_array during schema parsing. Defaults to the db_type argument.
If a block is given, it is treated as the :converter option.
# File lib/sequel/extensions/pg_array.rb 178 def register_array_type(db_type, opts=OPTS, &block) 179 oid = opts[:oid] 180 soid = opts[:scalar_oid] 181 182 if has_converter = opts.has_key?(:converter) 183 raise Error, "can't provide both a block and :converter option to register_array_type" if block 184 converter = opts[:converter] 185 else 186 has_converter = true if block 187 converter = block 188 end 189 190 unless (soid || has_converter) && oid 191 array_oid, scalar_oid = from(:pg_type).where(:typname=>db_type.to_s).get([:typarray, :oid]) 192 soid ||= scalar_oid unless has_converter 193 oid ||= array_oid 194 end 195 196 db_type = db_type.to_s 197 type = (opts[:type_symbol] || db_type).to_sym 198 typecast_method_map = @pg_array_schema_types 199 200 if soid 201 raise Error, "can't provide both a converter and :scalar_oid option to register" if has_converter 202 converter = conversion_procs[soid] 203 end 204 205 array_type = (opts[:array_type] || db_type).to_s.dup.freeze 206 creator = Creator.new(array_type, converter) 207 add_conversion_proc(oid, creator) 208 209 typecast_method_map[db_type] = :"#{type}_array" 210 211 singleton_class.class_eval do 212 meth = :"typecast_value_#{type}_array" 213 scalar_typecast_method = :"typecast_value_#{opts.fetch(:scalar_typecast, type)}" 214 define_method(meth){|v| typecast_value_pg_array(v, creator, scalar_typecast_method)} 215 private meth 216 alias_method(meth, meth) 217 end 218 219 @schema_type_classes[:"#{type}_array"] = PGArray 220 nil 221 end
Private Instance Methods
Format arrays used in bound variables.
# File lib/sequel/extensions/pg_array.rb 226 def bound_variable_array(a) 227 case a 228 when Array 229 "{#{a.map{|i| bound_variable_array(i)}.join(',')}}" 230 when Sequel::SQL::Blob 231 "\"#{literal(a)[BLOB_RANGE].gsub("''", "'").gsub(/("|\\)/, '\\\\\1')}\"" 232 when Sequel::LiteralString 233 a 234 when String 235 "\"#{a.gsub(/("|\\)/, '\\\\\1')}\"" 236 else 237 literal(a) 238 end 239 end
Convert ruby arrays to PostgreSQL arrays when used as default values.
# File lib/sequel/extensions/pg_array.rb 269 def column_definition_default_sql(sql, column) 270 if (d = column[:default]) && d.is_a?(Array) && !Sequel.condition_specifier?(d) 271 sql << " DEFAULT (#{literal(Sequel.pg_array(d))}::#{type_literal(column)})" 272 else 273 super 274 end 275 end
Look into both the current database’s array schema types and the global array schema types to get the type symbol for the given database type string.
# File lib/sequel/extensions/pg_array.rb 244 def pg_array_schema_type(type) 245 @pg_array_schema_types[type] 246 end
Make the column type detection handle registered array types.
# File lib/sequel/extensions/pg_array.rb 249 def schema_column_type(db_type) 250 if (db_type =~ /\A([^(]+)(?:\([^(]+\))?\[\]\z/io) && (type = pg_array_schema_type($1)) 251 type 252 else 253 super 254 end 255 end
Set the :callable_default value if the default value is recognized as an empty array.
# File lib/sequel/extensions/pg_array.rb 258 def schema_post_process(_) 259 super.each do |a| 260 h = a[1] 261 if h[:default] =~ /\A(?:'\{\}'|ARRAY\[\])::([\w ]+)\[\]\z/ 262 type = $1.freeze 263 h[:callable_default] = lambda{Sequel.pg_array([], type)} 264 end 265 end 266 end
Given a value to typecast and the type of PGArray
subclass:
-
If given a
PGArray
with a matching array_type, use it directly. -
If given a
PGArray
with a different array_type, return aPGArray
with the creator’s type. -
If given an
Array
, create a newPGArray
instance for it. This does not typecast all members of the array in ruby for performance reasons, but it will cast the array the appropriate database type when the array is literalized.
# File lib/sequel/extensions/pg_array.rb 285 def typecast_value_pg_array(value, creator, scalar_typecast_method=nil) 286 case value 287 when PGArray 288 if value.array_type != creator.type 289 PGArray.new(value.to_a, creator.type) 290 else 291 value 292 end 293 when Array 294 if scalar_typecast_method && respond_to?(scalar_typecast_method, true) 295 value = Sequel.recursive_map(value, method(scalar_typecast_method)) 296 end 297 PGArray.new(value, creator.type) 298 else 299 raise Sequel::InvalidValue, "invalid value for array type: #{value.inspect}" 300 end 301 end