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, quote_array, 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.



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/og/store/sql.rb', line 265

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.



263
264
265
# File 'lib/og/store/sql.rb', line 263

def conn
  @conn
end

Instance Method Details

#aggregate(term = 'COUNT(*)', options = {}) ⇒ Object Also known as: calculate

Perform an aggregation or calculation over query results. This low level method is used by the Entity calculation / aggregation methods.

Example

calculate ‘COUNT(*)’ calculate ‘MIN(age)’ calculate ‘SUM(age)’, :group => :name



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
# File 'lib/og/store/sql.rb', line 494

def aggregate(term = 'COUNT(*)', options = {})
  if options.is_a?(String)
    sql = options
  else
    sql = "SELECT #{term} 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?
    if group = options[:group]
      sql << " GROUP BY #{group}"
    end
    if extra = options[:extra_sql]
      sql << " {extra}"
    end        
  end
 
  if group
    # This is an aggregation, so return the calculated values
    # as an array.
    values = []
    res = query(sql)
    res.each_row do |row, idx|
      values << row[0].to_f 
    end
    return values
  else
    #--
    # gmosx, TODO: don't convert to float by default, perhaps
    # should consult an option.
    #++ 
    return query(sql).first_value.to_f
  end
end

#commitObject

Commit a transaction.



578
579
580
581
# File 'lib/og/store/sql.rb', line 578

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

#count(options = {}) ⇒ Object

Perform a count query.



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

def count(options = {})
  calculate('COUNT(*)', options).to_i
end

#delete_all(klass) ⇒ Object



561
562
563
564
565
# File 'lib/og/store/sql.rb', line 561

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. ++



290
291
292
293
294
295
296
# File 'lib/og/store/sql.rb', line 290

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.



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
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/og/store/sql.rb', line 324

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)



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

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.



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

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.



542
543
544
545
546
547
548
549
550
# File 'lib/og/store/sql.rb', line 542

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.



372
373
374
375
376
377
378
# File 'lib/og/store/sql.rb', line 372

def load(pk, klass)
  pk_field = klass.primary_key.field || klass.primary_key.symbol
  sql = "SELECT * FROM #{klass::OGTABLE} WHERE #{pk_field}=#{pk.to_i}"
  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.



313
314
315
316
317
318
319
320
# File 'lib/og/store/sql.rb', line 313

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.



383
384
385
386
387
388
389
390
391
# File 'lib/og/store/sql.rb', line 383

def reload(obj, pk)
  raise 'Cannot reload unmanaged object' unless obj.saved?
  sql = "SELECT * FROM #{obj.class.table} WHERE #{obj.class.pk_symbol}=#{pk.to_i}"
  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.



585
586
587
588
# File 'lib/og/store/sql.rb', line 585

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.



470
471
472
473
# File 'lib/og/store/sql.rb', line 470

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.



478
479
480
481
# File 'lib/og/store/sql.rb', line 478

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.



594
595
596
597
# File 'lib/og/store/sql.rb', line 594

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

#startObject

Start a new transaction.



571
572
573
574
# File 'lib/og/store/sql.rb', line 571

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.



555
556
557
558
559
# File 'lib/og/store/sql.rb', line 555

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.



301
302
303
304
305
306
307
308
# File 'lib/og/store/sql.rb', line 301

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 :( ++



400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
# File 'lib/og/store/sql.rb', line 400

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.



430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/og/store/sql.rb', line 430

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.



422
423
424
# File 'lib/og/store/sql.rb', line 422

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