Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Last active October 22, 2017 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshCheek/e91fa717c14a16c04e9befb0cf5fe921 to your computer and use it in GitHub Desktop.
Save JoshCheek/e91fa717c14a16c04e9befb0cf5fe921 to your computer and use it in GitHub Desktop.
How to save a postgresql record into a backup table and restore it
require 'pg'
# PG.connect(dbname: 'postgres').exec('create database josh_testing')
db = PG.connect(dbname: 'josh_testing').tap { |db| db.exec 'begin' }
# Make the db a little nicer to work with for our experiment
def db.exec(*)
super.to_a
end
db.exec <<~SQL
create extension hstore;
create table backups (
col_values hstore
);
-- test it against a table with some sophisticated types
create type user_type as enum ('admin', 'moderator', 'user');
create type login_device as (name varchar, loggedin_at timestamp);
create type hours as range (subtype = time);
create table users (
id serial primary key, -- an autoincrementing value
name varchar, -- string
type user_type, -- enum
is_active bool, -- boolean
preferences hstore, -- hstore (this will be nested)
device login_device, -- composite type
oncall_hours hours -- a custom range
);
SQL
# create some users (but should work for an arbitrary table)
original = db.exec <<~SQL
insert into users (name, type, is_active, preferences, device, oncall_hours)
values ('Josh', 'admin', true, 'a=>b,c=>d', ('macbook', now()), hours('08:30 AM', '04:30 PM')),
('Sindy', 'moderator', false, '', null, null)
returning *;
SQL
# => [{"id"=>"1",
# "name"=>"Josh",
# "type"=>"admin",
# "is_active"=>"t",
# "preferences"=>"\"a\"=>\"b\", \"c\"=>\"d\"",
# "device"=>"(macbook,\"2017-10-22 09:13:10.504474\")",
# "oncall_hours"=>"[08:30:00,16:30:00)"},
# {"id"=>"2",
# "name"=>"Sindy",
# "type"=>"moderator",
# "is_active"=>"f",
# "preferences"=>"",
# "device"=>nil,
# "oncall_hours"=>nil}]
# Back the users up
db.exec <<~SQL
insert into backups (col_values)
select hstore(users)
from users
returning *;
SQL
# => [{"col_values"=>
# "\"id\"=>\"1\", \"name\"=>\"Josh\", \"type\"=>\"admin\", \"device\"=>\"(macbook,\\\"2017-10-22 09:13:10.504474\\\")\", \"is_active\"=>\"t\", \"preferences\"=>\"\\\"a\\\"=>\\\"b\\\", \\\"c\\\"=>\\\"d\\\"\", \"oncall_hours\"=>\"[08:30:00,16:30:00)\""},
# {"col_values"=>
# "\"id\"=>\"2\", \"name\"=>\"Sindy\", \"type\"=>\"moderator\", \"device\"=>NULL, \"is_active\"=>\"f\", \"preferences\"=>\"\", \"oncall_hours\"=>NULL"}]
# Delete the users
db.exec <<~SQL
delete from users;
select * from users;
SQL
# => []
# Restore them from the backup
backed_up = db.exec <<~SQL
insert into users
select r.*
from backups, populate_record(null::users, col_values) as r;
select * from users;
SQL
# => [{"id"=>"1",
# "name"=>"Josh",
# "type"=>"admin",
# "is_active"=>"t",
# "preferences"=>"\"a\"=>\"b\", \"c\"=>\"d\"",
# "device"=>"(macbook,\"2017-10-22 09:13:10.504474\")",
# "oncall_hours"=>"[08:30:00,16:30:00)"},
# {"id"=>"2",
# "name"=>"Sindy",
# "type"=>"moderator",
# "is_active"=>"f",
# "preferences"=>"",
# "device"=>nil,
# "oncall_hours"=>nil}]
# Do they match?
original == backed_up # => true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment