Class: Exwiw::Adapter::Mysql2Adapter
- Defined in:
- lib/exwiw/adapter/mysql2_adapter.rb
Instance Attribute Summary
Attributes inherited from Base
Instance Method Summary collapse
- #build_query(table, dump_target, table_by_name) ⇒ Object
- #compile_ast(query_ast) ⇒ Object
- #dump_schema(ordered_tables, output_path) ⇒ Object
- #execute(query_ast) ⇒ Object
- #explain(query_ast) ⇒ Object
- #to_bulk_delete(select_query_ast, table) ⇒ Object
- #to_bulk_insert(results, table) ⇒ Object
Methods inherited from Base
#dumpable?, #initialize, #output_extension, #post_insert_sql, #schema_output_extension, #supports_bulk_delete?, table_config_class, #to_copy_from_stdin, #validate_as_dump_target!
Constructor Details
This class inherits a constructor from Exwiw::Adapter::Base
Instance Method Details
#build_query(table, dump_target, table_by_name) ⇒ Object
6 7 8 |
# File 'lib/exwiw/adapter/mysql2_adapter.rb', line 6 def build_query(table, dump_target, table_by_name) Exwiw::QueryAstBuilder.run(table.name, table_by_name, dump_target, @logger) end |
#compile_ast(query_ast) ⇒ Object
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/exwiw/adapter/mysql2_adapter.rb', line 140 def compile_ast(query_ast) raise NotImplementedError unless query_ast.is_a?(Exwiw::QueryAst::Select) sql = "SELECT " sql += query_ast.columns.map { |col| compile_column_name(query_ast, col) }.join(', ') sql += " FROM #{query_ast.from_table_name}" query_ast.join_clauses.each do |join| sql += " JOIN #{join.join_table_name} ON #{join.base_table_name}.#{join.foreign_key} = #{join.join_table_name}.#{join.primary_key}" join.where_clauses.each do |where| compiled_where_condition = compile_where_condition(where, join.join_table_name) sql += " AND #{compiled_where_condition}" end end if query_ast.where_clauses.any? sql += " WHERE " sql += query_ast.where_clauses.map { |where| compile_where_condition(where, query_ast.from_table_name) }.join(' AND ') end sql end |
#dump_schema(ordered_tables, output_path) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/exwiw/adapter/mysql2_adapter.rb', line 28 def dump_schema(ordered_tables, output_path) require 'open3' table_names = ordered_tables.map(&:name) if table_names.empty? File.write(output_path, "-- Auto-generated by exwiw. No tables in scope.\n") return end cmd = [ 'mysqldump', "--host=#{@connection_config.host}", "--port=#{@connection_config.port}", "--user=#{@connection_config.user}", '--no-data', '--skip-add-drop-table', # `--skip-comments` only suppresses the dump's header lines # (e.g. `-- MySQL dump ...`, server version banner). Column and # table `COMMENT '...'` clauses are emitted inline inside # CREATE TABLE statements and are NOT affected, so this flag is # purely about reducing noise in the generated file. '--skip-comments', '--skip-set-charset', # Suppress `SET @@GLOBAL.GTID_PURGED=...` from the dump. It is intended # for replication setup and breaks when the target already has GTIDs # (ERROR 3546: added gtid set must not overlap with @@GLOBAL.GTID_EXECUTED). '--set-gtid-purged=OFF', '--compact', @connection_config.database_name, *table_names, ] env = { 'MYSQL_PWD' => @connection_config.password.to_s } @logger.debug(" Running mysqldump for #{table_names.size} table(s)...") stdout, stderr, status = Open3.capture3(env, *cmd) unless status.success? if stderr.include?('command not found') || stderr.empty? raise "Failed to run `mysqldump`. Ensure the mysql client is installed and on PATH. stderr: #{stderr}" end raise "mysqldump failed (exit #{status.exitstatus}): #{stderr}" end idempotent = DdlPostprocessor.add_if_not_exists_to_create_table(stdout) File.open(output_path, 'w') do |file| file.puts("-- Auto-generated by exwiw via mysqldump. Idempotent CREATE TABLE statements for mysql.") file.write(idempotent) end @logger.info(" Wrote schema for #{table_names.size} table(s) to #{output_path}.") end |
#execute(query_ast) ⇒ Object
10 11 12 13 14 15 |
# File 'lib/exwiw/adapter/mysql2_adapter.rb', line 10 def execute(query_ast) sql = compile_ast(query_ast) @logger.debug(" Executing SQL: \n#{sql}") connection.query(sql, cast: false, as: :array).to_a end |
#explain(query_ast) ⇒ Object
17 18 19 20 21 22 23 24 25 26 |
# File 'lib/exwiw/adapter/mysql2_adapter.rb', line 17 def explain(query_ast) sql = compile_ast(query_ast) @logger.debug(" Executing EXPLAIN: \n#{sql}") rows = connection.query("EXPLAIN #{sql}", cast: false).to_a rows.each_with_index.flat_map do |row, i| ["*************************** #{i + 1}. row ***************************"] + row.map { |k, v| "#{k}: #{v}" } end.join("\n") end |
#to_bulk_delete(select_query_ast, table) ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/exwiw/adapter/mysql2_adapter.rb', line 94 def to_bulk_delete(select_query_ast, table) raise NotImplementedError unless select_query_ast.is_a?(Exwiw::QueryAst::Select) sql = "DELETE FROM #{select_query_ast.from_table_name}" if select_query_ast.join_clauses.empty? # Ignore filter option, because bulk delete is for cleaning before import, # so it should delete all records to avoid foreign key violation & data consistancy. compiled_where_conditions = select_query_ast. where_clauses. select { |where| where.is_a?(Exwiw::QueryAst::WhereClause) }. map do |where| compile_where_condition(where, select_query_ast.from_table_name) end if compiled_where_conditions.size > 0 sql += "\nWHERE " sql += compiled_where_conditions.join(' AND ') end sql += ";" return sql end subquery_ast = Exwiw::QueryAst::Select.new first_join = select_query_ast.join_clauses.first.clone subquery_ast.from(first_join.join_table_name) primay_key_col = table.columns.find { |col| col.name == table.primary_key } subquery_ast.select([primay_key_col]) select_query_ast.join_clauses[1..].each do |join| subquery_ast.join(join) end first_join.where_clauses.each do |where| # Ignore filter option, because bulk delete is for cleaning before import, # so it should delete all records to avoid foreign key violation & data consistancy. subquery_ast.where(where) if where.is_a?(Exwiw::QueryAst::WhereClause) end foreign_key = first_join.foreign_key subquery_sql = compile_ast(subquery_ast) sql += "\nWHERE #{select_query_ast.from_table_name}.#{foreign_key} IN (#{subquery_sql});" sql end |
#to_bulk_insert(results, table) ⇒ Object
79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/exwiw/adapter/mysql2_adapter.rb', line 79 def to_bulk_insert(results, table) table_name = table.name value_list = results.map do |row| quoted_values = row.map do |value| escape_value(value) end "(" + quoted_values.join(', ') + ")" end values = value_list.join(",\n") column_names = table.columns.map(&:name).join(', ') "INSERT INTO #{table_name} (#{column_names}) VALUES\n#{values};" end |