Module: Sequel::Postgres::PGRow::DatabaseMethods

Defined in:
lib/sequel/extensions/pg_row.rb

Constant Summary collapse

ESCAPE_RE =
/("|\\)/.freeze
ESCAPE_REPLACEMENT =
'\\\\\1'.freeze
COMMA =
','.freeze
STRING_TYPES =
[18, 19, 25, 1042, 1043].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#row_typesObject (readonly)

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.



400
401
402
# File 'lib/sequel/extensions/pg_row.rb', line 400

def row_types
  @row_types
end

Class Method Details

.extended(db) ⇒ Object

Do some setup for the data structures the module uses.



403
404
405
406
407
408
409
410
411
412
413
# File 'lib/sequel/extensions/pg_row.rb', line 403

def self.extended(db)
  db.instance_eval do
    @row_types = {}
    @row_schema_types = {}
    extend(@row_type_method_module = Module.new)
    add_conversion_proc(2249, PGRow::Parser.new(:converter=>PGRow::ArrayRow))
    if respond_to?(:register_array_type)
      register_array_type('record', :oid=>2287, :scalar_oid=>2249)
    end
  end
end

Instance Method Details

#bound_variable_arg(arg, conn) ⇒ Object

Handle ArrayRow and HashRow values in bound variables.



416
417
418
419
420
421
422
423
424
425
426
# File 'lib/sequel/extensions/pg_row.rb', line 416

def bound_variable_arg(arg, conn)
  case arg
  when ArrayRow
    "(#{arg.map{|v| bound_variable_array(v) if v}.join(',')})"
  when HashRow
    arg.check_columns!
    "(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(',')})"
  else
    super
  end
end

#freezeObject

Freeze the row types and row schema types to prevent adding new ones.



429
430
431
432
433
434
# File 'lib/sequel/extensions/pg_row.rb', line 429

def freeze
  @row_types.freeze
  @row_schema_types.freeze
  @row_type_method_module.freeze
  super
end

#register_row_type(db_type, opts = OPTS) ⇒ Object

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.



447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
# File 'lib/sequel/extensions/pg_row.rb', line 447

def register_row_type(db_type, opts=OPTS)
  procs = @conversion_procs
  rel_oid = nil
  array_oid = nil
  parser_opts = {}

  # Try to handle schema-qualified types.
  type_schema, type_name = schema_and_table(db_type)
  schema_type_string = type_name.to_s

  # Get basic oid information for the composite type.
  ds = from(:pg_type).
    select{[pg_type[:oid], :typrelid, :typarray]}.
    where([[:typtype, 'c'], [:typname, type_name.to_s]])
  if type_schema
    ds = ds.join(:pg_namespace, [[:oid, :typnamespace], [:nspname, type_schema.to_s]])
    schema_type_symbol = :"pg_row_#{type_schema}__#{type_name}" 
  else
    schema_type_symbol = :"pg_row_#{type_name}"
  end
  unless row = ds.first
    raise Error, "row type #{db_type.inspect} not found in database"
  end
  # Manually cast to integer using to_i, because adapter may not cast oid type
  # correctly (e.g. swift)
  parser_opts[:oid], rel_oid, array_oid = row.values_at(:oid, :typrelid, :typarray).map(&:to_i)

  # Get column names and oids for each of the members of the composite type.
  res = from(:pg_attribute).
    join(:pg_type, :oid=>:atttypid).
    where(:attrelid=>rel_oid).
    where{attnum > 0}.
    exclude(:attisdropped).
    order(:attnum).
    select_map{[:attname, Sequel.case({0=>:atttypid}, pg_type[:typbasetype], pg_type[:typbasetype]).as(:atttypid)]}
  if res.empty?
    raise Error, "no columns for row type #{db_type.inspect} in database"
  end
  parser_opts[:columns] = res.map{|r| r[0].to_sym}
  parser_opts[:column_oids] = res.map{|r| r[1].to_i}

  # Using the conversion_procs, lookup converters for each member of the composite type
  parser_opts[:column_converters] = parser_opts[:column_oids].map do |oid|
    # procs[oid] # SEQUEL5

    # SEQUEL5: Remove
    if pr = procs[oid]
      pr
    elsif !STRING_TYPES.include?(oid)
      # It's not a string type, and it's possible a conversion proc for this
      # oid will be added later, so do a runtime check for it.
      lambda do |s|
        if (pr = procs[oid])
          Sequel::Deprecation.deprecate("Calling conversion proc for subtype (oid: #{oid}) of composite type (oid: #{parser_opts[:oid]}) not added until after composite type registration", "Register subtype conversion procs before registering composite type")
          pr.call(s)
        else
          s
        end
      end
    end
  end

  # Setup the converter and typecaster
  parser_opts[:converter] = opts.fetch(:converter){HashRow.subclass(db_type, parser_opts[:columns])}
  parser_opts[:typecaster] = opts.fetch(:typecaster, parser_opts[:converter])

  parser = Parser.new(parser_opts)
  add_conversion_proc(parser.oid, parser)

  if respond_to?(:register_array_type) && array_oid && array_oid > 0
    array_type_name = if type_schema
      "#{type_schema}.#{type_name}"
    else
      type_name
    end
    register_array_type(array_type_name, :oid=>array_oid, :converter=>parser, :scalar_typecast=>schema_type_symbol)
  end

  @row_types[literal(db_type)] = opts.merge(:parser=>parser, :type=>db_type)
  @row_schema_types[schema_type_string] = schema_type_symbol 
  @schema_type_classes[schema_type_symbol] = ROW_TYPE_CLASSES
  @row_type_method_module.class_eval do
    meth = :"typecast_value_#{schema_type_symbol}"
    define_method(meth) do |v|
      row_type(db_type, v)
    end
    private meth
  end

  conversion_procs_updated # SEQUEL5: Remove
  nil
end

#reset_conversion_procsObject

SEQUEL5: Remove



541
542
543
544
545
546
547
548
549
# File 'lib/sequel/extensions/pg_row.rb', line 541

def reset_conversion_procs
  procs = super

  row_types.values.each do |opts|
    register_row_type(opts[:type], opts)
  end

  procs
end

#row_type(db_type, obj) ⇒ Object

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.



554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# File 'lib/sequel/extensions/pg_row.rb', line 554

def row_type(db_type, obj)
  (type_hash = @row_types[literal(db_type)]) &&
    (parser = type_hash[:parser])

  case obj
  when ArrayRow, HashRow
    obj
  when Array
    if parser
      parser.typecast(obj)
    else
      obj = ArrayRow.new(obj)
      obj.db_type = db_type
      obj
    end
  when Hash
    if parser 
      parser.typecast(obj)
    else
      raise InvalidValue, "Database#row_type requires the #{db_type.inspect} type have a registered parser and typecaster when called with a hash"
    end
  else
    raise InvalidValue, "cannot convert #{obj.inspect} to row type #{db_type.inspect}"
  end
end