# code: # * George Moschovitis # # (c) 2004 Navel, all rights reserved. # $Id: backend.rb 155 2004-11-13 20:32:12Z gmosx $ require "yaml" require "og/connection" module Og # = OgUtils # # A collection of useful utilities. # module Utils # The name of the SQL table where objects of this class are stored. # The Module separators are replaced with _ and NOT stripped out so # that we can convert back to the original notation if needed. # The leading module if available is removed. # def self.table(klass) return "_#{klass.name.gsub(/^.*::/, "")}".gsub(/::/, "_").downcase end # Returns the props that will be included in the insert query. # For some backends the oid should be stripped. # def self.props_for_insert(klass) klass.__props end # Precompile the insert code for the given class. # The generated code sets the oid when inserting! # def self.eval_og_insert(klass) props = props_for_insert(klass) values = props.collect { |p| write_prop(p) } sql = "INSERT INTO #{table(klass)} (#{props.collect {|p| p.name}.join(',')}) VALUES (#{values.join(',')})" if klass.instance_methods.include?("og_pre_insert") pre_cb = "og_pre_insert(conn);" else pre_cb = "" end if klass.instance_methods.include?("og_post_insert") post_cb = "og_post_insert(conn);" else post_cb = "" end if klass.instance_methods.include?("og_pre_insert_update") pre_cb << "og_pre_insert_update(conn);" end if klass.instance_methods.include?("og_post_insert_update") post_cb << "og_post_insert_update(conn);" end klass.class_eval %{ def og_insert(conn) #{insert_code(klass, sql, pre_cb, post_cb)} end } end # Precompile the update code for the given class. # Ignore the oid when updating! # def self.eval_og_update(klass) props = klass.__props.reject { |p| :oid == p.symbol } updates = props.collect { |p| "#{p.name}=#{write_prop(p)}" } sql = "UPDATE #{klass::DBTABLE} SET #{updates.join(', ')} WHERE oid=#\{@oid\}" if klass.instance_methods.include?("og_pre_update") pre_cb = "og_pre_update(conn);" else pre_cb = "" end if klass.instance_methods.include?("og_post_update") post_cb = "og_post_update(conn);" else post_cb = "" end if klass.instance_methods.include?("og_pre_insert_update") pre_cb << "og_pre_insert_update(conn);" end if klass.instance_methods.include?("og_post_insert_update") post_cb << "og_post_insert_update(conn);" end klass.class_eval %{ def og_update(conn) #{pre_cb} conn.exec "#{sql}" #{post_cb} end } end # Precompile the code to read objects of the given class # from the backend. In order to allow for changing field/attribute # orders we have to use a field mapping hash. # def self.eval_og_deserialize(klass, og) calc_field_index(klass, og) props = klass.__props code = [] props.each do |p| if idx = og.managed_classes[klass].field_index[p.name] # more fault tolerant if a new field is added and it # doesnt exist in the database. code << "@#{p.name} = #{Og::Utils.read_prop(p, idx)}" end end klass.class_eval %{ def og_deserialize(res, tuple = nil) #{code.join('; ')} end } end end # = Backend # # Abstract backend. A backend communicates with the RDBMS. # This is the base class for the various backend implementations. # class Backend # The actual connection to the database attr_accessor :conn # Intitialize the connection to the RDBMS. # def initialize(config) raise "Not implemented" end # Close the connection to the RDBMS. # def close() @conn.close() end # Create the database. # def self.create_db(database, user = nil, password = nil) $log.info "Creating database '#{database}'." end # Drop the database. # def self.drop_db(database, user = nil, password = nil) $log.info "Dropping database '#{database}'." end # Execute an SQL query and return the result. # def query(sql) raise "Not implemented" end # Execute an SQL query, no result returned. # def exec(sql) raise "Not implemented" end # Execute an SQL query and return the result. Wrapped in a rescue # block. # def safe_query(sql) raise "Not implemented" end # Execute an SQL query, no result returned. Wrapped in a rescue # block. # def safe_exec(sql) raise "Not implemented" end # Check if it is a valid resultset. # def valid?(res) raise "Not implemented" end # Start a new transaction. # def start exec "START TRANSACTION" end # Commit a transaction. # def commit exec "COMMIT" end # Rollback transaction. # def rollback exec "ROLLBACK" end # 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) return if query("SELECT * FROM #{klass::DBTABLE} LIMIT 1") end # Drop the managed object table # def drop_table(klass) exec "DROP TABLE #{klass::DBTABLE}" end # Deserialize one row of the resultset. # def deserialize_one(res, klass) raise "Not implemented" end # Deserialize all rows of the resultset. # def deserialize_all(res, klass) raise "Not implemented" end # Return a single integer value from the resultset. # def get_int(res, idx = 0) raise "Not implemented" end end end # module