The base implementation for all SQL stores. Reused by all SQL adapters.

Contents
Methods
Included Modules
Attributes
[RW] conn The connection to the SQL backend.
[RW] typemap Ruby type <-> SQL type mappings.
Public Class methods
new(options)

Initialize the store.

     # File lib/og/store/sql.rb, line 173
173:   def initialize(options)
174:     super
175: 
176:     # The default Ruby <-> SQL type mappings, should be valid 
177:     # for most RDBM systems.
178: 
179:     @typemap = {
180:       Integer => 'integer',
181:       Fixnum => 'integer',
182:       Float => 'float',
183:       String => 'text',
184:       Time => 'timestamp',
185:       Date => 'date',
186:       TrueClass => 'boolean',
187:       Object => 'text',
188:       Array => 'text',
189:       Hash => 'text',
190:       Og::Blob => 'bytea' # psql
191:     }
192:   end
Public Instance methods
close()
     # File lib/og/store/sql.rb, line 198
198:   def close
199:     @conn.close
200:     super
201:   end
create_db(options)

Creates the database where Og managed objects are serialized.

     # File lib/og/store/sql.rb, line 208
208:   def create_db(options)
209:     Logger.info "Created database '#{options[:name]}'"
210:   end
destroy_db(options)

Destroys the database where Og managed objects are serialized.

This method is also aliased as drop_db
     # File lib/og/store/sql.rb, line 217
217:   def destroy_db(options)
218:     Logger.info "Dropped database '#{options[:name]}'"
219:   end
drop_db(options)

Alias for #destroy_db

enchant(klass, manager)

Performs SQL related enchanting to the class. This method is further extended in more specialized adapters to add backend specific enchanting.

Defines:

  • the table constant, and .table / .schema aliases.
  • the index method for defing sql indices.
  • precompiles the object lifecycle callbacks.
     # File lib/og/store/sql.rb, line 246
246:   def enchant(klass, manager)
247:     # setup the table where this class is mapped.
248: 
249:     #FIXME: jl: Remove references to table, then remove these 5 lines
250:     if klass.schema_inheritance_child?
251:       klass.const_set 'OGTABLE', table(klass.schema_inheritance_root_class)
252:     else
253:       klass.const_set 'OGTABLE', table(klass)
254:     end
255: 
256:     # Define table and schema aliases for table.
257: 
258:     klass.extend SqlEnchantmentClassMethods
259:     if klass.schema_inheritance_child?
260:       klass.set_table(table(klass.schema_inheritance_root_class))
261:     else
262:       klass.set_table(table(klass))
263:     end
264: 
265:     klass.store = self
266: 
267:     # Perform base store enchantment.
268:     super
269: 
270:     unless klass.polymorphic_parent?
271:       # precompile class specific lifecycle methods.
272:       
273:       unless klass.ancestors.include? SqlEnchantmentMixin
274:         klass.include SqlEnchantmentMixin
275:       end
276: 
277:       # create the table if needed.
278:       klass.allocate.og_create_schema(self)
279:       klass.set_field_map(create_field_map(klass))
280:     end
281:   end
force_primary_key(klass)

Force the creation of a primary key class.

     # File lib/og/store/sql.rb, line 228
228:   def force_primary_key(klass)
229:     # Automatically add an :oid serializable field if none is 
230:     # defined and no other primary key is defined.
231:     if klass.primary_key == :oid and !klass.attributes.include?(:oid)
232:       klass.attr_accessor :oid, Fixnum, :sql => primary_key_type
233:     end    
234:   end
join(obj1, obj2, table, options = nil)

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

     # File lib/og/store/sql/join.rb, line 137
137:   def join(obj1, obj2, table, options = nil)
138:     first, second = join_object_ordering(obj1, obj2)
139:     first_key, second_key = ordered_join_table_keys(obj1.class, obj2.class)
140:     if options
141:       exec "INSERT INTO #{table} (#{first_key},#{second_key}, #{options.keys.join(',')}) VALUES (#{first.pk},#{second.pk}, #{options.values.map { |v| quote(v) }.join(',')})"
142:     else
143:       exec "INSERT INTO #{table} (#{first_key},#{second_key}) VALUES (#{first.pk}, #{second.pk})"
144:     end
145:   end
primary_key_type()

The type used for default primary keys.

     # File lib/og/store/sql.rb, line 223
223:   def primary_key_type
224:   'integer PRIMARY KEY'
225:   end
unjoin(obj1, obj2, table)

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

     # File lib/og/store/sql/join.rb, line 150
150:   def unjoin(obj1, obj2, table)
151:     first, second = join_object_ordering(obj1, obj2)
152:     first_key, second_key = ordered_join_table_keys(obj1.class, obj2.class)
153:     exec "DELETE FROM #{table} WHERE #{first_key}=#{first.pk} AND #{second_key}=#{second.pk}"    
154:   end
Public Instance methods
aggregate(term = 'COUNT(*)', options = {})

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

Options

:field = the return type.

Example

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

This method is also aliased as calculate
     # File lib/og/store/sql.rb, line 409
409:   def aggregate(term = 'COUNT(*)', options = {})
410:     # Leave this .dup here, causes problems because options are changed
411:     options = options.dup
412: 
413:     klass = options[:class]
414: 
415:     # Rename search term, SQL92 but _not_ SQL89 compatible
416:     options.update(:select => "#{term} AS #{term[/^\w+/]}")
417:     unless options[:group] || options[:group_by]
418:       options.delete(:order)
419:       options.delete(:order_by)
420:     end
421:     sql = resolve_options(klass, options)
422: 
423:     if field = options[:field]    
424:       return_type = klass.ann(field, :class) || Integer
425:     else
426:       return_type = Integer    
427:     end
428:     if options[:group] || options[:group_by]
429:       # This is an aggregation, so return the calculated values
430:       # as an array.
431:       values = []
432:       res = query(sql)
433:       res.each_row do |row, idx|
434:         values << type_cast(return_type, row[0])
435:       end
436:       return values
437:     else
438:       return type_cast(return_type, query(sql).first_value)
439:     end
440:   end
calculate(term = 'COUNT(*)', options = {})

Alias for #aggregate

count(options = {})

Perform a count query.

     # File lib/og/store/sql.rb, line 444
444:   def count(options = {})
445:     calculate('COUNT(*)', options).to_i
446:   end
delete_all(klass)

Delete all instances of the given class from the backend.

     # File lib/og/store/sql.rb, line 449
449:   def delete_all(klass)
450:     sql = "DELETE FROM #{klass.table}"
451:     sql << " WHERE ogtype='#{klass}'" if klass.schema_inheritance? and not klass.schema_inheritance_root?
452:     exec sql
453:   end
exist?(pk, klass)

Alias for #load

find(options)

Find a collection of objects.

Examples

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

     # File lib/og/store/sql.rb, line 362
362:   def find(options)  
363:     klass = options[:class]
364:     sql = resolve_options(klass, options)
365:     read_all(query(sql), klass, options)
366:   end
find_by_sql(sql, klass)

Alias for #select

find_by_sql_one(sql, klass)

Alias for #select_one

find_one(options)

Find one object.

     # File lib/og/store/sql.rb, line 370
370:   def find_one(options)
371:     klass = options[:class]
372:     # gmosx, THINK: should not set this by default.
373:     # options[:limit] ||= 1        
374:     sql = resolve_options(klass, options)
375:     read_one(query(sql), klass, options)
376:   end
load(pk, klass)

Loads an object from the store using the primary key. Returns nil if the passes pk is nil.

This method is also aliased as exist?
     # File lib/og/store/sql.rb, line 287
287:   def load(pk, klass)
288:     return nil unless pk
289: 
290:     sql = "SELECT * FROM #{klass.table} WHERE #{pk_field klass}=#{quote(pk)}"
291:     sql << " AND ogtype='#{klass}'" if klass.schema_inheritance_child?
292:     res = query sql
293:     read_one(res, klass)
294:   end
reload(obj, pk)

Reloads an object from the store. Returns nil if the passes pk is nil.

     # File lib/og/store/sql.rb, line 300
300:   def reload(obj, pk)
301:     return nil unless pk
302: 
303:     klass = obj.class
304:     raise 'Cannot reload unmanaged object' unless obj.saved?
305:     sql = "SELECT * FROM #{klass.table} WHERE #{pk_field klass}=#{quote(pk)}"
306:     sql << " AND ogtype='#{klass}'" if klass.schema_inheritance_child?
307:     res = query sql
308:     obj.og_read(res.next, 0)
309:   ensure
310:     res.close if res
311:   end
select(sql, klass)

Perform a custom sql query and deserialize the results.

This method is also aliased as find_by_sql
     # File lib/og/store/sql.rb, line 381
381:   def select(sql, klass)
382:     sql = "SELECT * FROM #{klass.table} " + sql unless sql =~ /SELECT/i
383:     read_all(query(sql), klass)
384:   end
select_one(sql, klass)

Specialized one result version of select.

This method is also aliased as find_by_sql_one
     # File lib/og/store/sql.rb, line 389
389:   def select_one(sql, klass)
390:     sql = "SELECT * FROM #{klass.table} " + sql unless sql =~ /SELECT/i
391:     read_one(query(sql), klass)
392:   end
update(obj, options = nil)

If an attributes collection is provided, only updates the selected attributes. Pass the required attributes as symbols or strings.

     # File lib/og/store/sql.rb, line 320
320:   def update(obj, options = nil)
321:     if options and attrs = options[:only]
322:       if attrs.is_a?(Array)
323:         set = []
324:         for a in attrs
325:           set << "#{a}=#{quote(obj.send(a))}"
326:         end
327:         set = set.join(',')
328:       else
329:         set = "#{attrs}=#{quote(obj.send(attrs))}"
330:       end
331:       sql = "UPDATE #{obj.class.table} SET #{set} WHERE #{pk_field obj.class}=#{quote(obj.pk)}"
332:       sql << " AND #{options[:condition]}" if options[:condition]
333:       sql_update(sql)
334:     else
335:       obj.og_update(self, options)
336:     end
337:   end
update_by_sql(target, set, options = nil)

More generalized method, also allows for batch updates.

     # File lib/og/store/sql.rb, line 341
341:   def update_by_sql(target, set, options = nil)
342:     set = set.gsub(/@/, '')
343: 
344:     if target.is_a? Class
345:       sql = "UPDATE #{target.table} SET #{set} "
346:       sql << " WHERE #{options[:condition]}" if options and options[:condition]
347:       sql_update(sql)
348:     else
349:       sql = "UPDATE #{target.class.table} SET #{set} WHERE #{pk_field target.class}=#{quote(target.pk)}"
350:       sql << " AND #{options[:condition]}" if options and options[:condition]
351:       sql_update(sql)
352:     end      
353:   end
Public Instance methods
create_table(klass)

Create the SQL table where instances of the given class will be serialized.

     # File lib/og/store/sql.rb, line 458
458:   def create_table(klass)
459:     fields = fields_for_class(klass)
460: 
461:     sql = "CREATE TABLE #{klass.table} (#{fields.join(', ')}"
462: 
463:     # Create table constraints.
464: 
465:     if constraints = klass.ann(:self, :sql_constraint)
466:       sql << ", #{constraints.join(', ')}"
467:     end
468: 
469:     # Set the table type (Mysql default, InnoDB, Hash, etc)
470:     if table_type = @options[:table_type]
471:       sql << ") TYPE = #{table_type};"  
472:     else
473:       sql << ")"
474:     end
475: 
476:     begin
477:       exec(sql, false)
478:       Logger.info "Created table '#{klass.table}'."
479:     rescue Object => ex
480:       if table_already_exists_exception? ex
481:         # Don't return yet. Fall trough to also check for the 
482:         # join table.
483:       else
484:         handle_sql_exception(ex, sql)
485:       end
486:     end
487:   end
create_table_indices(klass)

Create indices.

An example index definition:

classs MyClass

  attr_accessor :age, Fixnum, :index => true, :pre_index => ...,
                :post_index => ...

end

     # File lib/og/store/sql.rb, line 498
498:   def create_table_indices(klass)
499:     for idx in sql_indices_for_class(klass)
500:       anno = klass.ann(idx)
501:       idx = idx.to_s
502:       pre_sql, post_sql = anno[:pre_index], anno[:post_index]
503:       idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
504:       sql = "CREATE #{pre_sql} INDEX #{klass.table}_#{idxname}_idx #{post_sql} ON #{klass.table} (#{idx})"
505:       exec sql
506:     end
507:   end
create_table_joins(klass)

Create join tables if needed. Join tables are used in ‘many_to_many’ relations.

     # File lib/og/store/sql.rb, line 511
511:   def create_table_joins(klass)
512:     if join_tables = klass.ann(:self, :join_tables) 
513:       for info in join_tables
514:         begin
515:           create_join_table_sql(info).each do |sql|
516:             exec(sql, false)
517:           end
518:           Logger.debug "Created jointable '#{info[:table]}'." if $DBG
519:         rescue Object => ex
520:           if table_already_exists_exception? ex
521:             Logger.debug 'Join table already exists' if $DBG
522:           else
523:             raise
524:           end
525:         end
526:       end
527:     end
528:   end
destroy(klass)

Alias for #drop_table

drop_table(klass)

Drop the sql table where objects of this class are persisted.

This method is also aliased as destroy
     # File lib/og/store/sql.rb, line 532
532:   def drop_table(klass)
533:     # Remove leftover data from some join tabkes.
534:     klass.relations.each do |rel|
535:       if rel.class.to_s == "Og::JoinsMany" and rel.join_table
536:         target_class =  rel.target_class
537:         exec "DELETE FROM #{rel.join_table}"
538:       end
539:     end
540:     exec "DROP TABLE #{klass.table}"
541:   end
exec(sql, rescue_exception = true)

Perform an sql query with no results.

     # File lib/og/store/sql.rb, line 566
566:   def exec(sql, rescue_exception = true)
567:     Logger.debug sql if $DBG
568:     exec_statement(sql)
569:   rescue Object => ex
570:     if rescue_exception
571:       handle_sql_exception(ex, sql)
572:     else 
573:       raise
574:     end
575:   end
exec_statement(sql)
     # File lib/og/store/sql.rb, line 580
580:   def exec_statement(sql)
581:     return @conn.exec(sql)
582:   end
handle_sql_exception(ex, sql = nil)

Gracefully handle a backend exception.

     # File lib/og/store/sql.rb, line 585
585:   def handle_sql_exception(ex, sql = nil)
586:     Logger.error "DB error #{ex}, [#{sql}]"
587:     Logger.error ex.backtrace.join("\n")
588:     raise StoreException.new(ex, sql) if Og.raise_store_exceptions
589: 
590:     # FIXME: should return :error or something.
591:     return nil 
592:   end
last_insert_id(klass)

Return the last inserted row id.

     # File lib/og/store/sql.rb, line 607
607:   def last_insert_id(klass)
608:     # return last insert id
609:   end
prepare_statement(condition)

takes an array, the first parameter of which is a prepared statement style string; this handles parameter escaping.

     # File lib/og/store/sql.rb, line 720
720:   def prepare_statement(condition)
721:     args = condition.dup
722:     str = args.shift
723:     # ? handles a single type.
724:     # ?* handles an array.
725:     args.each { |arg| str.sub!(/\?\*/, quotea(arg)); str.sub!(/\?/, quote(arg)) }
726:     condition = str
727:   end
query(sql, rescue_exception = true)

Perform an sql query with results.

     # File lib/og/store/sql.rb, line 547
547:   def query(sql, rescue_exception = true)
548:     Logger.debug sql if $DBG
549:     return query_statement(sql)
550:   rescue Object => ex
551:     if rescue_exception
552:       handle_sql_exception(ex, sql)
553:     else 
554:       raise
555:     end
556:   end
query_statement(sql)
     # File lib/og/store/sql.rb, line 561
561:   def query_statement(sql)
562:     return @conn.query(sql)
563:   end
resolve_limit_options(options, sql)

Subclasses can override this if they need some other order. This is needed because different backends require different order of the keywords.

     # File lib/og/store/sql.rb, line 733
733:   def resolve_limit_options(options, sql)
734:     if limit = options[:limit]
735:       sql << " LIMIT #{limit}"
736: 
737:       if offset = options[:offset]
738:         sql << " OFFSET #{offset}"
739:       end
740:     end
741:   end
resolve_options(klass, options)

Resolve the finder options. Also takes scope into account. This method handles among other the following cases:

User.find :condition => "name LIKE ‘g%’", :order => ‘name ASC’ User.find :where => "name LIKE ‘g%’", :order => ‘name ASC’ User.find :sql => "WHERE name LIKE ‘g%’ ORDER BY name ASC" User.find :condition => [ ‘name LIKE ?’, ‘g%’ ], :order => ‘name ASC’, :limit => 10

If an array is passed as a condition, use prepared statement style escaping. For example:

User.find :condition => [ ‘name = ? AND age > ?’, ‘gmosx’, 12 ]

Proper escaping is performed to avoid SQL injection attacks.

     # File lib/og/store/sql.rb, line 628
628:   def resolve_options(klass, options)
629:     # Factor in scope.
630:     if scope = klass.get_scope
631:       scope = scope.dup
632:       scond = scope.delete(:condition)
633:       scope.update(options)
634:       options = scope
635:     end
636: 
637:     if sql = options[:sql]
638:       sql = "SELECT * FROM #{klass.table} " + sql unless sql =~ /SELECT/i
639:       return sql
640:     end
641: 
642:     tables = [klass::table]
643: 
644:     if included = options[:include]
645:       join_conditions = []
646: 
647:       for name in [included].flatten
648:         if rel = klass.relation(name)
649:           target_table = rel[:target_class]::table
650:           tables << target_table
651: 
652:           if rel.is_a?(JoinsMany)
653:             tables << rel[:join_table]
654:             klass.ogmanager.with_store do |s|
655:               owner_key, target_key = s.join_table_keys(klass, rel[:target_class])
656:             end
657:             join_conditions << "#{rel.join_table}.#{owner_key}=#{klass::table}.#{rel.owner_class.primary_key} AND \
658:                                 #{rel.join_table}.#{target_key}=#{rel.target_class::table}.#{rel.target_class.primary_key}"
659:           else
660:             join_conditions << "#{klass::table}.#{rel.foreign_key}=#{target_table}.#{rel.target_class.primary_key}"
661:           end
662:         else
663:           raise 'Unknown relation name'
664:         end
665:       end
666: 
667:       fields = tables.collect { |t| "#{t}.*" }.join(',')
668: 
669:       update_condition options, join_conditions.join(' AND ')
670:     elsif fields = options[:select]
671:       # query the provided fields.
672:     else
673:       fields = '*'
674:     end
675: 
676:     if join_table = options[:join_table]
677:       tables << join_table
678:       update_condition options, options[:join_condition]
679:     end
680: 
681:     # Factor in scope in the conditions.
682:     update_condition(options, scond) if scond
683: 
684:     # rp: type is not set in all instances such as Class.first 
685:     # so this fix goes here for now.
686:     if ogtype = options[:type] || (klass.schema_inheritance_child? ? "#{klass}" : nil)
687:       update_condition options, "ogtype='#{ogtype}'"
688:     end
689: 
690:     sql = "SELECT #{fields} FROM #{tables.join(',')}"
691: 
692:     if condition = options[:condition] || options[:where]
693:       # If an array is passed as a condition, use prepared 
694:       # statement style escaping. 
695:       if condition.is_a?(Array)
696:         condition = prepare_statement(condition)
697:       end
698: 
699:       sql << " WHERE #{condition}"
700:     end
701:     if group = options[:group] || options[:group_by]
702:       sql << " GROUP BY #{group}"
703:     end
704: 
705:     if order = options[:order] || options[:order_by]
706:       sql << " ORDER BY #{order}"
707:     end
708: 
709:     resolve_limit_options(options, sql)
710: 
711:     if extra = options[:extra] || options[:extra_sql]
712:       sql << " #{extra}"
713:     end
714: 
715:     return sql
716:   end
sql_update(sql)

Perform an sql update, return the number of updated rows.

     # File lib/og/store/sql.rb, line 598
598:   def sql_update(sql)
599:     exec(sql)
600:     # return affected rows.
601:   end
Public Instance methods
annotation_for_field(klass, a)

Create the fields that correspond to the class serializable attributes. The generated fields array is used in create_table.

If the property has an :sql annotation this overrides the default mapping. If the property has an :extra_sql annotation the extra sql is appended after the default mapping.

      # File lib/og/store/sql.rb, line 1004
1004:   def annotation_for_field(klass, a)
1005:     anno = klass.ann(a)
1006:     if klass.schema_inheritance?
1007:       for desc in klass.schema_inheritance_root_class.descendents
1008:         anno = desc.ann(a).merge(anno) 
1009:         #JL: anno clashes should perhaps fail in this case.
1010:         #I'm further thinking that the fields_for and sql_for_klass stuff
1011:         #should be pushed into Entity, and SchemaInheritance should alter
1012:         #them.  Otherwise we'll be chasing STI around with conditionals
1013:         #all over the source
1014:       end
1015:     end
1016:     return anno
1017:   end
create_field_map(klass, rebuild = true)
      # File lib/og/store/sql.rb, line 1051
1051:   def create_field_map(klass, rebuild = true)
1052:     return @@field_map if defined?(@@field_map) && !rebuild
1053:     real_klass = klass.table_class
1054: 
1055:     sql = "SELECT * FROM #{table(real_klass)}"
1056:     resolve_limit_options({:limit => 1}, sql)
1057:     res = query(sql)
1058:     @@field_map = map = {}
1059: 
1060:     # Check if the field should be ignored.
1061:     ignore = klass.ann(:self, :ignore_field) || klass.ann(:self, :ignore_fields) || klass.ann(:self, :ignore_columns)   
1062: 
1063:     res.fields.each_with_index do |f, i|
1064:       field_name = f.to_sym
1065:       unless (ignore and ignore.include?(field_name))
1066:         map[field_name] = i
1067:       end
1068:     end
1069:     return @@field_map
1070:   ensure
1071:     res.close if res
1072:   end
field_for_attribute(a, anno)

Return the SQL table field for the given serializable attribute. You can override the default field name by annotating the attribute with a :field annotation.

     # File lib/og/store/sql.rb, line 760
760:   def field_for_attribute(a, anno)
761:     (f = anno[:field]) ? f : a
762:   end
field_sql_for_attribute(a, anno)
     # File lib/og/store/sql.rb, line 764
764:   def field_sql_for_attribute(a, anno)
765:     field = "#{field_for_attribute(a, anno)}"
766: 
767:     if anno[:sql]
768:       field << " #{anno[:sql]}"
769:     else
770:       field << " #{sql_type_for_class(anno[:class])}"
771:       field << " UNIQUE" if anno[:unique]  
772:       field << " DEFAULT #{quote(anno[:default])} NOT NULL" if anno[:default]
773:       field << " #{anno[:extra_sql]}" if anno[:extra_sql]
774:     end
775: 
776:     return field
777:   end
fields_for_class(klass)
      # File lib/og/store/sql.rb, line 1019
1019:   def fields_for_class(klass)
1020:     fields = []
1021:     attrs = serializable_attributes_for_class(klass)
1022: 
1023:     for a in attrs
1024:       anno = annotation_for_field(klass, a)
1025:       fields << field_sql_for_attribute(a, anno)
1026:     end
1027: 
1028:     return fields
1029:   end
fields_for_class(klass)

Create the fields that correspond to the class serializable attributes. The generated fields array is used in create_table.

If the property has an :sql annotation this overrides the default mapping. If the property has an :extra_sql annotation the extra sql is appended after the default mapping.

     # File lib/og/store/sql.rb, line 963
963:   def fields_for_class(klass)
964:     fields = []
965: 
966:     for a in serializable_attributes_for_class(klass)
967:       fields << field_sql_for_attribute(a, klass.ann(a))
968:     end
969: 
970:     return fields
971:   end
insert(klass, inserts)
     # File lib/og/store/sql.rb, line 779
779:   def insert(klass, inserts)
780:     exec(insert_sql(klass, inserts))
781:     return last_insert_id(klass)
782:   end
insert_sql(klass, inserts)
     # File lib/og/store/sql.rb, line 784
784:   def insert_sql(klass, inserts)
785:     fields = []
786:     values = []
787: 
788:     inserts.each_pair do |field,value|
789:       fields << field
790:       values << value
791:     end
792: 
793:     fields = fields.join(', ')
794:     values = values.join(', ')
795: 
796:     return "INSERT INTO #{klass.table} (#{fields}) VALUES (#{values})"
797:   end
pk_field(klass)

Generates the SQL field of the primary key for this class.

      # File lib/og/store/sql.rb, line 1075
1075:   def pk_field klass
1076:     pk = klass.primary_key
1077:     return klass.ann(pk, :field) || pk
1078:   end
read_attr(anno, res, col, offset)

Generate code to deserialize an SQL table field into an attribute. Get the fields from the database table. Also handles the change of ordering of the fields in the table.

To ignore a database field use the ignore_fields annotation ie,

class Article

  ann :self, :ignore_fields => [ :tsearch_idx, :ext_field ]

end

other aliases for ignore_fiels: ignore_field, ignore_column.

     # File lib/og/store/sql.rb, line 931
931:   def read_attr(anno, res, col, offset)
932:     if anno[:class].ancestor? Integer
933:       self.class.parse_int(res[col + offset])
934:     elsif anno[:class].ancestor? Float
935:       self.class.parse_float(res[col + offset])
936:     elsif anno[:class].ancestor? String
937:       res[col + offset]
938:     elsif anno[:class].ancestor? Time
939:       self.class.parse_timestamp(res[col+ offset])
940:     elsif anno[:class].ancestor? Date
941:       self.class.parse_date(res[col + offset])
942:     elsif anno[:class].ancestor? TrueClass
943:       self.class.parse_boolean(res[col + offset])
944:     elsif anno[:class].ancestor? Og::Blob
945:       self.class.parse_blob(res[col + offset])
946:     else 
947:       if res[col+offset].nil? or res[col+offset].empty?
948:         return nil
949:       else
950:         return YAML::load(res[col + offset])
951:       end
952:     end        
953:   end
serializable_attributes_for_class(klass)

Return either the serializable attributes for the class or, in the case of schema inheritance, all of the serializable attributes of the class hierarchy, starting from the schema inheritance root class.

     # File lib/og/store/sql.rb, line 749
749:   def serializable_attributes_for_class(klass)
750:     attrs = klass.serializable_attributes
751:     klass.table_class.each_schema_child do |desc|
752:         attrs.concat desc.serializable_attributes
753:     end
754:     return attrs.uniq
755:   end
sql_indices_for_class(klass)

Returns the SQL indexed serializable attributes for the given class.

      # File lib/og/store/sql.rb, line 1034
1034:   def sql_indices_for_class klass
1035:     indices = []
1036: 
1037:     for a in klass.serializable_attributes
1038:       indices << a if klass.ann(a, :index)
1039:     end
1040: 
1041:     return indices
1042:   end
sql_indices_for_class(klass)

Returns the SQL indexed serializable attributes for the given class.

     # File lib/og/store/sql.rb, line 976
976:   def sql_indices_for_class klass
977:     indices = []
978: 
979:     for a in klass.serializable_attributes
980:       indices << a if klass.ann(a, :index)
981:     end
982: 
983:     return indices
984:   end
sql_type_for_class(klass)

Return the SQL type for the given Ruby class.

      # File lib/og/store/sql.rb, line 1046
1046:   def sql_type_for_class klass
1047:     @typemap[klass]    
1048:   end
sql_type_for_class(klass)

Return the SQL type for the given Ruby class.

     # File lib/og/store/sql.rb, line 988
988:   def sql_type_for_class klass
989:     @typemap[klass]    
990:   end
type_cast(klass, val)
      # File lib/og/store/sql.rb, line 1080
1080:   def type_cast(klass, val)
1081:     typemap = {
1082:       Time      => :parse_timestamp,
1083:       Date      => :parse_date,
1084:       TrueClass => :parse_boolean
1085:     }
1086: 
1087:     if method = typemap[klass]
1088:       send(method, val)
1089:     else
1090:       Integer(val) rescue Float(val) rescue raise "No conversion for #{klass} (#{val.inspect})"
1091:     end
1092:   end
update_sql(klass, pk, updates)
     # File lib/og/store/sql.rb, line 799
799:   def update_sql(klass, pk, updates)
800:     updates = updates.map do |key,value|
801:       key + "=" + value
802:     end.join(', ')
803: 
804:     pk_field = klass.primary_key
805:     pk_field = klass.ann(pk_field, :field) || pk_field
806: 
807:     sql = "UPDATE #{klass.table} SET #{updates} WHERE #{pk_field}=#{pk}"
808:     return sql
809:   end
write_attr(value, anno)
     # File lib/og/store/sql.rb, line 883
883:   def write_attr(value, anno)
884:     store = self.class
885:     result = nil
886: 
887:     if anno[:class].ancestor? Integer
888:       result = write_attr_integer(value)
889:     elsif anno[:class].ancestor? Float
890:       result = write_attr_float(value)
891:     elsif anno[:class].ancestor? String
892:       result = write_attr_string(value)
893:     elsif anno[:class].ancestor? Time
894:       result = write_attr_time(value)
895:     elsif anno[:class].ancestor? Date
896:       result = write_attr_date(value)
897:     elsif anno[:class].ancestor? TrueClass
898:       result = write_attr_boolean(value)
899:     elsif anno[:class].ancestor? Og::Blob
900:       result = write_attr_blob(value)
901:     else
902:       # keep the '' for nil symbols.
903:       return write_attr_other(value)
904:     end
905: 
906:     if value.nil?
907:       return write_attr_nil
908:     end
909: 
910:     return result
911:   end
write_attr_blob(value)
     # File lib/og/store/sql.rb, line 871
871:   def write_attr_blob(value)
872:     "'#{self.class.escape(self.class.blob(value))}'" 
873:   end
write_attr_blob(value)
     # File lib/og/store/sql.rb, line 851
851:   def write_attr_blob(value)
852:     "'#{self.class.escape(self.class.blob(value))}'" 
853:   end
write_attr_boolean(value)
     # File lib/og/store/sql.rb, line 847
847:   def write_attr_boolean(value)
848:     value ? "'t'" : 'NULL'
849:   end
write_attr_boolean(value)
     # File lib/og/store/sql.rb, line 867
867:   def write_attr_boolean(value)
868:     value ? "'t'" : 'NULL'
869:   end
write_attr_date(value)
     # File lib/og/store/sql.rb, line 843
843:   def write_attr_date(value)
844:     "'#{self.class.date(value)}'"
845:   end
write_attr_date(value)
     # File lib/og/store/sql.rb, line 863
863:   def write_attr_date(value)
864:     "'#{self.class.date(value)}'"
865:   end
write_attr_float(value)
     # File lib/og/store/sql.rb, line 831
831:   def write_attr_float(value)
832:     value.to_s
833:   end
write_attr_integer(value)

Generate code to serialize an attribute to an SQL table field. YAML is used (instead of Marshal) to store general Ruby objects to be more portable.

Input

  • s = attribute symbol
  • a = attribute annotations
     # File lib/og/store/sql.rb, line 827
827:   def write_attr_integer(value)
828:     value.to_s
829:   end
write_attr_nil()
     # File lib/og/store/sql.rb, line 879
879:   def write_attr_nil
880:     'NULL'
881:   end
write_attr_other(value)
     # File lib/og/store/sql.rb, line 875
875:   def write_attr_other(value)
876:     return value ? "'#{self.class.escape(value.to_yaml)}'" : "''"
877:   end
write_attr_string(value)
     # File lib/og/store/sql.rb, line 855
855:   def write_attr_string(value)
856:     "'#{self.class.escape(value)}'"
857:   end