Module: Polyrun::Data::SqlSnapshot

Defined in:
lib/polyrun/data/snapshot.rb

Overview

PostgreSQL data snapshots via pg_dump / psql (no pg gem). Configure with ENV or explicit args. Non-Postgres adapters: use native backup/export tools; not covered here.

Class Method Summary collapse

Class Method Details

.create!(name, root:, database: nil, username: nil, host: nil, port: nil) ⇒ Object

Writes data-only SQL to root/spec/fixtures/sql_snapshots/<name>.sql

Raises:



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/polyrun/data/snapshot.rb', line 21

def create!(name, root:, database: nil, username: nil, host: nil, port: nil)
  database ||= default_connection[:database] or raise Polyrun::Error, "SqlSnapshot: set database: or PGDATABASE"
  username ||= default_connection[:username]
  path = File.join(root, "spec", "fixtures", "sql_snapshots", "#{name}.sql")
  FileUtils.mkdir_p(File.dirname(path))

  cmd = ["pg_dump", "--data-only", "-U", username]
  cmd += ["-h", host] if host && !host.to_s.empty?
  cmd += ["-p", port.to_s] if port && !port.to_s.empty?
  cmd << database

  out, err, st = Open3.capture3(*cmd)
  raise Polyrun::Error, "pg_dump failed: #{err}" unless st.success?

  File.write(path, out)
  path
end

.default_connectionObject



11
12
13
14
15
16
17
18
# File 'lib/polyrun/data/snapshot.rb', line 11

def default_connection
  {
    host: ENV["PGHOST"],
    port: ENV["PGPORT"],
    username: ENV["PGUSER"] || ENV["USER"],
    database: ENV["PGDATABASE"]
  }
end

.load!(name, root:, database: nil, username: nil, host: nil, port: nil, tables: nil) ⇒ Object

Truncates listed tables (if any), then loads snapshot SQL. tables optional; if nil and ActiveRecord is loaded, uses connection.tables.

Raises:



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/polyrun/data/snapshot.rb', line 41

def load!(name, root:, database: nil, username: nil, host: nil, port: nil, tables: nil)
  database ||= default_connection[:database] or raise Polyrun::Error, "SqlSnapshot: set database: or PGDATABASE"
  username ||= default_connection[:username]
  path = File.join(root, "spec", "fixtures", "sql_snapshots", "#{name}.sql")
  raise Polyrun::Error, "SqlSnapshot: missing #{path}" unless File.file?(path)

  if tables.nil? && defined?(ActiveRecord::Base) && ActiveRecord::Base.connected?
    tables = ActiveRecord::Base.connection.tables
  end
  tables ||= []

  psql = sql_snapshot_psql_base(username, database, host, port)
  sql_snapshot_truncate_tables!(psql, tables) if tables.any?
  sql_snapshot_load_file!(psql, path)
  true
end

.sql_snapshot_load_file!(psql, path) ⇒ Object

Raises:



72
73
74
75
76
77
78
79
80
81
# File 'lib/polyrun/data/snapshot.rb', line 72

def sql_snapshot_load_file!(psql, path)
  _load_out, err, st = Open3.capture3(
    *psql,
    "-v", "ON_ERROR_STOP=1",
    "-c", "SET session_replication_role = 'replica';",
    "-f", path,
    "-c", "SET session_replication_role = 'origin';"
  )
  raise Polyrun::Error, "psql load failed: #{err}" unless st.success?
end

.sql_snapshot_psql_base(username, database, host, port) ⇒ Object



58
59
60
61
62
63
# File 'lib/polyrun/data/snapshot.rb', line 58

def sql_snapshot_psql_base(username, database, host, port)
  psql = ["psql", "-U", username, "-d", database]
  psql += ["-h", host] if host && !host.to_s.empty?
  psql += ["-p", port.to_s] if port && !port.to_s.empty?
  psql
end

.sql_snapshot_truncate_tables!(psql, tables) ⇒ Object

Raises:



65
66
67
68
69
70
# File 'lib/polyrun/data/snapshot.rb', line 65

def sql_snapshot_truncate_tables!(psql, tables)
  quoted = tables.map { |t| %("#{t.gsub('"', '""')}") }.join(", ")
  trunc = "TRUNCATE TABLE #{quoted} CASCADE;"
  _trunc_out, err, st = Open3.capture3(*psql, "-v", "ON_ERROR_STOP=1", "-c", trunc)
  raise Polyrun::Error, "psql truncate failed: #{err}" unless st.success?
end