/*
 * Copyright (c) 2003, PostgreSQL Global Development Group
 * See the LICENSE file in the project root for more information.
 */

package org.postgresql.fastpath;

import org.postgresql.core.BaseConnection;
import org.postgresql.core.ParameterList;
import org.postgresql.core.QueryExecutor;
import org.postgresql.util.ByteConverter;
import org.postgresql.util.GT;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;

This class implements the Fastpath api.

This is a means of executing functions embedded in the backend from within a java application.

It is based around the file src/interfaces/libpq/fe-exec.c

Deprecated:This API is somewhat obsolete, as one may achieve similar performance and greater functionality by setting up a prepared statement to define the function call. Then, executing the statement with binary transmission of parameters and results substitutes for a fast-path function call.
/** * <p>This class implements the Fastpath api.</p> * * <p>This is a means of executing functions embedded in the backend from within a java application.</p> * * <p>It is based around the file src/interfaces/libpq/fe-exec.c</p> * * @deprecated This API is somewhat obsolete, as one may achieve similar performance * and greater functionality by setting up a prepared statement to define * the function call. Then, executing the statement with binary transmission of parameters * and results substitutes for a fast-path function call. */
@Deprecated public class Fastpath { // Java passes oids around as longs, but in the backend // it's an unsigned int, so we use this to make the conversion // of long -> signed int which the backend interprets as unsigned. private static final long NUM_OIDS = 4294967296L; // 2^32 // This maps the functions names to their id's (possible unique just // to a connection). private final Map<String, Integer> func = new HashMap<String, Integer>(); private final QueryExecutor executor; private final BaseConnection connection;
Initialises the fastpath system.
Params:
  • conn – BaseConnection to attach to
/** * Initialises the fastpath system. * * @param conn BaseConnection to attach to */
public Fastpath(BaseConnection conn) { this.connection = conn; this.executor = conn.getQueryExecutor(); }
Send a function call to the PostgreSQL backend.
Params:
  • fnId – Function id
  • resultType – True if the result is a numeric (Integer or Long)
  • args – FastpathArguments to pass to fastpath
Throws:
Returns:null if no data, Integer if an integer result, Long if a long result, or byte[] otherwise
Deprecated:please use fastpath(int, FastpathArg[])
/** * Send a function call to the PostgreSQL backend. * * @param fnId Function id * @param resultType True if the result is a numeric (Integer or Long) * @param args FastpathArguments to pass to fastpath * @return null if no data, Integer if an integer result, Long if a long result, or byte[] * otherwise * @throws SQLException if a database-access error occurs. * @deprecated please use {@link #fastpath(int, FastpathArg[])} */
@Deprecated public Object fastpath(int fnId, boolean resultType, FastpathArg[] args) throws SQLException { // Run it. byte[] returnValue = fastpath(fnId, args); // Interpret results. if (!resultType || returnValue == null) { return returnValue; } if (returnValue.length == 4) { return ByteConverter.int4(returnValue, 0); } else if (returnValue.length == 8) { return ByteConverter.int8(returnValue, 0); } else { throw new PSQLException( GT.tr("Fastpath call {0} - No result was returned and we expected a numeric.", fnId), PSQLState.NO_DATA); } }
Send a function call to the PostgreSQL backend.
Params:
  • fnId – Function id
  • args – FastpathArguments to pass to fastpath
Throws:
Returns:null if no data, byte[] otherwise
/** * Send a function call to the PostgreSQL backend. * * @param fnId Function id * @param args FastpathArguments to pass to fastpath * @return null if no data, byte[] otherwise * @throws SQLException if a database-access error occurs. */
public byte[] fastpath(int fnId, FastpathArg[] args) throws SQLException { // Turn fastpath array into a parameter list. ParameterList params = executor.createFastpathParameters(args.length); for (int i = 0; i < args.length; ++i) { args[i].populateParameter(params, i + 1); } // Run it. return executor.fastpathCall(fnId, params, connection.getAutoCommit()); }
Params:
  • name – Function name
  • resulttype – True if the result is a numeric (Integer or Long)
  • args – FastpathArguments to pass to fastpath
Throws:
See Also:
Returns:null if no data, Integer if an integer result, Long if a long result, or byte[] otherwise
Deprecated:Use getData(String, FastpathArg[]) if you expect a binary result, or one of getInteger(String, FastpathArg[]) or getLong(String, FastpathArg[]) if you expect a numeric one
/** * @param name Function name * @param resulttype True if the result is a numeric (Integer or Long) * @param args FastpathArguments to pass to fastpath * @return null if no data, Integer if an integer result, Long if a long result, or byte[] * otherwise * @throws SQLException if something goes wrong * @see #fastpath(int, FastpathArg[]) * @see #fastpath(String, FastpathArg[]) * @deprecated Use {@link #getData(String, FastpathArg[])} if you expect a binary result, or one * of {@link #getInteger(String, FastpathArg[])} or * {@link #getLong(String, FastpathArg[])} if you expect a numeric one */
@Deprecated public Object fastpath(String name, boolean resulttype, FastpathArg[] args) throws SQLException { connection.getLogger().log(Level.FINEST, "Fastpath: calling {0}", name); return fastpath(getID(name), resulttype, args); }

Send a function call to the PostgreSQL backend by name.

Note: the mapping for the procedure name to function id needs to exist, usually to an earlier call to addfunction().

This is the preferred method to call, as function id's can/may change between versions of the backend.

For an example of how this works, refer to org.postgresql.largeobject.LargeObject

Params:
  • name – Function name
  • args – FastpathArguments to pass to fastpath
Throws:
  • SQLException – if name is unknown or if a database-access error occurs.
See Also:
Returns:null if no data, byte[] otherwise
/** * <p>Send a function call to the PostgreSQL backend by name.</p> * * <p>Note: the mapping for the procedure name to function id needs to exist, usually to an earlier * call to addfunction().</p> * * <p>This is the preferred method to call, as function id's can/may change between versions of the * backend.</p> * * <p>For an example of how this works, refer to org.postgresql.largeobject.LargeObject</p> * * @param name Function name * @param args FastpathArguments to pass to fastpath * @return null if no data, byte[] otherwise * @throws SQLException if name is unknown or if a database-access error occurs. * @see org.postgresql.largeobject.LargeObject */
public byte[] fastpath(String name, FastpathArg[] args) throws SQLException { connection.getLogger().log(Level.FINEST, "Fastpath: calling {0}", name); return fastpath(getID(name), args); }
This convenience method assumes that the return value is an integer.
Params:
  • name – Function name
  • args – Function arguments
Throws:
  • SQLException – if a database-access error occurs or no result
Returns:integer result
/** * This convenience method assumes that the return value is an integer. * * @param name Function name * @param args Function arguments * @return integer result * @throws SQLException if a database-access error occurs or no result */
public int getInteger(String name, FastpathArg[] args) throws SQLException { byte[] returnValue = fastpath(name, args); if (returnValue == null) { throw new PSQLException( GT.tr("Fastpath call {0} - No result was returned and we expected an integer.", name), PSQLState.NO_DATA); } if (returnValue.length == 4) { return ByteConverter.int4(returnValue, 0); } else { throw new PSQLException(GT.tr( "Fastpath call {0} - No result was returned or wrong size while expecting an integer.", name), PSQLState.NO_DATA); } }
This convenience method assumes that the return value is a long (bigint).
Params:
  • name – Function name
  • args – Function arguments
Throws:
  • SQLException – if a database-access error occurs or no result
Returns:long result
/** * This convenience method assumes that the return value is a long (bigint). * * @param name Function name * @param args Function arguments * @return long result * @throws SQLException if a database-access error occurs or no result */
public long getLong(String name, FastpathArg[] args) throws SQLException { byte[] returnValue = fastpath(name, args); if (returnValue == null) { throw new PSQLException( GT.tr("Fastpath call {0} - No result was returned and we expected a long.", name), PSQLState.NO_DATA); } if (returnValue.length == 8) { return ByteConverter.int8(returnValue, 0); } else { throw new PSQLException( GT.tr("Fastpath call {0} - No result was returned or wrong size while expecting a long.", name), PSQLState.NO_DATA); } }
This convenience method assumes that the return value is an oid.
Params:
  • name – Function name
  • args – Function arguments
Throws:
  • SQLException – if a database-access error occurs or no result
Returns:oid of the given call
/** * This convenience method assumes that the return value is an oid. * * @param name Function name * @param args Function arguments * @return oid of the given call * @throws SQLException if a database-access error occurs or no result */
public long getOID(String name, FastpathArg[] args) throws SQLException { long oid = getInteger(name, args); if (oid < 0) { oid += NUM_OIDS; } return oid; }
This convenience method assumes that the return value is not an Integer.
Params:
  • name – Function name
  • args – Function arguments
Throws:
  • SQLException – if a database-access error occurs or no result
Returns:byte[] array containing result
/** * This convenience method assumes that the return value is not an Integer. * * @param name Function name * @param args Function arguments * @return byte[] array containing result * @throws SQLException if a database-access error occurs or no result */
public byte[] getData(String name, FastpathArg[] args) throws SQLException { return fastpath(name, args); }

This adds a function to our lookup table.

User code should use the addFunctions method, which is based upon a query, rather than hard coding the oid. The oid for a function is not guaranteed to remain static, even on different servers of the same version.

Params:
  • name – Function name
  • fnid – Function id
/** * <p>This adds a function to our lookup table.</p> * * <p>User code should use the addFunctions method, which is based upon a query, rather than hard * coding the oid. The oid for a function is not guaranteed to remain static, even on different * servers of the same version.</p> * * @param name Function name * @param fnid Function id */
public void addFunction(String name, int fnid) { func.put(name, fnid); }

This takes a ResultSet containing two columns. Column 1 contains the function name, Column 2 the oid.

It reads the entire ResultSet, loading the values into the function table.

REMEMBER to close() the resultset after calling this!!

Implementation note about function name lookups:

PostgreSQL stores the function id's and their corresponding names in the pg_proc table. To speed things up locally, instead of querying each function from that table when required, a HashMap is used. Also, only the function's required are entered into this table, keeping connection times as fast as possible.

The org.postgresql.largeobject.LargeObject class performs a query upon it's startup, and passes the returned ResultSet to the addFunctions() method here.

Once this has been done, the LargeObject api refers to the functions by name.

Don't think that manually converting them to the oid's will work. Ok, they will for now, but they can change during development (there was some discussion about this for V7.0), so this is implemented to prevent any unwarranted headaches in the future.

Params:
  • rs – ResultSet
Throws:
See Also:
/** * <p>This takes a ResultSet containing two columns. Column 1 contains the function name, Column 2 * the oid.</p> * * <p>It reads the entire ResultSet, loading the values into the function table.</p> * * <p><b>REMEMBER</b> to close() the resultset after calling this!!</p> * * <p><b><em>Implementation note about function name lookups:</em></b></p> * * <p>PostgreSQL stores the function id's and their corresponding names in the pg_proc table. To * speed things up locally, instead of querying each function from that table when required, a * HashMap is used. Also, only the function's required are entered into this table, keeping * connection times as fast as possible.</p> * * <p>The org.postgresql.largeobject.LargeObject class performs a query upon it's startup, and passes * the returned ResultSet to the addFunctions() method here.</p> * * <p>Once this has been done, the LargeObject api refers to the functions by name.</p> * * <p>Don't think that manually converting them to the oid's will work. Ok, they will for now, but * they can change during development (there was some discussion about this for V7.0), so this is * implemented to prevent any unwarranted headaches in the future.</p> * * @param rs ResultSet * @throws SQLException if a database-access error occurs. * @see org.postgresql.largeobject.LargeObjectManager */
public void addFunctions(ResultSet rs) throws SQLException { while (rs.next()) { func.put(rs.getString(1), rs.getInt(2)); } }

This returns the function id associated by its name.

If addFunction() or addFunctions() have not been called for this name, then an SQLException is thrown.

Params:
  • name – Function name to lookup
Throws:
Returns:Function ID for fastpath call
/** * <p>This returns the function id associated by its name.</p> * * <p>If addFunction() or addFunctions() have not been called for this name, then an SQLException is * thrown.</p> * * @param name Function name to lookup * @return Function ID for fastpath call * @throws SQLException is function is unknown. */
public int getID(String name) throws SQLException { Integer id = func.get(name); // may be we could add a lookup to the database here, and store the result // in our lookup table, throwing the exception if that fails. // We must, however, ensure that if we do, any existing ResultSet is // unaffected, otherwise we could break user code. // // so, until we know we can do this (needs testing, on the TODO list) // for now, we throw the exception and do no lookups. if (id == null) { throw new PSQLException(GT.tr("The fastpath function {0} is unknown.", name), PSQLState.UNEXPECTED_ERROR); } return id; }
Creates a FastpathArg with an oid parameter. This is here instead of a constructor of FastpathArg because the constructor can't tell the difference between an long that's really int8 and a long thats an oid.
Params:
  • oid – input oid
Returns:FastpathArg with an oid parameter
/** * Creates a FastpathArg with an oid parameter. This is here instead of a constructor of * FastpathArg because the constructor can't tell the difference between an long that's really * int8 and a long thats an oid. * * @param oid input oid * @return FastpathArg with an oid parameter */
public static FastpathArg createOIDArg(long oid) { if (oid > Integer.MAX_VALUE) { oid -= NUM_OIDS; } return new FastpathArg((int) oid); } }