ShopChest/libs/org/sqlite/Conn.java
2015-09-06 11:58:25 +02:00

441 lines
15 KiB
Java

/*
* 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.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
class Conn implements Connection
{
private final String url;
private String fileName;
private DB db = null;
private MetaData meta = null;
private boolean autoCommit = true;
private int transactionIsolation = TRANSACTION_SERIALIZABLE;
private int timeout = 0;
public Conn(String url, String fileName) throws SQLException {
this(url, fileName, new Properties());
}
public Conn(String url, String fileName, Properties prop) throws SQLException {
this.url = url;
this.fileName = fileName;
SQLiteConfig config = new SQLiteConfig(prop);
open(config.getOpenModeFlags());
boolean enableSharedCache = config.isEnabledSharedCache();
boolean enableLoadExtension = config.isEnabledLoadExtension();
db.shared_cache(enableSharedCache);
db.enable_load_extension(enableLoadExtension);
// set pragmas
config.apply(this);
}
private static final String RESOURCE_NAME_PREFIX = ":resource:";
private void open(int openModeFlags) throws SQLException {
// check the path to the file exists
if (!":memory:".equals(fileName)) {
if (fileName.startsWith(RESOURCE_NAME_PREFIX)) {
String resourceName = fileName.substring(RESOURCE_NAME_PREFIX.length());
// search the class path
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
URL resourceAddr = contextCL.getResource(resourceName);
if (resourceAddr == null) {
try {
resourceAddr = new URL(resourceName);
}
catch (MalformedURLException e) {
throw new SQLException(String.format("resource %s not found: %s", resourceName, e));
}
}
try {
fileName = extractResource(resourceAddr).getAbsolutePath();
}
catch (IOException e) {
throw new SQLException(String.format("failed to load %s: %s", resourceName, e));
}
}
else {
File file = new File(fileName).getAbsoluteFile();
File parent = file.getParentFile();
if (parent != null && !parent.exists()) {
for (File up = parent; up != null && !up.exists();) {
parent = up;
up = up.getParentFile();
}
throw new SQLException("path to '" + fileName + "': '" + parent + "' does not exist");
}
// check write access if file does not exist
try {
// The extra check to exists() is necessary as createNewFile()
// does not follow the JavaDoc when used on read-only shares.
if (!file.exists() && file.createNewFile())
file.delete();
}
catch (Exception e) {
throw new SQLException("opening db: '" + fileName + "': " + e.getMessage());
}
fileName = file.getAbsolutePath();
}
}
// tries to load native library first
try {
Class< ? > nativedb = Class.forName("org.sqlite.NativeDB");
if (((Boolean) nativedb.getDeclaredMethod("load", (Class< ? >[]) null).invoke((Object) null,
(Object[]) null)).booleanValue())
db = (DB) nativedb.newInstance();
}
catch (Exception e) {} // fall through to nested library
// load nested library (pure-java SQLite)
if (db == null) {
try {
db = (DB) Class.forName("org.sqlite.NestedDB").newInstance();
}
catch (Exception e) {
throw new SQLException("no SQLite library found");
}
}
db.open(this, fileName, openModeFlags);
setTimeout(3000);
}
/**
* @param resourceAddr
* @return extracted file name
* @throws IOException
*/
private File extractResource(URL resourceAddr) throws IOException {
if (resourceAddr.getProtocol().equals("file")) {
try {
return new File(resourceAddr.toURI());
}
catch (URISyntaxException e) {
throw new IOException(e.getMessage());
}
}
String tempFolder = new File(System.getProperty("java.io.tmpdir")).getAbsolutePath();
String dbFileName = String.format("sqlite-jdbc-tmp-%d.db", resourceAddr.hashCode());
File dbFile = new File(tempFolder, dbFileName);
if (dbFile.exists()) {
long resourceLastModified = resourceAddr.openConnection().getLastModified();
long tmpFileLastModified = dbFile.lastModified();
if (resourceLastModified < tmpFileLastModified) {
return dbFile;
}
else {
// remove the old DB file
boolean deletionSucceeded = dbFile.delete();
if (!deletionSucceeded) {
throw new IOException("failed to remove existing DB file: " + dbFile.getAbsolutePath());
}
}
// String md5sum1 = SQLiteJDBCLoader.md5sum(resourceAddr.openStream());
// String md5sum2 = SQLiteJDBCLoader.md5sum(new FileInputStream(dbFile));
//
// if (md5sum1.equals(md5sum2))
// return dbFile; // no need to extract the DB file
// else
// {
// }
}
byte[] buffer = new byte[8192]; // 8K buffer
FileOutputStream writer = new FileOutputStream(dbFile);
InputStream reader = resourceAddr.openStream();
try {
int bytesRead = 0;
while ((bytesRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, bytesRead);
}
return dbFile;
}
finally {
writer.close();
reader.close();
}
}
int getTimeout() {
return timeout;
}
void setTimeout(int ms) throws SQLException {
timeout = ms;
db.busy_timeout(ms);
}
String url() {
return url;
}
String libversion() throws SQLException {
return db.libversion();
}
DB db() {
return db;
}
private void checkOpen() throws SQLException {
if (db == null)
throw new SQLException("database connection closed");
}
private void checkCursor(int rst, int rsc, int rsh) throws SQLException {
if (rst != ResultSet.TYPE_FORWARD_ONLY)
throw new SQLException("SQLite only supports TYPE_FORWARD_ONLY cursors");
if (rsc != ResultSet.CONCUR_READ_ONLY)
throw new SQLException("SQLite only supports CONCUR_READ_ONLY cursors");
if (rsh != ResultSet.CLOSE_CURSORS_AT_COMMIT)
throw new SQLException("SQLite only supports closing cursors at commit");
}
@Override
public void finalize() throws SQLException {
close();
}
public void close() throws SQLException {
if (db == null)
return;
if (meta != null)
meta.close();
db.close();
db = null;
}
public boolean isClosed() throws SQLException {
return db == null;
}
public String getCatalog() throws SQLException {
checkOpen();
return null;
}
public void setCatalog(String catalog) throws SQLException {
checkOpen();
}
public int getHoldability() throws SQLException {
checkOpen();
return ResultSet.CLOSE_CURSORS_AT_COMMIT;
}
public void setHoldability(int h) throws SQLException {
checkOpen();
if (h != ResultSet.CLOSE_CURSORS_AT_COMMIT)
throw new SQLException("SQLite only supports CLOSE_CURSORS_AT_COMMIT");
}
public int getTransactionIsolation() {
return transactionIsolation;
}
public void setTransactionIsolation(int level) throws SQLException {
switch (level) {
case TRANSACTION_SERIALIZABLE:
db.exec("PRAGMA read_uncommitted = false;");
break;
case TRANSACTION_READ_UNCOMMITTED:
db.exec("PRAGMA read_uncommitted = true;");
break;
default:
throw new SQLException("SQLite supports only TRANSACTION_SERIALIZABLE and TRANSACTION_READ_UNCOMMITTED.");
}
transactionIsolation = level;
}
public Map getTypeMap() throws SQLException {
throw new SQLException("not yet implemented");
}
public void setTypeMap(Map map) throws SQLException {
throw new SQLException("not yet implemented");
}
public boolean isReadOnly() throws SQLException {
return false;
} // FIXME
public void setReadOnly(boolean ro) throws SQLException {}
public DatabaseMetaData getMetaData() {
if (meta == null)
meta = new MetaData(this);
return meta;
}
public String nativeSQL(String sql) {
return sql;
}
public void clearWarnings() throws SQLException {}
public SQLWarning getWarnings() throws SQLException {
return null;
}
public boolean getAutoCommit() throws SQLException {
checkOpen();
return autoCommit;
}
public void setAutoCommit(boolean ac) throws SQLException {
checkOpen();
if (autoCommit == ac)
return;
autoCommit = ac;
db.exec(autoCommit ? "commit;" : "begin;");
}
public void commit() throws SQLException {
checkOpen();
if (autoCommit)
throw new SQLException("database in auto-commit mode");
db.exec("commit;");
db.exec("begin;");
}
public void rollback() throws SQLException {
checkOpen();
if (autoCommit)
throw new SQLException("database in auto-commit mode");
db.exec("rollback;");
db.exec("begin;");
}
public Statement createStatement() throws SQLException {
return createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
ResultSet.CLOSE_CURSORS_AT_COMMIT);
}
public Statement createStatement(int rsType, int rsConcurr) throws SQLException {
return createStatement(rsType, rsConcurr, ResultSet.CLOSE_CURSORS_AT_COMMIT);
}
public Statement createStatement(int rst, int rsc, int rsh) throws SQLException {
checkCursor(rst, rsc, rsh);
return new Stmt(this);
}
public CallableStatement prepareCall(String sql) throws SQLException {
return prepareCall(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
ResultSet.CLOSE_CURSORS_AT_COMMIT);
}
public CallableStatement prepareCall(String sql, int rst, int rsc) throws SQLException {
return prepareCall(sql, rst, rsc, ResultSet.CLOSE_CURSORS_AT_COMMIT);
}
public CallableStatement prepareCall(String sql, int rst, int rsc, int rsh) throws SQLException {
throw new SQLException("SQLite does not support Stored Procedures");
}
public PreparedStatement prepareStatement(String sql) throws SQLException {
return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
}
public PreparedStatement prepareStatement(String sql, int autoC) throws SQLException {
return prepareStatement(sql);
}
public PreparedStatement prepareStatement(String sql, int[] colInds) throws SQLException {
return prepareStatement(sql);
}
public PreparedStatement prepareStatement(String sql, String[] colNames) throws SQLException {
return prepareStatement(sql);
}
public PreparedStatement prepareStatement(String sql, int rst, int rsc) throws SQLException {
return prepareStatement(sql, rst, rsc, ResultSet.CLOSE_CURSORS_AT_COMMIT);
}
public PreparedStatement prepareStatement(String sql, int rst, int rsc, int rsh) throws SQLException {
checkCursor(rst, rsc, rsh);
return new PrepStmt(this, sql);
}
/** Used to supply DatabaseMetaData.getDriverVersion(). */
String getDriverVersion() {
if (db != null) {
String dbname = db.getClass().getName();
if (dbname.indexOf("NestedDB") >= 0)
return "pure";
if (dbname.indexOf("NativeDB") >= 0)
return "native";
}
return "unloaded";
}
// UNUSED FUNCTIONS /////////////////////////////////////////////
public Savepoint setSavepoint() throws SQLException {
throw new SQLException("unsupported by SQLite: savepoints");
}
public Savepoint setSavepoint(String name) throws SQLException {
throw new SQLException("unsupported by SQLite: savepoints");
}
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
throw new SQLException("unsupported by SQLite: savepoints");
}
public void rollback(Savepoint savepoint) throws SQLException {
throw new SQLException("unsupported by SQLite: savepoints");
}
public Struct createStruct(String t, Object[] attr) throws SQLException {
throw new SQLException("unsupported by SQLite");
}
}