pg 0.18.4 → 1.2.3

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 (85) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/BSDL +2 -2
  5. data/ChangeLog +0 -5911
  6. data/History.rdoc +240 -0
  7. data/Manifest.txt +8 -20
  8. data/README-Windows.rdoc +4 -4
  9. data/README.ja.rdoc +1 -2
  10. data/README.rdoc +64 -15
  11. data/Rakefile +20 -21
  12. data/Rakefile.cross +67 -69
  13. data/ext/errorcodes.def +101 -0
  14. data/ext/errorcodes.rb +1 -1
  15. data/ext/errorcodes.txt +33 -2
  16. data/ext/extconf.rb +26 -36
  17. data/ext/gvl_wrappers.c +4 -0
  18. data/ext/gvl_wrappers.h +27 -39
  19. data/ext/pg.c +156 -145
  20. data/ext/pg.h +74 -98
  21. data/ext/pg_binary_decoder.c +82 -15
  22. data/ext/pg_binary_encoder.c +20 -19
  23. data/ext/pg_coder.c +103 -21
  24. data/ext/pg_connection.c +917 -523
  25. data/ext/pg_copy_coder.c +50 -12
  26. data/ext/pg_record_coder.c +491 -0
  27. data/ext/pg_result.c +590 -208
  28. data/ext/pg_text_decoder.c +606 -40
  29. data/ext/pg_text_encoder.c +245 -94
  30. data/ext/pg_tuple.c +549 -0
  31. data/ext/pg_type_map.c +14 -7
  32. data/ext/pg_type_map_all_strings.c +4 -4
  33. data/ext/pg_type_map_by_class.c +9 -4
  34. data/ext/pg_type_map_by_column.c +7 -6
  35. data/ext/pg_type_map_by_mri_type.c +1 -1
  36. data/ext/pg_type_map_by_oid.c +3 -2
  37. data/ext/pg_type_map_in_ruby.c +1 -1
  38. data/ext/{util.c → pg_util.c} +10 -10
  39. data/ext/{util.h → pg_util.h} +2 -2
  40. data/lib/pg.rb +23 -13
  41. data/lib/pg/basic_type_mapping.rb +155 -32
  42. data/lib/pg/binary_decoder.rb +23 -0
  43. data/lib/pg/coder.rb +23 -2
  44. data/lib/pg/connection.rb +73 -13
  45. data/lib/pg/constants.rb +2 -1
  46. data/lib/pg/exceptions.rb +2 -1
  47. data/lib/pg/result.rb +24 -7
  48. data/lib/pg/text_decoder.rb +24 -22
  49. data/lib/pg/text_encoder.rb +40 -8
  50. data/lib/pg/tuple.rb +30 -0
  51. data/lib/pg/type_map_by_column.rb +3 -2
  52. data/spec/helpers.rb +61 -36
  53. data/spec/pg/basic_type_mapping_spec.rb +415 -36
  54. data/spec/pg/connection_spec.rb +732 -327
  55. data/spec/pg/connection_sync_spec.rb +41 -0
  56. data/spec/pg/result_spec.rb +253 -21
  57. data/spec/pg/tuple_spec.rb +333 -0
  58. data/spec/pg/type_map_by_class_spec.rb +4 -4
  59. data/spec/pg/type_map_by_column_spec.rb +6 -2
  60. data/spec/pg/type_map_by_mri_type_spec.rb +2 -2
  61. data/spec/pg/type_map_by_oid_spec.rb +3 -3
  62. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  63. data/spec/pg/type_map_spec.rb +1 -1
  64. data/spec/pg/type_spec.rb +446 -20
  65. data/spec/pg_spec.rb +2 -2
  66. metadata +63 -72
  67. metadata.gz.sig +0 -0
  68. data/sample/array_insert.rb +0 -20
  69. data/sample/async_api.rb +0 -106
  70. data/sample/async_copyto.rb +0 -39
  71. data/sample/async_mixed.rb +0 -56
  72. data/sample/check_conn.rb +0 -21
  73. data/sample/copyfrom.rb +0 -81
  74. data/sample/copyto.rb +0 -19
  75. data/sample/cursor.rb +0 -21
  76. data/sample/disk_usage_report.rb +0 -186
  77. data/sample/issue-119.rb +0 -94
  78. data/sample/losample.rb +0 -69
  79. data/sample/minimal-testcase.rb +0 -17
  80. data/sample/notify_wait.rb +0 -72
  81. data/sample/pg_statistics.rb +0 -294
  82. data/sample/replication_monitor.rb +0 -231
  83. data/sample/test_binary_values.rb +0 -33
  84. data/sample/wal_shipper.rb +0 -434
  85. data/sample/warehouse_partitions.rb +0 -320
@@ -0,0 +1,23 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ module PG
5
+ module BinaryDecoder
6
+ # Convenience classes for timezone options
7
+ class TimestampUtc < Timestamp
8
+ def initialize(params={})
9
+ super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_UTC))
10
+ end
11
+ end
12
+ class TimestampUtcToLocal < Timestamp
13
+ def initialize(params={})
14
+ super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL))
15
+ end
16
+ end
17
+ class TimestampLocal < Timestamp
18
+ def initialize(params={})
19
+ super(params.merge(flags: PG::Coder::TIMESTAMP_DB_LOCAL | PG::Coder::TIMESTAMP_APP_LOCAL))
20
+ end
21
+ end
22
+ end
23
+ end # module PG
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
 
3
4
  module PG
4
5
 
@@ -28,6 +29,7 @@ module PG
28
29
  {
29
30
  oid: oid,
30
31
  format: format,
32
+ flags: flags,
31
33
  name: name,
32
34
  }
33
35
  end
@@ -52,6 +54,18 @@ module PG
52
54
  str[-1,0] = "#{name_str} #{oid_str}#{format_str}"
53
55
  str
54
56
  end
57
+
58
+ def inspect_short
59
+ str = case format
60
+ when 0 then "T"
61
+ when 1 then "B"
62
+ else format.to_s
63
+ end
64
+ str += "E" if respond_to?(:encode)
65
+ str += "D" if respond_to?(:decode)
66
+
67
+ "#{name || self.class.name}:#{str}"
68
+ end
55
69
  end
56
70
 
57
71
  class CompositeCoder < Coder
@@ -79,5 +93,12 @@ module PG
79
93
  })
80
94
  end
81
95
  end
82
- end # module PG
83
96
 
97
+ class RecordCoder < Coder
98
+ def to_h
99
+ super.merge!({
100
+ type_map: type_map,
101
+ })
102
+ end
103
+ end
104
+ end # module PG
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pg' unless defined?( PG )
4
5
  require 'uri'
@@ -47,7 +48,7 @@ class PG::Connection
47
48
 
48
49
  if args.length == 1
49
50
  case args.first
50
- when URI, URI.regexp
51
+ when URI, /\A#{URI::ABS_URI_REF}\z/
51
52
  uri = URI(args.first)
52
53
  options.merge!( Hash[URI.decode_www_form( uri.query )] ) if uri.query
53
54
  when /=/
@@ -85,7 +86,7 @@ class PG::Connection
85
86
 
86
87
 
87
88
  # call-seq:
88
- # conn.copy_data( sql ) {|sql_result| ... } -> PG::Result
89
+ # conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
89
90
  #
90
91
  # Execute a copy process for transfering data to or from the server.
91
92
  #
@@ -109,13 +110,26 @@ class PG::Connection
109
110
  # of blocking mode of operation, #copy_data is preferred to raw calls
110
111
  # of #put_copy_data, #get_copy_data and #put_copy_end.
111
112
  #
113
+ # _coder_ can be a PG::Coder derivation
114
+ # (typically PG::TextEncoder::CopyRow or PG::TextDecoder::CopyRow).
115
+ # This enables encoding of data fields given to #put_copy_data
116
+ # or decoding of fields received by #get_copy_data.
117
+ #
112
118
  # Example with CSV input format:
113
- # conn.exec "create table my_table (a text,b text,c text,d text,e text)"
119
+ # conn.exec "create table my_table (a text,b text,c text,d text)"
114
120
  # conn.copy_data "COPY my_table FROM STDIN CSV" do
115
- # conn.put_copy_data "some,csv,data,to,copy\n"
116
- # conn.put_copy_data "more,csv,data,to,copy\n"
121
+ # conn.put_copy_data "some,data,to,copy\n"
122
+ # conn.put_copy_data "more,data,to,copy\n"
123
+ # end
124
+ # This creates +my_table+ and inserts two CSV rows.
125
+ #
126
+ # The same with text format encoder PG::TextEncoder::CopyRow
127
+ # and Array input:
128
+ # enco = PG::TextEncoder::CopyRow.new
129
+ # conn.copy_data "COPY my_table FROM STDIN", enco do
130
+ # conn.put_copy_data ['some', 'data', 'to', 'copy']
131
+ # conn.put_copy_data ['more', 'data', 'to', 'copy']
117
132
  # end
118
- # This creates +my_table+ and inserts two rows.
119
133
  #
120
134
  # Example with CSV output format:
121
135
  # conn.copy_data "COPY my_table TO STDOUT CSV" do
@@ -124,8 +138,21 @@ class PG::Connection
124
138
  # end
125
139
  # end
126
140
  # This prints all rows of +my_table+ to stdout:
127
- # "some,csv,data,to,copy\n"
128
- # "more,csv,data,to,copy\n"
141
+ # "some,data,to,copy\n"
142
+ # "more,data,to,copy\n"
143
+ #
144
+ # The same with text format decoder PG::TextDecoder::CopyRow
145
+ # and Array output:
146
+ # deco = PG::TextDecoder::CopyRow.new
147
+ # conn.copy_data "COPY my_table TO STDOUT", deco do
148
+ # while row=conn.get_copy_data
149
+ # p row
150
+ # end
151
+ # end
152
+ # This receives all rows of +my_table+ as ruby array:
153
+ # ["some", "data", "to", "copy"]
154
+ # ["more", "data", "to", "copy"]
155
+
129
156
  def copy_data( sql, coder=nil )
130
157
  res = exec( sql )
131
158
 
@@ -165,7 +192,7 @@ class PG::Connection
165
192
  raise
166
193
  else
167
194
  res = get_last_result
168
- if res.result_status != PGRES_COMMAND_OK
195
+ if !res || res.result_status != PGRES_COMMAND_OK
169
196
  while get_copy_data
170
197
  end
171
198
  while get_result
@@ -224,8 +251,41 @@ class PG::Connection
224
251
  end
225
252
  end
226
253
 
227
- end # class PG::Connection
254
+ # Method 'ssl_attribute' was introduced in PostgreSQL 9.5.
255
+ if self.instance_methods.find{|m| m.to_sym == :ssl_attribute }
256
+ # call-seq:
257
+ # conn.ssl_attributes -> Hash<String,String>
258
+ #
259
+ # Returns SSL-related information about the connection as key/value pairs
260
+ #
261
+ # The available attributes varies depending on the SSL library being used,
262
+ # and the type of connection.
263
+ #
264
+ # See also #ssl_attribute
265
+ def ssl_attributes
266
+ ssl_attribute_names.each.with_object({}) do |n,h|
267
+ h[n] = ssl_attribute(n)
268
+ end
269
+ end
270
+ end
228
271
 
229
- # Backward-compatible alias
230
- PGconn = PG::Connection
272
+ REDIRECT_METHODS = {
273
+ :exec => [:async_exec, :sync_exec],
274
+ :query => [:async_exec, :sync_exec],
275
+ :exec_params => [:async_exec_params, :sync_exec_params],
276
+ :prepare => [:async_prepare, :sync_prepare],
277
+ :exec_prepared => [:async_exec_prepared, :sync_exec_prepared],
278
+ :describe_portal => [:async_describe_portal, :sync_describe_portal],
279
+ :describe_prepared => [:async_describe_prepared, :sync_describe_prepared],
280
+ }
231
281
 
282
+ def self.async_api=(enable)
283
+ REDIRECT_METHODS.each do |ali, (async, sync)|
284
+ remove_method(ali) if method_defined?(ali)
285
+ alias_method( ali, enable ? async : sync )
286
+ end
287
+ end
288
+
289
+ # pg-1.1.0+ defaults to libpq's async API for query related blocking methods
290
+ self.async_api = true
291
+ end # class PG::Connection
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pg' unless defined?( PG )
4
5
 
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pg' unless defined?( PG )
4
5
 
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pg' unless defined?( PG )
4
5
 
@@ -9,18 +10,34 @@ class PG::Result
9
10
  #
10
11
  # +type_map+: a PG::TypeMap instance.
11
12
  #
12
- # See PG::BasicTypeMapForResults
13
+ # This method is equal to #type_map= , but returns self, so that calls can be chained.
14
+ #
15
+ # See also PG::BasicTypeMapForResults
13
16
  def map_types!(type_map)
14
17
  self.type_map = type_map
15
- self
18
+ return self
19
+ end
20
+
21
+ # Set the data type for all field name returning methods.
22
+ #
23
+ # +type+: a Symbol defining the field name type.
24
+ #
25
+ # This method is equal to #field_name_type= , but returns self, so that calls can be chained.
26
+ def field_names_as(type)
27
+ self.field_name_type = type
28
+ return self
16
29
  end
17
30
 
31
+ ### Return a String representation of the object suitable for debugging.
18
32
  def inspect
19
33
  str = self.to_s
20
- str[-1,0] = " status=#{res_status(result_status)} ntuples=#{ntuples} nfields=#{nfields} cmd_tuples=#{cmd_tuples}"
21
- str
34
+ str[-1,0] = if cleared?
35
+ " cleared"
36
+ else
37
+ " status=#{res_status(result_status)} ntuples=#{ntuples} nfields=#{nfields} cmd_tuples=#{cmd_tuples}"
38
+ end
39
+ return str
22
40
  end
41
+
23
42
  end # class PG::Result
24
43
 
25
- # Backward-compatible alias
26
- PGresult = PG::Result
@@ -1,14 +1,14 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'date'
5
+ require 'json'
4
6
 
5
7
  module PG
6
8
  module TextDecoder
7
9
  class Date < SimpleDecoder
8
- ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
9
-
10
10
  def decode(string, tuple=nil, field=nil)
11
- if string =~ ISO_DATE
11
+ if string =~ /\A(\d{4})-(\d\d)-(\d\d)\z/
12
12
  ::Date.new $1.to_i, $2.to_i, $3.to_i
13
13
  else
14
14
  string
@@ -16,29 +16,31 @@ module PG
16
16
  end
17
17
  end
18
18
 
19
- class TimestampWithoutTimeZone < SimpleDecoder
20
- ISO_DATETIME_WITHOUT_TIMEZONE = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
21
-
19
+ class JSON < SimpleDecoder
22
20
  def decode(string, tuple=nil, field=nil)
23
- if string =~ ISO_DATETIME_WITHOUT_TIMEZONE
24
- Time.new $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, "#{$6}#{$7}".to_r
25
- else
26
- string
27
- end
21
+ ::JSON.parse(string, quirks_mode: true)
28
22
  end
29
23
  end
30
24
 
31
- class TimestampWithTimeZone < SimpleDecoder
32
- ISO_DATETIME_WITH_TIMEZONE = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?([-\+]\d\d):?(\d\d)?:?(\d\d)?\z/
33
-
34
- def decode(string, tuple=nil, field=nil)
35
- if string =~ ISO_DATETIME_WITH_TIMEZONE
36
- Time.new $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, "#{$6}#{$7}".to_r, "#{$8}:#{$9 || '00'}:#{$10 || '00'}"
37
- else
38
- string
39
- end
25
+ # Convenience classes for timezone options
26
+ class TimestampUtc < Timestamp
27
+ def initialize(params={})
28
+ super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_UTC))
29
+ end
30
+ end
31
+ class TimestampUtcToLocal < Timestamp
32
+ def initialize(params={})
33
+ super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL))
40
34
  end
41
35
  end
36
+ class TimestampLocal < Timestamp
37
+ def initialize(params={})
38
+ super(params.merge(flags: PG::Coder::TIMESTAMP_DB_LOCAL | PG::Coder::TIMESTAMP_APP_LOCAL))
39
+ end
40
+ end
41
+
42
+ # For backward compatibility:
43
+ TimestampWithoutTimeZone = TimestampLocal
44
+ TimestampWithTimeZone = Timestamp
42
45
  end
43
46
  end # module PG
44
-
@@ -1,27 +1,59 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'json'
5
+ require 'ipaddr'
2
6
 
3
7
  module PG
4
8
  module TextEncoder
5
9
  class Date < SimpleEncoder
6
- STRFTIME_ISO_DATE = "%Y-%m-%d".freeze
7
10
  def encode(value)
8
- value.respond_to?(:strftime) ? value.strftime(STRFTIME_ISO_DATE) : value
11
+ value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d") : value
9
12
  end
10
13
  end
11
14
 
12
15
  class TimestampWithoutTimeZone < SimpleEncoder
13
- STRFTIME_ISO_DATETIME_WITHOUT_TIMEZONE = "%Y-%m-%d %H:%M:%S.%N".freeze
14
16
  def encode(value)
15
- value.respond_to?(:strftime) ? value.strftime(STRFTIME_ISO_DATETIME_WITHOUT_TIMEZONE) : value
17
+ value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S.%N") : value
18
+ end
19
+ end
20
+
21
+ class TimestampUtc < SimpleEncoder
22
+ def encode(value)
23
+ value.respond_to?(:utc) ? value.utc.strftime("%Y-%m-%d %H:%M:%S.%N") : value
16
24
  end
17
25
  end
18
26
 
19
27
  class TimestampWithTimeZone < SimpleEncoder
20
- STRFTIME_ISO_DATETIME_WITH_TIMEZONE = "%Y-%m-%d %H:%M:%S.%N %:z".freeze
21
28
  def encode(value)
22
- value.respond_to?(:strftime) ? value.strftime(STRFTIME_ISO_DATETIME_WITH_TIMEZONE) : value
29
+ value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S.%N %:z") : value
30
+ end
31
+ end
32
+
33
+ class JSON < SimpleEncoder
34
+ def encode(value)
35
+ ::JSON.generate(value, quirks_mode: true)
36
+ end
37
+ end
38
+
39
+ class Inet < SimpleEncoder
40
+ def encode(value)
41
+ case value
42
+ when IPAddr
43
+ default_prefix = (value.family == Socket::AF_INET ? 32 : 128)
44
+ s = value.to_s
45
+ if value.respond_to?(:prefix)
46
+ prefix = value.prefix
47
+ else
48
+ range = value.to_range
49
+ prefix = default_prefix - Math.log(((range.end.to_i - range.begin.to_i) + 1), 2).to_i
50
+ end
51
+ s << "/" << prefix.to_s if prefix != default_prefix
52
+ s
53
+ else
54
+ value
55
+ end
23
56
  end
24
57
  end
25
58
  end
26
59
  end # module PG
27
-
@@ -0,0 +1,30 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'pg' unless defined?( PG )
5
+
6
+
7
+ class PG::Tuple
8
+
9
+ ### Return a String representation of the object suitable for debugging.
10
+ def inspect
11
+ "#<#{self.class} #{self.map{|k,v| "#{k}: #{v.inspect}" }.join(", ") }>"
12
+ end
13
+
14
+ def has_key?(key)
15
+ field_map.has_key?(key)
16
+ end
17
+ alias key? has_key?
18
+
19
+ def keys
20
+ field_names || field_map.keys.freeze
21
+ end
22
+
23
+ def each_key(&block)
24
+ if fn=field_names
25
+ fn.each(&block)
26
+ else
27
+ field_map.each_key(&block)
28
+ end
29
+ end
30
+ end
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pg' unless defined?( PG )
4
5
 
@@ -9,7 +10,7 @@ class PG::TypeMapByColumn
9
10
  end
10
11
 
11
12
  def inspect
12
- type_strings = coders.map{|c| c ? "#{c.name}:#{c.format}" : 'nil' }
13
+ type_strings = coders.map{|c| c ? c.inspect_short : 'nil' }
13
14
  "#<#{self.class} #{type_strings.join(' ')}>"
14
15
  end
15
16
  end