提交 528531e5 authored 作者: Thomas Mueller's avatar Thomas Mueller

javadocs

上级 2c2e30e7
...@@ -374,4 +374,10 @@ The SQL statement ANALYZE can be used to automatically estimate the selectivity ...@@ -374,4 +374,10 @@ The SQL statement ANALYZE can be used to automatically estimate the selectivity
This command should be run from time to time to improve the query plans generated by the optimizer. This command should be run from time to time to improve the query plans generated by the optimizer.
</p> </p>
<h3>Optimization Examples</h3>
<p>
See <code>src/test/org/h2/samples/optimizations.sql</code> for a few examples of queries
that benefit from special optimizations built into the database.
</p>
</div></td></tr></table><!-- analytics --></body></html> </div></td></tr></table><!-- analytics --></body></html>
\ No newline at end of file
...@@ -6211,6 +6211,12 @@ If a table has multiple indexes, sometimes more than one index could be used. Ex ...@@ -6211,6 +6211,12 @@ If a table has multiple indexes, sometimes more than one index could be used. Ex
@performance_1400_p @performance_1400_p
The SQL statement ANALYZE can be used to automatically estimate the selectivity of the columns in the tables. This command should be run from time to time to improve the query plans generated by the optimizer. The SQL statement ANALYZE can be used to automatically estimate the selectivity of the columns in the tables. This command should be run from time to time to improve the query plans generated by the optimizer.
@performance_1401_h3
Optimization Examples
@performance_1402_p
See <code>src/test/org/h2/samples/optimizations.sql</code> for a few examples of queries that benefit from special optimizations built into the database.
@quickstart_1000_h1 @quickstart_1000_h1
Quickstart Quickstart
......
...@@ -6216,6 +6216,12 @@ MySQL ...@@ -6216,6 +6216,12 @@ MySQL
@performance_1400_p @performance_1400_p
#The SQL statement ANALYZE can be used to automatically estimate the selectivity of the columns in the tables. This command should be run from time to time to improve the query plans generated by the optimizer. #The SQL statement ANALYZE can be used to automatically estimate the selectivity of the columns in the tables. This command should be run from time to time to improve the query plans generated by the optimizer.
@performance_1401_h3
#Optimization Examples
@performance_1402_p
#See <code>src/test/org/h2/samples/optimizations.sql</code> for a few examples of queries that benefit from special optimizations built into the database.
@quickstart_1000_h1 @quickstart_1000_h1
クイックスタート クイックスタート
......
...@@ -2069,6 +2069,8 @@ performance_1397_h3=Updating Optimizer Statistics / Column Selectivity ...@@ -2069,6 +2069,8 @@ performance_1397_h3=Updating Optimizer Statistics / Column Selectivity
performance_1398_p=When executing a query, at most one index per joined table can be used. If the same table is joined multiple times, for each join only one index is used. Example\: for the query SELECT * FROM TEST T1, TEST T2 WHERE T1.NAME\='A' AND T2.ID\=T1.ID, two index can be used, in this case the index on NAME for T1 and the index on ID for T2. performance_1398_p=When executing a query, at most one index per joined table can be used. If the same table is joined multiple times, for each join only one index is used. Example\: for the query SELECT * FROM TEST T1, TEST T2 WHERE T1.NAME\='A' AND T2.ID\=T1.ID, two index can be used, in this case the index on NAME for T1 and the index on ID for T2.
performance_1399_p=If a table has multiple indexes, sometimes more than one index could be used. Example\: if there is a table TEST(ID, NAME, FIRSTNAME) and an index on each column, then two indexes could be used for the query SELECT * FROM TEST WHERE NAME\='A' AND FIRSTNAME\='B', the index on NAME or the index on FIRSTNAME. It is not possible to use both indexes at the same time. Which index is used depends on the selectivity of the column. The selectivity describes the 'uniqueness' of values in a column. A selectivity of 100 means each value appears only once, and a selectivity of 1 means the same value appears in many or most rows. For the query above, the index on NAME should be used if the table contains more distinct names than first names. performance_1399_p=If a table has multiple indexes, sometimes more than one index could be used. Example\: if there is a table TEST(ID, NAME, FIRSTNAME) and an index on each column, then two indexes could be used for the query SELECT * FROM TEST WHERE NAME\='A' AND FIRSTNAME\='B', the index on NAME or the index on FIRSTNAME. It is not possible to use both indexes at the same time. Which index is used depends on the selectivity of the column. The selectivity describes the 'uniqueness' of values in a column. A selectivity of 100 means each value appears only once, and a selectivity of 1 means the same value appears in many or most rows. For the query above, the index on NAME should be used if the table contains more distinct names than first names.
performance_1400_p=The SQL statement ANALYZE can be used to automatically estimate the selectivity of the columns in the tables. This command should be run from time to time to improve the query plans generated by the optimizer. performance_1400_p=The SQL statement ANALYZE can be used to automatically estimate the selectivity of the columns in the tables. This command should be run from time to time to improve the query plans generated by the optimizer.
performance_1401_h3=Optimization Examples
performance_1402_p=See <code>src/test/org/h2/samples/optimizations.sql</code> for a few examples of queries that benefit from special optimizations built into the database.
quickstart_1000_h1=Quickstart quickstart_1000_h1=Quickstart
quickstart_1001_a=Embedding H2 in an Application quickstart_1001_a=Embedding H2 in an Application
quickstart_1002_a=The H2 Console Application quickstart_1002_a=The H2 Console Application
......
...@@ -81,7 +81,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler { ...@@ -81,7 +81,7 @@ public abstract class ScriptBase extends Prepared implements DataHandler {
protected String getFileName() throws SQLException { protected String getFileName() throws SQLException {
if (file != null && fileName == null) { if (file != null && fileName == null) {
fileName = file.getValue(session).getString(); fileName = file.optimize(session).getValue(session).getString();
if (fileName == null || fileName.trim().length() == 0) { if (fileName == null || fileName.trim().length() == 0) {
fileName = "script.sql"; fileName = "script.sql";
} }
......
...@@ -444,7 +444,7 @@ public class ScriptCommand extends ScriptBase { ...@@ -444,7 +444,7 @@ public class ScriptCommand extends ScriptBase {
*/ */
public static Reader combineClob(Connection conn, int id) throws SQLException, IOException { public static Reader combineClob(Connection conn, int id) throws SQLException, IOException {
ResultSet rs = getLobStream(conn, "CDATA", id); ResultSet rs = getLobStream(conn, "CDATA", id);
Writer out = FileUtils.openFileWriter(TEMP_LOB_FILENAME, false); Writer out = IOUtils.getWriter(FileUtils.openFileOutputStream(TEMP_LOB_FILENAME, false));
while (rs.next()) { while (rs.next()) {
Reader in = new BufferedReader(rs.getCharacterStream(1)); Reader in = new BufferedReader(rs.getCharacterStream(1));
IOUtils.copyAndCloseInput(in, out); IOUtils.copyAndCloseInput(in, out);
......
...@@ -17,6 +17,7 @@ import org.h2.constant.SysProperties; ...@@ -17,6 +17,7 @@ import org.h2.constant.SysProperties;
import org.h2.expression.ParameterInterface; import org.h2.expression.ParameterInterface;
import org.h2.util.ByteUtils; import org.h2.util.ByteUtils;
import org.h2.util.FileUtils; import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.ObjectArray; import org.h2.util.ObjectArray;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -179,7 +180,7 @@ public class TraceObject { ...@@ -179,7 +180,7 @@ public class TraceObject {
synchronized (TraceObject.class) { synchronized (TraceObject.class) {
// e.printStackTrace(); // e.printStackTrace();
try { try {
Writer writer = FileUtils.openFileWriter(SysProperties.LOG_ALL_ERRORS_FILE, true); Writer writer = IOUtils.getWriter(FileUtils.openFileOutputStream(SysProperties.LOG_ALL_ERRORS_FILE, true));
PrintWriter p = new PrintWriter(writer); PrintWriter p = new PrintWriter(writer);
e.printStackTrace(p); e.printStackTrace(p);
p.close(); p.close();
......
...@@ -19,6 +19,7 @@ import org.h2.constant.SysProperties; ...@@ -19,6 +19,7 @@ import org.h2.constant.SysProperties;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.util.ClassUtils; import org.h2.util.ClassUtils;
import org.h2.util.FileUtils; import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.SmallLRUCache; import org.h2.util.SmallLRUCache;
/** /**
...@@ -302,7 +303,7 @@ public class TraceSystem implements TraceWriter { ...@@ -302,7 +303,7 @@ public class TraceSystem implements TraceWriter {
// can't be opened // can't be opened
return false; return false;
} }
fileWriter = FileUtils.openFileWriter(fileName, true); fileWriter = IOUtils.getWriter(FileUtils.openFileOutputStream(fileName, true));
printWriter = new PrintWriter(fileWriter, true); printWriter = new PrintWriter(fileWriter, true);
} catch (Exception e) { } catch (Exception e) {
logWritingError(e); logWritingError(e);
......
...@@ -28,6 +28,7 @@ import org.h2.util.FileUtils; ...@@ -28,6 +28,7 @@ import org.h2.util.FileUtils;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.NetUtils; import org.h2.util.NetUtils;
import org.h2.util.SortedProperties;
/** /**
* Small FTP Server. Intended for ad-hoc networks in a secure environment. * Small FTP Server. Intended for ad-hoc networks in a secure environment.
...@@ -274,7 +275,7 @@ public class FtpServer implements Service { ...@@ -274,7 +275,7 @@ public class FtpServer implements Service {
new StreamRedirect(path, p.getInputStream(), null).start(); new StreamRedirect(path, p.getInputStream(), null).start();
return; return;
} }
Properties prop = FileUtils.loadProperties(path); Properties prop = SortedProperties.loadProperties(path);
String command = prop.getProperty("command"); String command = prop.getProperty("command");
String outFile = path.substring(0, path.length() - TASK_SUFFIX.length()); String outFile = path.substring(0, path.length() - TASK_SUFFIX.length());
String errorFile = root + "/" + prop.getProperty("error", outFile + ".err.txt"); String errorFile = root + "/" + prop.getProperty("error", outFile + ".err.txt");
......
...@@ -193,9 +193,9 @@ public class WebServer implements Service { ...@@ -193,9 +193,9 @@ public class WebServer implements Service {
// TODO web: support using a different properties file // TODO web: support using a different properties file
Properties prop = loadProperties(); Properties prop = loadProperties();
driverList = prop.getProperty("drivers"); driverList = prop.getProperty("drivers");
port = FileUtils.getIntProperty(prop, "webPort", Constants.DEFAULT_HTTP_PORT); port = SortedProperties.getIntProperty(prop, "webPort", Constants.DEFAULT_HTTP_PORT);
ssl = FileUtils.getBooleanProperty(prop, "webSSL", Constants.DEFAULT_HTTP_SSL); ssl = SortedProperties.getBooleanProperty(prop, "webSSL", Constants.DEFAULT_HTTP_SSL);
allowOthers = FileUtils.getBooleanProperty(prop, "webAllowOthers", Constants.DEFAULT_HTTP_ALLOW_OTHERS); allowOthers = SortedProperties.getBooleanProperty(prop, "webAllowOthers", Constants.DEFAULT_HTTP_ALLOW_OTHERS);
for (int i = 0; args != null && i < args.length; i++) { for (int i = 0; args != null && i < args.length; i++) {
String a = args[i]; String a = args[i];
if ("-webPort".equals(a)) { if ("-webPort".equals(a)) {
...@@ -437,7 +437,7 @@ public class WebServer implements Service { ...@@ -437,7 +437,7 @@ public class WebServer implements Service {
private Properties loadProperties() { private Properties loadProperties() {
String fileName = getPropertiesFileName(); String fileName = getPropertiesFileName();
try { try {
return FileUtils.loadProperties(fileName); return SortedProperties.loadProperties(fileName);
} catch (IOException e) { } catch (IOException e) {
// TODO log exception // TODO log exception
return new Properties(); return new Properties();
......
...@@ -24,7 +24,6 @@ import org.h2.message.Trace; ...@@ -24,7 +24,6 @@ import org.h2.message.Trace;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
import org.h2.store.fs.FileSystem; import org.h2.store.fs.FileSystem;
import org.h2.util.ByteUtils; import org.h2.util.ByteUtils;
import org.h2.util.FileUtils;
import org.h2.util.NetUtils; import org.h2.util.NetUtils;
import org.h2.util.RandomUtils; import org.h2.util.RandomUtils;
import org.h2.util.SortedProperties; import org.h2.util.SortedProperties;
...@@ -169,7 +168,7 @@ public class FileLock { ...@@ -169,7 +168,7 @@ public class FileLock {
private Properties load() throws SQLException { private Properties load() throws SQLException {
try { try {
Properties p2 = FileUtils.loadProperties(fileName); Properties p2 = SortedProperties.loadProperties(fileName);
trace.debug("load " + p2); trace.debug("load " + p2);
return p2; return p2;
} catch (IOException e) { } catch (IOException e) {
......
...@@ -107,12 +107,14 @@ public abstract class FileSystem { ...@@ -107,12 +107,14 @@ public abstract class FileSystem {
/** /**
* Create a new temporary file. * Create a new temporary file.
* *
* @param prefix the file name prefix * @param prefix the prefix of the file name (including directory name if
* @param suffix the file name suffix * required)
* @param deleteOnExit if the file should be deleted when the system exists * @param suffix the suffix
* @param inTempDir if the file should be stored in the temp file * @param deleteOnExit if the file should be deleted when the virtual
* @return the file name * machine exists
* @param inTempDir if the file should be stored in the temporary directory
* @return the name of the created file
*/ */
public abstract String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException; public abstract String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException;
......
...@@ -118,8 +118,8 @@ public class ConvertTraceFile extends Tool { ...@@ -118,8 +118,8 @@ public class ConvertTraceFile extends Tool {
*/ */
private void convertFile(String traceFileName, String javaClassName, String script) throws IOException, SQLException { private void convertFile(String traceFileName, String javaClassName, String script) throws IOException, SQLException {
LineNumberReader reader = new LineNumberReader(IOUtils.getReader(FileUtils.openFileInputStream(traceFileName))); LineNumberReader reader = new LineNumberReader(IOUtils.getReader(FileUtils.openFileInputStream(traceFileName)));
PrintWriter javaWriter = new PrintWriter(FileUtils.openFileWriter(javaClassName + ".java", false)); PrintWriter javaWriter = new PrintWriter(IOUtils.getWriter(FileUtils.openFileOutputStream(javaClassName + ".java", false)));
PrintWriter scriptWriter = new PrintWriter(FileUtils.openFileWriter(script, false)); PrintWriter scriptWriter = new PrintWriter(IOUtils.getWriter(FileUtils.openFileOutputStream(script, false)));
javaWriter.println("import java.io.*;"); javaWriter.println("import java.io.*;");
javaWriter.println("import java.sql.*;"); javaWriter.println("import java.sql.*;");
javaWriter.println("import java.math.*;"); javaWriter.println("import java.math.*;");
......
...@@ -8,7 +8,6 @@ package org.h2.tools; ...@@ -8,7 +8,6 @@ package org.h2.tools;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.FileInputStream; import java.io.FileInputStream;
...@@ -318,7 +317,7 @@ public class Recover extends Tool implements DataHandler { ...@@ -318,7 +317,7 @@ public class Recover extends Tool implements DataHandler {
fileName = fileName.substring(0, fileName.length() - 3); fileName = fileName.substring(0, fileName.length() - 3);
String outputFile = fileName + suffix; String outputFile = fileName + suffix;
trace("Created file: " + outputFile); trace("Created file: " + outputFile);
return new PrintWriter(new BufferedWriter(FileUtils.openFileWriter(outputFile, false))); return new PrintWriter(IOUtils.getWriter(FileUtils.openFileOutputStream(outputFile, false)));
} }
private void writeDataError(PrintWriter writer, String error, byte[] data, int dumpBlocks) { private void writeDataError(PrintWriter writer, String error, byte[] data, int dumpBlocks) {
......
...@@ -249,8 +249,12 @@ public class RunScript extends Tool { ...@@ -249,8 +249,12 @@ public class RunScript extends Tool {
} }
if (checkResults) { if (checkResults) {
String expected = r.readStatement() + ";"; String expected = r.readStatement() + ";";
expected = StringUtils.replaceAll(expected, "\r\n", "\n");
expected = StringUtils.replaceAll(expected, "\r", "\n");
if (!expected.equals(result)) { if (!expected.equals(result)) {
throw new SQLException("Unexpected output, got:\n" + result + "\nExpected:\n" + expected); expected = StringUtils.replaceAll(expected, " ", "+");
result = StringUtils.replaceAll(result, " ", "+");
throw new SQLException("Unexpected output for:\n" + sql.trim() + "\nGot:\n" + result + "\nExpected:\n" + expected);
} }
} }
......
...@@ -146,7 +146,7 @@ public class Script extends Tool { ...@@ -146,7 +146,7 @@ public class Script extends Tool {
org.h2.Driver.load(); org.h2.Driver.load();
conn = DriverManager.getConnection(url, user, password); conn = DriverManager.getConnection(url, user, password);
stat = conn.createStatement(); stat = conn.createStatement();
fileWriter = FileUtils.openFileWriter(fileName, false); fileWriter = IOUtils.getWriter(FileUtils.openFileOutputStream(fileName, false));
PrintWriter writer = new PrintWriter(fileWriter); PrintWriter writer = new PrintWriter(fileWriter);
ResultSet rs = stat.executeQuery("SCRIPT"); ResultSet rs = stat.executeQuery("SCRIPT");
while (rs.next()) { while (rs.next()) {
......
...@@ -27,6 +27,7 @@ import org.h2.util.ClassUtils; ...@@ -27,6 +27,7 @@ import org.h2.util.ClassUtils;
import org.h2.util.FileUtils; import org.h2.util.FileUtils;
import org.h2.util.JdbcDriverUtils; import org.h2.util.JdbcDriverUtils;
import org.h2.util.JdbcUtils; import org.h2.util.JdbcUtils;
import org.h2.util.SortedProperties;
/** /**
* Interactive command line tool to access a database using JDBC. * Interactive command line tool to access a database using JDBC.
...@@ -273,7 +274,7 @@ public class Shell { ...@@ -273,7 +274,7 @@ public class Shell {
String user = "sa"; String user = "sa";
String driver = null; String driver = null;
try { try {
Properties prop = FileUtils.loadProperties(propertiesFileName); Properties prop = SortedProperties.loadProperties(propertiesFileName);
String data = null; String data = null;
boolean found = false; boolean found = false;
for (int i = 0;; i++) { for (int i = 0;; i++) {
......
...@@ -22,14 +22,33 @@ public class ByteUtils { ...@@ -22,14 +22,33 @@ public class ByteUtils {
// utility class // utility class
} }
public static int readInt(byte[] buff, int pos) { private static int readInt(byte[] buff, int pos) {
return (buff[pos++] << 24) + ((buff[pos++] & 0xff) << 16) + ((buff[pos++] & 0xff) << 8) + (buff[pos] & 0xff); return (buff[pos++] << 24) + ((buff[pos++] & 0xff) << 16) + ((buff[pos++] & 0xff) << 8) + (buff[pos] & 0xff);
} }
/**
* Read a long value from the byte array at the given position. The most
* significant byte is read first.
*
* @param buff the byte array
* @param pos the position
* @return the value
*/
public static long readLong(byte[] buff, int pos) { public static long readLong(byte[] buff, int pos) {
return ((long) (readInt(buff, pos)) << 32) + (readInt(buff, pos + 4) & 0xffffffffL); return ((long) (readInt(buff, pos)) << 32) + (readInt(buff, pos + 4) & 0xffffffffL);
} }
/**
* Calculate the index of the first occurrence of the pattern in the byte
* array, starting with the given index. This methods returns -1 if the
* pattern has not been found, and the start position if the pattern is
* empty.
*
* @param bytes the byte array
* @param pattern the pattern
* @param start the start index from where to search
* @return the index
*/
public static int indexOf(byte[] bytes, byte[] pattern, int start) { public static int indexOf(byte[] bytes, byte[] pattern, int start) {
if (pattern.length == 0) { if (pattern.length == 0) {
return start; return start;
...@@ -49,6 +68,12 @@ public class ByteUtils { ...@@ -49,6 +68,12 @@ public class ByteUtils {
return -1; return -1;
} }
/**
* Convert a hex encoded string to a byte array.
*
* @param s the hex encoded string
* @return the byte array
*/
public static byte[] convertStringToBytes(String s) throws SQLException { public static byte[] convertStringToBytes(String s) throws SQLException {
int len = s.length(); int len = s.length();
if (len % 2 != 0) { if (len % 2 != 0) {
...@@ -75,6 +100,12 @@ public class ByteUtils { ...@@ -75,6 +100,12 @@ public class ByteUtils {
} }
} }
/**
* Calculate the hash code of the given byte array.
*
* @param value the byte array
* @return the hash code
*/
public static int getByteArrayHash(byte[] value) { public static int getByteArrayHash(byte[] value) {
int h = 1; int h = 1;
for (int i = 0; i < value.length;) { for (int i = 0; i < value.length;) {
...@@ -83,10 +114,23 @@ public class ByteUtils { ...@@ -83,10 +114,23 @@ public class ByteUtils {
return h; return h;
} }
/**
* Convert a byte array to a hex encoded string.
*
* @param value the byte array
* @return the hex encoded string
*/
public static String convertBytesToString(byte[] value) { public static String convertBytesToString(byte[] value) {
return convertBytesToString(value, value.length); return convertBytesToString(value, value.length);
} }
/**
* Convert a byte array to a hex encoded string.
*
* @param value the byte array
* @param len the number of bytes to encode
* @return the hex encoded string
*/
public static String convertBytesToString(byte[] value, int len) { public static String convertBytesToString(byte[] value, int len) {
char[] buff = new char[len + len]; char[] buff = new char[len + len];
char[] hex = HEX; char[] hex = HEX;
...@@ -98,6 +142,15 @@ public class ByteUtils { ...@@ -98,6 +142,15 @@ public class ByteUtils {
return new String(buff); return new String(buff);
} }
/**
* Compare two byte arrays. This method will always loop over all bytes and
* doesn't use conditional operations in the loop to make sure an attacker
* can not use a timing attack when trying out passwords.
*
* @param test the first array
* @param good the second array
* @return true if both byte arrays contain the same bytes
*/
public static boolean compareSecure(byte[] test, byte[] good) { public static boolean compareSecure(byte[] test, byte[] good) {
if ((test == null) || (good == null)) { if ((test == null) || (good == null)) {
return (test == null) && (good == null); return (test == null) && (good == null);
...@@ -108,24 +161,36 @@ public class ByteUtils { ...@@ -108,24 +161,36 @@ public class ByteUtils {
if (test.length == 0) { if (test.length == 0) {
return true; return true;
} }
// silly loop: this should help a little against timing attacks // don't use conditional operations inside the loop
boolean correct = true, correct2 = false; int bits = 0;
for (int i = 0; i < good.length; i++) { for (int i = 0; i < good.length; i++) {
if (test[i] != good[i]) { // this will never reset any bits
correct = false; bits |= test[i] ^ good[i];
} else {
correct2 = true;
}
} }
return correct && correct2; return bits == 0;
} }
/**
* Set all elements of the array to zero.
*
* @param buff the byte array
*/
public static void clear(byte[] buff) { public static void clear(byte[] buff) {
for (int i = 0; i < buff.length; i++) { for (int i = 0; i < buff.length; i++) {
buff[i] = 0; buff[i] = 0;
} }
} }
/**
* Compare the contents of two byte arrays. If the content or length of the
* first array is smaller than the second array, -1 is returned. If the
* content or length of the second array is smaller than the first array, 1
* is returned. If the contents and lengths are the same, 0 is returned.
*
* @param data1 the first byte array (must not be null)
* @param data2 the second byte array (must not be null)
* @return the result of the comparison (-1, 1 or 0)
*/
public static int compareNotNull(byte[] data1, byte[] data2) { public static int compareNotNull(byte[] data1, byte[] data2) {
int len = Math.min(data1.length, data2.length); int len = Math.min(data1.length, data2.length);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
...@@ -139,22 +204,14 @@ public class ByteUtils { ...@@ -139,22 +204,14 @@ public class ByteUtils {
return c == 0 ? 0 : (c < 0 ? -1 : 1); return c == 0 ? 0 : (c < 0 ? -1 : 1);
} }
public static String convertToBinString(byte[] buff) { /**
char[] chars = new char[buff.length]; * Copy the contents of the source array to the target array. If the size if
for (int i = 0; i < buff.length; i++) { * the target array is too small, a larger array is created.
chars[i] = (char) (buff[i] & 0xff); *
} * @param source the source array
return new String(chars); * @param target the target array
} * @return the target array or a new one if the target array was too small
*/
public static byte[] convertBinStringToBytes(String data) {
byte[] buff = new byte[data.length()];
for (int i = 0; i < data.length(); i++) {
buff[i] = (byte) (data.charAt(i) & 0xff);
}
return buff;
}
public static byte[] copy(byte[] source, byte[] target) { public static byte[] copy(byte[] source, byte[] target) {
int len = source.length; int len = source.length;
if (len > target.length) { if (len > target.length) {
...@@ -164,6 +221,13 @@ public class ByteUtils { ...@@ -164,6 +221,13 @@ public class ByteUtils {
return target; return target;
} }
/**
* Create a new byte array and copy all the data. If the size of the byte
* array is zero, the same array is returned.
*
* @param b the byte array (may not be null)
* @return a new byte array
*/
public static byte[] cloneByteArray(byte[] b) { public static byte[] cloneByteArray(byte[] b) {
int len = b.length; int len = b.length;
if (len == 0) { if (len == 0) {
......
...@@ -35,6 +35,11 @@ public abstract class CacheObject { ...@@ -35,6 +35,11 @@ public abstract class CacheObject {
*/ */
public abstract boolean canRemove(); public abstract boolean canRemove();
/**
* Order the given list of cache objects by position.
*
* @param recordList the list of cache objects
*/
public static void sort(ObjectArray recordList) { public static void sort(ObjectArray recordList) {
recordList.sort(new Comparator() { recordList.sort(new Comparator() {
public int compare(Object a, Object b) { public int compare(Object a, Object b) {
...@@ -64,6 +69,12 @@ public abstract class CacheObject { ...@@ -64,6 +69,12 @@ public abstract class CacheObject {
return pos; return pos;
} }
/**
* Check if this cache object has been changed and thus needs to be written
* back to the storage.
*
* @return if it has been changed
*/
public boolean isChanged() { public boolean isChanged() {
return changed; return changed;
} }
...@@ -72,6 +83,11 @@ public abstract class CacheObject { ...@@ -72,6 +83,11 @@ public abstract class CacheObject {
changed = b; changed = b;
} }
/**
* Check if this cache object can be removed from the cache.
*
* @return if it can be removed
*/
public boolean isPinned() { public boolean isPinned() {
return false; return false;
} }
......
...@@ -48,10 +48,24 @@ public class ClassUtils { ...@@ -48,10 +48,24 @@ public class ClassUtils {
// utility class // utility class
} }
/**
* Load a class without performing access rights checking.
*
* @param className the name of the class
* @return the class object
*/
public static Class loadSystemClass(String className) throws ClassNotFoundException { public static Class loadSystemClass(String className) throws ClassNotFoundException {
return Class.forName(className); return Class.forName(className);
} }
/**
* Load a class, but check if it is allowed to load this class first. To
* perform access rights checking, the system property h2.allowedClasses
* needs to be set to a list of class file name prefixes.
*
* @param className the name of the class
* @return the class object
*/
public static Class loadUserClass(String className) throws SQLException { public static Class loadUserClass(String className) throws SQLException {
if (!ALLOW_ALL && !ALLOWED_CLASS_NAMES.contains(className)) { if (!ALLOW_ALL && !ALLOWED_CLASS_NAMES.contains(className)) {
boolean allowed = false; boolean allowed = false;
......
...@@ -31,6 +31,13 @@ public class DateTimeUtils { ...@@ -31,6 +31,13 @@ public class DateTimeUtils {
// utility class // utility class
} }
/**
* Convert the timestamp to the specified time zone.
*
* @param x the timestamp
* @param calendar the calendar
* @return the timestamp using the correct time zone
*/
public static Timestamp convertTimestampToCalendar(Timestamp x, Calendar calendar) throws SQLException { public static Timestamp convertTimestampToCalendar(Timestamp x, Calendar calendar) throws SQLException {
if (x != null) { if (x != null) {
Timestamp y = new Timestamp(getLocalTime(x, calendar)); Timestamp y = new Timestamp(getLocalTime(x, calendar));
...@@ -41,6 +48,12 @@ public class DateTimeUtils { ...@@ -41,6 +48,12 @@ public class DateTimeUtils {
return x; return x;
} }
/**
* Clone a time object and reset the day to 1970-01-01.
*
* @param value the time value
* @return the time value without the date component
*/
public static Time cloneAndNormalizeTime(Time value) { public static Time cloneAndNormalizeTime(Time value) {
Calendar cal = CALENDAR; Calendar cal = CALENDAR;
long time; long time;
...@@ -52,6 +65,13 @@ public class DateTimeUtils { ...@@ -52,6 +65,13 @@ public class DateTimeUtils {
return new Time(time); return new Time(time);
} }
/**
* Clone a date object and reset the hour, minutes, seconds, and
* milliseconds to zero.
*
* @param value the date value
* @return the date value at midnight
*/
public static Date cloneAndNormalizeDate(Date value) { public static Date cloneAndNormalizeDate(Date value) {
Calendar cal = CALENDAR; Calendar cal = CALENDAR;
long time; long time;
...@@ -66,14 +86,35 @@ public class DateTimeUtils { ...@@ -66,14 +86,35 @@ public class DateTimeUtils {
return new Date(time); return new Date(time);
} }
/**
* Convert the date from the specified time zone to UTC.
*
* @param x the date
* @param source the calendar
* @return the date in UTC
*/
public static Value convertDateToUniversal(Date x, Calendar source) throws SQLException { public static Value convertDateToUniversal(Date x, Calendar source) throws SQLException {
return ValueDate.get(new Date(getUniversalTime(source, x))); return ValueDate.get(new Date(getUniversalTime(source, x)));
} }
/**
* Convert the time from the specified time zone to UTC.
*
* @param x the time
* @param source the calendar
* @return the time in UTC
*/
public static Value convertTimeToUniversal(Time x, Calendar source) throws SQLException { public static Value convertTimeToUniversal(Time x, Calendar source) throws SQLException {
return ValueTime.get(new Time(getUniversalTime(source, x))); return ValueTime.get(new Time(getUniversalTime(source, x)));
} }
/**
* Convert the timestamp from the specified time zone to UTC.
*
* @param x the time
* @param source the calendar
* @return the timestamp in UTC
*/
public static Value convertTimestampToUniversal(Timestamp x, Calendar source) throws SQLException { public static Value convertTimestampToUniversal(Timestamp x, Calendar source) throws SQLException {
Timestamp y = new Timestamp(getUniversalTime(source, x)); Timestamp y = new Timestamp(getUniversalTime(source, x));
// fix the nano seconds // fix the nano seconds
...@@ -113,14 +154,38 @@ public class DateTimeUtils { ...@@ -113,14 +154,38 @@ public class DateTimeUtils {
to.set(Calendar.MILLISECOND, from.get(Calendar.MILLISECOND)); to.set(Calendar.MILLISECOND, from.get(Calendar.MILLISECOND));
} }
/**
* Convert the date to the specified time zone.
*
* @param x the date
* @param calendar the calendar
* @return the date using the correct time zone
*/
public static Date convertDateToCalendar(Date x, Calendar calendar) throws SQLException { public static Date convertDateToCalendar(Date x, Calendar calendar) throws SQLException {
return x == null ? null : new Date(getLocalTime(x, calendar)); return x == null ? null : new Date(getLocalTime(x, calendar));
} }
/**
* Convert the time to the specified time zone.
*
* @param x the time
* @param calendar the calendar
* @return the time using the correct time zone
*/
public static Time convertTimeToCalendar(Time x, Calendar calendar) throws SQLException { public static Time convertTimeToCalendar(Time x, Calendar calendar) throws SQLException {
return x == null ? null : new Time(getLocalTime(x, calendar)); return x == null ? null : new Time(getLocalTime(x, calendar));
} }
/**
* Parse a date, time or timestamp value. This method supports the format
* +/-year-month-day hour:minute:seconds.fractional and an optional timezone
* part.
*
* @param original the original string
* @param type the value type (Value.TIME, TIMESTAMP, or DATE)
* @param errorCode the error code to use if an error occurs
* @return the date object
*/
public static java.util.Date parseDateTime(String original, int type, int errorCode) throws SQLException { public static java.util.Date parseDateTime(String original, int type, int errorCode) throws SQLException {
String s = original; String s = original;
if (s == null) { if (s == null) {
...@@ -251,6 +316,14 @@ public class DateTimeUtils { ...@@ -251,6 +316,14 @@ public class DateTimeUtils {
return c.getTime().getTime(); return c.getTime().getTime();
} }
/**
* Get the specified field of a date, however with years normalized to
* positive or negative, and month starting with 1.
*
* @param d the date
* @param field the field type
* @return the value
*/
public static int getDatePart(java.util.Date d, int field) { public static int getDatePart(java.util.Date d, int field) {
Calendar c = Calendar.getInstance(); Calendar c = Calendar.getInstance();
c.setTime(d); c.setTime(d);
......
...@@ -6,21 +6,14 @@ ...@@ -6,21 +6,14 @@
*/ */
package org.h2.util; package org.h2.util;
import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Properties;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.message.Message;
import org.h2.message.TraceSystem;
import org.h2.store.fs.FileSystem; import org.h2.store.fs.FileSystem;
/** /**
...@@ -32,6 +25,12 @@ public class FileUtils { ...@@ -32,6 +25,12 @@ public class FileUtils {
// utility class // utility class
} }
/**
* Change the length of the file.
*
* @param file the random access file
* @param newLength the new length
*/
public static void setLength(RandomAccessFile file, long newLength) throws IOException { public static void setLength(RandomAccessFile file, long newLength) throws IOException {
try { try {
trace("setLength", null, file); trace("setLength", null, file);
...@@ -56,39 +55,12 @@ public class FileUtils { ...@@ -56,39 +55,12 @@ public class FileUtils {
} }
} }
public static synchronized Properties loadProperties(String fileName) throws IOException { /**
Properties prop = new SortedProperties(); * Get the absolute file path of a file in the user home directory.
if (exists(fileName)) { *
InputStream in = openFileInputStream(fileName); * @param fileName
try { * @return the absolute path
prop.load(in); */
} finally {
in.close();
}
}
return prop;
}
public static boolean getBooleanProperty(Properties prop, String key, boolean def) {
String value = prop.getProperty(key, ""+def);
try {
return Boolean.valueOf(value).booleanValue();
} catch (Exception e) {
TraceSystem.traceThrowable(e);
return def;
}
}
public static int getIntProperty(Properties prop, String key, int def) {
String value = prop.getProperty(key, ""+def);
try {
return MathUtils.decodeInt(value);
} catch (Exception e) {
TraceSystem.traceThrowable(e);
return def;
}
}
public static String getFileInUserHome(String fileName) { public static String getFileInUserHome(String fileName) {
String userDir = SysProperties.USER_HOME; String userDir = SysProperties.USER_HOME;
if (userDir == null) { if (userDir == null) {
...@@ -98,32 +70,68 @@ public class FileUtils { ...@@ -98,32 +70,68 @@ public class FileUtils {
return file.getAbsolutePath(); return file.getAbsolutePath();
} }
public static void trace(String method, String fileName, Object o) { static void trace(String method, String fileName, Object o) {
if (SysProperties.TRACE_IO) { if (SysProperties.TRACE_IO) {
System.out.println("FileUtils." + method + " " + fileName + " " + o); System.out.println("FileUtils." + method + " " + fileName + " " + o);
} }
} }
/**
* Get the file name (without directory part).
*
* @param name the directory and file name
* @return just the file name
*/
public static String getFileName(String name) throws SQLException { public static String getFileName(String name) throws SQLException {
return FileSystem.getInstance(name).getFileName(name); return FileSystem.getInstance(name).getFileName(name);
} }
/**
* Normalize a file name.
*
* @param fileName the file name
* @return the normalized file name
*/
public static String normalize(String fileName) throws SQLException { public static String normalize(String fileName) throws SQLException {
return FileSystem.getInstance(fileName).normalize(fileName); return FileSystem.getInstance(fileName).normalize(fileName);
} }
/**
* Try to delete a file.
*
* @param fileName the file name
* @return true if it could be deleted
*/
public static void tryDelete(String fileName) { public static void tryDelete(String fileName) {
FileSystem.getInstance(fileName).tryDelete(fileName); FileSystem.getInstance(fileName).tryDelete(fileName);
} }
/**
* Check if a file is read-only.
*
* @param fileName the file name
* @return if it is read only
*/
public static boolean isReadOnly(String fileName) { public static boolean isReadOnly(String fileName) {
return FileSystem.getInstance(fileName).isReadOnly(fileName); return FileSystem.getInstance(fileName).isReadOnly(fileName);
} }
/**
* Checks if a file exists.
*
* @param fileName the file name
* @return true if it exists
*/
public static boolean exists(String fileName) { public static boolean exists(String fileName) {
return FileSystem.getInstance(fileName).exists(fileName); return FileSystem.getInstance(fileName).exists(fileName);
} }
/**
* Get the length of a file.
*
* @param fileName the file name
* @return the length in bytes
*/
public static long length(String fileName) { public static long length(String fileName) {
return FileSystem.getInstance(fileName).length(fileName); return FileSystem.getInstance(fileName).length(fileName);
} }
...@@ -144,59 +152,124 @@ public class FileUtils { ...@@ -144,59 +152,124 @@ public class FileUtils {
return FileSystem.getInstance(prefix).createTempFile(prefix, suffix, deleteOnExit, inTempDir); return FileSystem.getInstance(prefix).createTempFile(prefix, suffix, deleteOnExit, inTempDir);
} }
/**
* Get the parent directory of a file or directory.
*
* @param fileName the file or directory name
* @return the parent directory name
*/
public static String getParent(String fileName) { public static String getParent(String fileName) {
return FileSystem.getInstance(fileName).getParent(fileName); return FileSystem.getInstance(fileName).getParent(fileName);
} }
/**
* List the files in the given directory.
*
* @param path the directory
* @return the list of fully qualified file names
*/
public static String[] listFiles(String path) throws SQLException { public static String[] listFiles(String path) throws SQLException {
return FileSystem.getInstance(path).listFiles(path); return FileSystem.getInstance(path).listFiles(path);
} }
/**
* Check if it is a file or a directory.
*
* @param fileName the file or directory name
* @return true if it is a directory
*/
public static boolean isDirectory(String fileName) { public static boolean isDirectory(String fileName) {
return FileSystem.getInstance(fileName).isDirectory(fileName); return FileSystem.getInstance(fileName).isDirectory(fileName);
} }
/**
* Check if the file name includes a path.
*
* @param fileName the file name
* @return if the file name is absolute
*/
public static boolean isAbsolute(String fileName) { public static boolean isAbsolute(String fileName) {
return FileSystem.getInstance(fileName).isAbsolute(fileName); return FileSystem.getInstance(fileName).isAbsolute(fileName);
} }
/**
* Get the absolute file name.
*
* @param fileName the file name
* @return the absolute file name
*/
public static String getAbsolutePath(String fileName) { public static String getAbsolutePath(String fileName) {
return FileSystem.getInstance(fileName).getAbsolutePath(fileName); return FileSystem.getInstance(fileName).getAbsolutePath(fileName);
} }
public static Writer openFileWriter(String fileName, boolean append) throws SQLException { /**
OutputStream out = FileSystem.getInstance(fileName).openFileOutputStream(fileName, append); * Check if a file starts with a given prefix.
try { *
return new BufferedWriter(new OutputStreamWriter(out, "UTF-8")); * @param fileName the complete file name
} catch (UnsupportedEncodingException e) { * @param prefix the prefix
throw Message.convert(e); * @return true if it starts with the prefix
} */
}
public static boolean fileStartsWith(String fileName, String prefix) { public static boolean fileStartsWith(String fileName, String prefix) {
return FileSystem.getInstance(fileName).fileStartsWith(fileName, prefix); return FileSystem.getInstance(fileName).fileStartsWith(fileName, prefix);
} }
/**
* Create an input stream to read from the file.
*
* @param fileName the file name
* @return the input stream
*/
public static InputStream openFileInputStream(String fileName) throws IOException { public static InputStream openFileInputStream(String fileName) throws IOException {
return FileSystem.getInstance(fileName).openFileInputStream(fileName); return FileSystem.getInstance(fileName).openFileInputStream(fileName);
} }
/**
* Create an output stream to write into the file.
*
* @param fileName the file name
* @param append if true, the file will grow, if false, the file will be
* truncated first
* @return the output stream
*/
public static OutputStream openFileOutputStream(String fileName, boolean append) throws SQLException { public static OutputStream openFileOutputStream(String fileName, boolean append) throws SQLException {
return FileSystem.getInstance(fileName).openFileOutputStream(fileName, append); return FileSystem.getInstance(fileName).openFileOutputStream(fileName, append);
} }
/**
* Rename a file if this is allowed.
*
* @param oldName the old fully qualified file name
* @param newName the new fully qualified file name
* @throws SQLException
*/
public static void rename(String oldName, String newName) throws SQLException { public static void rename(String oldName, String newName) throws SQLException {
FileSystem.getInstance(oldName).rename(oldName, newName); FileSystem.getInstance(oldName).rename(oldName, newName);
} }
/**
* Create all required directories that are required for this file.
*
* @param fileName the file name (not directory name)
*/
public static void createDirs(String fileName) throws SQLException { public static void createDirs(String fileName) throws SQLException {
FileSystem.getInstance(fileName).createDirs(fileName); FileSystem.getInstance(fileName).createDirs(fileName);
} }
/**
* Delete a file.
*
* @param fileName the file name
*/
public static void delete(String fileName) throws SQLException { public static void delete(String fileName) throws SQLException {
FileSystem.getInstance(fileName).delete(fileName); FileSystem.getInstance(fileName).delete(fileName);
} }
/**
* Get the last modified date of a file
*
* @param fileName the file name
* @return the last modified date
*/
public static long getLastModified(String fileName) { public static long getLastModified(String fileName) {
return FileSystem.getInstance(fileName).getLastModified(fileName); return FileSystem.getInstance(fileName).getLastModified(fileName);
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
package org.h2.util; package org.h2.util;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.EOFException; import java.io.EOFException;
...@@ -14,6 +15,7 @@ import java.io.IOException; ...@@ -14,6 +15,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
...@@ -351,6 +353,21 @@ public class IOUtils { ...@@ -351,6 +353,21 @@ public class IOUtils {
throw Message.convert(e); throw Message.convert(e);
} }
} }
/**
* Create a writer to write to an output stream using the UTF-8 format. If
* the output stream is null, this method returns null.
*
* @param out the output stream or null
* @return the writer
*/
public static Writer getWriter(OutputStream out) throws SQLException {
try {
return out == null ? null : new BufferedWriter(new OutputStreamWriter(out, Constants.UTF8));
} catch (UnsupportedEncodingException e) {
throw Message.convert(e);
}
}
/** /**
* Create an input stream to read from a string. The string is converted to * Create an input stream to read from a string. The string is converted to
......
...@@ -6,11 +6,15 @@ ...@@ -6,11 +6,15 @@
*/ */
package org.h2.util; package org.h2.util;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Properties; import java.util.Properties;
import java.util.Vector; import java.util.Vector;
import org.h2.message.TraceSystem;
/** /**
* Sorted properties file. * Sorted properties file.
* This implementation requires that store() internally calls keys(). * This implementation requires that store() internally calls keys().
...@@ -24,5 +28,60 @@ public class SortedProperties extends Properties { ...@@ -24,5 +28,60 @@ public class SortedProperties extends Properties {
Collections.sort(v); Collections.sort(v);
return v.elements(); return v.elements();
} }
/**
* Get a boolean property value from a properties object.
*
* @param prop the properties object
* @param key the key
* @param def the default value
* @return the value if set, or the default value if not
*/
public static boolean getBooleanProperty(Properties prop, String key, boolean def) {
String value = prop.getProperty(key, ""+def);
try {
return Boolean.valueOf(value).booleanValue();
} catch (Exception e) {
TraceSystem.traceThrowable(e);
return def;
}
}
/**
* Get an int property value from a properties object.
*
* @param prop the properties object
* @param key the key
* @param def the default value
* @return the value if set, or the default value if not
*/
public static int getIntProperty(Properties prop, String key, int def) {
String value = prop.getProperty(key, ""+def);
try {
return MathUtils.decodeInt(value);
} catch (Exception e) {
TraceSystem.traceThrowable(e);
return def;
}
}
/**
* Load a properties object from a file.
*
* @param fileName the name of the properties file
* @return the properties object
*/
public static synchronized SortedProperties loadProperties(String fileName) throws IOException {
SortedProperties prop = new SortedProperties();
if (FileUtils.exists(fileName)) {
InputStream in = FileUtils.openFileInputStream(fileName);
try {
prop.load(in);
} finally {
in.close();
}
}
return prop;
}
} }
...@@ -190,4 +190,4 @@ EXPLAIN SELECT VALUE FROM TEST ORDER BY VALUE DESC LIMIT 10; ...@@ -190,4 +190,4 @@ EXPLAIN SELECT VALUE FROM TEST ORDER BY VALUE DESC LIMIT 10;
--> LIMIT 10 --> LIMIT 10
; ;
DROP TABLE TEST; DROP TABLE TEST;
\ No newline at end of file
...@@ -169,10 +169,27 @@ java org.h2.test.TestAll timer ...@@ -169,10 +169,27 @@ java org.h2.test.TestAll timer
/* /*
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)
measure and improve performance of ObjectArray.toArray() measure and improve performance of ObjectArray.toArray()
test & document optimizations.sql
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
......
...@@ -321,10 +321,10 @@ public abstract class TestBase { ...@@ -321,10 +321,10 @@ public abstract class TestBase {
int al = expected.length(); int al = expected.length();
int bl = actual.length(); int bl = actual.length();
if (al > 100) { if (al > 100) {
expected = expected.substring(0, 100); expected = expected.substring(0, 400);
} }
if (bl > 100) { if (bl > 100) {
actual = actual.substring(0, 100); actual = actual.substring(0, 400);
} }
fail("Expected: " + expected + " (" + al + ") actual: " + actual + " (" + bl + ")"); fail("Expected: " + expected + " (" + al + ") actual: " + actual + " (" + bl + ")");
} }
......
...@@ -8,6 +8,7 @@ package org.h2.test.unit; ...@@ -8,6 +8,7 @@ package org.h2.test.unit;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import org.h2.test.TestBase; import org.h2.test.TestBase;
...@@ -20,6 +21,11 @@ import org.h2.util.StringUtils; ...@@ -20,6 +21,11 @@ import org.h2.util.StringUtils;
public class TestSampleApps extends TestBase { public class TestSampleApps extends TestBase {
public void test() throws Exception { public void test() throws Exception {
deleteDb("optimizations");
String url = "jdbc:h2:" + baseDir + "/optimizations";
testApp(org.h2.tools.RunScript.class, new String[] { "-url", url, "-user", "sa", "-password", "sa", "-script",
"src/test/org/h2/samples/optimizations.sql", "-checkResults" }, "");
testApp(org.h2.samples.Compact.class, null, "Compacting...\nDone."); testApp(org.h2.samples.Compact.class, null, "Compacting...\nDone.");
testApp(org.h2.samples.CsvSample.class, null, "NAME: Bob Meier\n" + "EMAIL: bob.meier@abcde.abc\n" testApp(org.h2.samples.CsvSample.class, null, "NAME: Bob Meier\n" + "EMAIL: bob.meier@abcde.abc\n"
+ "PHONE: +41123456789\n\n" + "NAME: John Jones\n" + "EMAIL: john.jones@abcde.abc\n" + "PHONE: +41123456789\n\n" + "NAME: John Jones\n" + "EMAIL: john.jones@abcde.abc\n"
...@@ -51,6 +57,8 @@ public class TestSampleApps extends TestBase { ...@@ -51,6 +57,8 @@ public class TestSampleApps extends TestBase {
System.setErr(out); System.setErr(out);
try { try {
m.invoke(null, new Object[] { args }); m.invoke(null, new Object[] { args });
} catch (InvocationTargetException e) {
TestBase.logError("error", e.getTargetException());
} catch (Throwable e) { } catch (Throwable e) {
TestBase.logError("error", e); TestBase.logError("error", e);
} }
......
...@@ -519,4 +519,4 @@ indicate timezone unmounting beep ignoring gary tong extending respective ...@@ -519,4 +519,4 @@ indicate timezone unmounting beep ignoring gary tong extending respective
overloaded decide clash involve verification dining recursively originating overloaded decide clash involve verification dining recursively originating
philosophers technologies modeling federation enterprise semantic deductive 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 monitor benefit performing conditional significant
\ No newline at end of file \ No newline at end of file
...@@ -30,7 +30,6 @@ import java.util.Map.Entry; ...@@ -30,7 +30,6 @@ import java.util.Map.Entry;
import org.h2.build.doc.XMLParser; import org.h2.build.doc.XMLParser;
import org.h2.server.web.PageParser; import org.h2.server.web.PageParser;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.SortedProperties; import org.h2.util.SortedProperties;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -94,12 +93,12 @@ public class PrepareTranslation { ...@@ -94,12 +93,12 @@ public class PrepareTranslation {
new File(targetDir).mkdirs(); new File(targetDir).mkdirs();
// load the main 'translation' // load the main 'translation'
String propName = templateDir + "/_docs_" + MAIN_LANGUAGE + ".properties"; String propName = templateDir + "/_docs_" + MAIN_LANGUAGE + ".properties";
Properties prop = FileUtils.loadProperties(propName); Properties prop = SortedProperties.loadProperties(propName);
propName = templateDir + "/_docs_" + language + ".properties"; propName = templateDir + "/_docs_" + language + ".properties";
if (!(new File(propName)).exists()) { if (!(new File(propName)).exists()) {
throw new IOException("Translation not found: " + propName); throw new IOException("Translation not found: " + propName);
} }
Properties transProp = FileUtils.loadProperties(propName); Properties transProp = SortedProperties.loadProperties(propName);
for (Iterator it = transProp.keySet().iterator(); it.hasNext();) { for (Iterator it = transProp.keySet().iterator(); it.hasNext();) {
String key = (String) it.next(); String key = (String) it.next();
String t = transProp.getProperty(key); String t = transProp.getProperty(key);
...@@ -363,7 +362,7 @@ public class PrepareTranslation { ...@@ -363,7 +362,7 @@ public class PrepareTranslation {
} }
new File(target).mkdirs(); new File(target).mkdirs();
String propFileName = target + "/_docs_" + MAIN_LANGUAGE + ".properties"; String propFileName = target + "/_docs_" + MAIN_LANGUAGE + ".properties";
Properties old = FileUtils.loadProperties(propFileName); Properties old = SortedProperties.loadProperties(propFileName);
prop.putAll(old); prop.putAll(old);
PropertiesToUTF8.storeProperties(prop, propFileName); PropertiesToUTF8.storeProperties(prop, propFileName);
String t = template.toString(); String t = template.toString();
...@@ -416,8 +415,8 @@ public class PrepareTranslation { ...@@ -416,8 +415,8 @@ public class PrepareTranslation {
} }
} }
} }
Properties p = FileUtils.loadProperties(main.getAbsolutePath()); Properties p = SortedProperties.loadProperties(main.getAbsolutePath());
Properties base = FileUtils.loadProperties(baseDir + "/" + main.getName()); Properties base = SortedProperties.loadProperties(baseDir + "/" + main.getName());
PropertiesToUTF8.storeProperties(p, main.getAbsolutePath()); PropertiesToUTF8.storeProperties(p, main.getAbsolutePath());
for (int i = 0; i < translations.size(); i++) { for (int i = 0; i < translations.size(); i++) {
File trans = (File) translations.get(i); File trans = (File) translations.get(i);
...@@ -429,7 +428,7 @@ public class PrepareTranslation { ...@@ -429,7 +428,7 @@ public class PrepareTranslation {
} }
private void prepare(Properties main, Properties base, File trans, String language) throws IOException { private void prepare(Properties main, Properties base, File trans, String language) throws IOException {
Properties p = FileUtils.loadProperties(trans.getAbsolutePath()); Properties p = SortedProperties.loadProperties(trans.getAbsolutePath());
Properties oldTranslations = new Properties(); Properties oldTranslations = new Properties();
for (Iterator it = base.keySet().iterator(); it.hasNext();) { for (Iterator it = base.keySet().iterator(); it.hasNext();) {
String key = (String) it.next(); String key = (String) it.next();
......
...@@ -24,7 +24,6 @@ import java.util.Properties; ...@@ -24,7 +24,6 @@ import java.util.Properties;
import org.h2.build.code.CheckTextFiles; import org.h2.build.code.CheckTextFiles;
import org.h2.build.indexer.HtmlConverter; import org.h2.build.indexer.HtmlConverter;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.SortedProperties; import org.h2.util.SortedProperties;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -48,7 +47,7 @@ public class PropertiesToUTF8 { ...@@ -48,7 +47,7 @@ public class PropertiesToUTF8 {
if (!new File(source).exists()) { if (!new File(source).exists()) {
return; return;
} }
Properties prop = FileUtils.loadProperties(source); Properties prop = SortedProperties.loadProperties(source);
FileOutputStream out = new FileOutputStream(target); FileOutputStream out = new FileOutputStream(target);
PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8")); PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
// keys is sorted // keys is sorted
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论