require 'csv' require 'tmpdir' require 'posix/spawn' require 'unix_utils' class DataMiner class Step # A step that executes a SQL, either from a string or as retrieved from a URL. # # Create these by calling +sql+ inside a +data_miner+ block. # # @see DataMiner::ActiveRecordClassMethods#data_miner Overview of how to define data miner scripts inside of ActiveRecord models. # @see DataMiner::Script#sql Creating a sql step by calling DataMiner::Script#sql from inside a data miner script class Sql < Step URL_DETECTOR = %r{^[^\s]*/[^\*]} # Description of what this step does. # @return [String] attr_reader :description # Location of the SQL file. # @return [String] attr_reader :url # String containing the SQL. # @return [String] attr_reader :statement # @private def initialize(script, description, url_or_statement, ignored_options = nil) @script = script @description = description if url_or_statement =~ URL_DETECTOR @url = url_or_statement else @statement = url_or_statement end end # @private def start if statement ActiveRecord::Base.connection.execute statement else tmp_path = UnixUtils.curl url send config[:adapter], tmp_path File.unlink tmp_path end end private def config if ActiveRecord::Base.respond_to?(:connection_config) ActiveRecord::Base.connection_config else ActiveRecord::Base.connection_pool.spec.config end end def mysql(path) connect = if config[:socket] [ '--socket', config[:socket] ] else [ '--host', config.fetch(:host, '127.0.0.1'), '--port', config.fetch(:port, 3306).to_s ] end argv = [ 'mysql', '--compress', '--user', config[:username], "-p#{config[:password]}", connect, '--default-character-set', 'utf8', config[:database] ].flatten File.open(path) do |f| pid = POSIX::Spawn.spawn(*(argv+[{:in => f}])) ::Process.waitpid pid end unless $?.success? raise RuntimeError, "[data_miner] Failed: ARGV #{argv.join(' ').inspect}" end nil end alias :mysql2 :mysql def postgresql(path) env = {} env['PGHOST'] = config['host'] if config['host'] env['PGPORT'] = config['port'].to_s if config['port'] env['PGPASSWORD'] = config['password'].to_s if config['password'] env['PGUSER'] = config['username'].to_s if config['username'] argv = [ 'psql', '--quiet', '--dbname', config[:database], '--file', path ].flatten child = POSIX::Spawn::Child.new(*([env]+argv)) $stderr.puts child.out $stderr.puts child.err unless child.success? raise RuntimeError, "[data_miner] Failed: ENV #{env.inspect} ARGV #{argv.join(' ').inspect} (#{child.err.inspect})" end nil end def sqlite3(path) argv = [ 'sqlite3', config[:database] ] File.open(path) do |f| pid = POSIX::Spawn.spawn(*(argv+[{:in => f}])) ::Process.waitpid pid end unless $?.success? raise RuntimeError, %{[data_miner] Failed: "cat #{path} | #{argv.join(' ')}"} end nil end end end end