From 44a5dbfff2c494ddb6c152683197908b72271faf Mon Sep 17 00:00:00 2001 From: Pierre Merlin Date: Thu, 2 Jul 2026 15:02:01 +0200 Subject: [PATCH 1/8] feat: enable prepared statement plan caching via adhoc: false in n1ql queries Add adhoc: false to N1ql.config default so every n1ql-macro query opts into Couchbase server-side plan caching. The value flows through the reverse_merge in the generated method, maintaining the same override priority as scan_consistency: per-call > per-definition > N1ql.config > default. Co-Authored-By: Claude Sonnet 4.6 --- lib/couchbase-orm/n1ql.rb | 8 ++++++-- spec/n1ql_spec.rb | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/couchbase-orm/n1ql.rb b/lib/couchbase-orm/n1ql.rb index e057bd3..5150fab 100644 --- a/lib/couchbase-orm/n1ql.rb +++ b/lib/couchbase-orm/n1ql.rb @@ -23,7 +23,8 @@ def self.sanitize(value) def self.config(new_config = nil) Thread.current['__couchbaseorm_n1ql_config__'] = new_config if new_config Thread.current['__couchbaseorm_n1ql_config__'] || { - scan_consistency: DEFAULT_SCAN_CONSISTENCY + scan_consistency: DEFAULT_SCAN_CONSISTENCY, + adhoc: false } end @@ -57,7 +58,10 @@ def n1ql(name, query_fn: nil, emit_key: [], custom_order: nil, **options) @indexes[name] = method_opts singleton_class.__send__(:define_method, name) do |key: NO_VALUE, **opts, &result_modifier| - opts = options.merge(opts).reverse_merge(scan_consistency: CouchbaseOrm::N1ql.config[:scan_consistency]) + opts = options.merge(opts).reverse_merge( + scan_consistency: CouchbaseOrm::N1ql.config[:scan_consistency], + adhoc: CouchbaseOrm::N1ql.config[:adhoc] + ) values = key == NO_VALUE ? NO_VALUE : convert_values(method_opts[:emit_key], key) current_query = run_query(method_opts[:emit_key], values, query_fn, custom_order: custom_order, **opts.except(:include_docs, :key)) if result_modifier diff --git a/spec/n1ql_spec.rb b/spec/n1ql_spec.rb index b1f7430..36318e9 100644 --- a/spec/n1ql_spec.rb +++ b/spec/n1ql_spec.rb @@ -196,6 +196,25 @@ class N1QLTest < CouchbaseOrm::Base end end + it "should use adhoc: false by default for prepared statement plan caching" do + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original + N1QLTest.by_rating_reverse() + end + + it "should allow overriding adhoc per call" do + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original + N1QLTest.by_rating_reverse(adhoc: true) + end + + it "should respect N1ql.config adhoc setting" do + default_config = CouchbaseOrm::N1ql.config + CouchbaseOrm::N1ql.config({ adhoc: true }) + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original + N1QLTest.by_rating_reverse() + ensure + CouchbaseOrm::N1ql.config(default_config) + end + after(:all) do N1QLTest.delete_all end From b0c28938ab26c64836fe232839a82a9b7829bff3 Mon Sep 17 00:00:00 2001 From: Pierre Merlin Date: Thu, 2 Jul 2026 15:02:11 +0200 Subject: [PATCH 2/8] feat: enable prepared statement plan caching via adhoc: false in relation queries Read adhoc from N1ql.config in build_query_options so all Relation-based queries (.where, .all, .first, .last, .ids, update_all, delete_all, etc.) also benefit from server-side plan caching without any caller changes. Co-Authored-By: Claude Sonnet 4.6 --- lib/couchbase-orm/relation.rb | 5 ++++- spec/relation_spec.rb | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/couchbase-orm/relation.rb b/lib/couchbase-orm/relation.rb index 984b93a..173ae64 100644 --- a/lib/couchbase-orm/relation.rb +++ b/lib/couchbase-orm/relation.rb @@ -238,7 +238,10 @@ def build_update_with_params(params, **cond) end def build_query_options(positional_parameters: []) - opts = { scan_consistency: CouchbaseOrm::N1ql.config[:scan_consistency] } + opts = { + scan_consistency: CouchbaseOrm::N1ql.config[:scan_consistency], + adhoc: CouchbaseOrm::N1ql.config[:adhoc] + } opts[:positional_parameters] = positional_parameters unless positional_parameters.empty? Couchbase::Options::Query.new(**opts) end diff --git a/spec/relation_spec.rb b/spec/relation_spec.rb index f67f1fa..879f8fc 100644 --- a/spec/relation_spec.rb +++ b/spec/relation_spec.rb @@ -501,5 +501,10 @@ def self.active end end end + + it "should use adhoc: false by default for prepared statement plan caching" do + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original + RelationModel.where(active: true).ids + end end From 66bef313a2cbf55b7564800ae7347f965683679e Mon Sep 17 00:00:00 2001 From: Pierre Merlin Date: Fri, 3 Jul 2026 10:18:54 +0200 Subject: [PATCH 3/8] docs: document adhoc option and prepared statement plan caching Co-Authored-By: Claude Sonnet 4.6 --- .../07-sqlpp-queries.md | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docusaurus/docs/tutorial-ruby-couchbase-orm/07-sqlpp-queries.md b/docusaurus/docs/tutorial-ruby-couchbase-orm/07-sqlpp-queries.md index ee12175..6d198ae 100644 --- a/docusaurus/docs/tutorial-ruby-couchbase-orm/07-sqlpp-queries.md +++ b/docusaurus/docs/tutorial-ruby-couchbase-orm/07-sqlpp-queries.md @@ -147,6 +147,47 @@ docs = N1QLTest.by_custom_rating_values(key: [[1, 2]]).collect { |ob| ob.name } In the above examples, the `collect` method is used to extract the `name` attribute from each document in the result set. +## 7.8 Prepared Statement Plan Caching + +Couchbase Server can cache the query execution plan for a SQL++ query so that subsequent executions skip the planning step. CouchbaseOrm opts into this behaviour by default: every query is sent with `adhoc: false`, which tells the server to prepare and cache the plan on first execution and reuse it on subsequent ones. + +### Default behaviour + +No configuration is required. All queries — both `n1ql`-macro queries and relation queries (`where`, `all`, `first`, `last`, `ids`, `update_all`, `delete_all`) — use `adhoc: false` out of the box. + +### Disabling caching for a specific call + +Pass `adhoc: true` directly to the query method to execute without caching (useful for one-off or highly dynamic queries): + +```ruby +# Run once without caching the plan +N1QLTest.by_rating(key: 1, adhoc: true) + +# Relation query without caching +User.where(country: 'FR').adhoc(true).to_a +``` + +### Disabling caching for a specific `n1ql` definition + +Set `adhoc: true` in the macro options to always run that particular query as ad-hoc: + +```ruby +n1ql :by_dynamic_filter, emit_key: [:name], adhoc: true +``` + +### Changing the global default + +Override the thread-local config to change the default for all queries in the current thread: + +```ruby +# Disable plan caching for all queries in this thread +CouchbaseOrm::N1ql.config(adhoc: true) +``` + +### Override priority + +From highest to lowest: **per-call kwarg** > **per-`n1ql`-definition option** > **`N1ql.config`** > **default (`false`)**. + ## 7.7 Indexing for SQL++ To optimize the performance of SQL++ queries, it's important to create appropriate indexes on the fields used in the query conditions. Couchbase Server provides a way to create indexes using the Index service. From d64883a6c351cc902bb5d8b611f3a49fb6952c59 Mon Sep 17 00:00:00 2001 From: Pierre Merlin Date: Fri, 3 Jul 2026 10:29:50 +0200 Subject: [PATCH 4/8] feat: add adhoc method on relation for per-query prepared statement override Co-Authored-By: Claude Sonnet 4.6 --- lib/couchbase-orm/relation.rb | 13 +++++++++---- spec/relation_spec.rb | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/lib/couchbase-orm/relation.rb b/lib/couchbase-orm/relation.rb index 173ae64..9243752 100644 --- a/lib/couchbase-orm/relation.rb +++ b/lib/couchbase-orm/relation.rb @@ -3,7 +3,7 @@ module Relation extend ActiveSupport::Concern class CouchbaseOrm_Relation - def initialize(model:, where: where = nil, order: order = nil, limit: limit = nil, _not: _not = false, strict_loading: strict_loading = false) + def initialize(model:, where: where = nil, order: order = nil, limit: limit = nil, _not: _not = false, strict_loading: strict_loading = false, adhoc: adhoc = nil) CouchbaseOrm::logger.debug "CouchbaseOrm_Relation init: #{model} where:#{where.inspect} not:#{_not.inspect} order:#{order.inspect} limit: #{limit} strict_loading: #{strict_loading}" @model = model @limit = limit @@ -12,6 +12,7 @@ def initialize(model:, where: where = nil, order: order = nil, limit: limit = ni @order = merge_order(**order) if order @where = merge_where(where, _not) if where @strict_loading = strict_loading + @adhoc = adhoc CouchbaseOrm::logger.debug "- #{to_s}" end @@ -70,6 +71,10 @@ def strict_loading? !!@strict_loading end + def adhoc(value) + CouchbaseOrm_Relation.new(**initializer_arguments.merge(adhoc: value)) + end + def first n1ql_query, params = self.limit(1).to_n1ql_with_params result = @model.cluster.query(n1ql_query, build_query_options(positional_parameters: params)) @@ -168,7 +173,7 @@ def build_limit end def initializer_arguments - { model: @model, order: @order, where: @where, limit: @limit, strict_loading: @strict_loading } + { model: @model, order: @order, where: @where, limit: @limit, strict_loading: @strict_loading, adhoc: @adhoc } end def merge_order(*lorder, **horder) @@ -240,7 +245,7 @@ def build_update_with_params(params, **cond) def build_query_options(positional_parameters: []) opts = { scan_consistency: CouchbaseOrm::N1ql.config[:scan_consistency], - adhoc: CouchbaseOrm::N1ql.config[:adhoc] + adhoc: @adhoc.nil? ? CouchbaseOrm::N1ql.config[:adhoc] : @adhoc } opts[:positional_parameters] = positional_parameters unless positional_parameters.empty? Couchbase::Options::Query.new(**opts) @@ -264,7 +269,7 @@ def relation delegate :ids, :update_all, :delete_all, :count, :empty?, :filter, :reduce, :find_by, to: :all - delegate :where, :not, :order, :limit, :all, :strict_loading, :strict_loading?, to: :relation + delegate :where, :not, :order, :limit, :all, :strict_loading, :strict_loading?, :adhoc, to: :relation end end end diff --git a/spec/relation_spec.rb b/spec/relation_spec.rb index 879f8fc..a338a42 100644 --- a/spec/relation_spec.rb +++ b/spec/relation_spec.rb @@ -506,5 +506,40 @@ def self.active expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original RelationModel.where(active: true).ids end + + describe "adhoc option" do + it "should return a relation when calling adhoc" do + expect(RelationModel.all.adhoc(true)).to be_a(CouchbaseOrm::Relation::CouchbaseOrm_Relation) + end + + it "should pass adhoc: true to query options when set on the relation" do + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original + RelationModel.where(active: true).adhoc(true).ids + end + + it "should pass adhoc: false to query options when explicitly set on the relation" do + CouchbaseOrm::N1ql.config(adhoc: true) + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original + RelationModel.where(active: true).adhoc(false).ids + ensure + CouchbaseOrm::N1ql.config(adhoc: false) + end + + it "should override N1ql.config adhoc when set on the relation" do + CouchbaseOrm::N1ql.config(adhoc: true) + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original + RelationModel.where(active: true).adhoc(false).ids + ensure + CouchbaseOrm::N1ql.config(adhoc: false) + end + + it "should be chainable with other relation methods" do + m1 = RelationModel.create!(active: true, age: 10) + _m2 = RelationModel.create!(active: false, age: 20) + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original + result = RelationModel.where(active: true).order(:age).adhoc(true).to_a + expect(result).to match_array([m1]) + end + end end From 626a9bf85bec04f8419a231d4431f97fd5211514 Mon Sep 17 00:00:00 2001 From: Pierre Merlin Date: Mon, 6 Jul 2026 16:37:46 +0200 Subject: [PATCH 5/8] refactor: add DEFAULT_ADHOC constant and fix config merge behavior Default adhoc to true to preserve original gem behavior (no prepared statement caching unless explicitly opted in). Use DEFAULT_ADHOC constant for consistency with DEFAULT_SCAN_CONSISTENCY. Fix config to merge thread-local overrides on top of defaults instead of replacing them. Co-Authored-By: Claude Sonnet 4.6 --- lib/couchbase-orm/n1ql.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/couchbase-orm/n1ql.rb b/lib/couchbase-orm/n1ql.rb index 5150fab..51535cb 100644 --- a/lib/couchbase-orm/n1ql.rb +++ b/lib/couchbase-orm/n1ql.rb @@ -9,6 +9,7 @@ module N1ql extend ActiveSupport::Concern NO_VALUE = :no_value_specified DEFAULT_SCAN_CONSISTENCY = :request_plus + DEFAULT_ADHOC = true # sanitize for injection query def self.sanitize(value) if value.is_a?(String) @@ -22,10 +23,10 @@ def self.sanitize(value) def self.config(new_config = nil) Thread.current['__couchbaseorm_n1ql_config__'] = new_config if new_config - Thread.current['__couchbaseorm_n1ql_config__'] || { + { scan_consistency: DEFAULT_SCAN_CONSISTENCY, - adhoc: false - } + adhoc: DEFAULT_ADHOC + }.merge(Thread.current['__couchbaseorm_n1ql_config__'] || {}) end module ClassMethods From d1dd7d792e2319833bcd27cfe4ca5a2df5547a3d Mon Sep 17 00:00:00 2001 From: Pierre Merlin Date: Mon, 6 Jul 2026 16:39:50 +0200 Subject: [PATCH 6/8] refactor: replace adhoc field with generic query_options on relation Introduce `query_options(**opts)` on CouchbaseOrm_Relation that merges arbitrary Couchbase::Options::Query keyword args (scan_consistency, adhoc, timeout, etc.) into the query. `adhoc(value)` becomes a convenience wrapper over `query_options(adhoc: value)`, preserving the existing API. build_query_options now merges N1ql.config defaults with per-relation overrides, eliminating the nil-guard on @adhoc. Co-Authored-By: Claude Sonnet 4.6 --- lib/couchbase-orm/relation.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/couchbase-orm/relation.rb b/lib/couchbase-orm/relation.rb index 9243752..d6f667d 100644 --- a/lib/couchbase-orm/relation.rb +++ b/lib/couchbase-orm/relation.rb @@ -3,7 +3,7 @@ module Relation extend ActiveSupport::Concern class CouchbaseOrm_Relation - def initialize(model:, where: where = nil, order: order = nil, limit: limit = nil, _not: _not = false, strict_loading: strict_loading = false, adhoc: adhoc = nil) + def initialize(model:, where: where = nil, order: order = nil, limit: limit = nil, _not: _not = false, strict_loading: strict_loading = false, query_options: query_options = {}) CouchbaseOrm::logger.debug "CouchbaseOrm_Relation init: #{model} where:#{where.inspect} not:#{_not.inspect} order:#{order.inspect} limit: #{limit} strict_loading: #{strict_loading}" @model = model @limit = limit @@ -12,7 +12,7 @@ def initialize(model:, where: where = nil, order: order = nil, limit: limit = ni @order = merge_order(**order) if order @where = merge_where(where, _not) if where @strict_loading = strict_loading - @adhoc = adhoc + @query_options = query_options || {} CouchbaseOrm::logger.debug "- #{to_s}" end @@ -71,8 +71,12 @@ def strict_loading? !!@strict_loading end + def with(opts = {}) + CouchbaseOrm_Relation.new(**initializer_arguments.merge(query_options: @query_options.merge(opts))) + end + def adhoc(value) - CouchbaseOrm_Relation.new(**initializer_arguments.merge(adhoc: value)) + with(adhoc: value) end def first @@ -173,7 +177,7 @@ def build_limit end def initializer_arguments - { model: @model, order: @order, where: @where, limit: @limit, strict_loading: @strict_loading, adhoc: @adhoc } + { model: @model, order: @order, where: @where, limit: @limit, strict_loading: @strict_loading, query_options: @query_options } end def merge_order(*lorder, **horder) @@ -243,10 +247,7 @@ def build_update_with_params(params, **cond) end def build_query_options(positional_parameters: []) - opts = { - scan_consistency: CouchbaseOrm::N1ql.config[:scan_consistency], - adhoc: @adhoc.nil? ? CouchbaseOrm::N1ql.config[:adhoc] : @adhoc - } + opts = CouchbaseOrm::N1ql.config.merge(@query_options) opts[:positional_parameters] = positional_parameters unless positional_parameters.empty? Couchbase::Options::Query.new(**opts) end @@ -269,7 +270,7 @@ def relation delegate :ids, :update_all, :delete_all, :count, :empty?, :filter, :reduce, :find_by, to: :all - delegate :where, :not, :order, :limit, :all, :strict_loading, :strict_loading?, :adhoc, to: :relation + delegate :where, :not, :order, :limit, :all, :strict_loading, :strict_loading?, :with, :adhoc, to: :relation end end end From 897ddd3b23a9c8bf186ce53e1879b9252647b538 Mon Sep 17 00:00:00 2001 From: Pierre Merlin Date: Mon, 6 Jul 2026 17:49:26 +0200 Subject: [PATCH 7/8] refactor: default adhoc to true and drop dedicated relation method Address review feedback: - Keep existing behaviour: default adhoc: true (opt into plan caching with adhoc: false), fix specs to reflect true as the default value - Remove the dedicated Relation#adhoc method and its delegate alias; the generic `with(adhoc:)` covers the per-query override - Update SQL++ docs to reflect the flipped default and `with` usage Co-Authored-By: Claude Opus 4.8 --- .../07-sqlpp-queries.md | 28 +++++++-------- lib/couchbase-orm/relation.rb | 6 +--- spec/n1ql_spec.rb | 14 ++++---- spec/relation_spec.rb | 35 ++++++++----------- 4 files changed, 36 insertions(+), 47 deletions(-) diff --git a/docusaurus/docs/tutorial-ruby-couchbase-orm/07-sqlpp-queries.md b/docusaurus/docs/tutorial-ruby-couchbase-orm/07-sqlpp-queries.md index 6d198ae..9c0cefd 100644 --- a/docusaurus/docs/tutorial-ruby-couchbase-orm/07-sqlpp-queries.md +++ b/docusaurus/docs/tutorial-ruby-couchbase-orm/07-sqlpp-queries.md @@ -149,30 +149,30 @@ In the above examples, the `collect` method is used to extract the `name` attrib ## 7.8 Prepared Statement Plan Caching -Couchbase Server can cache the query execution plan for a SQL++ query so that subsequent executions skip the planning step. CouchbaseOrm opts into this behaviour by default: every query is sent with `adhoc: false`, which tells the server to prepare and cache the plan on first execution and reuse it on subsequent ones. +Couchbase Server can cache the query execution plan for a SQL++ query so that subsequent executions skip the planning step. This is controlled by the `adhoc` query option: `adhoc: false` tells the server to prepare and cache the plan on first execution and reuse it on subsequent ones. ### Default behaviour -No configuration is required. All queries — both `n1ql`-macro queries and relation queries (`where`, `all`, `first`, `last`, `ids`, `update_all`, `delete_all`) — use `adhoc: false` out of the box. +By default CouchbaseOrm runs queries with `adhoc: true` (the Couchbase SDK default), meaning no plan caching. This preserves the existing behaviour — you opt into plan caching explicitly. -### Disabling caching for a specific call +### Enabling caching for a specific call -Pass `adhoc: true` directly to the query method to execute without caching (useful for one-off or highly dynamic queries): +Pass `adhoc: false` directly to the query method to prepare and cache the plan (useful for frequently repeated queries): ```ruby -# Run once without caching the plan -N1QLTest.by_rating(key: 1, adhoc: true) +# Cache the plan for this query +N1QLTest.by_rating(key: 1, adhoc: false) -# Relation query without caching -User.where(country: 'FR').adhoc(true).to_a +# Relation query with plan caching +User.where(country: 'FR').with(adhoc: false).to_a ``` -### Disabling caching for a specific `n1ql` definition +### Enabling caching for a specific `n1ql` definition -Set `adhoc: true` in the macro options to always run that particular query as ad-hoc: +Set `adhoc: false` in the macro options to always cache the plan for that particular query: ```ruby -n1ql :by_dynamic_filter, emit_key: [:name], adhoc: true +n1ql :by_stable_filter, emit_key: [:name], adhoc: false ``` ### Changing the global default @@ -180,13 +180,13 @@ n1ql :by_dynamic_filter, emit_key: [:name], adhoc: true Override the thread-local config to change the default for all queries in the current thread: ```ruby -# Disable plan caching for all queries in this thread -CouchbaseOrm::N1ql.config(adhoc: true) +# Enable plan caching for all queries in this thread +CouchbaseOrm::N1ql.config(adhoc: false) ``` ### Override priority -From highest to lowest: **per-call kwarg** > **per-`n1ql`-definition option** > **`N1ql.config`** > **default (`false`)**. +From highest to lowest: **per-call kwarg** > **per-`n1ql`-definition option** > **`N1ql.config`** > **default (`true`)**. ## 7.7 Indexing for SQL++ diff --git a/lib/couchbase-orm/relation.rb b/lib/couchbase-orm/relation.rb index d6f667d..927efe8 100644 --- a/lib/couchbase-orm/relation.rb +++ b/lib/couchbase-orm/relation.rb @@ -75,10 +75,6 @@ def with(opts = {}) CouchbaseOrm_Relation.new(**initializer_arguments.merge(query_options: @query_options.merge(opts))) end - def adhoc(value) - with(adhoc: value) - end - def first n1ql_query, params = self.limit(1).to_n1ql_with_params result = @model.cluster.query(n1ql_query, build_query_options(positional_parameters: params)) @@ -270,7 +266,7 @@ def relation delegate :ids, :update_all, :delete_all, :count, :empty?, :filter, :reduce, :find_by, to: :all - delegate :where, :not, :order, :limit, :all, :strict_loading, :strict_loading?, :with, :adhoc, to: :relation + delegate :where, :not, :order, :limit, :all, :strict_loading, :strict_loading?, :with, to: :relation end end end diff --git a/spec/n1ql_spec.rb b/spec/n1ql_spec.rb index 36318e9..ca42599 100644 --- a/spec/n1ql_spec.rb +++ b/spec/n1ql_spec.rb @@ -196,20 +196,20 @@ class N1QLTest < CouchbaseOrm::Base end end - it "should use adhoc: false by default for prepared statement plan caching" do - expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original + it "should use adhoc: true by default (no prepared statement plan caching)" do + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original N1QLTest.by_rating_reverse() end - it "should allow overriding adhoc per call" do - expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original - N1QLTest.by_rating_reverse(adhoc: true) + it "should allow overriding adhoc per call to enable plan caching" do + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original + N1QLTest.by_rating_reverse(adhoc: false) end it "should respect N1ql.config adhoc setting" do default_config = CouchbaseOrm::N1ql.config - CouchbaseOrm::N1ql.config({ adhoc: true }) - expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original + CouchbaseOrm::N1ql.config({ adhoc: false }) + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original N1QLTest.by_rating_reverse() ensure CouchbaseOrm::N1ql.config(default_config) diff --git a/spec/relation_spec.rb b/spec/relation_spec.rb index a338a42..e9b4b84 100644 --- a/spec/relation_spec.rb +++ b/spec/relation_spec.rb @@ -502,42 +502,35 @@ def self.active end end - it "should use adhoc: false by default for prepared statement plan caching" do - expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original + it "should use adhoc: true by default (no prepared statement plan caching)" do + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original RelationModel.where(active: true).ids end - describe "adhoc option" do - it "should return a relation when calling adhoc" do - expect(RelationModel.all.adhoc(true)).to be_a(CouchbaseOrm::Relation::CouchbaseOrm_Relation) + describe "adhoc option via with" do + it "should return a relation when calling with(adhoc:)" do + expect(RelationModel.all.with(adhoc: false)).to be_a(CouchbaseOrm::Relation::CouchbaseOrm_Relation) end - it "should pass adhoc: true to query options when set on the relation" do - expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original - RelationModel.where(active: true).adhoc(true).ids - end - - it "should pass adhoc: false to query options when explicitly set on the relation" do - CouchbaseOrm::N1ql.config(adhoc: true) + it "should pass adhoc: false to query options when set on the relation" do expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original - RelationModel.where(active: true).adhoc(false).ids - ensure - CouchbaseOrm::N1ql.config(adhoc: false) + RelationModel.where(active: true).with(adhoc: false).ids end it "should override N1ql.config adhoc when set on the relation" do - CouchbaseOrm::N1ql.config(adhoc: true) - expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original - RelationModel.where(active: true).adhoc(false).ids - ensure + default_config = CouchbaseOrm::N1ql.config CouchbaseOrm::N1ql.config(adhoc: false) + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original + RelationModel.where(active: true).with(adhoc: true).ids + ensure + CouchbaseOrm::N1ql.config(default_config) end it "should be chainable with other relation methods" do m1 = RelationModel.create!(active: true, age: 10) _m2 = RelationModel.create!(active: false, age: 20) - expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original - result = RelationModel.where(active: true).order(:age).adhoc(true).to_a + expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original + result = RelationModel.where(active: true).order(:age).with(adhoc: false).to_a expect(result).to match_array([m1]) end end From 255f017c943637d1df6e1d027b1a4c309e17ca81 Mon Sep 17 00:00:00 2001 From: Pierre Merlin Date: Mon, 6 Jul 2026 17:51:26 +0200 Subject: [PATCH 8/8] ci(): fix libtinfo5 install by using apt repo instead of static deb URL Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/test.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 48b172f..3173c80 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,9 +34,10 @@ jobs: name: ${{ matrix.ruby }} rails-${{ matrix.active-model }} couchbase-${{ matrix.couchbase }} steps: - uses: actions/checkout@v3 - - run: sudo apt-get update && sudo apt-get install libevent-dev libev-dev python3-httplib2 - - run: wget http://security.ubuntu.com/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb - - run: sudo apt install ./libtinfo5_6.3-2ubuntu0.1_amd64.deb + - run: | + sudo apt-get update + sudo apt-get install -y libevent-dev libev-dev python3-httplib2 + sudo apt-get install -y libtinfo5 || sudo apt-get install -y libtinfo6 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }}