1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-03 15:22:11 +03:00
Files
postgres/src/interfaces/jdbc/org/postgresql/PG_Stream.java
Peter Mount f41dcbe4d8 Major update part I involving delayed patches, reworked Makefile, and how
the versioning works. There's also a new utils directory used by Makefile
2000-10-12 08:55:28 +00:00

581 lines
14 KiB
Java

package org.postgresql;
import java.io.*;
import java.lang.*;
import java.net.*;
import java.util.*;
import java.sql.*;
import org.postgresql.*;
import org.postgresql.util.*;
/**
* @version 1.0 15-APR-1997
*
* This class is used by Connection & PGlobj for communicating with the
* backend.
*
* @see java.sql.Connection
*/
// This class handles all the Streamed I/O for a org.postgresql connection
public class PG_Stream
{
private Socket connection;
private InputStream pg_input;
private BufferedOutputStream pg_output;
BytePoolDim1 bytePoolDim1 = new BytePoolDim1();
BytePoolDim2 bytePoolDim2 = new BytePoolDim2();
/**
* Constructor: Connect to the PostgreSQL back end and return
* a stream connection.
*
* @param host the hostname to connect to
* @param port the port number that the postmaster is sitting on
* @exception IOException if an IOException occurs below it.
*/
public PG_Stream(String host, int port) throws IOException
{
connection = new Socket(host, port);
// Submitted by Jason Venner <jason@idiom.com> adds a 10x speed
// improvement on FreeBSD machines (caused by a bug in their TCP Stack)
connection.setTcpNoDelay(true);
// Buffer sizes submitted by Sverre H Huseby <sverrehu@online.no>
pg_input = new BufferedInputStream(connection.getInputStream(), 8192);
pg_output = new BufferedOutputStream(connection.getOutputStream(), 8192);
}
/**
* Sends a single character to the back end
*
* @param val the character to be sent
* @exception IOException if an I/O error occurs
*/
public void SendChar(int val) throws IOException
{
// Original code
//byte b[] = new byte[1];
//b[0] = (byte)val;
//pg_output.write(b);
// Optimised version by Sverre H. Huseby Aug 22 1999 Applied Sep 13 1999
pg_output.write((byte)val);
}
/**
* Sends an integer to the back end
*
* @param val the integer to be sent
* @param siz the length of the integer in bytes (size of structure)
* @exception IOException if an I/O error occurs
*/
public void SendInteger(int val, int siz) throws IOException
{
byte[] buf = bytePoolDim1.allocByte(siz);
while (siz-- > 0)
{
buf[siz] = (byte)(val & 0xff);
val >>= 8;
}
Send(buf);
}
/**
* Sends an integer to the back end in reverse order.
*
* This is required when the backend uses the routines in the
* src/backend/libpq/pqcomprim.c module.
*
* As time goes by, this should become obsolete.
*
* @param val the integer to be sent
* @param siz the length of the integer in bytes (size of structure)
* @exception IOException if an I/O error occurs
*/
public void SendIntegerReverse(int val, int siz) throws IOException
{
byte[] buf = bytePoolDim1.allocByte(siz);
int p=0;
while (siz-- > 0)
{
buf[p++] = (byte)(val & 0xff);
val >>= 8;
}
Send(buf);
}
/**
* Send an array of bytes to the backend
*
* @param buf The array of bytes to be sent
* @exception IOException if an I/O error occurs
*/
public void Send(byte buf[]) throws IOException
{
pg_output.write(buf);
}
/**
* Send an exact array of bytes to the backend - if the length
* has not been reached, send nulls until it has.
*
* @param buf the array of bytes to be sent
* @param siz the number of bytes to be sent
* @exception IOException if an I/O error occurs
*/
public void Send(byte buf[], int siz) throws IOException
{
Send(buf,0,siz);
}
/**
* Send an exact array of bytes to the backend - if the length
* has not been reached, send nulls until it has.
*
* @param buf the array of bytes to be sent
* @param off offset in the array to start sending from
* @param siz the number of bytes to be sent
* @exception IOException if an I/O error occurs
*/
public void Send(byte buf[], int off, int siz) throws IOException
{
int i;
pg_output.write(buf, off, ((buf.length-off) < siz ? (buf.length-off) : siz));
if((buf.length-off) < siz)
{
for (i = buf.length-off ; i < siz ; ++i)
{
pg_output.write(0);
}
}
}
/**
* Sends a packet, prefixed with the packet's length
* @param buf buffer to send
* @exception SQLException if an I/O Error returns
*/
public void SendPacket(byte[] buf) throws IOException
{
SendInteger(buf.length+4,4);
Send(buf);
}
/**
* Receives a single character from the backend
*
* @return the character received
* @exception SQLException if an I/O Error returns
*/
public int ReceiveChar() throws SQLException
{
int c = 0;
try
{
c = pg_input.read();
if (c < 0) throw new PSQLException("postgresql.stream.eof");
} catch (IOException e) {
throw new PSQLException("postgresql.stream.ioerror",e);
}
return c;
}
/**
* Receives an integer from the backend
*
* @param siz length of the integer in bytes
* @return the integer received from the backend
* @exception SQLException if an I/O error occurs
*/
public int ReceiveInteger(int siz) throws SQLException
{
int n = 0;
try
{
for (int i = 0 ; i < siz ; i++)
{
int b = pg_input.read();
if (b < 0)
throw new PSQLException("postgresql.stream.eof");
n = n | (b << (8 * i)) ;
}
} catch (IOException e) {
throw new PSQLException("postgresql.stream.ioerror",e);
}
return n;
}
/**
* Receives an integer from the backend
*
* @param siz length of the integer in bytes
* @return the integer received from the backend
* @exception SQLException if an I/O error occurs
*/
public int ReceiveIntegerR(int siz) throws SQLException
{
int n = 0;
try
{
for (int i = 0 ; i < siz ; i++)
{
int b = pg_input.read();
if (b < 0)
throw new PSQLException("postgresql.stream.eof");
n = b | (n << 8);
}
} catch (IOException e) {
throw new PSQLException("postgresql.stream.ioerror",e);
}
return n;
}
/**
* Receives a null-terminated string from the backend. Maximum of
* maxsiz bytes - if we don't see a null, then we assume something
* has gone wrong.
*
* @param maxsiz maximum length of string
* @return string from back end
* @exception SQLException if an I/O error occurs
*/
public String ReceiveString(int maxsiz) throws SQLException
{
byte[] rst = bytePoolDim1.allocByte(maxsiz);
return ReceiveString(rst, maxsiz, null);
}
/**
* Receives a null-terminated string from the backend. Maximum of
* maxsiz bytes - if we don't see a null, then we assume something
* has gone wrong.
*
* @param maxsiz maximum length of string
* @param encoding the charset encoding to use.
* @param maxsiz maximum length of string in bytes
* @return string from back end
* @exception SQLException if an I/O error occurs
*/
public String ReceiveString(int maxsiz, String encoding) throws SQLException
{
byte[] rst = bytePoolDim1.allocByte(maxsiz);
return ReceiveString(rst, maxsiz, encoding);
}
/**
* Receives a null-terminated string from the backend. Maximum of
* maxsiz bytes - if we don't see a null, then we assume something
* has gone wrong.
*
* @param rst byte array to read the String into. rst.length must
* equal to or greater than maxsize.
* @param maxsiz maximum length of string in bytes
* @param encoding the charset encoding to use.
* @return string from back end
* @exception SQLException if an I/O error occurs
*/
public String ReceiveString(byte rst[], int maxsiz, String encoding)
throws SQLException
{
int s = 0;
try
{
while (s < maxsiz)
{
int c = pg_input.read();
if (c < 0)
throw new PSQLException("postgresql.stream.eof");
else if (c == 0) {
rst[s] = 0;
break;
} else
rst[s++] = (byte)c;
}
if (s >= maxsiz)
throw new PSQLException("postgresql.stream.toomuch");
} catch (IOException e) {
throw new PSQLException("postgresql.stream.ioerror",e);
}
String v = null;
if (encoding == null)
v = new String(rst, 0, s);
else {
try {
v = new String(rst, 0, s, encoding);
} catch (UnsupportedEncodingException unse) {
throw new PSQLException("postgresql.stream.encoding", unse);
}
}
return v;
}
/**
* Read a tuple from the back end. A tuple is a two dimensional
* array of bytes
*
* @param nf the number of fields expected
* @param bin true if the tuple is a binary tuple
* @return null if the current response has no more tuples, otherwise
* an array of strings
* @exception SQLException if a data I/O error occurs
*/
public byte[][] ReceiveTuple(int nf, boolean bin) throws SQLException
{
int i, bim = (nf + 7)/8;
byte[] bitmask = Receive(bim);
byte[][] answer = bytePoolDim2.allocByte(nf);
int whichbit = 0x80;
int whichbyte = 0;
for (i = 0 ; i < nf ; ++i)
{
boolean isNull = ((bitmask[whichbyte] & whichbit) == 0);
whichbit >>= 1;
if (whichbit == 0)
{
++whichbyte;
whichbit = 0x80;
}
if (isNull)
answer[i] = null;
else
{
int len = ReceiveIntegerR(4);
if (!bin)
len -= 4;
if (len < 0)
len = 0;
answer[i] = Receive(len);
}
}
return answer;
}
/**
* Reads in a given number of bytes from the backend
*
* @param siz number of bytes to read
* @return array of bytes received
* @exception SQLException if a data I/O error occurs
*/
private byte[] Receive(int siz) throws SQLException
{
byte[] answer = bytePoolDim1.allocByte(siz);
Receive(answer,0,siz);
return answer;
}
/**
* Reads in a given number of bytes from the backend
*
* @param buf buffer to store result
* @param off offset in buffer
* @param siz number of bytes to read
* @exception SQLException if a data I/O error occurs
*/
public void Receive(byte[] b,int off,int siz) throws SQLException
{
int s = 0;
try
{
while (s < siz)
{
int w = pg_input.read(b, off+s, siz - s);
if (w < 0)
throw new PSQLException("postgresql.stream.eof");
s += w;
}
} catch (IOException e) {
throw new PSQLException("postgresql.stream.ioerror",e);
}
}
/**
* This flushes any pending output to the backend. It is used primarily
* by the Fastpath code.
* @exception SQLException if an I/O error occurs
*/
public void flush() throws SQLException
{
try {
pg_output.flush();
} catch (IOException e) {
throw new PSQLException("postgresql.stream.flush",e);
}
}
/**
* Closes the connection
*
* @exception IOException if a IO Error occurs
*/
public void close() throws IOException
{
pg_output.write("X\0".getBytes());
pg_output.flush();
pg_output.close();
pg_input.close();
connection.close();
}
/**
* Deallocate all resources that has been associated with any previous
* query.
*/
public void deallocate(){
bytePoolDim1.deallocate();
bytePoolDim2.deallocate();
}
}
/**
* A simple and fast object pool implementation that can pool objects
* of any type. This implementation is not thread safe, it is up to the users
* of this class to assure thread safety.
*/
class ObjectPool {
int cursize = 0;
int maxsize = 16;
Object arr[] = new Object[maxsize];
public void add(Object o){
if(cursize >= maxsize){
Object newarr[] = new Object[maxsize*2];
System.arraycopy(arr, 0, newarr, 0, maxsize);
maxsize = maxsize * 2;
arr = newarr;
}
arr[cursize++] = o;
}
public Object remove(){
return arr[--cursize];
}
public boolean isEmpty(){
return cursize == 0;
}
public int size(){
return cursize;
}
public void addAll(ObjectPool pool){
int srcsize = pool.size();
if(srcsize == 0)
return;
int totalsize = srcsize + cursize;
if(totalsize > maxsize){
Object newarr[] = new Object[totalsize*2];
System.arraycopy(arr, 0, newarr, 0, cursize);
maxsize = maxsize = totalsize * 2;
arr = newarr;
}
System.arraycopy(pool.arr, 0, arr, cursize, srcsize);
cursize = totalsize;
}
public void clear(){
cursize = 0;
}
}
/**
* A simple and efficient class to pool one dimensional byte arrays
* of different sizes.
*/
class BytePoolDim1 {
int maxsize = 256;
ObjectPool notusemap[] = new ObjectPool[maxsize];
ObjectPool inusemap[] = new ObjectPool[maxsize];
byte binit[][] = new byte[maxsize][0];
public BytePoolDim1(){
for(int i = 0; i < maxsize; i++){
binit[i] = new byte[i];
inusemap[i] = new ObjectPool();
notusemap[i] = new ObjectPool();
}
}
public byte[] allocByte(int size){
if(size > maxsize){
return new byte[size];
}
ObjectPool not_usel = notusemap[size];
ObjectPool in_usel = inusemap[size];
byte b[] = null;
if(!not_usel.isEmpty()) {
Object o = not_usel.remove();
b = (byte[]) o;
} else
b = new byte[size];
in_usel.add(b);
return b;
}
public void deallocate(){
for(int i = 0; i < maxsize; i++){
notusemap[i].addAll(inusemap[i]);
inusemap[i].clear();
}
}
}
/**
* A simple and efficient class to pool two dimensional byte arrays
* of different sizes.
*/
class BytePoolDim2 {
int maxsize = 32;
ObjectPool notusemap[] = new ObjectPool[maxsize];
ObjectPool inusemap[] = new ObjectPool[maxsize];
public BytePoolDim2(){
for(int i = 0; i < maxsize; i++){
inusemap[i] = new ObjectPool();
notusemap[i] = new ObjectPool();
}
}
public byte[][] allocByte(int size){
if(size > maxsize){
return new byte[size][0];
}
ObjectPool not_usel = notusemap[size];
ObjectPool in_usel = inusemap[size];
byte b[][] = null;
if(!not_usel.isEmpty()) {
Object o = not_usel.remove();
b = (byte[][]) o;
} else
b = new byte[size][0];
in_usel.add(b);
return b;
}
public void deallocate(){
for(int i = 0; i < maxsize; i++){
notusemap[i].addAll(inusemap[i]);
inusemap[i].clear();
}
}
}