/*
* Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Rodrigo Westrupp
*/
package com.caucho.quercus.lib.db;
import com.caucho.quercus.UnimplementedException;
import com.caucho.quercus.annotation.NotNull;
import com.caucho.quercus.annotation.Optional;
import com.caucho.quercus.annotation.ReturnNullAsFalse;
import com.caucho.quercus.env.*;
import com.caucho.quercus.module.AbstractQuercusModule;
import com.caucho.util.CharBuffer;
import com.caucho.util.L10N;
import com.caucho.util.Log;
import com.caucho.vfs.Path;
import com.caucho.vfs.ReadStream;
import com.caucho.vfs.WriteStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
// Do not add new compile dependencies (use reflection instead)
// import org.postgresql.largeobject.*;
/**
* Quercus postgres routines.
*/
public class PostgresModule extends AbstractQuercusModule {
private static final Logger log = Log.open(PostgresModule.class);
private static final L10N L = new L10N(PostgresModule.class);
public static final int PGSQL_ASSOC = 0x01;
public static final int PGSQL_NUM = 0x02;
public static final int PGSQL_BOTH = 0x03;
public static final int PGSQL_CONNECT_FORCE_NEW = 0x04;
public static final int PGSQL_CONNECTION_BAD = 0x05;
public static final int PGSQL_CONNECTION_OK = 0x06;
public static final int PGSQL_SEEK_SET = 0x07;
public static final int PGSQL_SEEK_CUR = 0x08;
public static final int PGSQL_SEEK_END = 0x09;
public static final int PGSQL_EMPTY_QUERY = 0x0A;
public static final int PGSQL_COMMAND_OK = 0x0B;
public static final int PGSQL_TUPLES_OK = 0x0C;
public static final int PGSQL_COPY_OUT = 0x0D;
public static final int PGSQL_COPY_IN = 0x0E;
public static final int PGSQL_BAD_RESPONSE = 0x0F;
public static final int PGSQL_NONFATAL_ERROR = 0x10;
public static final int PGSQL_FATAL_ERROR = 0x11;
public static final int PGSQL_TRANSACTION_IDLE = 0x12;
public static final int PGSQL_TRANSACTION_ACTIVE = 0x13;
public static final int PGSQL_TRANSACTION_INTRANS = 0x14;
public static final int PGSQL_TRANSACTION_INERROR = 0x15;
public static final int PGSQL_TRANSACTION_UNKNOWN = 0x16;
public static final int PGSQL_DIAG_SEVERITY = 0x17;
public static final int PGSQL_DIAG_SQLSTATE = 0x18;
public static final int PGSQL_DIAG_MESSAGE_PRIMARY = 0x19;
public static final int PGSQL_DIAG_MESSAGE_DETAIL = 0x20;
public static final int PGSQL_DIAG_MESSAGE_HINT = 0x21;
public static final int PGSQL_DIAG_STATEMENT_POSITION = 0x22;
public static final int PGSQL_DIAG_INTERNAL_POSITION = 0x23;
public static final int PGSQL_DIAG_INTERNAL_QUERY = 0x24;
public static final int PGSQL_DIAG_CONTEXT = 0x25;
public static final int PGSQL_DIAG_SOURCE_FILE = 0x26;
public static final int PGSQL_DIAG_SOURCE_LINE = 0x27;
public static final int PGSQL_DIAG_SOURCE_FUNCTION = 0x28;
public static final int PGSQL_ERRORS_TERSE = 0x29;
public static final int PGSQL_ERRORS_DEFAULT = 0x2A;
public static final int PGSQL_ERRORS_VERBOSE = 0x2B;
public static final int PGSQL_STATUS_LONG = 0x2C;
public static final int PGSQL_STATUS_STRING = 0x2D;
public static final int PGSQL_CONV_IGNORE_DEFAULT = 0x2E;
public static final int PGSQL_CONV_FORCE_NULL = 0x2F;
/**
* Constructor
*/
public PostgresModule()
{
}
/**
* Returns true for the postgres extension.
*/
public String []getLoadedExtensions()
{
return new String[] { "postgres", "pgsql" };
}
/**
* Returns number of affected records (tuples)
*/
public static int pg_affected_rows(Env env,
@NotNull PostgresResult result)
{
try {
if (result == null)
return -1;
return result.getAffectedRows();
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return 0;
}
}
/**
* pg_affected_rows() alias.
*/
public static int pg_cmdtuples(Env env,
@NotNull PostgresResult result)
{
if (result == null)
return -1;
return pg_affected_rows(env, result);
}
/**
* Cancel an asynchronous query
*/
public static boolean pg_cancel_query(Env env,
@NotNull Postgres conn)
{
try {
if (conn == null)
return false;
conn.setAsynchronousStatement(null);
conn.setAsynchronousResult(null);
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* Gets the client encoding
*/
@ReturnNullAsFalse
public static String pg_client_encoding(Env env,
@Optional Postgres conn)
{
try {
if (conn == null)
conn = getConnection(env);
return conn.getClientEncoding();
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Closes a PostgreSQL connection
*/
public static boolean pg_close(Env env,
@Optional Postgres conn)
{
try {
if (conn == null)
return false;
if (conn == env.getSpecialValue("caucho.postgres"))
env.removeSpecialValue("caucho.postgres");
conn.close(env);
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
}
return false;
}
/**
* Open a PostgreSQL connection
*/
@ReturnNullAsFalse
public static Postgres pg_connect(Env env,
String connectionString,
@Optional int connectionType)
{
try {
String host = "localhost";
int port = 5432;
String dbName = "";
String userName = "";
String password = "";
HashMap<String, String> nameValueMap
= parseConnectionString(connectionString);
String value = nameValueMap.get("host");
if (value != null)
host = nameValueMap.get("host");
value = nameValueMap.get("port");
if (value != null) {
port = 0;
int len = value.length();
for (int i = 0; i < len; i++) {
char ch = value.charAt(i);
if ('0' <= ch && ch <= '9')
port = port * 10 + value.charAt(i) - '0';
else
break;
}
}
value = nameValueMap.get("dbname");
if (value != null)
dbName = value;
value = nameValueMap.get("user");
if (value != null)
userName = value;
value = nameValueMap.get("password");
if (value != null)
password = value;
String driver = "org.postgresql.Driver";
String url = "jdbc:postgresql://" + host + ":" + port + "/" + dbName;
Postgres postgres
= new Postgres(env, host, userName, password, dbName, port, driver, url);
if (! postgres.isConnected())
return null;
env.setSpecialValue("caucho.postgres", postgres);
return postgres;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Returns the name/value pairs from the postgres connection string.
*/
private static HashMap<String, String> parseConnectionString(String s)
{
HashMap<String, String> map = new HashMap<String, String>();
char ch;
int len = s.length();
int i = 0;
CharBuffer buffer = new CharBuffer();
while (i < len) {
buffer.clear();
// skip whitespace
for (; i < len && Character.isWhitespace(ch = s.charAt(i)); i++) {
}
// get name
for (;
i < len && ! Character.isWhitespace(ch = s.charAt(i))
&& ch != '='
; i++) {
buffer.append(ch);
}
String name = buffer.toString();
buffer.clear();
// skip until '='
while (i < len && (ch = s.charAt(i++)) != '=') {
}
// skip whitespace
for (; i < len && Character.isWhitespace(ch = s.charAt(i)); i++) {
}
boolean isQuoted = false;
// value may be quoted
if (i < len) {
if ((ch = s.charAt(i++)) == '\'')
isQuoted = true;
else
buffer.append(ch);
}
boolean isEscaped = false;
// get value
loop:
while (i < len) {
ch = s.charAt(i++);
switch(ch) {
case '\\':
if (isEscaped)
buffer.append(ch);
isEscaped = !isEscaped;
break;
case '\'':
if (isEscaped) {
buffer.append(ch);
isEscaped = false;
break;
}
else if (isQuoted)
break loop;
case ' ':
case '\n':
case '\r':
case '\f':
case '\t':
if (isQuoted) {
buffer.append(ch);
break;
}
else if (isEscaped) {
buffer.append('\\');
break loop;
}
else
break loop;
default:
if (isEscaped) {
buffer.append('\\');
isEscaped = false;
}
buffer.append(ch);
}
}
String value = buffer.toString();
if (name.length() > 0)
map.put(name, value);
}
return map;
}
/**
* Get connection is busy or not
*/
public static boolean pg_connection_busy(Env env,
@NotNull Postgres conn)
{
// Always return false, for now (pg_send_xxxx are not asynchronous)
// so there should be no reason for a connection to become busy in
// between different pg_xxx calls.
return false;
}
/**
* Reset connection (reconnect)
*/
public static boolean pg_connection_reset(Env env,
@NotNull Postgres conn)
{
try {
if (conn == null)
return false;
// Query database name before closing connection
String dbname = conn.getDbName();
conn.close(env);
conn = new Postgres(env,
conn.getHost(),
conn.getUserName(),
conn.getPassword(),
dbname,
conn.getPort(),
conn.getDriver(),
conn.getUrl());
env.setSpecialValue("caucho.postgres", conn);
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* Get connection status
*/
public static int pg_connection_status(Env env,
@NotNull Postgres conn)
{
try {
if (conn == null)
return PGSQL_CONNECTION_BAD;
boolean ping = pg_ping(env, conn);
return ping ? PGSQL_CONNECTION_OK : PGSQL_CONNECTION_BAD;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return PGSQL_CONNECTION_BAD;
}
}
/**
* Convert associative array values into suitable for SQL statement
*/
@ReturnNullAsFalse
public static ArrayValue pg_convert(Env env,
@NotNull Postgres conn,
String tableName,
ArrayValue assocArray,
@Optional("0") int options)
{
try {
if (conn == null)
return null;
// XXX: options has not been implemented yet.
// XXX: the following PHP note has not been implemented yet.
// Note: If there are boolean fields in table_name don't use
// the constant TRUE in assoc_array. It will be converted to the
// string 'TRUE' which is no valid entry for boolean fields in
// PostgreSQL. Use one of t, true, 1, y, yes instead.
if (options > 0) {
throw new UnimplementedException("pg_convert with options");
}
Connection jdbcConn = conn.getJavaConnection();
DatabaseMetaData dbMetaData = jdbcConn.getMetaData();
ResultSet rs = dbMetaData.getColumns("", "", tableName, "");
// Check column count
ResultSetMetaData rsMetaData = rs.getMetaData();
int n = rsMetaData.getColumnCount();
if (n < assocArray.getSize())
return null;
ArrayValueImpl newArray = new ArrayValueImpl();
// Keep track of column matches: assocArray vs. table columns
int matches = 0;
while (rs.next()) {
// Retrieve the original value to be converted
String columnName = rs.getString("COLUMN_NAME");
Value columnNameV = StringValue.create(columnName);
Value value = assocArray.get(columnNameV);
// Check for column not passed in
if (value == UnsetValue.UNSET)
continue;
matches++;
if (value.isNull()) {
value = StringValue.create("NULL");
// Add the converted value
newArray.put(columnNameV, value);
continue;
}
// Retrieve the database column type
int dataType = rs.getInt("DATA_TYPE");
// Convert the original value to the database type
switch (dataType) {
case Types.BIT:
case Types.TINYINT:
case Types.SMALLINT:
case Types.INTEGER:
case Types.BIGINT:
if (value.isLongConvertible()) {
value = LongValue.create(value.toLong());
} else {
StringValue sb = env.createUnicodeBuilder();
value = sb.append("'").append(value).append("'");
}
break;
case Types.DECIMAL:
case Types.DOUBLE:
case Types.FLOAT:
case Types.NUMERIC:
case Types.REAL:
if (value.isDoubleConvertible()) {
value = DoubleValue.create(value.toDouble());
} else {
StringValue sb = env.createUnicodeBuilder();
value = sb.append("'").append(value).append("'");
}
break;
default:
StringValue sb = env.createUnicodeBuilder();
if (value.isNumberConvertible()) {
value = sb.append(value);
} else {
value = sb.append("'").append(value).append("'");
}
}
// Add the converted value
newArray.put(columnNameV, value);
}
rs.close();
// Check if all columns were consumed. Otherwise, there are
// wrong column names passed in.
if (matches < assocArray.getSize()) {
return null;
}
return newArray;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Insert records into a table from an array
*/
public static boolean pg_copy_from(Env env,
@NotNull Postgres conn,
String tableName,
ArrayValue rows,
@Optional("") String delimiter,
@Optional("") String nullAs)
{
try {
if (conn == null)
return false;
// XXX: At the time this was implemented, the JDBC driver
// did not support SQL COPY operations that could simplify
// the code below.
String delimiterRegex;
if (delimiter.equals("")) {
delimiter = "\t";
delimiterRegex = "\\t";
} else {
// XXX: even the native php version does not seem to do it very well.
throw new UnimplementedException("pg_copy_from with non-default delimiter");
}
if (nullAs.equals("")) {
nullAs = "\\N";
} else {
// XXX: even the native php version does not seem to do it very well.
throw new UnimplementedException("pg_copy_from with non-default nullAs");
}
ArrayValueImpl array = (ArrayValueImpl) rows;
int size = array.size();
String baseInsert = "INSERT INTO " + tableName + " VALUES(";
StringBuilder sb = new StringBuilder(baseInsert);
int lenBaseInsert = sb.length();
for (int i=0; i<size; i++) {
// Every line has a new-line '\n' character and
// possibly many NULL values "\\N". Ex:
// line = "\\N\tNUMBER1col\t\\N\t\\N\tNUMBER2col\tNUMBER3col\tNUMBER4col\t\\N\n";
String line = array.get(LongValue.create(i)).toString();
line = line.substring(0, line.length()-1);
// "INSERT INTO " + tableName + " VALUES("
sb.setLength(lenBaseInsert);
// Split columns
String cols[] = line.split(delimiterRegex);
int len = cols.length;
if (len > 0) {
len--;
for (int j=0; j<len; j++) {
if (cols[j].equals(nullAs)) {
sb.append("NULL, ");
} else {
sb.append("'");
sb.append(cols[j]);
sb.append("', ");
}
}
if (cols[len].equals(nullAs)) {
sb.append("NULL)");
} else {
sb.append("'");
sb.append(cols[len]);
sb.append("')");
}
// Insert record
pg_query(env, conn, sb.toString());
}
}
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* Copy a table to an array
*/
@ReturnNullAsFalse
public static ArrayValue pg_copy_to(Env env,
@NotNull Postgres conn,
String tableName,
@Optional("") String delimiter,
@Optional("") String nullAs)
{
try {
if (conn == null)
return null;
// XXX: At the time this was implemented, the JDBC driver
// did not support SQL COPY operations that could simplify
// the code below.
// XXX: This should be replaced when @Optional("\t") is fixed.
if (delimiter.equals("")) {
delimiter = "\t";
}
// XXX: This should be replaced when @Optional("\\N") is fixed.
// Note: according to php.net, it must be \\N, i.e. the
// two-character sequence: {'\\', 'N'}
if (nullAs.equals("")) {
nullAs = "\\N";
}
PostgresResult result = pg_query(env, conn, "SELECT * FROM " + tableName);
ArrayValueImpl newArray = new ArrayValueImpl();
Object value;
int curr = 0;
while ((value = result.fetchArray(env, PGSQL_NUM)) != null) {
ArrayValueImpl arr = (ArrayValueImpl) value;
int count = arr.size();
StringValue sb = env.createUnicodeBuilder();
LongValue currValue = LongValue.create(curr);
for (int i=0; i<count; i++) {
if (sb.length() > 0)
sb.append(delimiter);
newArray.get(currValue);
Value fieldValue = arr.get(LongValue.create(i));
if (fieldValue instanceof NullValue) {
sb.append(nullAs);
} else {
sb.append(fieldValue.toString());
}
}
// Every line has a new-line character.
sb.append("\n");
newArray.put(currValue, sb);
curr++;
}
return newArray;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Get the database name
*/
@ReturnNullAsFalse
public static String pg_dbname(Env env,
@Optional Postgres conn)
{
try {
if (conn == null)
conn = getConnection(env);
return conn.getDbName();
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Deletes records
*/
public static boolean pg_delete(Env env,
@NotNull Postgres conn,
String tableName,
ArrayValue assocArray,
@Optional("-1") int options)
{
// From php.net: this function is EXPERIMENTAL.
// This function is EXPERIMENTAL. The behaviour of this function,
// the name of this function, and anything else documented about this function
// may change without notice in a future release of PHP.
// Use this function at your own risk.
try {
if (conn == null)
return false;
if (options > 0) {
throw new UnimplementedException("pg_delete with options");
}
StringBuilder condition = new StringBuilder();
boolean isFirst = true;
for (Map.Entry<Value,Value> entry : assocArray.entrySet()) {
Value k = entry.getKey();
Value v = entry.getValue();
if (isFirst) {
isFirst = false;
} else {
condition.append(" AND ");
}
condition.append(k.toString());
condition.append("='");
condition.append(v.toString());
condition.append("'");
}
StringBuilder query = new StringBuilder();
query.append("DELETE FROM ");
query.append(tableName);
query.append(" WHERE ");
query.append(condition);
pg_query(env, conn, query.toString());
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* Sync with PostgreSQL backend
*/
public static boolean pg_end_copy(Env env,
@Optional Postgres conn)
{
env.stub("pg_end_copy");
return false;
}
/**
* Escape a string for insertion into a bytea field
*/
@ReturnNullAsFalse
public static StringValue pg_escape_bytea(Env env,
StringValue data)
{
if (data.length() == 0)
return data;
try {
Class<?> cl = Class.forName("org.postgresql.util.PGbytea");
Method method = cl.getDeclaredMethod("toPGString", new Class[] {byte[].class});
String s = (String) method.invoke(cl, new Object[] { data.toBytes()});
return Postgres.pgRealEscapeString(env.createString(s, "UTF-8" /* XXX */));
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Escape a string for insertion into a bytea field.
*/
@ReturnNullAsFalse
public static StringValue pg_escape_bytea(Env env,
@NotNull Postgres conn,
StringValue data)
{
return pg_escape_bytea(env, data);
}
/**
* Escape a string for insertion into a text field
*/
@ReturnNullAsFalse
public static StringValue pg_escape_string(Env env,
StringValue data)
{
try {
Postgres conn = getConnection(env);
if (conn == null)
return null;
return conn.realEscapeString(data);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Sends a request to execute a prepared statement with given parameters,
* and waits for the result
*/
@ReturnNullAsFalse
public static PostgresResult pg_execute(Env env,
@NotNull Postgres conn,
String stmtName,
ArrayValue params)
{
try {
if (conn == null)
return null;
PostgresStatement pstmt = conn.getStatement(stmtName);
return executeInternal(env, conn, pstmt, params);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
conn.setResultResource(null);
return null;
}
}
/**
* Fetches all rows in a particular result column as an array
*/
@ReturnNullAsFalse
public static ArrayValue pg_fetch_all_columns(Env env,
@NotNull PostgresResult result,
@Optional("0") int column)
{
try {
if (result == null)
return null;
ArrayValueImpl newArray = new ArrayValueImpl();
int curr = 0;
for (ArrayValue row = result.fetchRow(env);
row != null;
row = result.fetchRow(env)) {
newArray.put(LongValue.create(curr++),
row.get(LongValue.create(column)));
}
if (newArray.getSize() > 0) {
return newArray;
}
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
}
return null;
}
/**
* Fetches all rows from a result as an array
*/
@ReturnNullAsFalse
public static ArrayValue pg_fetch_all(Env env,
@NotNull PostgresResult result)
{
try {
if (result == null)
return null;
ArrayValueImpl newArray = new ArrayValueImpl();
int curr = 0;
for (ArrayValue row = result.fetchAssoc(env);
row != null;
row = result.fetchAssoc(env)) {
newArray.put(LongValue.create(curr++), row);
}
if (newArray.getSize() > 0) {
return newArray;
}
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
}
return null;
}
/**
* Fetch a row as an array
*/
@ReturnNullAsFalse
public static ArrayValue pg_fetch_array(Env env,
@NotNull PostgresResult result,
@Optional("-1") Value row,
@Optional("PGSQL_BOTH") int resultType)
{
try {
if (result == null)
return null;
// NOTE: pg_fetch_array has an interesting memory feature.
// Calls to pg_fetch_array usually return the next row for
// successive calls. There is an exception though.
// The first time a NULL row is passed in, the previously
// returned row is returned again. After that, successive
// calls return the next row as usual.
// We set a flag for this. See PostgresResult and php/4342
if (row.isNull()) {
if (result.getPassedNullRow()) {
result.setPassedNullRow();
} else {
// Step the cursor back to the previous position
ResultSet rs = result.getResultSet();
rs.previous();
}
}
// NOTE: row is of type Value because row is optional and there is
// only one way to specify that 'row' will not be used:
//
// pg_fetch_array(result, NULL, resultType)
//
// The resultType will be used above though.
//
// For such a case, the marshalling code passes row in as NullValue.NULL
// If we used 'int row' there would be no way to distinguish row 'zero'
// from row 'null'.
if (result == null)
return null;
if (row.isLongConvertible() && row.toInt() >= 0) {
if (!result.seek(env, row.toInt())) {
env.warning(L.l("Unable to jump to row {0} on PostgreSQL result",
row.toInt()));
return null;
}
}
return result.fetchArray(env, resultType);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Fetch a row as an associative array
*/
@ReturnNullAsFalse
public static ArrayValue pg_fetch_assoc(Env env,
@NotNull PostgresResult result,
@Optional("-1") Value row)
{
try {
if (result == null)
return null;
if (! row.isNull() && row.toInt() >= 0) {
result.seek(env, row.toInt());
}
return result.fetchAssoc(env);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Fetch a row as an object
*/
public static Value pg_fetch_object(Env env,
@NotNull PostgresResult result,
@Optional("-1") Value row,
@Optional int resultType)
{
try {
if (result == null)
return null;
//@todo use optional resultType
if ((row != null) && (!row.equals(NullValue.NULL)) && (row.toInt() >= 0)) {
result.seek(env, row.toInt());
}
Value resultValue = result.fetchObject(env);
// php/430l
if (resultValue.isNull())
return BooleanValue.FALSE;
else
return resultValue;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return BooleanValue.FALSE;
}
}
/**
* Returns values from a result resource
*/
public static Value pg_fetch_result(Env env,
@NotNull PostgresResult result,
Value row,
@Optional("-1") Value fieldNameOrNumber)
{
try {
if (result == null)
return null;
// NOTE: row is of type Value because there is a case where
// row is optional. In such a case, the row value passed in
// is actually the field number or field name.
int rowNumber = -1;
// Handle the case: optional row with mandatory fieldNameOrNumber.
if (fieldNameOrNumber.isLongConvertible() &&
(fieldNameOrNumber.toInt() < 0)) {
fieldNameOrNumber = row;
rowNumber = -1;
} else {
rowNumber = row.toInt();
}
if (rowNumber >= 0) {
result.seek(env, rowNumber);
}
Value fetchRow = result.fetchRow(env);
int fieldNumber = result.getColumnNumber(fieldNameOrNumber, 0);
return fetchRow.get(LongValue.create(fieldNumber));
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return BooleanValue.FALSE;
}
}
/**
* Returns values from a result resource
*/
public static Value pg_result(Env env,
@NotNull PostgresResult result,
Value row,
@Optional("-1") Value fieldNameOrNumber)
{
return pg_fetch_result(env, result, row, fieldNameOrNumber);
}
/**
* Get a row as an enumerated array
*/
@ReturnNullAsFalse
public static ArrayValue pg_fetch_row(Env env,
@NotNull PostgresResult result,
@Optional("-1") Value row)
{
try {
if (result == null)
return null;
if (row != null && ! row.equals(NullValue.NULL) && row.toInt() >= 0) {
result.seek(env, row.toInt());
}
return result.fetchRow(env);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Test if a field is SQL NULL
*/
@ReturnNullAsFalse
public static LongValue pg_field_is_null(Env env,
@NotNull PostgresResult result,
Value row,
@Optional("-1") Value fieldNameOrNumber)
{
try {
if (result == null)
return null;
// NOTE: row is of type Value because there is a case where
// row is optional. In such a case, the row value passed in
// is actually the field number or field name.
int rowNumber = -1;
// Handle the case: optional row with mandatory fieldNameOrNumber.
if (fieldNameOrNumber.isLongConvertible() &&
(fieldNameOrNumber.toInt() == -1)) {
fieldNameOrNumber = row;
rowNumber = -1;
} else {
rowNumber = row.toInt();
}
if (rowNumber >= 0) {
if (!result.seek(env, rowNumber)) {
env.warning(L.l("Unable to jump to row {0} on PostgreSQL result",
rowNumber));
return null;
}
}
int fieldNumber = result.getColumnNumber(fieldNameOrNumber, 0);
Value field = pg_fetch_result(env,
result,
LongValue.MINUS_ONE,
LongValue.create(fieldNumber));
if (field == null || field.isNull())
return LongValue.ONE;
else
return LongValue.ZERO;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* pg_field_is_null() alias.
*/
@ReturnNullAsFalse
public static LongValue pg_fieldisnull(Env env,
@NotNull PostgresResult result,
Value row,
@Optional("-1") Value fieldNameOrNumber)
{
return pg_field_is_null(env, result, row, fieldNameOrNumber);
}
/**
* Returns the name of a field
*/
public static Value pg_field_name(Env env,
@NotNull PostgresResult result,
int fieldNumber)
{
try {
if (result == null)
return BooleanValue.FALSE;
return result.getFieldName(env, fieldNumber);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return BooleanValue.FALSE;
}
}
/**
* pg_field_name() alias.
*/
public static Value pg_fieldname(Env env,
@NotNull PostgresResult result,
int fieldNumber)
{
return pg_field_name(env, result, fieldNumber);
}
/**
* Returns the field number of the named field
*
* @return the field number (0-based) or -1 on error
*/
public static int pg_field_num(Env env,
@NotNull PostgresResult result,
String fieldName)
{
try {
if (result == null)
return -1;
return result.getColumnNumber(fieldName);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return -1;
}
}
/**
* pg_field_num() alias.
*/
public static int pg_fieldnum(Env env,
@NotNull PostgresResult result,
String fieldName)
{
return pg_field_num(env, result, fieldName);
}
/**
* Returns the printed length
*/
public static int pg_field_prtlen(Env env,
@NotNull PostgresResult result,
Value rowNumber,
@Optional("-1") Value fieldNameOrNumber)
{
try {
if (result == null)
return -1;
int row = rowNumber.toInt();
if (fieldNameOrNumber.toString().equals("-1")) {
fieldNameOrNumber = rowNumber;
row = -1;
}
int fieldNumber = result.getColumnNumber(fieldNameOrNumber, 0);
ResultSetMetaData metaData = result.getMetaData();
String typeName = metaData.getColumnTypeName(fieldNumber+1);
if (typeName.equals("bool")) {
return 1;
}
Value value = pg_fetch_result(env,
result,
LongValue.create(row),
LongValue.create(fieldNumber));
// Step the cursor back to the original position
// See php/430p
result.getResultSet().previous();
int len = value.toString().length();
// XXX: check this...
// if (typeName.equals("money")) {
// len++;
// }
return len;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return -1;
}
}
/**
* pg_field_ptrlen() alias.
*/
public static int pg_fieldprtlen(Env env,
@NotNull PostgresResult result,
Value rowNumber,
@Optional("-1") Value fieldNameOrNumber)
{
return pg_field_prtlen(env, result, rowNumber, fieldNameOrNumber);
}
/**
* Returns the internal storage size of the named field
*/
@ReturnNullAsFalse
public static LongValue pg_field_size(Env env,
@NotNull PostgresResult result,
int fieldNumber)
{
try {
if (result == null)
return LongValue.create(-1);
ResultSetMetaData metaData = result.getMetaData();
fieldNumber++;
int dataType = metaData.getColumnType(fieldNumber);
int size = -1;
switch (dataType) {
case Types.BIT:
{
String typeName = metaData.getColumnTypeName(fieldNumber);
if (typeName.equals("bool")) {
size = 1;
}
break;
}
case Types.TINYINT:
size = 1;
break;
case Types.SMALLINT:
size = 2;
break;
case Types.DATE:
case Types.FLOAT:
case Types.INTEGER:
case Types.REAL:
size = 4;
break;
case Types.BIGINT:
case Types.DOUBLE:
{
size = 8;
String typeName = metaData.getColumnTypeName(fieldNumber);
if (typeName.equals("money")) {
size = 4;
}
}
break;
case Types.TIME:
case Types.TIMESTAMP:
size = 8;
// fall to specific cases
default:
{
String typeName = metaData.getColumnTypeName(fieldNumber);
if (typeName.equals("timetz") ||
typeName.equals("interval")) {
size = 12;
} else if (typeName.equals("macaddr")) {
size = 6;
} else if (typeName.equals("point")) {
size = 16;
} else if (typeName.equals("circle")) {
size = 24;
} else if (typeName.equals("box") ||
typeName.equals("lseg")) {
size = 32;
}
}
}
return LongValue.create(size);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* pg_field_size() alias.
*/
@ReturnNullAsFalse
public static LongValue pg_fieldsize(Env env,
@NotNull PostgresResult result,
int fieldNumber)
{
return pg_field_size(env, result, fieldNumber);
}
/**
* Returns the name or oid of the tables field
*
* @return By default the tables name that field belongs to
* is returned but if oid_only is set to TRUE,
* then the oid will instead be returned.
*/
@ReturnNullAsFalse
public static String pg_field_table(Env env,
@NotNull PostgresResult result,
int fieldNumber,
@Optional("false") boolean oidOnly)
{
// The Postgres JDBC driver doesn't have a concept of exposing to the client
// what table maps to a particular select item in a result set, therefore the
// driver cannot report anything useful to the caller. Thus the driver always
// returns "" to ResultSetMetaData.getTableName(fieldNumber+1)
env.stub("pg_field_table");
return "";
}
/**
* Returns the type ID (OID) for the corresponding field number
*/
@ReturnNullAsFalse
public static LongValue pg_field_type_oid(Env env,
@NotNull PostgresResult result,
int fieldNumber)
{
try {
if (result == null)
return null;
ResultSetMetaData metaData = result.getMetaData();
String columnTypeName = metaData.getColumnTypeName(fieldNumber + 1);
String metaQuery = ("SELECT oid FROM pg_type WHERE typname='" + columnTypeName + "'");
result = pg_query(env, (Postgres) result.getConnection(), metaQuery);
Value value = pg_fetch_result(env,
result,
LongValue.MINUS_ONE,
LongValue.ZERO);
if (value.isLongConvertible()) {
return LongValue.create(value.toLong());
}
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
}
return null;
}
/**
* Returns the type name for the corresponding field number
*/
@ReturnNullAsFalse
public static StringValue pg_field_type(Env env,
@NotNull PostgresResult result,
int fieldNumber)
{
try {
if (result == null)
return null;
ResultSetMetaData metaData = result.getMetaData();
fieldNumber++;
String typeName = metaData.getColumnTypeName(fieldNumber);
return (StringValue) StringValue.create(typeName);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* pg_field_type() alias.
*/
@ReturnNullAsFalse
public static StringValue pg_fieldtype(Env env,
@NotNull PostgresResult result,
int fieldNumber)
{
return pg_field_type(env, result, fieldNumber);
}
/**
* Free result memory
*/
public static boolean pg_free_result(Env env,
PostgresResult result)
{
try {
if (result == null)
return true;
result.close();
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return true;
}
}
/**
* pg_free_result() alias.
*/
public static boolean pg_freeresult(Env env,
PostgresResult result)
{
if (result == null)
return true;
return pg_free_result(env, result);
}
/**
* Gets SQL NOTIFY message
*/
@ReturnNullAsFalse
public static ArrayValue pg_get_notify(Env env,
@NotNull Postgres conn,
@Optional("-1") int resultType)
{
try {
if (conn == null)
return null;
if (resultType > 0) {
throw new UnimplementedException("pg_get_notify with result type");
}
// org.postgresql.PGConnection
Class<?> cl = Class.forName("org.postgresql.PGConnection");
// public PGNotification[] getNotifications() throws SQLException;
Method method = cl.getDeclaredMethod("getNotifications");
Connection pgconn = conn.getJavaConnection();
// getNotifications()
Object notifications[] = (Object[]) method.invoke(pgconn);
// org.postgresql.PGNotification
cl = Class.forName("org.postgresql.PGNotification");
// public String getName();
Method methodGetName = cl.getDeclaredMethod("getName");
// public int getPID();
Method methodGetPID = cl.getDeclaredMethod("getPID");
ArrayValueImpl arrayValue = new ArrayValueImpl();
int n = notifications.length;
StringValue k;
LongValue v;
for (int i=0; i<n; i++) {
// getName()
k = (StringValue) StringValue.create(methodGetName.invoke(notifications[i],
new Object[] {}));
// getPID()
v = (LongValue) LongValue.create((Integer) methodGetPID.invoke(notifications[i],
new Object[] {}));
arrayValue.put(k, v);
}
return arrayValue;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Gets the backend's process ID
*/
public static int pg_get_pid(Env env,
@NotNull Postgres conn)
{
try {
if (conn == null)
return -1;
// @todo create a random string
String randomLabel = "caucho_pg_get_pid_random_label";
pg_query(env, conn, "LISTEN "+randomLabel);
pg_query(env, conn, "NOTIFY "+randomLabel);
ArrayValue arrayValue = pg_get_notify(env, conn, -1);
LongValue pid = (LongValue) arrayValue.get(StringValue.create(randomLabel));
return pid.toInt();
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return -1;
}
}
/**
* Get asynchronous query result
*/
@ReturnNullAsFalse
public static PostgresResult pg_get_result(Env env,
@Optional Postgres conn)
{
// Three different scenarios for pg_get_result:
//
// 1. pg_send_prepare/pg_send_execute - php/431m
//
// pg_send_prepare($conn, "my_query", 'SELECT * FROM test WHERE data = $1');
// $res1 = pg_get_result($conn);
//
// pg_send_execute($conn, "my_query", array("Joe's Widgets"));
// $res2 = pg_get_result($conn);
//
// pg_send_execute($conn, "my_query", array("Clothes Clothes Clothes"));
// $res3 = pg_get_result($conn);
//
// 2. Multiquery with pg_send_query - php/430y
//
// pg_send_query($conn, "select * from test; select count(*) from test;");
//
// // select * from test
// $res = pg_get_result($conn);
// $rows = pg_num_rows($res);
//
// // select count(*) from test
// $res = pg_get_result($conn);
// $rows = pg_num_rows($res);
//
// 3. Individual pg_send_query - php/431g
//
// $res = pg_send_query($conn, "select * from test;");
// var_dump($res);
// $res = pg_get_result($conn);
// var_dump($res);
//
// $res = pg_send_query($conn, "select * from doesnotexist;");
// var_dump($res);
// $res = pg_get_result($conn);
// var_dump($res);
try {
if (conn == null)
conn = getConnection(env);
PostgresResult result = (PostgresResult) conn.getResultResource();
// 1. pg_send_prepare/pg_send_execute
if (conn.getAsynchronousStatement() != null) {
if (conn.getAsynchronousResult() != null) {
conn.setAsynchronousResult(null);
return result;
}
return null;
}
// 2. pg_send_query
if (conn.getAsynchronousResult() != null) {
// Check for next result
// Ex: pg_send_query($conn, "select * from test; select count(*) from test;");
Statement stmt = result.getJavaStatement();
if (stmt.getMoreResults()) {
result = (PostgresResult) conn.createResult(env, stmt,
stmt.getResultSet());
} else {
// 3. Individual pg_send_query (clean up; no futher results)
conn.setResultResource(null);
}
}
conn.setAsynchronousResult(result);
return result;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Returns the host name associated with the connection
*/
@ReturnNullAsFalse
public static String pg_host(Env env,
@Optional Postgres conn)
{
try {
if (conn == null)
conn = getConnection(env);
return conn.getHost();
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Insert array into table
*/
public static boolean pg_insert(Env env,
@NotNull Postgres conn,
String tableName,
ArrayValue assocArray,
@Optional("-1") int options)
{
try {
if (conn == null)
return false;
if (options > 0) {
throw new UnimplementedException("pg_insert with options");
}
StringBuilder names = new StringBuilder();
StringBuilder values = new StringBuilder();
boolean isFirst = true;
for (Map.Entry<Value,Value> entry : assocArray.entrySet()) {
Value k = entry.getKey();
Value v = entry.getValue();
if (isFirst) {
isFirst = false;
} else {
values.append("','");
names.append(",");
}
values.append(v.toString());
names.append(k.toString());
}
StringBuilder query = new StringBuilder();
query.append("INSERT INTO ");
query.append(tableName);
query.append("(");
query.append(names);
query.append(") VALUES('");
query.append(values);
query.append("')");
pg_query(env, conn, query.toString());
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* Get the last error message string of a connection
*/
@ReturnNullAsFalse
public static StringValue pg_last_error(Env env,
@Optional Postgres conn)
{
try {
if (conn == null)
conn = getConnection(env);
return conn.error(env);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* pg_last_error() alias.
*/
@ReturnNullAsFalse
public static StringValue pg_errormessage(Env env,
@Optional Postgres conn)
{
return pg_last_error(env, conn);
}
/**
* Returns the last notice message from PostgreSQL server
*/
@ReturnNullAsFalse
public static String pg_last_notice(Env env,
@NotNull Postgres conn)
{
try {
if (conn == null)
return null;
SQLWarning warning = conn.getWarnings();
if (warning != null)
return warning.toString();
else
return null;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Returns the last row's OID
*
* Note that:
* - OID is a unique id. It will not work if the table was created with "No oid".
* - MySql's "mysql_insert_id" receives the conection handler as argument but
* PostgreSQL's "pg_last_oid" uses the result handler.
*/
@ReturnNullAsFalse
public static String pg_last_oid(Env env,
PostgresResult result)
{
try {
Statement stmt = result.getJavaStatement();
Class<?> cl = Class.forName("org.postgresql.jdbc2.AbstractJdbc2Statement");
Method method = cl.getDeclaredMethod("getLastOID");
int oid = Integer.parseInt(method.invoke(stmt, new Object[] {}).toString());
if (oid > 0)
return ""+oid;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
}
return null;
}
@ReturnNullAsFalse
public static String pg_getlastoid(Env env,
PostgresResult result)
{
return pg_last_oid(env, result);
}
/**
* Close a large object
*/
public static boolean pg_lo_close(Env env,
Object largeObject)
{
try {
Class<?> cl = Class.forName("org.postgresql.largeobject.LargeObject");
Method method = cl.getDeclaredMethod("close");
method.invoke(largeObject, new Object[] {});
// largeObject.close();
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* pg_lo_close() alias.
*/
public static boolean pg_loclose(Env env,
Object largeObject)
{
return pg_lo_close(env, largeObject);
}
/**
* Create a large object
*/
@ReturnNullAsFalse
public static LongValue pg_lo_create(Env env,
@Optional Postgres conn)
{
try {
int oid = -1;
if (conn == null)
conn = getConnection(env);
// LargeObjectManager lobManager;
Object lobManager;
// org.postgresql.PGConnection
Class<?> cl = Class.forName("org.postgresql.PGConnection");
Method method = cl.getDeclaredMethod("getLargeObjectAPI");
Connection pgconn = conn.getJavaConnection();
// Large Objects may not be used in auto-commit mode.
pgconn.setAutoCommit(false);
lobManager = method.invoke(pgconn, new Object[] {});
// lobManager = ((org.postgresql.PGConnection)conn).getLargeObjectAPI();
// org.postgresql.largeobject.LargeObjectManager
cl = Class.forName("org.postgresql.largeobject.LargeObjectManager");
method = cl.getDeclaredMethod("create");
Object oidObj = method.invoke(lobManager, new Object[] {});
oid = Integer.parseInt(oidObj.toString());
// oid = lobManager.create();
return LongValue.create(oid);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* pg_lo_create() alias
*/
@ReturnNullAsFalse
public static LongValue pg_locreate(Env env,
@Optional Postgres conn)
{
return pg_lo_create(env, conn);
}
/**
* Export a large object to a file
*/
public static boolean pg_lo_export(Env env,
@NotNull Postgres conn,
int oid,
Path path)
{
try {
if (conn == null)
return false;
//@todo conn should be optional
// LargeObjectManager lobManager;
Object lobManager;
//org.postgresql.largeobject.LargeObjectManager
Class<?> cl = Class.forName("org.postgresql.PGConnection");
Method method = cl.getDeclaredMethod("getLargeObjectAPI");
Connection pgconn = conn.getJavaConnection();
lobManager = method.invoke(pgconn, new Object[] {});
// lobManager = ((org.postgresql.PGConnection)conn).getLargeObjectAPI();
cl = Class.forName("org.postgresql.largeobject.LargeObjectManager");
method = cl.getDeclaredMethod("open", new Class[] {Integer.TYPE});
Object lobj = method.invoke(lobManager, new Object[] {oid});
cl = Class.forName("org.postgresql.largeobject.LargeObject");
method = cl.getDeclaredMethod("getInputStream");
Object isObj = method.invoke(lobj, new Object[] {});
InputStream is = (InputStream)isObj;
// Open the file
WriteStream os = path.openWrite();
// copy the data from the large object to the file
os.writeStream(is);
os.close();
is.close();
// Close the large object
method = cl.getDeclaredMethod("close");
method.invoke(lobj, new Object[] {});
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* pg_lo_export() alias.
*/
public static boolean pg_loexport(Env env,
@NotNull Postgres conn,
int oid,
Path path)
{
return pg_lo_export(env, conn, oid, path);
}
/**
* Import a large object from file
*/
@ReturnNullAsFalse
public static LongValue pg_lo_import(Env env,
@NotNull Postgres conn,
Path path)
{
try {
if (conn == null)
return null;
//@todo conn should be optional
LongValue value = pg_lo_create(env, conn);
if (value != null) {
int oid = value.toInt();
Object largeObject = pg_lo_open(env, conn, oid, "w");
// Open the file
ReadStream is = path.openRead();
writeLobInternal(largeObject, is, Integer.MAX_VALUE);
pg_lo_close(env, largeObject);
is.close();
return LongValue.create(oid);
}
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
}
return null;
}
/**
* pg_lo_import() alias.
*/
@ReturnNullAsFalse
public static LongValue pg_loimport(Env env,
@NotNull Postgres conn,
Path path)
{
return pg_lo_import(env, conn, path);
}
/**
* Open a large object
*/
@ReturnNullAsFalse
public static Object pg_lo_open(Env env,
@NotNull Postgres conn,
int oid,
String mode)
{
try {
if (conn == null)
return null;
Object largeObject = null;
// LargeObjectManager lobManager;
Object lobManager;
//org.postgresql.largeobject.LargeObjectManager
Class<?> cl = Class.forName("org.postgresql.PGConnection");
Method method = cl.getDeclaredMethod("getLargeObjectAPI");
Connection pgconn = conn.getJavaConnection();
lobManager = method.invoke(pgconn, new Object[] {});
cl = Class.forName("org.postgresql.largeobject.LargeObjectManager");
method = cl.getDeclaredMethod("open", new Class[] {Integer.TYPE, Integer.TYPE});
boolean write = mode.indexOf("w") >= 0;
boolean read = mode.indexOf("r") >= 0;
int modeREAD = cl.getDeclaredField("READ").getInt(null);
int modeREADWRITE = cl.getDeclaredField("READWRITE").getInt(null);
int modeWRITE = cl.getDeclaredField("WRITE").getInt(null);
int intMode = modeREAD;
if (read) {
if (write) {
intMode = modeREADWRITE;
}
} else if (write) {
intMode = modeWRITE;
}
largeObject = method.invoke(lobManager, new Object[] {oid, intMode});
return largeObject;
} catch (Exception ex) {
env.warning(L.l("Unable to open PostgreSQL large object"));
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* pg_lo_open() alias.
*/
@ReturnNullAsFalse
public static Object pg_loopen(Env env,
@NotNull Postgres conn,
int oid,
String mode)
{
return pg_lo_open(env, conn, oid, mode);
}
/**
* Reads an entire large object and send straight to browser
*/
@ReturnNullAsFalse
public static LongValue pg_lo_read_all(Env env,
Object largeObject)
{
try {
StringValue contents = pg_lo_read(env, largeObject, -1);
if (contents != null) {
env.getOut().print(contents);
}
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
}
return null;
}
/**
* pg_lo_read_all() alias.
*/
@ReturnNullAsFalse
public static LongValue pg_loreadall(Env env,
Object largeObject)
{
return pg_lo_read_all(env, largeObject);
}
/**
* Read a large object
*/
@ReturnNullAsFalse
public static StringValue pg_lo_read(Env env,
Object largeObject,
@Optional("-1") int len)
{
try {
if (len < 0) {
len = Integer.MAX_VALUE;
}
Class<?> cl = Class.forName("org.postgresql.largeobject.LargeObject");
Method method = cl.getDeclaredMethod("getInputStream");
InputStream is = (InputStream) method.invoke(largeObject, new Object[] {});
try {
StringValue bb = env.createBinaryBuilder();
bb.appendReadAll(is, len);
return bb;
} finally {
is.close();
}
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* pg_lo_read() alias.
*/
@ReturnNullAsFalse
public static StringValue pg_loread(Env env,
Object largeObject,
@Optional("-1") int len)
{
return pg_lo_read(env, largeObject, len);
}
/**
* Seeks position within a large object
*/
public static boolean pg_lo_seek(Env env,
Object largeObject,
int offset,
@Optional int whence)
{
try {
Class<?> cl = Class.forName("org.postgresql.largeobject.LargeObject");
int seekSET = cl.getDeclaredField("SEEK_SET").getInt(null);
int seekEND = cl.getDeclaredField("SEEK_END").getInt(null);
int seekCUR = cl.getDeclaredField("SEEK_CUR").getInt(null);
switch (whence) {
case PGSQL_SEEK_SET:
whence = seekSET;
break;
case PGSQL_SEEK_END:
whence = seekEND;
break;
default:
whence = seekCUR;
}
Method method = cl.getDeclaredMethod("seek", new Class[]{Integer.TYPE,Integer.TYPE});
method.invoke(largeObject, new Object[] {offset, whence});
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* Returns current seek position a of large object
*/
public static int pg_lo_tell(Env env,
Object largeObject)
{
try {
Class<?> cl = Class.forName("org.postgresql.largeobject.LargeObject");
Method method = cl.getDeclaredMethod("tell");
Object obj = method.invoke(largeObject, new Object[] {});
return Integer.parseInt(obj.toString());
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return -1;
}
}
/**
* Delete a large object
*/
public static boolean pg_lo_unlink(Env env,
@NotNull Postgres conn,
int oid)
{
try {
if (conn == null)
return false;
// LargeObjectManager lobManager;
Object lobManager;
//org.postgresql.largeobject.LargeObjectManager
Class<?> cl = Class.forName("org.postgresql.PGConnection");
Method method = cl.getDeclaredMethod("getLargeObjectAPI");
Connection pgconn = conn.getJavaConnection();
lobManager = method.invoke(pgconn, new Object[] {});
cl = Class.forName("org.postgresql.largeobject.LargeObjectManager");
method = cl.getDeclaredMethod("unlink", Integer.TYPE);
method.invoke(lobManager, new Object[] {oid});
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* pg_lo_unlink() alias.
*/
public static boolean pg_lounlink(Env env,
@NotNull Postgres conn,
int oid)
{
return pg_lo_unlink(env, conn, oid);
}
/**
* Write to a large object
*/
@ReturnNullAsFalse
public static LongValue pg_lo_write(Env env,
@NotNull Object largeObject,
String data,
@Optional int len)
{
try {
if (largeObject == null)
return null;
if (len <= 0) {
len = data.length();
}
int written = len;
Class<?> cl = Class.forName("org.postgresql.largeobject.LargeObject");
Method method = cl.getDeclaredMethod("write",
new Class[] {byte[].class,
Integer.TYPE,
Integer.TYPE});
method.invoke(largeObject, new Object[] {data.getBytes(), 0, len});
return LongValue.create(written);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* pg_lo_write() alias.
*/
@ReturnNullAsFalse
public static LongValue pg_lowrite(Env env,
@NotNull Object largeObject,
String data,
@Optional int len)
{
return pg_lo_write(env, largeObject, data, len);
}
/**
* Get meta data for table
*/
@ReturnNullAsFalse
public static ArrayValue pg_meta_data(Env env,
@NotNull Postgres conn,
String tableName)
{
env.stub("pg_meta_data");
return null;
}
/**
* Returns the number of fields in a result
*/
public static int pg_num_fields(Env env,
@NotNull PostgresResult result)
{
try {
return result.getFieldCount();
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return -1;
}
}
/**
* pg_num_fields() alias.
*/
public static int pg_numfields(Env env,
@NotNull PostgresResult result)
{
return pg_num_fields(env, result);
}
/**
* Returns the number of rows in a result
*/
public static LongValue pg_num_rows(Env env,
@NotNull PostgresResult result)
{
int numRows = -1;
try {
if (result == null)
return LongValue.create(-1);
if ((result != null) && (result.getResultSet() != null)) {
numRows = result.getNumRows();
}
if (numRows < 0) {
env.warning(L.l("supplied argument is not a valid PostgreSQL result resource"));
}
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
}
return LongValue.create(numRows);
}
/**
* pg_num_rows() alias.
*/
public static LongValue pg_numrows(Env env,
@NotNull PostgresResult result)
{
return pg_num_rows(env, result);
}
/**
* Get the options associated with the connection
*/
public static String pg_options(Env env,
@Optional Postgres conn)
{
throw new UnimplementedException("pg_options");
}
/**
* Looks up a current parameter setting of the server
*/
public static Value pg_parameter_status(Env env,
@NotNull Postgres conn,
@NotNull StringValue paramName)
{
try {
if (conn == null || paramName == null)
return BooleanValue.FALSE;
PostgresResult result = pg_query(env, conn, "SHOW "+paramName);
Value value = pg_fetch_result(env, result, LongValue.ZERO, LongValue.ZERO);
if (value == null || value.isNull())
return BooleanValue.FALSE;
if (paramName.toString().equals("server_encoding")) {
if (value.equals(env.createString("UNICODE", "UTF-8" /* XXX */)))
value = env.createString("UTF8", "UTF-8" /* XXX */);
}
return value;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return BooleanValue.FALSE;
}
}
/**
* Looks up a current parameter setting of the server
*/
public static Value pg_parameter_status(Env env,
@NotNull StringValue paramName)
{
Postgres conn = getConnection(env);
return pg_parameter_status(env, conn, paramName);
}
/**
* Open a persistent PostgreSQL connection
*/
@ReturnNullAsFalse
public static Postgres pg_pconnect(Env env,
String connectionString,
@Optional int connectType)
{
return pg_connect(env, connectionString, connectType);
}
/**
* Ping database connection
*/
public static boolean pg_ping(Env env,
@Optional Postgres conn)
{
try {
if (conn == null)
conn = getConnection(env);
return pg_query(env, conn, "SELECT 1") != null;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* Return the port number associated with the connection
*/
@ReturnNullAsFalse
public static StringValue pg_port(Env env,
@Optional Postgres conn)
{
try {
if (conn == null)
conn = getConnection(env);
// In PHP, a real pg_port test case returns String
return (StringValue) StringValue.create(conn.getPort());
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Submits a request to create a prepared statement with the given parameters,
* and waits for completion
*/
@ReturnNullAsFalse
public static PostgresStatement pg_prepare(Env env,
@NotNull Postgres conn,
String stmtName,
String query)
{
try {
if (conn == null)
return null;
PostgresStatement pstmt = conn.prepare(env, env.createString(query, "UTF-8" /* XXX */));
conn.putStatement(stmtName, pstmt);
return pstmt;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Send a NULL-terminated string to PostgreSQL backend
*/
public static boolean pg_put_line(Env env,
@NotNull Postgres conn,
String data)
{
try {
if (conn == null)
return false;
Class<?> cl = Class.forName("org.postgresql.core.PGStream");
Constructor<?> constructor = cl.getDeclaredConstructor(new Class[] {
String.class, Integer.TYPE});
Object object = constructor.newInstance(new Object[] {conn.getHost(), conn.getPort()});
byte dataArray[] = data.getBytes();
Method method = cl.getDeclaredMethod("Send", new Class[] {byte[].class});
method.invoke(object, new Object[] {dataArray});
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* Submits a command to the server and waits for the result,
* with the ability to pass parameters separately from the SQL command text
*/
@ReturnNullAsFalse
public static PostgresResult pg_query_params(Env env,
@NotNull Postgres conn,
String query,
ArrayValue params)
{
try {
if (conn == null)
return null;
if (pg_send_query_params(env, conn, query, params)) {
return (PostgresResult) conn.getResultResource();
}
return null;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Execute a query
*/
@ReturnNullAsFalse
public static PostgresResult pg_query(Env env, @NotNull String query)
{
if (query == null)
return null;
return pg_query_impl(env, getConnection(env), query, true);
}
/**
* Execute a query
*/
@ReturnNullAsFalse
public static PostgresResult pg_query(Env env,
@NotNull Postgres conn,
@NotNull String query)
{
if (conn == null || query == null)
return null;
return pg_query_impl(env, conn, query, true);
}
/**
* pg_query() alias
*/
@ReturnNullAsFalse
public static PostgresResult pg_exec(Env env,
@NotNull Postgres conn,
String query)
{
if (conn == null)
return null;
return pg_query(env, conn, query);
}
/**
* Execute a query
*/
private static PostgresResult pg_query_impl(Env env,
Postgres conn,
String query,
boolean reportError)
{
try {
// XXX: the PHP api allows conn to be optional but we
// totally disallow this case.
if (conn == null)
conn = getConnection(env);
PostgresResult result = conn.query(env, query);
StringValue error = conn.error(env);
if (error.length() != 0) {
if (reportError)
env.warning(L.l("Query failed: {0}", error));
return null;
}
return result;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
}
return null;
}
/**
* Returns an individual field of an error report
*/
public static Value pg_result_error_field(Env env,
@NotNull PostgresResult result,
int fieldCode)
{
try {
if (result == null)
return BooleanValue.FALSE;
Object errorField = null;
// Get the postgres specific server error message
// org.postgresql.util.ServerErrorMessage
Object serverError = ((Postgres) result.getConnection()).getServerErrorMessage();
if (serverError != null) {
Class<?> cl = Class.forName("org.postgresql.util.ServerErrorMessage");
String methodName;
switch (fieldCode) {
case PGSQL_DIAG_SEVERITY:
methodName = "getSeverity";
break;
case PGSQL_DIAG_SQLSTATE:
methodName = "getSQLState";
break;
case PGSQL_DIAG_MESSAGE_PRIMARY:
methodName = "getMessage";
break;
case PGSQL_DIAG_MESSAGE_DETAIL:
methodName = "getDetail";
break;
case PGSQL_DIAG_MESSAGE_HINT:
methodName = "getHint";
break;
case PGSQL_DIAG_STATEMENT_POSITION:
methodName = "getPosition";
break;
case PGSQL_DIAG_INTERNAL_POSITION:
methodName = "getInternalPosition";
break;
case PGSQL_DIAG_INTERNAL_QUERY:
methodName = "getInternalQuery";
break;
case PGSQL_DIAG_CONTEXT:
methodName = "getWhere";
break;
case PGSQL_DIAG_SOURCE_FILE:
methodName = "getFile";
break;
case PGSQL_DIAG_SOURCE_LINE:
methodName = "getLine";
break;
case PGSQL_DIAG_SOURCE_FUNCTION:
methodName = "getRoutine";
break;
default:
return null;
}
Method method = cl.getDeclaredMethod(methodName);
errorField = method.invoke(serverError);
}
if (errorField == null) {
/* XXX: php/431g
if (fieldCode == PGSQL_DIAG_INTERNAL_QUERY)
return BooleanValue.FALSE;
*/
return NullValue.NULL;
}
if (fieldCode == PGSQL_DIAG_STATEMENT_POSITION) {
Integer position = (Integer) errorField;
if (position.intValue() == 0)
return NullValue.NULL;
}
if (fieldCode == PGSQL_DIAG_INTERNAL_POSITION) {
Integer position = (Integer) errorField;
if (position.intValue() == 0)
// XXX: php/431g return BooleanValue.FALSE;
return NullValue.NULL;
}
return StringValue.create(errorField.toString());
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return NullValue.NULL;
}
}
/**
* Get error message associated with result
*/
@ReturnNullAsFalse
public static String pg_result_error(Env env,
@Optional PostgresResult result)
{
try {
if (result != null)
return result.getConnection().getErrorMessage();
else
return null;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Set internal row offset in result resource
*/
public static boolean pg_result_seek(Env env,
@NotNull PostgresResult result,
int offset)
{
try {
if (result == null)
return false;
return result.seek(env, offset);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* Get status of query result
*/
public static int pg_result_status(Env env,
@NotNull PostgresResult result,
@Optional("PGSQL_STATUS_LONG") int type)
{
try {
if (result == null)
return -1;
if (type == PGSQL_STATUS_STRING) {
throw new UnimplementedException("pg_result_status with PGSQL_STATUS_STRING");
}
if (result != null) {
Statement stmt = result.getStatement();
if (stmt.getUpdateCount() >= 0) {
return PGSQL_COMMAND_OK;
}
ResultSet rs = result.getResultSet();
if (rs == null) {
return PGSQL_EMPTY_QUERY;
}
return PGSQL_TUPLES_OK;
}
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
}
return -1;
}
/**
* Select records
*/
@ReturnNullAsFalse
public static ArrayValue pg_select(Env env,
@NotNull Postgres conn,
String tableName,
ArrayValue assocArray,
@Optional("-1") int options)
{
try {
if (conn == null)
return null;
StringValue whereClause = env.createUnicodeBuilder();
boolean isFirst = true;
for (Map.Entry<Value,Value> entry : assocArray.entrySet()) {
Value k = entry.getKey();
Value v = entry.getValue();
if (isFirst) {
isFirst = false;
} else {
whereClause.append(" AND ");
}
whereClause.append(k.toString()).append("='").append(v.toString()).append("'");
// String pi = conn.realEscapeString(p).toString();
// pi = pi.replaceAll("\\\\", "\\\\\\\\");
}
StringValue query = env.createUnicodeBuilder();
query.append("SELECT * FROM ").append(tableName).append(" WHERE ").append(whereClause);
PostgresResult result = pg_query(env, conn, query.toString());
return pg_fetch_all(env, result);
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Sends a request to execute a prepared statement with given parameters,
* without waiting for the result(s)
*/
public static boolean pg_send_execute(Env env,
@NotNull Postgres conn,
String stmtName,
ArrayValue params)
{
try {
// Note: for now, this is essentially the same as pg_execute.
PostgresResult result = pg_execute(env, conn, stmtName, params);
conn.setAsynchronousResult(result);
if (result != null) {
return true;
}
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
}
return false;
}
/**
* Sends a request to create a prepared statement with the given parameters,
* without waiting for completion
*/
public static boolean pg_send_prepare(Env env,
@NotNull Postgres conn,
String stmtName,
String query)
{
try {
// Note: for now, this is the same as pg_prepare.
PostgresStatement stmt = pg_prepare(env, conn, stmtName, query);
conn.setAsynchronousStatement(stmt);
if (stmt != null) {
return true;
}
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
}
return false;
}
/**
* Submits a command and separate parameters to the server without waiting for the result(s)
*/
public static boolean pg_send_query_params(Env env,
@NotNull Postgres conn,
String query,
ArrayValue params)
{
try {
PostgresStatement pstmt = conn.prepare(env, env.createString(query, "UTF-8" /* XXX */));
return executeInternal(env, conn, pstmt, params) != null;
/*
ArrayValueImpl arrayImpl = (ArrayValueImpl)params;
int sz = arrayImpl.size();
for (int i=0; i<sz; i++) {
StringValue p = arrayImpl.get(LongValue.create(i)).toStringValue();
String pi = conn.realEscapeString(p).toString();
pi = pi.replaceAll("\\\\", "\\\\\\\\");
query = query.replaceAll("\\$"+(i+1), "\\'"+pi+"\\'");
}
pg_send_query(env, conn, query);
return true;
*/
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* Sends asynchronous query
*/
public static boolean pg_send_query(Env env,
@NotNull Postgres conn,
String query)
{
if (conn == null)
return false;
try {
pg_query_impl(env, conn, query, false);
// We probably won't need this for now. See pg_get_result().
// conn.setAsynchronousResult(result);
// php/431g
// This is to be compliant with real expected results.
// Even a SELECT * FROM doesnotexist returns true from pg_send_query.
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
}
return false;
}
/**
* Set the client encoding
*/
public static int pg_set_client_encoding(Env env,
@NotNull Postgres conn,
String encoding)
{
//@todo conn should be optional
if (conn == null)
conn = getConnection(env);
if (conn.setClientEncoding(encoding))
return 0;
else
return -1;
}
/**
* Determines the verbosity of messages returned by pg_last_error() and pg_result_error()
*/
public static int pg_set_error_verbosity(Env env,
@NotNull Postgres conn,
int intVerbosity)
{
try {
//@todo conn should be optional
String verbosity;
PostgresResult result = pg_query(env, conn, "SHOW log_error_verbosity");
ArrayValue arr = pg_fetch_row(env, result, LongValue.ZERO);
String prevVerbosity = arr.get(LongValue.ZERO).toString();
switch (intVerbosity) {
case PGSQL_ERRORS_TERSE:
verbosity = "TERSE";
break;
case PGSQL_ERRORS_VERBOSE:
verbosity = "VERBOSE";
break;
default:
verbosity = "DEFAULT";
}
pg_query(env, conn, "SET log_error_verbosity TO '"+verbosity+"'");
if (prevVerbosity.equals("TERSE")) {
return PGSQL_ERRORS_TERSE;
} else if (prevVerbosity.equals("VERBOSE")) {
return PGSQL_ERRORS_VERBOSE;
} else {
return PGSQL_ERRORS_DEFAULT;
}
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return -1;
}
}
/**
* Enable tracing a PostgreSQL connection
*/
public static boolean pg_trace(Env env,
Path path,
@Optional String mode,
@Optional Postgres conn)
{
env.stub("pg_trace");
return false;
}
/**
* Returns the current in-transaction status of the server
*/
public static int pg_transaction_status(Env env,
@Optional Postgres conn)
{
return PGSQL_TRANSACTION_IDLE;
}
/**
* Return the TTY name associated with the connection
*/
public static String pg_tty(Env env,
@Optional Postgres conn)
{
// Note: pg_tty() is obsolete, since the server no longer pays attention to
// the TTY setting, but the function remains for backwards compatibility.
env.stub("pg_tty");
return "";
}
/**
* Unescape binary for bytea type
*/
@ReturnNullAsFalse
public static String pg_unescape_bytea(Env env,
String data)
{
try {
byte dataBytes[] = data.getBytes();
Class<?> cl = Class.forName("org.postgresql.util.PGbytea");
Method method = cl.getDeclaredMethod("toBytes", new Class[] {byte[].class});
return new String((byte[]) method.invoke(cl, new Object[] {dataBytes}));
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
/**
* Disable tracing of a PostgreSQL connection
*/
public static boolean pg_untrace(Env env,
@Optional Postgres conn)
{
// Always returns TRUE
env.stub("pg_untrace");
return true;
}
/**
* Update table
*/
public static boolean pg_update(Env env,
@NotNull Postgres conn,
String tableName,
ArrayValue data,
ArrayValue condition,
@Optional int options)
{
// From php.net: This function is EXPERIMENTAL.
// The behaviour of this function, the name of this function, and
// anything else documented about this function may change without
// notice in a future release of PHP. Use this function at your own risk.
try {
if (options > 0) {
throw new UnimplementedException("pg_update with options");
}
StringBuilder values = new StringBuilder();
boolean isFirst = true;
for (Map.Entry<Value,Value> entry : data.entrySet()) {
Value k = entry.getKey();
Value v = entry.getValue();
if (isFirst) {
isFirst = false;
} else {
values.append(", ");
}
values.append(k.toString());
values.append("='");
values.append(v.toString());
values.append("'");
}
StringBuilder whereClause = new StringBuilder();
isFirst = true;
for (Map.Entry<Value,Value> entry : condition.entrySet()) {
Value k = entry.getKey();
Value v = entry.getValue();
if (isFirst) {
isFirst = false;
} else {
whereClause.append(" AND ");
}
whereClause.append(k.toString());
whereClause.append("='");
whereClause.append(v.toString());
whereClause.append("'");
}
StringBuilder query = new StringBuilder();
query.append("UPDATE ");
query.append(tableName);
query.append(" SET ");
query.append(values);
query.append(" WHERE ");
query.append(whereClause);
pg_query(env, conn, query.toString());
return true;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return false;
}
}
/**
* Returns an array with client, protocol and server version (when available)
*/
@ReturnNullAsFalse
public static ArrayValue pg_version(Env env,
@Optional Postgres conn)
{
try {
//@todo return an array
if (conn == null)
conn = (Postgres) env.getSpecialValue("caucho.postgres");
ArrayValue result = new ArrayValueImpl();
result.append(env.createStringOld("client"),
env.createString(conn.getClientInfo(), "UTF-8" /* XXX */));
result.append(env.createStringOld("server_version"),
env.createString(conn.getServerInfo(), "UTF-8" /* XXX */));
return result;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
private static Postgres getConnection(Env env)
{
Postgres conn = (Postgres) env.getSpecialValue("caucho.postgres");
if (conn != null)
return conn;
String driver = "org.postgresql.Driver";
String url = "jdbc:postgresql://localhost:5432/";
conn = new Postgres(env, "localhost", "", "", "", 5432, driver, url);
env.setSpecialValue("caucho.postgres", conn);
return conn;
}
private static PostgresResult executeInternal(Env env,
@NotNull Postgres conn,
PostgresStatement pstmt,
ArrayValue params)
{
try {
StringBuilder stringBuilder = new StringBuilder();
int size = params.getSize(); // pstmt.getPreparedMappingSize();
for (int i = 0; i < size; i++)
stringBuilder.append('s');
String types = stringBuilder.toString();
Value value[] = params.valuesToArray();
pstmt.bindParams(env, types, value);
if (!pstmt.execute(env))
return null;
if (pstmt.getStatementType().equals("SELECT")) {
PostgresResult result = new PostgresResult(env, null, pstmt.getResultSet(), null);
conn.setResultResource(result);
return result;
} else {
// XXX: ??? return type?
return null;
// return pstmt;
}
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return null;
}
}
private static int writeLobInternal(Object largeObject,
InputStream is,
int len)
{
try {
Class<?> cl = Class.forName("org.postgresql.largeobject.LargeObject");
Method method = cl.getDeclaredMethod("getOutputStream");
OutputStream os = (OutputStream) method.invoke(largeObject, new Object[] {});
int written = 0;
int b;
while(((b = is.read()) >= 0) && (written++ < len)) {
os.write(b);
}
os.close();
return written;
} catch (Exception ex) {
log.log(Level.FINE, ex.toString(), ex);
return -1;
}
}
}