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