pg 1.0.0 → 1.1.0.pre20180730144600

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 (54) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +0 -6595
  5. data/History.rdoc +52 -0
  6. data/README.rdoc +11 -0
  7. data/Rakefile +1 -1
  8. data/Rakefile.cross +1 -1
  9. data/ext/errorcodes.rb +1 -1
  10. data/ext/extconf.rb +2 -0
  11. data/ext/pg.c +3 -2
  12. data/ext/pg.h +33 -5
  13. data/ext/pg_binary_decoder.c +69 -6
  14. data/ext/pg_binary_encoder.c +1 -1
  15. data/ext/pg_coder.c +52 -3
  16. data/ext/pg_connection.c +290 -103
  17. data/ext/pg_copy_coder.c +10 -5
  18. data/ext/pg_result.c +339 -113
  19. data/ext/pg_text_decoder.c +597 -37
  20. data/ext/pg_text_encoder.c +1 -1
  21. data/ext/pg_tuple.c +540 -0
  22. data/ext/pg_type_map.c +1 -1
  23. data/ext/pg_type_map_all_strings.c +1 -1
  24. data/ext/pg_type_map_by_class.c +1 -1
  25. data/ext/pg_type_map_by_column.c +1 -1
  26. data/ext/pg_type_map_by_mri_type.c +1 -1
  27. data/ext/pg_type_map_by_oid.c +1 -1
  28. data/ext/pg_type_map_in_ruby.c +1 -1
  29. data/ext/util.c +6 -6
  30. data/ext/util.h +2 -2
  31. data/lib/pg.rb +5 -3
  32. data/lib/pg/basic_type_mapping.rb +40 -7
  33. data/lib/pg/coder.rb +1 -1
  34. data/lib/pg/connection.rb +20 -1
  35. data/lib/pg/constants.rb +1 -1
  36. data/lib/pg/exceptions.rb +1 -1
  37. data/lib/pg/result.rb +1 -1
  38. data/lib/pg/text_decoder.rb +19 -23
  39. data/lib/pg/text_encoder.rb +35 -1
  40. data/lib/pg/type_map_by_column.rb +1 -1
  41. data/spec/helpers.rb +39 -7
  42. data/spec/pg/basic_type_mapping_spec.rb +230 -27
  43. data/spec/pg/connection_spec.rb +116 -77
  44. data/spec/pg/result_spec.rb +46 -11
  45. data/spec/pg/type_map_by_class_spec.rb +1 -1
  46. data/spec/pg/type_map_by_column_spec.rb +1 -1
  47. data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
  48. data/spec/pg/type_map_by_oid_spec.rb +1 -1
  49. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  50. data/spec/pg/type_map_spec.rb +1 -1
  51. data/spec/pg/type_spec.rb +177 -11
  52. data/spec/pg_spec.rb +1 -1
  53. metadata +24 -28
  54. metadata.gz.sig +0 -0
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_column_map.c - PG::ColumnMap class extension
3
- * $Id: pg_type_map.c,v 2af122820861 2017/01/14 19:56:36 lars $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_type_map_all_strings.c - PG::TypeMapAllStrings class extension
3
- * $Id: pg_type_map_all_strings.c,v c53f993a4254 2014/12/12 21:57:29 lars $
3
+ * $Id$
4
4
  *
5
5
  * This is the default typemap.
6
6
  *
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_type_map_by_class.c - PG::TypeMapByClass class extension
3
- * $Id: pg_type_map_by_class.c,v eeb8a82c5328 2014/11/10 19:34:02 lars $
3
+ * $Id$
4
4
  *
5
5
  * This type map can be used to select value encoders based on the class
6
6
  * of the given value to be send.
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_column_map.c - PG::ColumnMap class extension
3
- * $Id: pg_type_map_by_column.c,v fcf731d3dff7 2015/09/08 12:25:06 jfali $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_type_map_by_mri_type.c - PG::TypeMapByMriType class extension
3
- * $Id: pg_type_map_by_mri_type.c,v 1269b8ad77b8 2015/02/06 16:38:23 lars $
3
+ * $Id$
4
4
  *
5
5
  * This type map can be used to select value encoders based on the MRI-internal
6
6
  * value type code.
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_type_map_by_oid.c - PG::TypeMapByOid class extension
3
- * $Id: pg_type_map_by_oid.c,v c99d26015e3c 2014/12/12 20:58:25 lars $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_type_map_in_ruby.c - PG::TypeMapInRuby class extension
3
- * $Id: pg_type_map_in_ruby.c,v 3d89d3aae4fd 2015/01/05 16:19:41 kanis $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
data/ext/util.c CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * util.c - Utils for ruby-pg
3
- * $Id: util.c,v 5fb9170f6a7d 2015/06/29 11:15:12 kanis $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
@@ -15,9 +15,9 @@ static const char base64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk
15
15
  * in-place (with _out_ == _in_).
16
16
  */
17
17
  void
18
- base64_encode( char *out, char *in, int len)
18
+ base64_encode( char *out, const char *in, int len)
19
19
  {
20
- unsigned char *in_ptr = (unsigned char *)in + len;
20
+ const unsigned char *in_ptr = (const unsigned char *)in + len;
21
21
  char *out_ptr = out + BASE64_ENCODED_SIZE(len);
22
22
  int part_len = len % 3;
23
23
 
@@ -72,12 +72,12 @@ static const unsigned char base64_decode_table[] =
72
72
  * It is possible to decode a string in-place (with _out_ == _in_).
73
73
  */
74
74
  int
75
- base64_decode( char *out, char *in, unsigned int len)
75
+ base64_decode( char *out, const char *in, unsigned int len)
76
76
  {
77
77
  unsigned char a, b, c, d;
78
- unsigned char *in_ptr = (unsigned char *)in;
78
+ const unsigned char *in_ptr = (const unsigned char *)in;
79
79
  unsigned char *out_ptr = (unsigned char *)out;
80
- unsigned char *iend_ptr = (unsigned char *)in + len;
80
+ const unsigned char *iend_ptr = (unsigned char *)in + len;
81
81
 
82
82
  for(;;){
83
83
  if( in_ptr+3 < iend_ptr &&
data/ext/util.h CHANGED
@@ -57,8 +57,8 @@
57
57
  #define BASE64_ENCODED_SIZE(strlen) (((strlen) + 2) / 3 * 4)
58
58
  #define BASE64_DECODED_SIZE(base64len) (((base64len) + 3) / 4 * 3)
59
59
 
60
- void base64_encode( char *out, char *in, int len);
61
- int base64_decode( char *out, char *in, unsigned int len);
60
+ void base64_encode( char *out, const char *in, int len);
61
+ int base64_decode( char *out, const char *in, unsigned int len);
62
62
 
63
63
  int rbpg_strncasecmp(const char *s1, const char *s2, size_t n);
64
64
 
data/lib/pg.rb CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  begin
4
4
  require 'pg_ext'
@@ -35,10 +35,10 @@ end
35
35
  module PG
36
36
 
37
37
  # Library version
38
- VERSION = '1.0.0'
38
+ VERSION = '1.1.0.pre20180730144600'
39
39
 
40
40
  # VCS revision
41
- REVISION = %q$Revision: fef434914848 $
41
+ REVISION = %q$Revision$
42
42
 
43
43
  class NotAllCopyDataRetrieved < PG::Error
44
44
  end
@@ -60,12 +60,14 @@ module PG
60
60
  require 'pg/exceptions'
61
61
  require 'pg/constants'
62
62
  require 'pg/coder'
63
+ require 'pg/binary_decoder'
63
64
  require 'pg/text_encoder'
64
65
  require 'pg/text_decoder'
65
66
  require 'pg/basic_type_mapping'
66
67
  require 'pg/type_map_by_column'
67
68
  require 'pg/connection'
68
69
  require 'pg/result'
70
+ require 'pg/tuple'
69
71
 
70
72
  end # module PG
71
73
 
@@ -1,7 +1,27 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  require 'pg' unless defined?( PG )
4
4
 
5
+ # This module defines the mapping between OID and encoder/decoder classes for PG::BasicTypeMapForResults, PG::BasicTypeMapForQueries and PG::BasicTypeMapBasedOnResult.
6
+ #
7
+ # Additional types can be added like so:
8
+ #
9
+ # require 'pg'
10
+ # require 'ipaddr'
11
+ #
12
+ # class InetDecoder < PG::SimpleDecoder
13
+ # def decode(string, tuple=nil, field=nil)
14
+ # IPAddr.new(string)
15
+ # end
16
+ # end
17
+ # class InetEncoder < PG::SimpleEncoder
18
+ # def encode(ip_addr)
19
+ # ip_addr.to_s
20
+ # end
21
+ # end
22
+ #
23
+ # # 0 if for text format, can also be 1 for binary
24
+ # PG::BasicTypeRegistry.register_type(0, 'inet', InetEncoder, InetDecoder)
5
25
  module PG::BasicTypeRegistry
6
26
  # An instance of this class stores the coders that should be used for a given wire format (text or binary)
7
27
  # and type cast direction (encoder or decoder).
@@ -137,6 +157,7 @@ module PG::BasicTypeRegistry
137
157
  # Register an OID type named +name+ with a typecasting encoder and decoder object in
138
158
  # +type+. +name+ should correspond to the `typname` column in
139
159
  # the `pg_type` table.
160
+ # +format+ can be 0 for text format and 1 for binary.
140
161
  def self.register_type(format, name, encoder_class, decoder_class)
141
162
  CODERS_BY_NAME[format] ||= { encoder: {}, decoder: {} }
142
163
  CODERS_BY_NAME[format][:encoder][name] = encoder_class.new(name: name, format: format) if encoder_class
@@ -145,8 +166,14 @@ module PG::BasicTypeRegistry
145
166
 
146
167
  # Alias the +old+ type to the +new+ type.
147
168
  def self.alias_type(format, new, old)
148
- CODERS_BY_NAME[format][:encoder][new] = CODERS_BY_NAME[format][:encoder][old]
149
- CODERS_BY_NAME[format][:decoder][new] = CODERS_BY_NAME[format][:decoder][old]
169
+ [:encoder, :decoder].each do |ende|
170
+ enc = CODERS_BY_NAME[format][ende][old]
171
+ if enc
172
+ CODERS_BY_NAME[format][ende][new] = enc
173
+ else
174
+ CODERS_BY_NAME[format][ende].delete(new)
175
+ end
176
+ end
150
177
  end
151
178
 
152
179
  register_type 0, 'int2', PG::TextEncoder::Integer, PG::TextDecoder::Integer
@@ -154,7 +181,7 @@ module PG::BasicTypeRegistry
154
181
  alias_type 0, 'int8', 'int2'
155
182
  alias_type 0, 'oid', 'int2'
156
183
 
157
- # register_type 0, 'numeric', OID::Decimal.new
184
+ register_type 0, 'numeric', PG::TextEncoder::Numeric, PG::TextDecoder::Numeric
158
185
  register_type 0, 'text', PG::TextEncoder::String, PG::TextDecoder::String
159
186
  alias_type 0, 'varchar', 'text'
160
187
  alias_type 0, 'char', 'text'
@@ -193,8 +220,8 @@ module PG::BasicTypeRegistry
193
220
  # register_type 'citext', OID::Text.new
194
221
  # register_type 'ltree', OID::Text.new
195
222
  #
196
- # register_type 'cidr', OID::Cidr.new
197
- # alias_type 'inet', 'cidr'
223
+ register_type 0, 'inet', PG::TextEncoder::Inet, PG::TextDecoder::Inet
224
+ alias_type 0, 'cidr', 'inet'
198
225
 
199
226
 
200
227
 
@@ -213,6 +240,8 @@ module PG::BasicTypeRegistry
213
240
  register_type 1, 'bool', PG::BinaryEncoder::Boolean, PG::BinaryDecoder::Boolean
214
241
  register_type 1, 'float4', nil, PG::BinaryDecoder::Float
215
242
  register_type 1, 'float8', nil, PG::BinaryDecoder::Float
243
+ register_type 1, 'timestamp', nil, PG::BinaryDecoder::TimestampUtc
244
+ register_type 1, 'timestamptz', nil, PG::BinaryDecoder::TimestampUtcToLocal
216
245
  end
217
246
 
218
247
  # Simple set of rules for type casting common PostgreSQL types to Ruby.
@@ -258,7 +287,7 @@ end
258
287
  # This prints the rows with type casted columns:
259
288
  # ["a", 123, [5, 4, 3]]
260
289
  #
261
- # See also PG::BasicTypeMapBasedOnResult for the encoder direction.
290
+ # See also PG::BasicTypeMapBasedOnResult for the encoder direction and PG::BasicTypeRegistry for the definition of additional types.
262
291
  class PG::BasicTypeMapForResults < PG::TypeMapByOid
263
292
  include PG::BasicTypeRegistry
264
293
 
@@ -412,6 +441,10 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
412
441
  # to unnecessary type conversions on server side.
413
442
  Integer => [0, 'int8'],
414
443
  Float => [0, 'float8'],
444
+ BigDecimal => [0, 'numeric'],
445
+ # We use text format and no type OID for IPAddr, because setting the OID can lead
446
+ # to unnecessary inet/cidr conversions on the server side.
447
+ IPAddr => [0, 'inet'],
415
448
  Array => :get_array_type,
416
449
  }
417
450
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  module PG
4
4
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  require 'pg' unless defined?( PG )
4
4
  require 'uri'
@@ -268,5 +268,24 @@ class PG::Connection
268
268
  end
269
269
  end
270
270
 
271
+ REDIRECT_METHODS = {
272
+ :exec => [:async_exec, :sync_exec],
273
+ :query => [:async_exec, :sync_exec],
274
+ :exec_params => [:async_exec_params, :sync_exec_params],
275
+ :prepare => [:async_prepare, :sync_prepare],
276
+ :exec_prepared => [:async_exec_prepared, :sync_exec_prepared],
277
+ :describe_portal => [:async_describe_portal, :sync_describe_portal],
278
+ :describe_prepared => [:async_describe_prepared, :sync_describe_prepared],
279
+ }
280
+
281
+ def self.async_api=(enable)
282
+ REDIRECT_METHODS.each do |ali, (async, sync)|
283
+ remove_method(ali) if method_defined?(ali)
284
+ alias_method( ali, enable ? async : sync )
285
+ end
286
+ end
287
+
288
+ # pg-1.1.0+ defaults to libpq's async API for query related blocking methods
289
+ self.async_api = true
271
290
  end # class PG::Connection
272
291
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  require 'pg' unless defined?( PG )
4
4
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  require 'pg' unless defined?( PG )
4
4
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  require 'pg' unless defined?( PG )
4
4
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  require 'date'
4
4
  require 'json'
@@ -17,35 +17,31 @@ module PG
17
17
  end
18
18
  end
19
19
 
20
- class TimestampWithoutTimeZone < SimpleDecoder
21
- ISO_DATETIME_WITHOUT_TIMEZONE = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
22
-
20
+ class JSON < SimpleDecoder
23
21
  def decode(string, tuple=nil, field=nil)
24
- if string =~ ISO_DATETIME_WITHOUT_TIMEZONE
25
- Time.new $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, "#{$6}#{$7}".to_r
26
- else
27
- string
28
- end
22
+ ::JSON.parse(string, quirks_mode: true)
29
23
  end
30
24
  end
31
25
 
32
- class TimestampWithTimeZone < SimpleDecoder
33
- 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/
34
-
35
- def decode(string, tuple=nil, field=nil)
36
- if string =~ ISO_DATETIME_WITH_TIMEZONE
37
- 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'}"
38
- else
39
- string
40
- end
26
+ # Convenience classes for timezone options
27
+ class TimestampUtc < Timestamp
28
+ def initialize(params={})
29
+ super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_UTC))
41
30
  end
42
31
  end
43
-
44
- class JSON < SimpleDecoder
45
- def decode(string, tuple=nil, field=nil)
46
- ::JSON.parse(string, quirks_mode: true)
32
+ class TimestampUtcToLocal < Timestamp
33
+ def initialize(params={})
34
+ super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL))
47
35
  end
48
36
  end
37
+ class TimestampLocal < Timestamp
38
+ def initialize(params={})
39
+ super(params.merge(flags: PG::Coder::TIMESTAMP_DB_LOCAL | PG::Coder::TIMESTAMP_APP_LOCAL))
40
+ end
41
+ end
42
+
43
+ # For backward compatibility:
44
+ TimestampWithoutTimeZone = TimestampLocal
45
+ TimestampWithTimeZone = Timestamp
49
46
  end
50
47
  end # module PG
51
-
@@ -1,6 +1,7 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  require 'json'
4
+ require 'ipaddr'
4
5
 
5
6
  module PG
6
7
  module TextEncoder
@@ -18,6 +19,13 @@ module PG
18
19
  end
19
20
  end
20
21
 
22
+ class TimestampUtc < SimpleEncoder
23
+ STRFTIME_ISO_DATETIME_WITHOUT_TIMEZONE_UTC = "%Y-%m-%d %H:%M:%S.%N".freeze
24
+ def encode(value)
25
+ value.respond_to?(:utc) ? value.utc.strftime(STRFTIME_ISO_DATETIME_WITHOUT_TIMEZONE_UTC) : value
26
+ end
27
+ end
28
+
21
29
  class TimestampWithTimeZone < SimpleEncoder
22
30
  STRFTIME_ISO_DATETIME_WITH_TIMEZONE = "%Y-%m-%d %H:%M:%S.%N %:z".freeze
23
31
  def encode(value)
@@ -25,11 +33,37 @@ module PG
25
33
  end
26
34
  end
27
35
 
36
+ class Numeric < SimpleEncoder
37
+ def encode(value)
38
+ value.is_a?(BigDecimal) ? value.to_s('F') : value
39
+ end
40
+ end
41
+
28
42
  class JSON < SimpleEncoder
29
43
  def encode(value)
30
44
  ::JSON.generate(value, quirks_mode: true)
31
45
  end
32
46
  end
47
+
48
+ class Inet < SimpleEncoder
49
+ def encode(value)
50
+ case value
51
+ when IPAddr
52
+ default_prefix = (value.family == Socket::AF_INET ? 32 : 128)
53
+ s = value.to_s
54
+ if value.respond_to?(:prefix)
55
+ prefix = value.prefix
56
+ else
57
+ range = value.to_range
58
+ prefix = default_prefix - Math.log(((range.end.to_i - range.begin.to_i) + 1), 2).to_i
59
+ end
60
+ s << "/" << prefix.to_s if prefix != default_prefix
61
+ s
62
+ else
63
+ value
64
+ end
65
+ end
66
+ end
33
67
  end
34
68
  end # module PG
35
69
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  require 'pg' unless defined?( PG )
4
4
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  require 'pathname'
4
4
  require 'rspec'
@@ -23,11 +23,9 @@ module PG::TestingHelpers
23
23
  mod.around( :each ) do |example|
24
24
  begin
25
25
  @conn.exec( 'BEGIN' ) unless example.metadata[:without_transaction]
26
- if PG.respond_to?( :library_version )
27
- desc = example.source_location.join(':')
28
- @conn.exec_params %Q{SET application_name TO '%s'} %
29
- [@conn.escape_string(desc.slice(-60))]
30
- end
26
+ desc = example.source_location.join(':')
27
+ @conn.exec %Q{SET application_name TO '%s'} %
28
+ [@conn.escape_string(desc.slice(-60))]
31
29
  example.run
32
30
  ensure
33
31
  @conn.exec( 'ROLLBACK' ) unless example.metadata[:without_transaction]
@@ -266,7 +264,7 @@ module PG::TestingHelpers
266
264
 
267
265
  # Retrieve the names of the column types of a given result set.
268
266
  def result_typenames(res)
269
- @conn.exec( "SELECT " + res.nfields.times.map{|i| "format_type($#{i*2+1},$#{i*2+2})"}.join(","),
267
+ @conn.exec_params( "SELECT " + res.nfields.times.map{|i| "format_type($#{i*2+1},$#{i*2+2})"}.join(","),
270
268
  res.nfields.times.map{|i| [res.ftype(i), res.fmod(i)] }.flatten ).
271
269
  values[0]
272
270
  end
@@ -320,6 +318,40 @@ module PG::TestingHelpers
320
318
  return ConnStillUsableMatcher.new
321
319
  end
322
320
 
321
+ def wait_for_polling_ok(conn)
322
+ socket = conn.socket_io
323
+ status = conn.connect_poll
324
+
325
+ while status != PG::PGRES_POLLING_OK
326
+ if status == PG::PGRES_POLLING_READING
327
+ select( [socket], [], [], 5.0 ) or
328
+ raise "Asynchronous connection timed out!"
329
+
330
+ elsif status == PG::PGRES_POLLING_WRITING
331
+ select( [], [socket], [], 5.0 ) or
332
+ raise "Asynchronous connection timed out!"
333
+ end
334
+ status = conn.connect_poll
335
+ end
336
+ end
337
+
338
+ def wait_for_query_result(conn)
339
+ result = nil
340
+ loop do
341
+ # Buffer any incoming data on the socket until a full result is ready.
342
+ conn.consume_input
343
+ while conn.is_busy
344
+ select( [conn.socket_io], nil, nil, 5.0 ) or
345
+ raise "Timeout waiting for query response."
346
+ conn.consume_input
347
+ end
348
+
349
+ # Fetch the next result. If there isn't one, the query is finished
350
+ result = conn.get_result || break
351
+ end
352
+ result
353
+ end
354
+
323
355
  end
324
356
 
325
357