/*
* Copyright (c) 1998-2011 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 Charles Reich
*/
package com.caucho.quercus.lib.db;
import com.caucho.quercus.annotation.Optional;
import com.caucho.quercus.annotation.ResourceType;
import com.caucho.quercus.annotation.ReturnNullAsFalse;
import com.caucho.quercus.env.*;
import com.caucho.util.L10N;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* mysqli object oriented API facade
*/
@ResourceType("mysql result")
public class MysqliResult extends JdbcResultResource {
private static final Logger log
= Logger.getLogger(MysqliResult.class.getName());
private static final L10N L
= new L10N(MysqliResult.class);
private int _resultSetSize;
/**
* Constructor for MysqliResult
*
* @param stmt the corresponding statement
* @param rs the corresponding result set
* @param conn the corresponding connection
*/
public MysqliResult(Env env,
Statement stmt,
ResultSet rs,
Mysqli conn)
{
super(env, stmt, rs, conn);
// getNumRows() is efficient for MySQL
_resultSetSize = getNumRows();
}
/**
* Constructor for MysqliResult
*
* @param metaData the corresponding result set meta data
* @param conn the corresponding connection
*/
public MysqliResult(Env env,
ResultSetMetaData metaData,
Mysqli conn)
{
super(env, metaData, conn);
}
public String getResourceType()
{
return "mysql result";
}
public boolean isLastSqlDescribe()
{
return ((Mysqli) getConnection()).isLastSqlDescribe();
}
/**
* Seeks to an arbitrary result pointer specified
* by the offset in the result set represented by result.
*
* @param env the PHP executing environment
* @param rowNumber the row offset
* @return true on success or false on failure
*/
public boolean data_seek(Env env, int rowNumber)
{
if (seek(env, rowNumber)) {
return true;
} else {
return false;
}
}
/**
* Fetch a result row as an associative, a numeric array, or both.
*
* @param type one of MYSQLI_ASSOC, MYSQLI_NUM, or MYSQLI_BOTH (default)
* By using the MYSQLI_ASSOC constant this function will behave
* identically to the mysqli_fetch_assoc(), while MYSQLI_NUM will
* behave identically to the mysqli_fetch_row() function. The final
* option MYSQLI_BOTH will create a single array with the attributes
* of both.
*
* @return a result row as an associative, a numeric array, or both
* or null if there are no more rows in the result set
*/
@ReturnNullAsFalse
public ArrayValue fetch_array(Env env,
@Optional("MYSQLI_BOTH") int type)
{
if (type != MysqliModule.MYSQLI_ASSOC
&& type != MysqliModule.MYSQLI_BOTH
&& type != MysqliModule.MYSQLI_NUM) {
env.warning(L.l("invalid result_type"));
return null;
}
return fetchArray(env, type);
}
/**
* Returns an associative array representing the row.
*
* @return an associative array representing the row
* or null if there are no more rows in the result set
*/
public ArrayValue fetch_assoc(Env env)
{
return fetchArray(env, JdbcResultResource.FETCH_ASSOC);
}
/**
* Returns field metadata for a single field.
*
* @param env the PHP executing environment
* @param offset the field number
* @return the field metadata or false if no field
* information for specified offset is available
*/
public Value fetch_field_direct(Env env, int offset)
{
return fetchFieldDirect(env, offset);
}
/**
* Returns the next field in the result set.
*
* @param env the PHP executing environment
* @return the next field in the result set or
* false if no information is available
*/
public Value fetch_field(Env env)
{
return fetchNextField(env);
}
/**
* Returns metadata for all fields in the result set.
*
* @param env the PHP executing environment
* @return an array of objects which contains field
* definition information or FALSE if no field
* information is available
*/
public Value fetch_fields(Env env)
{
return getFieldDirectArray(env);
}
/**
* Returns the lengths of the columns of the
* current row in the result set.
*
* @return an array with the lengths of the
* columns of the current row in the result set
* or false if you call it before calling
* mysqli_fetch_row/array/object or after
* retrieving all rows in the result set
*/
public Value fetch_lengths()
{
return getLengths();
}
/**
* Returns an object representing the current row.
*
* @param env the PHP executing environment
* @return an object that corresponds to the
* fetched row or NULL if there are no more
* rows in resultset
*/
public Value fetch_object(Env env)
{
return fetchObject(env);
}
/**
* Returns a numerical array representing the current row.
*
* @return an array that corresponds to the
* fetched row or NULL if there are no more
* rows in result set
*/
public ArrayValue fetch_row(Env env)
{
return fetchArray(env, JdbcResultResource.FETCH_NUM);
}
/**
* Returns the number of fields in the result set.
*
* @param env the PHP executing environment
* @return the number of fields in the result set
*/
public int field_count(Env env)
{
return getFieldCount();
}
/**
* returns an object containing the following field information:
*
* name: The name of the column
* orgname: The original name if an alias was specified
* table: The name of the table
* orgtable: The original name if an alias was specified
* def: default value for this field, represented as a string
* max_length: The maximum width of the field for the result set
* flags: An integer representing the bit-flags for the field (see _constMap).
* type: The data type used for this field (an integer... also see _constMap)
* decimals: The number of decimals used (for integer fields)
*
* @param env the PHP executing environment
* @param fieldOffset 0 <= fieldOffset < number of fields
* @return an object or BooleanValue.FALSE
*/
protected Value fetchFieldDirect(Env env,
int fieldOffset)
{
if (! isValidFieldOffset(fieldOffset)) {
// php/1f77 : No warning printed for invalid index
return BooleanValue.FALSE;
}
try {
ResultSetMetaData md = getMetaData();
if (md == null)
return BooleanValue.FALSE;
int offset = fieldOffset + 1;
if (offset < 1 || md.getColumnCount() < offset)
return BooleanValue.FALSE;
int jdbcType = md.getColumnType(offset);
String catalogName = md.getCatalogName(offset);
String fieldTable = md.getTableName(offset);
String fieldSchema = md.getSchemaName(offset);
String fieldName = md.getColumnName(offset);
String fieldAlias = md.getColumnLabel(offset);
String fieldMysqlType = md.getColumnTypeName(offset);
int fieldLength = md.getPrecision(offset);
int fieldScale = md.getScale(offset);
if (fieldTable == null || "".equals(fieldTable)) {
return fetchFieldImproved(env, md, offset);
}
String sql = "SHOW FULL COLUMNS FROM "
+ fieldTable + " LIKE \'" + fieldName + "\'";
MysqliResult metaResult;
metaResult = ((Mysqli) getConnection()).metaQuery(env,
sql,
catalogName);
if (metaResult == null) {
return fetchFieldImproved(env, md, offset);
}
return metaResult.fetchFieldImproved(env,
fieldLength,
fieldAlias,
fieldName,
fieldTable,
jdbcType,
fieldMysqlType,
fieldScale);
} catch (SQLException e) {
log.log(Level.FINE, e.toString(), e);
return BooleanValue.FALSE;
}
}
protected Value fetchFieldImproved(Env env,
ResultSetMetaData md,
int offset)
{
Value result = env.createObject();
try {
int jdbcType = md.getColumnType(offset);
String catalogName = md.getCatalogName(offset);
String fieldTable = md.getTableName(offset);
String fieldSchema = md.getSchemaName(offset);
String fieldName = md.getColumnName(offset);
String fieldAlias = md.getColumnLabel(offset);
String mysqlType = md.getColumnTypeName(offset);
int fieldLength = md.getPrecision(offset);
int scale = md.getScale(offset);
if ((fieldTable == null || "".equals(fieldTable))
&& ((Mysqli) getConnection()).isLastSqlDescribe())
fieldTable = "COLUMNS";
result.putField(env, "name", env.createString(fieldAlias));
result.putField(env, "orgname", env.createString(fieldName));
result.putField(env, "table", env.createString(fieldTable));
//XXX: orgtable same as table
result.putField(env, "orgtable", env.createString(fieldTable));
result.putField(env, "def", env.createString(""));
// "max_length" is the maximum width of this field in this
// result set.
result.putField(env, "max_length", LongValue.ZERO);
// "length" is the width of the field defined in the table
// declaration.
result.putField(env, "length", LongValue.create(fieldLength));
//generate flags
long flags = 0;
result.putField(env, "flags", LongValue.create(flags));
//generate PHP type
int quercusType = 0;
switch (jdbcType) {
case Types.DECIMAL:
quercusType = MysqliModule.MYSQLI_TYPE_DECIMAL;
break;
case Types.BIT:
// Connector-J enables the tinyInt1isBit property
// by default and converts TINYINT to BIT. Use
// the mysql type name to tell the two apart.
if (mysqlType.equals("BIT")) {
quercusType = MysqliModule.MYSQLI_TYPE_BIT;
} else {
quercusType = MysqliModule.MYSQLI_TYPE_TINY;
}
break;
case Types.SMALLINT:
quercusType = MysqliModule.MYSQLI_TYPE_SHORT;
break;
case Types.INTEGER: {
if (! isInResultString(2, "medium"))
quercusType = MysqliModule.MYSQLI_TYPE_LONG;
else
quercusType = MysqliModule.MYSQLI_TYPE_INT24;
break;
}
case Types.REAL:
quercusType = MysqliModule.MYSQLI_TYPE_FLOAT;
break;
case Types.DOUBLE:
quercusType = MysqliModule.MYSQLI_TYPE_DOUBLE;
break;
case Types.BIGINT:
quercusType = MysqliModule.MYSQLI_TYPE_LONGLONG;
break;
case Types.DATE:
if (mysqlType.equals("YEAR")) {
quercusType = MysqliModule.MYSQLI_TYPE_YEAR;
} else {
quercusType = MysqliModule.MYSQLI_TYPE_DATE;
}
break;
case Types.TINYINT:
quercusType = MysqliModule.MYSQLI_TYPE_TINY;
break;
case Types.TIME:
quercusType = MysqliModule.MYSQLI_TYPE_TIME;
break;
case Types.TIMESTAMP:
if (mysqlType.equals("TIMESTAMP")) {
quercusType = MysqliModule.MYSQLI_TYPE_TIMESTAMP;
} else {
quercusType = MysqliModule.MYSQLI_TYPE_DATETIME;
}
break;
case Types.LONGVARBINARY:
case Types.LONGVARCHAR:
quercusType = MysqliModule.MYSQLI_TYPE_BLOB;
break;
case Types.BINARY:
case Types.CHAR:
quercusType = MysqliModule.MYSQLI_TYPE_STRING;
break;
case Types.VARBINARY:
case Types.VARCHAR:
quercusType = MysqliModule.MYSQLI_TYPE_VAR_STRING;
break;
// XXX: may need to revisit default
default:
quercusType = MysqliModule.MYSQLI_TYPE_NULL;
break;
}
result.putField(env, "type", LongValue.create(quercusType));
result.putField(env, "decimals", LongValue.create(scale));
// The "charsetnr" field is an integer identifier
// for the character set used to encode the field.
// This integer is sent by the server to the JDBC client
// and is stored as com.mysql.jdbc.Field.charsetIndex,
// but this field is private and the class does not provide
// any means to access the field. There is also no way
// to lookup the mysql index given a Java or Mysql encoding
// name.
result.putField(env, "charsetnr", LongValue.ZERO);
} catch (SQLException e) {
log.log(Level.FINE, e.toString(), e);
return BooleanValue.FALSE;
}
return result;
}
/**
* Returns an object with the following fields:
*
* name: The name of the column
* orgname: The original name if an alias was specified
* table: The name of the table
* orgtable: The original name if an alias was specified
* def: default value for this field, represented as a string
* max_length: The maximum width of the field for the result set
* flags: An integer representing the bit-flags for the field
* type: An integer respresenting the data type used for this field
* decimals: The number of decimals used (for integer fields)
*
* @param env the PHP executing environment
* @param fieldLength the field length as defined in the table declaration.
* @param name the field name
* @param originalName the field original name
* @param table the field table name
* @param type the field type
* @param scale the field scale
* @return an object containing field metadata
*/
protected Value fetchFieldImproved(Env env,
int fieldLength,
String name,
String originalName,
String table,
int jdbcType,
String mysqlType,
int scale)
{
Value result = env.createObject();
try {
ResultSetMetaData md = getMetaData();
if (! _rs.next())
return BooleanValue.FALSE;
result.putField(env, "name", env.createString(name));
result.putField(env, "orgname", env.createString(originalName));
result.putField(env, "table", env.createString(table));
//XXX: orgtable same as table
result.putField(env, "orgtable", env.createString(table));
result.putField(env, "def", env.createString(_rs.getString(6)));
// "max_length" is the maximum width of this field in this
// result set.
result.putField(env, "max_length", LongValue.ZERO);
// "length" is the width of the field defined in the table
// declaration.
result.putField(env, "length", LongValue.create(fieldLength));
//generate flags
long flags = 0;
if (! isInResultString(4, "YES"))
flags += MysqliModule.NOT_NULL_FLAG;
if (isInResultString(5, "PRI")) {
flags += MysqliModule.PRI_KEY_FLAG;
flags += MysqliModule.PART_KEY_FLAG;
}
if (isInResultString(5, "MUL")) {
flags += MysqliModule.MULTIPLE_KEY_FLAG;
flags += MysqliModule.PART_KEY_FLAG;
}
if (isInResultString(2, "blob")
|| (jdbcType == Types.LONGVARCHAR)
|| (jdbcType == Types.LONGVARBINARY))
flags += MysqliModule.BLOB_FLAG;
if (isInResultString(2, "unsigned"))
flags += MysqliModule.UNSIGNED_FLAG;
if (isInResultString(2, "zerofill"))
flags += MysqliModule.ZEROFILL_FLAG;
// php/1f73 - null check
if (isInResultString(3, "bin")
|| (jdbcType == Types.LONGVARBINARY)
|| (jdbcType == Types.DATE)
|| (jdbcType == Types.TIMESTAMP))
flags += MysqliModule.BINARY_FLAG;
if (isInResultString(2, "enum"))
flags += MysqliModule.ENUM_FLAG;
if (isInResultString(7, "auto"))
flags += MysqliModule.AUTO_INCREMENT_FLAG;
if (isInResultString(2, "set"))
flags += MysqliModule.SET_FLAG;
if ((jdbcType == Types.BIGINT)
|| (jdbcType == Types.BIT)
|| (jdbcType == Types.BOOLEAN)
|| (jdbcType == Types.DECIMAL)
|| (jdbcType == Types.DOUBLE)
|| (jdbcType == Types.REAL)
|| (jdbcType == Types.INTEGER)
|| (jdbcType == Types.SMALLINT))
flags += MysqliModule.NUM_FLAG;
result.putField(env, "flags", LongValue.create(flags));
//generate PHP type
int quercusType = 0;
switch (jdbcType) {
case Types.DECIMAL:
quercusType = MysqliModule.MYSQLI_TYPE_DECIMAL;
break;
case Types.BIT:
// Connector-J enables the tinyInt1isBit property
// by default and converts TINYINT to BIT. Use
// the mysql type name to tell the two apart.
if (mysqlType.equals("BIT")) {
quercusType = MysqliModule.MYSQLI_TYPE_BIT;
} else {
quercusType = MysqliModule.MYSQLI_TYPE_TINY;
}
break;
case Types.SMALLINT:
quercusType = MysqliModule.MYSQLI_TYPE_SHORT;
break;
case Types.INTEGER: {
if (! isInResultString(2, "medium"))
quercusType = MysqliModule.MYSQLI_TYPE_LONG;
else
quercusType = MysqliModule.MYSQLI_TYPE_INT24;
break;
}
case Types.REAL:
quercusType = MysqliModule.MYSQLI_TYPE_FLOAT;
break;
case Types.DOUBLE:
quercusType = MysqliModule.MYSQLI_TYPE_DOUBLE;
break;
case Types.BIGINT:
quercusType = MysqliModule.MYSQLI_TYPE_LONGLONG;
break;
case Types.DATE:
if (mysqlType.equals("YEAR")) {
quercusType = MysqliModule.MYSQLI_TYPE_YEAR;
} else {
quercusType = MysqliModule.MYSQLI_TYPE_DATE;
}
break;
case Types.TINYINT:
quercusType = MysqliModule.MYSQLI_TYPE_TINY;
break;
case Types.TIME:
quercusType = MysqliModule.MYSQLI_TYPE_TIME;
break;
case Types.TIMESTAMP:
if (mysqlType.equals("TIMESTAMP")) {
quercusType = MysqliModule.MYSQLI_TYPE_TIMESTAMP;
} else {
quercusType = MysqliModule.MYSQLI_TYPE_DATETIME;
}
break;
case Types.LONGVARBINARY:
case Types.LONGVARCHAR:
quercusType = MysqliModule.MYSQLI_TYPE_BLOB;
break;
case Types.BINARY:
case Types.CHAR:
quercusType = MysqliModule.MYSQLI_TYPE_STRING;
break;
case Types.VARBINARY:
case Types.VARCHAR:
quercusType = MysqliModule.MYSQLI_TYPE_VAR_STRING;
break;
// XXX: may need to revisit default
default:
quercusType = MysqliModule.MYSQLI_TYPE_NULL;
break;
}
result.putField(env, "type", LongValue.create(quercusType));
result.putField(env, "decimals", LongValue.create(scale));
// The "charsetnr" field is an integer identifier
// for the character set used to encode the field.
// This integer is sent by the server to the JDBC client
// and is stored as com.mysql.jdbc.Field.charsetIndex,
// but this field is private and the class does not provide
// any means to access the field. There is also no way
// to lookup the mysql index given a Java or Mysql encoding
// name.
result.putField(env, "charsetnr", LongValue.ZERO);
} catch (SQLException e) {
log.log(Level.FINE, e.toString(), e);
return BooleanValue.FALSE;
}
return result;
}
/**
* Returns the following field flags: not_null,
* primary_key, multiple_key, blob,
* unsigned zerofill, binary, enum, auto_increment and timestamp
* <p/>
* it does not return the MySQL / PHP flag unique_key
* <p/>
* MysqlModule generates a special result set with the appropriate values
*
* @return the field flags
*/
public Value getFieldFlagsImproved(Env env, int jdbcType, String mysqlType)
{
try {
StringBuilder flags = new StringBuilder();
// php/142r
if (! _rs.next())
return BooleanValue.FALSE;
if (! isInResultString(4, "YES"))
flags.append("not_null");
if (isInResultString(5, "PRI")) {
if (flags.length() > 0)
flags.append(' ');
flags.append("primary_key");
} else {
if (isInResultString(5, "MUL")) {
if (flags.length() > 0)
flags.append(' ');
flags.append("multiple_key");
}
}
final boolean isTimestamp = (jdbcType == Types.TIMESTAMP)
&& mysqlType.equals("TIMESTAMP");
if (isInResultString(2, "blob")
|| (jdbcType == Types.LONGVARCHAR)) {
if (flags.length() > 0)
flags.append(' ');
flags.append("blob");
}
if (isInResultString(2, "unsigned")
|| (jdbcType == Types.BIT && mysqlType.equals("BIT"))
|| isTimestamp) {
if (flags.length() > 0)
flags.append(' ');
flags.append("unsigned");
}
if (isInResultString(2, "zerofill")
|| isTimestamp) {
if (flags.length() > 0)
flags.append(' ');
flags.append("zerofill");
}
if (isInResultString(3, "bin")
|| (jdbcType == Types.BINARY)
|| (jdbcType == Types.LONGVARBINARY)
|| (jdbcType == Types.VARBINARY)
|| (jdbcType == Types.TIME)
|| isTimestamp
|| isInResultString(2, "date")) {
if (flags.length() > 0)
flags.append(' ');
flags.append("binary");
}
if (isInResultString(2, "enum")) {
if (flags.length() > 0)
flags.append(' ');
flags.append("enum");
}
if (isInResultString(2, "set")) {
if (flags.length() > 0)
flags.append(' ');
flags.append("set");
}
if (isInResultString(7, "auto_increment")) {
if (flags.length() > 0)
flags.append(' ');
flags.append("auto_increment");
}
if (isTimestamp) {
if (flags.length() > 0)
flags.append(' ');
flags.append("timestamp");
}
return env.createString(flags.toString());
} catch (SQLException e) {
log.log(Level.FINE, e.toString(), e);
return BooleanValue.FALSE;
}
}
/**
* Get Mysql type string
*
* @param fieldOffset the field number (0-based)
* @return the Mysql type
*/
protected String getMysqlType(int fieldOffset)
{
try {
ResultSetMetaData md = getMetaData();
return md.getColumnTypeName(fieldOffset + 1);
} catch (SQLException e) {
log.log(Level.FINE, e.toString(), e);
return null;
}
}
/**
* Given the JDBC type of the field at the given offset,
* return a PHP type string.
*/
protected String getFieldType(int fieldOffset, int jdbcType)
{
if (jdbcType == Types.TIMESTAMP) {
// The Mysql types DATETIME and TIMESTAMP both map to Types.TIMESTAMP
String mysqlType = getMysqlType(fieldOffset);
if (mysqlType.equals("TIMESTAMP")) {
return TIMESTAMP;
} else {
return DATETIME;
}
} else if (jdbcType == Types.DATE) {
// The Mysql types DATE and YEAR both map to Types.DATE
String mysqlType = getMysqlType(fieldOffset);
if (mysqlType.equals("YEAR")) {
return YEAR;
} else {
return DATE;
}
} else {
return super.getFieldType(fieldOffset, jdbcType);
}
}
/**
* @see Value fetchFieldDirect
*
* increments the fieldOffset counter by one;
*
* @param env the PHP executing environment
* @return
*/
protected Value fetchNextField(Env env)
{
int fieldOffset = getFieldOffset();
Value result = fetchFieldDirect(env, fieldOffset);
setFieldOffset(fieldOffset + 1);
return result;
}
/**
* Sets the field metadata cursor to the
* given offset. The next call to
* mysqli_fetch_field() will retrieve the
* field definition of the column associated
* with that offset.
*
* @param env the PHP executing environment
* @return previous value of field cursor
*/
public boolean field_seek(Env env, int offset)
{
boolean success = setFieldOffset(offset);
if (! success)
env.invalidArgument("field", offset);
return success;
}
/**
* Get current field offset of a result pointer.
*
* @param env the PHP executing environment
* @return current offset of field cursor
*/
public int field_tell(Env env)
{
return getFieldOffset();
}
/**
* Closes the result.
*/
public void free()
{
close();
}
/**
* Closes the result
*/
public void free_result()
{
close();
}
/**
*
* @param env the PHP executing environment
* @return array of fieldDirect objects
*/
public Value getFieldDirectArray(Env env)
{
ArrayValue array = new ArrayValueImpl();
try {
int numColumns = getMetaData().getColumnCount();
for (int i = 0; i < numColumns; i++) {
array.put(fetchFieldDirect(env, i));
}
return array;
} catch (SQLException e) {
log.log(Level.FINE, e.toString(), e);
return BooleanValue.FALSE;
}
}
/**
* Get the number of fields in the result set.
*
* @return the number of columns in the result set
*/
public int num_fields()
{
return getFieldCount();
}
/**
* Get the number of rows in the result set.
*
* @return the number of rows in the result set
*/
public int num_rows()
{
return _resultSetSize;
}
@Override
protected Value getColumnString(Env env,
ResultSet rs,
ResultSetMetaData md,
int column)
throws SQLException
{
// php/1464, php/144f, php/144g, php/144b
// The "SET NAMES 'latin1'" in Mysqli is important to make the default
// encoding sane
Mysqli mysqli = getMysqli();
if (rs instanceof QuercusResultSet) {
QuercusResultSet qRs = (QuercusResultSet) rs;
int length = qRs.getStringLength(column);
if (length < 0)
return NullValue.NULL;
// XXX: i18n
StringBuilderValue sb = new StringBuilderValue();
sb.ensureAppendCapacity(length);
qRs.getString(column, sb.getBuffer(), sb.getOffset());
sb.setOffset(sb.getOffset() + length);
return sb;
}
Method getColumnCharacterSetMethod
= mysqli.getColumnCharacterSetMethod(md.getClass());
String encoding = null;
try {
if (getColumnCharacterSetMethod != null)
encoding = (String) getColumnCharacterSetMethod.invoke(md, column);
} catch (Exception e) {
log.log(Level.FINE, e.toString(), e);
}
if (encoding == null) {
String value = rs.getString(column);
if (value != null)
return env.createString(value);
else
return NullValue.NULL;
}
// calling getString() will decode using the database encoding, so
// get bytes directly. Also, getBytes is faster for MySQL since
// getString converts from bytes to string.
if ("UTF-8".equals(encoding)) {
byte []bytes = rs.getBytes(column);
if (bytes == null)
return NullValue.NULL;
StringValue bb = env.createUnicodeBuilder();
int length = bytes.length;
int offset = 0;
bb.appendUtf8(bytes);
/*
while (offset < length) {
int ch = bytes[offset++] & 0xff;
if (ch < 0x80) {
bb.append((char) ch);
}
else if (ch < 0xe0) {
int ch2 = bytes[offset++] & 0xff;
int v = ((ch & 0x1f) << 6) + ((ch2 & 0x3f));
bb.append((char) MysqlLatin1Utility.decode(v));
}
else {
int ch2 = bytes[offset++] & 0xff;
int ch3 = bytes[offset++] & 0xff;
int v = ((ch & 0xf) << 12) + ((ch2 & 0x3f) << 6) + ((ch3 & 0x3f));
bb.append((char) MysqlLatin1Utility.decode(v));
}
}
*/
return bb;
}
else if ("Cp1252".equals(encoding) || "LATIN1".equals(encoding)) {
byte []bytes = rs.getBytes(column);
if (bytes == null)
return NullValue.NULL;
StringValue bb = env.createUnicodeBuilder();
bb.append(bytes);
return bb;
}
else {
String value = rs.getString(column);
if (value != null)
return env.createString(value);
else
return NullValue.NULL;
}
}
protected Mysqli getMysqli()
{
return (Mysqli) getConnection();
}
}