Skip to content

Commit

Permalink
Make Database#table_exists? on PostgreSQL handle lock or statement ti…
Browse files Browse the repository at this point in the history
…meout errors as evidence the table exists (Fixes #2106)

This checks the exception message so it works with the postgres
adapter with both pg and pure-ruby postgres drivers, and with
the jdbc/postgresql adapter.
  • Loading branch information
jeremyevans committed Dec 15, 2023
1 parent aecf741 commit d7a6c1d
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
=== master

* Make Database#table_exists? on PostgreSQL handle lock or statement timeout errors as evidence the table exists (jeremyevans) (#2106)

* Work around DateTime.jd fractional second bug on JRuby in named_timezones extension (jeremyevans)

* Support fractional times and timestamps on SQLAnywhere (jeremyevans)
Expand Down
8 changes: 8 additions & 0 deletions lib/sequel/adapters/shared/postgres.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,14 @@ def _set_constraints_sql(type, opts)
sql << type
end

# Consider lock or statement timeout errors as evidence that the table exists
# but is locked.
def _table_exists?(ds)
super
rescue DatabaseError => e
raise e unless /canceling statement due to (?:statement|lock) timeout/ =~ e.message
end

def alter_table_add_column_sql(table, op)
"ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
end
Expand Down
21 changes: 21 additions & 0 deletions spec/adapters/postgres_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,27 @@ def c.exec_prepared(*); super; nil end
Sequel.datetime_class = Time
end

it "should return true for table_exists? if table exists but is locked" do
begin
@db.create_table(:test){Integer :id}
t = []
@db[:test].lock('ACCESS EXCLUSIVE') do
Thread.new do
@db.run "SET lock_timeout = 1"
t << @db.table_exists?(:test)
end.join
Thread.new do
@db.run "SET statement_timeout = 1"
t << @db.table_exists?(:test)
end.join
end
t.must_equal [true, true]
ensure
# Must disconnect to ensure connections with lock/statement timeout are not left in the pool
@db.disconnect
end
end

it "should be able to handle various types of IN/NOT IN queries" do
ds = @db.select(1)
ds.where(2=>[2, 3]).wont_be_empty
Expand Down

0 comments on commit d7a6c1d

Please sign in to comment.