Mercurial > hg > monetdb-java
changeset 42:dfea8468cd1a embedded
Finished Java code for CRUD operations on tables and the documentation.
line wrap: on
line diff
--- a/src/main/java/nl/cwi/monetdb/embedded/env/MonetDBEmbeddedConnection.java +++ b/src/main/java/nl/cwi/monetdb/embedded/env/MonetDBEmbeddedConnection.java @@ -8,10 +8,10 @@ package nl.cwi.monetdb.embedded.env; -import nl.cwi.monetdb.embedded.resultset.EmbeddedPreparedStatement; -import nl.cwi.monetdb.embedded.resultset.QueryResultSet; -import nl.cwi.monetdb.embedded.resultset.QueryResultSetColumn; -import nl.cwi.monetdb.embedded.resultset.UpdateResultSet; +import nl.cwi.monetdb.embedded.resultset.*; +import nl.cwi.monetdb.embedded.tables.MonetDBTable; +import nl.cwi.monetdb.embedded.tables.MonetDBTableColumn; +import nl.cwi.monetdb.embedded.utils.StringEscaper; import java.util.HashSet; import java.util.Set; @@ -44,7 +44,7 @@ public class MonetDBEmbeddedConnection { * @throws MonetDBEmbeddedException If an error in the database occurred */ public String getCurrentSchema() throws MonetDBEmbeddedException { - QueryResultSet eqr = this.sendQuery("select current_schema from sys.var();"); + QueryResultSet eqr = this.sendQuery("SELECT current_schema FROM sys.var();"); QueryResultSetColumn<String> col = eqr.getColumn(0); String res = col.fetchFirstNColumnValues(1)[0]; eqr.close(); @@ -54,12 +54,12 @@ public class MonetDBEmbeddedConnection { /** * Sets the current schema on the connection. * - * @param currentSchema Java String with the name of the schema + * @param newSchema Java String with the name of the schema * @throws MonetDBEmbeddedException If an error in the database occurred */ - public void setCurrentSchema(String currentSchema) throws MonetDBEmbeddedException { - String valueToSubmit = "'" + currentSchema.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'") + "';"; - this.sendUpdate("SET SCHEMA " + valueToSubmit).close(); + public void setCurrentSchema(String newSchema) throws MonetDBEmbeddedException { + newSchema = StringEscaper.SQLStringEscape(newSchema); + this.sendUpdate("SET SCHEMA " + newSchema + ";").close(); } /** @@ -152,12 +152,12 @@ public class MonetDBEmbeddedConnection { * @return An instance of EmbeddedPreparedStatement * @throws MonetDBEmbeddedException If an error in the database occurred */ - public EmbeddedPreparedStatement createPreparedStatement(String query) throws MonetDBEmbeddedException { + /*public EmbeddedPreparedStatement createPreparedStatement(String query) throws MonetDBEmbeddedException { if (!query.endsWith(";")) { query += ";"; } return this.createPreparedStatementInternal(this.connectionPointer, query); - } + }*/ /** * Creates a prepared query statement likewise the PreparedStatement in JDBC asynchronously. @@ -171,13 +171,56 @@ public class MonetDBEmbeddedConnection { throw new UnsupportedOperationException("Must wait for Java 8 :("); }*/ - /*public MonetDBTable getMonetDBTable(String schemaName, String tableName) throws MonetDBEmbeddedException { - MonetDBTable res = this.getMonetDBTableInternal(schemaName, tableName, this.connectionPointer); - //results.add(res); + /** + * Retrieves a MonetDB Table for further operations + * + * @param schemaName The schema of the table + * @param tableName The name of the table + * @return A MonetDBTable instance with currentColumns details + * @throws MonetDBEmbeddedException If an error in the database occurred + */ + public MonetDBTable getMonetDBTable(String schemaName, String tableName) throws MonetDBEmbeddedException { + String qschemaName = StringEscaper.SQLStringEscape(schemaName); + String qtableName = StringEscaper.SQLStringEscape(tableName); + String query = "SELECT currentColumns.\"name\" AS column, currentColumns.\"type\", currentColumns.\"type_digits\", currentColumns.\"type_scale\", currentColumns.\"default\", currentColumns.\"null\" FROM (SELECT \"id\", \"table_id\", \"name\", \"type\", \"type_digits\", \"type_scale\", \"default\", \"null\", \"number\" FROM sys.currentColumns) AS currentColumns INNER JOIN (SELECT \"id\", \"schema_id\" FROM sys.tables WHERE name=" + + qtableName + ") AS tables ON (tables.\"id\"=currentColumns.\"table_id\") INNER JOIN (SELECT \"id\" FROM sys.schemas WHERE name=" + + qschemaName + ") AS schemas ON (tables.\"schema_id\"=schemas.\"id\") ORDER BY currentColumns.\"number\";"; + + QueryResultSet eqr = this.sendQuery(query); + int numberOfRows = eqr.getNumberOfRows(); + if(numberOfRows == 0) { + throw new MonetDBEmbeddedException("The table " + tableName + " on schema " + schemaName + " does not exist!"); + } + QueryResultSetRows rows = eqr.fetchAllRowValues(); + eqr.close(); + + MonetDBTableColumn<?>[] array = new MonetDBTableColumn<?>[numberOfRows]; + int i = 0; + for(QueryResultSetRows.QueryResulSetRow row : rows.getAllRows()) { + String columnName = row.getColumn(0); + String columnType = row.getColumn(1); + int ndigits = row.getColumn(2); + int nscale = row.getColumn(3); + String defaultValue = row.getColumn(4); + boolean isNullable = row.getColumn(5); + array[i] = new MonetDBTableColumn(i, columnName, columnType, ndigits, nscale, defaultValue, isNullable); + i++; + } + MonetDBTable res = new MonetDBTable(this, schemaName, tableName, array); + results.add(res); return res; } - add the method from current schema - public MonetDBTable getMonetDBTableAsync(String schema, String tableName) throws MonetDBEmbeddedException { + + /** + * Retrieves a MonetDB Table for further operations asynchronously. + * + * @param schemaName The schema of the table + * @param tableName The name of the table + * @return A MonetDBTable instance with currentColumns details + * @throws MonetDBEmbeddedException If an error in the database occurred + */ + /*public MonetDBTable getMonetDBTableAsync(String schemaName, String tableName) throws MonetDBEmbeddedException { + CompletableFuture.supplyAsync(() -> this.getMonetDBTable(schemaName, tableName)); throw new UnsupportedOperationException("Must wait for Java 8 :("); }*/ @@ -205,8 +248,10 @@ public class MonetDBEmbeddedConnection { * @throws MonetDBEmbeddedException If an error in the database occurred */ public boolean checkIfTableExists(String schemaName, String tableName) throws MonetDBEmbeddedException { + schemaName = StringEscaper.SQLStringEscape(schemaName); + tableName = StringEscaper.SQLStringEscape(tableName); String query = - "select schemas.name as sn, tables.name as tn from sys.tables join sys.schemas on tables.schema_id=schemas.id where tables.system=true order by sn, tn and schemas.name ='" + + "select schemas.name as sn, tables.name as tn from sys.tables join sys.schemas on sys.tables.schema_id=schemas.id where tables.system=true order by sn, tn and schemas.name ='" + schemaName + "' and tables.name ='" + tableName + "';"; QueryResultSet eqr = this.sendQuery(query); eqr.close(); @@ -221,6 +266,8 @@ public class MonetDBEmbeddedConnection { * @throws MonetDBEmbeddedException If an error in the database occurred */ public void removeTable(String schemaName, String tableName) throws MonetDBEmbeddedException { + schemaName = StringEscaper.SQLStringEscape(schemaName); + tableName = StringEscaper.SQLStringEscape(tableName); String query = "drop table " + schemaName + "." + tableName + ";"; this.sendUpdate(query).close(); } @@ -258,17 +305,23 @@ public class MonetDBEmbeddedConnection { this.results.remove(res); } + /** + * Internal implementation of sendUpdate. + */ private native UpdateResultSet sendUpdateInternal(long connectionPointer, String query, boolean execute) throws MonetDBEmbeddedException; + /** + * Internal implementation of sendQuery. + */ private native QueryResultSet sendQueryInternal(long connectionPointer, String query, boolean execute) throws MonetDBEmbeddedException; - private native EmbeddedPreparedStatement createPreparedStatementInternal(long connectionPointer, String query) - throws MonetDBEmbeddedException; - - /*private native MonetDBTable getMonetDBTableInternal(long connectionPointer, String schemaName, String tableName) + /*private native EmbeddedPreparedStatement createPreparedStatementInternal(long connectionPointer, String query) throws MonetDBEmbeddedException;*/ + /** + * Internal implementation to close a connection. + */ private native void closeConnectionInternal(long connectionPointer); }
--- a/src/main/java/nl/cwi/monetdb/embedded/env/MonetDBEmbeddedDatabase.java +++ b/src/main/java/nl/cwi/monetdb/embedded/env/MonetDBEmbeddedDatabase.java @@ -160,11 +160,20 @@ public class MonetDBEmbeddedDatabase { this.connections.remove(con); } + /** + * Internal implementation to start a database. + */ private static native MonetDBEmbeddedDatabase StartDatabaseInternal(String dbDirectory, boolean silentFlag, boolean sequentialFlag) throws MonetDBEmbeddedException; + /** + * Internal implementation to stop a database. + */ private native void stopDatabaseInternal(); + /** + * Internal implementation to create a connection on this database. + */ private native MonetDBEmbeddedConnection createConnectionInternal() throws MonetDBEmbeddedException; }
--- a/src/main/java/nl/cwi/monetdb/embedded/env/MonetDBEmbeddedException.java +++ b/src/main/java/nl/cwi/monetdb/embedded/env/MonetDBEmbeddedException.java @@ -15,7 +15,5 @@ package nl.cwi.monetdb.embedded.env; */ public class MonetDBEmbeddedException extends Exception { - public MonetDBEmbeddedException(String message) { - super(message); - } + public MonetDBEmbeddedException(String message) { super(message); } }
--- a/src/main/java/nl/cwi/monetdb/embedded/mapping/AbstractColumn.java +++ b/src/main/java/nl/cwi/monetdb/embedded/mapping/AbstractColumn.java @@ -8,8 +8,6 @@ package nl.cwi.monetdb.embedded.mapping; -import nl.cwi.monetdb.embedded.mapping.MonetDBToJavaMapping; - /** * A single Java representation of a MonetDB column. * @@ -19,7 +17,7 @@ import nl.cwi.monetdb.embedded.mapping.M public abstract class AbstractColumn<T> { /** - * Index on the result set. + * The column index on the result set. */ protected final int resultSetIndex; @@ -39,7 +37,7 @@ public abstract class AbstractColumn<T> protected final int columnDigits; /** - * The precision after decimal point. Only applicable for decimal/numeric types. + * The precision after decimal point. Only applicable for decimal/numeric types. */ protected final int columnScale; @@ -53,14 +51,14 @@ public abstract class AbstractColumn<T> } /** - * Get the result set index of the column. + * Gets the result set index of the column. * * @return The index number */ public int getResultSetIndex() { return resultSetIndex; } /** - * Get the name of the column. + * Gets the name of the column. * * @return The column name */ @@ -69,31 +67,30 @@ public abstract class AbstractColumn<T> } /** - * Get the type of the column. + * Gets the type of the column. * * @return The Column type */ public String getColumnType() { return mapping.toString(); } /** - * Get the Java mapping of the column. + * Gets the Java mapping of the column. * * @return A enum constant of the Java mapping */ public MonetDBToJavaMapping getMapping() { return mapping; } /** - * Get column digits of the column. + * Gets the number digits of the column. * * @return The number of digits */ public int getColumnDigits() { return columnDigits; } /** - * Get scale of the column. + * Gets the scale of the column. * * @return The scale */ public int getColumnScale() { return columnScale; } - }
--- a/src/main/java/nl/cwi/monetdb/embedded/mapping/AbstractResultTable.java +++ b/src/main/java/nl/cwi/monetdb/embedded/mapping/AbstractResultTable.java @@ -10,14 +10,12 @@ import nl.cwi.monetdb.embedded.env.Monet */ public abstract class AbstractResultTable extends AbstractConnectionResult { - public AbstractResultTable(MonetDBEmbeddedConnection connection) { - super(connection); - } + public AbstractResultTable(MonetDBEmbeddedConnection connection) { super(connection); } /** - * Returns an array of columns in the result set; + * Returns an array of columns in the result set. * - * @return An array of columns in the result set; + * @return An array of columns in the result set */ protected abstract AbstractColumn<?>[] getColumns(); @@ -36,7 +34,7 @@ public abstract class AbstractResultTabl public abstract int getNumberOfRows(); /** - * Get the columns names as a string array. + * Gets the columns names as a string array. * * @return The columns names array */ @@ -50,7 +48,7 @@ public abstract class AbstractResultTabl } /** - * Get the columns types as a string array. + * Gets the columns types as a string array. * * @return The columns types array */ @@ -64,7 +62,7 @@ public abstract class AbstractResultTabl } /** - * Get the Java mappings as a MonetDBToJavaMapping array. + * Gets the Java mappings as a MonetDBToJavaMapping array. * * @return The columns MonetDBToJavaMapping array */ @@ -78,7 +76,7 @@ public abstract class AbstractResultTabl } /** - * Get the columns digits as a int array. + * Gets the columns digits as an integer array. * * @return The columns digits array */ @@ -92,7 +90,7 @@ public abstract class AbstractResultTabl } /** - * Get the columns scales as a int array. + * Gets the columns scales as an integer array. * * @return The columns scales array */
--- a/src/main/java/nl/cwi/monetdb/embedded/mapping/MonetDBEmbeddedBlob.java +++ b/src/main/java/nl/cwi/monetdb/embedded/mapping/MonetDBEmbeddedBlob.java @@ -1,24 +1,45 @@ package nl.cwi.monetdb.embedded.mapping; +import java.util.Arrays; + /** - * A Java representation for Blob data type. Added for more efficient data mapping when fetching from the database. + * A Java representation for the BLOB data type. Added for more efficient data mapping when fetching from the database. * * @author <a href="mailto:pedro.ferreira@monetdbsolutions.com">Pedro Ferreira</a> */ public class MonetDBEmbeddedBlob { + /** + * The BLOB's content as a Java byte array. + */ private final byte[] blob; - public MonetDBEmbeddedBlob(byte[] blob) { - this.blob = blob; + public MonetDBEmbeddedBlob(byte[] blob) { this.blob = blob; } + + /** + * Get the BLOB content itself, + * + * @return A Java byte array containing the BLOB itself + */ + public byte[] getBlob() { return blob; } + + /** + * Overriding the equals method for the byte array. + */ + @Override + public boolean equals(Object obj) { + return obj instanceof MonetDBEmbeddedBlob && Arrays.equals(this.blob, ((MonetDBEmbeddedBlob) obj).getBlob()); } - public byte[] getBlob() { - return blob; - } - + /** + * Overriding the hashCode method for the byte array. + */ @Override - public String toString() { - return new String(this.blob); - } + public int hashCode() { return Arrays.hashCode(this.blob); } + + /** + * Overriding the toString method for the byte array. + */ + @Override + public String toString() { return Arrays.toString(blob); } }
--- a/src/main/java/nl/cwi/monetdb/embedded/mapping/MonetDBToJavaMapping.java +++ b/src/main/java/nl/cwi/monetdb/embedded/mapping/MonetDBToJavaMapping.java @@ -92,8 +92,5 @@ public enum MonetDBToJavaMapping { * @return The corresponding Java class for the enum value */ @SuppressWarnings("unchecked") - public <T> Class<T> getJavaClass() { - return (Class<T>) this.javaClass; - } - + public <T> Class<T> getJavaClass() { return (Class<T>) this.javaClass; } }
--- a/src/main/java/nl/cwi/monetdb/embedded/resultset/EmbeddedPreparedStatement.java +++ b/src/main/java/nl/cwi/monetdb/embedded/resultset/EmbeddedPreparedStatement.java @@ -12,6 +12,8 @@ import nl.cwi.monetdb.embedded.env.Monet import nl.cwi.monetdb.embedded.env.MonetDBEmbeddedException; import nl.cwi.monetdb.embedded.mapping.MonetDBEmbeddedBlob; import nl.cwi.monetdb.embedded.mapping.MonetDBToJavaMapping; +import nl.cwi.monetdb.embedded.utils.StringEscaper; + import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; @@ -350,8 +352,7 @@ public class EmbeddedPreparedStatement { throw new MonetDBEmbeddedException("The class " + javaClass.getSimpleName() + " is not supported by the mapping!"); } - this.parsedValues[parameter] = "'" + valueToSubmit.replaceAll("\\\\", "\\\\\\\\") - .replaceAll("'", "\\\\'") + "'"; + this.parsedValues[parameter] = StringEscaper.SQLStringEscape(valueToSubmit); } } @@ -360,9 +361,7 @@ public class EmbeddedPreparedStatement { * * @param parameter The index of the parameter */ - public void setParameterNull(int parameter) { - this.parsedValues[parameter] = "NULL"; - } + public void setParameterNull(int parameter) { this.parsedValues[parameter] = "NULL"; } /** * Creates the SQL String from the parsed parameters (adapted from the JDBC driver implementation).
--- a/src/main/java/nl/cwi/monetdb/embedded/resultset/QueryResultSet.java +++ b/src/main/java/nl/cwi/monetdb/embedded/resultset/QueryResultSet.java @@ -52,7 +52,7 @@ public class QueryResultSet extends Abst } /** - * Close the query data so no more new results can be retrieved. + * Closes the query data so no more new results can be retrieved. */ @Override public void closeImplementation() { @@ -64,9 +64,7 @@ public class QueryResultSet extends Abst protected AbstractColumn<?>[] getColumns() { return columns; } @Override - public int getNumberOfRows() { - return this.numberOfRows; - } + public int getNumberOfRows() { return this.numberOfRows; } @Override public int getNumberOfColumns() { return this.columns.length; } @@ -79,7 +77,7 @@ public class QueryResultSet extends Abst public boolean isStatementClosed() { return this.resultPointer == 0; } /** - * Get a columns' values from the result set by index. + * Gets a column from the result set by index. * * @param index QueryResultSetColumn index (starting from 0) * @return The columns, {@code null} if index not in bounds @@ -90,7 +88,7 @@ public class QueryResultSet extends Abst } /** - * Get a columns from the result set by name. + * Gets a column from the result set by name. * * @param name QueryResultSetColumn name * @return The columns @@ -202,5 +200,8 @@ public class QueryResultSet extends Abst } } + /** + * Internal implementation to clean the result set. + */ private native void cleanupResultInternal(long resultPointer); }
--- a/src/main/java/nl/cwi/monetdb/embedded/resultset/QueryResultSetColumn.java +++ b/src/main/java/nl/cwi/monetdb/embedded/resultset/QueryResultSetColumn.java @@ -16,8 +16,7 @@ import java.util.Arrays; import java.util.ListIterator; /** - * Am abstract class for accessing, - * materialised (Java-level) query result columns. + * An abstract class for accessing materialised (Java-level) query result columns. * * @param <T> A Java class mapped to a MonetDB data type * @author <a href="mailto:pedro.ferreira@monetdbsolutions.com">Pedro Ferreira</a> @@ -199,8 +198,8 @@ public class QueryResultSetColumn<T> ext * @return The column values as a Java array * @throws MonetDBEmbeddedException If an error in the database occurred */ - @SuppressWarnings("unchecked") - /*public T[] fetchColumnValuesAsync(int startIndex, int endIndex) throws MonetDBEmbeddedException { + /*@SuppressWarnings("unchecked") + public T[] fetchColumnValuesAsync(int startIndex, int endIndex) throws MonetDBEmbeddedException { return this.fetchColumnValuesAsync(startIndex, endIndex, (Class<T>) this.mapping.getJavaClass()); }*/ @@ -255,6 +254,9 @@ public class QueryResultSetColumn<T> ext } } + /** + * Internal implementation to fetch values from the column. + */ private native T[] fetchValuesInternal(long resultPointer, int resultSetIndex, Class<T> jclass, String className, int enumEntry, int first, int last) throws MonetDBEmbeddedException; }
--- a/src/main/java/nl/cwi/monetdb/embedded/resultset/QueryResultSetRows.java +++ b/src/main/java/nl/cwi/monetdb/embedded/resultset/QueryResultSetRows.java @@ -75,9 +75,7 @@ public class QueryResultSetRows implemen * @param javaClass The Java class * @return The column value as a Java class */ - public <T> T getColumn(int index, Class<T> javaClass) { - return javaClass.cast(columns[index]); - } + public <T> T getColumn(int index, Class<T> javaClass) { return javaClass.cast(columns[index]); } /** * Gets a column value as a Java class using the default mapping. @@ -92,9 +90,7 @@ public class QueryResultSetRows implemen } @Override - public ListIterator<Object> iterator() { - return Arrays.asList(this.columns).listIterator(); - } + public ListIterator<Object> iterator() { return Arrays.asList(this.columns).listIterator(); } } /** @@ -126,9 +122,7 @@ public class QueryResultSetRows implemen * * @return The original query result set this row set belongs */ - public QueryResultSet getQueryResultSet() { - return queryResultSet; - } + public QueryResultSet getQueryResultSet() { return queryResultSet; } /** * Gets all rows of this set. @@ -157,9 +151,7 @@ public class QueryResultSetRows implemen * @param row The index of the row to retrieve * @return A single row in this set */ - public QueryResulSetRow getSingleRow(int row) { - return rows[row]; - } + public QueryResulSetRow getSingleRow(int row) { return rows[row]; } /** * Gets a single value in this set as a Java class. @@ -221,7 +213,5 @@ public class QueryResultSetRows implemen } @Override - public ListIterator<QueryResulSetRow> iterator() { - return Arrays.asList(this.rows).listIterator(); - } + public ListIterator<QueryResulSetRow> iterator() { return Arrays.asList(this.rows).listIterator(); } }
--- a/src/main/java/nl/cwi/monetdb/embedded/resultset/UpdateResultSet.java +++ b/src/main/java/nl/cwi/monetdb/embedded/resultset/UpdateResultSet.java @@ -18,9 +18,10 @@ import nl.cwi.monetdb.embedded.env.Monet */ public class UpdateResultSet extends AbstractConnectionResult { - protected UpdateResultSet(MonetDBEmbeddedConnection connection) { - super(connection); - } + protected UpdateResultSet(MonetDBEmbeddedConnection connection) { super(connection); } + /** + * Close this result set. + */ public void closeImplementation() {} }
--- a/src/main/java/nl/cwi/monetdb/embedded/tables/IMonetDBTableBaseIterator.java +++ b/src/main/java/nl/cwi/monetdb/embedded/tables/IMonetDBTableBaseIterator.java @@ -1,11 +1,25 @@ package nl.cwi.monetdb.embedded.tables; /** - * Created by ferreira on 11/7/16. + * The base interface for iterating a MonetDB Table. The use can specify which rows to iterate in this interface. + * + * @author <a href="mailto:pedro.ferreira@monetdbsolutions.com">Pedro Ferreira</a> */ public interface IMonetDBTableBaseIterator { + /** + * Specify the first row in the table to iterate. If a negative number is provided, then the iteration + * will start on the first row. + * + * @return The first row in the table to iterate + */ int getFirstRowToIterate(); + /** + * Specify the last row in the table to iterate. If a negative number or a number larger than the number of rows + * is provided, then the iteration will end on the last row of the table. + * + * @return The last row in the table to iterate + */ int getLastRowToIterate(); }
--- a/src/main/java/nl/cwi/monetdb/embedded/tables/IMonetDBTableIterator.java +++ b/src/main/java/nl/cwi/monetdb/embedded/tables/IMonetDBTableIterator.java @@ -1,9 +1,16 @@ package nl.cwi.monetdb.embedded.tables; /** - * Created by ferreira on 11/7/16. + * A row iterator for a MonetDB table. + * + * @author <a href="mailto:pedro.ferreira@monetdbsolutions.com">Pedro Ferreira</a> */ public interface IMonetDBTableIterator extends IMonetDBTableBaseIterator { + /** + * The business logic for the iterator. + * + * @param nextRow The next row in the iteration. + */ void nextRow(RowIterator nextRow); }
--- a/src/main/java/nl/cwi/monetdb/embedded/tables/IMonetDBTableRemover.java +++ b/src/main/java/nl/cwi/monetdb/embedded/tables/IMonetDBTableRemover.java @@ -1,9 +1,18 @@ package nl.cwi.monetdb.embedded.tables; /** - * Created by ferreira on 11/7/16. + * A row removal iterator for a MonetDB table. + * + * @author <a href="mailto:pedro.ferreira@monetdbsolutions.com">Pedro Ferreira</a> */ public interface IMonetDBTableRemover extends IMonetDBTableBaseIterator { - void removeNextRow(RowRemover nextRow); + /** + * The business logic for the iterator. Use the + * {@link nl.cwi.monetdb.embedded.tables.RowRemover#setToRemove(boolean) setToRemove} + * method in <code>nextRow</code> to set the current row to remove. + * + * @param nextRow The next row in the iteration. + */ + void nextRow(RowRemover nextRow); }
--- a/src/main/java/nl/cwi/monetdb/embedded/tables/IMonetDBTableUpdater.java +++ b/src/main/java/nl/cwi/monetdb/embedded/tables/IMonetDBTableUpdater.java @@ -1,9 +1,18 @@ package nl.cwi.monetdb.embedded.tables; /** - * Created by ferreira on 11/7/16. + * A row update iterator for a MonetDB table. + * + * @author <a href="mailto:pedro.ferreira@monetdbsolutions.com">Pedro Ferreira</a> */ public interface IMonetDBTableUpdater extends IMonetDBTableBaseIterator { - void updateNextRow(RowUpdater nextRow); + /** + * The business logic for the iterator. Use the + * {@link nl.cwi.monetdb.embedded.tables.RowUpdater#setColumn(int, Object) setColumn} + * method in <code>nextRow</code> to update the current row. + * + * @param nextRow The next row in the iteration. + */ + void nextRow(RowUpdater nextRow); }
--- a/src/main/java/nl/cwi/monetdb/embedded/tables/MonetDBTable.java +++ b/src/main/java/nl/cwi/monetdb/embedded/tables/MonetDBTable.java @@ -4,40 +4,41 @@ import nl.cwi.monetdb.embedded.env.Monet import nl.cwi.monetdb.embedded.mapping.AbstractColumn; import nl.cwi.monetdb.embedded.mapping.AbstractResultTable; import nl.cwi.monetdb.embedded.env.MonetDBEmbeddedConnection; +import nl.cwi.monetdb.embedded.resultset.QueryResultSet; +import nl.cwi.monetdb.embedded.resultset.QueryResultSetColumn; +import nl.cwi.monetdb.embedded.utils.StringEscaper; /** - * Java representation of a MonetDB table. + * Java representation of a MonetDB table. It's possible to perform several CRUD operations using the respective + * provided interfaces. * * @author <a href="mailto:pedro.ferreira@monetdbsolutions.com">Pedro Ferreira</a> */ public class MonetDBTable extends AbstractResultTable { /** - * The table schema + * The table's schema. */ - private final String tableSchema; + private final String schemaName; /** - * The table name + * The table's name. */ private final String tableName; /** - * The table columns + * The table's columns. */ private final MonetDBTableColumn<?>[] columns; - public MonetDBTable(MonetDBEmbeddedConnection connection, String tableSchema, String tableName, + public MonetDBTable(MonetDBEmbeddedConnection connection, String schemaName, String tableName, MonetDBTableColumn<?>[] columns) { super(connection); - this.tableSchema = tableSchema; + this.schemaName = schemaName; this.tableName = tableName; this.columns = columns; } - /** - * Let's see... - */ @Override protected void closeImplementation() {} @@ -49,66 +50,141 @@ public class MonetDBTable extends Abstra @Override public int getNumberOfRows() { - return 0; + int res = -1; + try { + String qschemaName = StringEscaper.SQLStringEscape(this.schemaName); + String qtableName = StringEscaper.SQLStringEscape(this.tableName); + String query = "SELECT COUNT(*) FROM " + qschemaName + "." + qtableName + ";"; + QueryResultSet eqr = this.getConnection().sendQuery(query); + QueryResultSetColumn<Integer> eqc = eqr.getColumn(0); + res = eqc.fetchFirstNColumnValues(1)[0]; + } catch (MonetDBEmbeddedException ex) { + } + return res; } /** - * Get the table schema name + * Gets the table schema name. * * @return The table schema name */ - public String getTableSchema() { return tableSchema; } + public String getSchemaName() { return schemaName; } /** - * Get the table name + * Gets the table name. * * @return The table name */ public String getTableName() { return tableName; } - public void iterateTable(IMonetDBTableIterator iterator) throws MonetDBEmbeddedException { - RowIterator ri = new RowIterator(this, iterator.getFirstRowToIterate(), iterator.getLastRowToIterate()); - while(this.getNextRow(ri)) { - iterator.nextRow(ri); + /** + * Gets the columns nullable indexes as an array. + * + * @return The columns nullable indexes as an array + */ + public boolean[] getColumnNullableIndexes() { + int i = 0; + boolean[] result = new boolean[this.getNumberOfColumns()]; + for(MonetDBTableColumn col : this.columns) { + result[i] = col.isNullable(); } + return result; } - public int updateRows(IMonetDBTableUpdater updater) throws MonetDBEmbeddedException { + /** + * Gets the columns default values in an array. + * + * @return The columns default values in an array + */ + public String[] getColumnDefaultValues() { + int i = 0; + String[] result = new String[this.getNumberOfColumns()]; + for(MonetDBTableColumn col : this.columns) { + result[i] = col.getDefaultValue(); + } + return result; + } + + /** + * Iterate over the table using a {@link nl.cwi.monetdb.embedded.tables.IMonetDBTableIterator} instance. + * + * @param iterator The iterator with the business logic + * @return The number of rows iterated + */ + public int iterateTable(IMonetDBTableIterator iterator) { int res = 0; - RowUpdater ru = new RowUpdater(this, updater.getFirstRowToIterate(), updater.getLastRowToIterate()); - while(this.getNextRow(ru)) { - updater.updateNextRow(ru); - if(ru.toUpdate()) { - res++; - this.updateNextRow(ru); - } - + RowIterator ri = new RowIterator(this, iterator.getFirstRowToIterate(), iterator.getLastRowToIterate()); + while(ri.getNextTableRow()) { + iterator.nextRow(ri); + res++; } return res; } - public int removeRows(IMonetDBTableRemover remover) throws MonetDBEmbeddedException { + /** + * Perform an update iteration over the table using a {@link nl.cwi.monetdb.embedded.tables.IMonetDBTableUpdater} + * instance. + * + * @param updater The iterator with the business logic + * @return The number of rows updated + */ + public int updateRows(IMonetDBTableUpdater updater) { int res = 0; - RowRemover rr = new RowRemover(this, remover.getFirstRowToIterate(), remover.getLastRowToIterate()); - while(this.getNextRow(rr)) { - remover.removeNextRow(rr); - if(rr.isToRemove()) { + RowUpdater ru = new RowUpdater(this, updater.getFirstRowToIterate(), updater.getLastRowToIterate()); + while(ru.getNextTableRow()) { + updater.nextRow(ru); + if(ru.tryUpdate()) { res++; - this.removeNextRow(rr); } } return res; } - public native int truncate(); - - public int appendRows(Object[][] rows) { - return 0; + /** + * Perform a removal iteration over the table using a {@link nl.cwi.monetdb.embedded.tables.IMonetDBTableRemover} + * instance. + * + * @param remover The iterator with the business logic + * @return The number of rows removed + */ + public int removeRows(IMonetDBTableRemover remover) { + int res = 0; + RowRemover rr = new RowRemover(this, remover.getFirstRowToIterate(), remover.getLastRowToIterate()); + while(rr.getNextTableRow()) { + remover.nextRow(rr); + if(rr.tryRemove()) { + res++; + } + } + return res; } - private native boolean getNextRow(RowIterator ri) throws MonetDBEmbeddedException; + /** + * Deletes all rows in the table. + * + * @return The number of rows removed + */ + public native int truncateTable(); - private native boolean updateNextRow(RowUpdater ri) throws MonetDBEmbeddedException; + /** + * Appends new rows to the table. + * + * @param rows An array of rows to append + * @return The number of rows appended + */ + public int appendRows(Object[][] rows) { + int i = 0; + for (Object[] row : rows) { + if (row.length != this.getNumberOfColumns()) { + throw new ArrayStoreException("The values array at row " + i + " differs from the number of columns!"); + } + i++; + } + return this.appendRowsInternal(rows); + } - private native boolean removeNextRow(RowRemover rr) throws MonetDBEmbeddedException; + /** + * Internal implementation of rows insertion. + */ + private native int appendRowsInternal(Object[][] rows); }
--- a/src/main/java/nl/cwi/monetdb/embedded/tables/MonetDBTableColumn.java +++ b/src/main/java/nl/cwi/monetdb/embedded/tables/MonetDBTableColumn.java @@ -9,9 +9,34 @@ import nl.cwi.monetdb.embedded.mapping.A */ public class MonetDBTableColumn<T> extends AbstractColumn<T> { + /** + * A String representation of the default value if exists, otherwise is null. + */ + private final String defaultValue; + + /** + * A boolean indication if the column is nullable. + */ + private final boolean isNullable; + public MonetDBTableColumn(int resultSetIndex, String columnName, String columnType, int columnDigits, - int columnScale) { + int columnScale, String defaultValue, boolean isNullable) { super(resultSetIndex, columnName, columnType, columnDigits, columnScale); + this.defaultValue = defaultValue; + this.isNullable = isNullable; } + /** + * Get the default value if there is one, or null if none. + * + * @return The default value if there is one, or null if none + */ + public String getDefaultValue() { return defaultValue; } + + /** + * Get the indication if the column is nullable. + * + * @return The indication if the column is nullable + */ + public boolean isNullable() { return isNullable; } }
--- a/src/main/java/nl/cwi/monetdb/embedded/tables/RowIterator.java +++ b/src/main/java/nl/cwi/monetdb/embedded/tables/RowIterator.java @@ -3,7 +3,10 @@ package nl.cwi.monetdb.embedded.tables; import nl.cwi.monetdb.embedded.mapping.MonetDBToJavaMapping; /** - * Created by ferreira on 11/7/16. + * The iterator class for a MonetDB table. It's possible to inspect the current currentColumns in the row as well + * their mappings. + * + * @author <a href="mailto:pedro.ferreira@monetdbsolutions.com">Pedro Ferreira</a> */ public class RowIterator { @@ -13,29 +16,36 @@ public class RowIterator { protected final MonetDBTable table; /** - * The mappings of the columns. + * The mappings of the currentColumns. */ protected final MonetDBToJavaMapping[] mappings; /** - * The columns values as Java objects. + * The currentColumns values as Java objects. */ - protected Object[] columns; + protected Object[] currentColumns; /** * The current row number. */ - protected int rowNumber; + protected int currentRowNumber; - private final int firstIndex; + /** + * The first row in the table to iterate. + */ + protected final int firstIndex; - private final int lastIndex; + /** + * The last row in the table to iterate. + */ + protected final int lastIndex; public RowIterator(MonetDBTable table, int firstIndex, int lastIndex) { this.table = table; this.mappings = table.getMappings(); this.firstIndex = Math.max(firstIndex, 0); - this.lastIndex = Math.min(lastIndex, table.getNumberOfRows()); + this.lastIndex = Math.min(Math.min(lastIndex, table.getNumberOfRows()), 0); + this.currentRowNumber = this.firstIndex - 1; //starting on the row before the first index } /** @@ -46,20 +56,39 @@ public class RowIterator { public MonetDBTable getTable() { return table; } /** - * Gets the columns values as Java objects. + * Gets the current row currentColumns values as Java objects. * - * @return The columns values as Java objects + * @return The current row currentColumns values as Java objects */ - public Object[] getColumns() { - return columns; - } + public Object[] getCurrentColumns() { return currentColumns; } /** * Gets the current row number in the iteration. * * @return The current row number in the iteration */ - public int getRowNumber() { return rowNumber; } + public int getCurrentRowNumber() { return currentRowNumber; } + + /** + * Gets the first index used on this iteration. + * + * @return The first index used on this iteration + */ + public int getFirstIndex() { return firstIndex; } + + /** + * Gets the last index used on this iteration. + * + * @return The last index used on this iteration + */ + public int getLastIndex() { return lastIndex; } + + /** + * Checks if there are more rows to iterate after the current one. + * + * @return There are more rows to iterate + */ + public boolean hasMore() { return currentRowNumber < lastIndex; } /** * Gets a column value as a Java class. @@ -69,9 +98,7 @@ public class RowIterator { * @param javaClass The Java class * @return The column value as a Java class */ - public <T> T getColumn(int index, Class<T> javaClass) { - return javaClass.cast(columns[index]); - } + public <T> T getColumn(int index, Class<T> javaClass) { return javaClass.cast(this.currentColumns[index]); } /** * Gets a column value as a Java class using the default mapping. @@ -82,11 +109,23 @@ public class RowIterator { */ public <T> T getColumn(int index) { Class<T> javaClass = this.mappings[index].getJavaClass(); - return javaClass.cast(columns[index]); + return javaClass.cast(this.currentColumns[index]); } - protected void setNextIteration(Object[] columns, int rowNumber) { - this.columns = columns; - this.rowNumber = rowNumber; + /** + * Method used by JNI to set the next columns and increment the current row number. + * + * @param columns The next retrieved columns + */ + protected void setNextIteration(Object[] columns) { + this.currentColumns = columns; + this.currentRowNumber++; } + + /** + * Gets the next row in the iteration if there are more. + * + * @return A boolean indicating if a row was fetched + */ + protected native boolean getNextTableRow(); }
--- a/src/main/java/nl/cwi/monetdb/embedded/tables/RowRemover.java +++ b/src/main/java/nl/cwi/monetdb/embedded/tables/RowRemover.java @@ -1,10 +1,15 @@ package nl.cwi.monetdb.embedded.tables; /** - * Created by ferreira on 11/7/16. + * The removal iterator for a MonetDB table. + * + * @author <a href="mailto:pedro.ferreira@monetdbsolutions.com">Pedro Ferreira</a> */ public class RowRemover extends RowIterator { + /** + * If the next row is going to be removed. + */ private boolean toRemove; public RowRemover(MonetDBTable table, int firstIndex, int lastIndex) { @@ -12,17 +17,42 @@ public class RowRemover extends RowItera this.toRemove = false; } - public boolean isToRemove() { - return toRemove; + /** + * Checks if the next row is going to be removed. + * + * @return If the next row is going to be removed + */ + public boolean isToRemove() { return toRemove; } + + /** + * Sets the current row to remove or not. + * + * @param toRemove A boolean indicating if the next row will be removed + */ + public void setToRemove(boolean toRemove) { this.toRemove = toRemove; } + + /** + * To be called by the JNI interface in every iteration. + * + * @param columns The next row's columns + */ + @Override + protected void setNextIteration(Object[] columns) { + super.setNextIteration(columns); + this.toRemove = false; } - public void setToRemove(boolean toRemove) { - this.toRemove = toRemove; - } + /** + * Remove the current row if it was set for so. + * + * @return If the row was removed internally + */ + protected boolean tryRemove() { return this.isToRemove() && this.removeNextTableRow(); } - @Override - protected void setNextIteration(Object[] columns, int rowNumber) { - super.setNextIteration(columns, rowNumber); - this.toRemove = false; - } + /** + * Removes the next row. + * + * @return If the row was removed internally + */ + private native boolean removeNextTableRow(); }
--- a/src/main/java/nl/cwi/monetdb/embedded/tables/RowUpdater.java +++ b/src/main/java/nl/cwi/monetdb/embedded/tables/RowUpdater.java @@ -3,10 +3,15 @@ package nl.cwi.monetdb.embedded.tables; import java.util.Arrays; /** - * Created by ferreira on 11/7/16. + * The update iterator for a MonetDB table. + * + * @author <a href="mailto:pedro.ferreira@monetdbsolutions.com">Pedro Ferreira</a> */ public class RowUpdater extends RowIterator { + /** + * A boolean array to check the columns to be updated. + */ private final boolean[] updatedIndexes; public RowUpdater(MonetDBTable table, int firstIndex, int lastIndex) { @@ -14,23 +19,57 @@ public class RowUpdater extends RowItera this.updatedIndexes = new boolean[table.getNumberOfColumns()]; } + /** + * Sets a column value as a Java class. + * + * @param <T> A Java class mapped to a MonetDB data type + * @param index The index of the column + * @param value The value to set + */ public <T> void setColumn(int index, T value) { - this.columns[index] = value; + this.currentColumns[index] = this.mappings[index].getJavaClass().cast(value); + this.updatedIndexes[index] = true; + } + + /** + * Sets a column value as a Java class. + * + * @param <T> A Java class mapped to a MonetDB data type + * @param index The index of the column + * @param javaClass The Java class + * @param value The value to set + */ + public <T> void setColumn(int index, Class<T> javaClass, T value) { + this.currentColumns[index] = javaClass.cast(value); this.updatedIndexes[index] = true; } - public <T> void setColumn(int index, Class<T> javaClass, T value) { - this.columns[index] = value; - this.updatedIndexes[index] = true; - } - + /** + * Sets all column values as Java classes. + * + * @param values The values to set + */ public void setAllColumns(Object[] values) { - this.columns = values; + if(values.length != this.currentColumns.length) + throw new ArrayStoreException("The values array and the columns length differ!"); + this.currentColumns = values; Arrays.fill(this.updatedIndexes, true); } + /** + * Gets a boolean array of the columns indexes to be updated. + * + * @return A boolean array of the columns indexes to be updated + */ + public boolean[] getUpdatedIndexes() { return Arrays.copyOf(this.updatedIndexes, this.updatedIndexes.length); } + + /** + * Check if the current row is to be updated. + * + * @return A boolean indicating if the current row is to be updated + */ public boolean toUpdate() { - for (boolean bol : updatedIndexes) { + for (boolean bol : this.updatedIndexes) { if(bol) { return true; } @@ -38,9 +77,28 @@ public class RowUpdater extends RowItera return false; } + /** + * To be called by the JNI interface in every iteration. + * + * @param columns The next row's columns + */ @Override - protected void setNextIteration(Object[] columns, int rowNumber) { - super.setNextIteration(columns, rowNumber); + protected void setNextIteration(Object[] columns) { + super.setNextIteration(columns); Arrays.fill(this.updatedIndexes, false); } + + /** + * Update the current row if there are changes. + * + * @return If the row was updated internally + */ + protected boolean tryUpdate() { return this.toUpdate() && this.updateNextTableRow(); } + + /** + * Updates the next row. + * + * @return If the row was updated internally + */ + private native boolean updateNextTableRow(); }
new file mode 100644 --- /dev/null +++ b/src/main/java/nl/cwi/monetdb/embedded/utils/StringEscaper.java @@ -0,0 +1,19 @@ +package nl.cwi.monetdb.embedded.utils; + +/** + * An util class to escape Java Strings to avoid SQL Injection and other problems with SQL queries. + * + * @author <a href="mailto:pedro.ferreira@monetdbsolutions.com">Pedro Ferreira</a> + */ +public class StringEscaper { + + /** + * Escapes a Java String for usage in SQL queries. + * + * @param input The String to escape + * @return The input String escaped + */ + public static String SQLStringEscape(String input) { + return "'" + input.replaceAll("\\\\", "\\\\\\\\").replaceAll("'", "\\\\'") + "'"; + } +}