diff --git a/.gitignore b/.gitignore index e85405e..12c6492 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ Gemfile-custom *.gem *.dll + +vendor/* diff --git a/lib/libcouchbase/bucket.rb b/lib/libcouchbase/bucket.rb index 6177073..f8b582a 100644 --- a/lib/libcouchbase/bucket.rb +++ b/lib/libcouchbase/bucket.rb @@ -474,7 +474,7 @@ def subdoc(key, quiet: @quiet, **opts) end def subdoc_execute!(sd, extended: false, async: false, **opts) - promise = @connection.subdoc(sd, opts).then { |resp| + promise = @connection.subdoc(sd, **opts).then { |resp| raise resp.value if resp.value.is_a?(::Exception) extended ? resp : resp.value } diff --git a/lib/libcouchbase/connection.rb b/lib/libcouchbase/connection.rb index 7c9e5b6..7dc965a 100644 --- a/lib/libcouchbase/connection.rb +++ b/lib/libcouchbase/connection.rb @@ -562,7 +562,6 @@ def parse_document(raw_string) val end - private @@ -752,7 +751,7 @@ def subdoc_common(resp, req, cb) end # Return the single result instead of an array if single - is_single = resp[:rflags] & Ext::RESPFLAGS[:resp_f_sdsingle] > 0 + is_single = (resp[:rflags] & Ext::RESPFLAGS[:resp_f_sdsingle]) > 0 if is_single values = values.first elsif values.empty? # multiple mutate arrays should return true (same as a single mutate) @@ -855,7 +854,7 @@ def query_callback_common(row_data) view = @requests[row_data[:cookie].address] if row_data[:rc] == :success - value = JSON.parse(row_data[:row].read_string(row_data[:nrow]), DECODE_OPTIONS) + value = JSON.parse(row_text(row_data), DECODE_OPTIONS) if (row_data[:rflags] & Ext::RESPFLAGS[:resp_f_final]) > 0 # We can assume this is JSON @@ -867,13 +866,24 @@ def query_callback_common(row_data) error_klass = Error.lookup(row_data[:rc]) if error_klass == Error::HttpError http_resp = row_data[:htresp] - view.error error_klass.new(body_text(http_resp)) + body_text = body_text(http_resp) + body_text = row_text(row_data) if body_text.empty? + view.error error_klass.new(body_text) else view.error error_klass.new end end end + # Extracts the row content of a response + def row_text(row_data) + if row_data[:nrow] > 0 + row_data[:row].read_string(row_data[:nrow]) + else + '' + end + end + # Extracts the body content of a HTTP response def body_text(http_resp) if http_resp[:nbody] > 0 diff --git a/lib/libcouchbase/n1ql.rb b/lib/libcouchbase/n1ql.rb index d7017cd..b10e4bc 100644 --- a/lib/libcouchbase/n1ql.rb +++ b/lib/libcouchbase/n1ql.rb @@ -6,7 +6,7 @@ class N1QL :build_index, :create_index, :drop_index, :create_primary_index, :drop_primary_index, :grant, :on, :to, :infer, :select, :insert_into, :delete_from, :update, :from, :with, :use_keys, :unnest, :join, :where, - :group_by, :order_by, :limit, :offset, :upsert_into, :merge_into + :group_by, :order_by, :limit, :offset, :upsert_into, :merge_into, :query ] def initialize(bucket, explain: false, **options) @@ -45,6 +45,8 @@ def explain(val = nil) def to_s res = String.new res << "EXPLAIN\n" if @explain + return (res << @query) if @query + Ordering.each do |statement| val = public_send statement unless val.nil? diff --git a/lib/libcouchbase/query_n1ql.rb b/lib/libcouchbase/query_n1ql.rb index eebe694..1bb3b91 100644 --- a/lib/libcouchbase/query_n1ql.rb +++ b/lib/libcouchbase/query_n1ql.rb @@ -3,7 +3,7 @@ module Libcouchbase class QueryN1QL N1P_QUERY_STATEMENT = 1 - + N1P_CONSISTENCY_REQUEST = 2 def initialize(connection, reactor, n1ql, **opts) @connection = connection @@ -13,10 +13,8 @@ def initialize(connection, reactor, n1ql, **opts) @request_handle = FFI::MemoryPointer.new :pointer, 1 end - attr_reader :connection, :n1ql - def get_count(metadata) metadata[:metrics][:resultCount] end @@ -46,27 +44,31 @@ def perform(limit: nil, **options, &blk) @cmd = Ext::CMDN1QL.new @params = Ext.n1p_new - err = Ext.n1p_setquery(@params, @query_text, @query_text.bytesize, N1P_QUERY_STATEMENT) + err = Ext.n1p_setconsistency(@params, N1P_CONSISTENCY_REQUEST) if err == :success - - err = Ext.n1p_mkcmd(@params, @cmd) + err = Ext.n1p_setquery(@params, @query_text, @query_text.bytesize, N1P_QUERY_STATEMENT) if err == :success - pointer = @cmd.to_ptr - @connection.requests[pointer.address] = self + err = Ext.n1p_mkcmd(@params, @cmd) + if err == :success + pointer = @cmd.to_ptr + @connection.requests[pointer.address] = self - @cmd[:callback] = @connection.get_callback(:n1ql_callback) - @cmd[:handle] = @request_handle + @cmd[:callback] = @connection.get_callback(:n1ql_callback) + @cmd[:handle] = @request_handle - err = Ext.n1ql_query(@connection.handle, pointer, @cmd) - if err != :success + err = Ext.n1ql_query(@connection.handle, pointer, @cmd) + if err != :success error(Error.lookup(err).new('full text search not scheduled')) + end + else + error(Error.lookup(err).new('failed to build full text search command')) end else - error(Error.lookup(err).new('failed to build full text search command')) + error(Error.lookup(err).new('failed to build full text search query structure')) end else - error(Error.lookup(err).new('failed to build full text search query structure')) + error(Error.lookup(err).new('failed set consistency value')) end } end diff --git a/spec/n1ql_spec.rb b/spec/n1ql_spec.rb index 11e4c9b..62faceb 100644 --- a/spec/n1ql_spec.rb +++ b/spec/n1ql_spec.rb @@ -2,7 +2,6 @@ require 'libcouchbase' - describe Libcouchbase::N1QL, n1ql_query: true do before :each do @bucket = Libcouchbase::Bucket.new @@ -20,187 +19,242 @@ expect(@n1ql.to_s).to eq("SELECT *\nFROM default\nWHERE port == 10001\n") end - describe 'perform native queries' do - before :each do - @n1ql.select('*').from(:default).where('type == "mod"') - end - - it "should iterate results" do - results = @n1ql.results - @log << results.to_a.count - @log << results.count - @log << results.collect { |res| res.nil? } - expect(@log).to eq([12, 12, - [false, false, false, false, false, - false, false, false, false, false, - false, false - ]]) - end - - it "should cancel iteration when an error occurs" do - results = @n1ql.results - begin - count = 0 - results.collect { |res| - raise 'err' if count > 0 - @log << res.nil? - count += 1 - } - rescue => e - @log << :error - end - @log << results.count - expect(@log).to eq([false, :error, 12]) - end - - it "should cancel iteration when an error occurs in row modifer" do - count = 0 - results = @n1ql.results do |row| - raise 'err' if count > 0 - count += 1 - row - end - - begin - count = 0 - results.collect { |res| - @log << res.nil? - } - rescue => e - @log << e.message - end - expect(@log).to eq([false, 'err']) - end + it "should build a basic query from string" do + @n1ql.string("SELECT *\nFROM default\nWHERE port == 10001\n") + expect(@n1ql.to_s).to eq("SELECT *\nFROM default\nWHERE port == 10001\n") end - describe 'perform queries in libuv reactor' do - before :each do - @n1ql.select('*').from(:default).where('type == "mod"') - @reactor = ::Libuv::Reactor.default - end + describe 'perform native queries' do + context "without error syntax query" do + before :each do + @n1ql.select('*').from(:default).where('type == "mod"') + end - it "should iterate results" do - @reactor.run { |reactor| + it "should iterate results" do results = @n1ql.results @log << results.to_a.count @log << results.count @log << results.collect { |res| res.nil? } - } - - expect(@log).to eq([12, 12, - [false, false, false, false, false, - false, false, false, false, false, - false, false - ]] - ) - end + expect(@log).to eq([12, 12, + [false, false, false, false, false, + false, false, false, false, false, + false, false + ]]) + end - it "should cancel iteration when an error occurs" do - @reactor.run { |reactor| + it "should cancel iteration when an error occurs" do results = @n1ql.results begin count = 0 - results.collect { |res| + results.collect do |res| raise 'err' if count > 0 + @log << res.nil? count += 1 - } + end rescue => e @log << :error end @log << results.count - } - expect(@log).to eq([false, :error, 12]) - end + expect(@log).to eq([false, :error, 12]) + end - it "should cancel iteration when an error occurs in row modifer" do - @reactor.run { |reactor| + it "should cancel iteration when an error occurs in row modifer" do count = 0 results = @n1ql.results do |row| raise 'err' if count > 0 + count += 1 row end begin count = 0 - results.collect { |res| + results.collect do |res| @log << res.nil? - } + end rescue => e @log << e.message end - } - expect(@log).to eq([false, 'err']) + expect(@log).to eq([false, 'err']) + end + end + context "with error syntax query" do + before :each do + @n1ql.select('*azdazdazdazdza').from(:default).where('type == "mod"') + end + it "should cancel iteration" do + results = @n1ql.results + expect { results.to_a }.to(raise_error do |error| + expect(error).to be_a(Libcouchbase::Error::HttpError) + expect(error.message).not_to be_empty + end) + end end end - describe 'perform queries in event machine' do - require 'em-synchrony' + describe 'perform queries in libuv reactor' do + context "without error syntax query" do + before :each do + @n1ql.select('*').from(:default).where('type == "mod"') + @reactor = ::Libuv::Reactor.default + end - before :each do - @n1ql.select('*').from(:default).where('type == "mod"') - end + it "should iterate results" do + @reactor.run do |reactor| + results = @n1ql.results + @log << results.to_a.count + @log << results.count + @log << results.collect { |res| res.nil? } + end - it "should iterate results" do - EM.synchrony { - results = @n1ql.results - @log << results.to_a.count - @log << results.count - @log << results.collect { |res| res.nil? } + expect(@log).to eq([12, 12, + [false, false, false, false, false, + false, false, false, false, false, + false, false + ]] + ) + end - EM.stop - } + it "should cancel iteration when an error occurs" do + @reactor.run do |reactor| + results = @n1ql.results + begin + count = 0 + results.collect do |res| + raise 'err' if count > 0 - expect(@log).to eq([12, 12, - [false, false, false, false, false, - false, false, false, false, false, - false, false - ]] - ) - end + @log << res.nil? + count += 1 + end + rescue => e + @log << :error + end + @log << results.count + end + expect(@log).to eq([false, :error, 12]) + end - it "should cancel iteration when an error occurs" do - EM.synchrony { - results = @n1ql.results - begin + it "should cancel iteration when an error occurs in row modifer" do + @reactor.run do |reactor| count = 0 - results.collect { |res| + results = @n1ql.results do |row| raise 'err' if count > 0 - @log << res.nil? + count += 1 - } - rescue => e - @log << :error - end - @log << results.count + row + end - EM.stop - } - expect(@log).to eq([false, :error, 12]) + begin + count = 0 + results.collect do |res| + @log << res.nil? + end + rescue => e + @log << e.message + end + end + expect(@log).to eq([false, 'err']) + end + end + context "with error syntax query" do + before :each do + @n1ql.select('*azdzadazdzadza').from(:default).where('type == "mod"') + @reactor = ::Libuv::Reactor.default + end + it "should cancel iteration" do + results = @n1ql.results + expect { results.to_a }.to(raise_error do |error| + expect(error).to be_a(Libcouchbase::Error::HttpError) + expect(error.message).not_to be_empty + end) + end end + end - it "should cancel iteration when an error occurs in row modifer" do - EM.synchrony { - count = 0 - results = @n1ql.results do |row| - raise 'err' if count > 0 - count += 1 - row + describe 'perform queries in event machine' do + require 'em-synchrony' + + context "without error syntax query" do + before :each do + @n1ql.select('*').from(:default).where('type == "mod"') + end + + it "should iterate results" do + EM.synchrony do + results = @n1ql.results + @log << results.to_a.count + @log << results.count + @log << results.collect { |res| res.nil? } + + EM.stop end - begin + expect(@log).to eq([12, 12, + [false, false, false, false, false, + false, false, false, false, false, + false, false + ]] + ) + end + + it "should cancel iteration when an error occurs" do + EM.synchrony do + results = @n1ql.results + begin + count = 0 + results.collect do |res| + raise 'err' if count > 0 + + @log << res.nil? + count += 1 + end + rescue => e + @log << :error + end + @log << results.count + + EM.stop + end + expect(@log).to eq([false, :error, 12]) + end + + it "should cancel iteration when an error occurs in row modifer" do + EM.synchrony do count = 0 - results.collect { |res| - @log << res.nil? - } - rescue => e - @log << e.message + results = @n1ql.results do |row| + raise 'err' if count > 0 + + count += 1 + row + end + + begin + count = 0 + results.collect do |res| + @log << res.nil? + end + rescue => e + @log << e.message + end + + EM.stop end + expect(@log).to eq([false, 'err']) + end + end - EM.stop - } - expect(@log).to eq([false, 'err']) + context "with error syntax query" do + before :each do + @n1ql.select('*azdzadazdzadza').from(:default).where('type == "mod"') + end + it "should cancel iteration" do + results = @n1ql.results + expect { results.to_a }.to(raise_error do |error| + expect(error).to be_a(Libcouchbase::Error::HttpError) + expect(error.message).not_to be_empty + end) + end end end end