pg-hstore 1.1.6 → 1.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/pg_hstore.rb +69 -26
  2. metadata +5 -3
@@ -1,19 +1,25 @@
1
1
  module PgHstore
2
2
  SINGLE_QUOTE = "'"
3
+ E_SINGLE_QUOTE = "E'"
3
4
  DOUBLE_QUOTE = '"'
4
- DOLLAR_QUOTE = '$$' # TODO not infallible
5
5
  HASHROCKET = '=>'
6
6
  COMMA = ','
7
+ SLASH = '\\'
8
+
9
+ ESCAPED_CHAR = /\\(.)/
10
+ ESCAPED_SINGLE_QUOTE = '\\\''
11
+ ESCAPED_DOUBLE_QUOTE = '\\"'
12
+ ESCAPED_SLASH = '\\\\'
7
13
 
8
14
  QUOTED_LITERAL = /"[^"\\]*(?:\\.[^"\\]*)*"/
9
15
  UNQUOTED_LITERAL = /[^\s=,][^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
10
16
  LITERAL = /(#{QUOTED_LITERAL}|#{UNQUOTED_LITERAL})/
11
17
  PAIR = /#{LITERAL}\s*=>\s*#{LITERAL}/
12
18
  NULL = /\ANULL\z/i
19
+
13
20
  # set symbolize_keys = false if you want string keys
14
21
  # thanks to https://github.com/engageis/activerecord-postgres-hstore for regexps!
15
22
  def PgHstore.load(hstore, symbolize_keys = true)
16
- hstore = unquote hstore, DOLLAR_QUOTE
17
23
  hstore.scan(PAIR).inject({}) do |memo, (k, v)|
18
24
  k = unescape unquote(k, DOUBLE_QUOTE)
19
25
  k = k.to_sym if symbolize_keys
@@ -23,22 +29,28 @@ module PgHstore
23
29
  end
24
30
  end
25
31
 
26
- # set for_parameter = true if you're using the output for a bind variable
27
- def PgHstore.dump(hash, for_parameter = false)
28
- memo = hash.map do |k, v|
29
- if v.nil?
30
- v = "NULL"
31
- else
32
- v = DOUBLE_QUOTE + escape(v) + DOUBLE_QUOTE
33
- end
34
- k = DOUBLE_QUOTE + escape(k) + DOUBLE_QUOTE
35
- [k, v].join HASHROCKET
36
- end.join COMMA
37
- if for_parameter
38
- memo
39
- else
40
- DOLLAR_QUOTE + memo + DOLLAR_QUOTE
41
- end
32
+ # Serialize a hash to be sent to PostgreSQL as an hstore value.
33
+ #
34
+ # By default, returns a (Postgre)SQL string constant suitable for
35
+ # interpolating directly into a query. With raw_string = true,
36
+ # returns the plain string value suitable for use as a bind variable.
37
+ def PgHstore.dump(hash, raw_string = false)
38
+ # Per http://www.postgresql.org/docs/9.2/static/hstore.html :
39
+ #
40
+ # The text representation of an hstore, used for input and
41
+ # output, includes zero or more 'key => value' pairs separated
42
+ # by commas. [...] Whitespace between pairs or around the =>
43
+ # sign is ignored. Double-quote keys and values [... see
44
+ # escape_nonnull_for_hstore ...]
45
+ #
46
+ # A value (but not a key) can be an SQL NULL. Double-quote the
47
+ # NULL to treat it as the ordinary string "NULL".
48
+ hstore = hash.map do |k, v|
49
+ hstore_k = escape_nonnull_for_hstore(k)
50
+ hstore_v = (v.nil?) ? "NULL" : escape_nonnull_for_hstore(v)
51
+ [hstore_k, hstore_v].join(HASHROCKET)
52
+ end.join(COMMA)
53
+ raw_string ? hstore : as_postgresql_string_constant(hstore)
42
54
  end
43
55
 
44
56
  class << self
@@ -57,16 +69,47 @@ module PgHstore
57
69
  end
58
70
  end
59
71
 
60
- ESCAPED_CHAR = /\\(.)/
61
72
  def PgHstore.unescape(literal)
62
73
  literal.gsub ESCAPED_CHAR, '\1'
63
74
  end
64
-
65
- NON_ESCAPE_SLASH = '\\'
66
- ESCAPED_SLASH = '\\\\'
67
- ESCAPED_DOUBLE_QUOTE = '\"'
68
- def PgHstore.escape(string)
69
- string.to_s.gsub(NON_ESCAPE_SLASH) {ESCAPED_SLASH}.gsub DOUBLE_QUOTE, ESCAPED_DOUBLE_QUOTE
75
+
76
+ # Escape a value as a string to use as an hstore key or value.
77
+ #
78
+ # Per http://www.postgresql.org/docs/9.2/static/hstore.html :
79
+ #
80
+ # Double-quote keys and values that include [stuff]. To include a
81
+ # double quote or a backslash in a key or value, escape it with a
82
+ # backslash.
83
+ #
84
+ # You got it, boss.
85
+ def PgHstore.escape_nonnull_for_hstore(string)
86
+ interior = string.to_s.dup
87
+ interior.gsub!(SLASH) {ESCAPED_SLASH}
88
+ interior.gsub!(DOUBLE_QUOTE, ESCAPED_DOUBLE_QUOTE)
89
+ DOUBLE_QUOTE + interior + DOUBLE_QUOTE
70
90
  end
71
- end
72
91
 
92
+ # Escape a string as a string constant to be used in a SQL query
93
+ # to PostgreSQL.
94
+ #
95
+ # Ideally we would use plain SQL string constants, which are very simple:
96
+ # http://www.postgresql.org/docs/9.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS
97
+ # Unfortunately PostgreSQL treats these differently depending on the
98
+ # variable standard_conforming_strings, which defaulted to off until 9.1.
99
+ # It doesn't seem possible to generate them correctly for both cases at
100
+ # once, and trying to know the value of that variable and dispatch on it
101
+ # would be awful.
102
+ #
103
+ # Instead, use the slightly more cumbersome "escape" string constants:
104
+ # http://www.postgresql.org/docs/9.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-ESCAPE
105
+ # They're a little uglier and they're PostgreSQL-specific, but nobody has
106
+ # to see them and this whole module is PostgreSQL-specific. And, crucially,
107
+ # their behavior doesn't vary. Not allowing injection attacks: priceless.
108
+ # We don't use any of the fancy escapes, just neuter any backslashes and quotes.
109
+ def PgHstore.as_postgresql_string_constant(string)
110
+ interior = string.to_s.dup
111
+ interior.gsub!(SLASH) {ESCAPED_SLASH}
112
+ interior.gsub!(SINGLE_QUOTE) {ESCAPED_SINGLE_QUOTE}
113
+ E_SINGLE_QUOTE + interior + SINGLE_QUOTE
114
+ end
115
+ end
metadata CHANGED
@@ -1,16 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg-hstore
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.6
4
+ version: 1.1.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Peter van Hardenberg
9
9
  - Seamus Abshere
10
+ - Greg Price
10
11
  autorequire:
11
12
  bindir: bin
12
13
  cert_chain: []
13
- date: 2013-02-07 00:00:00.000000000 Z
14
+ date: 2013-02-20 00:00:00.000000000 Z
14
15
  dependencies:
15
16
  - !ruby/object:Gem::Dependency
16
17
  name: rspec
@@ -77,6 +78,7 @@ rubyforge_project:
77
78
  rubygems_version: 1.8.25
78
79
  signing_key:
79
80
  specification_version: 3
80
- summary: ''
81
+ summary: postgresql hstore parser/deparser - provides PgHstore.dump and PgHstore.load
82
+ (aka parse)
81
83
  test_files: []
82
84
  has_rdoc: