From fdea8d30c0859af78d1994995b6177ff00a375c7 Mon Sep 17 00:00:00 2001 From: Jean-Maxime Date: Mon, 21 Dec 2020 15:04:11 +0100 Subject: [PATCH 1/5] Set consistency to N1P_CONSISTENCY_REQUEST Signed-off-by: Jean-Maxime --- lib/libcouchbase/query_n1ql.rb | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) 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 From 08ae5b185dd1a9ec59792df95944b93b258d0204 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 2 Jun 2021 18:57:08 +0200 Subject: [PATCH 2/5] add string word and add error N1QL query syntax --- .gitignore | 2 + lib/libcouchbase/connection.rb | 34 ++-- lib/libcouchbase/n1ql.rb | 10 ++ spec/n1ql_spec.rb | 316 +++++++++++++++++++-------------- 4 files changed, 219 insertions(+), 143 deletions(-) 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/connection.rb b/lib/libcouchbase/connection.rb index 7c9e5b6..4fe6d16 100644 --- a/lib/libcouchbase/connection.rb +++ b/lib/libcouchbase/connection.rb @@ -16,7 +16,7 @@ rescue => e end end - sleep 2 if connections.length > 0 + sleep 2 if connections.length.positive? connections.each { |c| c.reactor.stop } end end @@ -321,10 +321,10 @@ def get(key, defer: nil, lock: false, cas: nil, **opts) # exptime == the lock expire time if lock time = lock == true ? 30 : lock.to_i - time = 30 if time > 30 || time < 0 + time = 30 if time > 30 || time.negative? # We only want to lock if time is between 1 and 30 - if time > 0 + if time.positive? cmd[:exptime] = time cmd[:lock] = 1 end @@ -562,7 +562,6 @@ def parse_document(raw_string) val end - private @@ -731,11 +730,11 @@ def subdoc_common(resp, req, cb) loop do check = Ext.sdresult_next(resp, cur_res, iterval) - break if check == 0 + break if check.zero? if cur_res[:status] == :success count = cur_res[:nvalue] - if count > 0 + if count.positive? result = cur_res[:value].read_string(count) else result = true # success response @@ -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]).positive? if is_single values = values.first elsif values.empty? # multiple mutate arrays should return true (same as a single mutate) @@ -821,7 +820,7 @@ def viewquery_callback(handle, type, row) view = @requests[row_data[:cookie].address] if row_data[:rc] == :success - if (row_data[:rflags] & Ext::RESPFLAGS[:resp_f_final]) > 0 + if (row_data[:rflags] & Ext::RESPFLAGS[:resp_f_final]).positive? # We can assume this is JSON view.received_final(JSON.parse(row_data[:value].read_string(row_data[:nvalue]), DECODE_OPTIONS)) else @@ -855,9 +854,9 @@ 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 + if (row_data[:rflags] & Ext::RESPFLAGS[:resp_f_final]).positive? # We can assume this is JSON view.received_final(value) else @@ -867,16 +866,27 @@ 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]).positive? + 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 + if (http_resp[:nbody]).positive? http_resp[:body].read_string(http_resp[:nbody]) else '' diff --git a/lib/libcouchbase/n1ql.rb b/lib/libcouchbase/n1ql.rb index d7017cd..36e98f5 100644 --- a/lib/libcouchbase/n1ql.rb +++ b/lib/libcouchbase/n1ql.rb @@ -23,6 +23,14 @@ def initialize(bucket, explain: false, **options) attr_accessor *Ordering attr_accessor :explain attr_reader :bucket, :connection + attr_accessor :string + + def string(val = nil) + return @string if val.nil? + + @string = val.to_s + self + end def explain(val = nil) return @explain if val.nil? @@ -45,6 +53,8 @@ def explain(val = nil) def to_s res = String.new res << "EXPLAIN\n" if @explain + return (res << @string) if @string + Ordering.each do |statement| val = public_send statement unless val.nil? 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 From 982cb7022bf8340673ae5bad17bec95a9121679f Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 12 Jul 2021 15:29:31 +0200 Subject: [PATCH 3/5] review --- lib/libcouchbase/connection.rb | 20 ++++++++++---------- lib/libcouchbase/n1ql.rb | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/libcouchbase/connection.rb b/lib/libcouchbase/connection.rb index 4fe6d16..7dc965a 100644 --- a/lib/libcouchbase/connection.rb +++ b/lib/libcouchbase/connection.rb @@ -16,7 +16,7 @@ rescue => e end end - sleep 2 if connections.length.positive? + sleep 2 if connections.length > 0 connections.each { |c| c.reactor.stop } end end @@ -321,10 +321,10 @@ def get(key, defer: nil, lock: false, cas: nil, **opts) # exptime == the lock expire time if lock time = lock == true ? 30 : lock.to_i - time = 30 if time > 30 || time.negative? + time = 30 if time > 30 || time < 0 # We only want to lock if time is between 1 and 30 - if time.positive? + if time > 0 cmd[:exptime] = time cmd[:lock] = 1 end @@ -730,11 +730,11 @@ def subdoc_common(resp, req, cb) loop do check = Ext.sdresult_next(resp, cur_res, iterval) - break if check.zero? + break if check == 0 if cur_res[:status] == :success count = cur_res[:nvalue] - if count.positive? + if count > 0 result = cur_res[:value].read_string(count) else result = true # success response @@ -751,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]).positive? + 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) @@ -820,7 +820,7 @@ def viewquery_callback(handle, type, row) view = @requests[row_data[:cookie].address] if row_data[:rc] == :success - if (row_data[:rflags] & Ext::RESPFLAGS[:resp_f_final]).positive? + if (row_data[:rflags] & Ext::RESPFLAGS[:resp_f_final]) > 0 # We can assume this is JSON view.received_final(JSON.parse(row_data[:value].read_string(row_data[:nvalue]), DECODE_OPTIONS)) else @@ -856,7 +856,7 @@ def query_callback_common(row_data) if row_data[:rc] == :success value = JSON.parse(row_text(row_data), DECODE_OPTIONS) - if (row_data[:rflags] & Ext::RESPFLAGS[:resp_f_final]).positive? + if (row_data[:rflags] & Ext::RESPFLAGS[:resp_f_final]) > 0 # We can assume this is JSON view.received_final(value) else @@ -877,7 +877,7 @@ def query_callback_common(row_data) # Extracts the row content of a response def row_text(row_data) - if (row_data[:nrow]).positive? + if row_data[:nrow] > 0 row_data[:row].read_string(row_data[:nrow]) else '' @@ -886,7 +886,7 @@ def row_text(row_data) # Extracts the body content of a HTTP response def body_text(http_resp) - if (http_resp[:nbody]).positive? + if http_resp[:nbody] > 0 http_resp[:body].read_string(http_resp[:nbody]) else '' diff --git a/lib/libcouchbase/n1ql.rb b/lib/libcouchbase/n1ql.rb index 36e98f5..fbca590 100644 --- a/lib/libcouchbase/n1ql.rb +++ b/lib/libcouchbase/n1ql.rb @@ -25,7 +25,7 @@ def initialize(bucket, explain: false, **options) attr_reader :bucket, :connection attr_accessor :string - def string(val = nil) + def query(val = nil) return @string if val.nil? @string = val.to_s From 7d0283135faa5366af01e13bb6f99ec70db76139 Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 12 Jul 2021 15:35:55 +0200 Subject: [PATCH 4/5] review --- lib/libcouchbase/n1ql.rb | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/libcouchbase/n1ql.rb b/lib/libcouchbase/n1ql.rb index fbca590..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) @@ -23,14 +23,6 @@ def initialize(bucket, explain: false, **options) attr_accessor *Ordering attr_accessor :explain attr_reader :bucket, :connection - attr_accessor :string - - def query(val = nil) - return @string if val.nil? - - @string = val.to_s - self - end def explain(val = nil) return @explain if val.nil? @@ -53,7 +45,7 @@ def explain(val = nil) def to_s res = String.new res << "EXPLAIN\n" if @explain - return (res << @string) if @string + return (res << @query) if @query Ordering.each do |statement| val = public_send statement From e63cfc512ab58ac82982a3a1c4f872acaf21d8cd Mon Sep 17 00:00:00 2001 From: Gauthier Monserand Date: Tue, 21 Jun 2022 15:35:05 +0200 Subject: [PATCH 5/5] Ruby 3 compatibility --- lib/libcouchbase/bucket.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 }