# -*- encoding: utf-8 -*- require 'spec_helper' describe Upsert do describe 'hstore on pg' do require 'pg_hstore' let(:deserializer) do klass = PgHstore.dup if RUBY_PLATFORM == "java" # activerecord-jdbc-adapter has native support for hstore klass.class_eval do def self.parse(obj) obj end end end klass end Pet.connection.execute 'CREATE EXTENSION IF NOT EXISTS HSTORE' Pet.connection.execute "ALTER TABLE pets ADD COLUMN crazy HSTORE" Pet.connection.execute "ALTER TABLE pets ADD COLUMN cool HSTORE" before do Pet.delete_all end let(:upsert) { Upsert.new $conn, :pets } it "works for ugly text" do uggy = <<-EOS {"results":[{"locations":[],"providedLocation":{"location":"3001 STRATTON WAY, MADISON, WI 53719 UNITED STATES"}}],"options":{"ignoreLatLngInput":true,"maxResults":1,"thumbMaps":false},"info":{"copyright":{"text":"© 2012 MapQuest, Inc.","imageUrl":"http://api.mqcdn.com/res/mqlogo.gif","imageAltText":"© 2012 MapQuest, Inc."},"statuscode":0,"messages":[]}} EOS upsert.row({:name => 'Uggy'}, :crazy => {:uggy => uggy}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Uggy'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'uggy' => uggy } end it "just works" do upsert.row({:name => 'Bill'}, :crazy => nil) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) row['crazy'].should == nil upsert.row({:name => 'Bill'}, :crazy => {:a => 1}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1' } upsert.row({:name => 'Bill'}, :crazy => nil) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) row['crazy'].should == nil upsert.row({:name => 'Bill'}, :crazy => {:a => 1}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1' } upsert.row({:name => 'Bill'}, :crazy => {:whatdat => 'whodat'}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1', 'whatdat' => 'whodat' } upsert.row({:name => 'Bill'}, :crazy => {:whatdat => "D'ONOFRIO"}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1', 'whatdat' => "D'ONOFRIO" } upsert.row({:name => 'Bill'}, :crazy => {:a => 2}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '2', 'whatdat' => "D'ONOFRIO" } end it "can nullify entire hstore" do upsert.row({:name => 'Bill'}, :crazy => {:a => 1}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1' } upsert.row({:name => 'Bill'}, :crazy => nil) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) row['crazy'].should == nil end it "deletes keys that are nil" do upsert.row({:name => 'Bill'}, :crazy => nil) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) row['crazy'].should == nil upsert.row({:name => 'Bill'}, :crazy => {:a => 1}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1' } upsert.row({:name => 'Bill'}, :crazy => {}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1' } upsert.row({:name => 'Bill'}, :crazy => {:a => nil}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == {} upsert.row({:name => 'Bill'}, :crazy => {:a => 1, :b => 5}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1', 'b' => '5' } upsert.row({:name => 'Bill'}, :crazy => {}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1', 'b' => '5' } upsert.row({:name => 'Bill'}, :crazy => {:a => nil}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'b' => '5' } upsert.row({:name => 'Bill'}, :crazy => {:a => 1, :b => 5}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1', 'b' => '5' } upsert.row({:name => 'Bill'}, :crazy => {:a => nil, :b => nil, :c => 12}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'c' => '12' } end it "takes dangerous keys" do upsert.row({:name => 'Bill'}, :crazy => nil) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) row['crazy'].should == nil upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => 1}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'foo"bar' => '1' } upsert.row({:name => 'Bill'}, :crazy => {}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'foo"bar' => '1' } upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => nil}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == {} upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => 1, :b => 5}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'foo"bar' => '1', 'b' => '5' } upsert.row({:name => 'Bill'}, :crazy => {}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'foo"bar' => '1', 'b' => '5' } upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => nil}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'b' => '5' } upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => 1, :b => 5}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'foo"bar' => '1', 'b' => '5' } upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => nil, :b => nil, :c => 12}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'c' => '12' } end it "handles multiple hstores" do upsert.row({:name => 'Bill'}, :crazy => {:a => 1, :b => 9}, :cool => {:c => 12, :d => 19}) row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1', 'b' => '9' } cool = deserializer.parse row['cool'] cool.should == { 'c' => '12', 'd' => '19' } end it "can deletes keys from multiple hstores at once" do upsert.row({:name => 'Bill'}, :crazy => {:a => 1}, :cool => {5 => 9}) row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1' } cool = deserializer.parse row['cool'] cool.should == { '5' => '9' } # NOOP upsert.row({:name => 'Bill'}, :crazy => {}, :cool => {}) row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1' } cool = deserializer.parse row['cool'] cool.should == { '5' => '9' } upsert.row({:name => 'Bill'}, :crazy => {:a => nil}, :cool => {13 => 17}) row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == {} cool = deserializer.parse row['cool'] cool.should == { '5' => '9', '13' => '17' } upsert.row({:name => 'Bill'}, :crazy => {:a => 1, :b => 5}) row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1', 'b' => '5' } upsert.row({:name => 'Bill'}, :crazy => {:b => nil}, :cool => {5 => nil}) row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == {'a' => '1'} cool = deserializer.parse row['cool'] cool.should == {'13' => '17' } end it "deletes keys whether new or existing record" do upsert.row({:name => 'Bill'}, :crazy => {:z => 1, :x => nil}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'z' => '1' } upsert.row({:name => 'Bill'}, :crazy => {:a => 1}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1', 'z' => '1' } end it "can turn off eager nullify" do upsert.row({:name => 'Bill'}, {:crazy => {:z => 1, :x => nil}}, :eager_nullify => false) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'z' => '1', 'x' => nil } upsert.row({:name => 'Bill'}, :crazy => {:a => 1}) row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'}) crazy = deserializer.parse row['crazy'] crazy.should == { 'a' => '1', 'z' => '1', 'x' => nil} end end end if ENV['DB'] == 'postgresql'