Class: Og::SqlStore

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

Overview

An abstract SQL powered store.

Direct Known Subclasses

KirbyStore, MysqlStore, SqliteStore, SqlserverStore

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_date, parse_float, parse_int, parse_timestamp, quote, table, tableize, timestamp

Methods inherited from Store

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

Constructor Details

#initialize(options) ⇒ SqlStore

Returns a new instance of SqlStore.



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/og/store/sql.rb', line 235

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.



233
234
235
# File 'lib/og/store/sql.rb', line 233

def conn
  @conn
end

Instance Method Details

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

Perform an aggregation over query results.



448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
# File 'lib/og/store/sql.rb', line 448

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.



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

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

#delete_all(klass) ⇒ Object



489
490
491
# File 'lib/og/store/sql.rb', line 489

def delete_all(klass)
  exec "DELETE FROM #{klass.table}"
end

#enable_loggingObject

– FIXME: not working. ++



260
261
262
263
264
265
266
# File 'lib/og/store/sql.rb', line 260

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.



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/og/store/sql.rb', line 294

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

  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)



413
414
415
416
417
# File 'lib/og/store/sql.rb', line 413

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.



421
422
423
424
425
426
427
# File 'lib/og/store/sql.rb', line 421

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.



470
471
472
473
474
475
476
477
478
# File 'lib/og/store/sql.rb', line 470

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.



335
336
337
338
339
340
# File 'lib/og/store/sql.rb', line 335

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



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

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.



345
346
347
348
349
350
351
352
353
# File 'lib/og/store/sql.rb', line 345

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.



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

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.



432
433
434
435
# File 'lib/og/store/sql.rb', line 432

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

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

Specialized one result version of select.



440
441
442
443
# File 'lib/og/store/sql.rb', line 440

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

#sql_update(sql) ⇒ Object

Encapsulates a low level update method.



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

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

#startObject

Start a new transaction.



497
498
499
500
# File 'lib/og/store/sql.rb', line 497

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.



483
484
485
486
487
# File 'lib/og/store/sql.rb', line 483

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.



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

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



362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/og/store/sql.rb', line 362

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.



392
393
394
395
396
397
398
399
400
401
402
403
404
# File 'lib/og/store/sql.rb', line 392

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.



384
385
386
# File 'lib/og/store/sql.rb', line 384

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