upsert 2.2.0 → 2.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/.ruby-version +1 -0
  3. data/.standard.yml +1 -0
  4. data/.travis.yml +54 -31
  5. data/CHANGELOG +16 -0
  6. data/Gemfile +12 -1
  7. data/LICENSE +3 -1
  8. data/README.md +43 -8
  9. data/Rakefile +7 -1
  10. data/lib/upsert.rb +49 -7
  11. data/lib/upsert/column_definition/mysql.rb +2 -2
  12. data/lib/upsert/column_definition/postgresql.rb +9 -8
  13. data/lib/upsert/column_definition/sqlite3.rb +3 -3
  14. data/lib/upsert/connection/Java_ComMysqlJdbc_JDBC4Connection.rb +5 -3
  15. data/lib/upsert/connection/Java_OrgPostgresqlJdbc_PgConnection.rb +33 -0
  16. data/lib/upsert/connection/PG_Connection.rb +5 -0
  17. data/lib/upsert/connection/jdbc.rb +7 -1
  18. data/lib/upsert/connection/postgresql.rb +2 -3
  19. data/lib/upsert/merge_function.rb +3 -2
  20. data/lib/upsert/merge_function/{Java_OrgPostgresqlJdbc4_Jdbc4Connection.rb → Java_OrgPostgresqlJdbc_PgConnection.rb} +2 -2
  21. data/lib/upsert/merge_function/PG_Connection.rb +2 -2
  22. data/lib/upsert/merge_function/postgresql.rb +81 -19
  23. data/lib/upsert/merge_function/sqlite3.rb +10 -0
  24. data/lib/upsert/version.rb +1 -1
  25. data/spec/correctness_spec.rb +20 -5
  26. data/spec/database_functions_spec.rb +6 -2
  27. data/spec/hstore_spec.rb +53 -38
  28. data/spec/logger_spec.rb +1 -1
  29. data/spec/postgresql_spec.rb +81 -3
  30. data/spec/reserved_words_spec.rb +18 -14
  31. data/spec/sequel_spec.rb +16 -7
  32. data/spec/spec_helper.rb +238 -111
  33. data/spec/speed_spec.rb +3 -33
  34. data/spec/threaded_spec.rb +35 -12
  35. data/spec/type_safety_spec.rb +2 -1
  36. data/travis/run_docker_db.sh +20 -0
  37. data/upsert-java.gemspec +13 -0
  38. data/upsert.gemspec +9 -58
  39. data/upsert.gemspec.common +107 -0
  40. metadata +39 -46
  41. data/lib/upsert/connection/Java_OrgPostgresqlJdbc4_Jdbc4Connection.rb +0 -20
@@ -17,9 +17,11 @@ class Upsert
17
17
 
18
18
  def bind_value(v)
19
19
  case v
20
- when Time, DateTime
21
- # mysql doesn't like it when you send timezone to a datetime
22
- Upsert.utc_iso8601 v, false
20
+ when DateTime, Time
21
+ date = v.utc
22
+ java.time.LocalDateTime.of(date.year, date.month, date.day, date.hour, date.min, date.sec, date.nsec)
23
+ when Date
24
+ java.time.LocalDate.of(v.year, v.month, v.day)
23
25
  else
24
26
  super
25
27
  end
@@ -0,0 +1,33 @@
1
+ require_relative "jdbc"
2
+ require_relative "postgresql"
3
+
4
+ class Upsert
5
+ class Connection
6
+ # @private
7
+ class Java_OrgPostgresqlJdbc_PgConnection < Connection
8
+ include Jdbc
9
+ include Postgresql
10
+
11
+ def quote_ident(k)
12
+ DOUBLE_QUOTE + k.to_s.gsub(DOUBLE_QUOTE, '""') + DOUBLE_QUOTE
13
+ end
14
+
15
+ def in_transaction?
16
+ # https://github.com/kares/activerecord-jdbc-adapter/commit/4d6e0e0c52d12b0166810dffc9f898141a23bee6
17
+ ![0, 4].include?(metal.get_transaction_state)
18
+ end
19
+
20
+ def bind_value(v)
21
+ case v
22
+ when DateTime, Time
23
+ date = v.utc
24
+ java.time.LocalDateTime.of(date.year, date.month, date.day, date.hour, date.min, date.sec, date.nsec)
25
+ when Date
26
+ java.time.LocalDate.of(v.year, v.month, v.day)
27
+ else
28
+ super
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,3 +1,5 @@
1
+ require_relative "postgresql"
2
+
1
3
  class Upsert
2
4
  class Connection
3
5
  # @private
@@ -7,6 +9,9 @@ class Upsert
7
9
  def execute(sql, params = nil)
8
10
  if params
9
11
  # Upsert.logger.debug { %{[upsert] #{sql} with #{params.inspect}} }
12
+ # The following will blow up if you pass a value that cannot be automatically type-casted,
13
+ # such as passing a string to an integer field. You'll get an error something along the
14
+ # lines of: "invalid input syntax for <type>: <value>"
10
15
  metal.exec sql, convert_binary(params)
11
16
  else
12
17
  Upsert.logger.debug { %{[upsert] #{sql}} }
@@ -4,12 +4,14 @@ class Upsert
4
4
  module Jdbc
5
5
  # /Users/seamusabshere/.rvm/gems/jruby-head/gems/activerecord-jdbc-adapter-1.2.2.1/src/java/arjdbc/jdbc/RubyJdbcConnection.java
6
6
  GETTER = {
7
+ java.sql.Types::CHAR => 'getString',
7
8
  java.sql.Types::VARCHAR => 'getString',
8
9
  java.sql.Types::OTHER => 'getString', # ?! i guess unicode text?
9
10
  java.sql.Types::BINARY => 'getBlob',
10
11
  java.sql.Types::LONGVARCHAR => 'getString',
11
12
  java.sql.Types::BIGINT => 'getLong',
12
13
  java.sql.Types::INTEGER => 'getInt',
14
+ java.sql.Types::REAL => "getLong",
13
15
  java.sql.Types::ARRAY => ->(r, i){ r.getArray(i).array.to_ary }
14
16
  }
15
17
  java.sql.Types.constants.each do |type_name|
@@ -24,6 +26,7 @@ class Upsert
24
26
  'TrueClass' => 'setBoolean',
25
27
  'FalseClass' => 'setBoolean',
26
28
  'Fixnum' => 'setInt',
29
+ 'Integer' => 'setInt'
27
30
  )
28
31
 
29
32
  def binary(v)
@@ -44,13 +47,16 @@ class Upsert
44
47
  case v
45
48
  when Upsert::Binary
46
49
  statement.setBytes i+1, binary(v)
47
- when BigDecimal
50
+ when Float, BigDecimal
48
51
  statement.setBigDecimal i+1, java.math.BigDecimal.new(v.to_s)
49
52
  when NilClass
50
53
  # http://stackoverflow.com/questions/4243513/why-does-preparedstatement-setnull-requires-sqltype
51
54
  statement.setObject i+1, nil
55
+ when java.time.LocalDateTime, java.time.Instant, java.time.LocalDate
56
+ statement.setObject i+1, v
52
57
  else
53
58
  setter = setters[v.class.name]
59
+ Upsert.logger.debug { "Setting [#{v.class}, #{v}] via #{setter}" }
54
60
  statement.send setter, i+1, v
55
61
  end
56
62
  end
@@ -8,9 +8,8 @@ class Upsert
8
8
  # pg array escaping lifted from https://github.com/tlconnor/activerecord-postgres-array/blob/master/lib/activerecord-postgres-array/array.rb
9
9
  '{' + v.map do |vv|
10
10
  vv = vv.to_s.dup
11
- vv.gsub! /\\/, '\&\&'
12
- vv.gsub! /'/, "''"
13
- vv.gsub! /"/, '\"'
11
+ vv.gsub!(/\\/, '\&\&')
12
+ vv.gsub!(/"/, '\"')
14
13
  %{"#{vv}"}
15
14
  end.join(',') + '}'
16
15
  when Hash
@@ -11,7 +11,7 @@ class Upsert
11
11
  def unique_name(table_name, selector_keys, setter_keys)
12
12
  parts = [
13
13
  NAME_PREFIX,
14
- table_name,
14
+ [*table_name].join("_").gsub(/[^\w_]+/, "_"),
15
15
  'SEL',
16
16
  selector_keys.join('_A_').gsub(" ","_"),
17
17
  'SET',
@@ -35,8 +35,9 @@ class Upsert
35
35
  @controller = controller
36
36
  @selector_keys = selector_keys
37
37
  @setter_keys = setter_keys
38
+ @assume_function_exists = assume_function_exists
38
39
  validate!
39
- create! unless assume_function_exists
40
+ create! unless @assume_function_exists
40
41
  end
41
42
 
42
43
  def name
@@ -3,7 +3,7 @@ require 'upsert/merge_function/postgresql'
3
3
  class Upsert
4
4
  class MergeFunction
5
5
  # @private
6
- class Java_OrgPostgresqlJdbc4_Jdbc4Connection < MergeFunction
6
+ class Java_OrgPostgresqlJdbc_PgConnection < MergeFunction
7
7
  ERROR_CLASS = org.postgresql.util.PSQLException
8
8
  include Postgresql
9
9
 
@@ -18,7 +18,7 @@ class Upsert
18
18
 
19
19
  def unique_index_on_selector?
20
20
  return @unique_index_on_selector if defined?(@unique_index_on_selector)
21
- @unique_index_on_selector = schema_query.any? do |row|
21
+ @unique_index_on_selector = unique_index_columns.any? do |row|
22
22
  row["index_columns"].sort == selector_keys.sort
23
23
  end
24
24
  end
@@ -15,9 +15,9 @@ class Upsert
15
15
  return @unique_index_on_selector if defined?(@unique_index_on_selector)
16
16
 
17
17
  type_map = PG::TypeMapByColumn.new([PG::TextDecoder::Array.new])
18
- schema_query.type_map = type_map
18
+ res = unique_index_columns.tap { |r| r.type_map = type_map }
19
19
 
20
- @unique_index_on_selector = schema_query.values.any? do |row|
20
+ @unique_index_on_selector = res.values.any? do |row|
21
21
  row.first.sort == selector_keys.sort
22
22
  end
23
23
  end
@@ -66,7 +66,7 @@ class Upsert
66
66
 
67
67
  first_try = true
68
68
  begin
69
- create! if connection.in_transaction? && !function_exists?
69
+ create! if !@assume_function_exists && (connection.in_transaction? && !function_exists?)
70
70
  execute_parameterized(sql, values.map { |v| connection.bind_value v })
71
71
  rescue self.class::ERROR_CLASS => pg_error
72
72
  if pg_error.message =~ /function #{name}.* does not exist/i
@@ -85,8 +85,7 @@ class Upsert
85
85
  end
86
86
 
87
87
  def function_exists?
88
- # The ::int is a hack until jruby+jdbc is happy with bigints being returned
89
- @function_exists ||= controller.connection.execute("SELECT count(*)::int AS cnt FROM pg_proc WHERE lower(proname) = lower('#{name}')").first["cnt"].to_i > 0
88
+ @function_exists ||= controller.connection.execute("SELECT count(*) AS cnt FROM pg_proc WHERE lower(proname) = lower('#{name}')").first["cnt"].to_i > 0
90
89
  end
91
90
 
92
91
  # strangely ? can't be used as a placeholder
@@ -107,34 +106,93 @@ class Upsert
107
106
  end
108
107
 
109
108
  def use_pg_native?
110
- server_version >= 95 && unique_index_on_selector?
109
+ return @use_pg_native if defined?(@use_pg_native)
110
+
111
+ @use_pg_native = server_version >= 90500 && unique_index_on_selector?
112
+ Upsert.logger.warn "[upsert] WARNING: Not using native PG CONFLICT / UPDATE" unless @use_pg_native
113
+ @use_pg_native
111
114
  end
112
115
 
113
116
  def server_version
114
- @server_version ||=
115
- controller.connection.execute("SHOW server_version").first["server_version"].split('.')[0..1].join('').to_i
117
+ @server_version ||= Upsert::MergeFunction::Postgresql.extract_version(
118
+ controller.connection.execute("SHOW server_version").first["server_version"]
119
+ )
120
+ end
121
+
122
+ # Extracted from https://github.com/dr-itz/activerecord-jdbc-adapter/blob/master/lib/arjdbc/postgresql/adapter.rb
123
+ def self.extract_version(version_string)
124
+ # Use the same versioning format as jdbc-postgresql and libpq
125
+ # https://github.com/dr-itz/activerecord-jdbc-adapter/commit/fd79756374c62fa9d009995dd1914d780e6a3dbf
126
+ # https://github.com/postgres/postgres/blob/master/src/interfaces/libpq/fe-exec.c
127
+ if (match = version_string.match(/([\d\.]*\d).*?/))
128
+ version = match[1].split('.').map(&:to_i)
129
+ # PostgreSQL version representation does not have more than 4 digits
130
+ # From version 10 onwards, PG has changed its versioning policy to
131
+ # limit it to only 2 digits. i.e. in 10.x, 10 being the major
132
+ # version and x representing the patch release
133
+ # Refer to:
134
+ # https://www.postgresql.org/support/versioning/
135
+ # https://www.postgresql.org/docs/10/static/libpq-status.html -> PQserverVersion()
136
+ # for more info
137
+
138
+ if version.size >= 3
139
+ (version[0] * 100 + version[1]) * 100 + version[2]
140
+ elsif version.size == 2
141
+ if version[0] >= 10
142
+ version[0] * 100 * 100 + version[1]
143
+ else
144
+ (version[0] * 100 + version[1]) * 100
145
+ end
146
+ elsif version.size == 1
147
+ version[0] * 100 * 100
148
+ else
149
+ 0
150
+ end
151
+ else
152
+ 0
153
+ end
116
154
  end
117
155
 
118
- def schema_query
156
+ def unique_index_columns
157
+ if table_name.is_a?(Array) && table_name.length > 1
158
+ schema_argument = '$2'
159
+ table_name_arguments = table_name
160
+ else
161
+ schema_argument = 'ANY(current_schemas(true)::text[])'
162
+ table_name_arguments = [*table_name]
163
+ end
164
+
165
+ table_name_arguments.reverse!
166
+
119
167
  execute_parameterized(
120
168
  %{
121
- SELECT array_agg(column_name::text) AS index_columns FROM information_schema.constraint_column_usage
122
- JOIN pg_catalog.pg_constraint ON constraint_name::text = conname::text
123
- WHERE table_name = $1 AND conrelid = $1::regclass::oid AND contype = 'u'
124
- GROUP BY table_catalog, table_name, constraint_name
169
+ SELECT
170
+ ARRAY(
171
+ SELECT pg_get_indexdef(pg_index.indexrelid, k + 1, TRUE)
172
+ FROM
173
+ generate_subscripts(pg_index.indkey, 1) AS k
174
+ ORDER BY k
175
+ ) AS index_columns
176
+ FROM pg_index
177
+ JOIN pg_class AS idx ON idx.oid = pg_index.indexrelid
178
+ JOIN pg_class AS tbl ON tbl.oid = pg_index.indrelid
179
+ JOIN pg_namespace ON pg_namespace.oid = idx.relnamespace
180
+ WHERE pg_index.indisunique IS TRUE AND pg_namespace.nspname = #{schema_argument} AND tbl.relname = $1
125
181
  },
126
- [table_name]
182
+ table_name_arguments
127
183
  )
128
184
  end
129
185
 
130
186
  def pg_native(row)
131
187
  bind_setter_values = row.setter.values.map { |v| connection.bind_value v }
188
+ # TODO: Is this needed?
189
+ row_syntax = server_version >= 100 ? "ROW" : ""
132
190
 
133
191
  upsert_sql = %{
134
192
  INSERT INTO #{quoted_table_name} (#{quoted_setter_names.join(',')})
135
193
  VALUES (#{insert_bind_placeholders(row).join(', ')})
136
194
  ON CONFLICT(#{quoted_selector_names.join(', ')})
137
- DO UPDATE SET (#{quoted_setter_names.join(', ')}) = (#{conflict_bind_placeholders(row).join(', ')})
195
+ DO UPDATE SET #{quoted_setter_names.zip(conflict_bind_placeholders(row)).map { |n, v| "#{n} = #{v}" }.join(', ')}
138
196
  }
139
197
 
140
198
  execute_parameterized(upsert_sql, bind_setter_values)
@@ -157,13 +215,17 @@ class Upsert
157
215
  def insert_bind_placeholders(row)
158
216
  if row.hstore_delete_keys.empty?
159
217
  @insert_bind_placeholders ||= setter_column_definitions.each_with_index.map do |column_definition, i|
160
- "$#{i + 1}"
218
+ if column_definition.hstore?
219
+ "CAST($#{i + 1} AS hstore)"
220
+ else
221
+ "$#{i + 1}"
222
+ end
161
223
  end
162
224
  else
163
225
  setter_column_definitions.each_with_index.map do |column_definition, i|
164
226
  idx = i + 1
165
227
  if column_definition.hstore?
166
- hstore_delete_function("$#{idx}", row, column_definition)
228
+ hstore_delete_function("CAST($#{idx} AS hstore)", row, column_definition)
167
229
  else
168
230
  "$#{idx}"
169
231
  end
@@ -176,8 +238,8 @@ class Upsert
176
238
  @conflict_bind_placeholders ||= setter_column_definitions.each_with_index.map do |column_definition, i|
177
239
  idx = i + 1
178
240
  if column_definition.hstore?
179
- "CASE WHEN #{quoted_table_name}.#{column_definition.quoted_name} IS NULL THEN $#{idx} ELSE" \
180
- + " (#{quoted_table_name}.#{column_definition.quoted_name} || $#{idx})" \
241
+ "CASE WHEN #{quoted_table_name}.#{column_definition.quoted_name} IS NULL THEN CAST($#{idx} AS hstore) ELSE" \
242
+ + " (#{quoted_table_name}.#{column_definition.quoted_name} || CAST($#{idx} AS hstore))" \
181
243
  + " END"
182
244
  else
183
245
  "$#{idx}"
@@ -188,9 +250,9 @@ class Upsert
188
250
  idx = i + 1
189
251
  if column_definition.hstore?
190
252
  "CASE WHEN #{quoted_table_name}.#{column_definition.quoted_name} IS NULL THEN " \
191
- + hstore_delete_function("$#{idx}", row, column_definition) \
253
+ + hstore_delete_function("CAST($#{idx} AS hstore)", row, column_definition) \
192
254
  + " ELSE " \
193
- + hstore_delete_function("(#{quoted_table_name}.#{column_definition.quoted_name} || $#{idx})", row, column_definition) \
255
+ + hstore_delete_function("(#{quoted_table_name}.#{column_definition.quoted_name} || CAST($#{idx} AS hstore))", row, column_definition) \
194
256
  + " END"
195
257
  else
196
258
  "$#{idx}"
@@ -2,6 +2,16 @@ class Upsert
2
2
  class MergeFunction
3
3
  # @private
4
4
  module Sqlite3
5
+ def self.included(klass)
6
+ klass.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+ def clear(*)
11
+ # not necessary
12
+ end
13
+ end
14
+
5
15
  attr_reader :quoted_setter_names
6
16
  attr_reader :quoted_update_names
7
17
  attr_reader :quoted_selector_names
@@ -1,3 +1,3 @@
1
1
  class Upsert
2
- VERSION = '2.2.0'
2
+ VERSION = "2.9.10"
3
3
  end
@@ -13,7 +13,8 @@ describe Upsert do
13
13
  u = Upsert.new($conn, :pets)
14
14
  selector = {:name => 'Jerry', :tag_number => 6}
15
15
  u.row(selector)
16
- Pet.find_by_name('Jerry').tag_number.should == 5
16
+ p.reload.tag_number.should == 5
17
+ next
17
18
 
18
19
  # won't change anything because selector is wrong
19
20
  u = Upsert.new($conn, :pets)
@@ -100,6 +101,15 @@ describe Upsert do
100
101
  lambda { u.row(:name => 'Jerry', :gibberish => 'ba', :gender => 'male') }.should raise_error(/invalid col/i)
101
102
  end
102
103
 
104
+ it "works with a long setter hash" do
105
+ Upsert.batch($conn, :alphabets) do |batch|
106
+ 10_000.times do |time|
107
+ setter = Hash[("a".."z").map { |letter| ["the_letter_#{letter}".to_sym, rand(100)] }]
108
+ selector = Hash[("a".."z").map { |letter| ["the_letter_#{letter}".to_sym, rand(100)] }]
109
+ batch.row(setter, selector)
110
+ end
111
+ end
112
+ end
103
113
  end
104
114
 
105
115
  describe "is just as correct as other ways" do
@@ -107,8 +117,8 @@ describe Upsert do
107
117
  it "is as correct as than new/set/save" do
108
118
  assert_same_result lotsa_records do |records|
109
119
  records.each do |selector, setter|
110
- if pet = Pet.where(selector).first
111
- pet.update_attributes setter, :without_protection => true
120
+ if (pet = Pet.where(selector).first)
121
+ pet.update_attributes(setter)
112
122
  else
113
123
  pet = Pet.new
114
124
  selector.each do |k, v|
@@ -148,12 +158,15 @@ describe Upsert do
148
158
  # end
149
159
  end
150
160
 
151
- if ENV['DB'] == 'mysql' && RUBY_VERSION >= '1.9'
161
+ if ENV['DB'] == 'mysql' || (UNIQUE_CONSTRAINT && ENV["DB"] == "postgresql")
152
162
  describe 'compared to activerecord-import' do
153
163
  it "is as correct as faking upserts with activerecord-import" do
154
164
  assert_same_result lotsa_records do |records|
155
165
  columns = nil
156
166
  all_values = []
167
+ # Reverse because we want to mimic an 'overwrite' of previous values
168
+ records = records.reverse.uniq { |s, _| s } if ENV['DB'] == "postgresql"
169
+
157
170
  records.each do |selector, setter|
158
171
  columns ||= (selector.keys + setter.keys).uniq
159
172
  all_values << columns.map do |k|
@@ -165,7 +178,9 @@ describe Upsert do
165
178
  end
166
179
  end
167
180
  end
168
- Pet.import columns, all_values, :timestamps => false, :on_duplicate_key_update => columns
181
+
182
+ conflict_update = ENV['DB'] == "postgresql" ? {conflict_target: records.first.first.keys, columns: columns} : columns
183
+ Pet.import columns, all_values, :timestamps => false, :on_duplicate_key_update => conflict_update
169
184
  end
170
185
  end
171
186
  end
@@ -1,10 +1,14 @@
1
1
  require 'spec_helper'
2
2
  require 'stringio'
3
+ require 'upsert/merge_function/postgresql'
4
+
3
5
  describe Upsert do
4
6
  describe 'database functions' do
5
- version = 'postgresql' == ENV['DB'] ? Pet.connection.select_value("SHOW server_version")[0..2].split('.').join('').to_i : 0
7
+ version = 'postgresql' == ENV['DB'] ? Upsert::MergeFunction::Postgresql.extract_version(
8
+ Pet.connection.select_value("SHOW server_version")
9
+ ) : 0
6
10
  before(:each) {
7
- skip "Not using DB functions" if 'postgresql' == ENV['DB'] && UNIQUE_CONSTRAINT && version >= 95
11
+ skip "Not using DB functions" if 'postgresql' == ENV['DB'] && UNIQUE_CONSTRAINT && version >= 90500
8
12
  }
9
13
  it "does not re-use merge functions across connections" do
10
14
  begin
data/spec/hstore_spec.rb CHANGED
@@ -3,6 +3,21 @@ require 'spec_helper'
3
3
  describe Upsert do
4
4
  describe 'hstore on pg' do
5
5
  require 'pg_hstore'
6
+
7
+ let(:deserializer) do
8
+ klass = PgHstore.dup
9
+ if RUBY_PLATFORM == "java"
10
+ # activerecord-jdbc-adapter has native support for hstore
11
+ klass.class_eval do
12
+ def self.parse(obj)
13
+ obj
14
+ end
15
+ end
16
+ end
17
+
18
+ klass
19
+ end
20
+
6
21
  Pet.connection.execute 'CREATE EXTENSION IF NOT EXISTS HSTORE'
7
22
  Pet.connection.execute "ALTER TABLE pets ADD COLUMN crazy HSTORE"
8
23
  Pet.connection.execute "ALTER TABLE pets ADD COLUMN cool HSTORE"
@@ -19,7 +34,7 @@ describe Upsert do
19
34
  EOS
20
35
  upsert.row({:name => 'Uggy'}, :crazy => {:uggy => uggy})
21
36
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Uggy'})
22
- crazy = PgHstore.parse row['crazy']
37
+ crazy = deserializer.parse row['crazy']
23
38
  crazy.should == { 'uggy' => uggy }
24
39
  end
25
40
 
@@ -30,7 +45,7 @@ EOS
30
45
 
31
46
  upsert.row({:name => 'Bill'}, :crazy => {:a => 1})
32
47
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
33
- crazy = PgHstore.parse row['crazy']
48
+ crazy = deserializer.parse row['crazy']
34
49
  crazy.should == { 'a' => '1' }
35
50
 
36
51
  upsert.row({:name => 'Bill'}, :crazy => nil)
@@ -39,29 +54,29 @@ EOS
39
54
 
40
55
  upsert.row({:name => 'Bill'}, :crazy => {:a => 1})
41
56
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
42
- crazy = PgHstore.parse row['crazy']
57
+ crazy = deserializer.parse row['crazy']
43
58
  crazy.should == { 'a' => '1' }
44
59
 
45
60
  upsert.row({:name => 'Bill'}, :crazy => {:whatdat => 'whodat'})
46
61
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
47
- crazy = PgHstore.parse row['crazy']
62
+ crazy = deserializer.parse row['crazy']
48
63
  crazy.should == { 'a' => '1', 'whatdat' => 'whodat' }
49
64
 
50
65
  upsert.row({:name => 'Bill'}, :crazy => {:whatdat => "D'ONOFRIO"})
51
66
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
52
- crazy = PgHstore.parse row['crazy']
67
+ crazy = deserializer.parse row['crazy']
53
68
  crazy.should == { 'a' => '1', 'whatdat' => "D'ONOFRIO" }
54
69
 
55
70
  upsert.row({:name => 'Bill'}, :crazy => {:a => 2})
56
71
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
57
- crazy = PgHstore.parse row['crazy']
72
+ crazy = deserializer.parse row['crazy']
58
73
  crazy.should == { 'a' => '2', 'whatdat' => "D'ONOFRIO" }
59
74
  end
60
75
 
61
76
  it "can nullify entire hstore" do
62
77
  upsert.row({:name => 'Bill'}, :crazy => {:a => 1})
63
78
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
64
- crazy = PgHstore.parse row['crazy']
79
+ crazy = deserializer.parse row['crazy']
65
80
  crazy.should == { 'a' => '1' }
66
81
 
67
82
  upsert.row({:name => 'Bill'}, :crazy => nil)
@@ -76,42 +91,42 @@ EOS
76
91
 
77
92
  upsert.row({:name => 'Bill'}, :crazy => {:a => 1})
78
93
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
79
- crazy = PgHstore.parse row['crazy']
94
+ crazy = deserializer.parse row['crazy']
80
95
  crazy.should == { 'a' => '1' }
81
96
 
82
97
  upsert.row({:name => 'Bill'}, :crazy => {})
83
98
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
84
- crazy = PgHstore.parse row['crazy']
99
+ crazy = deserializer.parse row['crazy']
85
100
  crazy.should == { 'a' => '1' }
86
101
 
87
102
  upsert.row({:name => 'Bill'}, :crazy => {:a => nil})
88
103
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
89
- crazy = PgHstore.parse row['crazy']
104
+ crazy = deserializer.parse row['crazy']
90
105
  crazy.should == {}
91
106
 
92
107
  upsert.row({:name => 'Bill'}, :crazy => {:a => 1, :b => 5})
93
108
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
94
- crazy = PgHstore.parse row['crazy']
109
+ crazy = deserializer.parse row['crazy']
95
110
  crazy.should == { 'a' => '1', 'b' => '5' }
96
111
 
97
112
  upsert.row({:name => 'Bill'}, :crazy => {})
98
113
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
99
- crazy = PgHstore.parse row['crazy']
114
+ crazy = deserializer.parse row['crazy']
100
115
  crazy.should == { 'a' => '1', 'b' => '5' }
101
116
 
102
117
  upsert.row({:name => 'Bill'}, :crazy => {:a => nil})
103
118
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
104
- crazy = PgHstore.parse row['crazy']
119
+ crazy = deserializer.parse row['crazy']
105
120
  crazy.should == { 'b' => '5' }
106
121
 
107
122
  upsert.row({:name => 'Bill'}, :crazy => {:a => 1, :b => 5})
108
123
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
109
- crazy = PgHstore.parse row['crazy']
124
+ crazy = deserializer.parse row['crazy']
110
125
  crazy.should == { 'a' => '1', 'b' => '5' }
111
126
 
112
127
  upsert.row({:name => 'Bill'}, :crazy => {:a => nil, :b => nil, :c => 12})
113
128
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
114
- crazy = PgHstore.parse row['crazy']
129
+ crazy = deserializer.parse row['crazy']
115
130
  crazy.should == { 'c' => '12' }
116
131
  end
117
132
 
@@ -122,111 +137,111 @@ EOS
122
137
 
123
138
  upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => 1})
124
139
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
125
- crazy = PgHstore.parse row['crazy']
140
+ crazy = deserializer.parse row['crazy']
126
141
  crazy.should == { 'foo"bar' => '1' }
127
142
 
128
143
  upsert.row({:name => 'Bill'}, :crazy => {})
129
144
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
130
- crazy = PgHstore.parse row['crazy']
145
+ crazy = deserializer.parse row['crazy']
131
146
  crazy.should == { 'foo"bar' => '1' }
132
147
 
133
148
  upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => nil})
134
149
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
135
- crazy = PgHstore.parse row['crazy']
150
+ crazy = deserializer.parse row['crazy']
136
151
  crazy.should == {}
137
152
 
138
153
  upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => 1, :b => 5})
139
154
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
140
- crazy = PgHstore.parse row['crazy']
155
+ crazy = deserializer.parse row['crazy']
141
156
  crazy.should == { 'foo"bar' => '1', 'b' => '5' }
142
157
 
143
158
  upsert.row({:name => 'Bill'}, :crazy => {})
144
159
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
145
- crazy = PgHstore.parse row['crazy']
160
+ crazy = deserializer.parse row['crazy']
146
161
  crazy.should == { 'foo"bar' => '1', 'b' => '5' }
147
162
 
148
163
  upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => nil})
149
164
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
150
- crazy = PgHstore.parse row['crazy']
165
+ crazy = deserializer.parse row['crazy']
151
166
  crazy.should == { 'b' => '5' }
152
167
 
153
168
  upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => 1, :b => 5})
154
169
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
155
- crazy = PgHstore.parse row['crazy']
170
+ crazy = deserializer.parse row['crazy']
156
171
  crazy.should == { 'foo"bar' => '1', 'b' => '5' }
157
172
 
158
173
  upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => nil, :b => nil, :c => 12})
159
174
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
160
- crazy = PgHstore.parse row['crazy']
175
+ crazy = deserializer.parse row['crazy']
161
176
  crazy.should == { 'c' => '12' }
162
177
  end
163
178
 
164
179
  it "handles multiple hstores" do
165
180
  upsert.row({:name => 'Bill'}, :crazy => {:a => 1, :b => 9}, :cool => {:c => 12, :d => 19})
166
181
  row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
167
- crazy = PgHstore.parse row['crazy']
182
+ crazy = deserializer.parse row['crazy']
168
183
  crazy.should == { 'a' => '1', 'b' => '9' }
169
- cool = PgHstore.parse row['cool']
184
+ cool = deserializer.parse row['cool']
170
185
  cool.should == { 'c' => '12', 'd' => '19' }
171
186
  end
172
187
 
173
188
  it "can deletes keys from multiple hstores at once" do
174
189
  upsert.row({:name => 'Bill'}, :crazy => {:a => 1}, :cool => {5 => 9})
175
190
  row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
176
- crazy = PgHstore.parse row['crazy']
191
+ crazy = deserializer.parse row['crazy']
177
192
  crazy.should == { 'a' => '1' }
178
- cool = PgHstore.parse row['cool']
193
+ cool = deserializer.parse row['cool']
179
194
  cool.should == { '5' => '9' }
180
195
 
181
196
  # NOOP
182
197
  upsert.row({:name => 'Bill'}, :crazy => {}, :cool => {})
183
198
  row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
184
- crazy = PgHstore.parse row['crazy']
199
+ crazy = deserializer.parse row['crazy']
185
200
  crazy.should == { 'a' => '1' }
186
- cool = PgHstore.parse row['cool']
201
+ cool = deserializer.parse row['cool']
187
202
  cool.should == { '5' => '9' }
188
203
 
189
204
  upsert.row({:name => 'Bill'}, :crazy => {:a => nil}, :cool => {13 => 17})
190
205
  row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
191
- crazy = PgHstore.parse row['crazy']
206
+ crazy = deserializer.parse row['crazy']
192
207
  crazy.should == {}
193
- cool = PgHstore.parse row['cool']
208
+ cool = deserializer.parse row['cool']
194
209
  cool.should == { '5' => '9', '13' => '17' }
195
210
 
196
211
  upsert.row({:name => 'Bill'}, :crazy => {:a => 1, :b => 5})
197
212
  row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
198
- crazy = PgHstore.parse row['crazy']
213
+ crazy = deserializer.parse row['crazy']
199
214
  crazy.should == { 'a' => '1', 'b' => '5' }
200
215
 
201
216
  upsert.row({:name => 'Bill'}, :crazy => {:b => nil}, :cool => {5 => nil})
202
217
  row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
203
- crazy = PgHstore.parse row['crazy']
218
+ crazy = deserializer.parse row['crazy']
204
219
  crazy.should == {'a' => '1'}
205
- cool = PgHstore.parse row['cool']
220
+ cool = deserializer.parse row['cool']
206
221
  cool.should == {'13' => '17' }
207
222
  end
208
223
 
209
224
  it "deletes keys whether new or existing record" do
210
225
  upsert.row({:name => 'Bill'}, :crazy => {:z => 1, :x => nil})
211
226
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
212
- crazy = PgHstore.parse row['crazy']
227
+ crazy = deserializer.parse row['crazy']
213
228
  crazy.should == { 'z' => '1' }
214
229
 
215
230
  upsert.row({:name => 'Bill'}, :crazy => {:a => 1})
216
231
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
217
- crazy = PgHstore.parse row['crazy']
232
+ crazy = deserializer.parse row['crazy']
218
233
  crazy.should == { 'a' => '1', 'z' => '1' }
219
234
  end
220
235
 
221
236
  it "can turn off eager nullify" do
222
237
  upsert.row({:name => 'Bill'}, {:crazy => {:z => 1, :x => nil}}, :eager_nullify => false)
223
238
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
224
- crazy = PgHstore.parse row['crazy']
239
+ crazy = deserializer.parse row['crazy']
225
240
  crazy.should == { 'z' => '1', 'x' => nil }
226
241
 
227
242
  upsert.row({:name => 'Bill'}, :crazy => {:a => 1})
228
243
  row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
229
- crazy = PgHstore.parse row['crazy']
244
+ crazy = deserializer.parse row['crazy']
230
245
  crazy.should == { 'a' => '1', 'z' => '1', 'x' => nil}
231
246
  end
232
247