pg 0.17.1 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/BSDL +2 -2
  4. data/ChangeLog +0 -3506
  5. data/History.rdoc +308 -0
  6. data/Manifest.txt +35 -19
  7. data/README-Windows.rdoc +17 -28
  8. data/README.ja.rdoc +1 -2
  9. data/README.rdoc +113 -14
  10. data/Rakefile +67 -30
  11. data/Rakefile.cross +109 -83
  12. data/ext/errorcodes.def +101 -0
  13. data/ext/errorcodes.rb +1 -1
  14. data/ext/errorcodes.txt +33 -2
  15. data/ext/extconf.rb +55 -58
  16. data/ext/gvl_wrappers.c +4 -0
  17. data/ext/gvl_wrappers.h +27 -39
  18. data/ext/pg.c +262 -130
  19. data/ext/pg.h +266 -54
  20. data/ext/pg_binary_decoder.c +229 -0
  21. data/ext/pg_binary_encoder.c +163 -0
  22. data/ext/pg_coder.c +561 -0
  23. data/ext/pg_connection.c +1689 -990
  24. data/ext/pg_copy_coder.c +599 -0
  25. data/ext/pg_errors.c +6 -0
  26. data/ext/pg_record_coder.c +491 -0
  27. data/ext/pg_result.c +897 -164
  28. data/ext/pg_text_decoder.c +987 -0
  29. data/ext/pg_text_encoder.c +814 -0
  30. data/ext/pg_tuple.c +549 -0
  31. data/ext/pg_type_map.c +166 -0
  32. data/ext/pg_type_map_all_strings.c +116 -0
  33. data/ext/pg_type_map_by_class.c +244 -0
  34. data/ext/pg_type_map_by_column.c +313 -0
  35. data/ext/pg_type_map_by_mri_type.c +284 -0
  36. data/ext/pg_type_map_by_oid.c +356 -0
  37. data/ext/pg_type_map_in_ruby.c +299 -0
  38. data/ext/pg_util.c +149 -0
  39. data/ext/pg_util.h +65 -0
  40. data/lib/pg/basic_type_mapping.rb +522 -0
  41. data/lib/pg/binary_decoder.rb +23 -0
  42. data/lib/pg/coder.rb +104 -0
  43. data/lib/pg/connection.rb +153 -41
  44. data/lib/pg/constants.rb +2 -1
  45. data/lib/pg/exceptions.rb +2 -1
  46. data/lib/pg/result.rb +33 -6
  47. data/lib/pg/text_decoder.rb +46 -0
  48. data/lib/pg/text_encoder.rb +59 -0
  49. data/lib/pg/tuple.rb +30 -0
  50. data/lib/pg/type_map_by_column.rb +16 -0
  51. data/lib/pg.rb +29 -9
  52. data/spec/{lib/helpers.rb → helpers.rb} +151 -64
  53. data/spec/pg/basic_type_mapping_spec.rb +630 -0
  54. data/spec/pg/connection_spec.rb +1180 -477
  55. data/spec/pg/connection_sync_spec.rb +41 -0
  56. data/spec/pg/result_spec.rb +456 -120
  57. data/spec/pg/tuple_spec.rb +333 -0
  58. data/spec/pg/type_map_by_class_spec.rb +138 -0
  59. data/spec/pg/type_map_by_column_spec.rb +226 -0
  60. data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
  61. data/spec/pg/type_map_by_oid_spec.rb +149 -0
  62. data/spec/pg/type_map_in_ruby_spec.rb +164 -0
  63. data/spec/pg/type_map_spec.rb +22 -0
  64. data/spec/pg/type_spec.rb +1123 -0
  65. data/spec/pg_spec.rb +26 -20
  66. data.tar.gz.sig +0 -0
  67. metadata +148 -91
  68. metadata.gz.sig +0 -0
  69. data/sample/array_insert.rb +0 -20
  70. data/sample/async_api.rb +0 -106
  71. data/sample/async_copyto.rb +0 -39
  72. data/sample/async_mixed.rb +0 -56
  73. data/sample/check_conn.rb +0 -21
  74. data/sample/copyfrom.rb +0 -81
  75. data/sample/copyto.rb +0 -19
  76. data/sample/cursor.rb +0 -21
  77. data/sample/disk_usage_report.rb +0 -186
  78. data/sample/issue-119.rb +0 -94
  79. data/sample/losample.rb +0 -69
  80. data/sample/minimal-testcase.rb +0 -17
  81. data/sample/notify_wait.rb +0 -72
  82. data/sample/pg_statistics.rb +0 -294
  83. data/sample/replication_monitor.rb +0 -231
  84. data/sample/test_binary_values.rb +0 -33
  85. data/sample/wal_shipper.rb +0 -434
  86. 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
data/lib/pg/coder.rb ADDED
@@ -0,0 +1,104 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ module PG
5
+
6
+ class Coder
7
+
8
+ module BinaryFormatting
9
+ Params = { format: 1 }
10
+ def initialize( params={} )
11
+ super(params.merge(Params))
12
+ end
13
+ end
14
+
15
+
16
+ # Create a new coder object based on the attribute Hash.
17
+ def initialize(params={})
18
+ params.each do |key, val|
19
+ send("#{key}=", val)
20
+ end
21
+ end
22
+
23
+ def dup
24
+ self.class.new(to_h)
25
+ end
26
+
27
+ # Returns coder attributes as Hash.
28
+ def to_h
29
+ {
30
+ oid: oid,
31
+ format: format,
32
+ flags: flags,
33
+ name: name,
34
+ }
35
+ end
36
+
37
+ def ==(v)
38
+ self.class == v.class && to_h == v.to_h
39
+ end
40
+
41
+ def marshal_dump
42
+ Marshal.dump(to_h)
43
+ end
44
+
45
+ def marshal_load(str)
46
+ initialize Marshal.load(str)
47
+ end
48
+
49
+ def inspect
50
+ str = self.to_s
51
+ oid_str = " oid=#{oid}" unless oid==0
52
+ format_str = " format=#{format}" unless format==0
53
+ name_str = " #{name.inspect}" if name
54
+ str[-1,0] = "#{name_str} #{oid_str}#{format_str}"
55
+ str
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
69
+ end
70
+
71
+ class CompositeCoder < Coder
72
+ def to_h
73
+ super.merge!({
74
+ elements_type: elements_type,
75
+ needs_quotation: needs_quotation?,
76
+ delimiter: delimiter,
77
+ })
78
+ end
79
+
80
+ def inspect
81
+ str = super
82
+ str[-1,0] = " elements_type=#{elements_type.inspect} #{needs_quotation? ? 'needs' : 'no'} quotation"
83
+ str
84
+ end
85
+ end
86
+
87
+ class CopyCoder < Coder
88
+ def to_h
89
+ super.merge!({
90
+ type_map: type_map,
91
+ delimiter: delimiter,
92
+ null_string: null_string,
93
+ })
94
+ end
95
+ end
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
data/lib/pg/connection.rb CHANGED
@@ -1,6 +1,8 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pg' unless defined?( PG )
5
+ require 'uri'
4
6
 
5
7
  # The PostgreSQL connection class. The interface for this class is based on
6
8
  # {libpq}[http://www.postgresql.org/docs/9.2/interactive/libpq.html], the C
@@ -34,48 +36,57 @@ class PG::Connection
34
36
  def self::parse_connect_args( *args )
35
37
  return '' if args.empty?
36
38
 
37
- # This will be swapped soon for code that makes options like those required for
38
- # PQconnectdbParams()/PQconnectStartParams(). For now, stick to an options string for
39
- # PQconnectdb()/PQconnectStart().
39
+ hash_arg = args.last.is_a?( Hash ) ? args.pop : {}
40
+ option_string = ''
41
+ options = {}
40
42
 
41
43
  # Parameter 'fallback_application_name' was introduced in PostgreSQL 9.0
42
44
  # together with PQescapeLiteral().
43
- if PG::Connection.instance_methods.find{|m| m.to_sym == :escape_literal }
44
- appname = $0.sub(/^(.{30}).{4,}(.{30})$/){ $1+"..."+$2 }
45
- appname = PG::Connection.quote_connstr( appname )
46
- connopts = ["fallback_application_name=#{appname}"]
47
- else
48
- connopts = []
45
+ if PG::Connection.instance_methods.find {|m| m.to_sym == :escape_literal }
46
+ options[:fallback_application_name] = $0.sub( /^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
49
47
  end
50
48
 
51
- # Handle an options hash first
52
- if args.last.is_a?( Hash )
53
- opthash = args.pop
54
- opthash.each do |key, val|
55
- connopts.push( "%s=%s" % [key, PG::Connection.quote_connstr(val)] )
49
+ if args.length == 1
50
+ case args.first
51
+ when URI, /\A#{URI::ABS_URI_REF}\z/
52
+ uri = URI(args.first)
53
+ options.merge!( Hash[URI.decode_www_form( uri.query )] ) if uri.query
54
+ when /=/
55
+ # Option string style
56
+ option_string = args.first.to_s
57
+ else
58
+ # Positional parameters
59
+ options[CONNECT_ARGUMENT_ORDER.first.to_sym] = args.first
56
60
  end
57
- end
58
-
59
- # Option string style
60
- if args.length == 1 && args.first.to_s.index( '=' )
61
- connopts.unshift( args.first )
62
-
63
- # Append positional parameters
64
61
  else
65
- args.each_with_index do |val, i|
66
- next unless val # Skip nil placeholders
62
+ max = CONNECT_ARGUMENT_ORDER.length
63
+ raise ArgumentError,
64
+ "Extra positional parameter %d: %p" % [ max + 1, args[max] ] if args.length > max
67
65
 
68
- key = CONNECT_ARGUMENT_ORDER[ i ] or
69
- raise ArgumentError, "Extra positional parameter %d: %p" % [ i+1, val ]
70
- connopts.push( "%s=%s" % [key, PG::Connection.quote_connstr(val.to_s)] )
66
+ CONNECT_ARGUMENT_ORDER.zip( args ) do |(k,v)|
67
+ options[ k.to_sym ] = v if v
71
68
  end
72
69
  end
73
70
 
74
- return connopts.join(' ')
71
+ options.merge!( hash_arg )
72
+
73
+ if uri
74
+ uri.host = nil if options[:host]
75
+ uri.port = nil if options[:port]
76
+ uri.user = nil if options[:user]
77
+ uri.password = nil if options[:password]
78
+ uri.path = '' if options[:dbname]
79
+ uri.query = URI.encode_www_form( options )
80
+ return uri.to_s.sub( /^#{uri.scheme}:(?!\/\/)/, "#{uri.scheme}://" )
81
+ else
82
+ option_string += ' ' unless option_string.empty? && options.empty?
83
+ return option_string + options.map { |k,v| "#{k}=#{quote_connstr(v)}" }.join( ' ' )
84
+ end
75
85
  end
76
86
 
87
+
77
88
  # call-seq:
78
- # conn.copy_data( sql ) {|sql_result| ... } -> PG::Result
89
+ # conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
79
90
  #
80
91
  # Execute a copy process for transfering data to or from the server.
81
92
  #
@@ -99,13 +110,26 @@ class PG::Connection
99
110
  # of blocking mode of operation, #copy_data is preferred to raw calls
100
111
  # of #put_copy_data, #get_copy_data and #put_copy_end.
101
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
+ #
102
118
  # Example with CSV input format:
103
- # conn.exec "create table my_table (a text,b text,c text,d text,e text)"
104
- # conn.copy_data "COPY my_table FROM STDOUT CSV" do
105
- # conn.put_copy_data "some,csv,data,to,copy\n"
106
- # conn.put_copy_data "more,csv,data,to,copy\n"
119
+ # conn.exec "create table my_table (a text,b text,c text,d text)"
120
+ # conn.copy_data "COPY my_table FROM STDIN CSV" do
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']
107
132
  # end
108
- # This creates +my_table+ and inserts two rows.
109
133
  #
110
134
  # Example with CSV output format:
111
135
  # conn.copy_data "COPY my_table TO STDOUT CSV" do
@@ -114,14 +138,31 @@ class PG::Connection
114
138
  # end
115
139
  # end
116
140
  # This prints all rows of +my_table+ to stdout:
117
- # "some,csv,data,to,copy\n"
118
- # "more,csv,data,to,copy\n"
119
- def copy_data( sql )
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
+
156
+ def copy_data( sql, coder=nil )
120
157
  res = exec( sql )
121
158
 
122
159
  case res.result_status
123
160
  when PGRES_COPY_IN
124
161
  begin
162
+ if coder
163
+ old_coder = self.encoder_for_put_copy_data
164
+ self.encoder_for_put_copy_data = coder
165
+ end
125
166
  yield res
126
167
  rescue Exception => err
127
168
  errmsg = "%s while copy data: %s" % [ err.class.name, err.message ]
@@ -131,10 +172,16 @@ class PG::Connection
131
172
  else
132
173
  put_copy_end
133
174
  get_last_result
175
+ ensure
176
+ self.encoder_for_put_copy_data = old_coder if coder
134
177
  end
135
178
 
136
179
  when PGRES_COPY_OUT
137
180
  begin
181
+ if coder
182
+ old_coder = self.decoder_for_get_copy_data
183
+ self.decoder_for_get_copy_data = coder
184
+ end
138
185
  yield res
139
186
  rescue Exception => err
140
187
  cancel
@@ -145,7 +192,7 @@ class PG::Connection
145
192
  raise
146
193
  else
147
194
  res = get_last_result
148
- if res.result_status != PGRES_COMMAND_OK
195
+ if !res || res.result_status != PGRES_COMMAND_OK
149
196
  while get_copy_data
150
197
  end
151
198
  while get_result
@@ -153,6 +200,8 @@ class PG::Connection
153
200
  raise PG::NotAllCopyDataRetrieved, "Not all COPY data retrieved"
154
201
  end
155
202
  res
203
+ ensure
204
+ self.decoder_for_get_copy_data = old_coder if coder
156
205
  end
157
206
 
158
207
  else
@@ -172,8 +221,71 @@ class PG::Connection
172
221
  return self.class.conndefaults
173
222
  end
174
223
 
175
- end # class PG::Connection
224
+ ### Return the Postgres connection defaults structure as a Hash keyed by option
225
+ ### keyword (as a Symbol).
226
+ ###
227
+ ### See also #conndefaults
228
+ def self.conndefaults_hash
229
+ return self.conndefaults.each_with_object({}) do |info, hash|
230
+ hash[ info[:keyword].to_sym ] = info[:val]
231
+ end
232
+ end
233
+
234
+ ### Returns a Hash with connection defaults. See ::conndefaults_hash
235
+ ### for details.
236
+ def conndefaults_hash
237
+ return self.class.conndefaults_hash
238
+ end
239
+
240
+ # Method 'conninfo' was introduced in PostgreSQL 9.3.
241
+ if self.instance_methods.find{|m| m.to_sym == :conninfo }
176
242
 
177
- # Backward-compatible alias
178
- PGconn = PG::Connection
243
+ ### Return the Postgres connection info structure as a Hash keyed by option
244
+ ### keyword (as a Symbol).
245
+ ###
246
+ ### See also #conninfo
247
+ def conninfo_hash
248
+ return self.conninfo.each_with_object({}) do |info, hash|
249
+ hash[ info[:keyword].to_sym ] = info[:val]
250
+ end
251
+ end
252
+ end
179
253
 
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
271
+
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
+ }
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
data/lib/pg/constants.rb CHANGED
@@ -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
 
data/lib/pg/exceptions.rb CHANGED
@@ -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
 
data/lib/pg/result.rb CHANGED
@@ -1,16 +1,43 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pg' unless defined?( PG )
4
5
 
5
6
 
6
7
  class PG::Result
7
8
 
8
- ### Returns all tuples as an array of arrays
9
- def values
10
- return enum_for(:each_row).to_a
9
+ # Apply a type map for all value retrieving methods.
10
+ #
11
+ # +type_map+: a PG::TypeMap instance.
12
+ #
13
+ # This method is equal to #type_map= , but returns self, so that calls can be chained.
14
+ #
15
+ # See also PG::BasicTypeMapForResults
16
+ def map_types!(type_map)
17
+ self.type_map = type_map
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
29
+ end
30
+
31
+ ### Return a String representation of the object suitable for debugging.
32
+ def inspect
33
+ str = self.to_s
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
11
40
  end
12
41
 
13
42
  end # class PG::Result
14
43
 
15
- # Backward-compatible alias
16
- PGresult = PG::Result