/* * 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 totalcross.db.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.text.DateFormat; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Map; import java.util.Hashtable; */ import totalcross.sql.*; import totalcross.util.*; import java.sql.SQLException; import java.sql.SQLWarning; public class SQLiteConnection implements Connection { // private static final String RESOURCE_NAME_PREFIX = ":resource:"; 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 busyTimeout = 0; private final int openModeFlags; private SQLiteConfig.TransactionMode transactionMode = SQLiteConfig.TransactionMode.DEFFERED; private final static Hashtable/*<TransactionMode, String>*/ beginCommandMap = new Hashtable/*<SQLiteConfig.TransactionMode, String>*/(10); static { beginCommandMap.put(SQLiteConfig.TransactionMode.DEFFERED, "begin;"); beginCommandMap.put(SQLiteConfig.TransactionMode.IMMEDIATE, "begin immediate;"); beginCommandMap.put(SQLiteConfig.TransactionMode.EXCLUSIVE, "begin exclusive;"); } /* Date storage configuration */ public final SQLiteConfig.DateClass dateClass; public final SQLiteConfig.DatePrecision datePrecision; //Calendar.SECOND or Calendar.MILLISECOND public final long dateMultiplier; public final SQLiteConfig.DateFormat dateFormat; /** * Constructor to create a connection to a database at the given location. * @param url The location of the database. * @param fileName The database. * @throws SQLException */ public SQLiteConnection(String url, String fileName) throws SQLException { this(url, fileName, new Hashtable(10)); } /** * Constructor to create a pre-configured connection to a database at the * given location. * @param url The location of the database file. * @param fileName The database. * @param prop The configurations to apply. * @throws SQLException */ public SQLiteConnection(String url, String fileName, Hashtable prop) throws SQLException { this.url = url; this.fileName = fileName; SQLiteConfig config = new SQLiteConfig(prop); this.dateClass = config.dateClass; this.dateMultiplier = config.dateMultiplier; this.dateFormat = new SQLiteConfig.DateFormat(config.dateStringFormat); this.datePrecision = config.datePrecision; this.transactionMode = config.getTransactionMode(); this.openModeFlags = config.getOpenModeFlags(); open(openModeFlags, config.busyTimeout); if (fileName.startsWith("file:") && fileName.indexOf("cache=") == -1) { // URI cache overrides flags db.shared_cache(config.isEnabledSharedCache()); } db.enable_load_extension(config.isEnabledLoadExtension()); // set pragmas config.apply(this); } /** * Opens a connection to the database using an SQLite library. * @param openModeFlags Flags for file open operations. * @throws SQLException * @see <a href="http://www.sqlite.org/c3ref/c_open_autoproxy.html">http://www.sqlite.org/c3ref/c_open_autoproxy.html</a> */ private void open(int openModeFlags, int busyTimeout) throws SQLException { // load the native DB try { if (!isLoaded) NativeDB.load(); db = new NativeDB(); } catch (Exception e) { throw new SQLException("Error opening connection"+RS.initCause(e)); } db.open(this, fileName, openModeFlags); setBusyTimeout(busyTimeout); } static boolean isLoaded; /** * @return The busy timeout value for the connection. * @see <a href="http://www.sqlite.org/c3ref/busy_timeout.html">http://www.sqlite.org/c3ref/busy_timeout.html</a> */ public int getBusyTimeout() { return busyTimeout; } /** * Sets the timeout value for the connection. * A timeout value less than or equal to zero turns off all busy handlers. * @see <a href="http://www.sqlite.org/c3ref/busy_timeout.html">http://www.sqlite.org/c3ref/busy_timeout.html</a> * @param milliseconds The timeout value in milliseconds. * @throws SQLException */ public void setBusyTimeout(int milliseconds) throws SQLException { busyTimeout = milliseconds; db.busy_timeout(busyTimeout); } /** * @return Where the database is located. */ String url() { return url; } /** * @return Compile-time library version numbers. * @throws SQLException * @see <a href="http://www.sqlite.org/c3ref/c_source_id.html">http://www.sqlite.org/c3ref/c_source_id.html</a> */ String libversion() throws SQLException { checkOpen(); return db.libversion(); } /** * @return The class interface to SQLite. */ DB db() { return db; } /** * Whether an SQLite library interface to the database has been established. */ private void checkOpen() throws SQLException { if (db == null) throw new SQLException("database connection closed"); } /** * Checks whether the type, concurrency, and holdability settings for a * {@link ResultSet} are supported by the SQLite interface. Supported * settings are:<ul> * <li>type: {@link ResultSet.TYPE_FORWARD_ONLY}</li> * <li>concurrency: {@link ResultSet.CONCUR_READ_ONLY})</li> * <li>holdability: {@link ResultSet.CLOSE_CURSORS_AT_COMMIT}</li></ul> * @param rst the type setting. * @param rsc the concurrency setting. * @param rsh the holdability setting. * @throws SQLException */ 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"); } /** * @see java.lang.Object#finalize() */ public void finalize() throws SQLException { close(); } /** * @see java.sql.Connection#close() */ public void close() throws SQLException { if (db == null) return; if (meta != null) meta.close(); db.close(); db = null; } /** * @see java.sql.Connection#isClosed() */ public boolean isClosed() throws SQLException { return db == null; } /** * @see java.sql.Connection#getCatalog() */ public String getCatalog() throws SQLException { checkOpen(); return null; } /** * @see java.sql.Connection#setCatalog(java.lang.String) */ public void setCatalog(String catalog) throws SQLException { checkOpen(); } /** * @see java.sql.Connection#getHoldability() */ public int getHoldability() throws SQLException { checkOpen(); return ResultSet.CLOSE_CURSORS_AT_COMMIT; } /** * @see java.sql.Connection#setHoldability(int) */ 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"); } /** * @see java.sql.Connection#getTransactionIsolation() */ public int getTransactionIsolation() { return transactionIsolation; } /** * @see java.sql.Connection#setTransactionIsolation(int) */ public void setTransactionIsolation(int level) throws SQLException { checkOpen(); 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; } /** * Sets the mode that will be used to start transactions on this connection. * @param mode One of {@link TransactionMode} * @see <a href="http://www.sqlite.org/lang_transaction.html">http://www.sqlite.org/lang_transaction.html</a> */ protected void setTransactionMode(SQLiteConfig.TransactionMode mode) { this.transactionMode = mode; } /** * @see java.sql.Connection#getTypeMap() */ public Hashtable getTypeMap() throws SQLException { throw new SQLException("not yet implemented"); } /** * @see java.sql.Connection#setTypeMap(java.util.Map) */ public void setTypeMap(Hashtable map) throws SQLException { throw new SQLException("not yet implemented"); } /** * @see java.sql.Connection#isReadOnly() */ public boolean isReadOnly() throws SQLException { return (openModeFlags & SQLiteOpenMode.READONLY.value) != 0; } /** * @see java.sql.Connection#setReadOnly(boolean) */ public void setReadOnly(boolean ro) throws SQLException { // trying to change read-only flag if (ro != isReadOnly()) { throw new SQLException( "Cannot change read-only flag after establishing a connection." + " Use SQLiteConfig#setReadOnly and SQLiteConfig.createConnection()."); } } /** * @throws SQLException * @see java.sql.Connection#getMetaData() */ public DatabaseMetaData getMetaData() throws SQLException { checkOpen(); if (meta == null) { meta = new MetaData(this); } return meta; } /** * @see java.sql.Connection#nativeSQL(java.lang.String) */ public String nativeSQL(String sql) { return sql; } /** * @see java.sql.Connection#clearWarnings() */ public void clearWarnings() throws SQLException {} /** * @see java.sql.Connection#getWarnings() */ public SQLWarning getWarnings() throws SQLException { return null; } /** * @see java.sql.Connection#getAutoCommit() */ public boolean getAutoCommit() throws SQLException { checkOpen(); return autoCommit; } /** * @see java.sql.Connection#setAutoCommit(boolean) */ public void setAutoCommit(boolean ac) throws SQLException { checkOpen(); if (autoCommit == ac) return; autoCommit = ac; db.exec(autoCommit ? "commit;" : (String)beginCommandMap.get(transactionMode)); } /** * @see java.sql.Connection#commit() */ public void commit() throws SQLException { checkOpen(); if (autoCommit) throw new SQLException("database in auto-commit mode"); db.exec("commit;"); db.exec((String)beginCommandMap.get(transactionMode)); } /** * @see java.sql.Connection#rollback() */ public void rollback() throws SQLException { checkOpen(); if (autoCommit) throw new SQLException("database in auto-commit mode"); db.exec("rollback;"); db.exec((String)beginCommandMap.get(transactionMode)); } /** * @see java.sql.Connection#createStatement() */ public Statement createStatement() throws SQLException { return createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT); } /** * @see java.sql.Connection#createStatement(int, int) */ public Statement createStatement(int rsType, int rsConcurr) throws SQLException { return createStatement(rsType, rsConcurr, ResultSet.CLOSE_CURSORS_AT_COMMIT); } /** * @see java.sql.Connection#createStatement(int, int, int) */ public Statement createStatement(int rst, int rsc, int rsh) throws SQLException { checkOpen(); checkCursor(rst, rsc, rsh); return new Stmt(this); } /** * @see java.sql.Connection#prepareStatement(java.lang.String) */ public PreparedStatement prepareStatement(String sql) throws SQLException { return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); } /** * @see java.sql.Connection#prepareStatement(java.lang.String, int) */ public PreparedStatement prepareStatement(String sql, int autoC) throws SQLException { return prepareStatement(sql); } /** * @see java.sql.Connection#prepareStatement(java.lang.String, int[]) */ public PreparedStatement prepareStatement(String sql, int[] colInds) throws SQLException { return prepareStatement(sql); } /** * @see java.sql.Connection#prepareStatement(java.lang.String, java.lang.String[]) */ public PreparedStatement prepareStatement(String sql, String[] colNames) throws SQLException { return prepareStatement(sql); } /** * @see java.sql.Connection#prepareStatement(java.lang.String, int, int) */ public PreparedStatement prepareStatement(String sql, int rst, int rsc) throws SQLException { return prepareStatement(sql, rst, rsc, ResultSet.CLOSE_CURSORS_AT_COMMIT); } /** * @see java.sql.Connection#prepareStatement(java.lang.String, int, int, int) */ public PreparedStatement prepareStatement(String sql, int rst, int rsc, int rsh) throws SQLException { checkOpen(); checkCursor(rst, rsc, rsh); return new PrepStmt(this, sql); } /** * @return One of "native" or "unloaded". */ String getDriverVersion() { // Used to supply DatabaseMetaData.getDriverVersion() return db != null ? "native" : "unloaded"; } }