class PG::Connection

def copy_data( sql, coder=nil )

def copy_data( sql, coder=nil )
	raise PG::NotInBlockingMode.new("copy_data can not be used in nonblocking mode", connection: self) if nonblocking?
	res = exec( sql )
	case res.result_status
	when PGRES_COPY_IN
		begin
			if coder && res.binary_tuples == 1
				# Binary file header (11 byte signature, 32 bit flags and 32 bit extension length)
				put_copy_data(BinarySignature + ("\x00" * 8))
			end
			if coder
				old_coder = self.encoder_for_put_copy_data
				self.encoder_for_put_copy_data = coder
			end
			yield res
		rescue Exception => err
			errmsg = "%s while copy data: %s" % [ err.class.name, err.message ]
			begin
				put_copy_end( errmsg )
			rescue PG::Error
				# Ignore error in cleanup to avoid losing original exception
			end
			discard_results
			raise err
		else
			begin
				self.encoder_for_put_copy_data = old_coder if coder
				if coder && res.binary_tuples == 1
					put_copy_data("\xFF\xFF") # Binary file trailer 16 bit "-1"
				end
				put_copy_end
			rescue PG::Error => err
				raise PG::LostCopyState.new("#{err} (probably by executing another SQL query while running a COPY command)", connection: self)
			end
			get_last_result
		ensure
			self.encoder_for_put_copy_data = old_coder if coder
		end
	when PGRES_COPY_OUT
		begin
			if coder
				old_coder = self.decoder_for_get_copy_data
				self.decoder_for_get_copy_data = coder
			end
			yield res
		rescue Exception
			cancel
			discard_results
			raise
		else
			if coder && res.binary_tuples == 1
				# There are two end markers in binary mode: file trailer and the final nil.
				# The file trailer is expected to be processed by BinaryDecoder::CopyRow and already returns nil, so that the remaining NULL from PQgetCopyData is retrieved here:
				if get_copy_data
					discard_results
					raise PG::NotAllCopyDataRetrieved.new("Not all binary COPY data retrieved", connection: self)
				end
			end
			res = get_last_result
			if !res
				discard_results
				raise PG::LostCopyState.new("Lost COPY state (probably by executing another SQL query while running a COPY command)", connection: self)
			elsif res.result_status != PGRES_COMMAND_OK
				discard_results
				raise PG::NotAllCopyDataRetrieved.new("Not all COPY data retrieved", connection: self)
			end
			res
		ensure
			self.decoder_for_get_copy_data = old_coder if coder
		end
	else
		raise ArgumentError, "SQL command is no COPY statement: #{sql}"
	end
end