Module | Sequel::Postgres::PGRow::DatabaseMethods |
In: |
lib/sequel/extensions/pg_row.rb
|
ESCAPE_RE | = | /("|\\)/.freeze |
ESCAPE_REPLACEMENT | = | '\\\\\1'.freeze |
COMMA | = | ','.freeze |
row_types | [R] | 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. |
Do some setup for the data structures the module uses.
# File lib/sequel/extensions/pg_row.rb, line 386 386: def self.extended(db) 387: # Return right away if row_types has already been set. This 388: # makes things not break if a user extends the database with 389: # this module more than once (since extended is called every 390: # time). 391: return if db.row_types 392: 393: db.instance_eval do 394: @row_types = {} 395: @row_schema_types = {} 396: extend(@row_type_method_module = Module.new) 397: copy_conversion_procs([2249, 2287]) 398: end 399: end
Handle ArrayRow and HashRow values in bound variables.
# File lib/sequel/extensions/pg_row.rb, line 402 402: def bound_variable_arg(arg, conn) 403: case arg 404: when ArrayRow 405: "(#{arg.map{|v| bound_variable_array(v) if v}.join(COMMA)})" 406: when HashRow 407: arg.check_columns! 408: "(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(COMMA)})" 409: else 410: super 411: end 412: 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, line 423 423: def register_row_type(db_type, opts=OPTS) 424: procs = @conversion_procs 425: rel_oid = nil 426: array_oid = nil 427: parser_opts = {} 428: 429: # Try to handle schema-qualified types. 430: type_schema, type_name = schema_and_table(db_type) 431: schema_type_string = type_name.to_s 432: 433: # Get basic oid information for the composite type. 434: ds = from(:pg_type). 435: select(:pg_type__oid, :typrelid, :typarray). 436: where([[:typtype, 'c'], [:typname, type_name.to_s]]) 437: if type_schema 438: ds = ds.join(:pg_namespace, [[:oid, :typnamespace], [:nspname, type_schema.to_s]]) 439: schema_type_symbol = "pg_row_#{type_schema}__#{type_name}""pg_row_#{type_schema}__#{type_name}" 440: else 441: schema_type_symbol = "pg_row_#{type_name}""pg_row_#{type_name}" 442: end 443: unless row = ds.first 444: raise Error, "row type #{db_type.inspect} not found in database" 445: end 446: # Manually cast to integer using to_i, because adapter may not cast oid type 447: # correctly (e.g. swift) 448: parser_opts[:oid], rel_oid, array_oid = row.values_at(:oid, :typrelid, :typarray).map{|i| i.to_i} 449: 450: # Get column names and oids for each of the members of the composite type. 451: res = from(:pg_attribute). 452: join(:pg_type, :oid=>:atttypid). 453: where(:attrelid=>rel_oid). 454: where{attnum > 0}. 455: exclude(:attisdropped). 456: order(:attnum). 457: select_map([:attname, Sequel.case({0=>:atttypid}, :pg_type__typbasetype, :pg_type__typbasetype).as(:atttypid)]) 458: if res.empty? 459: raise Error, "no columns for row type #{db_type.inspect} in database" 460: end 461: parser_opts[:columns] = res.map{|r| r[0].to_sym} 462: parser_opts[:column_oids] = res.map{|r| r[1].to_i} 463: 464: # Using the conversion_procs, lookup converters for each member of the composite type 465: parser_opts[:column_converters] = parser_opts[:column_oids].map do |oid| 466: if pr = procs[oid] 467: pr 468: elsif !Sequel::Postgres::STRING_TYPES.include?(oid) 469: # It's not a string type, and it's possible a conversion proc for this 470: # oid will be added later, so do a runtime check for it. 471: lambda{|s| (pr = procs[oid]) ? pr.call(s) : s} 472: end 473: end 474: 475: # Setup the converter and typecaster 476: parser_opts[:converter] = opts.fetch(:converter){HashRow.subclass(db_type, parser_opts[:columns])} 477: parser_opts[:typecaster] = opts.fetch(:typecaster, parser_opts[:converter]) 478: 479: parser = Parser.new(parser_opts) 480: @conversion_procs[parser.oid] = parser 481: 482: if defined?(PGArray) && PGArray.respond_to?(:register) && array_oid && array_oid > 0 483: array_type_name = if type_schema 484: "#{type_schema}.#{type_name}" 485: else 486: type_name 487: end 488: PGArray.register(array_type_name, :oid=>array_oid, :converter=>parser, :type_procs=>@conversion_procs, :scalar_typecast=>schema_type_symbol) 489: end 490: 491: @row_types[db_type] = opts.merge(:parser=>parser) 492: @row_schema_types[schema_type_string] = schema_type_symbol 493: @schema_type_classes[schema_type_symbol] = ROW_TYPE_CLASSES 494: @row_type_method_module.class_eval do 495: meth = "typecast_value_#{schema_type_symbol}""typecast_value_#{schema_type_symbol}" 496: define_method(meth) do |v| 497: row_type(db_type, v) 498: end 499: private meth 500: end 501: 502: conversion_procs_updated 503: nil 504: end
When reseting conversion procs, reregister all the row types so that the system tables are introspected again, picking up database changes.
# File lib/sequel/extensions/pg_row.rb, line 508 508: def reset_conversion_procs 509: procs = super 510: 511: row_types.each do |db_type, opts| 512: register_row_type(db_type, opts) 513: end 514: 515: procs 516: 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, line 521 521: def row_type(db_type, obj) 522: (type_hash = @row_types[db_type]) && 523: (parser = type_hash[:parser]) 524: 525: case obj 526: when ArrayRow, HashRow 527: obj 528: when Array 529: if parser 530: parser.typecast(obj) 531: else 532: obj = ArrayRow.new(obj) 533: obj.db_type = db_type 534: obj 535: end 536: when Hash 537: if parser 538: parser.typecast(obj) 539: else 540: raise InvalidValue, "Database#row_type requires the #{db_type.inspect} type have a registered parser and typecaster when called with a hash" 541: end 542: else 543: raise InvalidValue, "cannot convert #{obj.inspect} to row type #{db_type.inspect}" 544: end 545: end