+ public void dropTable(Connection con, Table table) throws SQLException
+ {
+ String sql = dropTableSql(table);
+ try (PreparedStatement ps = con.prepareStatement(sql)) {
+ ps.executeUpdate();
+ }
+ catch (SQLException exc) {
+ throw new SQLException("Failed to drop table: " + sql, exc);
+ }
+ }
+
+ 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 void dropSequence(Connection con, Sequence seq) throws SQLException
+ {
+ String sql = dropSequenceSql(seq);
+ try (PreparedStatement ps = con.prepareStatement(sql)) {
+ ps.executeUpdate();
+ }
+ catch (SQLException exc) {
+ throw new SQLException("Failed to drop sequence: " + sql, exc);
+ }
+ }
+
+ public Row selectExactlyOne(Connection con, Column[] columns, Table[] tables, Condition[] conditions)
+ throws SQLException
+ {
+ Sort[] sorts = new Sort[0];
+ int limit = 2;
+ List<Row> rows = select(con, columns, tables, conditions, sorts, limit);
+ if (rows.size() < 1) {
+ throw new SQLException("Expected one result, but found none: ", selectSql(columns, tables, conditions, sorts, limit));
+ }
+ if (rows.size() > 1) {
+ throw new SQLException("Expected one result, but found more than one: " + selectSql(columns, tables, conditions, sorts, limit));
+ }
+
+ return rows.get(0);
+ }
+
+ public List<Row> select(Connection con, Column[] columns, Table[] tables, Condition[] conditions)
+ throws SQLException
+ {
+ Sort[] sorts = new Sort[0];
+ int limit = (-1); // no limit
+
+ return select(con, columns, tables, conditions, sorts, limit);
+ }
+
+ public List<Row> select(Connection con, Column[] columns, Table[] tables, Condition[] conditions, Sort[] sorts, int limit)
+ throws SQLException
+ {
+ String sql = selectSql(columns, tables, conditions, sorts, limit);
+ ArrayList<Row> result = new ArrayList<Row>();
+
+ try (PreparedStatement ps = con.prepareStatement(sql)) {
+ int index = 0;
+ for (Condition condition : conditions) {
+ if (condition.getOperation().hasParam()) {
+ Column column = condition.getColumn();
+ index++;
+ column.setObject(ps, index, condition.getValue());
+ }
+ }
+
+ try (ResultSet rs = ps.executeQuery()) {
+ while (rs.next()) {
+ Object[] values = new Object[columns.length];
+ for (index = 0; index < columns.length; ++index) {
+ if (columns[index].getType().equals(Type.TIMESTAMPTZ)) {
+ long milliseconds = rs.getLong(index + 1);
+ values[index] = new java.util.Date(milliseconds);
+ }
+ else {
+ values[index] = rs.getObject(index + 1);
+ }
+ }
+ Row row = new Row(columns, values);
+ result.add(row);
+ }
+ }
+ }
+ catch (SQLException se) {
+ throw new SQLException("Error with SQL: " + sql, se);
+ }
+
+ 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;
+
+ assert( isValidInsert(table, values));
+
+ 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 idx = 0; idx < data.length; ++idx) {
+ Object obj = data[idx];
+ Column column = table.getColumn(idx);
+ column.setObject(ps, idx + 1, obj);
+ 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, Sort[] sorts, int limit)
+ {
+ 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());
+ }
+ }
+
+ if (null != sorts && sorts.length > 0) {
+ sb.append(" ORDER BY ");
+
+ boolean firstSort = true;
+
+ for (Sort sort : sorts) {
+ if (firstSort) {
+ firstSort = false;
+ }
+ else {
+ sb.append(", ");
+ }
+
+ sb.append(sort.getColumn().getName());
+
+ if (sort.getDirection().equals(Sort.Direction.ASCENDING)) {
+ sb.append(" ASC ");
+ }
+ else {
+ sb.append(" DESC ");
+ }
+ }
+ }
+
+ if (limit > 0) {
+ sb.append(" LIMIT " + limit + " ");
+ }
+
+ return sb.toString();
+ }