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 bound_variable_array_string(literal(a)[BLOB_RANGE].gsub("''", "'")) 232 when Sequel::LiteralString 233 a 234 when String 235 bound_variable_array_string(a) 236 when Float 237 if a.infinite? 238 a > 0 ? '"Infinity"' : '"-Infinity"' 239 elsif a.nan? 240 '"NaN"' 241 else 242 literal(a) 243 end 244 when Time, Date 245 @default_dataset.literal_date_or_time(a) 246 else 247 if (s = bound_variable_arg(a, nil)).is_a?(String) 248 bound_variable_array_string(s) 249 else 250 literal(a) 251 end 252 end 253 end
Escape strings used as array members in bound variables. Most complex will create a regular string with bound_variable_arg
, and then use this escaping to format it as an array member.
# File lib/sequel/extensions/pg_array.rb 258 def bound_variable_array_string(s) 259 "\"#{s.gsub(/("|\\)/, '\\\\\1')}\"" 260 end
Convert ruby arrays to PostgreSQL arrays when used as default values.
# File lib/sequel/extensions/pg_array.rb 290 def column_definition_default_sql(sql, column) 291 if (d = column[:default]) && d.is_a?(Array) && !Sequel.condition_specifier?(d) 292 sql << " DEFAULT (#{literal(Sequel.pg_array(d))}::#{type_literal(column)})" 293 else 294 super 295 end 296 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 265 def pg_array_schema_type(type) 266 @pg_array_schema_types[type] 267 end
Make the column type detection handle registered array types.
# File lib/sequel/extensions/pg_array.rb 270 def schema_array_type(db_type) 271 if (db_type =~ /\A([^(]+)(?:\([^(]+\))?\[\]\z/io) && (type = pg_array_schema_type($1)) 272 type 273 else 274 super 275 end 276 end
Set the :callable_default value if the default value is recognized as an empty array.
# File lib/sequel/extensions/pg_array.rb 279 def schema_post_process(_) 280 super.each do |a| 281 h = a[1] 282 if h[:default] =~ /\A(?:'\{\}'|ARRAY\[\])::([\w ]+)\[\]\z/ 283 type = $1.freeze 284 h[:callable_default] = lambda{Sequel.pg_array([], type)} 285 end 286 end 287 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 306 def typecast_value_pg_array(value, creator, scalar_typecast_method=nil) 307 case value 308 when PGArray 309 if value.array_type != creator.type 310 PGArray.new(value.to_a, creator.type) 311 else 312 value 313 end 314 when Array 315 if scalar_typecast_method && respond_to?(scalar_typecast_method, true) 316 value = Sequel.recursive_map(value, method(scalar_typecast_method)) 317 end 318 PGArray.new(value, creator.type) 319 else 320 raise Sequel::InvalidValue, "invalid value for array type: #{value.inspect}" 321 end 322 end