pg 1.0.0 → 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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +0 -6595
- data/History.rdoc +156 -0
- data/Manifest.txt +8 -2
- data/README-Windows.rdoc +4 -4
- data/README.ja.rdoc +1 -2
- data/README.rdoc +55 -9
- data/Rakefile +9 -7
- data/Rakefile.cross +58 -57
- data/ext/errorcodes.def +68 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +19 -2
- data/ext/extconf.rb +7 -5
- data/ext/pg.c +141 -98
- data/ext/pg.h +64 -21
- data/ext/pg_binary_decoder.c +82 -15
- data/ext/pg_binary_encoder.c +13 -12
- data/ext/pg_coder.c +73 -12
- data/ext/pg_connection.c +625 -346
- data/ext/pg_copy_coder.c +16 -8
- data/ext/pg_record_coder.c +491 -0
- data/ext/pg_result.c +571 -191
- data/ext/pg_text_decoder.c +606 -40
- data/ext/pg_text_encoder.c +185 -54
- data/ext/pg_tuple.c +549 -0
- data/ext/pg_type_map.c +1 -1
- data/ext/pg_type_map_all_strings.c +4 -4
- data/ext/pg_type_map_by_class.c +9 -4
- data/ext/pg_type_map_by_column.c +7 -6
- data/ext/pg_type_map_by_mri_type.c +1 -1
- data/ext/pg_type_map_by_oid.c +3 -2
- data/ext/pg_type_map_in_ruby.c +1 -1
- data/ext/{util.c → pg_util.c} +10 -10
- data/ext/{util.h → pg_util.h} +2 -2
- data/lib/pg.rb +8 -6
- data/lib/pg/basic_type_mapping.rb +121 -25
- data/lib/pg/binary_decoder.rb +23 -0
- data/lib/pg/coder.rb +23 -2
- data/lib/pg/connection.rb +22 -3
- data/lib/pg/constants.rb +2 -1
- data/lib/pg/exceptions.rb +2 -1
- data/lib/pg/result.rb +14 -2
- data/lib/pg/text_decoder.rb +21 -26
- data/lib/pg/text_encoder.rb +32 -8
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +3 -2
- data/spec/helpers.rb +52 -20
- data/spec/pg/basic_type_mapping_spec.rb +362 -37
- data/spec/pg/connection_spec.rb +376 -146
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +240 -15
- data/spec/pg/tuple_spec.rb +333 -0
- data/spec/pg/type_map_by_class_spec.rb +2 -2
- data/spec/pg/type_map_by_column_spec.rb +6 -2
- data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
- data/spec/pg/type_map_by_oid_spec.rb +3 -3
- data/spec/pg/type_map_in_ruby_spec.rb +1 -1
- data/spec/pg/type_map_spec.rb +1 -1
- data/spec/pg/type_spec.rb +363 -17
- data/spec/pg_spec.rb +1 -1
- metadata +47 -47
- metadata.gz.sig +0 -0
| @@ -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
    CHANGED
    
    | @@ -1,4 +1,5 @@ | |
| 1 | 
            -
             | 
| 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
         | 
    
        data/lib/pg/connection.rb
    CHANGED
    
    | @@ -1,4 +1,5 @@ | |
| 1 | 
            -
             | 
| 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, /\A#{URI | 
| 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 /=/
         | 
| @@ -268,5 +269,23 @@ class PG::Connection | |
| 268 269 | 
             
            		end
         | 
| 269 270 | 
             
            	end
         | 
| 270 271 |  | 
| 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 | 
            +
            	}
         | 
| 272 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
    
    
    
        data/lib/pg/exceptions.rb
    CHANGED
    
    
    
        data/lib/pg/result.rb
    CHANGED
    
    | @@ -1,4 +1,5 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # -*- ruby -*-
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 2 3 |  | 
| 3 4 | 
             
            require 'pg' unless defined?( PG )
         | 
| 4 5 |  | 
| @@ -9,12 +10,23 @@ class PG::Result | |
| 9 10 | 
             
            	#
         | 
| 10 11 | 
             
            	# +type_map+: a PG::TypeMap instance.
         | 
| 11 12 | 
             
            	#
         | 
| 12 | 
            -
            	#  | 
| 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 18 | 
             
            		return self
         | 
| 16 19 | 
             
            	end
         | 
| 17 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
         | 
| 18 30 |  | 
| 19 31 | 
             
            	### Return a String representation of the object suitable for debugging.
         | 
| 20 32 | 
             
            	def inspect
         | 
    
        data/lib/pg/text_decoder.rb
    CHANGED
    
    | @@ -1,4 +1,5 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # -*- ruby -*-
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 2 3 |  | 
| 3 4 | 
             
            require 'date'
         | 
| 4 5 | 
             
            require 'json'
         | 
| @@ -6,10 +7,8 @@ require 'json' | |
| 6 7 | 
             
            module PG
         | 
| 7 8 | 
             
            	module TextDecoder
         | 
| 8 9 | 
             
            		class Date < SimpleDecoder
         | 
| 9 | 
            -
            			ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
         | 
| 10 | 
            -
             | 
| 11 10 | 
             
            			def decode(string, tuple=nil, field=nil)
         | 
| 12 | 
            -
            				if string =~  | 
| 11 | 
            +
            				if string =~ /\A(\d{4})-(\d\d)-(\d\d)\z/
         | 
| 13 12 | 
             
            					::Date.new $1.to_i, $2.to_i, $3.to_i
         | 
| 14 13 | 
             
            				else
         | 
| 15 14 | 
             
            					string
         | 
| @@ -17,35 +16,31 @@ module PG | |
| 17 16 | 
             
            			end
         | 
| 18 17 | 
             
            		end
         | 
| 19 18 |  | 
| 20 | 
            -
            		class  | 
| 21 | 
            -
            			ISO_DATETIME_WITHOUT_TIMEZONE = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
         | 
| 22 | 
            -
             | 
| 19 | 
            +
            		class JSON < SimpleDecoder
         | 
| 23 20 | 
             
            			def decode(string, tuple=nil, field=nil)
         | 
| 24 | 
            -
            				 | 
| 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
         | 
| 21 | 
            +
            				::JSON.parse(string, quirks_mode: true)
         | 
| 29 22 | 
             
            			end
         | 
| 30 23 | 
             
            		end
         | 
| 31 24 |  | 
| 32 | 
            -
            		 | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 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
         | 
| 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))
         | 
| 41 29 | 
             
            			end
         | 
| 42 30 | 
             
            		end
         | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 31 | 
            +
            		class TimestampUtcToLocal < Timestamp
         | 
| 32 | 
            +
            			def initialize(params={})
         | 
| 33 | 
            +
            				super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL))
         | 
| 34 | 
            +
            			end
         | 
| 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))
         | 
| 47 39 | 
             
            			end
         | 
| 48 40 | 
             
            		end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            		# For backward compatibility:
         | 
| 43 | 
            +
            		TimestampWithoutTimeZone = TimestampLocal
         | 
| 44 | 
            +
            		TimestampWithTimeZone = Timestamp
         | 
| 49 45 | 
             
            	end
         | 
| 50 46 | 
             
            end # module PG
         | 
| 51 | 
            -
             | 
    
        data/lib/pg/text_encoder.rb
    CHANGED
    
    | @@ -1,27 +1,32 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # -*- ruby -*-
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 2 3 |  | 
| 3 4 | 
             
            require 'json'
         | 
| 5 | 
            +
            require 'ipaddr'
         | 
| 4 6 |  | 
| 5 7 | 
             
            module PG
         | 
| 6 8 | 
             
            	module TextEncoder
         | 
| 7 9 | 
             
            		class Date < SimpleEncoder
         | 
| 8 | 
            -
            			STRFTIME_ISO_DATE = "%Y-%m-%d".freeze
         | 
| 9 10 | 
             
            			def encode(value)
         | 
| 10 | 
            -
            				value.respond_to?(:strftime) ? value.strftime( | 
| 11 | 
            +
            				value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d") : value
         | 
| 11 12 | 
             
            			end
         | 
| 12 13 | 
             
            		end
         | 
| 13 14 |  | 
| 14 15 | 
             
            		class TimestampWithoutTimeZone < SimpleEncoder
         | 
| 15 | 
            -
            			STRFTIME_ISO_DATETIME_WITHOUT_TIMEZONE = "%Y-%m-%d %H:%M:%S.%N".freeze
         | 
| 16 16 | 
             
            			def encode(value)
         | 
| 17 | 
            -
            				value.respond_to?(:strftime) ? value.strftime( | 
| 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
         | 
| 18 24 | 
             
            			end
         | 
| 19 25 | 
             
            		end
         | 
| 20 26 |  | 
| 21 27 | 
             
            		class TimestampWithTimeZone < SimpleEncoder
         | 
| 22 | 
            -
            			STRFTIME_ISO_DATETIME_WITH_TIMEZONE = "%Y-%m-%d %H:%M:%S.%N %:z".freeze
         | 
| 23 28 | 
             
            			def encode(value)
         | 
| 24 | 
            -
            				value.respond_to?(:strftime) ? value.strftime( | 
| 29 | 
            +
            				value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S.%N %:z") : value
         | 
| 25 30 | 
             
            			end
         | 
| 26 31 | 
             
            		end
         | 
| 27 32 |  | 
| @@ -30,6 +35,25 @@ module PG | |
| 30 35 | 
             
            				::JSON.generate(value, quirks_mode: true)
         | 
| 31 36 | 
             
            			end
         | 
| 32 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
         | 
| 56 | 
            +
            			end
         | 
| 57 | 
            +
            		end
         | 
| 33 58 | 
             
            	end
         | 
| 34 59 | 
             
            end # module PG
         | 
| 35 | 
            -
             | 
    
        data/lib/pg/tuple.rb
    ADDED
    
    | @@ -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 | 
            -
             | 
| 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 ?  | 
| 13 | 
            +
            		type_strings = coders.map{|c| c ? c.inspect_short : 'nil' }
         | 
| 13 14 | 
             
            		"#<#{self.class} #{type_strings.join(' ')}>"
         | 
| 14 15 | 
             
            	end
         | 
| 15 16 | 
             
            end
         | 
    
        data/spec/helpers.rb
    CHANGED
    
    | @@ -1,4 +1,4 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # -*- ruby -*-
         | 
| 2 2 |  | 
| 3 3 | 
             
            require 'pathname'
         | 
| 4 4 | 
             
            require 'rspec'
         | 
| @@ -22,12 +22,11 @@ module PG::TestingHelpers | |
| 22 22 |  | 
| 23 23 | 
             
            			mod.around( :each ) do |example|
         | 
| 24 24 | 
             
            				begin
         | 
| 25 | 
            +
            					@conn.set_default_encoding
         | 
| 25 26 | 
             
            					@conn.exec( 'BEGIN' ) unless example.metadata[:without_transaction]
         | 
| 26 | 
            -
            					 | 
| 27 | 
            -
             | 
| 28 | 
            -
            						@conn. | 
| 29 | 
            -
            							[@conn.escape_string(desc.slice(-60))]
         | 
| 30 | 
            -
            					end
         | 
| 27 | 
            +
            					desc = example.source_location.join(':')
         | 
| 28 | 
            +
            					@conn.exec %Q{SET application_name TO '%s'} %
         | 
| 29 | 
            +
            						[@conn.escape_string(desc.slice(-60))]
         | 
| 31 30 | 
             
            					example.run
         | 
| 32 31 | 
             
            				ensure
         | 
| 33 32 | 
             
            					@conn.exec( 'ROLLBACK' ) unless example.metadata[:without_transaction]
         | 
| @@ -173,18 +172,18 @@ module PG::TestingHelpers | |
| 173 172 | 
             
            			datadir = testdir + 'data'
         | 
| 174 173 | 
             
            			pidfile = datadir + 'postmaster.pid'
         | 
| 175 174 | 
             
            			if pidfile.exist? && pid = pidfile.read.chomp.to_i
         | 
| 176 | 
            -
            				 | 
| 175 | 
            +
            				trace "pidfile (%p) exists: %d" % [ pidfile, pid ]
         | 
| 177 176 | 
             
            				begin
         | 
| 178 177 | 
             
            					Process.kill( 0, pid )
         | 
| 179 178 | 
             
            				rescue Errno::ESRCH
         | 
| 180 | 
            -
            					 | 
| 179 | 
            +
            					trace "No postmaster running for %s" % [ datadir ]
         | 
| 181 180 | 
             
            					# Process isn't alive, so don't try to stop it
         | 
| 182 181 | 
             
            				else
         | 
| 183 | 
            -
            					 | 
| 182 | 
            +
            					trace "Stopping lingering database at PID %d" % [ pid ]
         | 
| 184 183 | 
             
            					run 'pg_ctl', '-D', datadir.to_s, '-m', 'fast', 'stop'
         | 
| 185 184 | 
             
            				end
         | 
| 186 185 | 
             
            			else
         | 
| 187 | 
            -
            				 | 
| 186 | 
            +
            				trace "No pidfile (%p)" % [ pidfile ]
         | 
| 188 187 | 
             
            			end
         | 
| 189 188 | 
             
            		end
         | 
| 190 189 | 
             
            	end
         | 
| @@ -195,12 +194,12 @@ module PG::TestingHelpers | |
| 195 194 | 
             
            		require 'pg'
         | 
| 196 195 | 
             
            		stop_existing_postmasters()
         | 
| 197 196 |  | 
| 198 | 
            -
            		 | 
| 197 | 
            +
            		trace "Setting up test database for #{description}"
         | 
| 199 198 | 
             
            		@test_pgdata = TEST_DIRECTORY + 'data'
         | 
| 200 199 | 
             
            		@test_pgdata.mkpath
         | 
| 201 200 |  | 
| 202 | 
            -
            		 | 
| 203 | 
            -
            		ENV['PGPORT'] | 
| 201 | 
            +
            		ENV['PGPORT'] ||= "54321"
         | 
| 202 | 
            +
            		@port = ENV['PGPORT'].to_i
         | 
| 204 203 | 
             
            		ENV['PGHOST'] = 'localhost'
         | 
| 205 204 | 
             
            		@conninfo = "host=localhost port=#{@port} dbname=test"
         | 
| 206 205 |  | 
| @@ -210,7 +209,7 @@ module PG::TestingHelpers | |
| 210 209 | 
             
            		begin
         | 
| 211 210 | 
             
            			unless (@test_pgdata+"postgresql.conf").exist?
         | 
| 212 211 | 
             
            				FileUtils.rm_rf( @test_pgdata, :verbose => $DEBUG )
         | 
| 213 | 
            -
            				 | 
| 212 | 
            +
            				trace "Running initdb"
         | 
| 214 213 | 
             
            				log_and_run @logfile, 'initdb', '-E', 'UTF8', '--no-locale', '-D', @test_pgdata.to_s
         | 
| 215 214 | 
             
            			end
         | 
| 216 215 |  | 
| @@ -219,14 +218,14 @@ module PG::TestingHelpers | |
| 219 218 | 
             
            				'-D', @test_pgdata.to_s, 'start'
         | 
| 220 219 | 
             
            			sleep 2
         | 
| 221 220 |  | 
| 222 | 
            -
            			 | 
| 221 | 
            +
            			trace "Creating the test DB"
         | 
| 223 222 | 
             
            			log_and_run @logfile, 'psql', '-e', '-c', 'DROP DATABASE IF EXISTS test', 'postgres'
         | 
| 224 223 | 
             
            			log_and_run @logfile, 'createdb', '-e', 'test'
         | 
| 225 224 |  | 
| 226 225 | 
             
            		rescue => err
         | 
| 227 226 | 
             
            			$stderr.puts "%p during test setup: %s" % [ err.class, err.message ]
         | 
| 228 227 | 
             
            			$stderr.puts "See #{@logfile} for details."
         | 
| 229 | 
            -
            			$stderr.puts  | 
| 228 | 
            +
            			$stderr.puts err.backtrace if $DEBUG
         | 
| 230 229 | 
             
            			fail
         | 
| 231 230 | 
             
            		end
         | 
| 232 231 |  | 
| @@ -240,7 +239,7 @@ module PG::TestingHelpers | |
| 240 239 |  | 
| 241 240 |  | 
| 242 241 | 
             
            	def teardown_testing_db( conn )
         | 
| 243 | 
            -
            		 | 
| 242 | 
            +
            		trace "Tearing down test database"
         | 
| 244 243 |  | 
| 245 244 | 
             
            		if conn
         | 
| 246 245 | 
             
            			check_for_lingering_connections( conn )
         | 
| @@ -266,7 +265,7 @@ module PG::TestingHelpers | |
| 266 265 |  | 
| 267 266 | 
             
            	# Retrieve the names of the column types of a given result set.
         | 
| 268 267 | 
             
            	def result_typenames(res)
         | 
| 269 | 
            -
            		@conn. | 
| 268 | 
            +
            		@conn.exec_params( "SELECT " + res.nfields.times.map{|i| "format_type($#{i*2+1},$#{i*2+2})"}.join(","),
         | 
| 270 269 | 
             
            				res.nfields.times.map{|i| [res.ftype(i), res.fmod(i)] }.flatten ).
         | 
| 271 270 | 
             
            				values[0]
         | 
| 272 271 | 
             
            	end
         | 
| @@ -320,6 +319,39 @@ module PG::TestingHelpers | |
| 320 319 | 
             
            		return ConnStillUsableMatcher.new
         | 
| 321 320 | 
             
            	end
         | 
| 322 321 |  | 
| 322 | 
            +
            	def wait_for_polling_ok(conn, meth = :connect_poll)
         | 
| 323 | 
            +
            		status = conn.send(meth)
         | 
| 324 | 
            +
             | 
| 325 | 
            +
            		while status != PG::PGRES_POLLING_OK
         | 
| 326 | 
            +
            			if status == PG::PGRES_POLLING_READING
         | 
| 327 | 
            +
            				select( [conn.socket_io], [], [], 5.0 ) or
         | 
| 328 | 
            +
            					raise "Asynchronous connection timed out!"
         | 
| 329 | 
            +
             | 
| 330 | 
            +
            			elsif status == PG::PGRES_POLLING_WRITING
         | 
| 331 | 
            +
            				select( [], [conn.socket_io], [], 5.0 ) or
         | 
| 332 | 
            +
            					raise "Asynchronous connection timed out!"
         | 
| 333 | 
            +
            			end
         | 
| 334 | 
            +
            			status = conn.send(meth)
         | 
| 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 |  | 
| @@ -338,11 +370,11 @@ RSpec.configure do |config| | |
| 338 370 | 
             
            	else
         | 
| 339 371 | 
             
            		config.filter_run_excluding :windows
         | 
| 340 372 | 
             
            	end
         | 
| 341 | 
            -
            	config.filter_run_excluding :socket_io unless
         | 
| 342 | 
            -
            		PG::Connection.instance_methods.map( &:to_sym ).include?( :socket_io )
         | 
| 343 373 |  | 
| 344 374 | 
             
            	config.filter_run_excluding( :postgresql_93 ) if PG.library_version <  90300
         | 
| 345 375 | 
             
            	config.filter_run_excluding( :postgresql_94 ) if PG.library_version <  90400
         | 
| 346 376 | 
             
            	config.filter_run_excluding( :postgresql_95 ) if PG.library_version <  90500
         | 
| 377 | 
            +
            	config.filter_run_excluding( :postgresql_96 ) if PG.library_version <  90600
         | 
| 347 378 | 
             
            	config.filter_run_excluding( :postgresql_10 ) if PG.library_version < 100000
         | 
| 379 | 
            +
            	config.filter_run_excluding( :postgresql_12 ) if PG.library_version < 120000
         | 
| 348 380 | 
             
            end
         |