#-- # George Moschovitis # (c) 2004-2005 Navel, all rights reserved. # $Id: connection.rb 248 2005-01-31 13:38:34Z gmosx $ #++ class Og; require 'glue/property' require 'glue/array' require 'glue/time' # A Connection to the Database. This file defines the skeleton # functionality. A backend specific implementation file (driver) # implements all methods. # # === Future # # - support caching. # - support prepared statements. class Connection # The frontend (Og) contains useful strucutres. attr_reader :og # The backend attr_reader :db # If set to true, the select methods deserialize the # resultset to create entities. attr_accessor :deserialize # Initialize a connection to the database def initialize(og) @og = og @db = @og.config[:backend].new(@og.config) @deserialize = true Logger.debug "Created DB connection." if $DBG end # Close the connection to the database # def close() @db.close() Logger.debug "Closed DB connection." if $DBG end # Save an object to the database. Insert if this is a new object or # update if this is already stored in the database. # def save(obj) if obj.oid # object allready inserted, update! obj.og_update(self) else # not in the database, insert! obj.og_insert(self) end end alias_method :<<, :save alias_method :put, :save # Force insertion of managed object. # def insert(obj) obj.og_insert(self) end # Force update of managed object. # def update(obj) obj.og_update(self) end # Update only specific fields of the managed object. # # Input: # sql = the sql code to updated the properties. # # WARNING: the object in memoryis not updated. #-- # TODO: should update the object in memory. #++ # def update_properties(update_sql, obj_or_oid, klass = nil) oid = obj_or_oid.to_i klass = obj_or_oid.class unless klass exec "UPDATE #{klass::DBTABLE} SET #{update_sql} WHERE oid=#{oid}" end alias_method :pupdate, :update_properties # Load an object from the database. # # Input: # oid = the object oid, OR the object name. # def load(oid, klass) if oid.to_i > 0 # a valid Fixnum ? load_by_oid(oid, klass) else load_by_name(oid, klass) end end alias_method :get, :load # Load an object by oid. # def load_by_oid(oid, klass) res = query "SELECT * FROM #{klass::DBTABLE} WHERE oid=#{oid}" @deserialize? @db.deserialize_one(res, klass) : res end alias_method :get_by_oid, :load_by_oid # Load an object by name. # def load_by_name(name, klass) res = query "SELECT * FROM #{klass::DBTABLE} WHERE name='#{name}'" @deserialize? @db.deserialize_one(res, klass) : res end alias_method :get_by_name, :load_by_name # Load all objects of the given klass. # Used to be called 'collect' in an earlier version. # def load_all(klass, extrasql = nil) res = query "SELECT * FROM #{klass::DBTABLE} #{extrasql}" @deserialize? @db.deserialize_all(res, klass) : res end alias_method :get_all, :load_all # Perform a standard SQL query to the database. Deserializes the # results. # def select(sql, klass) unless sql =~ /SELECT/i sql = "SELECT * FROM #{klass::DBTABLE} WHERE #{sql}" end res = @db.safe_query(sql) @deserialize? @db.deserialize_all(res, klass) : res end # Optimized for one result. # def select_one(sql, klass) unless sql =~ /SELECT/i sql = "SELECT * FROM #{klass::DBTABLE} WHERE #{sql}" end res = @db.safe_query(sql) @deserialize? @db.deserialize_one(res, klass) : res end # Perform a count query. # def count(sql, klass = nil) unless sql =~ /SELECT/i sql = "SELECT COUNT(*) FROM #{klass::DBTABLE} WHERE #{sql}" end res = @db.safe_query(sql) return @db.get_int(res) end # Delete an object from the database. Allways perform a deep delete. # # No need to optimize here with pregenerated code. Deletes are # not used as much as reads or writes. # # === Input: # # obj_or_oid = Object or oid to delete. # klass = Class of object (can be nil if an object is passed) # def delete(obj_or_oid, klass = nil, cascade = true) oid = obj_or_oid.to_i klass = obj_or_oid.class unless klass # this is a class callback! if klass.respond_to?(:og_pre_delete) klass.og_pre_delete(self, oid) end # TODO: implement this as stored procedure? naaah. transaction do |tx| tx.exec "DELETE FROM #{klass::DBTABLE} WHERE oid=#{oid}" if cascade and klass.__meta.include?(:has) klass.__meta[:has].each do |dclass, linkback| tx.exec "DELETE FROM #{dclass::DBTABLE} WHERE #{linkback}=#{oid}" end end end end alias_method :delete!, :delete # Create the managed object table. The properties of the # object are mapped to the table columns. Additional sql relations # and constrains are created (indicices, sequences, etc). # def create_table(klass) @db.create_table(klass) end # Drop the managed object table. # def drop_table(klass) @db.drop_table(klass) end # Execute an SQL query and return the result # def query(sql) @db.safe_query(sql) end # Execute an SQL query, no result returned. # def exec(sql) @db.safe_exec(sql) end # Start a new transaction. # def start @db.start() end # Commit a transaction. # def commit @db.commit() end # Rollback transaction. # def rollback @db.rollback() end # Transaction helper. In the transaction block use # the db pointer to the backend. # def transaction(&block) begin @db.start() yield(@db) @db.commit() rescue => ex Logger.error "DB Error: ERROR IN TRANSACTION" Logger.error #{ex} Logger.error #{ex.backtrace} @db.rollback() end end end end