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

javadocs

上级 cf963b69
...@@ -17,7 +17,7 @@ Change Log ...@@ -17,7 +17,7 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <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. </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 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. index. This flag is enabled for Derby, Oracle, MSSQLServer, and HSQLDB.
......
...@@ -311,6 +311,20 @@ http://groups.google.com/group/h2-database/web/roadmap ...@@ -311,6 +311,20 @@ http://groups.google.com/group/h2-database/web/roadmap
<td class="compareY">Yes</td> <td class="compareY">Yes</td>
<td class="compareY">Yes</td> <td class="compareY">Yes</td>
</tr><tr> </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>Custom Aggregate Functions</td>
<td class="compareY">Yes</td> <td class="compareY">Yes</td>
<td class="compareN">No</td> <td class="compareN">No</td>
...@@ -331,7 +345,8 @@ http://groups.google.com/group/h2-database/web/roadmap ...@@ -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 /> *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 /> *3 Derby support for roles based security and password checking as an option.<br />
*4 Derby only supports global temporary tables.<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> </p>
<h3>Derby and HSQLDB</h3> <h3>Derby and HSQLDB</h3>
...@@ -973,38 +988,81 @@ IGNORECASE=TRUE to the database URL (example: jdbc:h2:~/test;IGNORECASE=TRUE). ...@@ -973,38 +988,81 @@ IGNORECASE=TRUE to the database URL (example: jdbc:h2:~/test;IGNORECASE=TRUE).
<h3>Compatibility Modes</h3> <h3>Compatibility Modes</h3>
<p> <p>
For certain features, this database can emulate the behavior of specific databases. Not all features or differences of those For certain features, this database can emulate the behavior of specific databases.
databases are implemented. Currently, this feature is mainly used for randomized comparative testing Not all features or differences of those databases are implemented.
(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>.
Here is the list of currently supported modes and the difference to the regular mode: Here is the list of currently supported modes and the difference to the regular mode:
</p> </p>
<table>
<tr><th>Mode</th><th>Differences</th></tr> <h3>PostgreSQL Compatibility Mode</h3>
<tr><td> <p>
PostgreSQL To use the PostgreSQL mode, use the database URL <code>jdbc:h2:~/test;MODE=PostgreSQL</code>
</td><td> or the SQL statement <code>SET MODE PostgreSQL</code>.
Concatenation of a NULL with another value results in NULL. </p>
Usually, the NULL is treated as an empty string if only one of the operators is NULL, <ul><li>Concatenation of a NULL with another value results in NULL. Usually, the NULL is treated as an empty
and NULL is only returned if both values are NULL. string if only one of the operators is NULL, and NULL is only returned if both values are NULL.
</td></tr> </li><li>When converting a floating point number to a integer, the fractional
<tr><td> digits should not be truncated, but the value should be rounded.
MySQL </li><li>The system columns 'CTID' and 'OID' should be supported.
</td><td> </li></ul>
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. <h3>MySQL Compatibility Mode</h3>
Usually, this operation is not allowed and an exception is thrown. <p>
</td></tr> To use the MySQL mode, use the database URL <code>jdbc:h2:~/test;MODE=MySQL</code>
<tr><td> or the SQL statement <code>SET MODE MySQL</code>.
HSQLDB </p>
</td><td> <ul><li>When inserting data, if a column is defined to be NOT NULL and NULL is inserted,
When converting the scale of decimal data, the number is only converted if the new scale is then a 0 (or empty string, or the current timestamp for timestamp columns) value is used.
smaller then current scale. Usually, this operation is not allowed and an exception is thrown.
Usually, the scale is converted and 0s are added if required. </li><li>When converting a floating point number to a integer, the fractional
</td></tr> digits should not be truncated, but the value should be rounded.
</table> </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> <br /><a name="trace_options"></a>
<h2>Using the Trace Options</h2> <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; ...@@ -32,7 +32,6 @@ import org.h2.util.StringUtils;
public class Bnf { public class Bnf {
private static final String SEPARATORS = " [](){}|.,\r\n<>:-+*/=<\">!'$"; private static final String SEPARATORS = " [](){}|.,\r\n<>:-+*/=<\">!'$";
private static final long MAX_PARSE_TIME = 100;
private final Random random = new Random(); private final Random random = new Random();
private final HashMap ruleMap = new HashMap(); private final HashMap ruleMap = new HashMap();
...@@ -286,20 +285,17 @@ public class Bnf { ...@@ -286,20 +285,17 @@ public class Bnf {
* @return the map of possible token types / tokens * @return the map of possible token types / tokens
*/ */
public HashMap getNextTokenList(String query) { public HashMap getNextTokenList(String query) {
HashMap next = new HashMap();
Sentence sentence = new Sentence(); Sentence sentence = new Sentence();
sentence.next = next; sentence.setQuery(query);
sentence.text = query;
for (int i = 0; i < statements.size(); i++) { for (int i = 0; i < statements.size(); i++) {
RuleHead head = (RuleHead) statements.get(i); RuleHead head = (RuleHead) statements.get(i);
if (!head.section.startsWith("Commands")) { if (!head.section.startsWith("Commands")) {
continue; continue;
} }
sentence.max = System.currentTimeMillis() + MAX_PARSE_TIME; sentence.start();
sentence.setQuery(query);
head.getRule().addNextTokenList(sentence); head.getRule().addNextTokenList(sentence);
} }
return next; return sentence.getNext();
} }
/** /**
......
...@@ -78,15 +78,15 @@ public class RuleElement implements Rule { ...@@ -78,15 +78,15 @@ public class RuleElement implements Rule {
} }
public boolean matchRemove(Sentence sentence) { public boolean matchRemove(Sentence sentence) {
if (sentence.stop()) { if (sentence.shouldStop()) {
return false; return false;
} }
String query = sentence.query; String query = sentence.getQuery();
if (query.length() == 0) { if (query.length() == 0) {
return false; return false;
} }
if (keyword) { if (keyword) {
String up = sentence.queryUpper; String up = sentence.getQueryUpper();
if (up.startsWith(name)) { if (up.startsWith(name)) {
query = query.substring(name.length()); query = query.substring(name.length());
while (!"_".equals(name) && query.length() > 0 && Character.isWhitespace(query.charAt(0))) { while (!"_".equals(name) && query.length() > 0 && Character.isWhitespace(query.charAt(0))) {
...@@ -101,7 +101,7 @@ public class RuleElement implements Rule { ...@@ -101,7 +101,7 @@ public class RuleElement implements Rule {
return false; return false;
} }
if (name != null && !name.startsWith("@") && (link.name() == null || !link.name().startsWith("@"))) { 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))) { while (query.length() > 0 && Character.isWhitespace(query.charAt(0))) {
query = query.substring(1); query = query.substring(1);
} }
...@@ -111,13 +111,13 @@ public class RuleElement implements Rule { ...@@ -111,13 +111,13 @@ public class RuleElement implements Rule {
} }
public void addNextTokenList(Sentence sentence) { public void addNextTokenList(Sentence sentence) {
if (sentence.stop()) { if (sentence.shouldStop()) {
return; return;
} }
if (keyword) { if (keyword) {
String query = sentence.query; String query = sentence.getQuery();
String q = query.trim(); String q = query.trim();
String up = sentence.queryUpper.trim(); String up = sentence.getQueryUpper().trim();
if (q.length() == 0 || name.startsWith(up)) { if (q.length() == 0 || name.startsWith(up)) {
if (q.length() < name.length()) { if (q.length() < name.length()) {
sentence.add(name, name.substring(q.length()), type); sentence.add(name, name.substring(q.length()), type);
......
...@@ -109,10 +109,10 @@ public class RuleFixed implements Rule { ...@@ -109,10 +109,10 @@ public class RuleFixed implements Rule {
} }
public boolean matchRemove(Sentence sentence) { public boolean matchRemove(Sentence sentence) {
if (sentence.stop()) { if (sentence.shouldStop()) {
return false; return false;
} }
String query = sentence.query; String query = sentence.getQuery();
if (query.length() == 0) { if (query.length() == 0) {
return false; return false;
} }
...@@ -223,10 +223,10 @@ public class RuleFixed implements Rule { ...@@ -223,10 +223,10 @@ public class RuleFixed implements Rule {
} }
public void addNextTokenList(Sentence sentence) { public void addNextTokenList(Sentence sentence) {
if (sentence.stop()) { if (sentence.shouldStop()) {
return; return;
} }
String query = sentence.query; String query = sentence.getQuery();
switch(type) { switch(type) {
case YMD: case YMD:
if (query.length() == 0) { if (query.length() == 0) {
......
...@@ -96,7 +96,7 @@ public class RuleList implements Rule { ...@@ -96,7 +96,7 @@ public class RuleList implements Rule {
} }
public boolean matchRemove(Sentence sentence) { public boolean matchRemove(Sentence sentence) {
String query = sentence.query; String query = sentence.getQuery();
if (query.length() == 0) { if (query.length() == 0) {
return false; return false;
} }
...@@ -118,7 +118,7 @@ public class RuleList implements Rule { ...@@ -118,7 +118,7 @@ public class RuleList implements Rule {
} }
public void addNextTokenList(Sentence sentence) { public void addNextTokenList(Sentence sentence) {
String old = sentence.query; String old = sentence.getQuery();
if (or) { if (or) {
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
sentence.setQuery(old); sentence.setQuery(old);
......
...@@ -46,10 +46,10 @@ public class RuleOptional implements Rule { ...@@ -46,10 +46,10 @@ public class RuleOptional implements Rule {
} }
public boolean matchRemove(Sentence sentence) { public boolean matchRemove(Sentence sentence) {
if (sentence.stop()) { if (sentence.shouldStop()) {
return false; return false;
} }
String query = sentence.query; String query = sentence.getQuery();
if (query.length() == 0) { if (query.length() == 0) {
return true; return true;
} }
...@@ -60,7 +60,7 @@ public class RuleOptional implements Rule { ...@@ -60,7 +60,7 @@ public class RuleOptional implements Rule {
} }
public void addNextTokenList(Sentence sentence) { public void addNextTokenList(Sentence sentence) {
if (sentence.stop()) { if (sentence.shouldStop()) {
return; return;
} }
rule.addNextTokenList(sentence); rule.addNextTokenList(sentence);
......
...@@ -40,10 +40,10 @@ public class RuleRepeat implements Rule { ...@@ -40,10 +40,10 @@ public class RuleRepeat implements Rule {
} }
public boolean matchRemove(Sentence sentence) { public boolean matchRemove(Sentence sentence) {
if (sentence.stop()) { if (sentence.shouldStop()) {
return false; return false;
} }
String query = sentence.query; String query = sentence.getQuery();
if (query.length() == 0) { if (query.length() == 0) {
return false; return false;
} }
...@@ -51,20 +51,20 @@ public class RuleRepeat implements Rule { ...@@ -51,20 +51,20 @@ public class RuleRepeat implements Rule {
if (!rule.matchRemove(sentence)) { if (!rule.matchRemove(sentence)) {
return true; return true;
} }
if (sentence.query.length() == 0) { if (sentence.getQuery().length() == 0) {
return true; return true;
} }
} }
} }
public void addNextTokenList(Sentence sentence) { public void addNextTokenList(Sentence sentence) {
if (sentence.stop()) { if (sentence.shouldStop()) {
return; return;
} }
String old = sentence.query; String old = sentence.getQuery();
while (true) { while (true) {
rule.addNextTokenList(sentence); rule.addNextTokenList(sentence);
if (!rule.matchRemove(sentence) || old == sentence.query) { if (!rule.matchRemove(sentence) || old == sentence.getQuery()) {
break; break;
} }
} }
......
...@@ -20,25 +20,60 @@ import org.h2.util.StringUtils; ...@@ -20,25 +20,60 @@ import org.h2.util.StringUtils;
public class Sentence { 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. * For example the item represents a table name of the current database.
*/ */
public static final int CONTEXT = 0; public static final int CONTEXT = 0;
/**
* The token type for a keyword.
*/
static final int KEYWORD = 1; static final int KEYWORD = 1;
/**
* The token type for a function name.
*/
static final int FUNCTION = 2; static final int FUNCTION = 2;
public String text;
public String query; private static final long MAX_PROCESSING_TIME = 100;
public String queryUpper;
HashMap next; /**
long max; * 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 DbSchema lastMatchedSchema;
private DbTableOrView lastMatchedTable; private DbTableOrView lastMatchedTable;
private DbTableOrView lastTable; private DbTableOrView lastTable;
private HashSet tables; private HashSet tables;
private HashMap aliases; 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 { ...@@ -46,7 +81,7 @@ public class Sentence {
* *
* @param n the token name * @param n the token name
* @param string an example text * @param string an example text
* @param type the type * @param type the token type
*/ */
public void add(String n, String string, int type) { public void add(String n, String string, int type) {
next.put(type+"#"+n, string); next.put(type+"#"+n, string);
...@@ -142,6 +177,11 @@ public class Sentence { ...@@ -142,6 +177,11 @@ public class Sentence {
return lastMatchedTable; return lastMatchedTable;
} }
/**
* Set the query string.
*
* @param query the query string
*/
public void setQuery(String query) { public void setQuery(String query) {
if (this.query != query) { if (this.query != query) {
this.query = query; this.query = query;
...@@ -149,4 +189,31 @@ public class Sentence { ...@@ -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 { ...@@ -148,7 +148,7 @@ public abstract class Command implements CommandInterface {
} }
} }
protected void start() { void start() {
startTime = System.currentTimeMillis(); startTime = System.currentTimeMillis();
} }
......
...@@ -143,6 +143,11 @@ public abstract class Prepared { ...@@ -143,6 +143,11 @@ public abstract class Prepared {
return parameters; return parameters;
} }
/**
* Check if all parameters have been set.
*
* @throws SQLException if any parameter has not been set
*/
protected void checkParameters() throws SQLException { protected void checkParameters() throws SQLException {
for (int i = 0; parameters != null && i < parameters.size(); i++) { for (int i = 0; parameters != null && i < parameters.size(); i++) {
Parameter param = (Parameter) parameters.get(i); Parameter param = (Parameter) parameters.get(i);
...@@ -214,10 +219,25 @@ public abstract class Prepared { ...@@ -214,10 +219,25 @@ public abstract class Prepared {
return sqlStatement; 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() { protected int getCurrentObjectId() {
return objectId; 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) { protected int getObjectId(boolean needFresh, boolean dataFile) {
Database db = session.getDatabase(); Database db = session.getDatabase();
int id = objectId; int id = objectId;
......
...@@ -242,7 +242,7 @@ public abstract class Query extends Prepared { ...@@ -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 { boolean mustBeInResult) throws SQLException {
for (int i = 0; i < orderList.size(); i++) { for (int i = 0; i < orderList.size(); i++) {
SelectOrderBy o = (SelectOrderBy) orderList.get(i); SelectOrderBy o = (SelectOrderBy) orderList.get(i);
......
...@@ -66,7 +66,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler { ...@@ -66,7 +66,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
cipher = c; cipher = c;
} }
protected boolean isEncrypted() { private boolean isEncrypted() {
return cipher != null; return cipher != null;
} }
...@@ -94,7 +94,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler { ...@@ -94,7 +94,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
return false; return false;
} }
protected void deleteStore() throws SQLException { void deleteStore() throws SQLException {
String fileName = getFileName(); String fileName = getFileName();
if (fileName != null) { if (fileName != null) {
FileUtils.delete(fileName); FileUtils.delete(fileName);
...@@ -111,7 +111,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler { ...@@ -111,7 +111,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
store.init(); store.init();
} }
protected void openOutput() throws SQLException { void openOutput() throws SQLException {
String fileName = getFileName(); String fileName = getFileName();
if (fileName == null) { if (fileName == null) {
return; return;
...@@ -128,7 +128,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler { ...@@ -128,7 +128,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
} }
} }
protected void openInput() throws SQLException { void openInput() throws SQLException {
String fileName = getFileName(); String fileName = getFileName();
if (fileName == null) { if (fileName == null) {
return; return;
...@@ -151,7 +151,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler { ...@@ -151,7 +151,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
} }
} }
protected void closeIO() { void closeIO() {
IOUtils.closeSilently(out); IOUtils.closeSilently(out);
out = null; out = null;
IOUtils.closeSilently(in); IOUtils.closeSilently(in);
......
...@@ -9,12 +9,34 @@ package org.h2.command.dml; ...@@ -9,12 +9,34 @@ package org.h2.command.dml;
import org.h2.expression.Expression; 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 { public class SelectOrderBy {
/**
* The order by expression.
*/
public Expression 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; public Expression columnIndexExpr;
/**
* If the column should be sorted descending.
*/
public boolean descending; public boolean descending;
/**
* If NULL should be appear first.
*/
public boolean nullsFirst; public boolean nullsFirst;
/**
* If NULL should be appear at the end.
*/
public boolean nullsLast; public boolean nullsLast;
} }
...@@ -54,9 +54,8 @@ public class ConstraintReferential extends Constraint { ...@@ -54,9 +54,8 @@ public class ConstraintReferential extends Constraint {
*/ */
public static final int SET_NULL = 3; public static final int SET_NULL = 3;
protected IndexColumn[] columns; private IndexColumn[] columns;
protected IndexColumn[] refColumns; private IndexColumn[] refColumns;
private int deleteAction; private int deleteAction;
private int updateAction; private int updateAction;
private Table refTable; private Table refTable;
......
...@@ -39,6 +39,14 @@ public abstract class DbObjectBase implements DbObject { ...@@ -39,6 +39,14 @@ public abstract class DbObjectBase implements DbObject {
private long modificationId; private long modificationId;
private boolean temporary; 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) { protected void initDbObjectBase(Database database, int id, String name, String traceModule) {
this.database = database; this.database = database;
this.trace = database.getTrace(traceModule); this.trace = database.getTrace(traceModule);
...@@ -125,6 +133,10 @@ public abstract class DbObjectBase implements DbObject { ...@@ -125,6 +133,10 @@ public abstract class DbObjectBase implements DbObject {
return objectName; return objectName;
} }
/**
* Set the main attributes to null to make sure the object is no longer
* used.
*/
protected void invalidate() { protected void invalidate() {
setModified(); setModified();
id = -1; id = -1;
......
...@@ -23,13 +23,54 @@ public class Mode { ...@@ -23,13 +23,54 @@ public class Mode {
private static final HashMap MODES = new HashMap(); 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; 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; 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; 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; public boolean roundWhenConvertToLong;
/**
* The identifiers should be returned in lower case.
*/
public boolean lowerCaseIdentifiers; public boolean lowerCaseIdentifiers;
/**
* Creating indexes in the CREATE TABLE statement should be supported.
*/
public boolean indexDefinitionInCreateTable; public boolean indexDefinitionInCreateTable;
/**
* The system columns 'CTID' and 'OID' should be supported.
*/
public boolean systemColumns; public boolean systemColumns;
/**
* Identifiers may be quoted using square brackets as in [Test].
*/
public boolean squareBracketQuotedNames; public boolean squareBracketQuotedNames;
/** /**
......
...@@ -59,7 +59,7 @@ public abstract class RightOwner extends DbObjectBase { ...@@ -59,7 +59,7 @@ public abstract class RightOwner extends DbObjectBase {
return false; return false;
} }
protected boolean isRightGrantedRecursive(Table table, int rightMask) { boolean isRightGrantedRecursive(Table table, int rightMask) {
Right right; Right right;
if (grantedRights != null) { if (grantedRights != null) {
right = (Right) grantedRights.get(table); right = (Right) grantedRights.get(table);
......
...@@ -1450,6 +1450,12 @@ public class Function extends Expression implements FunctionCall { ...@@ -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 { protected void checkParameterCount(int len) throws SQLException {
int min = 0, max = Integer.MAX_VALUE; int min = 0, max = Integer.MAX_VALUE;
switch (info.type) { switch (info.type) {
......
...@@ -633,7 +633,14 @@ public class FullText implements Trigger { ...@@ -633,7 +633,14 @@ public class FullText implements Trigger {
return search(conn, text, limit, offset, false); 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(); SimpleResultSet result = new SimpleResultSet();
if (data) { if (data) {
result.addColumn(FullText.FIELD_SCHEMA, Types.VARCHAR, 0, 0); result.addColumn(FullText.FIELD_SCHEMA, Types.VARCHAR, 0, 0);
...@@ -722,7 +729,14 @@ public class FullText implements Trigger { ...@@ -722,7 +729,14 @@ public class FullText implements Trigger {
return result; 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 columns = new ArrayList();
ArrayList data = new ArrayList(); ArrayList data = new ArrayList();
JdbcConnection c = (JdbcConnection) conn; JdbcConnection c = (JdbcConnection) conn;
......
...@@ -22,17 +22,28 @@ import org.h2.value.Value; ...@@ -22,17 +22,28 @@ import org.h2.value.Value;
* An abstract b-tree page. * An abstract b-tree page.
*/ */
public abstract class BtreePage extends Record { 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; protected static final int BLOCKS_PER_PAGE = 1024 / DiskFile.BLOCK_SIZE;
/**
* The b-tree index object
*/
protected BtreeIndex index; 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; protected ObjectArray pageData;
/**
* If this is the root page of the index.
*/
protected boolean root; protected boolean root;
BtreePage(BtreeIndex index) { BtreePage(BtreeIndex index) {
......
...@@ -13,12 +13,38 @@ import org.h2.store.Storage; ...@@ -13,12 +13,38 @@ import org.h2.store.Storage;
* Such records are only used when recovering. * Such records are only used when recovering.
*/ */
public class RedoLogRecord { public class RedoLogRecord {
/**
* The storage object to where this log record belongs to.
*/
public Storage storage; 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; public int sequenceId;
/**
* The position in the data file.
*/
public int recordId; public int recordId;
/**
* The offset in the data byte array.
*/
public int offset; public int offset;
/**
* The data.
*/
public byte[] data; public byte[] data;
/**
* Get the estimated memory size used by this object.
*
* @return the estimated memory size
*/
public int getSize() { public int getSize() {
// estimated memory size in bytes ((5 variables+myself) * 4 bytes each) // estimated memory size in bytes ((5 variables+myself) * 4 bytes each)
if (data == null) { if (data == null) {
...@@ -26,4 +52,5 @@ public class RedoLogRecord { ...@@ -26,4 +52,5 @@ public class RedoLogRecord {
} }
return 28 + data.length; return 28 + data.length;
} }
} }
...@@ -25,11 +25,87 @@ import org.h2.util.StringUtils; ...@@ -25,11 +25,87 @@ import org.h2.util.StringUtils;
* The base class for objects that can print trace information about themselves. * The base class for objects that can print trace information about themselves.
*/ */
public class TraceObject { 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, * The trace type id for callable statements.
PARAMETER_META_DATA = 11; */
protected static final int DATA_SOURCE = 12, XA_DATA_SOURCE = 13, XID = 14, ARRAY = 15; 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 LAST = ARRAY + 1;
private static final int[] ID = new int[LAST]; private static final int[] ID = new int[LAST];
private static final String[] PREFIX = { private static final String[] PREFIX = {
...@@ -41,6 +117,13 @@ public class TraceObject { ...@@ -41,6 +117,13 @@ public class TraceObject {
private int type; private int type;
private int id; 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) { protected void setTrace(Trace trace, int type, int id) {
this.trace = trace; this.trace = trace;
this.type = type; this.type = type;
...@@ -61,52 +144,116 @@ public class TraceObject { ...@@ -61,52 +144,116 @@ public class TraceObject {
return PREFIX[type] + id; 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) { protected int getNextId(int type) {
return ID[type]++; return ID[type]++;
} }
/**
* Check if the debug trace level is enabled.
*
* @return true if it is
*/
protected boolean isDebugEnabled() { protected boolean isDebugEnabled() {
return trace.isDebugEnabled(); return trace.isDebugEnabled();
} }
/**
* Check if info trace level is enabled.
*
* @return true if it is
*/
protected boolean isInfoEnabled() { protected boolean isInfoEnabled() {
return trace.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) { protected void debugCodeAssign(String className, int type, int id, String value) {
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
trace.debugCode(className + " " + PREFIX[type] + id + " = " + getTraceObjectName() + "." + value + ";"); 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()) { 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()) { 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()) { 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) { protected void debugCode(String text) {
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
trace.debugCode(getTraceObjectName() + "." + text); 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) { protected String quote(String s) {
return StringUtils.quoteJavaString(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) { protected String quoteTime(java.sql.Time x) {
if (x == null) { if (x == null) {
return "null"; return "null";
...@@ -114,6 +261,12 @@ public class TraceObject { ...@@ -114,6 +261,12 @@ public class TraceObject {
return "Time.valueOf(\"" + x.toString() + "\")"; 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) { protected String quoteTimestamp(java.sql.Timestamp x) {
if (x == null) { if (x == null) {
return "null"; return "null";
...@@ -121,6 +274,12 @@ public class TraceObject { ...@@ -121,6 +274,12 @@ public class TraceObject {
return "Timestamp.valueOf(\"" + x.toString() + "\")"; 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) { protected String quoteDate(java.sql.Date x) {
if (x == null) { if (x == null) {
return "null"; return "null";
...@@ -128,6 +287,12 @@ public class TraceObject { ...@@ -128,6 +287,12 @@ public class TraceObject {
return "Date.valueOf(\"" + x.toString() + "\")"; 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) { protected String quoteBigDecimal(BigDecimal x) {
if (x == null) { if (x == null) {
return "null"; return "null";
...@@ -135,6 +300,12 @@ public class TraceObject { ...@@ -135,6 +300,12 @@ public class TraceObject {
return "new BigDecimal(\"" + x.toString() + "\")"; 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) { protected String quoteBytes(byte[] x) {
if (x == null) { if (x == null) {
return "null"; return "null";
...@@ -142,14 +313,33 @@ public class TraceObject { ...@@ -142,14 +313,33 @@ public class TraceObject {
return "org.h2.util.ByteUtils.convertStringToBytes(\"" + ByteUtils.convertBytesToString(x) + "\")"; 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) { protected String quoteArray(String[] s) {
return StringUtils.quoteJavaStringArray(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) { protected String quoteIntArray(int[] s) {
return StringUtils.quoteJavaIntArray(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) { protected String quoteMap(Map map) {
if (map == null) { if (map == null) {
return "null"; return "null";
...@@ -175,6 +365,12 @@ public class TraceObject { ...@@ -175,6 +365,12 @@ public class TraceObject {
return buff.toString(); 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) { protected SQLException logAndConvert(Exception e) {
if (SysProperties.LOG_ALL_ERRORS) { if (SysProperties.LOG_ALL_ERRORS) {
synchronized (TraceObject.class) { synchronized (TraceObject.class) {
......
...@@ -15,6 +15,14 @@ public abstract class SchemaObjectBase extends DbObjectBase implements SchemaObj ...@@ -15,6 +15,14 @@ public abstract class SchemaObjectBase extends DbObjectBase implements SchemaObj
private Schema schema; 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) { protected void initSchemaObjectBase(Schema schema, int id, String name, String traceModule) {
initDbObjectBase(schema.getDatabase(), id, name, traceModule); initDbObjectBase(schema.getDatabase(), id, name, traceModule);
this.schema = schema; this.schema = schema;
......
...@@ -14,7 +14,24 @@ import org.h2.util.StringUtils; ...@@ -14,7 +14,24 @@ import org.h2.util.StringUtils;
* This class is used by the H2 Console. * This class is used by the H2 Console.
*/ */
public class ConnectionInfo { 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; String name;
int lastAccess; int lastAccess;
......
...@@ -98,7 +98,7 @@ public class DbContextRule implements Rule { ...@@ -98,7 +98,7 @@ public class DbContextRule implements Rule {
} }
private void addTableAlias(Sentence sentence) { private void addTableAlias(Sentence sentence) {
String query = sentence.query; String query = sentence.getQuery();
String q = StringUtils.toUpperEnglish(query.trim()); String q = StringUtils.toUpperEnglish(query.trim());
HashMap map = sentence.getAliases(); HashMap map = sentence.getAliases();
HashSet set = new HashSet(); HashSet set = new HashSet();
...@@ -136,7 +136,7 @@ public class DbContextRule implements Rule { ...@@ -136,7 +136,7 @@ public class DbContextRule implements Rule {
} }
private void addNewTableAlias(Sentence sentence) { private void addNewTableAlias(Sentence sentence) {
String query = sentence.query; String query = sentence.getQuery();
if (SUGGEST_TABLE_ALIAS) { if (SUGGEST_TABLE_ALIAS) {
// good when testing! // good when testing!
if (query.length() > 3) { if (query.length() > 3) {
...@@ -181,7 +181,7 @@ public class DbContextRule implements Rule { ...@@ -181,7 +181,7 @@ public class DbContextRule implements Rule {
// } // }
private void addSchema(Sentence sentence) { private void addSchema(Sentence sentence) {
String query = sentence.query; String query = sentence.getQuery();
String q = StringUtils.toUpperEnglish(query); String q = StringUtils.toUpperEnglish(query);
if (q.trim().length() == 0) { if (q.trim().length() == 0) {
q = q.trim(); q = q.trim();
...@@ -201,7 +201,7 @@ public class DbContextRule implements Rule { ...@@ -201,7 +201,7 @@ public class DbContextRule implements Rule {
} }
private void addTable(Sentence sentence) { private void addTable(Sentence sentence) {
String query = sentence.query; String query = sentence.getQuery();
DbSchema schema = sentence.getLastMatchedSchema(); DbSchema schema = sentence.getLastMatchedSchema();
if (schema == null) { if (schema == null) {
schema = contents.defaultSchema; schema = contents.defaultSchema;
...@@ -222,14 +222,11 @@ public class DbContextRule implements Rule { ...@@ -222,14 +222,11 @@ public class DbContextRule implements Rule {
} }
private void addColumn(Sentence sentence) { private void addColumn(Sentence sentence) {
String query = sentence.query; String query = sentence.getQuery();
String tableName = query; String tableName = query;
String columnPattern = ""; String columnPattern = "";
if (query.trim().length() == 0) { if (query.trim().length() == 0) {
tableName = null; tableName = null;
if (sentence.text.trim().endsWith(".")) {
return;
}
} else { } else {
tableName = StringUtils.toUpperEnglish(query.trim()); tableName = StringUtils.toUpperEnglish(query.trim());
if (tableName.endsWith(".")) { if (tableName.endsWith(".")) {
...@@ -286,7 +283,7 @@ public class DbContextRule implements Rule { ...@@ -286,7 +283,7 @@ public class DbContextRule implements Rule {
} }
public boolean matchRemove(Sentence sentence) { public boolean matchRemove(Sentence sentence) {
if (sentence.query.length() == 0) { if (sentence.getQuery().length() == 0) {
return false; return false;
} }
String s; String s;
...@@ -320,8 +317,8 @@ public class DbContextRule implements Rule { ...@@ -320,8 +317,8 @@ public class DbContextRule implements Rule {
} }
private String matchSchema(Sentence sentence) { private String matchSchema(Sentence sentence) {
String query = sentence.query; String query = sentence.getQuery();
String up = sentence.queryUpper; String up = sentence.getQueryUpper();
DbSchema[] schemas = contents.schemas; DbSchema[] schemas = contents.schemas;
String best = null; String best = null;
DbSchema bestSchema = null; DbSchema bestSchema = null;
...@@ -347,8 +344,8 @@ public class DbContextRule implements Rule { ...@@ -347,8 +344,8 @@ public class DbContextRule implements Rule {
} }
private String matchTable(Sentence sentence) { private String matchTable(Sentence sentence) {
String query = sentence.query; String query = sentence.getQuery();
String up = sentence.queryUpper; String up = sentence.getQueryUpper();
DbSchema schema = sentence.getLastMatchedSchema(); DbSchema schema = sentence.getLastMatchedSchema();
if (schema == null) { if (schema == null) {
schema = contents.defaultSchema; schema = contents.defaultSchema;
...@@ -378,8 +375,8 @@ public class DbContextRule implements Rule { ...@@ -378,8 +375,8 @@ public class DbContextRule implements Rule {
} }
private String matchColumnAlias(Sentence sentence) { private String matchColumnAlias(Sentence sentence) {
String query = sentence.query; String query = sentence.getQuery();
String up = sentence.queryUpper; String up = sentence.getQueryUpper();
int i = 0; int i = 0;
if (query.indexOf(' ') < 0) { if (query.indexOf(' ') < 0) {
return null; return null;
...@@ -401,8 +398,8 @@ public class DbContextRule implements Rule { ...@@ -401,8 +398,8 @@ public class DbContextRule implements Rule {
} }
private String matchTableAlias(Sentence sentence, boolean add) { private String matchTableAlias(Sentence sentence, boolean add) {
String query = sentence.query; String query = sentence.getQuery();
String up = sentence.queryUpper; String up = sentence.getQueryUpper();
int i = 0; int i = 0;
if (query.indexOf(' ') < 0) { if (query.indexOf(' ') < 0) {
return null; return null;
...@@ -455,8 +452,8 @@ public class DbContextRule implements Rule { ...@@ -455,8 +452,8 @@ public class DbContextRule implements Rule {
} }
private String matchColumn(Sentence sentence) { private String matchColumn(Sentence sentence) {
String query = sentence.query; String query = sentence.getQuery();
String up = sentence.queryUpper; String up = sentence.getQueryUpper();
HashSet set = sentence.getTables(); HashSet set = sentence.getTables();
String best = null; String best = null;
DbTableOrView last = sentence.getLastMatchedTable(); DbTableOrView last = sentence.getLastMatchedTable();
......
...@@ -139,6 +139,11 @@ public class FileStore { ...@@ -139,6 +139,11 @@ public class FileStore {
return store; return store;
} }
/**
* Generate the random salt bytes if required.
*
* @return the random salt or the magic
*/
protected byte[] generateSalt() { protected byte[] generateSalt() {
return magic; return magic;
} }
......
...@@ -871,7 +871,7 @@ public class MetaTable extends Table { ...@@ -871,7 +871,7 @@ public class MetaTable extends Table {
// PARAMS // PARAMS
t.params, t.params,
// AUTO_INCREMENT // AUTO_INCREMENT
String.valueOf(t.autoInc), String.valueOf(t.autoIncrement),
// MINIMUM_SCALE // MINIMUM_SCALE
String.valueOf(t.minScale), String.valueOf(t.minScale),
// MAXIMUM_SCALE // MAXIMUM_SCALE
......
...@@ -17,13 +17,34 @@ import org.h2.store.DiskFile; ...@@ -17,13 +17,34 @@ import org.h2.store.DiskFile;
*/ */
public abstract class CacheObject { 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; public int cacheQueue;
/** /**
* The number of blocks occupied by this object. * The number of blocks occupied by this object.
*/ */
protected int blockCount; protected int blockCount;
private int pos; private int pos;
private boolean changed; private boolean changed;
......
...@@ -67,7 +67,11 @@ public abstract class HashBase { ...@@ -67,7 +67,11 @@ public abstract class HashBase {
return size + (zeroKey ? 1 : 0); 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) { if (deletedCount > size) {
rehash(level); rehash(level);
} }
...@@ -76,6 +80,10 @@ public abstract class HashBase { ...@@ -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 { protected void checkSizeRemove() throws SQLException {
if (size < minSize && level > 0) { if (size < minSize && level > 0) {
rehash(level - 1); rehash(level - 1);
...@@ -84,6 +92,11 @@ public abstract class HashBase { ...@@ -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) { protected void reset(int newLevel) {
minSize = size * 3 / 4; minSize = size * 3 / 4;
size = 0; size = 0;
...@@ -95,6 +108,12 @@ public abstract class HashBase { ...@@ -95,6 +108,12 @@ public abstract class HashBase {
maxDeleted = 20 + len / 2; maxDeleted = 20 + len / 2;
} }
/**
* Calculate the index for this hash code.
*
* @param hash the hash code
* @return the index
*/
protected int getIndex(int hash) { protected int getIndex(int hash) {
return hash & mask; return hash & mask;
} }
......
...@@ -15,6 +15,9 @@ import java.sql.SQLException; ...@@ -15,6 +15,9 @@ import java.sql.SQLException;
*/ */
public abstract class Tool { public abstract class Tool {
/**
* The output stream where this tool writes to.
*/
protected PrintStream out = System.out; protected PrintStream out = System.out;
/** /**
......
...@@ -52,27 +52,111 @@ public class DataType { ...@@ -52,27 +52,111 @@ public class DataType {
private static ObjectArray types = new ObjectArray(); private static ObjectArray types = new ObjectArray();
private static HashMap typesByName = new HashMap(); private static HashMap typesByName = new HashMap();
private static DataType[] typesByValueType = new DataType[Value.TYPE_COUNT]; private static DataType[] typesByValueType = new DataType[Value.TYPE_COUNT];
/**
* The value type of this data type.
*/
public int type; public int type;
/**
* The data type name.
*/
public String name; public String name;
/**
* The SQL type.
*/
public int sqlType; public int sqlType;
/**
* The SQL type name.
*/
public String jdbc; 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; public int sqlTypePos;
/**
* The maximum supported precision.
*/
public int maxPrecision; 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 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 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 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; public long defaultPrecision;
/**
* The default scale.
*/
public int defaultScale; public int defaultScale;
/**
* The default display size.
*/
public int defaultDisplaySize; public int defaultDisplaySize;
/**
* If this data type should not be listed in the database meta data.
*/
public boolean hidden; public boolean hidden;
/**
* The number of bytes required for an object.
*/
public int memory; public int memory;
static { static {
...@@ -250,7 +334,7 @@ public class DataType { ...@@ -250,7 +334,7 @@ public class DataType {
dt.sqlType = sqlType; dt.sqlType = sqlType;
dt.jdbc = jdbc; dt.jdbc = jdbc;
dt.name = names[i]; dt.name = names[i];
dt.autoInc = dataType.autoInc; dt.autoIncrement = dataType.autoIncrement;
dt.decimal = dataType.decimal; dt.decimal = dataType.decimal;
dt.maxPrecision = dataType.maxPrecision; dt.maxPrecision = dataType.maxPrecision;
dt.maxScale = dataType.maxScale; dt.maxScale = dataType.maxScale;
...@@ -292,7 +376,7 @@ public class DataType { ...@@ -292,7 +376,7 @@ public class DataType {
dataType.supportsScale = true; dataType.supportsScale = true;
} }
dataType.decimal = true; dataType.decimal = true;
dataType.autoInc = autoInc; dataType.autoIncrement = autoInc;
return dataType; return dataType;
} }
......
...@@ -144,14 +144,110 @@ java org.h2.test.TestAll timer ...@@ -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; * If the test should run with many rows.
public int logMode = 1, traceLevelFile, throttle; */
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 String cipher;
public boolean traceTest, stopOnError; /**
* If only JDK 1.4 methods should be tested.
*/
public boolean jdk14 = true; 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; private Server server;
...@@ -168,34 +264,17 @@ java org.h2.test.TestAll timer ...@@ -168,34 +264,17 @@ java org.h2.test.TestAll timer
System.setProperty("h2.maxMemoryRowsDistinct", "128"); 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() measure and improve performance of ObjectArray.toArray()
H2 Console should support Java Queries
convert test.in.sql to RunScript syntax convert test.in.sql to RunScript syntax
C:\download\Data Concurrency and Consistency.pdf C:\download\Data Concurrency and Consistency.pdf
detect deadlock: alarm
not tested: not tested:
PreparedProcedure PREPARE <name>(column,...) AS ... PreparedProcedure PREPARE <name>(column,...) AS ...
Procedure Procedure
...@@ -208,8 +287,6 @@ deleted the same row when exactly does it occur in other databases ...@@ -208,8 +287,6 @@ deleted the same row when exactly does it occur in other databases
create an mbean for each database? server? (jconsole) create an mbean for each database? server? (jconsole)
jazoon
in help.csv, use complete examples for functions; add a test case in help.csv, use complete examples for functions; add a test case
improve javadocs improve javadocs
......
...@@ -31,10 +31,17 @@ import org.h2.tools.DeleteDbFiles; ...@@ -31,10 +31,17 @@ import org.h2.tools.DeleteDbFiles;
*/ */
public abstract class TestBase { public abstract class TestBase {
/**
* The base directory to write test databases.
*/
protected static String baseDir = getTestDir(""); protected static String baseDir = getTestDir("");
private static final String BASE_TEST_DIR = "data"; private static final String BASE_TEST_DIR = "data";
/**
* The test configuration.
*/
protected TestAll config; protected TestAll config;
private long start; private long start;
/** /**
...@@ -46,6 +53,9 @@ public abstract class TestBase { ...@@ -46,6 +53,9 @@ public abstract class TestBase {
return BASE_TEST_DIR + "/test" + name; return BASE_TEST_DIR + "/test" + name;
} }
/**
* Start the TCP server if enabled in the configuration.
*/
protected void startServerIfRequired() throws SQLException { protected void startServerIfRequired() throws SQLException {
config.beforeTest(); config.beforeTest();
} }
...@@ -130,6 +140,14 @@ public abstract class TestBase { ...@@ -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) { protected String getURL(String name, boolean admin) {
String url; String url;
if (name.startsWith("jdbc:")) { if (name.startsWith("jdbc:")) {
...@@ -213,7 +231,14 @@ public abstract class TestBase { ...@@ -213,7 +231,14 @@ public abstract class TestBase {
return conn; 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; return config.endless ? Integer.MAX_VALUE : config.big ? big : small;
} }
...@@ -221,6 +246,11 @@ public abstract class TestBase { ...@@ -221,6 +246,11 @@ public abstract class TestBase {
return "sa"; return "sa";
} }
/**
* Write a message to system out if trace is enabled.
*
* @param x the value to write
*/
protected void trace(int x) { protected void trace(int x) {
trace("" + x); trace("" + x);
} }
...@@ -236,6 +266,9 @@ public abstract class TestBase { ...@@ -236,6 +266,9 @@ public abstract class TestBase {
} }
} }
/**
* Print how much memory is currently used.
*/
protected void traceMemory() { protected void traceMemory() {
if (config.traceTest) { if (config.traceTest) {
trace("mem=" + getMemoryUsed()); trace("mem=" + getMemoryUsed());
...@@ -255,6 +288,11 @@ public abstract class TestBase { ...@@ -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() { public static int getMemoryUsed() {
Runtime rt = Runtime.getRuntime(); Runtime rt = Runtime.getRuntime();
long memory = Long.MAX_VALUE; long memory = Long.MAX_VALUE;
...@@ -270,10 +308,21 @@ public abstract class TestBase { ...@@ -270,10 +308,21 @@ public abstract class TestBase {
return mb; return mb;
} }
/**
* Called if the test reached a point that was not expected.
*
* @throws Exception always throws an exception
*/
protected void fail() throws Exception { protected void fail() throws Exception {
fail("Unexpected success"); 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 { protected void fail(String string) throws Exception {
println(string); println(string);
throw new Exception(string); throw new Exception(string);
...@@ -306,6 +355,11 @@ public abstract class TestBase { ...@@ -306,6 +355,11 @@ public abstract class TestBase {
} }
} }
/**
* Print a message to system out.
*
* @param s the message
*/
protected void println(String s) { protected void println(String s) {
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
printlnWithTime(time, getClass().getName() + " " + s); printlnWithTime(time, getClass().getName() + " " + s);
...@@ -317,15 +371,31 @@ public abstract class TestBase { ...@@ -317,15 +371,31 @@ public abstract class TestBase {
System.out.println(t + " " + s); System.out.println(t + " " + s);
} }
/**
* Print the current time and a message to system out.
*
* @param s the message
*/
protected void printTime(String s) { protected void printTime(String s) {
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
println(dateFormat.format(new java.util.Date()) + " " + s); 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 { protected void deleteDb(String name) throws Exception {
DeleteDbFiles.execute(baseDir, name, true); 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 { protected void deleteDb(String dir, String name) throws Exception {
DeleteDbFiles.execute(dir, name, true); DeleteDbFiles.execute(dir, name, true);
} }
...@@ -364,6 +434,13 @@ public abstract class TestBase { ...@@ -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 { protected void assertEquals(byte[] expected, byte[] actual) throws Exception {
assertTrue(expected.length == actual.length); assertTrue(expected.length == actual.length);
for (int i = 0; i < expected.length; i++) { for (int i = 0; i < expected.length; i++) {
...@@ -373,6 +450,13 @@ public abstract class TestBase { ...@@ -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 { protected void assertEquals(String expected, String actual) throws Exception {
if (expected == null && actual == null) { if (expected == null && actual == null) {
return; return;
...@@ -399,68 +483,151 @@ public abstract class TestBase { ...@@ -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 { protected void assertSmaller(long a, long b) throws Exception {
if (a >= b) { if (a >= b) {
fail("a: " + a + " is not smaller than b: " + 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 { protected void assertContains(String result, String contains) throws Exception {
if (result.indexOf(contains) < 0) { if (result.indexOf(contains) < 0) {
fail(result + " does not contain: " + contains); 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 { protected void assertStartsWith(String text, String expectedStart) throws Exception {
if (!text.startsWith(expectedStart)) { if (!text.startsWith(expectedStart)) {
fail(text + " does not start with: " + 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 { protected void assertEquals(long expected, long actual) throws Exception {
if (expected != actual) { if (expected != actual) {
fail("Expected: " + expected + " actual: " + 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 { protected void assertEquals(double expected, double actual) throws Exception {
if (expected != actual) { if (expected != actual) {
fail("Expected: " + expected + " actual: " + 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 { protected void assertEquals(float expected, float actual) throws Exception {
if (expected != actual) { if (expected != actual) {
fail("Expected: " + expected + " actual: " + 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 { protected void assertEquals(boolean expected, boolean actual) throws Exception {
if (expected != actual) { if (expected != actual) {
fail("Boolean expected: " + expected + " actual: " + 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 { protected void assertTrue(boolean condition) throws Exception {
assertTrue("Expected: true got: false", condition); 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 { protected void assertTrue(String message, boolean condition) throws Exception {
if (!condition) { if (!condition) {
fail(message); 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 { protected void assertFalse(boolean value) throws Exception {
assertFalse("Expected: false got: true", value); 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 { protected void assertFalse(String message, boolean value) throws Exception {
if (value) { if (value) {
fail(message); 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 { protected void assertResultRowCount(ResultSet rs, int expected) throws Exception {
int i = 0; int i = 0;
while (rs.next()) { while (rs.next()) {
...@@ -469,6 +636,14 @@ public abstract class TestBase { ...@@ -469,6 +636,14 @@ public abstract class TestBase {
assertEquals(i, expected); 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 { protected void assertSingleValue(Statement stat, String sql, int expected) throws Exception {
ResultSet rs = stat.executeQuery(sql); ResultSet rs = stat.executeQuery(sql);
assertTrue(rs.next()); assertTrue(rs.next());
...@@ -476,7 +651,17 @@ public abstract class TestBase { ...@@ -476,7 +651,17 @@ public abstract class TestBase {
assertFalse(rs.next()); 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 { int[] scale) throws Exception {
ResultSetMetaData meta = rs.getMetaData(); ResultSetMetaData meta = rs.getMetaData();
int cc = meta.getColumnCount(); int cc = meta.getColumnCount();
...@@ -538,15 +723,39 @@ public abstract class TestBase { ...@@ -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 len = rs.getMetaData().getColumnCount();
int rows = data.length; int rows = data.length;
if (rows == 0) { if (rows == 0) {
...@@ -589,7 +798,7 @@ public abstract class TestBase { ...@@ -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++) { for (int i = 0; i < len; i++) {
String sa = a[i]; String sa = a[i];
String sb = b[i]; String sb = b[i];
...@@ -624,6 +833,13 @@ public abstract class TestBase { ...@@ -624,6 +833,13 @@ public abstract class TestBase {
return "{" + sb + "}"; 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 { protected void crash(Connection conn) throws Exception {
((JdbcConnection) conn).setPowerOffCount(1); ((JdbcConnection) conn).setPowerOffCount(1);
try { try {
...@@ -640,6 +856,12 @@ public abstract class TestBase { ...@@ -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 { protected String readString(Reader reader) throws Exception {
if (reader == null) { if (reader == null) {
return null; return null;
...@@ -660,25 +882,52 @@ public abstract class TestBase { ...@@ -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 { protected void assertKnownException(SQLException e) throws Exception {
assertKnownException("", e); 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 { protected void assertKnownException(String message, SQLException e) throws Exception {
if (e != null && e.getSQLState().startsWith("HY000")) { if (e != null && e.getSQLState().startsWith("HY000")) {
TestBase.logError("Unexpected General error " + message, e); TestBase.logError("Unexpected General error " + message, e);
} }
} }
protected void assertEquals(Integer a, Integer b) throws Exception { /**
if (a == null || b == null) { * Check if two values are equal, and if not throw an exception.
assertTrue(a == b); *
* @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 { } 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 rs1 = stat1.executeQuery("SCRIPT NOPASSWORDS");
ResultSet rs2 = stat2.executeQuery("SCRIPT NOPASSWORDS"); ResultSet rs2 = stat2.executeQuery("SCRIPT NOPASSWORDS");
ArrayList list1 = new ArrayList(); ArrayList list1 = new ArrayList();
......
...@@ -43,12 +43,12 @@ public class TestBackup extends TestBase { ...@@ -43,12 +43,12 @@ public class TestBackup extends TestBase {
stat1.execute("backup to '" + baseDir + "/backup.zip'"); stat1.execute("backup to '" + baseDir + "/backup.zip'");
conn2.rollback(); conn2.rollback();
compareDatabases(stat1, stat2); assertEqualDatabases(stat1, stat2);
Restore.execute(baseDir + "/backup.zip", baseDir, "restored", true); Restore.execute(baseDir + "/backup.zip", baseDir, "restored", true);
conn3 = getConnection("restored"); conn3 = getConnection("restored");
stat3 = conn3.createStatement(); stat3 = conn3.createStatement();
compareDatabases(stat1, stat3); assertEqualDatabases(stat1, stat3);
conn1.close(); conn1.close();
conn2.close(); conn2.close();
......
...@@ -88,7 +88,7 @@ public class TestMemoryUsage extends TestBase { ...@@ -88,7 +88,7 @@ public class TestMemoryUsage extends TestBase {
System.gc(); System.gc();
System.gc(); System.gc();
int used = MemoryUtils.getMemoryUsed(); int used = MemoryUtils.getMemoryUsed();
if ((used - start) > 4000) { if ((used - start) > 5000) {
fail("Used: " + (used - start)); fail("Used: " + (used - start));
} }
stat.execute("drop table test"); stat.execute("drop table test");
......
...@@ -85,7 +85,7 @@ public class TestRunscript extends TestBase implements Trigger { ...@@ -85,7 +85,7 @@ public class TestRunscript extends TestBase implements Trigger {
stat2.execute(sql); stat2.execute(sql);
stat2.execute("script to '" + baseDir + "/backup.3.sql'"); stat2.execute("script to '" + baseDir + "/backup.3.sql'");
compareDatabases(stat1, stat2); assertEqualDatabases(stat1, stat2);
conn1.close(); conn1.close();
conn2.close(); conn2.close();
......
...@@ -248,20 +248,20 @@ public class TestMetaData extends TestBase { ...@@ -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 PROP FOR \"java.lang.System.getProperty(java.lang.String)\"");
stat.execute("CREATE ALIAS EXIT FOR \"java.lang.System.exit\""); stat.execute("CREATE ALIAS EXIT FOR \"java.lang.System.exit\"");
rs = meta.getProcedures(null, null, "EX%"); 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[] { "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.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.INTEGER,
Types.VARCHAR, Types.SMALLINT }, null, null); 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 }, }); "" + DatabaseMetaData.procedureNoResult }, });
rs = meta.getProcedureColumns(null, null, null, null); 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", 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" }, "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, 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, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT,
Types.VARCHAR , Types.INTEGER, Types.INTEGER}, null, null); Types.VARCHAR , Types.INTEGER, Types.INTEGER}, null, null);
testResultSetOrdered(rs, new String[][] { assertResultSetOrdered(rs, new String[][] {
{ catalog, Constants.SCHEMA_MAIN, "EXIT", "P1", "" + DatabaseMetaData.procedureColumnIn, { catalog, Constants.SCHEMA_MAIN, "EXIT", "P1", "" + DatabaseMetaData.procedureColumnIn,
"" + Types.INTEGER, "INTEGER", "10", "10", "0", "10", "" + DatabaseMetaData.procedureNoNulls }, "" + Types.INTEGER, "INTEGER", "10", "10", "0", "10", "" + DatabaseMetaData.procedureNoNulls },
{ catalog, Constants.SCHEMA_MAIN, "PROP", "P1", "" + DatabaseMetaData.procedureColumnIn, { catalog, Constants.SCHEMA_MAIN, "PROP", "P1", "" + DatabaseMetaData.procedureColumnIn,
...@@ -289,13 +289,13 @@ public class TestMetaData extends TestBase { ...@@ -289,13 +289,13 @@ public class TestMetaData extends TestBase {
} }
private void checkCrossRef(ResultSet rs) throws Exception { 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", "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, "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.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT }, null, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT }, null,
null); null);
testResultSetOrdered(rs, new String[][] { assertResultSetOrdered(rs, new String[][] {
{ catalog, Constants.SCHEMA_MAIN, "PARENT", "A", catalog, Constants.SCHEMA_MAIN, "CHILD", "PA", "1", { catalog, Constants.SCHEMA_MAIN, "PARENT", "A", catalog, Constants.SCHEMA_MAIN, "CHILD", "PA", "1",
"" + DatabaseMetaData.importedKeyRestrict, "" + DatabaseMetaData.importedKeyRestrict, "AB", "" + DatabaseMetaData.importedKeyRestrict, "" + DatabaseMetaData.importedKeyRestrict, "AB",
null, "" + DatabaseMetaData.importedKeyNotDeferrable }, null, "" + DatabaseMetaData.importedKeyNotDeferrable },
...@@ -579,7 +579,7 @@ public class TestMetaData extends TestBase { ...@@ -579,7 +579,7 @@ public class TestMetaData extends TestBase {
trace("getTables"); trace("getTables");
rs = meta.getTables(null, Constants.SCHEMA_MAIN, null, new String[] { "TABLE" }); 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, "SQL" }, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
Types.VARCHAR }, null, null); Types.VARCHAR }, null, null);
if (rs.next()) { if (rs.next()) {
...@@ -588,17 +588,17 @@ public class TestMetaData extends TestBase { ...@@ -588,17 +588,17 @@ public class TestMetaData extends TestBase {
stat.executeUpdate("CREATE TABLE TEST(" + "ID INT PRIMARY KEY," + "TEXT_V VARCHAR(120)," 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" + ")"); + "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" }); 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"); trace("getColumns");
rs = meta.getColumns(null, null, "TEST", null); 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", "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", "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, "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.INTEGER, Types.INTEGER, Types.INTEGER, Types.INTEGER,
Types.SMALLINT, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT, Types.INTEGER, Types.INTEGER, Types.SMALLINT, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT, Types.INTEGER, Types.INTEGER,
Types.INTEGER, Types.VARCHAR }, null, null); Types.INTEGER, Types.VARCHAR }, null, null);
testResultSetOrdered(rs, assertResultSetOrdered(rs,
new String[][] { new String[][] {
{ catalog, Constants.SCHEMA_MAIN, "TEST", "ID", "" + Types.INTEGER, "INTEGER", "10", "10", "0", { catalog, Constants.SCHEMA_MAIN, "TEST", "ID", "" + Types.INTEGER, "INTEGER", "10", "10", "0",
"10", "" + DatabaseMetaData.columnNoNulls, "", null, "" + Types.INTEGER, "0", "10", "1", "10", "" + DatabaseMetaData.columnNoNulls, "", null, "" + Types.INTEGER, "0", "10", "1",
...@@ -628,12 +628,12 @@ public class TestMetaData extends TestBase { ...@@ -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 INDEX IDX_TEXT_DEC ON TEST(TEXT_V,DEC_V)");
stat.executeUpdate("CREATE UNIQUE INDEX IDX_DATE ON TEST(DATE_V)"); stat.executeUpdate("CREATE UNIQUE INDEX IDX_DATE ON TEST(DATE_V)");
rs = meta.getIndexInfo(null, null, "TEST", false, false); 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", "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, "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, 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); 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", { catalog, Constants.SCHEMA_MAIN, "TEST", "FALSE", catalog, "IDX_DATE",
"" + DatabaseMetaData.tableIndexOther, "1", "DATE_V", "A", "0", "0", "" }, "" + DatabaseMetaData.tableIndexOther, "1", "DATE_V", "A", "0", "0", "" },
{ catalog, Constants.SCHEMA_MAIN, "TEST", "FALSE", catalog, "PRIMARY_KEY_2", { catalog, Constants.SCHEMA_MAIN, "TEST", "FALSE", catalog, "PRIMARY_KEY_2",
...@@ -645,44 +645,44 @@ public class TestMetaData extends TestBase { ...@@ -645,44 +645,44 @@ public class TestMetaData extends TestBase {
stat.executeUpdate("DROP INDEX IDX_TEXT_DEC"); stat.executeUpdate("DROP INDEX IDX_TEXT_DEC");
stat.executeUpdate("DROP INDEX IDX_DATE"); stat.executeUpdate("DROP INDEX IDX_DATE");
rs = meta.getIndexInfo(null, null, "TEST", false, false); 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", "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, "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, 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); 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", "" } }); "PRIMARY_KEY_2", "" + DatabaseMetaData.tableIndexOther, "1", "ID", "A", "0", "0", "" } });
trace("getPrimaryKeys"); trace("getPrimaryKeys");
rs = meta.getPrimaryKeys(null, null, "TEST"); 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, "PK_NAME" }, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT,
Types.VARCHAR }, null, null); Types.VARCHAR }, null, null);
testResultSetOrdered(rs, assertResultSetOrdered(rs,
new String[][] { { catalog, Constants.SCHEMA_MAIN, "TEST", "ID", "1", "PRIMARY_KEY_2" }, }); new String[][] { { catalog, Constants.SCHEMA_MAIN, "TEST", "ID", "1", "PRIMARY_KEY_2" }, });
trace("getTables - using a wildcard"); 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 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))"); 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); 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", "" } }); { catalog, Constants.SCHEMA_MAIN, "T_2", "TABLE", "" } });
trace("getTables - using a quoted _ character"); trace("getTables - using a quoted _ character");
rs = meta.getTables(null, null, "T\\_2", null); 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"); trace("getTables - using the % wildcard");
rs = meta.getTables(null, Constants.SCHEMA_MAIN, "%", new String[] { "TABLE" }); 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, "TX2", "TABLE", "" },
{ catalog, Constants.SCHEMA_MAIN, "T_2", "TABLE", "" } }); { catalog, Constants.SCHEMA_MAIN, "T_2", "TABLE", "" } });
stat.execute("DROP TABLE TEST"); stat.execute("DROP TABLE TEST");
trace("getColumns - using wildcards"); trace("getColumns - using wildcards");
rs = meta.getColumns(null, null, "___", "B%"); 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, "TX2", "B", "" + Types.INTEGER, "INTEGER", "10"},
{ catalog, Constants.SCHEMA_MAIN, "T_2", "B", "" + Types.INTEGER, "INTEGER", "10"}, }); { catalog, Constants.SCHEMA_MAIN, "T_2", "B", "" + Types.INTEGER, "INTEGER", "10"}, });
trace("getColumns - using wildcards"); trace("getColumns - using wildcards");
rs = meta.getColumns(null, null, "_\\__", "%"); 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", "B", "" + Types.INTEGER, "INTEGER", "10"},
{ catalog, Constants.SCHEMA_MAIN, "T_2", "A", "" + Types.VARCHAR, "VARCHAR", "6"}, { catalog, Constants.SCHEMA_MAIN, "T_2", "A", "" + Types.VARCHAR, "VARCHAR", "6"},
{ catalog, Constants.SCHEMA_MAIN, "T_2", "C", "" + Types.INTEGER, "INTEGER", "10"}, }); { catalog, Constants.SCHEMA_MAIN, "T_2", "C", "" + Types.INTEGER, "INTEGER", "10"}, });
...@@ -690,7 +690,7 @@ public class TestMetaData extends TestBase { ...@@ -690,7 +690,7 @@ public class TestMetaData extends TestBase {
stat.executeUpdate("CREATE UNIQUE INDEX A_INDEX ON TX2(B,C,A)"); stat.executeUpdate("CREATE UNIQUE INDEX A_INDEX ON TX2(B,C,A)");
stat.executeUpdate("CREATE INDEX B_INDEX ON TX2(A,B,C)"); stat.executeUpdate("CREATE INDEX B_INDEX ON TX2(A,B,C)");
rs = meta.getIndexInfo(null, null, "TX2", false, false); 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", { catalog, Constants.SCHEMA_MAIN, "TX2", "FALSE", catalog, "A_INDEX",
"" + DatabaseMetaData.tableIndexOther, "1", "B", "A" }, "" + DatabaseMetaData.tableIndexOther, "1", "B", "A" },
{ catalog, Constants.SCHEMA_MAIN, "TX2", "FALSE", catalog, "A_INDEX", { catalog, Constants.SCHEMA_MAIN, "TX2", "FALSE", catalog, "A_INDEX",
...@@ -711,7 +711,7 @@ public class TestMetaData extends TestBase { ...@@ -711,7 +711,7 @@ public class TestMetaData extends TestBase {
"" + DatabaseMetaData.tableIndexOther, "3", "C", "A" }, }); "" + DatabaseMetaData.tableIndexOther, "3", "C", "A" }, });
trace("getPrimaryKeys"); trace("getPrimaryKeys");
rs = meta.getPrimaryKeys(null, null, "T_2"); 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", "A", "2", "PRIMARY_KEY_1" },
{ catalog, Constants.SCHEMA_MAIN, "T_2", "B", "3", "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" }, }); { catalog, Constants.SCHEMA_MAIN, "T_2", "C", "1", "PRIMARY_KEY_1" }, });
...@@ -723,7 +723,7 @@ public class TestMetaData extends TestBase { ...@@ -723,7 +723,7 @@ public class TestMetaData extends TestBase {
trace("getImportedKeys"); trace("getImportedKeys");
rs = meta.getImportedKeys(null, null, "CHILD"); 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", "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, "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.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
...@@ -739,7 +739,7 @@ public class TestMetaData extends TestBase { ...@@ -739,7 +739,7 @@ public class TestMetaData extends TestBase {
trace("getExportedKeys"); trace("getExportedKeys");
rs = meta.getExportedKeys(null, null, "PARENT"); 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", "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, "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.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
...@@ -755,7 +755,7 @@ public class TestMetaData extends TestBase { ...@@ -755,7 +755,7 @@ public class TestMetaData extends TestBase {
*/ */
trace("getCrossReference"); trace("getCrossReference");
rs = meta.getCrossReference(null, null, "PARENT", null, null, "CHILD"); 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", "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, "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.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
...@@ -771,7 +771,7 @@ public class TestMetaData extends TestBase { ...@@ -771,7 +771,7 @@ public class TestMetaData extends TestBase {
*/ */
rs = meta.getSchemas(); 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); Types.VARCHAR, Types.VARCHAR, DataType.TYPE_BOOLEAN }, null, null);
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(rs.getString(1), "INFORMATION_SCHEMA"); assertEquals(rs.getString(1), "INFORMATION_SCHEMA");
...@@ -780,15 +780,15 @@ public class TestMetaData extends TestBase { ...@@ -780,15 +780,15 @@ public class TestMetaData extends TestBase {
assertFalse(rs.next()); assertFalse(rs.next());
rs = meta.getCatalogs(); rs = meta.getCatalogs();
testResultSetMeta(rs, 1, new String[] { "TABLE_CAT" }, new int[] { Types.VARCHAR }, null, null); assertResultSetMeta(rs, 1, new String[] { "TABLE_CAT" }, new int[] { Types.VARCHAR }, null, null);
testResultSetOrdered(rs, new String[][] { { catalog } }); assertResultSetOrdered(rs, new String[][] { { catalog } });
rs = meta.getTableTypes(); rs = meta.getTableTypes();
testResultSetMeta(rs, 1, new String[] { "TABLE_TYPE" }, new int[] { Types.VARCHAR }, null, null); assertResultSetMeta(rs, 1, new String[] { "TABLE_TYPE" }, new int[] { Types.VARCHAR }, null, null);
testResultSetOrdered(rs, new String[][] { { "SYSTEM TABLE" }, { "TABLE" }, { "TABLE LINK" }, { "VIEW" } }); assertResultSetOrdered(rs, new String[][] { { "SYSTEM TABLE" }, { "TABLE" }, { "TABLE LINK" }, { "VIEW" } });
rs = meta.getTypeInfo(); 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", "LITERAL_SUFFIX", "CREATE_PARAMS", "NULLABLE", "CASE_SENSITIVE", "SEARCHABLE", "UNSIGNED_ATTRIBUTE",
"FIXED_PREC_SCALE", "AUTO_INCREMENT", "LOCAL_TYPE_NAME", "MINIMUM_SCALE", "MAXIMUM_SCALE", "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, "SQL_DATA_TYPE", "SQL_DATETIME_SUB", "NUM_PREC_RADIX" }, new int[] { Types.VARCHAR, Types.SMALLINT,
...@@ -797,12 +797,12 @@ public class TestMetaData extends TestBase { ...@@ -797,12 +797,12 @@ public class TestMetaData extends TestBase {
Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.INTEGER, Types.INTEGER }, null, null); Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.INTEGER, Types.INTEGER }, null, null);
rs = meta.getTablePrivileges(null, 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, "PRIVILEGE", "IS_GRANTABLE" }, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR }, null, null); Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR }, null, null);
rs = meta.getColumnPrivileges(null, null, "TEST", 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, "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); Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR }, null, null);
......
...@@ -621,7 +621,7 @@ public class TestPreparedStatement extends TestBase { ...@@ -621,7 +621,7 @@ public class TestPreparedStatement extends TestBase {
assertTrue(stat.execute("SELECT * FROM T_INT ORDER BY ID")); assertTrue(stat.execute("SELECT * FROM T_INT ORDER BY ID"));
rs = stat.getResultSet(); 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 }, { "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" }, { "12", "1" }, { "13", "0" }, { "14", "-20" }, { "15", "100" }, { "16", "30000" }, { "17", "-30000" },
{ "18", "" + Integer.MAX_VALUE }, { "19", "" + Integer.MIN_VALUE }, }); { "18", "" + Integer.MAX_VALUE }, { "19", "" + Integer.MIN_VALUE }, });
......
...@@ -283,7 +283,7 @@ public class TestResultSet extends TestBase { ...@@ -283,7 +283,7 @@ public class TestResultSet extends TestBase {
assertEquals(meta.getColumnClassName(3), null); assertEquals(meta.getColumnClassName(3), null);
assertTrue(rs.getRow() == 0); 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 }); Types.NULL }, new int[] { 10, 10, 1 }, new int[] { 0, 0, 0 });
rs.next(); rs.next();
assertEquals(rs.getConcurrency(), ResultSet.CONCUR_READ_ONLY); assertEquals(rs.getConcurrency(), ResultSet.CONCUR_READ_ONLY);
...@@ -408,7 +408,7 @@ public class TestResultSet extends TestBase { ...@@ -408,7 +408,7 @@ public class TestResultSet extends TestBase {
stat.execute("INSERT INTO TEST VALUES(10,'\\''')"); stat.execute("INSERT INTO TEST VALUES(10,'\\''')");
stat.execute("INSERT INTO TEST VALUES(11,'\\%')"); stat.execute("INSERT INTO TEST VALUES(11,'\\%')");
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID"); 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 }); 10, 255 }, new int[] { 0, 0 });
String value; String value;
rs.next(); rs.next();
...@@ -488,7 +488,7 @@ public class TestResultSet extends TestBase { ...@@ -488,7 +488,7 @@ public class TestResultSet extends TestBase {
stat.execute("INSERT INTO TEST VALUES(7,-99999998.99)"); stat.execute("INSERT INTO TEST VALUES(7,-99999998.99)");
stat.execute("INSERT INTO TEST VALUES(8,NULL)"); stat.execute("INSERT INTO TEST VALUES(8,NULL)");
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID"); 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 }); 10, 10 }, new int[] { 0, 2 });
BigDecimal bd; BigDecimal bd;
rs.next(); rs.next();
...@@ -539,7 +539,7 @@ public class TestResultSet extends TestBase { ...@@ -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(7, -99999999.99, -99999999.99)");
stat.execute("INSERT INTO TEST VALUES(8, NULL, NULL)"); stat.execute("INSERT INTO TEST VALUES(8, NULL, NULL)");
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID"); 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 }); new int[] { Types.INTEGER, Types.DOUBLE, Types.REAL }, new int[] { 10, 17, 7 }, new int[] { 0, 0, 0 });
BigDecimal bd; BigDecimal bd;
rs.next(); rs.next();
...@@ -603,10 +603,10 @@ public class TestResultSet extends TestBase { ...@@ -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(4,TIMESTAMP '9999-12-31 23:59:59')");
stat.execute("INSERT INTO TEST VALUES(5,NULL)"); 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"); 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 }); new int[] { 10, 23 }, new int[] { 0, 10 });
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID"); 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 }); new int[] { 10, 23 }, new int[] { 0, 10 });
rs.next(); rs.next();
java.sql.Date date; java.sql.Date date;
...@@ -723,7 +723,7 @@ public class TestResultSet extends TestBase { ...@@ -723,7 +723,7 @@ public class TestResultSet extends TestBase {
prep.execute(); prep.execute();
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID"); 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 }); Types.TIME, Types.TIMESTAMP }, new int[] { 10, 8, 6, 23 }, new int[] { 0, 0, 0, 10 });
rs.next(); rs.next();
...@@ -777,7 +777,7 @@ public class TestResultSet extends TestBase { ...@@ -777,7 +777,7 @@ public class TestResultSet extends TestBase {
stat.execute("INSERT INTO TEST VALUES(5,X'0bcec1')"); stat.execute("INSERT INTO TEST VALUES(5,X'0bcec1')");
stat.execute("INSERT INTO TEST VALUES(6,NULL)"); stat.execute("INSERT INTO TEST VALUES(6,NULL)");
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID"); 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 }); 10, Integer.MAX_VALUE }, new int[] { 0, 0 });
rs.next(); rs.next();
checkBytes(rs.getBytes(2), new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01 }); checkBytes(rs.getBytes(2), new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01 });
...@@ -816,7 +816,7 @@ public class TestResultSet extends TestBase { ...@@ -816,7 +816,7 @@ public class TestResultSet extends TestBase {
stat.execute("INSERT INTO TEST VALUES(6,NULL)"); stat.execute("INSERT INTO TEST VALUES(6,NULL)");
stat.execute("INSERT INTO TEST VALUES(7,NULL)"); stat.execute("INSERT INTO TEST VALUES(7,NULL)");
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID"); 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 }); 10, Integer.MAX_VALUE }, new int[] { 0, 0 });
rs.next(); rs.next();
string = rs.getString(2); string = rs.getString(2);
......
...@@ -105,6 +105,7 @@ public class TestWeb extends TestBase { ...@@ -105,6 +105,7 @@ public class TestWeb extends TestBase {
result = client.get(url, "logout.do"); result = client.get(url, "logout.do");
result = client.get(url, "settingRemove.do?name=_test_"); result = client.get(url, "settingRemove.do?name=_test_");
server.stop(); server.stop();
} }
......
...@@ -30,14 +30,67 @@ import org.h2.util.IOUtils; ...@@ -30,14 +30,67 @@ import org.h2.util.IOUtils;
*/ */
public abstract class TestHalt extends TestBase { 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"); static final String DIR = TestBase.getTestDir("halt");
private static final String DATABASE_NAME = "halt"; private static final String DATABASE_NAME = "halt";
private static final String TRACE_FILE_NAME = "haltTrace.trace.db"; 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; protected Connection conn;
/**
* The pseudo random number generator used for this test.
*/
protected Random random = new Random(); protected Random random = new Random();
private SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm:ss "); private SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm:ss ");
...@@ -73,6 +126,11 @@ public abstract class TestHalt extends TestBase { ...@@ -73,6 +126,11 @@ public abstract class TestHalt extends TestBase {
return DriverManager.getConnection("jdbc:h2:" + baseDir + "/halt", "sa", "sa"); 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 { protected void start(String[] args) throws Exception {
if (args.length == 0) { if (args.length == 0) {
runTest(); runTest();
...@@ -111,10 +169,21 @@ public abstract class TestHalt extends TestBase { ...@@ -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) { protected void traceOperation(String s) {
trace(s, null); 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) { protected void trace(String s, Exception e) {
FileWriter writer = null; FileWriter writer = null;
try { try {
...@@ -191,6 +260,9 @@ public abstract class TestHalt extends TestBase { ...@@ -191,6 +260,9 @@ public abstract class TestHalt extends TestBase {
} }
} }
/**
* Close the database connection normally.
*/
protected void disconnect() { protected void disconnect() {
try { try {
traceOperation("disconnect"); traceOperation("disconnect");
...@@ -248,6 +320,12 @@ public abstract class TestHalt extends TestBase { ...@@ -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) { protected String getRandomString(int len) {
StringBuffer buff = new StringBuffer(); StringBuffer buff = new StringBuffer();
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
......
...@@ -37,6 +37,9 @@ public class TestHaltApp extends TestHalt { ...@@ -37,6 +37,9 @@ public class TestHaltApp extends TestHalt {
stat.execute(sql); stat.execute(sql);
} }
/**
* Initialize the database.
*/
protected void testInit() throws SQLException { protected void testInit() throws SQLException {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
// stat.execute("CREATE TABLE TEST(ID IDENTITY, NAME VARCHAR(255))"); // stat.execute("CREATE TABLE TEST(ID IDENTITY, NAME VARCHAR(255))");
...@@ -51,6 +54,9 @@ public class TestHaltApp extends TestHalt { ...@@ -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)"); 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 { protected void testWaitAfterAppStart() throws Exception {
int sleep = 10 + random.nextInt(300); int sleep = 10 + random.nextInt(300);
if ((flags & FLAG_NO_DELAY) == 0) { if ((flags & FLAG_NO_DELAY) == 0) {
...@@ -59,6 +65,12 @@ public class TestHaltApp extends TestHalt { ...@@ -59,6 +65,12 @@ public class TestHaltApp extends TestHalt {
Thread.sleep(sleep); 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 { protected void testCheckAfterCrash() throws Exception {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST"); ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST");
...@@ -71,6 +83,9 @@ public class TestHaltApp extends TestHalt { ...@@ -71,6 +83,9 @@ public class TestHaltApp extends TestHalt {
} }
} }
/**
* Initialize the application.
*/
protected void appStart() throws SQLException { protected void appStart() throws SQLException {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
if ((flags & FLAG_NO_DELAY) != 0) { if ((flags & FLAG_NO_DELAY) != 0) {
...@@ -83,6 +98,9 @@ public class TestHaltApp extends TestHalt { ...@@ -83,6 +98,9 @@ public class TestHaltApp extends TestHalt {
trace("rows: " + rowCount, null); trace("rows: " + rowCount, null);
} }
/**
* Run the application code.
*/
protected void appRun() throws Exception { protected void appRun() throws Exception {
conn.setAutoCommit(false); conn.setAutoCommit(false);
traceOperation("setAutoCommit false"); traceOperation("setAutoCommit false");
......
...@@ -17,6 +17,9 @@ import org.h2.test.TestBase; ...@@ -17,6 +17,9 @@ import org.h2.test.TestBase;
*/ */
public class TestMulti extends TestBase { public class TestMulti extends TestBase {
/**
* If set, the test should stop.
*/
public volatile boolean stop; public volatile boolean stop;
public void test() throws Exception { public void test() throws Exception {
......
...@@ -521,4 +521,5 @@ philosophers technologies modeling federation enterprise semantic deductive ...@@ -521,4 +521,5 @@ philosophers technologies modeling federation enterprise semantic deductive
fusion legacy decoded commented trimmed reaches indicating marks scaled tells fusion legacy decoded commented trimmed reaches indicating marks scaled tells
monitor benefit performing conditional significant arithmetic instrumented monitor benefit performing conditional significant arithmetic instrumented
doclets extremes instructions printable skips sava sources cms bytecode cfml doclets extremes instructions printable skips sava sources cms bytecode cfml
cold compiles markup spellchecker interleaved poormans programmed swt railo cold compiles markup spellchecker interleaved poormans programmed swt railo
\ No newline at end of file clobs resizes precisions scales
\ No newline at end of file
...@@ -334,7 +334,7 @@ public class Doclet { ...@@ -334,7 +334,7 @@ public class Doclet {
} }
private static boolean skipField(ClassDoc clazz, FieldDoc field) { 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 true;
} }
return false; return false;
...@@ -347,7 +347,7 @@ public class Doclet { ...@@ -347,7 +347,7 @@ public class Doclet {
return true; return true;
} }
String name = method.name(); String name = method.name();
if (!method.isPublic() || name.equals("finalize")) { if ((method.isPrivate() && !method.isPackagePrivate()) || name.equals("finalize")) {
return true; return true;
} }
if (method.getRawCommentText().trim().startsWith("@deprecated INTERNAL")) { if (method.getRawCommentText().trim().startsWith("@deprecated INTERNAL")) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论