Class: Lore::Table_Select

Inherits:
Object show all
Defined in:
lib/lore/strategies/table_select.rb

Constant Summary collapse

@@logger =
Lore.logger

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(accessor) ⇒ Table_Select

Returns a new instance of Table_Select.



13
14
15
# File 'lib/lore/strategies/table_select.rb', line 13

def initialize(accessor)
  @accessor = accessor
end

Class Method Details

.build_joined_query(accessor, join_type = 'JOIN', query_string = '', joined_tables = []) ⇒ Object

Extracted, recursive method for building the JOIN-part of a SELECT query.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/lore/strategies/table_select.rb', line 19

def self.build_joined_query(accessor, 
                            join_type='JOIN', 
                            query_string='', 
                            joined_tables=[])
# {{{
  associations     = accessor.__associations__
  top_table        = accessor.table_name
  is_a_hierarchy   = associations.joins()
  own_primary_keys = associations.primary_keys()
  own_foreign_keys = associations.foreign_keys()
  joined_models    = associations.joined_models

  # predefine
  own_p_keys       = Hash.new
  foreign_p_keys   = Hash.new
  on_string        = String.new
  field_counter    = 0

  is_a_hierarchy.each_pair { |foreign_table, foreign_base_tables|
    # Ensure no table is joined twice
    if !(joined_tables.include?(foreign_table)) then
      # [ [ a_id_f, a_id ], [ b_id_f, b_id ] ]
      mapping = own_foreign_keys[top_table][foreign_table]
      own_f_keys     = mapping.at(0)
      foreign_p_keys = mapping.at(1)

      if own_f_keys then
        joined_tables << foreign_table
        query_string  << "\n #{join_type} #{foreign_table} ON ("
        on_string      = ''
        foreign_p_keys.uniq.each_with_index { |foreign_field, field_counter|
          # base.table.foreign_field = this.table.own_field
          on_string    << "#{foreign_table}.#{foreign_field} = #{top_table}.#{own_f_keys[field_counter]}"
          query_string << ", " if field_counter > 0
          query_string << on_string
        } 
        query_string << ')'
      end
        
      # sub-joins of joined table: 
      query_string = build_joined_query(joined_models[foreign_table].first, 
                                        join_type, 
                                        query_string, 
                                        joined_tables)
    end
  }
  return query_string
end

.build_polymorphic_joined_query(accessor) ⇒ Object

}}}



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/lore/strategies/table_select.rb', line 155

def self.build_polymorphic_joined_query(accessor)
# {{{
  # Generates full outer join on all concrete submodels of this 
  # (abstract) polymorphic model. 
  
  # Correct query for this is: 
  # select * from asset 
  # ----  BEGIN IMPLICIT JOINS
  # -- full outer join on concrete model's base table
  # full outer join document_asset on (asset.asset_id = document_asset.asset_id) 
  #   -- left join on concrete model's implicitly joined tables
  #   left join document_asset_info on (document_asset_info.document_asset_id = document_asset.id)
  # -- full outer join on next concrete base table
  # full outer join media_asset on (asset.asset_id = media_asset.asset_id) 
  #   -- left join on concrete model's implicitly joined tables
  #   left join media_asset_info on (media_asset_info.media_asset_id = media_asset.id)
  # ----  END IMPLICIT JOINS
  # ----  BEGIN EXPLICIT JOINS
  # join Asset_Comments using (asset_id)
  # ----  END EXPLICIT JOINS
  # where ...
  # order by model;
  concrete_models = accessor.__associations__.concrete_models
  own_table_name  = accessor.table_name
  implicit_joins  = build_joined_query(accessor) 
  own_pkeys       = accessor.__associations__.primary_keys[own_table_name]
  concrete_models.each { |concrete_model|
    join_constraints = []
    concrete_model.__associations__.foreign_keys[concrete_model.table_name][own_table_name].first.each_with_index { |fkey,index|
      join_constraints << "#{own_table_name}.#{own_pkeys[index]} = #{concrete_model.table_name}.#{fkey}"
    }
    implicit_joins << "\nFULL OUTER JOIN #{concrete_model.table_name} ON (#{join_constraints.join(' AND ')}) "
    # Attach the concrete model's own implicit joins (is_a and aggregates), 
    # but don't join polymorphic base table (own_table_name) again: 
    implicit_joins << build_joined_query(concrete_model, '  LEFT JOIN', '', [own_table_name]) 
  }
  implicit_joins
end

Instance Method Details

#deallocate(plan_name) ⇒ Object

}}}



274
275
276
277
278
279
280
281
# File 'lib/lore/strategies/table_select.rb', line 274

def deallocate(plan_name)
# {{{
  begin
    query_string = "DEALLOCATE #{@accessor.table_name.gsub('.','_')}__#{plan_name.to_s}; "
    result = Lore::Connection.perform(query_string)
  rescue ::Exception => excep
  end
end

#prepare(plan_name, args, &block) ⇒ Object

}}}



250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/lore/strategies/table_select.rb', line 250

def prepare(plan_name, args, &block)
# {{{
  args_string = ''
  args.map { |a| a = Lore::TYPE_NAMES[a] } 
  if args.to_s != '' && args.length > 0 then args_string = "(#{args.join(',')})" end
  query_string = "PREPARE #{@accessor.table_name.gsub('.','_')}__#{plan_name.to_s}#{args_string} AS " << select_query(&block)[:query]
  begin
    Context.enter(@accessor.get_context) if @accessor.get_context
    result = Lore::Connection.perform(query_string)
  rescue ::Exception => excep
    @@logger.debug("Exception when preparing #{plan_name.to_s}: #{excep.message}")
  ensure
    Context.leave if @accessor.get_context
  end
end

#select(what, polymorphic = false, &block) ⇒ Object

}}}



194
195
196
197
198
# File 'lib/lore/strategies/table_select.rb', line 194

def select(what, polymorphic=false, &block)
# {{{
  query_string = select_query(what, nil, polymorphic, &block)
  return perform_select(query_string[:query])
end

#select_cached(what, clause_parser = nil, polymorphic = false, &block) ⇒ Object

}}}



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/lore/strategies/table_select.rb', line 200

def select_cached(what, clause_parser=nil, polymorphic=false, &block)
# {{{
  joined_models = []
  query_string  = nil
  query         = nil
  if clause_parser.nil? && block_given? then
    query         = select_query(what, nil, polymorphic, &block)
    query_string  = query[:query]
    joined_models = query[:joined_models]
  elsif !block_given? && clause_parser then
    query         = select_query(what, clause_parser, polymorphic)
    query_string  = query[:query]
    joined_models = query[:joined_models]
  else
    query_string  = what.to_s
  end
  what = false if what.empty?

  result = Array.new
  if Lore.cache_enabled? && 
     @accessor.entity_cache && 
     @accessor.entity_cache.include?(@accessor, query) then
    result = @accessor.entity_cache.read(@accessor, query)
    result = [] if result.to_s == ''
  else
    Context.enter(@accessor.get_context) if @accessor.get_context
    begin 
      result = Lore::Connection.perform(query_string).get_rows()
      if polymorphic && !what && @accessor.is_polymorphic? then
        result.map! { |row|
          Lore.logger.debug { "Polymorphic select returned: #{row.inspect}" }
          row = @accessor.new_polymorphic(row, joined_models)
        }
      else
        result.map! { |row|
          row = (@accessor.new(row, joined_models))
        }
      end
    rescue PGError => pge
      raise pge
    ensure
      Context.leave if @accessor.get_context
    end
    if Lore.cache_enabled? && @accessor.entity_cache then
      @accessor.entity_cache.create(@accessor, query, result)
    end
  end
  return result
end

#select_prepared(plan_name, *args) ⇒ Object

}}}



266
267
268
269
270
271
272
# File 'lib/lore/strategies/table_select.rb', line 266

def select_prepared(plan_name, *args)
# {{{
  args_string = ''
  if args.to_s != '' && args.length > 0 then args_string = "(#{args.join(',')})" end
  query_string = "EXECUTE #{plan_name.to_s} #{args_string}; "
  return select_cached(query_string)
end

#select_query(what = nil, clause_parser = nil, polymorphic = false, &block) ⇒ Object



105
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
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/lore/strategies/table_select.rb', line 105

def select_query(what=nil, clause_parser=nil, polymorphic=false, &block)
# {{{
  # Example: 
  # select(Car.name) -> SELECT max(id)
  if what.instance_of? Clause then
    what = what.to_s 
  end
  
  if(what.nil? || what == '*' || what == '') then
    query_as_part = '*'
  else 
    query_as_part = what.to_s      
  end

  if block_given? then
    yield_obj  = Clause_Parser.new(@accessor)
    clause_parser = yield *yield_obj
  end
  
  query_string = 'SELECT '
  
  query_parts = clause_parser.parts
  query_parts[:what] = query_as_part
  query_parts[:from] = "FROM #{@accessor.table_name}"
  # Add JOIN part for system defined type (user defined 
  # joins will be set in Clause_Parser object in later 
  # yield): 
  if polymorphic && @accessor.is_polymorphic? then
    query_parts[:all_joins] = self.class.build_polymorphic_joined_query(@accessor) << query_parts[:join]
  else
    query_parts[:all_joins] = self.class.build_joined_query(@accessor) << query_parts[:join]
  end
  query_string << [ :what, :from, :all_joins, :where, 
                    :group_by, :having, :filter, 
                    :order_by, :limit, :offset ].map { |part|
    query_parts[part]
  }.join(' ')

  # Attaching UNION selects: 
  if clause_parser.unions then
    clause_parser.unions.each { |select_query_obj|
      query_string << "\nUNION\n"
      union_sql = select_query_obj.sql
      query_string << union_sql
    }
  end

  return { :query => query_string, :joined_models => clause_parser.parts[:joined] }
end