提交 cd2cf099 authored 作者: Thomas Mueller's avatar Thomas Mueller

javadocs

上级 cf963b69
......@@ -17,7 +17,7 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>
<li>RUNSCRIPT could throw a NullPointerException if the script name was an expression.
</li><li>Improved compatibility. New compatibility modes for Oracle and Derby.
New compatibility flag uniqueIndexNullDistinct to only allow one row with 'NULL' in a unique
index. This flag is enabled for Derby, Oracle, MSSQLServer, and HSQLDB.
......
......@@ -311,6 +311,20 @@ http://groups.google.com/group/h2-database/web/roadmap
<td class="compareY">Yes</td>
<td class="compareY">Yes</td>
</tr><tr>
<td>Computed Columns</td>
<td class="compareY">Yes</td>
<td class="compareN">No</td>
<td class="compareN">No</td>
<td class="compareN">No</td>
<td class="compareY">Yes *6</td>
</tr><tr>
<td>Case Insensitive Columns</td>
<td class="compareY">Yes</td>
<td class="compareN">No</td>
<td class="compareY">Yes</td>
<td class="compareY">Yes</td>
<td class="compareY">Yes *6</td>
</tr><tr>
<td>Custom Aggregate Functions</td>
<td class="compareY">Yes</td>
<td class="compareN">No</td>
......@@ -331,7 +345,8 @@ http://groups.google.com/group/h2-database/web/roadmap
*2 MySQL supports linked MySQL tables under the name 'federated tables'.<br />
*3 Derby support for roles based security and password checking as an option.<br />
*4 Derby only supports global temporary tables.<br />
*5 The default H2 jar file contains debug information.
*5 The default H2 jar file contains debug information, jar files for other databases do not.<br />
*6 PostgreSQL supports functional indexes.
</p>
<h3>Derby and HSQLDB</h3>
......@@ -973,38 +988,81 @@ IGNORECASE=TRUE to the database URL (example: jdbc:h2:~/test;IGNORECASE=TRUE).
<h3>Compatibility Modes</h3>
<p>
For certain features, this database can emulate the behavior of specific databases. Not all features or differences of those
databases are implemented. Currently, this feature is mainly used for randomized comparative testing
(where random statements are executed against multiple databases and the results are compared).
The mode can be changed by specifying the mode in the database URL, or using the SQL statement SET MODE.
To use the HSQLDB mode, you can use the database URL <code>jdbc:h2:~/test;MODE=HSQLDB</code>
or the SQL statement <code>SET MODE HSQLDB</code>.
For certain features, this database can emulate the behavior of specific databases.
Not all features or differences of those databases are implemented.
Here is the list of currently supported modes and the difference to the regular mode:
</p>
<table>
<tr><th>Mode</th><th>Differences</th></tr>
<tr><td>
PostgreSQL
</td><td>
Concatenation of a NULL with another value results in NULL.
Usually, the NULL is treated as an empty string if only one of the operators is NULL,
and NULL is only returned if both values are NULL.
</td></tr>
<tr><td>
MySQL
</td><td>
When inserting data, if a column is defined to be NOT NULL and NULL is inserted,
then a 0 (or empty string, or the current timestamp for timestamp columns) value is used.
Usually, this operation is not allowed and an exception is thrown.
</td></tr>
<tr><td>
HSQLDB
</td><td>
When converting the scale of decimal data, the number is only converted if the new scale is
smaller then current scale.
Usually, the scale is converted and 0s are added if required.
</td></tr>
</table>
<h3>PostgreSQL Compatibility Mode</h3>
<p>
To use the PostgreSQL mode, use the database URL <code>jdbc:h2:~/test;MODE=PostgreSQL</code>
or the SQL statement <code>SET MODE PostgreSQL</code>.
</p>
<ul><li>Concatenation of a NULL with another value results in NULL. Usually, the NULL is treated as an empty
string if only one of the operators is NULL, and NULL is only returned if both values are NULL.
</li><li>When converting a floating point number to a integer, the fractional
digits should not be truncated, but the value should be rounded.
</li><li>The system columns 'CTID' and 'OID' should be supported.
</li></ul>
<h3>MySQL Compatibility Mode</h3>
<p>
To use the MySQL mode, use the database URL <code>jdbc:h2:~/test;MODE=MySQL</code>
or the SQL statement <code>SET MODE MySQL</code>.
</p>
<ul><li>When inserting data, if a column is defined to be NOT NULL and NULL is inserted,
then a 0 (or empty string, or the current timestamp for timestamp columns) value is used.
Usually, this operation is not allowed and an exception is thrown.
</li><li>When converting a floating point number to a integer, the fractional
digits should not be truncated, but the value should be rounded.
</li><li>The identifiers should be returned in lower case.
</li><li>Creating indexes in the CREATE TABLE statement should be supported.
</li></ul>
<h3>HSQLDB Compatibility Mode</h3>
<p>
To use the HSQLDB mode, use the database URL <code>jdbc:h2:~/test;MODE=HSQLDB</code>
or the SQL statement <code>SET MODE HSQLDB</code>.
</p>
<ul><li>Concatenation of a NULL with another value results in NULL. Usually, the NULL is treated as an empty
string if only one of the operators is NULL, and NULL is only returned if both values are NULL.
</li><li>When converting the scale of decimal data, the number is only converted if the new scale is
smaller then current scale. Usually, the scale is converted and 0s are added if required.
</li><li>When using unique indexes, multiple rows with NULL in one of the columns
are allowed by default. However many databases view NULL as distinct in
this regard and only allow one row with NULL.
</li></ul>
<h3>MS SQL Server Compatibility Mode</h3>
<p>
To use the MS SQL Server mode, use the database URL <code>jdbc:h2:~/test;MODE=MSSQLServer</code>
or the SQL statement <code>SET MODE MSSQLServer</code>.
</p>
<ul><li>Identifiers may be quoted using square brackets as in [Test].
</li><li>When using unique indexes, multiple rows with NULL in one of the columns
are allowed by default. However many databases view NULL as distinct in
this regard and only allow one row with NULL.
</li></ul>
<h3>Derby Compatibility Mode</h3>
<p>
To use the Derby mode, use the database URL <code>jdbc:h2:~/test;MODE=Derby</code>
or the SQL statement <code>SET MODE Derby</code>.
</p>
<ul><li>When using unique indexes, multiple rows with NULL in one of the columns
are allowed by default. However many databases view NULL as distinct in
this regard and only allow one row with NULL.
</li></ul>
<h3>Oracle Compatibility Mode</h3>
<p>
To use the Oracle mode, use the database URL <code>jdbc:h2:~/test;MODE=Oracle</code>
or the SQL statement <code>SET MODE Oracle</code>.
</p>
<ul><li>When using unique indexes, multiple rows with NULL in one of the columns
are allowed by default. However many databases view NULL as distinct in
this regard and only allow one row with NULL.
</li></ul>
<br /><a name="trace_options"></a>
<h2>Using the Trace Options</h2>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -32,7 +32,6 @@ import org.h2.util.StringUtils;
public class Bnf {
private static final String SEPARATORS = " [](){}|.,\r\n<>:-+*/=<\">!'$";
private static final long MAX_PARSE_TIME = 100;
private final Random random = new Random();
private final HashMap ruleMap = new HashMap();
......@@ -286,20 +285,17 @@ public class Bnf {
* @return the map of possible token types / tokens
*/
public HashMap getNextTokenList(String query) {
HashMap next = new HashMap();
Sentence sentence = new Sentence();
sentence.next = next;
sentence.text = query;
sentence.setQuery(query);
for (int i = 0; i < statements.size(); i++) {
RuleHead head = (RuleHead) statements.get(i);
if (!head.section.startsWith("Commands")) {
continue;
}
sentence.max = System.currentTimeMillis() + MAX_PARSE_TIME;
sentence.setQuery(query);
sentence.start();
head.getRule().addNextTokenList(sentence);
}
return next;
return sentence.getNext();
}
/**
......
......@@ -78,15 +78,15 @@ public class RuleElement implements Rule {
}
public boolean matchRemove(Sentence sentence) {
if (sentence.stop()) {
if (sentence.shouldStop()) {
return false;
}
String query = sentence.query;
String query = sentence.getQuery();
if (query.length() == 0) {
return false;
}
if (keyword) {
String up = sentence.queryUpper;
String up = sentence.getQueryUpper();
if (up.startsWith(name)) {
query = query.substring(name.length());
while (!"_".equals(name) && query.length() > 0 && Character.isWhitespace(query.charAt(0))) {
......@@ -101,7 +101,7 @@ public class RuleElement implements Rule {
return false;
}
if (name != null && !name.startsWith("@") && (link.name() == null || !link.name().startsWith("@"))) {
query = sentence.query;
query = sentence.getQuery();
while (query.length() > 0 && Character.isWhitespace(query.charAt(0))) {
query = query.substring(1);
}
......@@ -111,13 +111,13 @@ public class RuleElement implements Rule {
}
public void addNextTokenList(Sentence sentence) {
if (sentence.stop()) {
if (sentence.shouldStop()) {
return;
}
if (keyword) {
String query = sentence.query;
String query = sentence.getQuery();
String q = query.trim();
String up = sentence.queryUpper.trim();
String up = sentence.getQueryUpper().trim();
if (q.length() == 0 || name.startsWith(up)) {
if (q.length() < name.length()) {
sentence.add(name, name.substring(q.length()), type);
......
......@@ -109,10 +109,10 @@ public class RuleFixed implements Rule {
}
public boolean matchRemove(Sentence sentence) {
if (sentence.stop()) {
if (sentence.shouldStop()) {
return false;
}
String query = sentence.query;
String query = sentence.getQuery();
if (query.length() == 0) {
return false;
}
......@@ -223,10 +223,10 @@ public class RuleFixed implements Rule {
}
public void addNextTokenList(Sentence sentence) {
if (sentence.stop()) {
if (sentence.shouldStop()) {
return;
}
String query = sentence.query;
String query = sentence.getQuery();
switch(type) {
case YMD:
if (query.length() == 0) {
......
......@@ -96,7 +96,7 @@ public class RuleList implements Rule {
}
public boolean matchRemove(Sentence sentence) {
String query = sentence.query;
String query = sentence.getQuery();
if (query.length() == 0) {
return false;
}
......@@ -118,7 +118,7 @@ public class RuleList implements Rule {
}
public void addNextTokenList(Sentence sentence) {
String old = sentence.query;
String old = sentence.getQuery();
if (or) {
for (int i = 0; i < list.size(); i++) {
sentence.setQuery(old);
......
......@@ -46,10 +46,10 @@ public class RuleOptional implements Rule {
}
public boolean matchRemove(Sentence sentence) {
if (sentence.stop()) {
if (sentence.shouldStop()) {
return false;
}
String query = sentence.query;
String query = sentence.getQuery();
if (query.length() == 0) {
return true;
}
......@@ -60,7 +60,7 @@ public class RuleOptional implements Rule {
}
public void addNextTokenList(Sentence sentence) {
if (sentence.stop()) {
if (sentence.shouldStop()) {
return;
}
rule.addNextTokenList(sentence);
......
......@@ -40,10 +40,10 @@ public class RuleRepeat implements Rule {
}
public boolean matchRemove(Sentence sentence) {
if (sentence.stop()) {
if (sentence.shouldStop()) {
return false;
}
String query = sentence.query;
String query = sentence.getQuery();
if (query.length() == 0) {
return false;
}
......@@ -51,20 +51,20 @@ public class RuleRepeat implements Rule {
if (!rule.matchRemove(sentence)) {
return true;
}
if (sentence.query.length() == 0) {
if (sentence.getQuery().length() == 0) {
return true;
}
}
}
public void addNextTokenList(Sentence sentence) {
if (sentence.stop()) {
if (sentence.shouldStop()) {
return;
}
String old = sentence.query;
String old = sentence.getQuery();
while (true) {
rule.addNextTokenList(sentence);
if (!rule.matchRemove(sentence) || old == sentence.query) {
if (!rule.matchRemove(sentence) || old == sentence.getQuery()) {
break;
}
}
......
......@@ -20,25 +20,60 @@ import org.h2.util.StringUtils;
public class Sentence {
/**
* The possible choices of the item depend on the context.
* This token type means the possible choices of the item depend on the context.
* For example the item represents a table name of the current database.
*/
public static final int CONTEXT = 0;
/**
* The token type for a keyword.
*/
static final int KEYWORD = 1;
/**
* The token type for a function name.
*/
static final int FUNCTION = 2;
public String text;
public String query;
public String queryUpper;
HashMap next;
long max;
private static final long MAX_PROCESSING_TIME = 100;
/**
* The map of next tokens in the form type#tokenName token.
*/
private HashMap next = new HashMap();
/**
* The complete query string.
*/
private String query;
/**
* The uppercase version of the query string.
*/
private String queryUpper;
private long stopAt;
private DbSchema lastMatchedSchema;
private DbTableOrView lastMatchedTable;
private DbTableOrView lastTable;
private HashSet tables;
private HashMap aliases;
/**
* Start the timer to make sure processing doesn't take too long.
*/
void start() {
stopAt = System.currentTimeMillis() + MAX_PROCESSING_TIME;
}
boolean stop() {
return System.currentTimeMillis() > max;
/**
* Check if it's time to stop processing.
* Processing auto-complete shouldn't take more than a few milliseconds.
*
* @return true if it's time to stop processing
*/
boolean shouldStop() {
return System.currentTimeMillis() > stopAt;
}
/**
......@@ -46,7 +81,7 @@ public class Sentence {
*
* @param n the token name
* @param string an example text
* @param type the type
* @param type the token type
*/
public void add(String n, String string, int type) {
next.put(type+"#"+n, string);
......@@ -142,6 +177,11 @@ public class Sentence {
return lastMatchedTable;
}
/**
* Set the query string.
*
* @param query the query string
*/
public void setQuery(String query) {
if (this.query != query) {
this.query = query;
......@@ -149,4 +189,31 @@ public class Sentence {
}
}
/**
* Get the query string.
*
* @return the query
*/
public String getQuery() {
return query;
}
/**
* Get the uppercase version of the query string.
*
* @return the uppercase query
*/
public String getQueryUpper() {
return queryUpper;
}
/**
* Get the map of next tokens.
*
* @return the next token map
*/
public HashMap getNext() {
return next;
}
}
......@@ -148,7 +148,7 @@ public abstract class Command implements CommandInterface {
}
}
protected void start() {
void start() {
startTime = System.currentTimeMillis();
}
......
......@@ -143,6 +143,11 @@ public abstract class Prepared {
return parameters;
}
/**
* Check if all parameters have been set.
*
* @throws SQLException if any parameter has not been set
*/
protected void checkParameters() throws SQLException {
for (int i = 0; parameters != null && i < parameters.size(); i++) {
Parameter param = (Parameter) parameters.get(i);
......@@ -214,10 +219,25 @@ public abstract class Prepared {
return sqlStatement;
}
/**
* Get the object id to use for the database object that is created in this
* statement. This id is only set when the object is persistent.
* If not set, this method returns 0.
*
* @return the object id or 0 if not set
*/
protected int getCurrentObjectId() {
return objectId;
}
/**
* Get the current object id, or get a new id from the database. The object
* id is used when creating new database object (CREATE statement).
*
* @param needFresh if a fresh id is required
* @param dataFile if the object id is used for the
* @return the object id
*/
protected int getObjectId(boolean needFresh, boolean dataFile) {
Database db = session.getDatabase();
int id = objectId;
......
......@@ -242,7 +242,7 @@ public abstract class Query extends Prepared {
}
}
protected void initOrder(ObjectArray expressions, ObjectArray expressionSQL, ObjectArray orderList, int visible,
void initOrder(ObjectArray expressions, ObjectArray expressionSQL, ObjectArray orderList, int visible,
boolean mustBeInResult) throws SQLException {
for (int i = 0; i < orderList.size(); i++) {
SelectOrderBy o = (SelectOrderBy) orderList.get(i);
......
......@@ -66,7 +66,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
cipher = c;
}
protected boolean isEncrypted() {
private boolean isEncrypted() {
return cipher != null;
}
......@@ -94,7 +94,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
return false;
}
protected void deleteStore() throws SQLException {
void deleteStore() throws SQLException {
String fileName = getFileName();
if (fileName != null) {
FileUtils.delete(fileName);
......@@ -111,7 +111,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
store.init();
}
protected void openOutput() throws SQLException {
void openOutput() throws SQLException {
String fileName = getFileName();
if (fileName == null) {
return;
......@@ -128,7 +128,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
}
}
protected void openInput() throws SQLException {
void openInput() throws SQLException {
String fileName = getFileName();
if (fileName == null) {
return;
......@@ -151,7 +151,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
}
}
protected void closeIO() {
void closeIO() {
IOUtils.closeSilently(out);
out = null;
IOUtils.closeSilently(in);
......
......@@ -9,12 +9,34 @@ package org.h2.command.dml;
import org.h2.expression.Expression;
/**
* Describes the ORDER BY clause of a query.
* Describes one element of the ORDER BY clause of a query.
*/
public class SelectOrderBy {
/**
* The order by expression.
*/
public Expression expression;
/**
* The column index expression. This can be a column index number (1 meaning
* the first column of the select list) or a parameter (the parameter is a
* number representing the column index number).
*/
public Expression columnIndexExpr;
/**
* If the column should be sorted descending.
*/
public boolean descending;
/**
* If NULL should be appear first.
*/
public boolean nullsFirst;
/**
* If NULL should be appear at the end.
*/
public boolean nullsLast;
}
......@@ -54,9 +54,8 @@ public class ConstraintReferential extends Constraint {
*/
public static final int SET_NULL = 3;
protected IndexColumn[] columns;
protected IndexColumn[] refColumns;
private IndexColumn[] columns;
private IndexColumn[] refColumns;
private int deleteAction;
private int updateAction;
private Table refTable;
......
......@@ -39,6 +39,14 @@ public abstract class DbObjectBase implements DbObject {
private long modificationId;
private boolean temporary;
/**
* Initialize some attributes of this object.
*
* @param database the database
* @param id the object id
* @param name the name
* @param traceModule the trace module name
*/
protected void initDbObjectBase(Database database, int id, String name, String traceModule) {
this.database = database;
this.trace = database.getTrace(traceModule);
......@@ -125,6 +133,10 @@ public abstract class DbObjectBase implements DbObject {
return objectName;
}
/**
* Set the main attributes to null to make sure the object is no longer
* used.
*/
protected void invalidate() {
setModified();
id = -1;
......
......@@ -23,13 +23,54 @@ public class Mode {
private static final HashMap MODES = new HashMap();
// Modes are also documented in the features section
/**
* Concatenation of a NULL with another value results in NULL. Usually, the
* NULL is treated as an empty string if only one of the operators is NULL,
* and NULL is only returned if both values are NULL.
*/
public boolean nullConcatIsNull;
/**
* When inserting data, if a column is defined to be NOT NULL and NULL is
* inserted, then a 0 (or empty string, or the current timestamp for
* timestamp columns) value is used. Usually, this operation is not allowed
* and an exception is thrown.
*/
public boolean convertInsertNullToZero;
/**
* When converting the scale of decimal data, the number is only converted
* if the new scale is smaller then current scale. Usually, the scale is
* converted and 0s are added if required.
*/
public boolean convertOnlyToSmallerScale;
/**
* When converting a floating point number to a integer, the fractional
* digits should not be truncated, but the value should be rounded.
*/
public boolean roundWhenConvertToLong;
/**
* The identifiers should be returned in lower case.
*/
public boolean lowerCaseIdentifiers;
/**
* Creating indexes in the CREATE TABLE statement should be supported.
*/
public boolean indexDefinitionInCreateTable;
/**
* The system columns 'CTID' and 'OID' should be supported.
*/
public boolean systemColumns;
/**
* Identifiers may be quoted using square brackets as in [Test].
*/
public boolean squareBracketQuotedNames;
/**
......
......@@ -59,7 +59,7 @@ public abstract class RightOwner extends DbObjectBase {
return false;
}
protected boolean isRightGrantedRecursive(Table table, int rightMask) {
boolean isRightGrantedRecursive(Table table, int rightMask) {
Right right;
if (grantedRights != null) {
right = (Right) grantedRights.get(table);
......
......@@ -1450,6 +1450,12 @@ public class Function extends Expression implements FunctionCall {
}
}
/**
* Check if the parameter count is correct.
*
* @param len the number of parameters set
* @throws SQLException if the parameter count is incorrect
*/
protected void checkParameterCount(int len) throws SQLException {
int min = 0, max = Integer.MAX_VALUE;
switch (info.type) {
......
......@@ -633,7 +633,14 @@ public class FullText implements Trigger {
return search(conn, text, limit, offset, false);
}
protected static SimpleResultSet createResultSet(boolean data) throws SQLException {
/**
* Create an empty search result and initialize the columns.
*
* @param data true if the result set should contain the primary key data as
* an array.
* @return the empty result set
*/
static SimpleResultSet createResultSet(boolean data) throws SQLException {
SimpleResultSet result = new SimpleResultSet();
if (data) {
result.addColumn(FullText.FIELD_SCHEMA, Types.VARCHAR, 0, 0);
......@@ -722,7 +729,14 @@ public class FullText implements Trigger {
return result;
}
protected static Object[][] parseKey(Connection conn, String key) throws SQLException {
/**
* Parse a primary key condition into the primary key columns.
*
* @param conn the database connection
* @param key the primary key condition as a string
* @return an array containing the column name list and the data list
*/
static Object[][] parseKey(Connection conn, String key) throws SQLException {
ArrayList columns = new ArrayList();
ArrayList data = new ArrayList();
JdbcConnection c = (JdbcConnection) conn;
......
......@@ -22,17 +22,28 @@ import org.h2.value.Value;
* An abstract b-tree page.
*/
public abstract class BtreePage extends Record {
// TODO btree: make sure the indexed data is at most half this size!
// (and find a solution to work around this problem!)
// TODO memory: the btree page needs a lot of memory (in the cache) -
// probably better not use ObjectArray but array;
// not Row but ValueList / Value (for single key index), int array for row
// pos
/**
* The maximum number of blocks occupied by a b-tree page.
*/
protected static final int BLOCKS_PER_PAGE = 1024 / DiskFile.BLOCK_SIZE;
/**
* The b-tree index object
*/
protected BtreeIndex index;
// TODO memory: the btree page needs a lot of memory (in the cache) -
// probably better not use ObjectArray but array
/**
* The list of data pages.
*/
protected ObjectArray pageData;
/**
* If this is the root page of the index.
*/
protected boolean root;
BtreePage(BtreeIndex index) {
......
......@@ -13,12 +13,38 @@ import org.h2.store.Storage;
* Such records are only used when recovering.
*/
public class RedoLogRecord {
/**
* The storage object to where this log record belongs to.
*/
public Storage storage;
/**
* The sequence id. This id is used to sort the records in the same order as
* they appear in the log file.
*/
public int sequenceId;
/**
* The position in the data file.
*/
public int recordId;
/**
* The offset in the data byte array.
*/
public int offset;
/**
* The data.
*/
public byte[] data;
/**
* Get the estimated memory size used by this object.
*
* @return the estimated memory size
*/
public int getSize() {
// estimated memory size in bytes ((5 variables+myself) * 4 bytes each)
if (data == null) {
......@@ -26,4 +52,5 @@ public class RedoLogRecord {
}
return 28 + data.length;
}
}
......@@ -25,11 +25,87 @@ import org.h2.util.StringUtils;
* The base class for objects that can print trace information about themselves.
*/
public class TraceObject {
protected static final int CALLABLE_STATEMENT = 0, CONNECTION = 1, DATABASE_META_DATA = 2,
PREPARED_STATEMENT = 3, RESULT_SET = 4, RESULT_SET_META_DATA = 5,
SAVEPOINT = 6, SQL_EXCEPTION = 7, STATEMENT = 8, BLOB = 9, CLOB = 10,
PARAMETER_META_DATA = 11;
protected static final int DATA_SOURCE = 12, XA_DATA_SOURCE = 13, XID = 14, ARRAY = 15;
/**
* The trace type id for callable statements.
*/
protected static final int CALLABLE_STATEMENT = 0;
/**
* The trace type id for connections.
*/
protected static final int CONNECTION = 1;
/**
* The trace type id for database meta data objects.
*/
protected static final int DATABASE_META_DATA = 2;
/**
* The trace type id for prepared statements.
*/
protected static final int PREPARED_STATEMENT = 3;
/**
* The trace type id for result sets.
*/
protected static final int RESULT_SET = 4;
/**
* The trace type id for result set meta data objects.
*/
protected static final int RESULT_SET_META_DATA = 5;
/**
* The trace type id for savepoint objects.
*/
protected static final int SAVEPOINT = 6;
/**
* The trace type id for sql exceptions.
*/
protected static final int SQL_EXCEPTION = 7;
/**
* The trace type id for statements.
*/
protected static final int STATEMENT = 8;
/**
* The trace type id for blobs.
*/
protected static final int BLOB = 9;
/**
* The trace type id for clobs.
*/
protected static final int CLOB = 10;
/**
* The trace type id for parameter meta data objects.
*/
protected static final int PARAMETER_META_DATA = 11;
/**
* The trace type id for data sources.
*/
protected static final int DATA_SOURCE = 12;
/**
* The trace type id for XA data sources.
*/
protected static final int XA_DATA_SOURCE = 13;
/**
* The trace type id for transaction ids.
*/
protected static final int XID = 14;
/**
* The trace type id for array objects.
*/
protected static final int ARRAY = 15;
private static final int LAST = ARRAY + 1;
private static final int[] ID = new int[LAST];
private static final String[] PREFIX = {
......@@ -41,6 +117,13 @@ public class TraceObject {
private int type;
private int id;
/**
* Set the options to use when writing trace message.
*
* @param trace the trace object
* @param type the trace object type
* @param id the trace object id
*/
protected void setTrace(Trace trace, int type, int id) {
this.trace = trace;
this.type = type;
......@@ -61,52 +144,116 @@ public class TraceObject {
return PREFIX[type] + id;
}
/**
* Get the next trace object id for this object type.
*
* @param type the object type
* @return the new trace object id
*/
protected int getNextId(int type) {
return ID[type]++;
}
/**
* Check if the debug trace level is enabled.
*
* @return true if it is
*/
protected boolean isDebugEnabled() {
return trace.isDebugEnabled();
}
/**
* Check if info trace level is enabled.
*
* @return true if it is
*/
protected boolean isInfoEnabled() {
return trace.isInfoEnabled();
}
/**
* Write trace information as an assignment in the form
* className prefixId = objectName.value.
*
* @param className the class name of the result
* @param type the prefix type
* @param id the trace object id of the created object
* @param value the value to assign this new object to
*/
protected void debugCodeAssign(String className, int type, int id, String value) {
if (trace.isDebugEnabled()) {
trace.debugCode(className + " " + PREFIX[type] + id + " = " + getTraceObjectName() + "." + value + ";");
}
}
protected void debugCodeCall(String text) {
/**
* Write trace information as a method call in the form
* objectName.methodName().
*
* @param methodName the method name
*/
protected void debugCodeCall(String methodName) {
if (trace.isDebugEnabled()) {
trace.debugCode(getTraceObjectName() + "." + text + "();");
trace.debugCode(getTraceObjectName() + "." + methodName + "();");
}
}
protected void debugCodeCall(String text, long param) {
/**
* Write trace information as a method call in the form
* objectName.methodName(param) where the parameter is formatted as a long
* value.
*
* @param methodName the method name
* @param param one single long parameter
*/
protected void debugCodeCall(String methodName, long param) {
if (trace.isDebugEnabled()) {
trace.debugCode(getTraceObjectName() + "." + text + "(" + param + ");");
trace.debugCode(getTraceObjectName() + "." + methodName + "(" + param + ");");
}
}
protected void debugCodeCall(String text, String param) {
/**
* Write trace information as a method call in the form
* objectName.methodName(param) where the parameter is formatted as a Java
* string.
*
* @param methodName the method name
* @param param one single string parameter
*/
protected void debugCodeCall(String methodName, String param) {
if (trace.isDebugEnabled()) {
trace.debugCode(getTraceObjectName() + "." + text + "(" + quote(param) + ");");
trace.debugCode(getTraceObjectName() + "." + methodName + "(" + quote(param) + ");");
}
}
/**
* Write trace information in the form objectName.text.
*
* @param text the trace text
*/
protected void debugCode(String text) {
if (trace.isDebugEnabled()) {
trace.debugCode(getTraceObjectName() + "." + text);
}
}
/**
* Format a string as a Java string literal.
*
* @param s the string to convert
* @return the Java string literal
*/
protected String quote(String s) {
return StringUtils.quoteJavaString(s);
}
/**
* Format a time to the Java source code that represents this object.
*
* @param x the time to convert
* @return the Java source code
*/
protected String quoteTime(java.sql.Time x) {
if (x == null) {
return "null";
......@@ -114,6 +261,12 @@ public class TraceObject {
return "Time.valueOf(\"" + x.toString() + "\")";
}
/**
* Format a timestamp to the Java source code that represents this object.
*
* @param x the timestamp to convert
* @return the Java source code
*/
protected String quoteTimestamp(java.sql.Timestamp x) {
if (x == null) {
return "null";
......@@ -121,6 +274,12 @@ public class TraceObject {
return "Timestamp.valueOf(\"" + x.toString() + "\")";
}
/**
* Format a date to the Java source code that represents this object.
*
* @param x the date to convert
* @return the Java source code
*/
protected String quoteDate(java.sql.Date x) {
if (x == null) {
return "null";
......@@ -128,6 +287,12 @@ public class TraceObject {
return "Date.valueOf(\"" + x.toString() + "\")";
}
/**
* Format a big decimal to the Java source code that represents this object.
*
* @param x the big decimal to convert
* @return the Java source code
*/
protected String quoteBigDecimal(BigDecimal x) {
if (x == null) {
return "null";
......@@ -135,6 +300,12 @@ public class TraceObject {
return "new BigDecimal(\"" + x.toString() + "\")";
}
/**
* Format a byte array to the Java source code that represents this object.
*
* @param x the byte array to convert
* @return the Java source code
*/
protected String quoteBytes(byte[] x) {
if (x == null) {
return "null";
......@@ -142,14 +313,33 @@ public class TraceObject {
return "org.h2.util.ByteUtils.convertStringToBytes(\"" + ByteUtils.convertBytesToString(x) + "\")";
}
/**
* Format a string array to the Java source code that represents this
* object.
*
* @param s the string array to convert
* @return the Java source code
*/
protected String quoteArray(String[] s) {
return StringUtils.quoteJavaStringArray(s);
}
/**
* Format an int array to the Java source code that represents this object.
*
* @param s the int array to convert
* @return the Java source code
*/
protected String quoteIntArray(int[] s) {
return StringUtils.quoteJavaIntArray(s);
}
/**
* Format a map to the Java source code that represents this object.
*
* @param map the map to convert
* @return the Java source code
*/
protected String quoteMap(Map map) {
if (map == null) {
return "null";
......@@ -175,6 +365,12 @@ public class TraceObject {
return buff.toString();
}
/**
* Log an exception and convert it to a SQL exception if required.
*
* @param e the exception
* @return the SQL exception object
*/
protected SQLException logAndConvert(Exception e) {
if (SysProperties.LOG_ALL_ERRORS) {
synchronized (TraceObject.class) {
......
......@@ -15,6 +15,14 @@ public abstract class SchemaObjectBase extends DbObjectBase implements SchemaObj
private Schema schema;
/**
* Initialize some attributes of this object.
*
* @param schema the schema
* @param id the object id
* @param name the name
* @param traceModule the trace module name
*/
protected void initSchemaObjectBase(Schema schema, int id, String name, String traceModule) {
initDbObjectBase(schema.getDatabase(), id, name, traceModule);
this.schema = schema;
......
......@@ -14,7 +14,24 @@ import org.h2.util.StringUtils;
* This class is used by the H2 Console.
*/
public class ConnectionInfo {
public String driver, url, user;
/**
* The driver class name.
*/
public String driver;
/**
* The database URL.
*/
public String url;
/**
* The user name.
*/
public String user;
/**
* The connection display name.
*/
String name;
int lastAccess;
......
......@@ -98,7 +98,7 @@ public class DbContextRule implements Rule {
}
private void addTableAlias(Sentence sentence) {
String query = sentence.query;
String query = sentence.getQuery();
String q = StringUtils.toUpperEnglish(query.trim());
HashMap map = sentence.getAliases();
HashSet set = new HashSet();
......@@ -136,7 +136,7 @@ public class DbContextRule implements Rule {
}
private void addNewTableAlias(Sentence sentence) {
String query = sentence.query;
String query = sentence.getQuery();
if (SUGGEST_TABLE_ALIAS) {
// good when testing!
if (query.length() > 3) {
......@@ -181,7 +181,7 @@ public class DbContextRule implements Rule {
// }
private void addSchema(Sentence sentence) {
String query = sentence.query;
String query = sentence.getQuery();
String q = StringUtils.toUpperEnglish(query);
if (q.trim().length() == 0) {
q = q.trim();
......@@ -201,7 +201,7 @@ public class DbContextRule implements Rule {
}
private void addTable(Sentence sentence) {
String query = sentence.query;
String query = sentence.getQuery();
DbSchema schema = sentence.getLastMatchedSchema();
if (schema == null) {
schema = contents.defaultSchema;
......@@ -222,14 +222,11 @@ public class DbContextRule implements Rule {
}
private void addColumn(Sentence sentence) {
String query = sentence.query;
String query = sentence.getQuery();
String tableName = query;
String columnPattern = "";
if (query.trim().length() == 0) {
tableName = null;
if (sentence.text.trim().endsWith(".")) {
return;
}
} else {
tableName = StringUtils.toUpperEnglish(query.trim());
if (tableName.endsWith(".")) {
......@@ -286,7 +283,7 @@ public class DbContextRule implements Rule {
}
public boolean matchRemove(Sentence sentence) {
if (sentence.query.length() == 0) {
if (sentence.getQuery().length() == 0) {
return false;
}
String s;
......@@ -320,8 +317,8 @@ public class DbContextRule implements Rule {
}
private String matchSchema(Sentence sentence) {
String query = sentence.query;
String up = sentence.queryUpper;
String query = sentence.getQuery();
String up = sentence.getQueryUpper();
DbSchema[] schemas = contents.schemas;
String best = null;
DbSchema bestSchema = null;
......@@ -347,8 +344,8 @@ public class DbContextRule implements Rule {
}
private String matchTable(Sentence sentence) {
String query = sentence.query;
String up = sentence.queryUpper;
String query = sentence.getQuery();
String up = sentence.getQueryUpper();
DbSchema schema = sentence.getLastMatchedSchema();
if (schema == null) {
schema = contents.defaultSchema;
......@@ -378,8 +375,8 @@ public class DbContextRule implements Rule {
}
private String matchColumnAlias(Sentence sentence) {
String query = sentence.query;
String up = sentence.queryUpper;
String query = sentence.getQuery();
String up = sentence.getQueryUpper();
int i = 0;
if (query.indexOf(' ') < 0) {
return null;
......@@ -401,8 +398,8 @@ public class DbContextRule implements Rule {
}
private String matchTableAlias(Sentence sentence, boolean add) {
String query = sentence.query;
String up = sentence.queryUpper;
String query = sentence.getQuery();
String up = sentence.getQueryUpper();
int i = 0;
if (query.indexOf(' ') < 0) {
return null;
......@@ -455,8 +452,8 @@ public class DbContextRule implements Rule {
}
private String matchColumn(Sentence sentence) {
String query = sentence.query;
String up = sentence.queryUpper;
String query = sentence.getQuery();
String up = sentence.getQueryUpper();
HashSet set = sentence.getTables();
String best = null;
DbTableOrView last = sentence.getLastMatchedTable();
......
......@@ -139,6 +139,11 @@ public class FileStore {
return store;
}
/**
* Generate the random salt bytes if required.
*
* @return the random salt or the magic
*/
protected byte[] generateSalt() {
return magic;
}
......
......@@ -871,7 +871,7 @@ public class MetaTable extends Table {
// PARAMS
t.params,
// AUTO_INCREMENT
String.valueOf(t.autoInc),
String.valueOf(t.autoIncrement),
// MINIMUM_SCALE
String.valueOf(t.minScale),
// MAXIMUM_SCALE
......
......@@ -17,13 +17,34 @@ import org.h2.store.DiskFile;
*/
public abstract class CacheObject {
public CacheObject previous, next, chained;
/**
* The previous element in the LRU linked list. If the previous element is
* the head, then this element is the most recently used object.
*/
public CacheObject previous;
/**
* The next element in the LRU linked list. If the next element is the head,
* then this element is the least recently used object.
*/
public CacheObject next;
/**
* The next element in the hash chain.
*/
public CacheObject chained;
/**
* The cache queue identifier. This field is only used for the 2Q cache
* algorithm.
*/
public int cacheQueue;
/**
* The number of blocks occupied by this object.
*/
protected int blockCount;
private int pos;
private boolean changed;
......
......@@ -67,7 +67,11 @@ public abstract class HashBase {
return size + (zeroKey ? 1 : 0);
}
protected void checkSizePut() throws SQLException {
/**
* Check the size before adding an entry. This method resizes the map if
* required.
*/
void checkSizePut() throws SQLException {
if (deletedCount > size) {
rehash(level);
}
......@@ -76,6 +80,10 @@ public abstract class HashBase {
}
}
/**
* Check the size before removing an entry. This method resizes the map if
* required.
*/
protected void checkSizeRemove() throws SQLException {
if (size < minSize && level > 0) {
rehash(level - 1);
......@@ -84,6 +92,11 @@ public abstract class HashBase {
}
}
/**
* Clear the map and reset the level to the specified value.
*
* @param newLevel the new level
*/
protected void reset(int newLevel) {
minSize = size * 3 / 4;
size = 0;
......@@ -95,6 +108,12 @@ public abstract class HashBase {
maxDeleted = 20 + len / 2;
}
/**
* Calculate the index for this hash code.
*
* @param hash the hash code
* @return the index
*/
protected int getIndex(int hash) {
return hash & mask;
}
......
......@@ -15,6 +15,9 @@ import java.sql.SQLException;
*/
public abstract class Tool {
/**
* The output stream where this tool writes to.
*/
protected PrintStream out = System.out;
/**
......
......@@ -52,27 +52,111 @@ public class DataType {
private static ObjectArray types = new ObjectArray();
private static HashMap typesByName = new HashMap();
private static DataType[] typesByValueType = new DataType[Value.TYPE_COUNT];
/**
* The value type of this data type.
*/
public int type;
/**
* The data type name.
*/
public String name;
/**
* The SQL type.
*/
public int sqlType;
/**
* The SQL type name.
*/
public String jdbc;
// how closely the data type maps
// to the corresponding JDBC SQL type (low is best)
/**
* How closely the data type maps to the corresponding JDBC SQL type (low is
* best).
*/
public int sqlTypePos;
/**
* The maximum supported precision.
*/
public int maxPrecision;
public int minScale, maxScale;
/**
* The lowest possible scale.
*/
public int minScale;
/**
* The highest possible scale.
*/
public int maxScale;
/**
* If this is a numeric type.
*/
public boolean decimal;
public String prefix, suffix;
/**
* The prefix required for the SQL literal representation.
*/
public String prefix;
/**
* The suffix required for the SQL literal representation.
*/
public String suffix;
/**
* The list of parameters used in the column definition.
*/
public String params;
public boolean autoInc;
/**
* If this is an autoincrement type.
*/
public boolean autoIncrement;
/**
* If this data type is an autoincrement type.
*/
public boolean caseSensitive;
public boolean supportsPrecision, supportsScale;
/**
* If the precision parameter is supported.
*/
public boolean supportsPrecision;
/**
* If the scale parameter is supported.
*/
public boolean supportsScale;
/**
* The default precision.
*/
public long defaultPrecision;
/**
* The default scale.
*/
public int defaultScale;
/**
* The default display size.
*/
public int defaultDisplaySize;
/**
* If this data type should not be listed in the database meta data.
*/
public boolean hidden;
/**
* The number of bytes required for an object.
*/
public int memory;
static {
......@@ -250,7 +334,7 @@ public class DataType {
dt.sqlType = sqlType;
dt.jdbc = jdbc;
dt.name = names[i];
dt.autoInc = dataType.autoInc;
dt.autoIncrement = dataType.autoIncrement;
dt.decimal = dataType.decimal;
dt.maxPrecision = dataType.maxPrecision;
dt.maxScale = dataType.maxScale;
......@@ -292,7 +376,7 @@ public class DataType {
dataType.supportsScale = true;
}
dataType.decimal = true;
dataType.autoInc = autoInc;
dataType.autoIncrement = autoInc;
return dataType;
}
......
......@@ -144,14 +144,110 @@ java org.h2.test.TestAll timer
*/
public boolean smallLog, big, networked, memory, ssl, textStorage, diskUndo, diskResult, deleteIndex, traceSystemOut;
public boolean codeCoverage, mvcc, endless;
public int logMode = 1, traceLevelFile, throttle;
/**
* If the test should run with many rows.
*/
public boolean big;
/**
* If remote database connections should be used.
*/
public boolean networked;
/**
* If in-memory databases should be used.
*/
public boolean memory;
/**
* If index files should be deleted before re-opening the database.
*/
public boolean deleteIndex;
/**
* If code coverage is enabled.
*/
public boolean codeCoverage;
/**
* If the multi version concurrency control mode should be used.
*/
public boolean mvcc;
/**
* The log mode to use.
*/
public int logMode = 1;
/**
* The cipher to use (null for unencrypted).
*/
public String cipher;
public boolean traceTest, stopOnError;
/**
* If only JDK 1.4 methods should be tested.
*/
public boolean jdk14 = true;
public boolean cache2Q;
/**
* If the transaction log files should be kept small (that is, log files should be switched early).
*/
boolean smallLog;
/**
* If SSL should be used for remote connections.
*/
boolean ssl;
/**
* If MAX_MEMORY_UNDO=3 should be used.
*/
boolean diskUndo;
/**
* If the text storage mechanism should be used.
*/
boolean textStorage;
/**
* If a small cache and a low number for MAX_MEMORY_ROWS should be used.
*/
boolean diskResult;
/**
* If TRACE_LEVEL_SYSTEM_OUT should be set to 2 (for debugging only).
*/
boolean traceSystemOut;
/**
* If the tests should run forever.
*/
boolean endless;
/**
* The file trace level value to use.
*/
int traceLevelFile;
/**
* The THROTTLE value to use.
*/
int throttle;
/**
* If test trace information should be written (for debugging only).
*/
boolean traceTest;
/**
* If the test should stop when the first error occurs.
*/
boolean stopOnError;
/**
* If the Two-Queue cache algorithm should be used.
*/
boolean cache2Q;
private Server server;
......@@ -168,34 +264,17 @@ java org.h2.test.TestAll timer
System.setProperty("h2.maxMemoryRowsDistinct", "128");
/*
document bug
Caused by: java.lang.NullPointerException
at org.h2.expression.ExpressionColumn.getValue(ExpressionColumn.java:155)
at org.h2.expression.Operation.getValue(Operation.java:97)
at org.h2.command.dml.ScriptBase.getFileName(ScriptBase.java:84)
at org.h2.command.dml.ScriptBase.openInput(ScriptBase.java:132)
at org.h2.command.dml.RunScriptCommand.update(RunScriptCommand.java:38)
at org.h2.command.CommandContainer.update(CommandContainer.java:69)
at org.h2.command.Command.executeUpdate(Command.java:198)
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:163)
... 6 more
add test case
> RUNSCRIPT FROM SCRIPT_DIRECTORY || 'Create_Users.sql';
> SCRIPT_DIRECTORY is a constant.
feature list:
computed columns: H2, HSQLDB (PostgreSQL: functional index)
case insensitive columns: H2, HSQLDB, MySQL (PostgreSQL: functional index)
jazoon
measure and improve performance of ObjectArray.toArray()
H2 Console should support Java Queries
convert test.in.sql to RunScript syntax
C:\download\Data Concurrency and Consistency.pdf
detect deadlock: alarm
not tested:
PreparedProcedure PREPARE <name>(column,...) AS ...
Procedure
......@@ -208,8 +287,6 @@ deleted the same row when exactly does it occur in other databases
create an mbean for each database? server? (jconsole)
jazoon
in help.csv, use complete examples for functions; add a test case
improve javadocs
......
......@@ -31,10 +31,17 @@ import org.h2.tools.DeleteDbFiles;
*/
public abstract class TestBase {
/**
* The base directory to write test databases.
*/
protected static String baseDir = getTestDir("");
private static final String BASE_TEST_DIR = "data";
/**
* The test configuration.
*/
protected TestAll config;
private long start;
/**
......@@ -46,6 +53,9 @@ public abstract class TestBase {
return BASE_TEST_DIR + "/test" + name;
}
/**
* Start the TCP server if enabled in the configuration.
*/
protected void startServerIfRequired() throws SQLException {
config.beforeTest();
}
......@@ -130,6 +140,14 @@ public abstract class TestBase {
}
}
/**
* Get the database URL for the given database name using the current
* configuration options.
*
* @param name the database name
* @param admin true if the current user is an admin
* @return the database URL
*/
protected String getURL(String name, boolean admin) {
String url;
if (name.startsWith("jdbc:")) {
......@@ -213,7 +231,14 @@ public abstract class TestBase {
return conn;
}
protected int getSize(int small, int big) throws Exception {
/**
* Get the small or the big value depending on the configuration.
*
* @param small the value to return if the current test mode is 'small'
* @param big the value to return if the current test mode is 'big'
* @return small or big, depending on the configuration
*/
protected int getSize(int small, int big) {
return config.endless ? Integer.MAX_VALUE : config.big ? big : small;
}
......@@ -221,6 +246,11 @@ public abstract class TestBase {
return "sa";
}
/**
* Write a message to system out if trace is enabled.
*
* @param x the value to write
*/
protected void trace(int x) {
trace("" + x);
}
......@@ -236,6 +266,9 @@ public abstract class TestBase {
}
}
/**
* Print how much memory is currently used.
*/
protected void traceMemory() {
if (config.traceTest) {
trace("mem=" + getMemoryUsed());
......@@ -255,6 +288,11 @@ public abstract class TestBase {
}
}
/**
* Get the number of megabytes heap memory in use.
*
* @return the used megabytes
*/
public static int getMemoryUsed() {
Runtime rt = Runtime.getRuntime();
long memory = Long.MAX_VALUE;
......@@ -270,10 +308,21 @@ public abstract class TestBase {
return mb;
}
/**
* Called if the test reached a point that was not expected.
*
* @throws Exception always throws an exception
*/
protected void fail() throws Exception {
fail("Unexpected success");
}
/**
* Called if the test reached a point that was not expected.
*
* @param string the error message
* @throws Exception always throws an exception
*/
protected void fail(String string) throws Exception {
println(string);
throw new Exception(string);
......@@ -306,6 +355,11 @@ public abstract class TestBase {
}
}
/**
* Print a message to system out.
*
* @param s the message
*/
protected void println(String s) {
long time = System.currentTimeMillis() - start;
printlnWithTime(time, getClass().getName() + " " + s);
......@@ -317,15 +371,31 @@ public abstract class TestBase {
System.out.println(t + " " + s);
}
/**
* Print the current time and a message to system out.
*
* @param s the message
*/
protected void printTime(String s) {
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
println(dateFormat.format(new java.util.Date()) + " " + s);
}
/**
* Delete all database files for this database.
*
* @param name the database name
*/
protected void deleteDb(String name) throws Exception {
DeleteDbFiles.execute(baseDir, name, true);
}
/**
* Delete all database files for a database.
*
* @param dir the directory where the database files are located
* @param name the database name
*/
protected void deleteDb(String dir, String name) throws Exception {
DeleteDbFiles.execute(dir, name, true);
}
......@@ -364,6 +434,13 @@ public abstract class TestBase {
}
}
/**
* Check if two values are equal, and if not throw an exception.
*
* @param expected the expected value
* @param actual the actual value
* @throws Exception if the values are not equal
*/
protected void assertEquals(byte[] expected, byte[] actual) throws Exception {
assertTrue(expected.length == actual.length);
for (int i = 0; i < expected.length; i++) {
......@@ -373,6 +450,13 @@ public abstract class TestBase {
}
}
/**
* Check if two values are equal, and if not throw an exception.
*
* @param expected the expected value
* @param actual the actual value
* @throws Exception if the values are not equal
*/
protected void assertEquals(String expected, String actual) throws Exception {
if (expected == null && actual == null) {
return;
......@@ -399,68 +483,151 @@ public abstract class TestBase {
}
}
/**
* Check if the first value is larger or equal than the second value, and if
* not throw an exception.
*
* @param a the first value
* @param b the second value (must be smaller than the first value)
* @throws Exception if the first value is smaller
*/
protected void assertSmaller(long a, long b) throws Exception {
if (a >= b) {
fail("a: " + a + " is not smaller than b: " + b);
}
}
/**
* Check that a result contains the given substring.
*
* @param result the result value
* @param contains the term that should appear in the result
* @throws Exception if the term was not found
*/
protected void assertContains(String result, String contains) throws Exception {
if (result.indexOf(contains) < 0) {
fail(result + " does not contain: " + contains);
}
}
/**
* Check that a text starts with the expected characters..
*
* @param text the text
* @param expectedStart the expected prefix
* @throws Exception if the text does not start with the expected characters
*/
protected void assertStartsWith(String text, String expectedStart) throws Exception {
if (!text.startsWith(expectedStart)) {
fail(text + " does not start with: " + expectedStart);
}
}
/**
* Check if two values are equal, and if not throw an exception.
*
* @param expected the expected value
* @param actual the actual value
* @throws Exception if the values are not equal
*/
protected void assertEquals(long expected, long actual) throws Exception {
if (expected != actual) {
fail("Expected: " + expected + " actual: " + actual);
}
}
/**
* Check if two values are equal, and if not throw an exception.
*
* @param expected the expected value
* @param actual the actual value
* @throws Exception if the values are not equal
*/
protected void assertEquals(double expected, double actual) throws Exception {
if (expected != actual) {
fail("Expected: " + expected + " actual: " + actual);
}
}
/**
* Check if two values are equal, and if not throw an exception.
*
* @param expected the expected value
* @param actual the actual value
* @throws Exception if the values are not equal
*/
protected void assertEquals(float expected, float actual) throws Exception {
if (expected != actual) {
fail("Expected: " + expected + " actual: " + actual);
}
}
/**
* Check if two values are equal, and if not throw an exception.
*
* @param expected the expected value
* @param actual the actual value
* @throws Exception if the values are not equal
*/
protected void assertEquals(boolean expected, boolean actual) throws Exception {
if (expected != actual) {
fail("Boolean expected: " + expected + " actual: " + actual);
}
}
/**
* Check that the passed boolean is true.
*
* @param condition the condition
* @throws Exception if the condition is false
*/
protected void assertTrue(boolean condition) throws Exception {
assertTrue("Expected: true got: false", condition);
}
/**
* Check that the passed boolean is true.
*
* @param message the message to print if the condition is false
* @param condition the condition
* @throws Exception if the condition is false
*/
protected void assertTrue(String message, boolean condition) throws Exception {
if (!condition) {
fail(message);
}
}
/**
* Check that the passed boolean is false.
*
* @param value the condition
* @throws Exception if the condition is true
*/
protected void assertFalse(boolean value) throws Exception {
assertFalse("Expected: false got: true", value);
}
/**
* Check that the passed boolean is false.
*
* @param message the message to print if the condition is false
* @param value the condition
* @throws Exception if the condition is true
*/
protected void assertFalse(String message, boolean value) throws Exception {
if (value) {
fail(message);
}
}
/**
* Check that the result set row count matches.
*
* @param rs the result set
* @param expected the number of expected rows
* @throws Exception if a different number of rows have been found
*/
protected void assertResultRowCount(ResultSet rs, int expected) throws Exception {
int i = 0;
while (rs.next()) {
......@@ -469,6 +636,14 @@ public abstract class TestBase {
assertEquals(i, expected);
}
/**
* Check that the result set of a query is exactly this value.
*
* @param stat the statement
* @param sql the SQL statement to execute
* @param expected the expected result value
* @throws Exception if a different result value was returned
*/
protected void assertSingleValue(Statement stat, String sql, int expected) throws Exception {
ResultSet rs = stat.executeQuery(sql);
assertTrue(rs.next());
......@@ -476,7 +651,17 @@ public abstract class TestBase {
assertFalse(rs.next());
}
protected void testResultSetMeta(ResultSet rs, int columnCount, String[] labels, int[] datatypes, int[] precision,
/**
* Check if the result set meta data is correct.
*
* @param rs the result set
* @param columnCount the expected column count
* @param labels the expected column labels
* @param datatypes the expected data types
* @param precision the expected precisions
* @param scale the expected scales
*/
protected void assertResultSetMeta(ResultSet rs, int columnCount, String[] labels, int[] datatypes, int[] precision,
int[] scale) throws Exception {
ResultSetMetaData meta = rs.getMetaData();
int cc = meta.getColumnCount();
......@@ -538,15 +723,39 @@ public abstract class TestBase {
}
}
protected void testResultSetOrdered(ResultSet rs, String[][] data) throws Exception {
testResultSet(true, rs, data);
/**
* Check if a result set contains the expected data.
* The sort order is significant
*
* @param rs the result set
* @param data the expected data
* @throws Exception if there is a mismatch
*/
protected void assertResultSetOrdered(ResultSet rs, String[][] data) throws Exception {
assertResultSet(true, rs, data);
}
void testResultSetUnordered(ResultSet rs, String[][] data) throws Exception {
testResultSet(false, rs, data);
/**
* Check if a result set contains the expected data.
* The sort order is not significant
*
* @param rs the result set
* @param data the expected data
* @throws Exception if there is a mismatch
*/
void assertResultSetUnordered(ResultSet rs, String[][] data) throws Exception {
assertResultSet(false, rs, data);
}
void testResultSet(boolean ordered, ResultSet rs, String[][] data) throws Exception {
/**
* Check if a result set contains the expected data.
*
* @param ordered if the sort order is significant
* @param rs the result set
* @param data the expected data
* @throws Exception if there is a mismatch
*/
void assertResultSet(boolean ordered, ResultSet rs, String[][] data) throws Exception {
int len = rs.getMetaData().getColumnCount();
int rows = data.length;
if (rows == 0) {
......@@ -589,7 +798,7 @@ public abstract class TestBase {
}
}
boolean testRow(String[] a, String[] b, int len) {
private boolean testRow(String[] a, String[] b, int len) {
for (int i = 0; i < len; i++) {
String sa = a[i];
String sb = b[i];
......@@ -624,6 +833,13 @@ public abstract class TestBase {
return "{" + sb + "}";
}
/**
* Simulate a database crash. This method will also close the database
* files, but the files are in a state as the power was switched off. It
* doesn't throw an exception.
*
* @param conn the database connection
*/
protected void crash(Connection conn) throws Exception {
((JdbcConnection) conn).setPowerOffCount(1);
try {
......@@ -640,6 +856,12 @@ public abstract class TestBase {
}
}
/**
* Read a string from the reader. This method reads until end of file.
*
* @param reader the reader
* @return the string read
*/
protected String readString(Reader reader) throws Exception {
if (reader == null) {
return null;
......@@ -660,25 +882,52 @@ public abstract class TestBase {
}
}
/**
* Check that a given exception is not an unexpected 'general error'
* exception.
*
* @param e the error
*/
protected void assertKnownException(SQLException e) throws Exception {
assertKnownException("", e);
}
/**
* Check that a given exception is not an unexpected 'general error'
* exception.
*
* @param message the message
* @param e the exception
*/
protected void assertKnownException(String message, SQLException e) throws Exception {
if (e != null && e.getSQLState().startsWith("HY000")) {
TestBase.logError("Unexpected General error " + message, e);
}
}
protected void assertEquals(Integer a, Integer b) throws Exception {
if (a == null || b == null) {
assertTrue(a == b);
/**
* Check if two values are equal, and if not throw an exception.
*
* @param expected the expected value
* @param actual the actual value
* @throws Exception if the values are not equal
*/
protected void assertEquals(Integer expected, Integer actual) throws Exception {
if (expected == null || actual == null) {
assertTrue(expected == actual);
} else {
assertEquals(a.intValue(), b.intValue());
assertEquals(expected.intValue(), actual.intValue());
}
}
protected void compareDatabases(Statement stat1, Statement stat2) throws Exception {
/**
* Check if two databases contain the same met data.
*
* @param stat1 the connection to the first database
* @param stat2 the connection to the second database
* @throws Exception if the database don't match
*/
protected void assertEqualDatabases(Statement stat1, Statement stat2) throws Exception {
ResultSet rs1 = stat1.executeQuery("SCRIPT NOPASSWORDS");
ResultSet rs2 = stat2.executeQuery("SCRIPT NOPASSWORDS");
ArrayList list1 = new ArrayList();
......
......@@ -43,12 +43,12 @@ public class TestBackup extends TestBase {
stat1.execute("backup to '" + baseDir + "/backup.zip'");
conn2.rollback();
compareDatabases(stat1, stat2);
assertEqualDatabases(stat1, stat2);
Restore.execute(baseDir + "/backup.zip", baseDir, "restored", true);
conn3 = getConnection("restored");
stat3 = conn3.createStatement();
compareDatabases(stat1, stat3);
assertEqualDatabases(stat1, stat3);
conn1.close();
conn2.close();
......
......@@ -88,7 +88,7 @@ public class TestMemoryUsage extends TestBase {
System.gc();
System.gc();
int used = MemoryUtils.getMemoryUsed();
if ((used - start) > 4000) {
if ((used - start) > 5000) {
fail("Used: " + (used - start));
}
stat.execute("drop table test");
......
......@@ -85,7 +85,7 @@ public class TestRunscript extends TestBase implements Trigger {
stat2.execute(sql);
stat2.execute("script to '" + baseDir + "/backup.3.sql'");
compareDatabases(stat1, stat2);
assertEqualDatabases(stat1, stat2);
conn1.close();
conn2.close();
......
......@@ -248,20 +248,20 @@ public class TestMetaData extends TestBase {
stat.execute("CREATE ALIAS PROP FOR \"java.lang.System.getProperty(java.lang.String)\"");
stat.execute("CREATE ALIAS EXIT FOR \"java.lang.System.exit\"");
rs = meta.getProcedures(null, null, "EX%");
testResultSetMeta(rs, 8, new String[] { "PROCEDURE_CAT", "PROCEDURE_SCHEM", "PROCEDURE_NAME",
assertResultSetMeta(rs, 8, new String[] { "PROCEDURE_CAT", "PROCEDURE_SCHEM", "PROCEDURE_NAME",
"NUM_INPUT_PARAMS", "NUM_OUTPUT_PARAMS", "NUM_RESULT_SETS", "REMARKS", "PROCEDURE_TYPE" }, new int[] {
Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.INTEGER,
Types.VARCHAR, Types.SMALLINT }, null, null);
testResultSetOrdered(rs, new String[][] { { catalog, Constants.SCHEMA_MAIN, "EXIT", "1", "0", "0", "",
assertResultSetOrdered(rs, new String[][] { { catalog, Constants.SCHEMA_MAIN, "EXIT", "1", "0", "0", "",
"" + DatabaseMetaData.procedureNoResult }, });
rs = meta.getProcedureColumns(null, null, null, null);
testResultSetMeta(rs, 15,
assertResultSetMeta(rs, 15,
new String[] { "PROCEDURE_CAT", "PROCEDURE_SCHEM", "PROCEDURE_NAME", "COLUMN_NAME", "COLUMN_TYPE",
"DATA_TYPE", "TYPE_NAME", "PRECISION", "LENGTH", "SCALE", "RADIX", "NULLABLE", "REMARKS", "NUM_INPUT_PARAMS", "POS" },
new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT, Types.INTEGER,
Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT,
Types.VARCHAR , Types.INTEGER, Types.INTEGER}, null, null);
testResultSetOrdered(rs, new String[][] {
assertResultSetOrdered(rs, new String[][] {
{ catalog, Constants.SCHEMA_MAIN, "EXIT", "P1", "" + DatabaseMetaData.procedureColumnIn,
"" + Types.INTEGER, "INTEGER", "10", "10", "0", "10", "" + DatabaseMetaData.procedureNoNulls },
{ catalog, Constants.SCHEMA_MAIN, "PROP", "P1", "" + DatabaseMetaData.procedureColumnIn,
......@@ -289,13 +289,13 @@ public class TestMetaData extends TestBase {
}
private void checkCrossRef(ResultSet rs) throws Exception {
testResultSetMeta(rs, 14, new String[] { "PKTABLE_CAT", "PKTABLE_SCHEM", "PKTABLE_NAME", "PKCOLUMN_NAME",
assertResultSetMeta(rs, 14, new String[] { "PKTABLE_CAT", "PKTABLE_SCHEM", "PKTABLE_NAME", "PKCOLUMN_NAME",
"FKTABLE_CAT", "FKTABLE_SCHEM", "FKTABLE_NAME", "FKCOLUMN_NAME", "KEY_SEQ", "UPDATE_RULE",
"DELETE_RULE", "FK_NAME", "PK_NAME", "DEFERRABILITY" }, new int[] { Types.VARCHAR, Types.VARCHAR,
Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT }, null,
null);
testResultSetOrdered(rs, new String[][] {
assertResultSetOrdered(rs, new String[][] {
{ catalog, Constants.SCHEMA_MAIN, "PARENT", "A", catalog, Constants.SCHEMA_MAIN, "CHILD", "PA", "1",
"" + DatabaseMetaData.importedKeyRestrict, "" + DatabaseMetaData.importedKeyRestrict, "AB",
null, "" + DatabaseMetaData.importedKeyNotDeferrable },
......@@ -579,7 +579,7 @@ public class TestMetaData extends TestBase {
trace("getTables");
rs = meta.getTables(null, Constants.SCHEMA_MAIN, null, new String[] { "TABLE" });
testResultSetMeta(rs, 6, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "TABLE_TYPE", "REMARKS",
assertResultSetMeta(rs, 6, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "TABLE_TYPE", "REMARKS",
"SQL" }, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
Types.VARCHAR }, null, null);
if (rs.next()) {
......@@ -588,17 +588,17 @@ public class TestMetaData extends TestBase {
stat.executeUpdate("CREATE TABLE TEST(" + "ID INT PRIMARY KEY," + "TEXT_V VARCHAR(120),"
+ "DEC_V DECIMAL(12,3)," + "DATE_V DATETIME," + "BLOB_V BLOB," + "CLOB_V CLOB" + ")");
rs = meta.getTables(null, Constants.SCHEMA_MAIN, null, new String[] { "TABLE" });
testResultSetOrdered(rs, new String[][] { { catalog, Constants.SCHEMA_MAIN, "TEST", "TABLE", "" } });
assertResultSetOrdered(rs, new String[][] { { catalog, Constants.SCHEMA_MAIN, "TEST", "TABLE", "" } });
trace("getColumns");
rs = meta.getColumns(null, null, "TEST", null);
testResultSetMeta(rs, 18, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "COLUMN_NAME", "DATA_TYPE",
assertResultSetMeta(rs, 18, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "COLUMN_NAME", "DATA_TYPE",
"TYPE_NAME", "COLUMN_SIZE", "BUFFER_LENGTH", "DECIMAL_DIGITS", "NUM_PREC_RADIX", "NULLABLE", "REMARKS",
"COLUMN_DEF", "SQL_DATA_TYPE", "SQL_DATETIME_SUB", "CHAR_OCTET_LENGTH", "ORDINAL_POSITION",
"IS_NULLABLE" }, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
Types.SMALLINT, Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.INTEGER, Types.INTEGER,
Types.SMALLINT, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT, Types.INTEGER, Types.INTEGER,
Types.INTEGER, Types.VARCHAR }, null, null);
testResultSetOrdered(rs,
assertResultSetOrdered(rs,
new String[][] {
{ catalog, Constants.SCHEMA_MAIN, "TEST", "ID", "" + Types.INTEGER, "INTEGER", "10", "10", "0",
"10", "" + DatabaseMetaData.columnNoNulls, "", null, "" + Types.INTEGER, "0", "10", "1",
......@@ -628,12 +628,12 @@ public class TestMetaData extends TestBase {
stat.executeUpdate("CREATE INDEX IDX_TEXT_DEC ON TEST(TEXT_V,DEC_V)");
stat.executeUpdate("CREATE UNIQUE INDEX IDX_DATE ON TEST(DATE_V)");
rs = meta.getIndexInfo(null, null, "TEST", false, false);
testResultSetMeta(rs, 14, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "NON_UNIQUE",
assertResultSetMeta(rs, 14, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "NON_UNIQUE",
"INDEX_QUALIFIER", "INDEX_NAME", "TYPE", "ORDINAL_POSITION", "COLUMN_NAME", "ASC_OR_DESC",
"CARDINALITY", "PAGES", "FILTER_CONDITION", "SORT_TYPE"}, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
DataType.TYPE_BOOLEAN, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT, Types.SMALLINT, Types.VARCHAR,
Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.VARCHAR, Types.INTEGER}, null, null);
testResultSetOrdered(rs, new String[][] {
assertResultSetOrdered(rs, new String[][] {
{ catalog, Constants.SCHEMA_MAIN, "TEST", "FALSE", catalog, "IDX_DATE",
"" + DatabaseMetaData.tableIndexOther, "1", "DATE_V", "A", "0", "0", "" },
{ catalog, Constants.SCHEMA_MAIN, "TEST", "FALSE", catalog, "PRIMARY_KEY_2",
......@@ -645,44 +645,44 @@ public class TestMetaData extends TestBase {
stat.executeUpdate("DROP INDEX IDX_TEXT_DEC");
stat.executeUpdate("DROP INDEX IDX_DATE");
rs = meta.getIndexInfo(null, null, "TEST", false, false);
testResultSetMeta(rs, 14, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "NON_UNIQUE",
assertResultSetMeta(rs, 14, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "NON_UNIQUE",
"INDEX_QUALIFIER", "INDEX_NAME", "TYPE", "ORDINAL_POSITION", "COLUMN_NAME", "ASC_OR_DESC",
"CARDINALITY", "PAGES", "FILTER_CONDITION", "SORT_TYPE" }, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
DataType.TYPE_BOOLEAN, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT, Types.SMALLINT, Types.VARCHAR,
Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.VARCHAR, Types.INTEGER }, null, null);
testResultSetOrdered(rs, new String[][] { { catalog, Constants.SCHEMA_MAIN, "TEST", "FALSE", catalog,
assertResultSetOrdered(rs, new String[][] { { catalog, Constants.SCHEMA_MAIN, "TEST", "FALSE", catalog,
"PRIMARY_KEY_2", "" + DatabaseMetaData.tableIndexOther, "1", "ID", "A", "0", "0", "" } });
trace("getPrimaryKeys");
rs = meta.getPrimaryKeys(null, null, "TEST");
testResultSetMeta(rs, 6, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "COLUMN_NAME", "KEY_SEQ",
assertResultSetMeta(rs, 6, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "COLUMN_NAME", "KEY_SEQ",
"PK_NAME" }, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT,
Types.VARCHAR }, null, null);
testResultSetOrdered(rs,
assertResultSetOrdered(rs,
new String[][] { { catalog, Constants.SCHEMA_MAIN, "TEST", "ID", "1", "PRIMARY_KEY_2" }, });
trace("getTables - using a wildcard");
stat.executeUpdate("CREATE TABLE T_2(B INT,A VARCHAR(6),C INT,PRIMARY KEY(C,A,B))");
stat.executeUpdate("CREATE TABLE TX2(B INT,A VARCHAR(6),C INT,PRIMARY KEY(C,A,B))");
rs = meta.getTables(null, null, "T_2", null);
testResultSetOrdered(rs, new String[][] { { catalog, Constants.SCHEMA_MAIN, "TX2", "TABLE", "" },
assertResultSetOrdered(rs, new String[][] { { catalog, Constants.SCHEMA_MAIN, "TX2", "TABLE", "" },
{ catalog, Constants.SCHEMA_MAIN, "T_2", "TABLE", "" } });
trace("getTables - using a quoted _ character");
rs = meta.getTables(null, null, "T\\_2", null);
testResultSetOrdered(rs, new String[][] { { catalog, Constants.SCHEMA_MAIN, "T_2", "TABLE", "" } });
assertResultSetOrdered(rs, new String[][] { { catalog, Constants.SCHEMA_MAIN, "T_2", "TABLE", "" } });
trace("getTables - using the % wildcard");
rs = meta.getTables(null, Constants.SCHEMA_MAIN, "%", new String[] { "TABLE" });
testResultSetOrdered(rs, new String[][] { { catalog, Constants.SCHEMA_MAIN, "TEST", "TABLE", "" },
assertResultSetOrdered(rs, new String[][] { { catalog, Constants.SCHEMA_MAIN, "TEST", "TABLE", "" },
{ catalog, Constants.SCHEMA_MAIN, "TX2", "TABLE", "" },
{ catalog, Constants.SCHEMA_MAIN, "T_2", "TABLE", "" } });
stat.execute("DROP TABLE TEST");
trace("getColumns - using wildcards");
rs = meta.getColumns(null, null, "___", "B%");
testResultSetOrdered(rs, new String[][] {
assertResultSetOrdered(rs, new String[][] {
{ catalog, Constants.SCHEMA_MAIN, "TX2", "B", "" + Types.INTEGER, "INTEGER", "10"},
{ catalog, Constants.SCHEMA_MAIN, "T_2", "B", "" + Types.INTEGER, "INTEGER", "10"}, });
trace("getColumns - using wildcards");
rs = meta.getColumns(null, null, "_\\__", "%");
testResultSetOrdered(rs, new String[][] {
assertResultSetOrdered(rs, new String[][] {
{ catalog, Constants.SCHEMA_MAIN, "T_2", "B", "" + Types.INTEGER, "INTEGER", "10"},
{ catalog, Constants.SCHEMA_MAIN, "T_2", "A", "" + Types.VARCHAR, "VARCHAR", "6"},
{ catalog, Constants.SCHEMA_MAIN, "T_2", "C", "" + Types.INTEGER, "INTEGER", "10"}, });
......@@ -690,7 +690,7 @@ public class TestMetaData extends TestBase {
stat.executeUpdate("CREATE UNIQUE INDEX A_INDEX ON TX2(B,C,A)");
stat.executeUpdate("CREATE INDEX B_INDEX ON TX2(A,B,C)");
rs = meta.getIndexInfo(null, null, "TX2", false, false);
testResultSetOrdered(rs, new String[][] {
assertResultSetOrdered(rs, new String[][] {
{ catalog, Constants.SCHEMA_MAIN, "TX2", "FALSE", catalog, "A_INDEX",
"" + DatabaseMetaData.tableIndexOther, "1", "B", "A" },
{ catalog, Constants.SCHEMA_MAIN, "TX2", "FALSE", catalog, "A_INDEX",
......@@ -711,7 +711,7 @@ public class TestMetaData extends TestBase {
"" + DatabaseMetaData.tableIndexOther, "3", "C", "A" }, });
trace("getPrimaryKeys");
rs = meta.getPrimaryKeys(null, null, "T_2");
testResultSetOrdered(rs, new String[][] {
assertResultSetOrdered(rs, new String[][] {
{ catalog, Constants.SCHEMA_MAIN, "T_2", "A", "2", "PRIMARY_KEY_1" },
{ catalog, Constants.SCHEMA_MAIN, "T_2", "B", "3", "PRIMARY_KEY_1" },
{ catalog, Constants.SCHEMA_MAIN, "T_2", "C", "1", "PRIMARY_KEY_1" }, });
......@@ -723,7 +723,7 @@ public class TestMetaData extends TestBase {
trace("getImportedKeys");
rs = meta.getImportedKeys(null, null, "CHILD");
testResultSetMeta(rs, 14, new String[] { "PKTABLE_CAT", "PKTABLE_SCHEM", "PKTABLE_NAME", "PKCOLUMN_NAME",
assertResultSetMeta(rs, 14, new String[] { "PKTABLE_CAT", "PKTABLE_SCHEM", "PKTABLE_NAME", "PKCOLUMN_NAME",
"FKTABLE_CAT", "FKTABLE_SCHEM", "FKTABLE_NAME", "FKCOLUMN_NAME", "KEY_SEQ", "UPDATE_RULE",
"DELETE_RULE", "FK_NAME", "PK_NAME", "DEFERRABILITY" }, new int[] { Types.VARCHAR, Types.VARCHAR,
Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
......@@ -739,7 +739,7 @@ public class TestMetaData extends TestBase {
trace("getExportedKeys");
rs = meta.getExportedKeys(null, null, "PARENT");
testResultSetMeta(rs, 14, new String[] { "PKTABLE_CAT", "PKTABLE_SCHEM", "PKTABLE_NAME", "PKCOLUMN_NAME",
assertResultSetMeta(rs, 14, new String[] { "PKTABLE_CAT", "PKTABLE_SCHEM", "PKTABLE_NAME", "PKCOLUMN_NAME",
"FKTABLE_CAT", "FKTABLE_SCHEM", "FKTABLE_NAME", "FKCOLUMN_NAME", "KEY_SEQ", "UPDATE_RULE",
"DELETE_RULE", "FK_NAME", "PK_NAME", "DEFERRABILITY" }, new int[] { Types.VARCHAR, Types.VARCHAR,
Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
......@@ -755,7 +755,7 @@ public class TestMetaData extends TestBase {
*/
trace("getCrossReference");
rs = meta.getCrossReference(null, null, "PARENT", null, null, "CHILD");
testResultSetMeta(rs, 14, new String[] { "PKTABLE_CAT", "PKTABLE_SCHEM", "PKTABLE_NAME", "PKCOLUMN_NAME",
assertResultSetMeta(rs, 14, new String[] { "PKTABLE_CAT", "PKTABLE_SCHEM", "PKTABLE_NAME", "PKCOLUMN_NAME",
"FKTABLE_CAT", "FKTABLE_SCHEM", "FKTABLE_NAME", "FKCOLUMN_NAME", "KEY_SEQ", "UPDATE_RULE",
"DELETE_RULE", "FK_NAME", "PK_NAME", "DEFERRABILITY" }, new int[] { Types.VARCHAR, Types.VARCHAR,
Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
......@@ -771,7 +771,7 @@ public class TestMetaData extends TestBase {
*/
rs = meta.getSchemas();
testResultSetMeta(rs, 3, new String[] { "TABLE_SCHEM", "TABLE_CATALOG", "IS_DEFAULT" }, new int[] {
assertResultSetMeta(rs, 3, new String[] { "TABLE_SCHEM", "TABLE_CATALOG", "IS_DEFAULT" }, new int[] {
Types.VARCHAR, Types.VARCHAR, DataType.TYPE_BOOLEAN }, null, null);
assertTrue(rs.next());
assertEquals(rs.getString(1), "INFORMATION_SCHEMA");
......@@ -780,15 +780,15 @@ public class TestMetaData extends TestBase {
assertFalse(rs.next());
rs = meta.getCatalogs();
testResultSetMeta(rs, 1, new String[] { "TABLE_CAT" }, new int[] { Types.VARCHAR }, null, null);
testResultSetOrdered(rs, new String[][] { { catalog } });
assertResultSetMeta(rs, 1, new String[] { "TABLE_CAT" }, new int[] { Types.VARCHAR }, null, null);
assertResultSetOrdered(rs, new String[][] { { catalog } });
rs = meta.getTableTypes();
testResultSetMeta(rs, 1, new String[] { "TABLE_TYPE" }, new int[] { Types.VARCHAR }, null, null);
testResultSetOrdered(rs, new String[][] { { "SYSTEM TABLE" }, { "TABLE" }, { "TABLE LINK" }, { "VIEW" } });
assertResultSetMeta(rs, 1, new String[] { "TABLE_TYPE" }, new int[] { Types.VARCHAR }, null, null);
assertResultSetOrdered(rs, new String[][] { { "SYSTEM TABLE" }, { "TABLE" }, { "TABLE LINK" }, { "VIEW" } });
rs = meta.getTypeInfo();
testResultSetMeta(rs, 18, new String[] { "TYPE_NAME", "DATA_TYPE", "PRECISION", "LITERAL_PREFIX",
assertResultSetMeta(rs, 18, new String[] { "TYPE_NAME", "DATA_TYPE", "PRECISION", "LITERAL_PREFIX",
"LITERAL_SUFFIX", "CREATE_PARAMS", "NULLABLE", "CASE_SENSITIVE", "SEARCHABLE", "UNSIGNED_ATTRIBUTE",
"FIXED_PREC_SCALE", "AUTO_INCREMENT", "LOCAL_TYPE_NAME", "MINIMUM_SCALE", "MAXIMUM_SCALE",
"SQL_DATA_TYPE", "SQL_DATETIME_SUB", "NUM_PREC_RADIX" }, new int[] { Types.VARCHAR, Types.SMALLINT,
......@@ -797,12 +797,12 @@ public class TestMetaData extends TestBase {
Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.INTEGER, Types.INTEGER }, null, null);
rs = meta.getTablePrivileges(null, null, null);
testResultSetMeta(rs, 7, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "GRANTOR", "GRANTEE",
assertResultSetMeta(rs, 7, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "GRANTOR", "GRANTEE",
"PRIVILEGE", "IS_GRANTABLE" }, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR }, null, null);
rs = meta.getColumnPrivileges(null, null, "TEST", null);
testResultSetMeta(rs, 8, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "COLUMN_NAME", "GRANTOR",
assertResultSetMeta(rs, 8, new String[] { "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "COLUMN_NAME", "GRANTOR",
"GRANTEE", "PRIVILEGE", "IS_GRANTABLE" }, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR }, null, null);
......
......@@ -621,7 +621,7 @@ public class TestPreparedStatement extends TestBase {
assertTrue(stat.execute("SELECT * FROM T_INT ORDER BY ID"));
rs = stat.getResultSet();
testResultSetOrdered(rs, new String[][] { { "1", "0" }, { "2", "-1" }, { "3", "3" }, { "4", null },
assertResultSetOrdered(rs, new String[][] { { "1", "0" }, { "2", "-1" }, { "3", "3" }, { "4", null },
{ "5", "0" }, { "6", "-1" }, { "7", "3" }, { "8", null }, { "9", "-4" }, { "10", "5" }, { "11", null },
{ "12", "1" }, { "13", "0" }, { "14", "-20" }, { "15", "100" }, { "16", "30000" }, { "17", "-30000" },
{ "18", "" + Integer.MAX_VALUE }, { "19", "" + Integer.MIN_VALUE }, });
......
......@@ -283,7 +283,7 @@ public class TestResultSet extends TestBase {
assertEquals(meta.getColumnClassName(3), null);
assertTrue(rs.getRow() == 0);
testResultSetMeta(rs, 3, new String[] { "ID", "VALUE", "N" }, new int[] { Types.INTEGER, Types.INTEGER,
assertResultSetMeta(rs, 3, new String[] { "ID", "VALUE", "N" }, new int[] { Types.INTEGER, Types.INTEGER,
Types.NULL }, new int[] { 10, 10, 1 }, new int[] { 0, 0, 0 });
rs.next();
assertEquals(rs.getConcurrency(), ResultSet.CONCUR_READ_ONLY);
......@@ -408,7 +408,7 @@ public class TestResultSet extends TestBase {
stat.execute("INSERT INTO TEST VALUES(10,'\\''')");
stat.execute("INSERT INTO TEST VALUES(11,'\\%')");
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID");
testResultSetMeta(rs, 2, new String[] { "ID", "VALUE" }, new int[] { Types.INTEGER, Types.VARCHAR }, new int[] {
assertResultSetMeta(rs, 2, new String[] { "ID", "VALUE" }, new int[] { Types.INTEGER, Types.VARCHAR }, new int[] {
10, 255 }, new int[] { 0, 0 });
String value;
rs.next();
......@@ -488,7 +488,7 @@ public class TestResultSet extends TestBase {
stat.execute("INSERT INTO TEST VALUES(7,-99999998.99)");
stat.execute("INSERT INTO TEST VALUES(8,NULL)");
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID");
testResultSetMeta(rs, 2, new String[] { "ID", "VALUE" }, new int[] { Types.INTEGER, Types.DECIMAL }, new int[] {
assertResultSetMeta(rs, 2, new String[] { "ID", "VALUE" }, new int[] { Types.INTEGER, Types.DECIMAL }, new int[] {
10, 10 }, new int[] { 0, 2 });
BigDecimal bd;
rs.next();
......@@ -539,7 +539,7 @@ public class TestResultSet extends TestBase {
stat.execute("INSERT INTO TEST VALUES(7, -99999999.99, -99999999.99)");
stat.execute("INSERT INTO TEST VALUES(8, NULL, NULL)");
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID");
testResultSetMeta(rs, 3, new String[] { "ID", "D", "R" },
assertResultSetMeta(rs, 3, new String[] { "ID", "D", "R" },
new int[] { Types.INTEGER, Types.DOUBLE, Types.REAL }, new int[] { 10, 17, 7 }, new int[] { 0, 0, 0 });
BigDecimal bd;
rs.next();
......@@ -603,10 +603,10 @@ public class TestResultSet extends TestBase {
stat.execute("INSERT INTO TEST VALUES(4,TIMESTAMP '9999-12-31 23:59:59')");
stat.execute("INSERT INTO TEST VALUES(5,NULL)");
rs = stat.executeQuery("SELECT 0 ID, TIMESTAMP '9999-12-31 23:59:59' VALUE FROM TEST ORDER BY ID");
testResultSetMeta(rs, 2, new String[] { "ID", "VALUE" }, new int[] { Types.INTEGER, Types.TIMESTAMP },
assertResultSetMeta(rs, 2, new String[] { "ID", "VALUE" }, new int[] { Types.INTEGER, Types.TIMESTAMP },
new int[] { 10, 23 }, new int[] { 0, 10 });
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID");
testResultSetMeta(rs, 2, new String[] { "ID", "VALUE" }, new int[] { Types.INTEGER, Types.TIMESTAMP },
assertResultSetMeta(rs, 2, new String[] { "ID", "VALUE" }, new int[] { Types.INTEGER, Types.TIMESTAMP },
new int[] { 10, 23 }, new int[] { 0, 10 });
rs.next();
java.sql.Date date;
......@@ -723,7 +723,7 @@ public class TestResultSet extends TestBase {
prep.execute();
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID");
testResultSetMeta(rs, 4, new String[] { "ID", "D", "T", "TS" }, new int[] { Types.INTEGER, Types.DATE,
assertResultSetMeta(rs, 4, new String[] { "ID", "D", "T", "TS" }, new int[] { Types.INTEGER, Types.DATE,
Types.TIME, Types.TIMESTAMP }, new int[] { 10, 8, 6, 23 }, new int[] { 0, 0, 0, 10 });
rs.next();
......@@ -777,7 +777,7 @@ public class TestResultSet extends TestBase {
stat.execute("INSERT INTO TEST VALUES(5,X'0bcec1')");
stat.execute("INSERT INTO TEST VALUES(6,NULL)");
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID");
testResultSetMeta(rs, 2, new String[] { "ID", "VALUE" }, new int[] { Types.INTEGER, Types.BLOB }, new int[] {
assertResultSetMeta(rs, 2, new String[] { "ID", "VALUE" }, new int[] { Types.INTEGER, Types.BLOB }, new int[] {
10, Integer.MAX_VALUE }, new int[] { 0, 0 });
rs.next();
checkBytes(rs.getBytes(2), new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01 });
......@@ -816,7 +816,7 @@ public class TestResultSet extends TestBase {
stat.execute("INSERT INTO TEST VALUES(6,NULL)");
stat.execute("INSERT INTO TEST VALUES(7,NULL)");
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID");
testResultSetMeta(rs, 2, new String[] { "ID", "VALUE" }, new int[] { Types.INTEGER, Types.CLOB }, new int[] {
assertResultSetMeta(rs, 2, new String[] { "ID", "VALUE" }, new int[] { Types.INTEGER, Types.CLOB }, new int[] {
10, Integer.MAX_VALUE }, new int[] { 0, 0 });
rs.next();
string = rs.getString(2);
......
......@@ -105,6 +105,7 @@ public class TestWeb extends TestBase {
result = client.get(url, "logout.do");
result = client.get(url, "settingRemove.do?name=_test_");
server.stop();
}
......
......@@ -30,14 +30,67 @@ import org.h2.util.IOUtils;
*/
public abstract class TestHalt extends TestBase {
protected static final int OP_INSERT = 1, OP_DELETE = 2, OP_UPDATE = 4, OP_SELECT = 8;
protected static final int FLAG_NO_DELAY = 1, FLAG_LOBS = 2;
/**
* This bit flag means insert operations should be performed.
*/
protected static final int OP_INSERT = 1;
/**
* This bit flag means delete operations should be performed.
*/
protected static final int OP_DELETE = 2;
/**
* This bit flag means update operations should be performed.
*/
protected static final int OP_UPDATE = 4;
/**
* This bit flag means select operations should be performed.
*/
protected static final int OP_SELECT = 8;
/**
* This bit flag means operations should be written to the log file immediately.
*/
protected static final int FLAG_NO_DELAY = 1;
/**
* This bit flag means the test should use LOB values.
*/
protected static final int FLAG_LOBS = 2;
/**
* The test directory.
*/
static final String DIR = TestBase.getTestDir("halt");
private static final String DATABASE_NAME = "halt";
private static final String TRACE_FILE_NAME = "haltTrace.trace.db";
protected int operations, flags, value;
/**
* The current operations bit mask.
*/
protected int operations;
/**
* The current flags bit mask.
*/
protected int flags;
/**
* The current test value, for example the number of rows.
*/
protected int value;
/**
* The database connection.
*/
protected Connection conn;
/**
* The pseudo random number generator used for this test.
*/
protected Random random = new Random();
private SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm:ss ");
......@@ -73,6 +126,11 @@ public abstract class TestHalt extends TestBase {
return DriverManager.getConnection("jdbc:h2:" + baseDir + "/halt", "sa", "sa");
}
/**
* Start the program.
*
* @param args the command line arguments
*/
protected void start(String[] args) throws Exception {
if (args.length == 0) {
runTest();
......@@ -111,10 +169,21 @@ public abstract class TestHalt extends TestBase {
}
}
/**
* Print a trace message to the trace file.
*
* @param s the message
*/
protected void traceOperation(String s) {
trace(s, null);
}
/**
* Print a trace message to the trace file.
*
* @param s the message
* @param e the exception or null
*/
protected void trace(String s, Exception e) {
FileWriter writer = null;
try {
......@@ -191,6 +260,9 @@ public abstract class TestHalt extends TestBase {
}
}
/**
* Close the database connection normally.
*/
protected void disconnect() {
try {
traceOperation("disconnect");
......@@ -248,6 +320,12 @@ public abstract class TestHalt extends TestBase {
}
}
/**
* Create a random string with the specified length.
*
* @param len the number of characters
* @return the random string
*/
protected String getRandomString(int len) {
StringBuffer buff = new StringBuffer();
for (int i = 0; i < len; i++) {
......
......@@ -37,6 +37,9 @@ public class TestHaltApp extends TestHalt {
stat.execute(sql);
}
/**
* Initialize the database.
*/
protected void testInit() throws SQLException {
Statement stat = conn.createStatement();
// stat.execute("CREATE TABLE TEST(ID IDENTITY, NAME VARCHAR(255))");
......@@ -51,6 +54,9 @@ public class TestHaltApp extends TestHalt {
execute(stat, "CREATE TABLE TEST(ID BIGINT GENERATED BY DEFAULT AS IDENTITY, NAME VARCHAR(255), DATA CLOB)");
}
/**
* Wait after the application has been started.
*/
protected void testWaitAfterAppStart() throws Exception {
int sleep = 10 + random.nextInt(300);
if ((flags & FLAG_NO_DELAY) == 0) {
......@@ -59,6 +65,12 @@ public class TestHaltApp extends TestHalt {
Thread.sleep(sleep);
}
/**
* This method is called after a simulated crash. The method should check if
* the data is transactionally consistent and throw an exception if not.
*
* @throws Exception if the data is not consistent.
*/
protected void testCheckAfterCrash() throws Exception {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST");
......@@ -71,6 +83,9 @@ public class TestHaltApp extends TestHalt {
}
}
/**
* Initialize the application.
*/
protected void appStart() throws SQLException {
Statement stat = conn.createStatement();
if ((flags & FLAG_NO_DELAY) != 0) {
......@@ -83,6 +98,9 @@ public class TestHaltApp extends TestHalt {
trace("rows: " + rowCount, null);
}
/**
* Run the application code.
*/
protected void appRun() throws Exception {
conn.setAutoCommit(false);
traceOperation("setAutoCommit false");
......
......@@ -17,6 +17,9 @@ import org.h2.test.TestBase;
*/
public class TestMulti extends TestBase {
/**
* If set, the test should stop.
*/
public volatile boolean stop;
public void test() throws Exception {
......
......@@ -521,4 +521,5 @@ philosophers technologies modeling federation enterprise semantic deductive
fusion legacy decoded commented trimmed reaches indicating marks scaled tells
monitor benefit performing conditional significant arithmetic instrumented
doclets extremes instructions printable skips sava sources cms bytecode cfml
cold compiles markup spellchecker interleaved poormans programmed swt railo
\ No newline at end of file
cold compiles markup spellchecker interleaved poormans programmed swt railo
clobs resizes precisions scales
\ No newline at end of file
......@@ -334,7 +334,7 @@ public class Doclet {
}
private static boolean skipField(ClassDoc clazz, FieldDoc field) {
if (!field.isFinal() || !field.isStatic() || !field.isPublic() || field.containingClass() != clazz) {
if (field.isPrivate() || field.containingClass() != clazz) {
return true;
}
return false;
......@@ -347,7 +347,7 @@ public class Doclet {
return true;
}
String name = method.name();
if (!method.isPublic() || name.equals("finalize")) {
if ((method.isPrivate() && !method.isPackagePrivate()) || name.equals("finalize")) {
return true;
}
if (method.getRawCommentText().trim().startsWith("@deprecated INTERNAL")) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论