diff --git a/Gemfile b/Gemfile index 4f946f1..6b41abb 100644 --- a/Gemfile +++ b/Gemfile @@ -5,4 +5,5 @@ source 'http://rubygems.org' gemspec gem 'mysql2', '~> 0.5.2', :platform => :ruby +gem 'trilogy', '~> 2.12.0', :platform => :ruby gem 'jdbc-mysql', '~> 5.1.47', :platform => :jruby diff --git a/lib/riddle.rb b/lib/riddle.rb index 0375155..fad5e72 100644 --- a/lib/riddle.rb +++ b/lib/riddle.rb @@ -77,6 +77,7 @@ def self.version_warning require 'riddle/controller' require 'riddle/execute_command' require 'riddle/query' +require 'riddle/sql_client' Riddle.loaded_version = nil Riddle::AutoVersion.configure diff --git a/lib/riddle/query.rb b/lib/riddle/query.rb index 02ef096..8b5b9e4 100644 --- a/lib/riddle/query.rb +++ b/lib/riddle/query.rb @@ -7,16 +7,7 @@ module Riddle::Query MYSQL2_ESCAPE = defined?(Mysql2) && defined?(Mysql2::Client) def self.connection(address = '127.0.0.1', port = 9312) - require 'mysql2' - - # If you use localhost, MySQL insists on a socket connection, but Sphinx - # requires a TCP connection. Using 127.0.0.1 fixes that. - address = '127.0.0.1' if address == 'localhost' - - Mysql2::Client.new( - :host => address, - :port => port - ) + Riddle::SQLClient.connection(address, port) end def self.meta diff --git a/lib/riddle/sql_client.rb b/lib/riddle/sql_client.rb new file mode 100644 index 0000000..5ecc76a --- /dev/null +++ b/lib/riddle/sql_client.rb @@ -0,0 +1,36 @@ +# lib/sql_client.rb + +module Riddle::SQLClient + def self.connection(address = '127.0.0.1', port = 9312, adapter: nil) + adapter ||= default_adapter + require_relative "./sql_client/#{adapter}" + + # If you use localhost, MySQL insists on a socket connection, but Sphinx + # requires a TCP connection. Using 127.0.0.1 fixes that. + address = '127.0.0.1' if address == 'localhost' + + case adapter.to_sym + when :trilogy + Riddle::SQLClient::Trilogy.new(:host => address, :port => port) + when :mysql2 + Riddle::SQLClient::Mysql2.new(:host => address, :port => port) + else + raise ArgumentError, "Unknown adapter #{adapter}" + end + end + + def self.default_adapter + return @default_adapter if @default_adapter + %w[trilogy mysql2].each do |adapter| + require adapter + @default_adapter = adapter.to_sym + return adapter + rescue LoadError + end + raise "Either Trilogy or mysql2 are required to use the sql interface" + end + + def self.default_adapter=(adapter) + @default_adapter = adapter + end +end diff --git a/lib/riddle/sql_client/mysql2.rb b/lib/riddle/sql_client/mysql2.rb new file mode 100644 index 0000000..a4cd700 --- /dev/null +++ b/lib/riddle/sql_client/mysql2.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require 'mysql2' + +module Riddle::SQLClient + class Mysql2 < ::Mysql2::Client + end +end diff --git a/lib/riddle/sql_client/trilogy.rb b/lib/riddle/sql_client/trilogy.rb new file mode 100644 index 0000000..4cc89b8 --- /dev/null +++ b/lib/riddle/sql_client/trilogy.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'trilogy' + +module Riddle::SQLClient + class Trilogy < ::Trilogy + def query(statement) + super.each_hash.to_a + end + end +end diff --git a/spec/functional/escaping_spec.rb b/spec/functional/escaping_spec.rb index 9e1c255..19a9109 100644 --- a/spec/functional/escaping_spec.rb +++ b/spec/functional/escaping_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'SphinxQL escaping', :live => true do - let(:connection) { Mysql2::Client.new :host => '127.0.0.1', :port => 9306 } + let(:connection) { Riddle::SQLClient.connection '127.0.0.1', 9306 } def sphinxql_matching(string) select = Riddle::Query::Select.new @@ -23,6 +23,7 @@ def sphinxql_matching(string) context 'on snippets' do def snippets_for(text, words = '', options = nil) snippets_query = Riddle::Query.snippets(text, 'people', words, options) + connection.query(snippets_query).first['snippet'] end diff --git a/spec/functional/merging_spec.rb b/spec/functional/merging_spec.rb index 9e32e2f..de7fc26 100644 --- a/spec/functional/merging_spec.rb +++ b/spec/functional/merging_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe "Merging indices", :live => true do - let(:connection) { Mysql2::Client.new :host => '127.0.0.1', :port => 9306 } + let(:connection) { Riddle::SQLClient.connection '127.0.0.1', 9306 } let(:path) { "spec/fixtures/sphinx/spec.conf" } let(:configuration) do Riddle::Configuration::Parser.new(File.read(path)).parse! diff --git a/spec/riddle/query_spec.rb b/spec/riddle/query_spec.rb index 459f185..0446b83 100644 --- a/spec/riddle/query_spec.rb +++ b/spec/riddle/query_spec.rb @@ -7,12 +7,11 @@ let(:connection) { Riddle::Query.connection 'localhost', 9306 } let(:controller) { Riddle::Controller.new nil, '' } - it "returns a MySQL Client" do - expect(connection).to be_a(Mysql2::Client) + it "returns a MySQL-compatible client" do + expect(connection).to be_a(Mysql2::Client).or be_a(Trilogy) end it "should handle search requests" do - if controller.sphinx_version.to_i >= 5 expect(connection.query(Riddle::Query.tables).to_a).to match_array([ {'Table' => 'people', 'Type' => 'local'}, diff --git a/spec/riddle/sql_client_spec.rb b/spec/riddle/sql_client_spec.rb new file mode 100644 index 0000000..43a5342 --- /dev/null +++ b/spec/riddle/sql_client_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Riddle::SQLClient, live: true do + describe '.connection with adapter parameter' do + it "returns a Trilogy client when adapter is :trilogy" do + connection = Riddle::SQLClient.connection 'localhost', 9306, adapter: :trilogy + expect(connection).to be_a(Trilogy) + connection.close + end + + it "returns a Mysql2 client when adapter is :mysql2" do + connection = Riddle::SQLClient.connection 'localhost', 9306, adapter: :mysql2 + expect(connection).to be_a(Mysql2::Client) + connection.close + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index eaf4f16..211d701 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -12,6 +12,11 @@ require 'riddle' +if ENV['SPHINX_SQL_ADAPTER'] + require "riddle/sql_client" + Riddle::SQLClient.default_adapter = ENV['SPHINX_SQL_ADAPTER'] +end + RSpec.configure do |config| config.include BinaryFixtures diff --git a/spec/support/sphinx.rb b/spec/support/sphinx.rb index 1969e20..d14bfd6 100644 --- a/spec/support/sphinx.rb +++ b/spec/support/sphinx.rb @@ -39,15 +39,17 @@ def setup_mysql end mysql_client.execute 'USE riddle' - structure = File.open('spec/fixtures/sql/structure.sql') { |f| f.read } + structure = File.open("#{fixtures_path}/sql/structure.sql") { |f| f.read } structure.split(/;/).each { |sql| mysql_client.execute sql } - mysql_client.execute <<-SQL - LOAD DATA LOCAL INFILE '#{fixtures_path}/sql/data.tsv' INTO TABLE - `riddle`.`people` FIELDS TERMINATED BY ',' ENCLOSED BY "'" (gender, - first_name, middle_initial, last_name, street_address, city, state, - postcode, email, birthday) - SQL + File.foreach("#{fixtures_path}/sql/data.tsv") do |l| + # being lazy about escaping here - each row in data.tsv is suitable for directly inserting + mysql_client.execute <<~SQL + INSERT INTO `riddle`.`people` (gender, first_name, middle_initial, last_name, + street_address, city, state, postcode, email, birthday) + VALUES(#{l}) + SQL + end mysql_client.close end @@ -55,6 +57,8 @@ def setup_mysql def mysql_client @mysql_client ||= if RUBY_PLATFORM == 'java' JRubyClient.new host, username, password, port + elsif ENV['SPHINX_SQL_ADAPTER'] == 'trilogy' + TrilogyClient.new host, username, password, port else MRIClient.new host, username, password, port end diff --git a/spec/support/trilogy_client.rb b/spec/support/trilogy_client.rb new file mode 100644 index 0000000..2a904cd --- /dev/null +++ b/spec/support/trilogy_client.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class TrilogyClient + def initialize(host, username, password, port) + @client = Trilogy.new( + :host => host, + :username => username, + :password => password, + :port => port + ) + end + + def query(statement) + client.query(statement).to_a.flatten + end + + def execute(statement) + client.query(statement) + end + + def close + @client.close + end + + private + + attr_reader :client +end