提交 f4ee562c authored 作者: Thomas Mueller Graf's avatar Thomas Mueller Graf

Merge branch 'master' of https://github.com/h2database/h2database

......@@ -17,7 +17,7 @@ import org.h2.value.ValueNull;
* Represents a single SQL statements.
* It wraps a prepared statement.
*/
class CommandContainer extends Command {
public class CommandContainer extends Command {
private Prepared prepared;
private boolean readOnlyKnown;
......
......@@ -130,7 +130,7 @@ public class Delete extends Prepared {
condition = condition.optimize(session);
condition.createIndexConditions(session, tableFilter);
}
PlanItem item = tableFilter.getBestPlanItem(session, 1);
PlanItem item = tableFilter.getBestPlanItem(session, new TableFilter[]{tableFilter}, 0);
tableFilter.setPlanItem(item);
tableFilter.prepare();
}
......
......@@ -389,7 +389,7 @@ public class ScriptCommand extends ScriptBase {
}
private int generateInsertValues(int count, Table table) throws IOException {
PlanItem plan = table.getBestPlanItem(session, null, null, null);
PlanItem plan = table.getBestPlanItem(session, null, null, -1, null);
Index index = plan.getIndex();
Cursor cursor = index.find(session, null, null);
Column[] columns = table.getColumns();
......
......@@ -187,7 +187,7 @@ public class Update extends Prepared {
e.mapColumns(tableFilter, 0);
expressionMap.put(c, e.optimize(session));
}
PlanItem item = tableFilter.getBestPlanItem(session, 1);
PlanItem item = tableFilter.getBestPlanItem(session, new TableFilter[] {tableFilter}, 0);
tableFilter.setPlanItem(item);
tableFilter.prepare();
}
......
......@@ -5,8 +5,6 @@
*/
package org.h2.index;
import java.util.List;
import java.util.concurrent.Future;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.DbObject;
......@@ -152,12 +150,13 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
*
* @param masks the search mask
* @param rowCount the number of rows in the index
* @param filter the table filter
* @param filters all joined table filters
* @param filter the current table filter index
* @param sortOrder the sort order
* @return the estimated cost
*/
protected long getCostRangeIndex(int[] masks, long rowCount,
TableFilter filter, SortOrder sortOrder) {
TableFilter[] filters, int filter, SortOrder sortOrder) {
rowCount += Constants.COST_ROW_OFFSET;
long cost = rowCount;
long rows = rowCount;
......@@ -201,6 +200,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
boolean sortOrderMatches = true;
int coveringCount = 0;
int[] sortTypes = sortOrder.getSortTypes();
TableFilter tableFilter = filters == null ? null : filters[filter];
for (int i = 0, len = sortTypes.length; i < len; i++) {
if (i >= indexColumns.length) {
// we can still use this index if we are sorting by more
......@@ -209,7 +209,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
// more of the order by columns
break;
}
Column col = sortOrder.getColumn(i, filter);
Column col = sortOrder.getColumn(i, tableFilter);
if (col == null) {
sortOrderMatches = false;
break;
......@@ -427,13 +427,8 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
}
@Override
public int getPreferedLookupBatchSize() {
// No batched lookups supported by default.
return 0;
}
@Override
public List<Future<Cursor>> findBatched(TableFilter filter, List<SearchRow> firstLastPairs) {
throw DbException.throwInternalError("Must not be called if getPreferedLookupBatchSize() is 0.");
public IndexLookupBatch createLookupBatch(TableFilter filter) {
// Lookup batching is not supported.
return null;
}
}
......@@ -52,8 +52,8 @@ public class FunctionIndex extends BaseIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
if (masks != null) {
throw DbException.getUnsupportedException("ALIAS");
}
......
......@@ -113,8 +113,8 @@ public class HashIndex extends BaseIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
for (Column column : columns) {
int index = column.getColumnId();
int mask = masks[index];
......
......@@ -83,11 +83,12 @@ public interface Index extends SchemaObject {
* @param session the session
* @param masks per-column comparison bit masks, null means 'always false',
* see constants in IndexCondition
* @param filter the table filter
* @param filters all joined table filters
* @param filter the current table filter index
* @param sortOrder the sort order
* @return the estimated cost
*/
double getCost(Session session, int[] masks, TableFilter filter,
double getCost(Session session, int[] masks, TableFilter[] filters, int filter,
SortOrder sortOrder);
/**
......@@ -259,27 +260,11 @@ public interface Index extends SchemaObject {
void setSortedInsertMode(boolean sortedInsertMode);
/**
* If this index can do batched lookups, it may return it's preferred batch size,
* otherwise it must return 0.
* Creates new lookup batch. Note that returned {@link IndexLookupBatch} instance
* can be used multiple times.
*
* @return preferred batch size or 0 if lookup batching is not supported
* @see #findBatched(TableFilter, Collection)
* @param filter Table filter.
* @return Created batch or {@code null} if batched lookup is not supported by this index.
*/
int getPreferedLookupBatchSize();
/**
* Do batched lookup over the given collection of {@link SearchRow} pairs as in
* {@link #find(TableFilter, SearchRow, SearchRow)}.
* <br/><br/>
* Correct implementation must always return number of future cursors equal to
* {@code firstLastPairs.size() / 2}. Instead of {@link Future} containing empty
* {@link Cursor} it is possible to put {@code null} in result list.
*
* @param filter the table filter
* @param firstLastPairs List of batched search row pairs as in
* {@link #find(TableFilter, SearchRow, SearchRow)}, the collection will be reused by H2,
* thus it makes sense to defensively copy contents if needed.
* @return batched cursors for respective search row pairs in the same order
*/
List<Future<Cursor>> findBatched(TableFilter filter, List<SearchRow> firstLastPairs);
IndexLookupBatch createLookupBatch(TableFilter filter);
}
......@@ -347,6 +347,33 @@ public class IndexCondition {
return column;
}
/**
* Get expression.
*
* @return Expression.
*/
public Expression getExpression() {
return expression;
}
/**
* Get expression list.
*
* @return Expression list.
*/
public List<Expression> getExpressionList() {
return expressionList;
}
/**
* Get expression query.
*
* @return Expression query.
*/
public Query getExpressionQuery() {
return expressionQuery;
}
/**
* Check if the expression can be evaluated.
*
......
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.index;
import java.util.List;
import java.util.concurrent.Future;
import org.h2.result.SearchRow;
/**
* Support for asynchronous batched lookups in indexes. The flow is the following:
* H2 engine will be calling {@link #addSearchRows(SearchRow, SearchRow)} until
* method {@link #isBatchFull()}} will return {@code true} or there are no more
* search rows to add. Then method {@link #find()} will be called to execute batched lookup.
* Note that a single instance of {@link IndexLookupBatch} can be reused for multiple
* sequential batched lookups.
*
* @see Index#createLookupBatch(TableFilter)
* @author Sergi Vladykin
*/
public interface IndexLookupBatch {
/**
* Add search row pair to the batch.
*
* @param first the first row, or null for no limit
* @param last the last row, or null for no limit
* @see Index#find(TableFilter, SearchRow, SearchRow)
*/
void addSearchRows(SearchRow first, SearchRow last);
/**
* Check if this batch is full.
*
* @return {@code true} If batch is full, will not accept any
* more rows and {@link #find()} can be executed.
*/
boolean isBatchFull();
/**
* Execute batched lookup and return future cursor for each provided
* search row pair. Note that this method must return exactly the same number
* of future cursors in result list as number of {@link #addSearchRows(SearchRow, SearchRow)}
* calls has been done before {@link #find()} call exactly in the same order.
*
* @return List of future cursors for collected search rows.
*/
List<Future<Cursor>> find();
}
......@@ -141,10 +141,10 @@ public class LinkedIndex extends BaseIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
return 100 + getCostRangeIndex(masks, rowCount +
Constants.COST_ROW_OFFSET, filter, sortOrder);
Constants.COST_ROW_OFFSET, filters, filter, sortOrder);
}
@Override
......
......@@ -52,13 +52,13 @@ public class MetaIndex extends BaseIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
if (scan) {
return 10 * MetaTable.ROW_COUNT_APPROXIMATION;
}
return getCostRangeIndex(masks, MetaTable.ROW_COUNT_APPROXIMATION,
filter, sortOrder);
filters, filter, sortOrder);
}
@Override
......
......@@ -142,9 +142,9 @@ public class MultiVersionIndex implements Index {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
return base.getCost(session, masks, filter, sortOrder);
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
return base.getCost(session, masks, filters, filter, sortOrder);
}
@Override
......@@ -389,12 +389,8 @@ public class MultiVersionIndex implements Index {
}
@Override
public int getPreferedLookupBatchSize() {
return 0;
}
@Override
public List<Future<Cursor>> findBatched(TableFilter filter, List<SearchRow> firstLastPairs) {
throw DbException.throwInternalError("Must never be called.");
public IndexLookupBatch createLookupBatch(TableFilter filter) {
// Lookup batching is not supported.
return null;
}
}
......@@ -130,8 +130,8 @@ public class NonUniqueHashIndex extends BaseIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
for (Column column : columns) {
int index = column.getColumnId();
int mask = masks[index];
......
......@@ -217,10 +217,10 @@ public class PageBtreeIndex extends PageIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
return 10 * getCostRangeIndex(masks, tableData.getRowCount(session),
filter, sortOrder);
filters, filter, sortOrder);
}
@Override
......
......@@ -10,7 +10,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.Session;
......@@ -310,8 +309,8 @@ public class PageDataIndex extends PageIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
long cost = 10 * (tableData.getRowCountApproximation() +
Constants.COST_ROW_OFFSET);
return cost;
......
......@@ -96,10 +96,10 @@ public class PageDelegateIndex extends PageIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
return 10 * getCostRangeIndex(masks, mainIndex.getRowCount(session),
filter, sortOrder);
filters, filter, sortOrder);
}
@Override
......
......@@ -62,8 +62,8 @@ public class RangeIndex extends BaseIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
return 1;
}
......
......@@ -174,8 +174,8 @@ public class ScanIndex extends BaseIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
return tableData.getRowCountApproximation() + Constants.COST_ROW_OFFSET;
}
......
......@@ -180,7 +180,7 @@ public class SpatialTreeIndex extends BaseIndex implements SpatialIndex {
@Override
protected long getCostRangeIndex(int[] masks, long rowCount,
TableFilter filter, SortOrder sortOrder) {
TableFilter[] filters, int filter, SortOrder sortOrder) {
return getCostRangeIndex(masks, rowCount, columns);
}
......@@ -208,10 +208,10 @@ public class SpatialTreeIndex extends BaseIndex implements SpatialIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
return getCostRangeIndex(masks, table.getRowCountApproximation(),
filter, sortOrder);
filters, filter, sortOrder);
}
@Override
......
......@@ -318,10 +318,10 @@ public class TreeIndex extends BaseIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
public double getCost(Session session, int[] masks, TableFilter[] filters, int filter,
SortOrder sortOrder) {
return getCostRangeIndex(masks, tableData.getRowCountApproximation(),
filter, sortOrder);
filters, filter, sortOrder);
}
@Override
......
......@@ -115,7 +115,7 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
@Override
public synchronized double getCost(Session session, int[] masks,
TableFilter filter, SortOrder sortOrder) {
TableFilter[] filters, int filter, SortOrder sortOrder) {
if (recursive) {
return 1000;
}
......
......@@ -6,7 +6,6 @@
package org.h2.mvstore.db;
import java.util.List;
import org.h2.engine.Session;
import org.h2.index.BaseIndex;
import org.h2.index.Cursor;
......@@ -89,10 +88,10 @@ public class MVDelegateIndex extends BaseIndex implements MVIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
return 10 * getCostRangeIndex(masks,
mainIndex.getRowCountApproximation(), filter, sortOrder);
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
return 10 * getCostRangeIndex(masks, mainIndex.getRowCountApproximation(),
filters, filter, sortOrder);
}
@Override
......
......@@ -10,7 +10,6 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.Database;
......@@ -217,8 +216,8 @@ public class MVPrimaryIndex extends BaseIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
try {
long cost = 10 * (dataMap.sizeAsLongMax() + Constants.COST_ROW_OFFSET);
return cost;
......
......@@ -10,7 +10,6 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import org.h2.api.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
......@@ -352,11 +351,11 @@ public class MVSecondaryIndex extends BaseIndex implements MVIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
try {
return 10 * getCostRangeIndex(masks,
dataMap.sizeAsLongMax(), filter, sortOrder);
return 10 * getCostRangeIndex(masks, dataMap.sizeAsLongMax(),
filters, filter, sortOrder);
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED, e);
}
......
......@@ -7,7 +7,6 @@ package org.h2.mvstore.db;
import java.util.Iterator;
import java.util.List;
import org.h2.api.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
......@@ -33,7 +32,6 @@ import org.h2.value.Value;
import org.h2.value.ValueGeometry;
import org.h2.value.ValueLong;
import org.h2.value.ValueNull;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
......@@ -239,15 +237,15 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
}
@Override
public double getCost(Session session, int[] masks, TableFilter filter,
SortOrder sortOrder) {
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
return getCostRangeIndex(masks, table.getRowCountApproximation(),
filter, sortOrder);
filters, filter, sortOrder);
}
@Override
protected long getCostRangeIndex(int[] masks, long rowCount,
TableFilter filter, SortOrder sortOrder) {
TableFilter[] filters, int filter, SortOrder sortOrder) {
return SpatialTreeIndex.getCostRangeIndex(masks, rowCount, columns);
}
......
......@@ -106,9 +106,9 @@ public class Plan {
public double calculateCost(Session session) {
double cost = 1;
boolean invalidPlan = false;
int level = 1;
for (TableFilter tableFilter : allFilters) {
PlanItem item = tableFilter.getBestPlanItem(session, level++);
for (int i = 0; i < allFilters.length; i++) {
TableFilter tableFilter = allFilters[i];
PlanItem item = tableFilter.getBestPlanItem(session, allFilters, i);
planItems.put(tableFilter, item);
cost += cost * item.cost;
setEvaluatable(tableFilter, true);
......
......@@ -18,10 +18,19 @@ public class PlanItem {
*/
double cost;
private int[] masks;
private Index index;
private PlanItem joinPlan;
private PlanItem nestedJoinPlan;
void setMasks(int[] masks) {
this.masks = masks;
}
int[] getMasks() {
return masks;
}
void setIndex(Index index) {
this.index = index;
}
......
......@@ -678,20 +678,21 @@ public abstract class Table extends SchemaObjectBase {
* @param session the session
* @param masks per-column comparison bit masks, null means 'always false',
* see constants in IndexCondition
* @param filter the table filter
* @param filters all joined table filters
* @param filter the current table filter index
* @param sortOrder the sort order
* @return the plan item
*/
public PlanItem getBestPlanItem(Session session, int[] masks,
TableFilter filter, SortOrder sortOrder) {
TableFilter[] filters, int filter, SortOrder sortOrder) {
PlanItem item = new PlanItem();
item.setIndex(getScanIndex(session));
item.cost = item.getIndex().getCost(session, null, null, null);
item.cost = item.getIndex().getCost(session, null, filters, filter, null);
ArrayList<Index> indexes = getIndexes();
if (indexes != null && masks != null) {
for (int i = 1, size = indexes.size(); i < size; i++) {
Index index = indexes.get(i);
double cost = index.getCost(session, masks, filter, sortOrder);
double cost = index.getCost(session, masks, filters, filter, sortOrder);
if (cost < item.cost) {
item.cost = cost;
item.setIndex(index);
......
......@@ -225,9 +225,9 @@ public class TableView extends Table {
@Override
public PlanItem getBestPlanItem(Session session, int[] masks,
TableFilter filter, SortOrder sortOrder) {
TableFilter[] filters, int filter, SortOrder sortOrder) {
PlanItem item = new PlanItem();
item.cost = index.getCost(session, masks, filter, sortOrder);
item.cost = index.getCost(session, masks, filters, filter, sortOrder);
final CacheKey cacheKey = new CacheKey(masks, session);
synchronized (this) {
......@@ -434,7 +434,7 @@ public class TableView extends Table {
throw DbException.get(ErrorCode.VIEW_IS_INVALID_2,
createException, getSQL(), msg);
}
PlanItem item = getBestPlanItem(session, null, null, null);
PlanItem item = getBestPlanItem(session, null, null, -1, null);
return item.getIndex();
}
......
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Future which is already done.
*
* @param <T> Result value.
* @author Sergi Vladykin
*/
public class DoneFuture<T> implements Future<T> {
final T x;
public DoneFuture(T x) {
this.x = x;
}
@Override
public T get() throws InterruptedException, ExecutionException {
return x;
}
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return x;
}
@Override
public boolean isDone() {
return true;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public String toString() {
return "DoneFuture->" + x;
}
}
......@@ -5,6 +5,7 @@
*/
package org.h2.util;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
......@@ -13,6 +14,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Array;
......@@ -361,9 +363,22 @@ public class SourceCompiler {
}
private static void handleSyntaxError(String output) {
if (output.startsWith("Note:") || output.startsWith("warning:")) {
// just a warning (e.g. unchecked or unsafe operations)
} else if (output.length() > 0) {
boolean syntaxError = false;
final BufferedReader reader = new BufferedReader(new StringReader(output));
try {
for (String line; (line = reader.readLine()) != null; ) {
if (line.startsWith("Note:") || line.startsWith("warning:")) {
// just a warning (e.g. unchecked or unsafe operations)
} else {
syntaxError = true;
break;
}
}
} catch (IOException ignored) {
// exception ignored
}
if (syntaxError) {
output = StringUtils.replaceAll(output, COMPILE_DIR, "");
throw DbException.get(ErrorCode.SYNTAX_ERROR_1, output);
}
......
org.h2.test.ap.TestAnnotationProcessor
\ No newline at end of file
package org.h2.test.ap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
public class TestAnnotationProcessor extends AbstractProcessor {
public static final String MESSAGES_KEY = TestAnnotationProcessor.class.getName() + "-messages";
public Set<String> getSupportedAnnotationTypes() {
for (OutputMessage outputMessage : findMessages()) {
processingEnv.getMessager().printMessage(outputMessage.kind, outputMessage.message);
}
return Collections.emptySet();
}
private List<OutputMessage> findMessages() {
final String messagesStr = System.getProperty(MESSAGES_KEY);
if (messagesStr == null || messagesStr.isEmpty()) {
return Collections.emptyList();
} else {
final List<OutputMessage> outputMessages = new ArrayList<OutputMessage>();
for (String msg : messagesStr.split("\\|")) {
final String[] split = msg.split(",");
if (split.length == 2) {
outputMessages.add(new OutputMessage(Diagnostic.Kind.valueOf(split[0]), split[1]));
} else {
throw new IllegalStateException("Unable to parse messages definition for: '" + messagesStr + "'");
}
}
return outputMessages;
}
}
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_6;
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
private static class OutputMessage {
public final Diagnostic.Kind kind;
public final String message;
private OutputMessage(Diagnostic.Kind kind, String message) {
this.kind = kind;
this.message = message;
}
}
}
......@@ -41,8 +41,10 @@ import org.h2.api.Aggregate;
import org.h2.api.AggregateFunction;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.jdbc.JdbcSQLException;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
import org.h2.test.ap.TestAnnotationProcessor;
import org.h2.tools.SimpleResultSet;
import org.h2.util.IOUtils;
import org.h2.util.New;
......@@ -103,6 +105,7 @@ public class TestFunctions extends TestBase implements AggregateFunction {
testTranslate();
testGenerateSeries();
testFileWrite();
testAnnotationProcessorsOutput();
deleteDb("functions");
}
......@@ -1163,7 +1166,7 @@ public class TestFunctions extends TestBase implements AggregateFunction {
call.execute();
assertEquals(Integer[].class.getName(), call.getArray(1).getArray()
.getClass().getName());
assertEquals(new Integer[] { 2, 1 }, (Integer[]) call.getObject(1));
assertEquals(new Integer[]{2, 1}, (Integer[]) call.getObject(1));
stat.execute("drop alias array_test");
......@@ -1778,6 +1781,83 @@ public class TestFunctions extends TestBase implements AggregateFunction {
conn.close();
}
private void testAnnotationProcessorsOutput() throws SQLException {
testAnnotationProcessorsOutput_emptyKey();
testAnnotationProcessorsOutput_invalidKey();
testAnnotationProcessorsOutput_oneInvalidKey();
testAnnotationProcessorsOutput_warnAndError();
}
private void testAnnotationProcessorsOutput_emptyKey() throws SQLException {
try {
System.setProperty(TestAnnotationProcessor.MESSAGES_KEY, "");
callCompiledFunction("test_atp_empty_key");
} finally {
System.clearProperty(TestAnnotationProcessor.MESSAGES_KEY);
}
}
private void testAnnotationProcessorsOutput_invalidKey() throws SQLException {
try {
System.setProperty(TestAnnotationProcessor.MESSAGES_KEY, "invalid");
callCompiledFunction("test_atp_invalid_key");
fail();
} catch (JdbcSQLException e) {
assertEquals(ErrorCode.SYNTAX_ERROR_1, e.getErrorCode());
assertContains(e.getMessage(), "'invalid'");
} finally {
System.clearProperty(TestAnnotationProcessor.MESSAGES_KEY);
}
}
private void testAnnotationProcessorsOutput_oneInvalidKey() throws SQLException {
try {
System.setProperty(TestAnnotationProcessor.MESSAGES_KEY, "invalid,foo");
callCompiledFunction("test_atp_one_invalid_key");
fail();
} catch (JdbcSQLException e) {
assertEquals(ErrorCode.SYNTAX_ERROR_1, e.getErrorCode());
assertContains(e.getMessage(), "enum");
assertContains(e.getMessage(), "Kind.invalid");
} finally {
System.clearProperty(TestAnnotationProcessor.MESSAGES_KEY);
}
}
private void testAnnotationProcessorsOutput_warnAndError() throws SQLException {
try {
System.setProperty(TestAnnotationProcessor.MESSAGES_KEY, "WARNING,foo1|ERROR,foo2");
callCompiledFunction("test_atp_warn_and_error");
fail();
} catch (JdbcSQLException e) {
assertEquals(ErrorCode.SYNTAX_ERROR_1, e.getErrorCode());
assertContains(e.getMessage(), "foo1");
assertContains(e.getMessage(), "foo2");
} finally {
System.clearProperty(TestAnnotationProcessor.MESSAGES_KEY);
}
}
private void callCompiledFunction(String functionName) throws SQLException {
deleteDb("functions");
Connection conn = getConnection("functions");
Statement stat = conn.createStatement();
ResultSet rs;
stat.execute("create alias " + functionName + " AS "
+ "$$ boolean " + functionName + "() "
+ "{ return true; } $$;");
PreparedStatement stmt = conn.prepareStatement(
"select " + functionName + "() from dual");
rs = stmt.executeQuery();
rs.next();
assertEquals(Boolean.class.getName(), rs.getObject(1).getClass().getName());
stat.execute("drop alias " + functionName + "");
conn.close();
}
private void assertCallResult(String expected, Statement stat, String sql)
throws SQLException {
ResultSet rs = stat.executeQuery("CALL " + sql);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论