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 364 364: def self.extended(db) 365: # Return right away if row_types has already been set. This 366: # makes things not break if a user extends the database with 367: # this module more than once (since extended is called every 368: # time). 369: return if db.row_types 370: 371: db.instance_eval do 372: @row_types = {} 373: @row_schema_types = {} 374: extend(@row_type_method_module = Module.new) 375: end 376: end
Handle ArrayRow and HashRow values in bound variables.
# File lib/sequel/extensions/pg_row.rb, line 379 379: def bound_variable_arg(arg, conn) 380: case arg 381: when ArrayRow 382: "(#{arg.map{|v| bound_variable_array(v)}.join(COMMA)})" 383: when HashRow 384: arg.check_columns! 385: "(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v)}.join(COMMA)})" 386: else 387: super 388: end 389: 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 400 400: def register_row_type(db_type, opts={}) 401: procs = @conversion_procs 402: rel_oid = nil 403: array_oid = nil 404: parser_opts = {} 405: 406: # Try to handle schema-qualified types. 407: type_schema, type_name = schema_and_table(db_type) 408: schema_type_string = type_name.to_s 409: 410: # Get basic oid information for the composite type. 411: ds = from(:pg_type). 412: select(:pg_type__oid, :typrelid, :typarray). 413: where([[:typtype, 'c'], [:typname, type_name.to_s]]) 414: if type_schema 415: ds = ds.join(:pg_namespace, [[:oid, :typnamespace], [:nspname, type_schema.to_s]]) 416: schema_type_symbol = "pg_row_#{type_schema}__#{type_name}""pg_row_#{type_schema}__#{type_name}" 417: else 418: schema_type_symbol = "pg_row_#{type_name}""pg_row_#{type_name}" 419: end 420: unless row = ds.first 421: raise Error, "row type #{db_type.inspect} not found in database" 422: end 423: # Manually cast to integer using to_i, because adapter may not cast oid type 424: # correctly (e.g. swift) 425: parser_opts[:oid], rel_oid, array_oid = row.values_at(:oid, :typrelid, :typarray).map{|i| i.to_i} 426: 427: # Get column names and oids for each of the members of the composite type. 428: res = from(:pg_attribute). 429: where(:attrelid=>rel_oid). 430: where{attnum > 0}. 431: exclude(:attisdropped). 432: order(:attnum). 433: select_map([:attname, :atttypid]) 434: if res.empty? 435: raise Error, "no columns for row type #{db_type.inspect} in database" 436: end 437: parser_opts[:columns] = res.map{|r| r[0].to_sym} 438: parser_opts[:column_oids] = res.map{|r| r[1].to_i} 439: 440: # Using the conversion_procs, lookup converters for each member of the composite type 441: parser_opts[:column_converters] = parser_opts[:column_oids].map do |oid| 442: if pr = procs[oid] 443: pr 444: elsif !Sequel::Postgres::STRING_TYPES.include?(oid) 445: # It's not a string type, and it's possible a conversion proc for this 446: # oid will be added later, so do a runtime check for it. 447: lambda{|s| (pr = procs[oid]) ? pr.call(s) : s} 448: end 449: end 450: 451: # Setup the converter and typecaster 452: parser_opts[:converter] = opts.fetch(:converter){HashRow.subclass(db_type, parser_opts[:columns])} 453: parser_opts[:typecaster] = opts.fetch(:typecaster, parser_opts[:converter]) 454: 455: parser = Parser.new(parser_opts) 456: @conversion_procs[parser.oid] = parser 457: 458: if defined?(PGArray) && PGArray.respond_to?(:register) && array_oid && array_oid > 0 459: PGArray.register(db_type, :oid=>array_oid, :converter=>parser, :type_procs=>@conversion_procs, :scalar_typecast=>schema_type_symbol) 460: end 461: 462: @row_types[db_type] = opts.merge(:parser=>parser) 463: @row_schema_types[schema_type_string] = schema_type_symbol 464: @row_type_method_module.class_eval do 465: meth = "typecast_value_#{schema_type_symbol}""typecast_value_#{schema_type_symbol}" 466: define_method(meth) do |v| 467: row_type(db_type, v) 468: end 469: private meth 470: end 471: 472: nil 473: 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 477 477: def reset_conversion_procs 478: procs = super 479: 480: row_types.each do |db_type, opts| 481: register_row_type(db_type, opts) 482: end 483: 484: procs 485: 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 490 490: def row_type(db_type, obj) 491: (type_hash = @row_types[db_type]) && 492: (parser = type_hash[:parser]) 493: 494: case obj 495: when ArrayRow, HashRow 496: obj 497: when Array 498: if parser 499: parser.typecast(obj) 500: else 501: obj = ArrayRow.new(obj) 502: obj.db_type = db_type 503: obj 504: end 505: when Hash 506: if parser 507: parser.typecast(obj) 508: else 509: raise InvalidValue, "Database#row_type requires the #{db_type.inspect} type have a registered parser and typecaster when called with a hash" 510: end 511: else 512: raise InvalidValue, "cannot convert #{obj.inspect} to row type #{db_type.inspect}" 513: end 514: end