lib/og/store/mysql.rb in og-0.20.0 vs lib/og/store/mysql.rb in og-0.21.0
- old
+ new
@@ -1,313 +1,317 @@
begin
- require 'mysql'
+ require 'mysql'
rescue Object => ex
- Logger.error 'Ruby-Mysql bindings are not installed!'
- Logger.error 'Trying to uses the pure-Ruby binding included in Og'
- begin
- # Attempt to use the included pure ruby version.
- require 'vendor/mysql'
- require 'vendor/mysql411'
- rescue Object => ex
- Logger.error ex
- end
+ Logger.error 'Ruby-Mysql bindings are not installed!'
+ Logger.error 'Trying to uses the pure-Ruby binding included in Og'
+ begin
+ # Attempt to use the included pure ruby version.
+ require 'vendor/mysql'
+ require 'vendor/mysql411'
+ rescue Object => ex
+ Logger.error ex
+ end
end
require 'og/store/sql'
# Customize the standard mysql resultset to make
# more compatible with Og.
class Mysql::Result
- def blank?
- 0 == num_rows
- end
+ def blank?
+ 0 == num_rows
+ end
- alias_method :next, :fetch_row
+ 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
+ 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
+
+ def fields
+ fetch_fields.map { |f| f.name }
+ end
end
module Og
module MysqlUtils
- include SqlUtils
-
- def escape(str)
- return nil unless str
- return Mysql.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
+ include SqlUtils
+
+ def escape(str)
+ return nil unless str
+ return Mysql.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 MySQL database.
# To read documentation about the methods, consult the documentation
# for SqlStore and Store.
class MysqlStore < SqlStore
- extend MysqlUtils
- include MysqlUtils
+ extend MysqlUtils
+ include MysqlUtils
- def self.create(options)
- # gmosx: system is used to avoid shell expansion.
- system 'mysqladmin', '-f', "--user=#{options[:user]}",
- "--password=#{options[:password]}",
- 'create', options[:name]
- super
- end
+ def self.create(options)
+ # gmosx: system is used to avoid shell expansion.
+ system 'mysqladmin', '-f', "--user=#{options[:user]}",
+ "--password=#{options[:password]}",
+ 'create', options[:name]
+ super
+ end
- def self.destroy(options)
- system 'mysqladmin', '-f', "--user=#{options[:user]}",
- "--password=#{options[:password]}", 'drop',
- options[:name]
- super
- end
+ def self.destroy(options)
+ system 'mysqladmin', '-f', "--user=#{options[:user]}",
+ "--password=#{options[:password]}", 'drop',
+ options[:name]
+ super
+ end
- def initialize(options)
- super
+ def initialize(options)
+ super
- @typemap.update(TrueClass => 'tinyint')
+ @typemap.update(TrueClass => 'tinyint')
- @conn = Mysql.connect(
- options[:address] || 'localhost',
- options[:user],
- options[:password],
- options[:name]
- )
-
- # You should set recconect to true to avoid MySQL has
- # gone away errors.
-
- if @conn.respond_to? :reconnect
- options[:reconnect] = true unless options.has_key?(:reconnect)
- @conn.reconnect = options[:reconnect]
- end
-
- rescue => ex
- if ex.errno == 1049 # database does not exist.
- Logger.info "Database '#{options[:name]}' not found!"
- self.class.create(options)
- retry
- end
- raise
- end
+ @conn = Mysql.connect(
+ options[:address] || 'localhost',
+ options[:user],
+ options[:password],
+ options[:name]
+ )
+
+ # You should set recconect to true to avoid MySQL has
+ # gone away errors.
+
+ if @conn.respond_to? :reconnect
+ options[:reconnect] = true unless options.has_key?(:reconnect)
+ @conn.reconnect = options[:reconnect]
+ end
+
+ rescue => ex
+ if ex.errno == 1049 # database does not exist.
+ Logger.info "Database '#{options[:name]}' not found!"
+ self.class.create(options)
+ retry
+ end
+ raise
+ end
- def close
- @conn.close
- super
- end
+ def close
+ @conn.close
+ 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
- @conn.query_with_result = true
- return @conn.query(sql)
- rescue => ex
- handle_sql_exception(ex, sql)
- 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
+ @conn.query_with_result = true
+ return @conn.query(sql)
+ rescue => ex
+ handle_sql_exception(ex, sql)
+ end
- def exec(sql)
- Logger.debug sql if $DBG
- @conn.query_with_result = false
- @conn.query(sql)
- rescue => ex
- handle_sql_exception(ex, sql)
- end
+ def exec(sql)
+ Logger.debug sql if $DBG
+ @conn.query_with_result = false
+ @conn.query(sql)
+ rescue => ex
+ handle_sql_exception(ex, sql)
+ end
- def start
- # nop
- # FIXME: InnoDB supports transactions.
- end
-
- # Commit a transaction.
-
- def commit
- # nop, not supported?
- # FIXME: InnoDB supports transactions.
- end
-
- # Rollback a transaction.
-
- def rollback
- # nop, not supported?
- # FIXME: InnoDB supports transactions.
- end
+ def start
+ # nop
+ # FIXME: InnoDB supports transactions.
+ end
+
+ # Commit a transaction.
+
+ def commit
+ # nop, not supported?
+ # FIXME: InnoDB supports transactions.
+ end
+
+ # Rollback a transaction.
+
+ def rollback
+ # nop, not supported?
+ # FIXME: InnoDB supports transactions.
+ end
- def sql_update(sql)
- exec(sql)
- @conn.affected_rows
- end
+ def sql_update(sql)
+ exec(sql)
+ @conn.affected_rows
+ end
private
- def create_table(klass)
- fields = fields_for_class(klass)
+ 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
-
- if table_type = @options[:table_type]
- sql << ") TYPE = #{table_type};"
- else
- sql << ");"
- end
-
- # 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
+ sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
+
+ # Create table constrains.
+
+ if klass.__meta and constrains = klass.__meta[:sql_constrain]
+ sql << ", #{constrains.join(', ')}"
+ end
+
+ if table_type = @options[:table_type]
+ sql << ") TYPE = #{table_type};"
+ else
+ sql << ");"
+ end
+
+ # 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
- if ex.errno == 1050 # table already exists.
- Logger.debug 'Table already exists' if $DBG
- return
- else
- raise
- end
- end
-
- # Create join tables if needed. Join tables are used in
- # 'many_to_many' relations.
-
- if klass.__meta and join_tables = klass.__meta[:join_tables]
- for join_table in join_tables
- begin
- @conn.query("CREATE TABLE #{join_table} (key1 integer NOT NULL, key2 integer NOT NULL)")
- @conn.query("CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)")
- @conn.query("CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)")
- rescue => ex
- if ex.errno == 1050 # table already exists.
- Logger.debug 'Join table already exists'
- else
- raise
- end
- end
- end
- end
- end
+ begin
+ @conn.query(sql)
+ Logger.info "Created table '#{klass::OGTABLE}'."
+ rescue => ex
+ if ex.errno == 1050 # table already exists.
+ Logger.debug 'Table already exists' if $DBG
+ return
+ else
+ raise
+ end
+ end
+
+ # Create join tables if needed. Join tables are used in
+ # 'many_to_many' relations.
+
+ if klass.__meta and join_tables = klass.__meta[:join_tables]
+ for info in join_tables
+ begin
+ create_join_table_sql(info).each do |sql|
+ @conn.query sql
+ end
+ rescue => ex
+ if ex.respond_to?(:errno) and ex.errno == 1050 # table already exists.
+ Logger.debug 'Join table already exists' if $DBG
+ else
+ raise
+ 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 = {}
+ 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
+ res.num_fields.times do |i|
+ map[res.fetch_field.name.intern] = i
+ end
- return map
- ensure
- res.close if res
- 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 "#{self.class}.parse_int(res[#{col} + offset])"
- elsif p.klass.ancestors.include?(Float)
- return "#{self.class}.parse_float(res[#{col} + offset])"
- 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 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 "#{self.class}.parse_int(res[#{col} + offset])"
+ elsif p.klass.ancestors.include?(Float)
+ return "#{self.class}.parse_float(res[#{col} + offset])"
+ 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.dup
- values = props.collect { |p| write_prop(p) }.join(',')
-
- if klass.metadata.superclass or klass.metadata.subclasses
- props << Property.new(:ogtype, String)
- values << ", '#{klass}'"
- end
-
- sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| p.symbol.to_s}.join(',')}) VALUES (#{values})"
+ def eval_og_insert(klass)
+ props = klass.properties.dup
+ values = props.collect { |p| write_prop(p) }.join(',')
+
+ if klass.metadata.superclass or klass.metadata.subclasses
+ props << Property.new(:ogtype, String)
+ values << ", '#{klass}'"
+ end
+
+ 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
+ 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