/*
* Copyright (c) 2007 David Crawshaw <david@zentus.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package org.sqlite;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Implements a JDBC ResultSet.
*/
final class RS extends Unused implements ResultSet, ResultSetMetaData, Codes
{
private final Stmt stmt;
private final DB db;
boolean open = false; // true means have results and can iterate them
int maxRows; // max. number of rows as set by a Statement
String[] cols = null; // if null, the RS is closed()
String[] colsMeta = null; // same as cols, but used by Meta interface
boolean[][] meta = null;
private int limitRows; // 0 means no limit, must check against maxRows
private int row = 0; // number of current row, starts at 1 (0 is for before loading data)
private int lastCol; // last column accessed, for wasNull(). -1 if none
boolean closeStmt;
/**
* Default constructor for a given statement.
* @param stmt The statement.
* @param closeStmt TODO
*/
RS(Stmt stmt) {
this.stmt = stmt;
this.db = stmt.db;
}
// INTERNAL FUNCTIONS ///////////////////////////////////////////
/**
* Checks the status of the result set.
* @return True if has results and can iterate them; false otherwise.
*/
boolean isOpen() {
return open;
}
/**
* @throws SQLException if ResultSet is not open.
*/
void checkOpen() throws SQLException {
if (!open) {
throw new SQLException("ResultSet closed");
}
}
/**
* Takes col in [1,x] form, returns in [0,x-1] form
* @param col
* @return
* @throws SQLException
*/
private int checkCol(int col) throws SQLException {
if (colsMeta == null) {
throw new IllegalStateException("SQLite JDBC: inconsistent internal state");
}
if (col < 1 || col > colsMeta.length) {
throw new SQLException("column " + col + " out of bounds [1," + colsMeta.length + "]");
}
return --col;
}
/**
* Takes col in [1,x] form, marks it as last accessed and returns [0,x-1]
* @param col
* @return
* @throws SQLException
*/
private int markCol(int col) throws SQLException {
checkOpen();
checkCol(col);
lastCol = col;
return --col;
}
/**
* @throws SQLException
*/
private void checkMeta() throws SQLException {
checkCol(1);
if (meta == null) {
meta = db.column_metadata(stmt.pointer);
}
}
// ResultSet Functions //////////////////////////////////////////
/**
* @see java.sql.ResultSet#close()
*/
public void close() throws SQLException {
cols = null;
colsMeta = null;
meta = null;
open = false;
limitRows = 0;
row = 0;
lastCol = -1;
if (stmt == null) {
return;
}
if (stmt != null && stmt.pointer != 0) {
db.reset(stmt.pointer);
if (closeStmt) {
closeStmt = false; // break recursive call
stmt.close();
}
}
}
/**
* returns col in [1,x] form
* @see java.sql.ResultSet#findColumn(java.lang.String)
*/
public int findColumn(String col) throws SQLException {
checkOpen();
int c = -1;
for (int i = 0; i < cols.length; i++) {
if (col.equalsIgnoreCase(cols[i])
|| (cols[i].toUpperCase().endsWith(col.toUpperCase()) && cols[i].charAt(cols[i].length()
- col.length()) == '.')) {
if (c == -1) {
c = i;
}
else {
throw new SQLException("ambiguous column: '" + col + "'");
}
}
}
if (c == -1) {
throw new SQLException("no such column: '" + col + "'");
}
else {
return c + 1;
}
}
/**
* @see java.sql.ResultSet#next()
*/
public boolean next() throws SQLException {
if (!open)
{
return false; // finished ResultSet
}
lastCol = -1;
// first row is loaded by execute(), so do not step() again
if (row == 0) {
row++;
return true;
}
// check if we are row limited by the statement or the ResultSet
if (maxRows != 0 && row == maxRows) {
return false;
}
// do the real work
int statusCode = db.step(stmt.pointer);
switch (statusCode) {
case SQLITE_DONE:
close(); // agressive closing to avoid writer starvation
return false;
case SQLITE_ROW:
row++;
return true;
case SQLITE_BUSY:
default:
db.throwex(statusCode);
return false;
}
}
/**
* @see java.sql.ResultSet#getType()
*/
public int getType() throws SQLException {
return TYPE_FORWARD_ONLY;
}
/**
* @see java.sql.ResultSet#getFetchSize()
*/
public int getFetchSize() throws SQLException {
return limitRows;
}
/**
* @see java.sql.ResultSet#setFetchSize(int)
*/
public void setFetchSize(int rows) throws SQLException {
if (0 > rows || (maxRows != 0 && rows > maxRows)) {
throw new SQLException("fetch size " + rows + " out of bounds " + maxRows);
}
limitRows = rows;
}
/**
* @see java.sql.ResultSet#getFetchDirection()
*/
public int getFetchDirection() throws SQLException {
checkOpen();
return ResultSet.FETCH_FORWARD;
}
/**
* @see java.sql.ResultSet#setFetchDirection(int)
*/
public void setFetchDirection(int d) throws SQLException {
checkOpen();
if (d != ResultSet.FETCH_FORWARD) {
throw new SQLException("only FETCH_FORWARD direction supported");
}
}
/**
* @see java.sql.ResultSet#isAfterLast()
*/
public boolean isAfterLast() throws SQLException {
return !open;
}
/**
* @see java.sql.ResultSet#isBeforeFirst()
*/
public boolean isBeforeFirst() throws SQLException {
return open && row == 0;
}
/**
* @see java.sql.ResultSet#isFirst()
*/
public boolean isFirst() throws SQLException {
return row == 1;
}
/**
* @see java.sql.ResultSet#isLast()
*/
public boolean isLast() throws SQLException { // FIXME
throw new SQLException("function not yet implemented for SQLite");
}
/**
* @see java.lang.Object#finalize()
*/
@Override
protected void finalize() throws SQLException {
close();
}
/**
* @see java.sql.ResultSet#getRow()
*/
public int getRow() throws SQLException {
return row;
}
/**
* @see java.sql.ResultSet#wasNull()
*/
public boolean wasNull() throws SQLException {
return db.column_type(stmt.pointer, markCol(lastCol)) == SQLITE_NULL;
}
// DATA ACCESS FUNCTIONS ////////////////////////////////////////
/**
* @see java.sql.ResultSet#getBigDecimal(int)
*/
public BigDecimal getBigDecimal(int col) throws SQLException {
final String stringValue = getString(col);
if (stringValue == null) {
return null;
}
else {
try {
return new BigDecimal(stringValue);
}
catch (NumberFormatException e) {
throw new SQLException("Bad value for type BigDecimal : " + stringValue);
}
}
}
/**
* @see java.sql.ResultSet#getBigDecimal(java.lang.String)
*/
public BigDecimal getBigDecimal(String col) throws SQLException {
return getBigDecimal(findColumn(col));
}
/**
* @see java.sql.ResultSet#getBoolean(int)
*/
public boolean getBoolean(int col) throws SQLException {
return getInt(col) == 0 ? false : true;
}
/**
* @see java.sql.ResultSet#getBoolean(java.lang.String)
*/
public boolean getBoolean(String col) throws SQLException {
return getBoolean(findColumn(col));
}
/**
* @see java.sql.ResultSet#getBinaryStream(int)
*/
public InputStream getBinaryStream(int col) throws SQLException {
return new ByteArrayInputStream(getBytes(col));
}
/**
* @see java.sql.ResultSet#getBinaryStream(java.lang.String)
*/
public InputStream getBinaryStream(String col) throws SQLException {
return getBinaryStream(findColumn(col));
}
/**
* @see java.sql.ResultSet#getByte(int)
*/
public byte getByte(int col) throws SQLException {
return (byte) getInt(col);
}
/**
* @see java.sql.ResultSet#getByte(java.lang.String)
*/
public byte getByte(String col) throws SQLException {
return getByte(findColumn(col));
}
/**
* @see java.sql.ResultSet#getBytes(int)
*/
public byte[] getBytes(int col) throws SQLException {
return db.column_blob(stmt.pointer, markCol(col));
}
/**
* @see java.sql.ResultSet#getBytes(java.lang.String)
*/
public byte[] getBytes(String col) throws SQLException {
return getBytes(findColumn(col));
}
/**
* @see java.sql.ResultSet#getCharacterStream(int)
*/
public Reader getCharacterStream(int col) throws SQLException {
return new StringReader(getString(col));
}
/**
* @see java.sql.ResultSet#getCharacterStream(java.lang.String)
*/
public Reader getCharacterStream(String col) throws SQLException {
return getCharacterStream(findColumn(col));
}
/**
* @see java.sql.ResultSet#getDate(int)
*/
public Date getDate(int col) throws SQLException {
switch(db.column_type(stmt.pointer, markCol(col))) {
case SQLITE_NULL:
return null;
case SQLITE_TEXT:
try {
return new Date(stmt.conn.dateFormat.parse(db.column_text(stmt.pointer, markCol(col))).getTime());
}
catch (Exception e) {
SQLException error = new SQLException("Error parsing date");
error.initCause(e);
throw error;
}
case SQLITE_FLOAT:
return new Date(julianDateToCalendar(db.column_double(stmt.pointer, markCol(col))).getTimeInMillis());
default: //SQLITE_INTEGER:
return new Date(db.column_long(stmt.pointer, markCol(col)) * stmt.conn.dateMultiplier);
}
}
/**
* @see java.sql.ResultSet#getDate(int, java.util.Calendar)
*/
public Date getDate(int col, Calendar cal) throws SQLException {
checkCalendar(cal);
switch (db.column_type(stmt.pointer, markCol(col))) {
case SQLITE_NULL:
return null;
case SQLITE_TEXT:
try {
DateFormat dateFormat = (DateFormat) stmt.conn.dateFormat.clone();
dateFormat.setCalendar(cal);
return new java.sql.Date(dateFormat.parse(db.column_text(stmt.pointer, markCol(col))).getTime());
}
catch (Exception e) {
SQLException error = new SQLException("Error parsing time stamp");
error.initCause(e);
throw error;
}
case SQLITE_FLOAT:
return new Date(julianDateToCalendar(db.column_double(stmt.pointer, markCol(col)), cal).getTimeInMillis());
default: // SQLITE_INTEGER:
cal.setTimeInMillis(db.column_long(stmt.pointer, markCol(col)) * stmt.conn.dateMultiplier);
return new Date(cal.getTime().getTime());
}
}
/**
* @see java.sql.ResultSet#getDate(java.lang.String)
*/
public Date getDate(String col) throws SQLException {
return getDate(findColumn(col), Calendar.getInstance());
}
/**
* @see java.sql.ResultSet#getDate(java.lang.String, java.util.Calendar)
*/
public Date getDate(String col, Calendar cal) throws SQLException {
return getDate(findColumn(col), cal);
}
/**
* @see java.sql.ResultSet#getDouble(int)
*/
public double getDouble(int col) throws SQLException {
if (db.column_type(stmt.pointer, markCol(col)) == SQLITE_NULL) {
return 0;
}
return db.column_double(stmt.pointer, markCol(col));
}
/**
* @see java.sql.ResultSet#getDouble(java.lang.String)
*/
public double getDouble(String col) throws SQLException {
return getDouble(findColumn(col));
}
/**
* @see java.sql.ResultSet#getFloat(int)
*/
public float getFloat(int col) throws SQLException {
if (db.column_type(stmt.pointer, markCol(col)) == SQLITE_NULL) {
return 0;
}
return (float) db.column_double(stmt.pointer, markCol(col));
}
/**
* @see java.sql.ResultSet#getFloat(java.lang.String)
*/
public float getFloat(String col) throws SQLException {
return getFloat(findColumn(col));
}
/**
* @see java.sql.ResultSet#getInt(int)
*/
public int getInt(int col) throws SQLException {
return db.column_int(stmt.pointer, markCol(col));
}
/**
* @see java.sql.ResultSet#getInt(java.lang.String)
*/
public int getInt(String col) throws SQLException {
return getInt(findColumn(col));
}
/**
* @see java.sql.ResultSet#getLong(int)
*/
public long getLong(int col) throws SQLException {
return db.column_long(stmt.pointer, markCol(col));
}
/**
* @see java.sql.ResultSet#getLong(java.lang.String)
*/
public long getLong(String col) throws SQLException {
return getLong(findColumn(col));
}
/**
* @see java.sql.ResultSet#getShort(int)
*/
public short getShort(int col) throws SQLException {
return (short) getInt(col);
}
/**
* @see java.sql.ResultSet#getShort(java.lang.String)
*/
public short getShort(String col) throws SQLException {
return getShort(findColumn(col));
}
/**
* @see java.sql.ResultSet#getString(int)
*/
public String getString(int col) throws SQLException {
return db.column_text(stmt.pointer, markCol(col));
}
/**
* @see java.sql.ResultSet#getString(java.lang.String)
*/
public String getString(String col) throws SQLException {
return getString(findColumn(col));
}
/**
* @see java.sql.ResultSet#getTime(int)
*/
public Time getTime(int col) throws SQLException {
switch (db.column_type(stmt.pointer, markCol(col))) {
case SQLITE_NULL:
return null;
case SQLITE_TEXT:
try {
return new Time(stmt.conn.dateFormat.parse(db.column_text(stmt.pointer, markCol(col))).getTime());
}
catch (Exception e) {
SQLException error = new SQLException("Error parsing time");
error.initCause(e);
throw error;
}
case SQLITE_FLOAT:
return new Time(julianDateToCalendar(db.column_double(stmt.pointer, markCol(col))).getTimeInMillis());
default:// SQLITE_INTEGER
return new Time(db.column_long(stmt.pointer, markCol(col)) * stmt.conn.dateMultiplier);
}
}
/**
* @see java.sql.ResultSet#getTime(int, java.util.Calendar)
*/
public Time getTime(int col, Calendar cal) throws SQLException {
checkCalendar(cal);
switch (db.column_type(stmt.pointer, markCol(col))) {
case SQLITE_NULL:
return null;
case SQLITE_TEXT:
try {
DateFormat dateFormat = (DateFormat) stmt.conn.dateFormat.clone();
dateFormat.setCalendar(cal);
return new Time(dateFormat.parse(db.column_text(stmt.pointer, markCol(col))).getTime());
}
catch (Exception e) {
SQLException error = new SQLException("Error parsing time");
error.initCause(e);
throw error;
}
case SQLITE_FLOAT:
return new Time(julianDateToCalendar(db.column_double(stmt.pointer, markCol(col)), cal).getTimeInMillis());
default: //SQLITE_INTEGER
cal.setTimeInMillis(db.column_long(stmt.pointer, markCol(col)) * stmt.conn.dateMultiplier);
return new Time(cal.getTime().getTime());
}
}
/**
* @see java.sql.ResultSet#getTime(java.lang.String)
*/
public Time getTime(String col) throws SQLException {
return getTime(findColumn(col));
}
/**
* @see java.sql.ResultSet#getTime(java.lang.String, java.util.Calendar)
*/
public Time getTime(String col, Calendar cal) throws SQLException {
return getTime(findColumn(col), cal);
}
/**
* @see java.sql.ResultSet#getTimestamp(int)
*/
public Timestamp getTimestamp(int col) throws SQLException {
switch (db.column_type(stmt.pointer, markCol(col))) {
case SQLITE_NULL:
return null;
case SQLITE_TEXT:
try {
return new Timestamp(stmt.conn.dateFormat.parse(db.column_text(stmt.pointer, markCol(col))).getTime());
}
catch (Exception e) {
SQLException error = new SQLException("Error parsing time stamp");
error.initCause(e);
throw error;
}
case SQLITE_FLOAT:
return new Timestamp(julianDateToCalendar(db.column_double(stmt.pointer, markCol(col))).getTimeInMillis());
default: //SQLITE_INTEGER:
return new Timestamp(db.column_long(stmt.pointer, markCol(col)) * stmt.conn.dateMultiplier);
}
}
/**
* @see java.sql.ResultSet#getTimestamp(int, java.util.Calendar)
*/
public Timestamp getTimestamp(int col, Calendar cal) throws SQLException {
if (cal == null) {
return getTimestamp(col);
}
switch (db.column_type(stmt.pointer, markCol(col))) {
case SQLITE_NULL:
return null;
case SQLITE_TEXT:
try {
DateFormat dateFormat = (DateFormat)stmt.conn.dateFormat.clone();
dateFormat.setCalendar(cal);
return new Timestamp(dateFormat.parse(db.column_text(stmt.pointer, markCol(col))).getTime());
}
catch (Exception e) {
SQLException error = new SQLException("Error parsing time stamp");
error.initCause(e);
throw error;
}
case SQLITE_FLOAT:
return new Timestamp(julianDateToCalendar(db.column_double(stmt.pointer, markCol(col)), cal).getTimeInMillis());
default: //SQLITE_INTEGER
cal.setTimeInMillis(db.column_long(stmt.pointer, markCol(col)) * stmt.conn.dateMultiplier);
return new Timestamp(cal.getTime().getTime());
}
}
/**
* @see java.sql.ResultSet#getTimestamp(java.lang.String)
*/
public Timestamp getTimestamp(String col) throws SQLException {
return getTimestamp(findColumn(col));
}
/**
* @see java.sql.ResultSet#getTimestamp(java.lang.String, java.util.Calendar)
*/
public Timestamp getTimestamp(String c, Calendar ca) throws SQLException {
return getTimestamp(findColumn(c), ca);
}
/**
* @see java.sql.ResultSet#getObject(int)
*/
public Object getObject(int col) throws SQLException {
switch (db.column_type(stmt.pointer, checkCol(col))) {
case SQLITE_INTEGER:
long val = getLong(col);
if (val > Integer.MAX_VALUE || val < Integer.MIN_VALUE) {
return new Long(val);
}
else {
return new Integer((int) val);
}
case SQLITE_FLOAT:
return new Double(getDouble(col));
case SQLITE_BLOB:
return getBytes(col);
case SQLITE_NULL:
return null;
case SQLITE_TEXT:
default:
return getString(col);
}
}
/**
* @see java.sql.ResultSet#getObject(java.lang.String)
*/
public Object getObject(String col) throws SQLException {
return getObject(findColumn(col));
}
/**
* @see java.sql.ResultSet#getStatement()
*/
public Statement getStatement() {
return stmt;
}
/**
* @see java.sql.ResultSet#getCursorName()
*/
public String getCursorName() throws SQLException {
return null;
}
/**
* @see java.sql.ResultSet#getWarnings()
*/
public SQLWarning getWarnings() throws SQLException {
return null;
}
/**
* @see java.sql.ResultSet#clearWarnings()
*/
public void clearWarnings() throws SQLException {}
// ResultSetMetaData Functions //////////////////////////////////
/**
* Pattern used to extract the column type name from table column definition.
*/
protected final static Pattern COLUMN_TYPENAME = Pattern.compile("([^\\(]*)");
/**
* Pattern used to extract the column type name from a cast(col as type)
*/
protected final static Pattern COLUMN_TYPECAST = Pattern.compile("cast\\(.*?\\s+as\\s+(.*?)\\s*\\)");
/**
* Pattern used to extract the precision and scale from column meta returned by the JDBC driver.
*/
protected final static Pattern COLUMN_PRECISION = Pattern.compile(".*?\\((.*?)\\)");
// we do not need to check the RS is open, only that colsMeta
// is not null, done with checkCol(int).
/**
* @see java.sql.ResultSet#getMetaData()
*/
public ResultSetMetaData getMetaData() throws SQLException {
return this;
}
/**
* @see java.sql.ResultSetMetaData#getCatalogName(int)
*/
public String getCatalogName(int col) throws SQLException {
return db.column_table_name(stmt.pointer, checkCol(col));
}
/**
* @see java.sql.ResultSetMetaData#getColumnClassName(int)
*/
public String getColumnClassName(int col) throws SQLException {
checkCol(col);
return "java.lang.Object";
}
/**
* @see java.sql.ResultSetMetaData#getColumnCount()
*/
public int getColumnCount() throws SQLException {
checkCol(1);
return colsMeta.length;
}
/**
* @see java.sql.ResultSetMetaData#getColumnDisplaySize(int)
*/
public int getColumnDisplaySize(int col) throws SQLException {
return Integer.MAX_VALUE;
}
/**
* @see java.sql.ResultSetMetaData#getColumnLabel(int)
*/
public String getColumnLabel(int col) throws SQLException {
return getColumnName(col);
}
/**
* @see java.sql.ResultSetMetaData#getColumnName(int)
*/
public String getColumnName(int col) throws SQLException {
return db.column_name(stmt.pointer, checkCol(col));
}
/**
* @see java.sql.ResultSetMetaData#getColumnType(int)
*/
public int getColumnType(int col) throws SQLException {
String typeName = getColumnTypeName(col);
int valueType = db.column_type(stmt.pointer, checkCol(col));
if (valueType == SQLITE_INTEGER || valueType == SQLITE_NULL) {
if ("BOOLEAN".equals(typeName)) {
return Types.BOOLEAN;
}
if ("TINYINT".equals(typeName)) {
return Types.TINYINT;
}
if ("SMALLINT".equals(typeName) || "INT2".equals(typeName)) {
return Types.SMALLINT;
}
if ("BIGINT".equals(typeName) || "INT8".equals(typeName) ||
"UNSIGNED BIG INT".equals(typeName)) {
return Types.BIGINT;
}
if ("DATE".equals(typeName) || "DATETIME".equals(typeName)) {
return Types.DATE;
}
if (valueType == SQLITE_INTEGER ||
"INT".equals(typeName) ||
"INTEGER".equals(typeName) ||
"MEDIUMINT".equals(typeName)) {
return Types.INTEGER;
}
}
if (valueType == SQLITE_FLOAT || valueType == SQLITE_NULL) {
if ("DECIMAL".equals(typeName)) {
return Types.DECIMAL;
}
if ("DOUBLE".equals(typeName) || "DOUBLE PRECISION".equals(typeName)) {
return Types.DOUBLE;
}
if ("NUMERIC".equals(typeName)) {
return Types.NUMERIC;
}
if ("REAL".equals(typeName)) {
return Types.REAL;
}
if (valueType == SQLITE_FLOAT ||
"FLOAT".equals(typeName)) {
return Types.FLOAT;
}
}
if (valueType == SQLITE_TEXT || valueType == SQLITE_NULL) {
if ("CHARACTER".equals(typeName) || "NCHAR".equals(typeName) ||
"NATIVE CHARACTER".equals(typeName)) {
return Types.CHAR;
}
if ("CLOB".equals(typeName)) {
return Types.CLOB;
}
if ("DATE".equals(typeName) || "DATETIME".equals(typeName)) {
return Types.DATE;
}
if (valueType == SQLITE_TEXT ||
"VARCHAR".equals(typeName) ||
"VARYING CHARACTER".equals(typeName) ||
"NVARCHAR".equals(typeName) ||
"TEXT".equals(typeName)) {
return Types.VARCHAR;
}
}
if (valueType == SQLITE_BLOB || valueType == SQLITE_NULL) {
if ("BINARY".equals(typeName)) {
return Types.BINARY;
}
if (valueType == SQLITE_BLOB ||
"BLOB".equals(typeName)) {
return Types.BLOB;
}
}
return Types.NULL;
}
/**
* @return The data type from either the 'create table' statement,
* or CAST(expr AS TYPE) otherwise sqlite3_value_type.
* @see java.sql.ResultSetMetaData#getColumnTypeName(int)
*/
public String getColumnTypeName(int col) throws SQLException {
String declType = getColumnDeclType(col);
if (declType != null) {
Matcher matcher = COLUMN_TYPENAME.matcher(declType);
matcher.find();
return matcher.group(1).toUpperCase(Locale.ENGLISH);
}
switch (db.column_type(stmt.pointer, checkCol(col))) {
case SQLITE_INTEGER:
return "INTEGER";
case SQLITE_FLOAT:
return "FLOAT";
case SQLITE_BLOB:
return "BLOB";
case SQLITE_NULL:
return "NULL";
case SQLITE_TEXT:
default:
return "NULL";
}
}
/**
* @see java.sql.ResultSetMetaData#getPrecision(int)
*/
public int getPrecision(int col) throws SQLException {
String declType = getColumnDeclType(col);
if (declType != null) {
Matcher matcher = COLUMN_PRECISION.matcher(declType);
return matcher.find() ? Integer.parseInt(matcher.group(1).split(",")[0].trim()) : 0;
}
return 0;
}
private String getColumnDeclType(int col) throws SQLException {
String declType = db.column_decltype(stmt.pointer, checkCol(col));
if (declType == null) {
Matcher matcher = COLUMN_TYPECAST.matcher(db.column_name(stmt.pointer, checkCol(col)));
declType = matcher.find() ? matcher.group(1) : null;
}
return declType;
}
/**
* @see java.sql.ResultSetMetaData#getScale(int)
*/
public int getScale(int col) throws SQLException {
String declType = getColumnDeclType(col);
if (declType != null) {
Matcher matcher = COLUMN_PRECISION.matcher(declType);
if (matcher.find()) {
String array[] = matcher.group(1).split(",");
if (array.length == 2) {
return Integer.parseInt(array[1].trim());
}
}
}
return 0;
}
/**
* @see java.sql.ResultSetMetaData#getSchemaName(int)
*/
public String getSchemaName(int col) throws SQLException {
return "";
}
/**
* @see java.sql.ResultSetMetaData#getTableName(int)
*/
public String getTableName(int col) throws SQLException {
return db.column_table_name(stmt.pointer, checkCol(col));
}
/**
* @see java.sql.ResultSetMetaData#isNullable(int)
*/
public int isNullable(int col) throws SQLException {
checkMeta();
return meta[checkCol(col)][1] ? columnNoNulls : columnNullable;
}
/**
* @see java.sql.ResultSetMetaData#isAutoIncrement(int)
*/
public boolean isAutoIncrement(int col) throws SQLException {
checkMeta();
return meta[checkCol(col)][2];
}
/**
* @see java.sql.ResultSetMetaData#isCaseSensitive(int)
*/
public boolean isCaseSensitive(int col) throws SQLException {
return true;
}
/**
* @see java.sql.ResultSetMetaData#isCurrency(int)
*/
public boolean isCurrency(int col) throws SQLException {
return false;
}
/**
* @see java.sql.ResultSetMetaData#isDefinitelyWritable(int)
*/
public boolean isDefinitelyWritable(int col) throws SQLException {
return true;
} // FIXME: check db file constraints?
/**
* @see java.sql.ResultSetMetaData#isReadOnly(int)
*/
public boolean isReadOnly(int col) throws SQLException {
return false;
}
/**
* @see java.sql.ResultSetMetaData#isSearchable(int)
*/
public boolean isSearchable(int col) throws SQLException {
return true;
}
/**
* @see java.sql.ResultSetMetaData#isSigned(int)
*/
public boolean isSigned(int col) throws SQLException {
return false;
}
/**
* @see java.sql.ResultSetMetaData#isWritable(int)
*/
public boolean isWritable(int col) throws SQLException {
return true;
}
/**
* @see java.sql.ResultSet#getConcurrency()
*/
public int getConcurrency() throws SQLException {
return CONCUR_READ_ONLY;
}
/**
* @see java.sql.ResultSet#rowDeleted()
*/
public boolean rowDeleted() throws SQLException {
return false;
}
/**
* @see java.sql.ResultSet#rowInserted()
*/
public boolean rowInserted() throws SQLException {
return false;
}
/**
* @see java.sql.ResultSet#rowUpdated()
*/
public boolean rowUpdated() throws SQLException {
return false;
}
/**
* Transforms a Julian Date to java.util.Calendar object.
*/
private Calendar julianDateToCalendar(Double jd) {
return julianDateToCalendar(jd, Calendar.getInstance());
}
/**
* Transforms a Julian Date to java.util.Calendar object.
* Based on Guine Christian's function found here:
* http://java.ittoolbox.com/groups/technical-functional/java-l/java-function-to-convert-julian-date-to-calendar-date-1947446
*/
private Calendar julianDateToCalendar(Double jd, Calendar cal) {
if (jd == null) {
return null;
}
int yyyy, dd, mm, hh, mn, ss, ms , A;
double w = jd + 0.5;
int Z = (int)w;
double F = w - Z;
if (Z < 2299161) {
A = Z;
}
else {
int alpha = (int)((Z - 1867216.25) / 36524.25);
A = Z + 1 + alpha - (int)(alpha / 4.0);
}
int B = A + 1524;
int C = (int)((B - 122.1) / 365.25);
int D = (int)(365.25 * C);
int E = (int)((B - D) / 30.6001);
// month
mm = E - ((E < 13.5) ? 1 : 13);
// year
yyyy = C - ((mm > 2.5) ? 4716 : 4715);
// Day
double jjd = B - D - (int)(30.6001 * E) + F;
dd = (int)jjd;
// Hour
double hhd = jjd - dd;
hh = (int)(24 * hhd);
// Minutes
double mnd = (24 * hhd) - hh;
mn = (int)(60 * mnd);
// Seconds
double ssd = (60 * mnd) - mn;
ss = (int)(60 * ssd);
// Milliseconds
double msd = (60 * ssd) - ss;
ms = (int)(1000 * msd);
cal.set(yyyy, mm-1, dd, hh, mn, ss);
cal.set(Calendar.MILLISECOND, ms);
if (yyyy<1) {
cal.set(Calendar.ERA, GregorianCalendar.BC);
cal.set(Calendar.YEAR, -(yyyy-1));
}
return cal;
}
public void checkCalendar(Calendar cal) throws SQLException {
if (cal != null)
return;
SQLException e = new SQLException("Expected a calendar instance.");
e.initCause(new NullPointerException());
throw e;
}
}