From ea0be07e5c28fe097e6f414c0de839b7a42e7c7e Mon Sep 17 00:00:00 2001 From: dajiaohuang Date: Sat, 24 Oct 2026 14:03:53 +0800 Subject: [PATCH 1/2] Fix in-batches OR filter precedence --- lib/pgsync/task.rb | 10 ++++++++-- test/in_batches_test.rb | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/pgsync/task.rb b/lib/pgsync/task.rb index ed4add67..bc8b2501 100644 --- a/lib/pgsync/task.rb +++ b/lib/pgsync/task.rb @@ -111,8 +111,14 @@ def sync_data where = "#{quote_ident(primary_key)} >= #{starting_id} AND #{quote_ident(primary_key)} < #{starting_id + batch_size}" log " #{i}/#{batch_count}: #{where}" - # TODO be smarter for advance sql clauses - batch_sql_clause = " #{sql_clause.length > 0 ? "#{sql_clause} AND" : "WHERE"} #{where}" + # Apply the batch range to the complete filter, not just its last OR term. + batch_sql_clause = + if sql_clause.length > 0 + where_clause = sql_clause.sub(/\A\s*where\s+/i, "") + " WHERE (#{where_clause}) AND (#{where})" + else + " WHERE #{where}" + end batch_copy_to_command = "COPY (SELECT #{copy_fields} FROM #{quoted_table}#{batch_sql_clause}) TO STDOUT" copy(batch_copy_to_command, dest_table: table, dest_fields: fields) diff --git a/test/in_batches_test.rb b/test/in_batches_test.rb index c6c40a9e..b7dbec89 100644 --- a/test/in_batches_test.rb +++ b/test/in_batches_test.rb @@ -12,6 +12,12 @@ def test_works assert_result("--in-batches --batch-size 1", source, dest, expected) end + def test_where_clause_with_or + source = 3.times.map { |i| {"id" => i + 1, "title" => "Post #{i + 1}"} } + expected = [source[0], source[2]] + assert_result("where id = 1 OR id = 3 --in-batches --batch-size 1", source, [], expected) + end + def test_existing_data source = 3.times.map { |i| {"id" => i + 1, "title" => "Post #{i + 1}"} } dest = [{"id" => 1, "title" => "First Post"}, {"id" => 4, "title" => "Post 4"}] From 0f20a2c4b5f7f23a8797a5225a66f69cdf05cf2f Mon Sep 17 00:00:00 2001 From: dajiaohuang Date: Wed, 28 Oct 2026 06:45:48 +0800 Subject: [PATCH 2/2] Handle WHERE without whitespace in batched sync filters --- lib/pgsync/task.rb | 2 +- test/in_batches_test.rb | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/pgsync/task.rb b/lib/pgsync/task.rb index bc8b2501..d234d524 100644 --- a/lib/pgsync/task.rb +++ b/lib/pgsync/task.rb @@ -114,7 +114,7 @@ def sync_data # Apply the batch range to the complete filter, not just its last OR term. batch_sql_clause = if sql_clause.length > 0 - where_clause = sql_clause.sub(/\A\s*where\s+/i, "") + where_clause = sql_clause.sub(/\A\s*where\b/i, "") " WHERE (#{where_clause}) AND (#{where})" else " WHERE #{where}" diff --git a/test/in_batches_test.rb b/test/in_batches_test.rb index b7dbec89..0e8584cc 100644 --- a/test/in_batches_test.rb +++ b/test/in_batches_test.rb @@ -15,7 +15,13 @@ def test_works def test_where_clause_with_or source = 3.times.map { |i| {"id" => i + 1, "title" => "Post #{i + 1}"} } expected = [source[0], source[2]] - assert_result("where id = 1 OR id = 3 --in-batches --batch-size 1", source, [], expected) + assert_result("\"where id = 1 OR id = 3\" --in-batches --batch-size 1", source, [], expected) + end + + def test_where_clause_without_space + source = 3.times.map { |i| {"id" => i + 1, "title" => "Post #{i + 1}"} } + expected = [source[0], source[2]] + assert_result("\"where(id = 1 OR id = 3)\" --in-batches --batch-size 1", source, [], expected) end def test_existing_data