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