1 require "chef/mixin/command"
 
   2 require "rexml/document"
 
   6     include Chef::Mixin::Command
 
   9       :select, :insert, :update, :delete, :create, :drop, :reload,
 
  10       :shutdown, :process, :file, :grant, :references, :index, :alter,
 
  11       :show_db, :super, :create_tmp_table, :lock_tables, :execute,
 
  12       :repl_slave, :repl_client, :create_view, :show_view, :create_routine,
 
  13       :alter_routine, :create_user, :event, :trigger, :create_tablespace
 
  16     DATABASE_PRIVILEGES = [
 
  17       :select, :insert, :update, :delete, :create, :drop, :grant,
 
  18       :references, :index, :alter, :create_tmp_table, :lock_tables,
 
  19       :create_view, :show_view, :create_routine, :alter_routine,
 
  20       :execute, :event, :trigger
 
  24       # Create argument array
 
  27       # Work out how to authenticate
 
  29         args.push("--username=#{options[:user]}")
 
  30         args.push("--password=#{options[:password]}") if options[:password]
 
  32         args.push("--defaults-file=/etc/mysql/debian.cnf")
 
  35       # Build the other arguments
 
  36       args.push("--execute=\"#{options[:command]}\"") if options[:command]
 
  38       # Get the database to use
 
  39       database = options[:database] || "mysql"
 
  41       # Build the command to run
 
  42       command = "/usr/bin/mysql #{args.join(' ')} #{database}"
 
  44       # Escape backticks in the command
 
  45       command.gsub!(/`/, "\\\\`")
 
  48       run_command(:command => command, :user => "root", :group => "root")
 
  51     def query(sql, options = {})
 
  52       # Get the database to use
 
  53       database = options[:database] || "mysql"
 
  55       # Construct the command string
 
  56       command = "/usr/bin/mysql --defaults-file=/etc/mysql/debian.cnf --xml --execute='#{sql}' #{database}"
 
  59       status, stdout, stderr = output_of_command(command, :user => "root", :group => "root")
 
  60       handle_command_failures(status, "STDOUT: #{stdout}\nSTDERR: #{stderr}", :output_on_failure => true)
 
  63       document = REXML::Document.new(stdout)
 
  68       # Loop over the rows in the result set
 
  69       document.root.each_element("/resultset/row") do |row|
 
  73         # Loop over the fields, adding them to the record
 
  74         row.each_element("field") do |field|
 
  75           name = field.attributes["name"].downcase
 
  78           record[name.to_sym] = value
 
  81         # Add the record to the record list
 
  85       # Return the record list
 
  90       @users ||= query("SELECT * FROM user").inject({}) do |users,user|
 
  91         name = "'#{user[:user]}'@'#{user[:host]}'"
 
  93         users[name] = USER_PRIVILEGES.inject({}) do |privileges,privilege|
 
  94           privileges[privilege] = user["#{privilege}_priv".to_sym] == "Y"
 
 103       @databases ||= query("SHOW databases").inject({}) do |databases,database|
 
 104         databases[database[:database]] = {
 
 110       query("SELECT * FROM db").each do |record|
 
 111         if database = @databases[record[:db]]
 
 112           user = "'#{record[:user]}'@'#{record[:host]}'"
 
 114           database[:permissions][user] = DATABASE_PRIVILEGES.inject([]) do |privileges,privilege|
 
 115             privileges << privilege if record["#{privilege}_priv".to_sym] == "Y"
 
 124     def canonicalise_user(user)
 
 125       local, host = user.split("@")
 
 127       host = "%" unless host
 
 129       local = "'#{local}'" unless local =~ /^'.*'$/
 
 130       host = "'#{host}'" unless host =~ /^'.*'$/
 
 135     def privilege_name(privilege)
 
 139       when :create_tmp_table
 
 140         "CREATE TEMPORARY TABLES"
 
 142         privilege.to_s.upcase.tr("_", " ")