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