提交 c849f1cf 编写于 作者: B bluestreak02

PG: QuestDB query system supports PG-style bind variables '?' alongside named...

PG: QuestDB query system supports PG-style bind variables '?' alongside named bind variables ':name'
上级 967b9bee
......@@ -28,7 +28,8 @@ import com.questdb.cairo.ColumnType;
import com.questdb.cairo.sql.Function;
import com.questdb.cairo.sql.RecordMetadata;
import com.questdb.griffin.engine.functions.CursorFunction;
import com.questdb.griffin.engine.functions.bind.LinkFunction;
import com.questdb.griffin.engine.functions.bind.IndexedParameterLinkFunction;
import com.questdb.griffin.engine.functions.bind.NamedParameterLinkFunction;
import com.questdb.griffin.engine.functions.columns.*;
import com.questdb.griffin.engine.functions.constants.*;
import com.questdb.griffin.model.ExpressionNode;
......@@ -38,7 +39,7 @@ import com.questdb.std.*;
import java.util.ArrayDeque;
public class FunctionParser implements PostOrderTreeTraversalAlgo.Visitor {
public class FunctionParser implements PostOrderTreeTraversalAlgo.Visitor, Mutable {
private static final Log LOG = LogFactory.getLog(FunctionParser.class);
// order of values matters here, partial match must have greater value than fuzzy match
......@@ -49,6 +50,18 @@ public class FunctionParser implements PostOrderTreeTraversalAlgo.Visitor {
private static final IntHashSet invalidFunctionNameChars = new IntHashSet();
private static final CharSequenceHashSet invalidFunctionNames = new CharSequenceHashSet();
static {
for (int i = 0, n = SqlCompiler.sqlControlSymbols.size(); i < n; i++) {
invalidFunctionNames.add(SqlCompiler.sqlControlSymbols.getQuick(i));
}
invalidFunctionNameChars.add('.');
invalidFunctionNameChars.add(' ');
invalidFunctionNameChars.add('\"');
invalidFunctionNameChars.add('\'');
}
private final ObjList<Function> mutableArgs = new ObjList<>();
private final ArrayDeque<Function> stack = new ArrayDeque<>();
private final PostOrderTreeTraversalAlgo traverseAlgo = new PostOrderTreeTraversalAlgo();
......@@ -58,6 +71,7 @@ public class FunctionParser implements PostOrderTreeTraversalAlgo.Visitor {
private final ArrayDeque<RecordMetadata> metadataStack = new ArrayDeque<>();
private RecordMetadata metadata;
private SqlExecutionContext sqlExecutionContext;
private int bindVariableIndex = 0;
public FunctionParser(CairoConfiguration configuration, Iterable<FunctionFactory> functionFactories) {
this.configuration = configuration;
......@@ -160,12 +174,43 @@ public class FunctionParser implements PostOrderTreeTraversalAlgo.Visitor {
return openBraceIndex;
}
public Function createParameter(ExpressionNode node) throws SqlException {
private static SqlException invalidFunction(CharSequence message, ExpressionNode node, ObjList<Function> args) {
SqlException ex = SqlException.position(node.position);
ex.put(message);
ex.put(": ");
ex.put(node.token);
ex.put('(');
if (args != null) {
for (int i = 0, n = args.size(); i < n; i++) {
if (i > 0) {
ex.put(',');
}
ex.put(ColumnType.nameOf(args.getQuick(i).getType()));
}
}
ex.put(')');
return ex;
}
@Override
public void clear() {
bindVariableIndex = 0;
}
public Function createNamedParameter(ExpressionNode node) throws SqlException {
Function function = sqlExecutionContext.getBindVariableService().getFunction(node.token);
if (function == null) {
throw SqlException.position(node.position).put("undefined bind variable: ").put(node.token);
}
return new LinkFunction(Chars.toString(node.token), function.getType(), node.position);
return new NamedParameterLinkFunction(Chars.toString(node.token), function.getType(), node.position);
}
public Function createIndexParameter(int variableIndex, ExpressionNode node) throws SqlException {
Function function = sqlExecutionContext.getBindVariableService().getFunction(variableIndex);
if (function == null) {
throw SqlException.position(node.position).put("no bind variable defined at index ").put(variableIndex);
}
return new IndexedParameterLinkFunction(variableIndex, function.getType(), node.position);
}
public boolean isGroupBy(CharSequence name) {
......@@ -228,8 +273,10 @@ public class FunctionParser implements PostOrderTreeTraversalAlgo.Visitor {
if (argCount == 0) {
switch (node.type) {
case ExpressionNode.LITERAL:
if (Chars.startsWith(node.token, ':')) {
stack.push(createParameter(node));
if (Chars.equals(node.token, '?')) {
stack.push(createIndexParameter(bindVariableIndex++, node));
} else if (Chars.startsWith(node.token, ':')) {
stack.push(createNamedParameter(node));
} else {
// lookup column
stack.push(createColumn(node));
......@@ -260,24 +307,6 @@ public class FunctionParser implements PostOrderTreeTraversalAlgo.Visitor {
}
}
private static SqlException invalidFunction(CharSequence message, ExpressionNode node, ObjList<Function> args) {
SqlException ex = SqlException.position(node.position);
ex.put(message);
ex.put(": ");
ex.put(node.token);
ex.put('(');
if (args != null) {
for (int i = 0, n = args.size(); i < n; i++) {
if (i > 0) {
ex.put(',');
}
ex.put(ColumnType.nameOf(args.getQuick(i).getType()));
}
}
ex.put(')');
return ex;
}
private Function checkAndCreateFunction(FunctionFactory factory, ObjList<Function> args, int position, CairoConfiguration configuration) throws SqlException {
Function function;
try {
......@@ -668,15 +697,4 @@ public class FunctionParser implements PostOrderTreeTraversalAlgo.Visitor {
}
}
}
static {
for (int i = 0, n = SqlCompiler.sqlControlSymbols.size(); i < n; i++) {
invalidFunctionNames.add(SqlCompiler.sqlControlSymbols.getQuick(i));
}
invalidFunctionNameChars.add('.');
invalidFunctionNameChars.add(' ');
invalidFunctionNameChars.add('\"');
invalidFunctionNameChars.add('\'');
}
}
......@@ -24,7 +24,10 @@
package com.questdb.griffin;
import com.questdb.cairo.*;
import com.questdb.cairo.sql.*;
import com.questdb.cairo.sql.Record;
import com.questdb.cairo.sql.RecordCursor;
import com.questdb.cairo.sql.RecordCursorFactory;
import com.questdb.cairo.sql.RecordMetadata;
import com.questdb.griffin.engine.functions.bind.BindVariableService;
import com.questdb.griffin.model.*;
import com.questdb.log.Log;
......@@ -64,6 +67,7 @@ public class SqlCompiler implements Closeable {
private final ObjList<TableWriter> tableWriters = new ObjList<>();
private final TableStructureAdapter tableStructureAdapter = new TableStructureAdapter();
private final ExecutableMethod createTableMethod = this::createTable;
private final FunctionParser functionParser;
public SqlCompiler(CairoEngine engine) {
this(engine, null);
......@@ -81,7 +85,7 @@ public class SqlCompiler implements Closeable {
configuration.getSqlCharacterStoreSequencePoolCapacity()
);
this.lexer = new GenericLexer(configuration.getSqlLexerPoolCapacity());
final FunctionParser functionParser = new FunctionParser(configuration, ServiceLoader.load(FunctionFactory.class));
this.functionParser = new FunctionParser(configuration, ServiceLoader.load(FunctionFactory.class));
this.codeGenerator = new SqlCodeGenerator(engine, configuration, functionParser);
configureLexer(lexer);
......@@ -790,6 +794,7 @@ public class SqlCompiler implements Closeable {
}
private void clear() {
functionParser.clear();
sqlNodePool.clear();
characterStore.clear();
queryColumnPool.clear();
......
......@@ -29,31 +29,41 @@ import com.questdb.griffin.SqlException;
import com.questdb.std.BinarySequence;
import com.questdb.std.CharSequenceObjHashMap;
import com.questdb.std.Chars;
import com.questdb.std.ObjList;
public class BindVariableService {
private final CharSequenceObjHashMap<Function> variables = new CharSequenceObjHashMap<>();
private final CharSequenceObjHashMap<Function> namedVariables = new CharSequenceObjHashMap<>();
private final ObjList<Function> indexedVariables = new ObjList<>();
public void clear() {
variables.clear();
namedVariables.clear();
indexedVariables.clear();
}
public Function getFunction(CharSequence name) {
assert name != null;
assert Chars.startsWith(name, ':');
int index = variables.keyIndex(name, 1, name.length());
int index = namedVariables.keyIndex(name, 1, name.length());
if (index > -1) {
return null;
}
return variables.valueAt(index);
return namedVariables.valueAt(index);
}
public Function getFunction(int index) {
if (index < indexedVariables.size()) {
return indexedVariables.getQuick(index);
}
return null;
}
public void setBin(CharSequence name, BinarySequence value) throws SqlException {
int index = variables.keyIndex(name);
int index = namedVariables.keyIndex(name);
if (index > -1) {
variables.putAt(index, name, new BinBindVariable(value));
namedVariables.putAt(index, name, new BinBindVariable(value));
} else {
Function function = variables.valueAt(index);
Function function = namedVariables.valueAt(index);
if (function instanceof BinBindVariable) {
((BinBindVariable) function).value = value;
} else {
......@@ -63,11 +73,11 @@ public class BindVariableService {
}
public void setBoolean(CharSequence name, boolean value) throws SqlException {
int index = variables.keyIndex(name);
int index = namedVariables.keyIndex(name);
if (index > -1) {
variables.putAt(index, name, new BooleanBindVariable(value));
namedVariables.putAt(index, name, new BooleanBindVariable(value));
} else {
Function function = variables.valueAt(index);
Function function = namedVariables.valueAt(index);
if (function instanceof BooleanBindVariable) {
((BooleanBindVariable) function).value = value;
} else {
......@@ -77,11 +87,11 @@ public class BindVariableService {
}
public void setByte(CharSequence name, byte value) throws SqlException {
int index = variables.keyIndex(name);
int index = namedVariables.keyIndex(name);
if (index > -1) {
variables.putAt(index, name, new ByteBindVariable(value));
namedVariables.putAt(index, name, new ByteBindVariable(value));
} else {
Function function = variables.valueAt(index);
Function function = namedVariables.valueAt(index);
if (function instanceof ByteBindVariable) {
((ByteBindVariable) function).value = value;
} else {
......@@ -91,11 +101,11 @@ public class BindVariableService {
}
public void setDate(CharSequence name, long value) throws SqlException {
int index = variables.keyIndex(name);
int index = namedVariables.keyIndex(name);
if (index > -1) {
variables.putAt(index, name, new DateBindVariable(value));
namedVariables.putAt(index, name, new DateBindVariable(value));
} else {
Function function = variables.valueAt(index);
Function function = namedVariables.valueAt(index);
if (function instanceof DateBindVariable) {
((DateBindVariable) function).value = value;
} else {
......@@ -105,11 +115,11 @@ public class BindVariableService {
}
public void setDouble(CharSequence name, double value) throws SqlException {
int index = variables.keyIndex(name);
int index = namedVariables.keyIndex(name);
if (index > -1) {
variables.putAt(index, name, new DoubleBindVariable(value));
namedVariables.putAt(index, name, new DoubleBindVariable(value));
} else {
Function function = variables.valueAt(index);
Function function = namedVariables.valueAt(index);
if (function instanceof DoubleBindVariable) {
((DoubleBindVariable) function).value = value;
} else {
......@@ -119,11 +129,11 @@ public class BindVariableService {
}
public void setFloat(CharSequence name, float value) throws SqlException {
int index = variables.keyIndex(name);
int index = namedVariables.keyIndex(name);
if (index > -1) {
variables.putAt(index, name, new FloatBindVariable(value));
namedVariables.putAt(index, name, new FloatBindVariable(value));
} else {
Function function = variables.valueAt(index);
Function function = namedVariables.valueAt(index);
if (function instanceof FloatBindVariable) {
((FloatBindVariable) function).value = value;
} else {
......@@ -133,11 +143,11 @@ public class BindVariableService {
}
public void setInt(CharSequence name, int value) throws SqlException {
int index = variables.keyIndex(name);
int index = namedVariables.keyIndex(name);
if (index > -1) {
variables.putAt(index, name, new IntBindVariable(value));
namedVariables.putAt(index, name, new IntBindVariable(value));
} else {
Function function = variables.valueAt(index);
Function function = namedVariables.valueAt(index);
if (function instanceof IntBindVariable) {
((IntBindVariable) function).value = value;
} else {
......@@ -147,11 +157,11 @@ public class BindVariableService {
}
public void setLong(CharSequence name, long value) throws SqlException {
int index = variables.keyIndex(name);
int index = namedVariables.keyIndex(name);
if (index > -1) {
variables.putAt(index, name, new LongBindVariable(value));
namedVariables.putAt(index, name, new LongBindVariable(value));
} else {
Function function = variables.valueAt(index);
Function function = namedVariables.valueAt(index);
if (function instanceof LongBindVariable) {
((LongBindVariable) function).value = value;
} else {
......@@ -160,12 +170,177 @@ public class BindVariableService {
}
}
public void setLong(int index, long value) throws SqlException {
if (index < indexedVariables.size()) {
Function function = indexedVariables.getQuick(index);
if (function == null) {
indexedVariables.setQuick(index, new LongBindVariable(value));
} else if (function instanceof LongBindVariable) {
((LongBindVariable) function).value = value;
} else {
throw SqlException.position(0).put("bind variable at ").put(index).put(" is already defined as ").put(ColumnType.nameOf(function.getType()));
}
} else {
indexedVariables.extendAndSet(index, new LongBindVariable(value));
}
}
public void setBoolean(int index, boolean value) throws SqlException {
if (index < indexedVariables.size()) {
Function function = indexedVariables.getQuick(index);
if (function == null) {
indexedVariables.setQuick(index, new BooleanBindVariable(value));
} else if (function instanceof BooleanBindVariable) {
((BooleanBindVariable) function).value = value;
} else {
throw SqlException.position(0).put("bind variable at ").put(index).put(" is already defined as ").put(ColumnType.nameOf(function.getType()));
}
} else {
indexedVariables.extendAndSet(index, new BooleanBindVariable(value));
}
}
public void setDate(int index, long value) throws SqlException {
if (index < indexedVariables.size()) {
Function function = indexedVariables.getQuick(index);
if (function == null) {
indexedVariables.setQuick(index, new DateBindVariable(value));
} else if (function instanceof DateBindVariable) {
((DateBindVariable) function).value = value;
} else {
throw SqlException.position(0).put("bind variable at ").put(index).put(" is already defined as ").put(ColumnType.nameOf(function.getType()));
}
} else {
indexedVariables.extendAndSet(index, new DateBindVariable(value));
}
}
public void setTimestamp(int index, long value) throws SqlException {
if (index < indexedVariables.size()) {
Function function = indexedVariables.getQuick(index);
if (function == null) {
indexedVariables.setQuick(index, new TimestampBindVariable(value));
} else if (function instanceof TimestampBindVariable) {
((TimestampBindVariable) function).value = value;
} else {
throw SqlException.position(0).put("bind variable at ").put(index).put(" is already defined as ").put(ColumnType.nameOf(function.getType()));
}
} else {
indexedVariables.extendAndSet(index, new TimestampBindVariable(value));
}
}
public void setByte(int index, byte value) throws SqlException {
if (index < indexedVariables.size()) {
Function function = indexedVariables.getQuick(index);
if (function == null) {
indexedVariables.setQuick(index, new ByteBindVariable(value));
} else if (function instanceof ByteBindVariable) {
((ByteBindVariable) function).value = value;
} else {
throw SqlException.position(0).put("bind variable at ").put(index).put(" is already defined as ").put(ColumnType.nameOf(function.getType()));
}
} else {
indexedVariables.extendAndSet(index, new ByteBindVariable(value));
}
}
public void setShort(int index, short value) throws SqlException {
if (index < indexedVariables.size()) {
Function function = indexedVariables.getQuick(index);
if (function == null) {
indexedVariables.setQuick(index, new ShortBindVariable(value));
} else if (function instanceof ShortBindVariable) {
((ShortBindVariable) function).value = value;
} else {
throw SqlException.position(0).put("bind variable at ").put(index).put(" is already defined as ").put(ColumnType.nameOf(function.getType()));
}
} else {
indexedVariables.extendAndSet(index, new ShortBindVariable(value));
}
}
public void setStr(int index, CharSequence value) throws SqlException {
if (index < indexedVariables.size()) {
Function function = indexedVariables.getQuick(index);
if (function == null) {
indexedVariables.setQuick(index, new StrBindVariable(value));
} else if (function instanceof StrBindVariable) {
((StrBindVariable) function).value = value;
} else {
throw SqlException.position(0).put("bind variable at ").put(index).put(" is already defined as ").put(ColumnType.nameOf(function.getType()));
}
} else {
indexedVariables.extendAndSet(index, new StrBindVariable(value));
}
}
public void setBin(int index, BinarySequence value) throws SqlException {
if (index < indexedVariables.size()) {
Function function = indexedVariables.getQuick(index);
if (function == null) {
indexedVariables.setQuick(index, new BinBindVariable(value));
} else if (function instanceof BinBindVariable) {
((BinBindVariable) function).value = value;
} else {
throw SqlException.position(0).put("bind variable at ").put(index).put(" is already defined as ").put(ColumnType.nameOf(function.getType()));
}
} else {
indexedVariables.extendAndSet(index, new BinBindVariable(value));
}
}
public void setFloat(int index, float value) throws SqlException {
if (index < indexedVariables.size()) {
Function function = indexedVariables.getQuick(index);
if (function == null) {
indexedVariables.setQuick(index, new FloatBindVariable(value));
} else if (function instanceof FloatBindVariable) {
((FloatBindVariable) function).value = value;
} else {
throw SqlException.position(0).put("bind variable at ").put(index).put(" is already defined as ").put(ColumnType.nameOf(function.getType()));
}
} else {
indexedVariables.extendAndSet(index, new FloatBindVariable(value));
}
}
public void setDouble(int index, double value) throws SqlException {
if (index < indexedVariables.size()) {
Function function = indexedVariables.getQuick(index);
if (function == null) {
indexedVariables.setQuick(index, new DoubleBindVariable(value));
} else if (function instanceof DoubleBindVariable) {
((DoubleBindVariable) function).value = value;
} else {
throw SqlException.position(0).put("bind variable at ").put(index).put(" is already defined as ").put(ColumnType.nameOf(function.getType()));
}
} else {
indexedVariables.extendAndSet(index, new DoubleBindVariable(value));
}
}
public void setInt(int index, int value) throws SqlException {
if (index < indexedVariables.size()) {
Function function = indexedVariables.getQuick(index);
if (function == null) {
indexedVariables.setQuick(index, new IntBindVariable(value));
} else if (function instanceof IntBindVariable) {
((IntBindVariable) function).value = value;
} else {
throw SqlException.position(0).put("bind variable at ").put(index).put(" is already defined as ").put(ColumnType.nameOf(function.getType()));
}
} else {
indexedVariables.extendAndSet(index, new IntBindVariable(value));
}
}
public void setShort(CharSequence name, short value) throws SqlException {
int index = variables.keyIndex(name);
int index = namedVariables.keyIndex(name);
if (index > -1) {
variables.putAt(index, name, new ShortBindVariable(value));
namedVariables.putAt(index, name, new ShortBindVariable(value));
} else {
Function function = variables.valueAt(index);
Function function = namedVariables.valueAt(index);
if (function instanceof ShortBindVariable) {
((ShortBindVariable) function).value = value;
} else {
......@@ -175,11 +350,11 @@ public class BindVariableService {
}
public void setStr(CharSequence name, CharSequence value) throws SqlException {
int index = variables.keyIndex(name);
int index = namedVariables.keyIndex(name);
if (index > -1) {
variables.putAt(index, name, new StrBindVariable(value));
namedVariables.putAt(index, name, new StrBindVariable(value));
} else {
Function function = variables.valueAt(index);
Function function = namedVariables.valueAt(index);
if (function instanceof StrBindVariable) {
((StrBindVariable) function).value = value;
} else {
......@@ -189,11 +364,11 @@ public class BindVariableService {
}
public void setTimestamp(CharSequence name, long value) throws SqlException {
int index = variables.keyIndex(name);
int index = namedVariables.keyIndex(name);
if (index > -1) {
variables.putAt(index, name, new TimestampBindVariable(value));
namedVariables.putAt(index, name, new TimestampBindVariable(value));
} else {
Function function = variables.valueAt(index);
Function function = namedVariables.valueAt(index);
if (function instanceof TimestampBindVariable) {
((TimestampBindVariable) function).value = value;
} else {
......
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (C) 2014-2019 Appsicle
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
******************************************************************************/
package com.questdb.griffin.engine.functions.bind;
import com.questdb.cairo.CairoException;
import com.questdb.cairo.sql.*;
import com.questdb.std.BinarySequence;
import com.questdb.std.Misc;
import com.questdb.std.str.CharSink;
public class IndexedParameterLinkFunction implements Function {
private final int variableIndex;
private final int type;
private final int position;
private Function base;
public IndexedParameterLinkFunction(int variableIndex, int type, int position) {
this.variableIndex = variableIndex;
this.type = type;
this.position = position;
}
@Override
public void close() {
base = Misc.free(base);
}
@Override
public BinarySequence getBin(Record rec) {
return getBase().getBin(rec);
}
@Override
public long getBinLen(Record rec) {
return getBase().getBinLen(rec);
}
@Override
public boolean getBool(Record rec) {
return getBase().getBool(rec);
}
@Override
public byte getByte(Record rec) {
return getBase().getByte(rec);
}
@Override
public long getDate(Record rec) {
return getBase().getDate(rec);
}
@Override
public double getDouble(Record rec) {
return getBase().getDouble(rec);
}
@Override
public float getFloat(Record rec) {
return getBase().getFloat(rec);
}
@Override
public int getInt(Record rec) {
return getBase().getInt(rec);
}
@Override
public long getLong(Record rec) {
return getBase().getLong(rec);
}
@Override
public RecordMetadata getMetadata() {
return getBase().getMetadata();
}
@Override
public int getPosition() {
return position;
}
@Override
public RecordCursorFactory getRecordCursorFactory() {
return getBase().getRecordCursorFactory();
}
@Override
public short getShort(Record rec) {
return getBase().getShort(rec);
}
@Override
public CharSequence getStr(Record rec) {
return getBase().getStr(rec);
}
@Override
public void getStr(Record rec, CharSink sink) {
getBase().getStr(rec, sink);
}
@Override
public CharSequence getStrB(Record rec) {
return getBase().getStrB(rec);
}
@Override
public int getStrLen(Record rec) {
return getBase().getStrLen(rec);
}
@Override
public CharSequence getSymbol(Record rec) {
return getBase().getSymbol(rec);
}
@Override
public long getTimestamp(Record rec) {
return getBase().getTimestamp(rec);
}
@Override
public int getType() {
return type;
}
@Override
public void init(RecordCursor recordCursor, BindVariableService bindVariableService) {
base = bindVariableService.getFunction(variableIndex);
if (base == null) {
throw CairoException.instance(0).put("undefined bind variable: ").put(variableIndex);
}
assert base.getType() == type;
base.init(recordCursor, bindVariableService);
}
private Function getBase() {
assert base != null;
return base;
}
}
......@@ -29,13 +29,13 @@ import com.questdb.std.BinarySequence;
import com.questdb.std.Misc;
import com.questdb.std.str.CharSink;
public class LinkFunction implements Function {
public class NamedParameterLinkFunction implements Function {
private final String variableName;
private final int type;
private final int position;
private Function base;
public LinkFunction(String variableName, int type, int position) {
public NamedParameterLinkFunction(String variableName, int type, int position) {
this.variableName = variableName;
this.type = type;
this.position = position;
......
......@@ -399,6 +399,62 @@ public class LimitTest extends AbstractGriffinTest {
});
}
@Test
public void testTopNIndexVariable() throws Exception {
String query = "select * from y limit ?";
TestUtils.assertMemoryLeak(() -> {
try {
String expected1 = "i\tsym2\tprice\ttimestamp\tb\tc\td\te\tf\tg\tik\tj\tk\tl\tm\tn\n" +
"1\tmsft\t0.509000000000\t2018-01-01T00:02:00.000000Z\tfalse\tU\t0.524372285929\t0.8072\t365\t2015-05-02T19:30:57.935Z\t\t-4485747798769957016\t1970-01-01T00:00:00.000000Z\t19\t00000000 19 c4 95 94 36 53 49 b4 59 7e 3b 08 a1 1e\tYSBEOUOJSHRUEDRQ\n" +
"2\tgoogl\t0.423000000000\t2018-01-01T00:04:00.000000Z\tfalse\tG\t0.529840594176\tNaN\t493\t2015-04-09T11:42:28.332Z\tHYRX\t-8811278461560712840\t1970-01-01T00:16:40.000000Z\t29\t00000000 53 d0 fb 64 bb 1a d4 f0 2d 40 e2 4b b1 3e e3 f1\t\n" +
"3\tgoogl\t0.174000000000\t2018-01-01T00:06:00.000000Z\tfalse\tW\t0.882822836670\t0.7230\t845\t2015-08-26T10:57:26.275Z\tVTJW\t9029468389542245059\t1970-01-01T00:33:20.000000Z\t46\t00000000 e5 61 2f 64 0e 2c 7f d7 6f b8 c9 ae 28 c7 84 47\tDSWUGSHOLNV\n" +
"4\tibm\t0.148000000000\t2018-01-01T00:08:00.000000Z\ttrue\tI\t0.345689799154\t0.2401\t775\t2015-08-03T15:58:03.335Z\tVTJW\t-8910603140262731534\t1970-01-01T00:50:00.000000Z\t24\t00000000 ac a8 3b a6 dc 3b 7d 2b e3 92 fe 69 38 e1 77 9a\n" +
"00000010 e7 0c 89\tLJUMLGLHMLLEO\n";
String expected2 = "i\tsym2\tprice\ttimestamp\tb\tc\td\te\tf\tg\tik\tj\tk\tl\tm\tn\n" +
"1\tmsft\t0.509000000000\t2018-01-01T00:02:00.000000Z\tfalse\tU\t0.524372285929\t0.8072\t365\t2015-05-02T19:30:57.935Z\t\t-4485747798769957016\t1970-01-01T00:00:00.000000Z\t19\t00000000 19 c4 95 94 36 53 49 b4 59 7e 3b 08 a1 1e\tYSBEOUOJSHRUEDRQ\n" +
"2\tgoogl\t0.423000000000\t2018-01-01T00:04:00.000000Z\tfalse\tG\t0.529840594176\tNaN\t493\t2015-04-09T11:42:28.332Z\tHYRX\t-8811278461560712840\t1970-01-01T00:16:40.000000Z\t29\t00000000 53 d0 fb 64 bb 1a d4 f0 2d 40 e2 4b b1 3e e3 f1\t\n" +
"3\tgoogl\t0.174000000000\t2018-01-01T00:06:00.000000Z\tfalse\tW\t0.882822836670\t0.7230\t845\t2015-08-26T10:57:26.275Z\tVTJW\t9029468389542245059\t1970-01-01T00:33:20.000000Z\t46\t00000000 e5 61 2f 64 0e 2c 7f d7 6f b8 c9 ae 28 c7 84 47\tDSWUGSHOLNV\n" +
"4\tibm\t0.148000000000\t2018-01-01T00:08:00.000000Z\ttrue\tI\t0.345689799154\t0.2401\t775\t2015-08-03T15:58:03.335Z\tVTJW\t-8910603140262731534\t1970-01-01T00:50:00.000000Z\t24\t00000000 ac a8 3b a6 dc 3b 7d 2b e3 92 fe 69 38 e1 77 9a\n" +
"00000010 e7 0c 89\tLJUMLGLHMLLEO\n" +
"5\tgoogl\t0.868000000000\t2018-01-01T00:10:00.000000Z\ttrue\tZ\t0.427470428635\t0.0212\t179\t\t\t5746626297238459939\t1970-01-01T01:06:40.000000Z\t35\t00000000 91 88 28 a5 18 93 bd 0b 61 f5 5d d0 eb\tRGIIH\n" +
"6\tmsft\t0.297000000000\t2018-01-01T00:12:00.000000Z\tfalse\tY\t0.267212048922\t0.1326\t215\t\t\t-8534688874718947140\t1970-01-01T01:23:20.000000Z\t34\t00000000 1c 0b 20 a2 86 89 37 11 2c 14\tUSZMZVQE\n";
compiler.compile(
"create table y as (" +
"select" +
" to_int(x) i," +
" rnd_symbol('msft','ibm', 'googl') sym2," +
" round(rnd_double(0), 3) price," +
" to_timestamp('2018-01', 'yyyy-MM') + x * 120000000 timestamp," +
" rnd_boolean() b," +
" rnd_str(1,1,2) c," +
" rnd_double(2) d," +
" rnd_float(2) e," +
" rnd_short(10,1024) f," +
" rnd_date(to_date('2015', 'yyyy'), to_date('2016', 'yyyy'), 2) g," +
" rnd_symbol(4,4,4,2) ik," +
" rnd_long() j," +
" timestamp_sequence(to_timestamp(0), 1000000000) k," +
" rnd_byte(2,50) l," +
" rnd_bin(10, 20, 2) m," +
" rnd_str(5,16,2) n" +
" from long_sequence(30)" +
") timestamp(timestamp)"
, bindVariableService
);
bindVariableService.setLong(0, 4);
assertQueryAndCache(expected1, query, "timestamp", true);
bindVariableService.setLong(0, 6);
assertQueryAndCache(expected2, query, "timestamp", true);
} finally {
engine.releaseAllWriters();
engine.releaseAllReaders();
}
});
}
@Test
public void testRangeVariable() throws Exception {
String query = "select * from y limit :lo,:hi";
......
......@@ -46,6 +46,16 @@ public class BindVariableServiceTest {
}
}
@Test
public void testBinIndexedOverride() throws SqlException {
bindVariableService.setLong(0, 10);
try {
bindVariableService.setBin(0, null);
} catch (SqlException e) {
TestUtils.assertContains(e.getMessage(), "bind variable at 0 is already defined as LONG");
}
}
@Test
public void testBooleanOverride() throws SqlException {
bindVariableService.setLong("a", 10);
......@@ -56,6 +66,15 @@ public class BindVariableServiceTest {
}
}
@Test
public void testBooleanIndexedOverride() throws SqlException {
bindVariableService.setLong(0, 10);
try {
bindVariableService.setBoolean(0, false);
} catch (SqlException e) {
TestUtils.assertContains(e.getMessage(), "bind variable at 0 is already defined as LONG");
}
}
@Test
public void testByteOverride() throws SqlException {
bindVariableService.setLong("a", 10);
......@@ -66,6 +85,16 @@ public class BindVariableServiceTest {
}
}
@Test
public void testByteIndexedOverride() throws SqlException {
bindVariableService.setLong(0, 10);
try {
bindVariableService.setByte(0, (byte) 5);
} catch (SqlException e) {
TestUtils.assertContains(e.getMessage(), "bind variable at 0 is already defined as LONG");
}
}
@Test
public void testDateOverride() throws SqlException {
bindVariableService.setLong("a", 10);
......@@ -76,6 +105,16 @@ public class BindVariableServiceTest {
}
}
@Test
public void testDateIndexedOverride() throws SqlException {
bindVariableService.setLong(0, 10);
try {
bindVariableService.setDate(0, 5);
} catch (SqlException e) {
TestUtils.assertContains(e.getMessage(), "bind variable at 0 is already defined as LONG");
}
}
@Test
public void testDoubleOverride() throws SqlException {
bindVariableService.setInt("a", 10);
......@@ -86,6 +125,16 @@ public class BindVariableServiceTest {
}
}
@Test
public void testDoubleIndexedOverride() throws SqlException {
bindVariableService.setInt(2, 10);
try {
bindVariableService.setDouble(2, 5.4);
} catch (SqlException e) {
TestUtils.assertContains(e.getMessage(), "bind variable at 2 is already defined as INT");
}
}
@Test
public void testFloatOverride() throws SqlException {
bindVariableService.setLong("a", 10);
......@@ -96,6 +145,16 @@ public class BindVariableServiceTest {
}
}
@Test
public void testFloatIndexedOverride() throws SqlException {
bindVariableService.setLong(1, 10);
try {
bindVariableService.setFloat(1, 5);
} catch (SqlException e) {
TestUtils.assertContains(e.getMessage(), "bind variable at 1 is already defined as LONG");
}
}
@Test
public void testIntOverride() throws SqlException {
bindVariableService.setLong("a", 10);
......@@ -106,6 +165,16 @@ public class BindVariableServiceTest {
}
}
@Test
public void testIntIndexedOverride() throws SqlException {
bindVariableService.setLong(0, 10);
try {
bindVariableService.setInt(0, 5);
} catch (SqlException e) {
TestUtils.assertContains(e.getMessage(), "bind variable at 0 is already defined as LONG");
}
}
@Test
public void testLongOverride() throws SqlException {
bindVariableService.setInt("a", 10);
......@@ -116,6 +185,16 @@ public class BindVariableServiceTest {
}
}
@Test
public void testLongIndexedOverride() throws SqlException {
bindVariableService.setInt(0, 10);
try {
bindVariableService.setLong(0, 5);
} catch (SqlException e) {
TestUtils.assertContains(e.getMessage(), "bind variable at 0 is already defined as INT");
}
}
@Test
public void testShortOverride() throws SqlException {
bindVariableService.setLong("a", 10);
......@@ -126,6 +205,16 @@ public class BindVariableServiceTest {
}
}
@Test
public void testShortIndexedOverride() throws SqlException {
bindVariableService.setLong(0, 10);
try {
bindVariableService.setShort(0, (short) 5);
} catch (SqlException e) {
TestUtils.assertContains(e.getMessage(), "bind variable at 0 is already defined as LONG");
}
}
@Test
public void testStrOverride() throws SqlException {
bindVariableService.setLong("a", 10);
......@@ -136,6 +225,16 @@ public class BindVariableServiceTest {
}
}
@Test
public void testStrIndexedOverride() throws SqlException {
bindVariableService.setLong(0, 10);
try {
bindVariableService.setStr(0, "ok");
} catch (SqlException e) {
TestUtils.assertContains(e.getMessage(), "bind variable at 0 is already defined as LONG");
}
}
@Test
public void testTimestampOverride() throws SqlException {
bindVariableService.setLong("a", 10);
......@@ -145,4 +244,14 @@ public class BindVariableServiceTest {
TestUtils.assertContains(e.getMessage(), "bind variable 'a' is already defined as LONG");
}
}
@Test
public void testTimestampIndexedOverride() throws SqlException {
bindVariableService.setLong(0, 10);
try {
bindVariableService.setTimestamp(0, 5);
} catch (SqlException e) {
TestUtils.assertContains(e.getMessage(), "bind variable at 0 is already defined as LONG");
}
}
}
\ No newline at end of file
......@@ -119,6 +119,62 @@ public class BindVariablesTest extends BaseFunctionFactoryTest {
func.getStr(builder.getRecord()));
}
@Test
public void testBinIndexed() throws SqlException {
Rnd rnd = new Rnd();
TestBinarySequence sequence = new TestBinarySequence();
sequence.of(rnd.nextBytes(256));
bindVariableService.setBin(1, null);
bindVariableService.setBin(0, sequence);
Function func = expr("to_char(?)")
.withFunction(new ToCharBinFunctionFactory())
.withFunction(new ToCharTimestampFunctionFactory())
.$();
func.init(null, bindVariableService);
Function func2 = expr("length(?)")
.withFunction(new LengthBinFunctionFactory())
.$();
func2.init(null, bindVariableService);
TestUtils.assertEquals("00000000 56 54 4a 57 43 50 53 57 48 59 52 58 50 45 48 4e\n" +
"00000010 52 58 47 5a 53 58 55 58 49 42 42 54 47 50 47 57\n" +
"00000020 46 46 59 55 44 45 59 59 51 45 48 42 48 46 4f 57\n" +
"00000030 4c 50 44 58 59 53 42 45 4f 55 4f 4a 53 48 52 55\n" +
"00000040 45 44 52 51 51 55 4c 4f 46 4a 47 45 54 4a 52 53\n" +
"00000050 5a 53 52 59 52 46 42 56 54 4d 48 47 4f 4f 5a 5a\n" +
"00000060 56 44 5a 4a 4d 59 49 43 43 58 5a 4f 55 49 43 57\n" +
"00000070 45 4b 47 48 56 55 56 53 44 4f 54 53 45 44 59 59\n" +
"00000080 43 54 47 51 4f 4c 59 58 57 43 4b 59 4c 53 55 57\n" +
"00000090 44 53 57 55 47 53 48 4f 4c 4e 56 54 49 51 42 5a\n" +
"000000a0 58 49 4f 56 49 4b 4a 53 4d 53 53 55 51 53 52 4c\n" +
"000000b0 54 4b 56 56 53 4a 4f 4a 49 50 48 5a 45 50 49 48\n" +
"000000c0 56 4c 54 4f 56 4c 4a 55 4d 4c 47 4c 48 4d 4c 4c\n" +
"000000d0 45 4f 59 50 48 52 49 50 5a 49 4d 4e 5a 5a 52 4d\n" +
"000000e0 46 4d 42 45 5a 47 48 57 56 44 4b 46 4c 4f 50 4a\n" +
"000000f0 4f 58 50 4b 52 47 49 49 48 59 48 42 4f 51 4d 59",
func.getStr(builder.getRecord()));
// check that bin bind variable length is accurate
Assert.assertEquals(256, func2.getLong(builder.getRecord()));
sequence.of(rnd.nextBytes(16));
bindVariableService.setBin(0, sequence);
TestUtils.assertEquals("00000000 53 53 4d 50 47 4c 55 4f 48 4e 5a 48 5a 53 51 4c",
func.getStr(builder.getRecord()));
bindVariableService.setBin(0, new TestBinarySequence().of(rnd.nextBytes(24)));
TestUtils.assertEquals("00000000 44 47 4c 4f 47 49 46 4f 55 53 5a 4d 5a 56 51 45\n" +
"00000010 42 4e 44 43 51 43 45 48",
func.getStr(builder.getRecord()));
}
@Test
public void testBoolean() throws SqlException {
bindVariableService.setBoolean("xyz", false);
......@@ -133,6 +189,21 @@ public class BindVariablesTest extends BaseFunctionFactoryTest {
Assert.assertFalse(func.getBool(builder.getRecord()));
}
@Test
public void testBooleanIndexed() throws SqlException {
bindVariableService.setBoolean(1, false);
bindVariableService.setBoolean(0, false);
Function func = expr("not ?")
.withFunction(new NotFunctionFactory())
.$();
func.init(null, bindVariableService);
Assert.assertTrue(func.getBool(builder.getRecord()));
bindVariableService.setBoolean(0, true);
Assert.assertFalse(func.getBool(builder.getRecord()));
}
@Test
public void testByte() throws SqlException {
bindVariableService.setByte("xyz", (byte) 8);
......@@ -148,6 +219,22 @@ public class BindVariablesTest extends BaseFunctionFactoryTest {
Assert.assertEquals(32, func.getByte(builder.getRecord()));
}
@Test
public void testByteIndexed() throws SqlException {
bindVariableService.setByte(1, (byte) -1);
bindVariableService.setByte(0, (byte) 8);
Function func = expr("b + ?")
.withFunction(new AddByteFunctionFactory())
.withColumn("b", ColumnType.BYTE, (byte) 22)
.$();
func.init(null, bindVariableService);
Assert.assertEquals(30, func.getByte(builder.getRecord()));
bindVariableService.setByte(0, (byte) 10);
Assert.assertEquals(32, func.getByte(builder.getRecord()));
}
@Test
public void testDate() throws SqlException, NumericException {
bindVariableService.setDate("xyz", DateFormatUtils.parseDateTime("2015-04-10T10:00:00.000Z"));
......@@ -162,6 +249,21 @@ public class BindVariablesTest extends BaseFunctionFactoryTest {
TestUtils.assertEquals("2015-08", func.getStr(builder.getRecord()));
}
@Test
public void testDateIndexed() throws SqlException, NumericException {
bindVariableService.setDate(1, 0);
bindVariableService.setDate(0, DateFormatUtils.parseDateTime("2015-04-10T10:00:00.000Z"));
Function func = expr("to_char(?, 'yyyy-MM')")
.withFunction(new ToCharDateFunctionFactory())
.$();
func.init(null, bindVariableService);
TestUtils.assertEquals("2015-04", func.getStr(builder.getRecord()));
bindVariableService.setDate(0, DateFormatUtils.parseDateTime("2015-08-10T10:00:00.000Z"));
TestUtils.assertEquals("2015-08", func.getStr(builder.getRecord()));
}
@Test
public void testDouble() throws SqlException {
bindVariableService.setDouble("xyz", 7.98821);
......@@ -198,6 +300,50 @@ public class BindVariablesTest extends BaseFunctionFactoryTest {
func.close();
}
@Test
public void testFloatIndexed() throws SqlException {
bindVariableService.setFloat(2, Float.NaN);
bindVariableService.setFloat(0, 7.6f);
bindVariableService.setFloat(1, 9.21f);
Function func = expr("? + ?")
.withFunction(new AddFloatFunctionFactory())
.$();
func.init(null, bindVariableService);
Assert.assertEquals(16.81f, func.getFloat(builder.getRecord()), 0.001f);
bindVariableService.setFloat(1, 0.13f);
func.init(null, bindVariableService);
Assert.assertEquals(7.73f, func.getFloat(builder.getRecord()), 0.001f);
func.close();
}
@Test
public void testDoubleIndexed() throws SqlException {
bindVariableService.setDouble(2, Double.NaN);
bindVariableService.setDouble(0, 0.223232);
bindVariableService.setDouble(1, 9.222333);
Function func = expr("? + ?")
.withFunction(new AddDoubleFunctionFactory())
.$();
func.init(null, bindVariableService);
Assert.assertEquals(9.445565, func.getDouble(builder.getRecord()), 0.001);
bindVariableService.setDouble(1, 0.1322990);
func.init(null, bindVariableService);
Assert.assertEquals(0.355531, func.getDouble(builder.getRecord()), 0.001);
func.close();
}
@Test
public void testInt() throws SqlException {
bindVariableService.setInt("xyz", 10);
......@@ -233,6 +379,42 @@ public class BindVariablesTest extends BaseFunctionFactoryTest {
Assert.assertEquals(33L, func.getLong(builder.getRecord()));
}
@Test
public void testLongIndexed() throws SqlException {
bindVariableService.setLong(2, 90000);
bindVariableService.setLong(0, 9);
bindVariableService.setLong(1, 20);
Function func = expr("? + ?")
.withFunction(new AddLongFunctionFactory())
.$();
func.init(null, bindVariableService);
Assert.assertEquals(29L, func.getLong(builder.getRecord()));
bindVariableService.setLong(0, 11);
bindVariableService.setLong(1, 33);
Assert.assertEquals(44L, func.getLong(builder.getRecord()));
}
@Test
public void testIntIndexed() throws SqlException {
bindVariableService.setInt(2, 9000);
bindVariableService.setInt(0, 9);
bindVariableService.setInt(1, 20);
Function func = expr("? + ?")
.withFunction(new AddIntFunctionFactory())
.$();
func.init(null, bindVariableService);
Assert.assertEquals(29, func.getInt(builder.getRecord()));
bindVariableService.setInt(0, 11);
bindVariableService.setInt(1, 33);
Assert.assertEquals(44, func.getInt(builder.getRecord()));
}
@Test
public void testShort() throws SqlException {
bindVariableService.setShort("xyz", (short) 8);
......@@ -248,6 +430,22 @@ public class BindVariablesTest extends BaseFunctionFactoryTest {
Assert.assertEquals(55, func.getShort(builder.getRecord()));
}
@Test
public void testShortIndexed() throws SqlException {
bindVariableService.setShort(1, (short) 2);
bindVariableService.setShort(0, (short) 8);
Function func = expr("b + ?")
.withFunction(new AddShortFunctionFactory())
.withColumn("b", ColumnType.SHORT, (short) 22)
.$();
func.init(null, bindVariableService);
Assert.assertEquals(30, func.getShort(builder.getRecord()));
bindVariableService.setShort(0, (short) 33);
Assert.assertEquals(55, func.getShort(builder.getRecord()));
}
@Test
public void testStr() throws SqlException {
bindVariableService.setStr("str", "abc");
......@@ -262,6 +460,20 @@ public class BindVariablesTest extends BaseFunctionFactoryTest {
Assert.assertEquals(5, func.getInt(builder.getRecord()));
}
@Test
public void testStrIndexed() throws SqlException {
bindVariableService.setStr(0, "abc");
Function func = expr("length(?)")
.withFunction(new LengthStrFunctionFactory())
.$();
func.init(null, bindVariableService);
Assert.assertEquals(3, func.getInt(builder.getRecord()));
bindVariableService.setStr(0, "hello");
Assert.assertEquals(5, func.getInt(builder.getRecord()));
}
@Test
public void testStr2() throws SqlException {
bindVariableService.setStr("str", "abcd");
......@@ -276,6 +488,21 @@ public class BindVariablesTest extends BaseFunctionFactoryTest {
TestUtils.assertEquals("bcd", func.getStr(builder.getRecord()));
}
@Test
public void testStr2Indexed() throws SqlException {
bindVariableService.setLong(2, 10000);
bindVariableService.setInt(0, 1);
bindVariableService.setStr(1, "abcd");
Function func = expr("substr(?, ?)")
.withFunction(new SubStrFunctionFactory())
.$();
func.init(null, bindVariableService);
TestUtils.assertEquals("bcd", func.getStr(builder.getRecord()));
}
@Test
public void testTimestamp() throws SqlException, NumericException {
bindVariableService.setTimestamp("xyz", com.questdb.std.microtime.DateFormatUtils.parseDateTime("2015-04-10T10:00:00.000Z"));
......@@ -291,6 +518,22 @@ public class BindVariablesTest extends BaseFunctionFactoryTest {
TestUtils.assertEquals("2015-08", func.getStr(builder.getRecord()));
}
@Test
public void testTimestampIndexed() throws SqlException, NumericException {
bindVariableService.setTimestamp(1, 25);
bindVariableService.setTimestamp(0, com.questdb.std.microtime.DateFormatUtils.parseDateTime("2015-04-10T10:00:00.000Z"));
Function func = expr("to_char(?, 'yyyy-MM')")
.withFunction(new ToCharTimestampFunctionFactory())
.$();
func.init(null, bindVariableService);
TestUtils.assertEquals("2015-04", func.getStr(builder.getRecord()));
bindVariableService.setTimestamp(0, com.questdb.std.microtime.DateFormatUtils.parseDateTime("2015-08-10T10:00:00.000Z"));
TestUtils.assertEquals("2015-08", func.getStr(builder.getRecord()));
}
@Test
public void testUndefined() {
try {
......@@ -304,6 +547,19 @@ public class BindVariablesTest extends BaseFunctionFactoryTest {
}
}
@Test
public void testUndefinedIndexed() {
try {
expr("to_char(?, 'yyyy-MM')")
.withFunction(new ToCharDateFunctionFactory())
.withFunction(new ToCharTimestampFunctionFactory())
.$();
} catch (SqlException e) {
Assert.assertEquals(8, e.getPosition());
TestUtils.assertContains(e.getMessage(), "no bind variable defined at index 0");
}
}
private FunctionBuilder expr(String expression) {
return builder.withExpression(expression);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册