Restructure database code.
[cfb.git] / prod / net / jaekl / cfb / db / driver / DbDriver.java
index 169cfee3553b8cc2e247bea6914b97b40b5c5b70..ac9f8ccdad2030150d25d0fe06cd15078c8d7fb4 100644 (file)
@@ -1,17 +1,26 @@
 package net.jaekl.cfb.db.driver;
 
+// Copyright (C) 2015 Christian Jaekl
+
 import static net.jaekl.cfb.db.Column.Null.*;
 
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
 
 import net.jaekl.cfb.db.Column;
 import net.jaekl.cfb.db.Column.Type;
+import net.jaekl.cfb.db.Condition;
+import net.jaekl.cfb.db.Row;
+import net.jaekl.cfb.db.Sequence;
 import net.jaekl.cfb.db.Table;
 
 public abstract class DbDriver {
+       static int PENDING_LIMIT = 1024;        // Rough limit at which point we'll start a new batch for batch updates
+       
        DbDriver() {
                
        }
@@ -19,18 +28,179 @@ public abstract class DbDriver {
        // Load the JDBC driver
        public abstract void load() throws ClassNotFoundException;
        
-       public abstract Connection connect(String host, int port, String user, String pass);
+       public abstract Connection connect(String host, int port, String dbName, String user, String pass) throws SQLException;
        
        public boolean createTable(Connection con, Table table) throws SQLException {
                String sql = createTableSql(table);
                try (PreparedStatement ps = con.prepareStatement(sql)) {
                        ps.executeUpdate();
                }
+               catch (SQLException exc) {
+                       throw new SQLException("Failed to executeUpdate:  " + sql, exc);
+               }
                
                return true;
        }
        
-       public abstract ResultSet selectColumnsFromWhere(Column[] columns, Table[] tables, String where);
+       public boolean createSequence(Connection con, Sequence seq) throws SQLException 
+       {
+               String sql = createSequenceSql(seq);
+               try (PreparedStatement ps = con.prepareStatement(sql)) {
+                       ps.executeUpdate();
+               }
+               catch (SQLException exc) {
+                       throw new SQLException("Failed to executeUpdate:  " + sql, exc);
+               }
+               
+               return true;
+       }
+       
+       public List<Row> select(Connection con, Column[] columns, Table[] tables, Condition[] conditions)
+               throws SQLException
+       {
+               String sql = selectSql(columns, tables, conditions);
+               ArrayList<Row> result = new ArrayList<Row>();
+               
+               try (PreparedStatement ps = con.prepareStatement(sql)) {
+                       int index = 0;
+                       for (Condition condition : conditions) {
+                               if (condition.getOperation().hasParam()) {
+                                       index++;
+                                       ps.setObject(index, condition.getValue());
+                               }
+                       }
+                       
+                       try (ResultSet rs = ps.executeQuery()) {
+                               while (rs.next()) {
+                                       Object[] values = new Object[columns.length];
+                                       for (index = 0; index < columns.length; ++index) {
+                                               values[index] = rs.getObject(index);
+                                       }
+                                       Row row = new Row(columns, values);
+                                       result.add(row);
+                               }
+                       }
+               }
+               
+               return result;
+       }
+       
+       // Returns the number of rows inserted
+       public int insert(Connection con, Table table, Object[][] values) throws SQLException 
+       {
+               int count = 0;
+               int pendingValues = 0;
+               
+               String sql = insertSql(table);
+               
+               try (PreparedStatement ps = con.prepareStatement(sql))
+               {
+                       for (int row = 0; row < values.length; ++row) {
+                               Object[] data = values[row];
+                               
+                               assert(null != data);
+                               assert(data.length == table.getNumColumns());
+                               
+                               for (int col = 0; col < data.length; ++col) {
+                                       ps.setObject(col + 1, data[col]);
+                                       pendingValues++;
+                               }
+                               ps.addBatch();
+                               
+                               int rowsFlushed = checkFlushBatch(ps, pendingValues, false);
+                               if (rowsFlushed > 0) {
+                                       count += rowsFlushed;
+                                       pendingValues = 0;
+                               }
+                       }
+                       
+                       count += checkFlushBatch(ps, pendingValues, true);
+               }
+               
+               return count;
+       }
+       
+       int checkFlushBatch(PreparedStatement ps, int pendingValues, boolean forceFlush) throws SQLException
+       {
+               int count = 0;
+               
+               if (forceFlush || (pendingValues >= PENDING_LIMIT)) 
+               {
+                       int[] updateCounts = ps.executeBatch();
+                       for (int i = 0; i < updateCounts.length; ++i) {
+                               if (updateCounts[i] > 0) {
+                                       count += updateCounts[i];
+                               }
+                       }
+               }
+               
+               return count;
+       }
+       
+       String insertSql(Table table) {
+               StringBuilder sb = new StringBuilder("INSERT INTO ");
+               sb.append(table.getName())
+                 .append(" VALUES (");
+               
+               for (int i = 0; i < table.getNumColumns(); ++i) {
+                       if (i > 0) {
+                               sb.append(",");
+                       }
+                       sb.append("?");
+               }
+               sb.append(")");
+               
+               return sb.toString();
+       }
+       
+       protected String selectSql(Column[] columns, Table[] tables, Condition[] conditions) 
+       {
+               StringBuilder sb = new StringBuilder("SELECT ");
+               
+               boolean firstColumn = true;
+               for (Column column : columns) {
+                       if (firstColumn) {
+                               firstColumn = false;
+                       }
+                       else {
+                               sb.append(", ");
+                       }
+                       sb.append(column.getName());
+               }
+               
+               sb.append(" FROM ");
+               
+               boolean firstTable = true;
+               for (Table table : tables) {
+                       if (firstTable) {
+                               firstTable = false;
+                       }
+                       else {
+                               sb.append(", ");
+                       }
+                       sb.append(table.getName());
+               }
+               
+               if (null != conditions && conditions.length > 0) {                      
+                       sb.append(" WHERE ");
+                       
+                       boolean firstCondition = true;
+                       
+                       for (Condition condition : conditions) {
+                               if (firstCondition) {
+                                       firstCondition = false;
+                               }
+                               else {
+                                       sb.append(" AND ");
+                               }
+                               
+                               sb.append(condition.getColumn().getName())
+                                 .append(condition.getOperation().getSql());
+                       }
+               }
+               
+               return sb.toString();
+       }
        
        protected String typeName(Type type) {
                return type.toString();
@@ -76,4 +246,11 @@ public abstract class DbDriver {
                
                return sb.toString();
        }       
+       
+       protected String createSequenceSql(Sequence seq) {
+               assert(null != seq);
+               assert(null != seq.getName());
+               
+               return "CREATE SEQUENCE " + seq.getName();
+       }
 }