package io.github.lucaseasedup.logit.storage; import java.io.IOException; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.LinkedList; import java.util.List; import java.util.Properties; public final class SqliteStorage implements Storage { public SqliteStorage(String host) { if (host == null) throw new IllegalArgumentException(); this.host = host; } @Override public void connect() throws IOException { try { connection = org.sqlite.JDBC.createConnection( host, new Properties() ); statement = connection.createStatement(); } catch (SQLException ex) { throw new IOException(ex); } } @Override public boolean isConnected() throws IOException { try { return connection != null && !connection.isClosed(); } catch (SQLException ex) { throw new IOException(ex); } } @Override public void ping() throws IOException { try { statement.execute("SELECT 1"); } catch (SQLException ex) { throw new IOException(ex); } } @Override public void close() throws IOException { if (connection != null) { try { connection.close(); } catch (SQLException ex) { throw new IOException(ex); } finally { connection = null; } } if (statement != null) { try { statement.close(); } catch (SQLException ex) { throw new IOException(ex); } finally { statement = null; } } } @Override public List<String> getUnitNames() throws IOException { List<String> units = new LinkedList<>(); String sql = "SELECT name FROM sqlite_master WHERE type = 'table';"; try (ResultSet rs = executeQuery(sql)) { while (rs.next()) { units.add(rs.getString(1)); } } catch (SQLException ex) { throw new IOException(ex); } return units; } @Override public UnitKeys getKeys(String unit) throws IOException { UnitKeys keys = new UnitKeys(); String sql = "PRAGMA table_info('" + SqlUtils.escapeQuotes(unit, "'", true) + "');"; try (ResultSet tableInfo = executeQuery(sql)) { while (tableInfo.next()) { String name = tableInfo.getString("name"); DataType type = SqlUtils.decodeType(tableInfo.getString("type")); keys.put(name, type); } } catch (SQLException ex) { throw new IOException(ex); } return keys; } @Override public String getPrimaryKey(String unit) throws IOException { String sql = "PRAGMA table_info('" + SqlUtils.escapeQuotes(unit, "'", true) + "');"; try (ResultSet rs = executeQuery(sql)) { while (rs.next()) { if ("1".equals(rs.getString("pk"))) { return rs.getString("name"); } } } catch (SQLException ex) { throw new IOException(ex); } return null; } @Override public List<StorageEntry> selectEntries(String unit) throws IOException { String sql = "SELECT * FROM `" + SqlUtils.escapeQuotes(unit, "`", true) + "`;"; try { return SqlUtils.copyResultSet(executeQuery(sql)); } catch (SQLException ex) { throw new IOException(ex); } } @Override public List<StorageEntry> selectEntries(String unit, Selector selector) throws IOException { String sql = "SELECT * FROM `" + SqlUtils.escapeQuotes(unit, "`", true) + "`" + " WHERE " + SqlUtils.translateSelector(selector, "`", "'") + ";"; try { return SqlUtils.copyResultSet(executeQuery(sql)); } catch (SQLException ex) { throw new IOException(ex); } } @Override public List<StorageEntry> selectEntries(String unit, List<String> keys) throws IOException { String sql = "SELECT " + SqlUtils.translateKeyList(keys, "`") + " FROM `" + SqlUtils.escapeQuotes(unit, "`", true) + "`;"; try { return SqlUtils.copyResultSet(executeQuery(sql)); } catch (SQLException ex) { throw new IOException(ex); } } @Override public List<StorageEntry> selectEntries( String unit, List<String> keys, Selector selector ) throws IOException { String sql = "SELECT " + SqlUtils.translateKeyList(keys, "`") + " FROM `" + SqlUtils.escapeQuotes(unit, "`", true) + "`" + " WHERE " + SqlUtils.translateSelector(selector, "`", "'") + ";"; try { return SqlUtils.copyResultSet(executeQuery(sql)); } catch (SQLException ex) { throw new IOException(ex); } } @Override public void createUnit(String unit, UnitKeys keys, String primaryKey) throws IOException { String sql = "CREATE TABLE IF NOT EXISTS `" + SqlUtils.escapeQuotes(unit, "`", true) + "`" + " (" + SqlUtils.translateKeyTypeList(keys, primaryKey, "`") + ");"; try { executeStatement(sql); } catch (SQLException ex) { throw new IOException(ex); } } @Override public void renameUnit(String unit, String newName) throws IOException { String sql = "ALTER TABLE `" + SqlUtils.escapeQuotes(unit, "`", true) + "`" + " RENAME TO `" + SqlUtils.escapeQuotes(newName, "`", true) + "`;"; try { executeStatement(sql); } catch (SQLException ex) { throw new IOException(ex); } } @Override public void eraseUnit(String unit) throws IOException { String sql = "DELETE FROM `" + SqlUtils.escapeQuotes(unit, "`", true) + "`;"; try { executeStatement(sql); } catch (SQLException ex) { throw new IOException(ex); } } @Override public void removeUnit(String unit) throws IOException { String sql = "DROP TABLE `" + SqlUtils.escapeQuotes(unit, "`", true) + "`;"; try { executeStatement(sql); } catch (SQLException ex) { throw new IOException(ex); } } @Override public void addKey(String unit, String key, DataType type) throws IOException { String sql = "ALTER TABLE `" + SqlUtils.escapeQuotes(unit, "`", true) + "`" + " ADD COLUMN `" + SqlUtils.escapeQuotes(key, "`", true) + "` " + SqlUtils.encodeType(type) + ";"; try { executeStatement(sql); } catch (SQLException ex) { throw new IOException(ex); } } @Override public void addEntry(String unit, StorageEntry entry) throws DuplicateEntryException, IOException { String sql = "INSERT INTO `" + SqlUtils.escapeQuotes(unit, "`", true) + "`" + " (" + SqlUtils.translateEntryNames(entry, "`") + ")" + " VALUES (" + SqlUtils.translateEntryValues(entry, "'") + ");"; try { executeStatement(sql); } catch (SQLException ex) { if ("23000".equals(ex.getSQLState())) { throw new DuplicateEntryException(); } else { throw new IOException(ex); } } } @Override public void updateEntries( String unit, StorageEntry entrySubset, Selector selector ) throws IOException { String sql = "UPDATE `" + SqlUtils.escapeQuotes(unit, "`", true) + "`" + " SET " + SqlUtils.translateEntrySubset(entrySubset, "`", "'") + " WHERE " + SqlUtils.translateSelector(selector, "`", "'") + ";"; try { executeStatement(sql); } catch (SQLException ex) { throw new IOException(ex); } } @Override public void removeEntries(String unit, Selector selector) throws IOException { String sql = "DELETE FROM `" + SqlUtils.escapeQuotes(unit, "`", true) + "`" + " WHERE " + SqlUtils.translateSelector(selector, "`", "'") + ";"; try { executeStatement(sql); } catch (SQLException ex) { throw new IOException(ex); } } @Override public boolean isAutobatchEnabled() { return autobatch; } @Override public void setAutobatchEnabled(boolean status) { autobatch = status; } @Override public void executeBatch() throws IOException { try { statement.executeBatch(); statement.clearBatch(); } catch (SQLException ex) { throw new IOException(ex); } } @Override public void clearBatch() throws IOException { try { statement.clearBatch(); } catch (SQLException ex) { throw new IOException(ex); } } private ResultSet executeQuery(String sql) throws SQLException { return statement.executeQuery(sql); } private boolean executeStatement(String sql) throws SQLException { if (!isAutobatchEnabled()) { return statement.execute(sql); } addBatch(sql); return false; } private void addBatch(String sql) throws SQLException { statement.addBatch(sql); } private final String host; private Connection connection; private Statement statement; private boolean autobatch = false; }