# WARNING: # This store is not converted to the latest Og codebase. # DO NOT USE YET! begin require 'dbi' rescue Object => ex Logger.error 'Ruby-DBI bindings not present or ADO support not available.' Logger.error ex end require 'og/store/sql' # Customize the standard SqlServer resultset to make # more compatible with Og. =begin class Sqlserver::Result def blank? 0 == num_rows end alias_method :next, :fetch_row def each_row each do |row| yield(row, 0) end end def first_value val = fetch_row[0] free return val end alias_method :close, :free end =end module Og module SqlserverUtils include SqlUtils def escape(str) return nil unless str return Sqlserver.quote(str) end def quote(val) case val when Fixnum, Integer, Float val ? val.to_s : 'NULL' when String val ? "'#{escape(val)}'" : 'NULL' when Time val ? "'#{timestamp(val)}'" : 'NULL' when Date val ? "'#{date(val)}'" : 'NULL' when TrueClass val ? "'1'" : 'NULL' else # gmosx: keep the '' for nil symbols. val ? escape(val.to_yaml) : '' end end end # A Store that persists objects into a Sqlserver database. # To read documentation about the methods, consult the documentation # for SqlStore and Store. class SqlserverStore < SqlStore extend SqlserverUtils include SqlserverUtils def self.create(options) raise 'Not implemented' end def self.destroy(options) raise 'Not implemented' end def initialize(options) super begin @conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=#{options[:address]};Initial Catalog=#{options[:name]};User Id=#{options[:user]};Password=#{options[:password]};") rescue => ex # gmosx, FIXME: drak, fix this! if ex.to_s =~ /database .* does not exist/i Logger.info "Database '#{options[:name]}' not found!" self.class.create(options) retry end raise end end def close @conn.disconnect super end def enchant(klass, manager) klass.property :oid, Fixnum, :sql => 'integer AUTO_INCREMENT PRIMARY KEY' super end def query(sql) # Logger.debug sql if $DBG return @conn.select_all(sql) rescue => ex handle_sql_exception(ex, sql) end def exec(sql) # Logger.debug sql if $DBG @conn.execute(sql).finish rescue => ex handle_sql_exception(ex, sql) end private def create_table(klass) fields = fields_for_class(klass) sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}" # Create table constrains. if klass.__meta and constrains = klass.__meta[:sql_constrain] sql << ", #{constrains.join(', ')}" end sql << ");" # Create indices. if klass.__meta and indices = klass.__meta[:index] for data in indices idx, options = *data idx = idx.to_s pre_sql, post_sql = options[:pre], options[:post] idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "") sql << " CREATE #{pre_sql} INDEX #{klass::OGTABLE}_#{idxname}_idx #{post_sql} ON #{klass::OGTABLE} (#{idx});" end end conn.query_with_result = false begin conn.query(sql) Logger.info "Created table '#{klass::OGTABLE}'." rescue => ex # gmosx: any idea how to better test this? if ex.errno == 1050 # table already exists. Logger.debug 'Table already exists' if $DBG return else raise end end =begin # Create join tables if needed. Join tables are used in # 'many_to_many' relations. if klass.__meta and joins = klass.__meta[:sql_join] for data in joins # the class to join to and some options. join_name, join_class, options = *data join_table = "#{self.class.join_table(klass, join_class, join_name)}" join_src = "#{self.class.encode(klass)}_oid" join_dst = "#{self.class.encode(join_class)}_oid" begin conn.exec("CREATE TABLE #{join_table} ( key1 integer NOT NULL, key2 integer NOT NULL )").clear conn.exec("CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)").clear conn.exec("CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)").clear rescue => ex # gmosx: any idea how to better test this? if ex.errno == 1050 # table already exists. Logger.debug 'Join table already exists' else raise end end end end =end end def create_field_map(klass) conn.query_with_result = true res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1" map = {} res.num_fields.times do |i| map[res.fetch_field.name.intern] = i end return map ensure res.close if res end def write_prop(p) if p.klass.ancestors.include?(Integer) return "#\{@#{p.symbol} || 'NULL'\}" elsif p.klass.ancestors.include?(Float) return "#\{@#{p.symbol} || 'NULL'\}" elsif p.klass.ancestors.include?(String) return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol})\}'" : 'NULL'\}| elsif p.klass.ancestors.include?(Time) return %|#\{@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : 'NULL'\}| elsif p.klass.ancestors.include?(Date) return %|#\{@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : 'NULL'\}| elsif p.klass.ancestors.include?(TrueClass) return "#\{@#{p.symbol} ? \"'1'\" : 'NULL' \}" else # gmosx: keep the '' for nil symbols. return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}| end end def read_prop(p, col) if p.klass.ancestors.include?(Integer) return "res[#{col} + offset].to_i" elsif p.klass.ancestors.include?(Float) return "res[#{col} + offset].to_f" elsif p.klass.ancestors.include?(String) return "res[#{col} + offset]" elsif p.klass.ancestors.include?(Time) return "#{self.class}.parse_timestamp(res[#{col} + offset])" elsif p.klass.ancestors.include?(Date) return "#{self.class}.parse_date(res[#{col} + offset])" elsif p.klass.ancestors.include?(TrueClass) return "('0' != res[#{col} + offset])" else return "YAML.load(res[#{col} + offset])" end end def eval_og_insert(klass) props = klass.properties values = props.collect { |p| write_prop(p) }.join(',') sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| p.symbol.to_s}.join(',')}) VALUES (#{values})" klass.class_eval %{ def og_insert(store) #{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)} store.conn.query_with_result = false store.conn.query "#{sql}" @#{klass.pk_symbol} = store.conn.insert_id #{Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)} end } end end end