Class: Og::SqlStore

Inherits:
Store
  • Object
show all
Extended by:
SqlUtils
Includes:
SqlUtils
Defined in:
lib/og/store/sql.rb

Overview

An abstract SQL powered store.

Instance Attribute Summary collapse

Attributes inherited from Store

#options, #transaction_nesting

Instance Method Summary collapse

Methods included from SqlUtils

blob, build_join_name, create_join_table_sql, date, escape, join_class_ordering, join_object_ordering, join_table, join_table_index, join_table_info, join_table_key, join_table_keys, ordered_join_table_keys, parse_blob, parse_boolean, parse_date, parse_float, parse_int, parse_timestamp, quote, table, tableize, timestamp

Methods inherited from Store

#close, create, #delete, destroy, for_name, #force_save!, #insert, #save, #transaction

Constructor Details

#initialize(options) ⇒ SqlStore

Returns a new instance of SqlStore.



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/og/store/sql.rb', line 248

def initialize(options)
  super

  # The default Ruby <-> SQL type mappings, should be valid 
  # for most RDBM systems.

  @typemap = {
    Integer => 'integer',
    Fixnum => 'integer',
    Float => 'float',
    String => 'text',
    Time => 'timestamp',
    Date => 'date',
    TrueClass => 'boolean',
    Object => 'text',
    Array => 'text',
    Hash => 'text',
    Og::Blob => 'bytea' # psql
  }
end

Instance Attribute Details

#connObject

The connection to the backend SQL RDBMS.



246
247
248
# File 'lib/og/store/sql.rb', line 246

def conn
  @conn
end

Instance Method Details

#aggregate(options) ⇒ Object Also known as: count

Perform an aggregation over query results.



469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
# File 'lib/og/store/sql.rb', line 469

def aggregate(options)
  if options.is_a?(String)
    sql = options
  else
    aggregate = options[:aggregate] || 'COUNT(*)'
    sql = "SELECT #{aggregate} FROM #{options[:class].table}"
    if condition = options[:condition]
      sql << " WHERE #{condition}"
      sql << " AND " if options[:class].schema_inheritance_child?
    else
      sql << " WHERE " if options[:class].schema_inheritance_child?
    end
    sql << "ogtype='#{options[:class]}'" if options[:class].schema_inheritance_child?
  end

  query(sql).first_value.to_i
end

#commitObject

Commit a transaction.



527
528
529
530
# File 'lib/og/store/sql.rb', line 527

def commit
  @transaction_nesting -= 1
  exec('COMMIT') if @transaction_nesting < 1
end

#delete_all(klass) ⇒ Object



510
511
512
513
514
# File 'lib/og/store/sql.rb', line 510

def delete_all(klass)
  sql = "DELETE FROM #{klass.table}"
  sql << " WHERE ogtype='#{klass}'" if klass.schema_inheritance? and not klass.schema_inheritance_root?
  exec sql
end

#enable_loggingObject

– FIXME: not working. ++



273
274
275
276
277
278
279
# File 'lib/og/store/sql.rb', line 273

def enable_logging
  require 'glue/aspects'
  klass = self.class
  klass.send :include, Glue::Aspects
  klass.pre "Logger.info sql", :on => [:exec, :query]
  Glue::Aspects.wrap(klass, [:exec, :query])
end

#enchant(klass, manager) ⇒ Object

Enchants a class.



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/og/store/sql.rb', line 307

def enchant(klass, manager)
  # setup the table where this class is mapped.

  if klass.schema_inheritance_child?
    # farms: allow deeper inheritance (TODO: use annotation :superclass)
    klass.const_set 'OGTABLE', table(klass.schema_inheritance_root_class)
  else
    klass.const_set 'OGTABLE', table(klass)
  end

  #--
  # FIXME: use an SQL agnostic name like schema instead
  # of table.
  #++
  
  klass.module_eval 'def self.table; OGTABLE; end'

  eval_og_allocate(klass)

  super

  unless klass.polymorphic_parent?
    # precompile class specific lifecycle methods.
    
    eval_og_create_schema(klass)
    eval_og_insert(klass)
    eval_og_update(klass)
    eval_og_delete(klass)

    # create the table if needed.
    
    klass.allocate.og_create_schema(self)

    # finish up with eval_og_read, since we can't do that
    # until after the table is created.
    # Possible FIXME:  This means you can't do any find-type
    # operations in og_create_schema.  Luckily, you're most
    # likely to want to do .create, which is covered by 
    # og_insert.
    
    eval_og_read(klass)
  end
end

#find(options) ⇒ Object

Find a collection of objects.

Examples

User.find(:condition => ‘age > 15’, :order => ‘score ASC’, :offet => 10, :limit =>10) Comment.find(:include => :entry)



434
435
436
437
438
# File 'lib/og/store/sql.rb', line 434

def find(options)  
  klass = options[:class]
  sql = resolve_options(klass, options)
  read_all(query(sql), klass, options)
end

#find_one(options) ⇒ Object

Find one object.



442
443
444
445
446
447
448
# File 'lib/og/store/sql.rb', line 442

def find_one(options)
  klass = options[:class]
# gmosx, THINK: should not set this by default.
#    options[:limit] ||= 1        
  sql = resolve_options(klass, options)
  read_one(query(sql), klass, options)
end

#join(obj1, obj2, table, options = nil) ⇒ Object

Relate two objects through an intermediate join table. Typically used in joins_many and many_to_many relations.



491
492
493
494
495
496
497
498
499
# File 'lib/og/store/sql.rb', line 491

def join(obj1, obj2, table, options = nil)
  first, second = join_object_ordering(obj1, obj2)
  first_key, second_key = ordered_join_table_keys(obj1.class, obj2.class)
  if options
    exec "INSERT INTO #{table} (#{first_key},#{second_key}, #{options.keys.join(',')}) VALUES (#{first.pk},#{second.pk}, #{options.values.map { |v| quote(v) }.join(',')})"
  else
    exec "INSERT INTO #{table} (#{first_key},#{second_key}) VALUES (#{first.pk}, #{second.pk})"
  end
end

#load(pk, klass) ⇒ Object Also known as: exist?

Loads an object from the store using the primary key.



355
356
357
358
359
360
361
# File 'lib/og/store/sql.rb', line 355

def load(pk, klass)
  pk_field = klass.primary_key.field || klass.primary_key.symbol
  sql = "SELECT * FROM #{klass::OGTABLE} WHERE #{pk_field}=#{pk}"
  sql << " AND ogtype='#{klass}'" if klass.schema_inheritance_child?
  res = query sql
  read_one(res, klass)
end

#managed_tables(manager) ⇒ Object

Returns a list of tables within the database that are there to support a class managed by the supplied manager.



296
297
298
299
300
301
302
303
# File 'lib/og/store/sql.rb', line 296

def managed_tables(manager)
  ret = Array.new
  manager.managed_classes.each do |klass|
    ret << klass::OGTABLE
    ret.concat(klass.relations.reject{|rel| not rel.options[:join_table]}.map{|rel| rel.options[:join_table]})
  end
  ret
end

#reload(obj, pk) ⇒ Object

Reloads an object from the store.



366
367
368
369
370
371
372
373
374
# File 'lib/og/store/sql.rb', line 366

def reload(obj, pk)
  raise 'Cannot reload unmanaged object' unless obj.saved?
  sql = "SELECT * FROM #{obj.class.table} WHERE #{obj.class.pk_symbol}=#{pk}"
  sql << " AND ogtype='#{obj.class}'" if obj.class.schema_inheritance_child?
  res = query sql
  obj.og_read(res.next, 0)
ensure
  res.close if res
end

#rollbackObject

Rollback a transaction.



534
535
536
537
# File 'lib/og/store/sql.rb', line 534

def rollback
  @transaction_nesting -= 1
  exec('ROLLBACK') if @transaction_nesting < 1
end

#select(sql, klass) ⇒ Object Also known as: find_by_sql

Perform a custom sql query and deserialize the results.



453
454
455
456
# File 'lib/og/store/sql.rb', line 453

def select(sql, klass)
  sql = "SELECT * FROM #{klass.table} " + sql unless sql =~ /SELECT/i
  read_all(query(sql), klass)
end

#select_one(sql, klass) ⇒ Object Also known as: find_by_sql_one

Specialized one result version of select.



461
462
463
464
# File 'lib/og/store/sql.rb', line 461

def select_one(sql, klass)
  sql = "SELECT * FROM #{klass.table} " + sql unless sql =~ /SELECT/i
  read_one(query(sql), klass)
end

#sql_update(sql) ⇒ Object

Encapsulates a low level update method.



543
544
545
546
# File 'lib/og/store/sql.rb', line 543

def sql_update(sql)
  exec(sql)
  # return affected rows.
end

#startObject

Start a new transaction.



520
521
522
523
# File 'lib/og/store/sql.rb', line 520

def start  
  exec('START TRANSACTION') if @transaction_nesting < 1
  @transaction_nesting += 1
end

#unjoin(obj1, obj2, table) ⇒ Object

Unrelate two objects be removing their relation from the join table.



504
505
506
507
508
# File 'lib/og/store/sql.rb', line 504

def unjoin(obj1, obj2, table)
  first, second = join_object_ordering(obj1, obj2)
  first_key, second_key = ordered_join_table_keys(obj1.class, obj2.class)
  exec "DELETE FROM #{table} WHERE #{first_key}=#{first.pk} AND #{second_key}=#{second.pk}"    
end

#unmanaged_tables(manager) ⇒ Object

Returns a list of tables that exist within the database but are not managed by the supplied manager.



284
285
286
287
288
289
290
291
# File 'lib/og/store/sql.rb', line 284

def unmanaged_tables(manager)
  ret = Array.new
  mt = managed_tables(manager)
  @conn.list_tables.each do |table|
    ret << table unless mt.include?(table)
  end
  ret
end

#update(obj, options = nil) ⇒ Object

If a properties collection is provided, only updates the selected properties. Pass the required properties as symbols or strings. – gmosx, THINK: condition is not really useful here :( ++



383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/og/store/sql.rb', line 383

def update(obj, options = nil)
  if options and properties = options[:only]
    if properties.is_a?(Array)
      set = []
      for p in properties
        set << "#{p}=#{quote(obj.send(p))}"
      end
      set = set.join(',')
    else
      set = "#{properties}=#{quote(obj.send(properties))}"
    end
    sql = "UPDATE #{obj.class.table} SET #{set} WHERE #{obj.class.pk_symbol}=#{obj.pk}"
    sql << " AND #{options[:condition]}" if options[:condition]
    sql_update(sql)
  else
    obj.og_update(self, options)
  end
end

#update_by_sql(target, set, options = nil) ⇒ Object

More generalized method, also allows for batch updates.



413
414
415
416
417
418
419
420
421
422
423
424
425
# File 'lib/og/store/sql.rb', line 413

def update_by_sql(target, set, options = nil)
  set = set.gsub(/@/, '')

  if target.is_a?(Class)
    sql = "UPDATE #{target.table} SET #{set} "
    sql << " WHERE #{options[:condition]}" if options and options[:condition]
    sql_update(sql)
  else
    sql = "UPDATE #{target.class.table} SET #{set} WHERE #{target.class.pk_symbol}=#{target.pk}"
    sql << " AND #{options[:condition]}" if options and options[:condition]
    sql_update(sql)
  end      
end

#update_properties(target, *properties) ⇒ Object Also known as: pupdate, update_property

Update selected properties of an object or class of objects.



405
406
407
# File 'lib/og/store/sql.rb', line 405

def update_properties(target, *properties)
  update(target, :only => properties)
end