module Sequel::Postgres::PGRow::DatabaseMethods
Attributes
A hash mapping row type keys (usually symbols), to option hashes. At the least, the values will contain the :parser option for the Parser
instance that the type will use.
Public Class Methods
Do some setup for the data structures the module uses.
# File lib/sequel/extensions/pg_row.rb 372 def self.extended(db) 373 db.instance_exec do 374 @row_types = {} 375 @row_schema_types = {} 376 extend(@row_type_method_module = Module.new) 377 add_conversion_proc(2249, PGRow::Parser.new(:converter=>PGRow::ArrayRow)) 378 if respond_to?(:register_array_type) 379 register_array_type('record', :oid=>2287, :scalar_oid=>2249) 380 end 381 end 382 end
Public Instance Methods
Handle ArrayRow
and HashRow
values in bound variables.
# File lib/sequel/extensions/pg_row.rb 385 def bound_variable_arg(arg, conn) 386 case arg 387 when ArrayRow 388 "(#{arg.map{|v| bound_variable_array(v) if v}.join(',')})" 389 when HashRow 390 arg.check_columns! 391 "(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(',')})" 392 else 393 super 394 end 395 end
Freeze the row types and row schema types to prevent adding new ones.
# File lib/sequel/extensions/pg_row.rb 398 def freeze 399 @row_types.freeze 400 @row_schema_types.freeze 401 @row_type_method_module.freeze 402 super 403 end
Register a new row type for the Database
instance. db_type should be the type symbol. This parses the PostgreSQL system tables to get information the composite type, and by default has the type return instances of a subclass of HashRow
.
The following options are supported:
- :converter
-
Use a custom converter for the parser.
- :typecaster
-
Use a custom typecaster for the parser.
# File lib/sequel/extensions/pg_row.rb 414 def register_row_type(db_type, opts=OPTS) 415 procs = @conversion_procs 416 rel_oid = nil 417 array_oid = nil 418 parser_opts = {} 419 420 # Try to handle schema-qualified types. 421 type_schema, type_name = schema_and_table(db_type) 422 schema_type_string = type_name.to_s 423 424 # Get basic oid information for the composite type. 425 ds = from(:pg_type). 426 select{[pg_type[:oid], :typrelid, :typarray]}. 427 where([[:typtype, 'c'], [:typname, type_name.to_s]]) 428 if type_schema 429 ds = ds.join(:pg_namespace, [[:oid, :typnamespace], [:nspname, type_schema.to_s]]) 430 schema_type_symbol = :"pg_row_#{type_schema}__#{type_name}" 431 else 432 schema_type_symbol = :"pg_row_#{type_name}" 433 end 434 unless row = ds.first 435 raise Error, "row type #{db_type.inspect} not found in database" 436 end 437 # Manually cast to integer using to_i, because adapter may not cast oid type 438 # correctly (e.g. swift) 439 parser_opts[:oid], rel_oid, array_oid = row.values_at(:oid, :typrelid, :typarray).map(&:to_i) 440 441 # Get column names and oids for each of the members of the composite type. 442 res = from(:pg_attribute). 443 join(:pg_type, :oid=>:atttypid). 444 where(:attrelid=>rel_oid). 445 where{attnum > 0}. 446 exclude(:attisdropped). 447 order(:attnum). 448 select_map{[:attname, Sequel.case({0=>:atttypid}, pg_type[:typbasetype], pg_type[:typbasetype]).as(:atttypid)]} 449 if res.empty? 450 raise Error, "no columns for row type #{db_type.inspect} in database" 451 end 452 parser_opts[:columns] = res.map{|r| r[0].to_sym} 453 parser_opts[:column_oids] = res.map{|r| r[1].to_i} 454 455 # Using the conversion_procs, lookup converters for each member of the composite type 456 parser_opts[:column_converters] = parser_opts[:column_oids].map do |oid| 457 procs[oid] 458 end 459 460 # Setup the converter and typecaster 461 parser_opts[:converter] = opts.fetch(:converter){HashRow.subclass(db_type, parser_opts[:columns])} 462 parser_opts[:typecaster] = opts.fetch(:typecaster, parser_opts[:converter]) 463 464 parser = Parser.new(parser_opts) 465 add_conversion_proc(parser.oid, parser) 466 467 if respond_to?(:register_array_type) && array_oid && array_oid > 0 468 array_type_name = if type_schema 469 "#{type_schema}.#{type_name}" 470 else 471 type_name 472 end 473 register_array_type(array_type_name, :oid=>array_oid, :converter=>parser, :scalar_typecast=>schema_type_symbol) 474 end 475 476 @row_types[literal(db_type)] = opts.merge(:parser=>parser, :type=>db_type) 477 @row_schema_types[schema_type_string] = schema_type_symbol 478 @schema_type_classes[schema_type_symbol] = ROW_TYPE_CLASSES 479 @row_type_method_module.class_eval do 480 meth = :"typecast_value_#{schema_type_symbol}" 481 define_method(meth) do |v| 482 row_type(db_type, v) 483 end 484 private meth 485 end 486 487 nil 488 end
Handle typecasting of the given object to the given database type. In general, the given database type should already be registered, but if obj is an array, this will handled unregistered types.
# File lib/sequel/extensions/pg_row.rb 493 def row_type(db_type, obj) 494 (type_hash = @row_types[literal(db_type)]) && 495 (parser = type_hash[:parser]) 496 497 case obj 498 when ArrayRow, HashRow 499 obj 500 when Array 501 if parser 502 parser.typecast(obj) 503 else 504 obj = ArrayRow.new(obj) 505 obj.db_type = db_type 506 obj 507 end 508 when Hash 509 if parser 510 parser.typecast(obj) 511 else 512 raise InvalidValue, "Database#row_type requires the #{db_type.inspect} type have a registered parser and typecaster when called with a hash" 513 end 514 else 515 raise InvalidValue, "cannot convert #{obj.inspect} to row type #{db_type.inspect}" 516 end 517 end
Private Instance Methods
Format composite types used in bound variable arrays.
# File lib/sequel/extensions/pg_row.rb 522 def bound_variable_array(arg) 523 case arg 524 when ArrayRow 525 "\"(#{arg.map{|v| bound_variable_array(v) if v}.join(',').gsub(/("|\\)/, '\\\\\1')})\"" 526 when HashRow 527 arg.check_columns! 528 "\"(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(',').gsub(/("|\\)/, '\\\\\1')})\"" 529 else 530 super 531 end 532 end
Make the column type detection handle registered row types.
# File lib/sequel/extensions/pg_row.rb 535 def schema_column_type(db_type) 536 if type = @row_schema_types[db_type] 537 type 538 else 539 super 540 end 541 end