Class: Flounder::Connection

Inherits:
Object
  • Object
show all
Includes:
PostgresUtils
Defined in:
lib/flounder/connection.rb

Defined Under Namespace

Classes: Column

Constant Summary

Constants included from PostgresUtils

PostgresUtils::OID_BOOLEAN, PostgresUtils::OID_DATE, PostgresUtils::OID_FLOAT, PostgresUtils::OID_INTEGER, PostgresUtils::OID_SMALLINT, PostgresUtils::OID_TEXT, PostgresUtils::OID_TIME, PostgresUtils::OID_TIMESTAMP, PostgresUtils::OID_TIMESTAMPTZ, PostgresUtils::OID_VARCHAR

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from PostgresUtils

#access_value, #each_field, #oid_hstore, #type_name, #type_oid_to_sym, #typecast

Constructor Details

#initialize(pg_conn_opts) ⇒ Connection

Returns a new instance of Connection.



7
8
9
10
11
12
13
14
15
16
# File 'lib/flounder/connection.rb', line 7

def initialize pg_conn_opts
  search_path = pg_conn_opts.delete(:search_path)

  @pg = PG.connect(pg_conn_opts)
  @visitor = Arel::Visitors::PostgreSQL.new(self)

  if search_path
    exec('set search_path=' + search_path)
  end
end

Instance Attribute Details

#pgObject (readonly)

Returns the value of attribute pg.



4
5
6
# File 'lib/flounder/connection.rb', line 4

def pg
  @pg
end

#visitorObject (readonly)

Returns the value of attribute visitor.



5
6
7
# File 'lib/flounder/connection.rb', line 5

def visitor
  @visitor
end

Instance Method Details

#columns(name, message = nil) ⇒ Object



59
60
61
# File 'lib/flounder/connection.rb', line 59

def columns name, message = nil
  fail NotImplementedError
end

#columns_hash(table_name) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/flounder/connection.rb', line 27

def columns_hash table_name
  hash = {}
  pg.exec(%Q(select * from #{quote_table_name(table_name)} limit 0)) do |result|

    # TBD This is a duplicate from the code in Query.
    result.nfields.times do |idx|
      name = result.fname(idx)
      type_oid = result.ftype(idx)
      mod  = result.fmod(idx)
      typesym = type_oid_to_sym(self, type_oid)

      unless typesym
        type_string = type_name(type_oid, mod)
        fail "No map for oid #{type_oid} found, type(#{type_string})."
      end

      hash[name] = Column.new(name, typesym)
    end
  end

  hash
end

#exec(*args, &block) ⇒ Object



21
22
23
# File 'lib/flounder/connection.rb', line 21

def exec *args, &block
  pg.exec *args, &block
end

#objectify_result_row(ent, result, row_idx) ⇒ Hashie::Mash

Turns a PG result row into a hash-like object. There are some transformation rules that govern this conversion:

  • All data types are converted to their closest Ruby equivalent (type conversion)

  • Fields from the main entity (the entity that started the select) are returned on the top level of the hash.

  • Fields from joined entities are returned in a subhash stored under the singular name of the joined entity.

Example:

row = users.join(posts).on(:id => :user_id).first 
row[:id]            # refers to users.id, also as row.id
row[:post][:id]     # refers to posts.id, also as row.post.id

row.keys            # hash keys of the row, not equal to row[:keys]!

Parameters:

  • ent (Entity)

    entity that the query originated from

  • result (PG::Result)
  • row_idx (Fixnum)

    row we’re interested in

Returns:

  • (Hashie::Mash)

    result row as hash-like object



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/flounder/connection.rb', line 106

def objectify_result_row ent, result, row_idx
  obj = Hashie::Mash.new
  
  each_field(ent, result, row_idx) do 
    |entity, name, value, type_oid, binary, idx|

    # NOTE entity resolution is done both through aliasing and through
    # postgres column reporting. The above entity variable carries what
    # postgres reports to us; the block below resolves aliased entity 
    # names:
    processed_entity, processed_name = yield name if block_given?
    entity = processed_entity if processed_entity
    name   = processed_name if processed_name

    typecast_value = typecast(type_oid, value)

    # JOIN tables are available from the result using their singular
    # names.
    if entity
      obj[entity.singular] ||= {}

      sub_obj = obj[entity.singular]
      sub_obj[name] = typecast_value
    end

    # The main entity and custom fields (AS something) are available on the
    # top-level of the result. 
    if !entity || entity == ent
      raise DuplicateField, "#{name.inspect} already defined in result set, aliasing occurs." \
        if obj.has_key? name

      obj[name] = typecast_value
    end
  end

  return obj
end

#primary_key(name) ⇒ Object



50
51
52
# File 'lib/flounder/connection.rb', line 50

def primary_key name
  fail NotImplementedError
end

#quote(thing, column = nil) ⇒ Object



75
76
77
78
79
80
# File 'lib/flounder/connection.rb', line 75

def quote thing, column = nil
  # require 'pp '
  # p [:quote, thing, column]
  # pp caller.first(10)
  pg.escape_literal(thing.to_s)
end

#quote_column_name(name) ⇒ Object



67
68
69
# File 'lib/flounder/connection.rb', line 67

def quote_column_name name
  pg.quote_ident name.to_s
end

#quote_table_name(name) ⇒ Object



63
64
65
# File 'lib/flounder/connection.rb', line 63

def quote_table_name name
  pg.quote_ident name.to_s
end

#schema_cacheObject



71
72
73
# File 'lib/flounder/connection.rb', line 71

def schema_cache
  self
end

#table_exists?(table_name) ⇒ Boolean

Returns:

  • (Boolean)


54
55
56
57
# File 'lib/flounder/connection.rb', line 54

def table_exists? table_name
  # TBD Centralize these direct SQL statements in some class
  ! pg.exec(%Q(select count(*) from pg_class where relname = #{quote(table_name)})).getvalue(0,0).nil?
end

#transaction(&block) ⇒ Object



18
19
20
# File 'lib/flounder/connection.rb', line 18

def transaction &block
  pg.transaction(&block)
end