提交 690f985f 编写于 作者: V Vlad Ilyushchenko

misc: ServerMain - working standalone http server using Cairo (storage) and Griffin (query) engines

上级 d6dce89e
......@@ -31,10 +31,7 @@ import com.questdb.cutlass.http.processors.TextImportProcessorConfiguration;
import com.questdb.cutlass.line.udp.LineUdpReceiverConfiguration;
import com.questdb.cutlass.text.TextConfiguration;
import com.questdb.network.*;
import com.questdb.std.FilesFacade;
import com.questdb.std.FilesFacadeImpl;
import com.questdb.std.Numbers;
import com.questdb.std.NumericException;
import com.questdb.std.*;
import com.questdb.std.microtime.MicrosecondClock;
import com.questdb.std.microtime.MicrosecondClockImpl;
import com.questdb.std.str.Path;
......@@ -98,7 +95,6 @@ public class PropServerConfiguration implements ServerConfigurationV2 {
private final int mkdirMode;
private final int parallelIndexThreshold;
private final int readerPoolMaxSegments;
private final CharSequence root;
private final long spinLockTimeoutUs;
private final int sqlCacheRows;
private final int sqlCacheBlocks;
......@@ -128,13 +124,14 @@ public class PropServerConfiguration implements ServerConfigurationV2 {
private final int lineUdpMsgCount;
private final int lineUdpReceiveBufferSize;
private final MimeTypesCache mimeTypesCache;
private final String databaseRoot;
private final String keepAliveHeader;
private int bindIPv4Address;
private int bindPort;
private int lineUdpBindIPV4Address;
private int lineUdpPort;
public PropServerConfiguration(String root, Properties properties) throws ServerConfigurationException {
this.root = root;
this.connectionPoolInitialCapacity = getInt(properties, "http.connection.pool.initial.capacity", 16);
this.connectionStringPoolCapacity = getInt(properties, "http.connection.string.pool.capacity", 128);
this.multipartHeaderBufferSize = getIntSize(properties, "http.multipart.header.buffer.size", 512);
......@@ -146,6 +143,15 @@ public class PropServerConfiguration implements ServerConfigurationV2 {
this.sendBufferSize = getIntSize(properties, "http.send.buffer.size", 2 * 1024 * 1024);
this.indexFileName = getString(properties, "http.static.index.file.name", "index.html");
int keepAliveTimeout = getInt(properties, "http.keep-alive.timeout", 30);
int keepAliveMax = getInt(properties, "http.keep-alive.max", 1_000_000);
if (keepAliveTimeout > 0 && keepAliveMax > 0) {
this.keepAliveHeader = "Keep-Alive: timeout=5, max=10000" + Misc.EOL;
} else {
this.keepAliveHeader = null;
}
final String publicDirectory = getString(properties, "http.static.pubic.directory", "public");
// translate public directory into absolute path
// this will generate some garbage, but this is ok - we just doing this once on startup
......@@ -155,6 +161,13 @@ public class PropServerConfiguration implements ServerConfigurationV2 {
this.publicDirectory = new File(root, publicDirectory).getAbsolutePath();
}
final String databaseRoot = getString(properties, "cairo.root", "db");
if (new File(databaseRoot).isAbsolute()) {
this.databaseRoot = databaseRoot;
} else {
this.databaseRoot = new File(root, databaseRoot).getAbsolutePath();
}
this.abortBrokenUploads = getBoolean(properties, "http.text.abort.broken.uploads", true);
this.activeConnectionLimit = getInt(properties, "http.net.active.connection.limit", 256);
this.eventCapacity = getInt(properties, "http.net.event.capacity", 1024);
......@@ -178,6 +191,7 @@ public class PropServerConfiguration implements ServerConfigurationV2 {
this.timestampAdapterPoolCapacity = getInt(properties, "http.text.timestamp.adapter.pool.capacity", 64);
this.utf8SinkSize = getIntSize(properties, "http.text.utf8.sink.size", 4096);
parseBindTo(properties, "http.bind.to", "0.0.0.0:9000", (a, p) -> {
bindIPv4Address = a;
bindPort = p;
......@@ -374,6 +388,11 @@ public class PropServerConfiguration implements ServerConfigurationV2 {
public CharSequence getPublicDirectory() {
return publicDirectory;
}
@Override
public String getKeepAliveHeader() {
return keepAliveHeader;
}
}
private class PropTextImportProcessorConfiguration implements TextImportProcessorConfiguration {
......@@ -677,7 +696,7 @@ public class PropServerConfiguration implements ServerConfigurationV2 {
@Override
public CharSequence getRoot() {
return root;
return databaseRoot;
}
@Override
......
......@@ -24,7 +24,14 @@
package com.questdb;
import com.questdb.cairo.CairoEngine;
import com.questdb.net.http.HttpServer;
import com.questdb.cutlass.http.HttpRequestProcessor;
import com.questdb.cutlass.http.HttpRequestProcessorFactory;
import com.questdb.cutlass.http.HttpServer;
import com.questdb.cutlass.http.HttpServerConfiguration;
import com.questdb.cutlass.http.processors.JsonQueryProcessor;
import com.questdb.cutlass.http.processors.StaticContentProcessor;
import com.questdb.cutlass.http.processors.TableStatusCheckProcessor;
import com.questdb.cutlass.http.processors.TextImportProcessor;
import com.questdb.std.CharSequenceObjHashMap;
import com.questdb.std.Os;
......@@ -81,6 +88,56 @@ public class ServerMain {
final PropServerConfiguration configuration = new PropServerConfiguration(rootDirectory, properties);
final CairoEngine cairoEngine = new CairoEngine(configuration.getCairoConfiguration());
final HttpServer httpServer = new HttpServer(configuration.getHttpServerConfiguration());
httpServer.bind(new HttpRequestProcessorFactory() {
@Override
public String getUrl() {
return "/exec";
}
@Override
public HttpRequestProcessor newInstance() {
return new JsonQueryProcessor(cairoEngine);
}
});
httpServer.bind(new HttpRequestProcessorFactory() {
@Override
public String getUrl() {
return "/imp";
}
@Override
public HttpRequestProcessor newInstance() {
return new TextImportProcessor(configuration.getHttpServerConfiguration().getTextImportProcessorConfiguration(), cairoEngine);
}
});
httpServer.bind(new HttpRequestProcessorFactory() {
@Override
public String getUrl() {
return "/chk";
}
@Override
public HttpRequestProcessor newInstance() {
return new TableStatusCheckProcessor(cairoEngine);
}
});
httpServer.bind(new HttpRequestProcessorFactory() {
@Override
public String getUrl() {
return HttpServerConfiguration.DEFAULT_PROCESSOR_URL;
}
@Override
public HttpRequestProcessor newInstance() {
return new StaticContentProcessor(configuration.getHttpServerConfiguration().getStaticContentProcessorConfiguration());
}
});
httpServer.start();
}
public static void main(String[] args) throws Exception {
......@@ -118,7 +175,7 @@ public class ServerMain {
private static void extractSite(String dir, boolean force) throws URISyntaxException, IOException {
System.out.println("Preparing site content...");
URL url = HttpServer.class.getResource("/site/");
URL url = ServerMain.class.getResource("/site/");
String[] components = url.toURI().toString().split("!");
FileSystem fs = null;
final Path source;
......
......@@ -58,6 +58,11 @@ class DefaultHttpServerConfiguration implements HttpServerConfiguration {
public CharSequence getPublicDirectory() {
return ".";
}
@Override
public String getKeepAliveHeader() {
return null;
}
};
private final TextImportProcessorConfiguration textImportProcessorConfiguration = new DefaultTextImportProcessorConfiguration();
......
......@@ -299,11 +299,6 @@ public class JsonQueryProcessor implements HttpRequestProcessor, Closeable {
}
private void sendConfirmation(HttpChunkedResponseSocket socket) throws PeerDisconnectedException, PeerIsSlowToReadException {
// todo: test what happens when we cannot send a simple confirmation out
// it looks like on retry "send" we don't even handle this case unless the whole
// of the confirmation fits nicely into send buffer
// what happens when it doesn't?
socket.put('{').putQuoted("ddl").put(':').putQuoted("OK").put('}');
socket.sendChunk();
socket.done();
......@@ -417,7 +412,7 @@ public class JsonQueryProcessor implements HttpRequestProcessor, Closeable {
state.count++;
if (state.fetchAll && state.count > state.stop) {
state.cancellationHandler.check();
// state.cancellationHandler.check();
continue;
}
......
......@@ -43,12 +43,14 @@ public class StaticContentProcessor implements HttpRequestProcessor, Closeable {
private final PrefixedPath prefixedPath;
private final CharSequence indexFileName;
private final FilesFacade ff;
private final String keepAliveHeader;
public StaticContentProcessor(StaticContentProcessorConfiguration configuration) {
this.mimeTypes = configuration.getMimeTypesCache();
this.prefixedPath = new PrefixedPath(configuration.getPublicDirectory());
this.indexFileName = configuration.getIndexFileName();
this.ff = configuration.getFilesFacade();
this.keepAliveHeader = configuration.getKeepAliveHeader();
}
@Override
......@@ -202,6 +204,9 @@ public class StaticContentProcessor implements HttpRequestProcessor, Closeable {
header.put("Accept-Ranges: bytes").put(Misc.EOL);
header.put("Content-Range: bytes ").put(lo).put('-').put(state.sendMax).put('/').put(length).put(Misc.EOL);
header.put("ETag: ").put(ff.getLastModified(path)).put(Misc.EOL);
if (keepAliveHeader != null) {
header.put(keepAliveHeader);
}
header.send();
resumeSend(context, dispatcher);
}
......@@ -241,6 +246,10 @@ public class StaticContentProcessor implements HttpRequestProcessor, Closeable {
header.put("Content-Disposition: attachment; filename=\"").put(FileNameExtractorCharSequence.get(path)).put("\"").put(Misc.EOL);
}
header.put("ETag: ").put('"').put(ff.getLastModified(path)).put('"').put(Misc.EOL);
if (keepAliveHeader != null) {
header.put(keepAliveHeader);
}
header.send();
resumeSend(context, dispatcher);
}
......
......@@ -34,4 +34,6 @@ public interface StaticContentProcessorConfiguration {
MimeTypesCache getMimeTypesCache();
CharSequence getPublicDirectory();
String getKeepAliveHeader();
}
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* 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.cutlass.http.processors;
import com.questdb.cairo.CairoEngine;
import com.questdb.cairo.TableUtils;
import com.questdb.cutlass.http.*;
import com.questdb.network.IODispatcher;
import com.questdb.network.IOOperation;
import com.questdb.std.Chars;
import com.questdb.std.Misc;
import com.questdb.std.str.Path;
public class TableStatusCheckProcessor implements HttpRequestProcessor {
private final CairoEngine cairoEngine;
private final Path path = new Path();
public TableStatusCheckProcessor(CairoEngine cairoEngine) {
this.cairoEngine = cairoEngine;
}
private static String toResponse(int existenceCheckResult) {
switch (existenceCheckResult) {
case TableUtils.TABLE_EXISTS:
return "Exists";
case TableUtils.TABLE_DOES_NOT_EXIST:
return "Does not exist";
default:
return "Reserved name";
}
}
@Override
public void onHeadersReady(HttpConnectionContext context) {
}
@Override
public void resumeRecv(HttpConnectionContext context, IODispatcher<HttpConnectionContext> dispatcher) {
}
@Override
public void resumeSend(HttpConnectionContext context, IODispatcher<HttpConnectionContext> dispatcher) {
}
@Override
public void onRequestComplete(HttpConnectionContext context, IODispatcher<HttpConnectionContext> dispatcher) throws PeerDisconnectedException, PeerIsSlowToReadException {
CharSequence tableName = context.getRequestHeader().getUrlParam("j");
if (tableName == null) {
context.simpleResponse().sendStatus(400, "table name missing");
} else {
int check = cairoEngine.getStatus(path, tableName);
if (Chars.equalsNc("json", context.getRequestHeader().getUrlParam("f"))) {
HttpChunkedResponseSocket r = context.getChunkedResponseSocket();
r.status(200, "application/json");
// todo: configure this header externally
r.headers().put("Keep-Alive: timeout=5, max=10000").put(Misc.EOL);
r.sendHeader();
r.put('{').putQuoted("status").put(':').putQuoted(toResponse(check)).put('}');
r.sendChunk();
r.done();
} else {
context.simpleResponse().sendStatus(200, toResponse(check));
}
}
dispatcher.registerChannel(context, IOOperation.READ);
}
}
......@@ -24,11 +24,14 @@
package com.questdb.griffin.engine.functions.eq;
import com.questdb.cairo.CairoConfiguration;
import com.questdb.cairo.ColumnType;
import com.questdb.cairo.sql.Function;
import com.questdb.cairo.sql.Record;
import com.questdb.griffin.FunctionFactory;
import com.questdb.griffin.engine.functions.BinaryFunction;
import com.questdb.griffin.engine.functions.BooleanFunction;
import com.questdb.griffin.engine.functions.UnaryFunction;
import com.questdb.std.Numbers;
import com.questdb.std.ObjList;
public class EqDoubleFunctionFactory implements FunctionFactory {
......@@ -39,6 +42,48 @@ public class EqDoubleFunctionFactory implements FunctionFactory {
@Override
public Function newInstance(ObjList<Function> args, int position, CairoConfiguration configuration) {
// this is probably a special case factory
// NaN is always a double, so this could lead comparisons of all primitive types
// to NaN route to this factory. Obviously comparing naively will not work
// We have to check arg types and when NaN is present we would generate special case
// functions for NaN checks.
Function left = args.getQuick(0);
Function rigt = args.getQuick(1);
if (left.isConstant() && left.getType() == ColumnType.DOUBLE && Double.isNaN(left.getDouble(null))) {
switch (rigt.getType()) {
case ColumnType.INT:
return new FuncIntIsNaN(position, rigt);
case ColumnType.LONG:
return new FuncLongIsNaN(position, rigt);
case ColumnType.DATE:
return new FuncDateIsNaN(position, rigt);
case ColumnType.TIMESTAMP:
return new FuncTimestampIsNaN(position, rigt);
case ColumnType.FLOAT:
return new FuncFloatIsNaN(position, rigt);
default:
// double
return new FuncDoubleIsNaN(position, rigt);
}
} else if (rigt.isConstant() && rigt.getType() == ColumnType.DOUBLE && Double.isNaN(rigt.getDouble(null))) {
switch (left.getType()) {
case ColumnType.INT:
return new FuncIntIsNaN(position, left);
case ColumnType.LONG:
return new FuncLongIsNaN(position, left);
case ColumnType.DATE:
return new FuncDateIsNaN(position, left);
case ColumnType.TIMESTAMP:
return new FuncTimestampIsNaN(position, left);
case ColumnType.FLOAT:
return new FuncFloatIsNaN(position, left);
default:
// double
return new FuncDoubleIsNaN(position, left);
}
}
return new Func(position, args.getQuick(0), args.getQuick(1));
}
......@@ -69,4 +114,118 @@ public class EqDoubleFunctionFactory implements FunctionFactory {
return right;
}
}
private static class FuncIntIsNaN extends BooleanFunction implements UnaryFunction {
private final Function arg;
public FuncIntIsNaN(int position, Function arg) {
super(position);
this.arg = arg;
}
@Override
public boolean getBool(Record rec) {
return arg.getInt(rec) == Numbers.INT_NaN;
}
@Override
public Function getArg() {
return arg;
}
}
private static class FuncLongIsNaN extends BooleanFunction implements UnaryFunction {
private final Function arg;
public FuncLongIsNaN(int position, Function arg) {
super(position);
this.arg = arg;
}
@Override
public boolean getBool(Record rec) {
return arg.getLong(rec) == Numbers.LONG_NaN;
}
@Override
public Function getArg() {
return arg;
}
}
private static class FuncDateIsNaN extends BooleanFunction implements UnaryFunction {
private final Function arg;
public FuncDateIsNaN(int position, Function arg) {
super(position);
this.arg = arg;
}
@Override
public boolean getBool(Record rec) {
return arg.getDate(rec) == Numbers.LONG_NaN;
}
@Override
public Function getArg() {
return arg;
}
}
private static class FuncTimestampIsNaN extends BooleanFunction implements UnaryFunction {
private final Function arg;
public FuncTimestampIsNaN(int position, Function arg) {
super(position);
this.arg = arg;
}
@Override
public boolean getBool(Record rec) {
return arg.getTimestamp(rec) == Numbers.LONG_NaN;
}
@Override
public Function getArg() {
return arg;
}
}
private static class FuncFloatIsNaN extends BooleanFunction implements UnaryFunction {
private final Function arg;
public FuncFloatIsNaN(int position, Function arg) {
super(position);
this.arg = arg;
}
@Override
public boolean getBool(Record rec) {
return Float.isNaN(arg.getFloat(rec));
}
@Override
public Function getArg() {
return arg;
}
}
private static class FuncDoubleIsNaN extends BooleanFunction implements UnaryFunction {
private final Function arg;
public FuncDoubleIsNaN(int position, Function arg) {
super(position);
this.arg = arg;
}
@Override
public boolean getBool(Record rec) {
return Double.isNaN(arg.getDouble(rec));
}
@Override
public Function getArg() {
return arg;
}
}
}
......@@ -145,7 +145,5 @@ public class EqStrFunctionFactory implements FunctionFactory {
return b != null && Chars.equals(a, b);
}
}
}
......@@ -173,7 +173,7 @@ public class PropServerConfigurationTest {
Assert.assertSame(MillisecondClockImpl.INSTANCE, configuration.getCairoConfiguration().getMillisecondClock());
Assert.assertSame(MicrosecondClockImpl.INSTANCE, configuration.getCairoConfiguration().getMicrosecondClock());
Assert.assertSame(NetworkFacadeImpl.INSTANCE, configuration.getLineUdpReceiverConfiguration().getNetworkFacade());
TestUtils.assertEquals(root.getAbsolutePath(), configuration.getCairoConfiguration().getRoot());
TestUtils.assertEquals(new File(root, "db").getAbsolutePath(), configuration.getCairoConfiguration().getRoot());
// assert mime types
TestUtils.assertEquals("application/json", configuration.getHttpServerConfiguration().getStaticContentProcessorConfiguration().getMimeTypesCache().get("json"));
......
......@@ -2225,6 +2225,11 @@ public class IODispatcherTest {
public CharSequence getPublicDirectory() {
return baseDir;
}
@Override
public String getKeepAliveHeader() {
return null;
}
};
@Override
......
......@@ -52,7 +52,7 @@ public abstract class AbstractFunctionFactoryTest extends BaseFunctionFactoryTes
public void assertFailure(boolean forceConstant, int expectedPosition, CharSequence expectedMsg, Object... args) {
try {
callCustomised(forceConstant, args);
callCustomised(forceConstant, true, args);
Assert.fail();
} catch (SqlException e) {
Assert.assertEquals(expectedPosition, e.getPosition());
......@@ -64,10 +64,10 @@ public abstract class AbstractFunctionFactoryTest extends BaseFunctionFactoryTes
}
protected Invocation call(Object... args) throws SqlException {
return callCustomised(false, args);
return callCustomised(false, true, args);
}
protected Invocation callCustomised(boolean forceConstant, Object... args) throws SqlException {
protected Invocation callCustomised(boolean forceConstant, boolean argTypeFromSig, Object... args) throws SqlException {
setUp2();
toShortRefs = 0;
toByteRefs = 0;
......@@ -127,7 +127,7 @@ public abstract class AbstractFunctionFactoryTest extends BaseFunctionFactoryTes
pos,
forceConstant,
metadata,
true,
argTypeFromSig,
constVarArg,
expression1,
expression2,
......@@ -141,7 +141,7 @@ public abstract class AbstractFunctionFactoryTest extends BaseFunctionFactoryTes
pos,
forceConstant,
metadata,
true,
argTypeFromSig,
constVarArg,
expression1,
expression2,
......@@ -156,7 +156,7 @@ public abstract class AbstractFunctionFactoryTest extends BaseFunctionFactoryTes
pos,
forceConstant,
metadata,
true,
argTypeFromSig,
constVarArg,
expression1,
expression2,
......@@ -166,7 +166,6 @@ public abstract class AbstractFunctionFactoryTest extends BaseFunctionFactoryTes
}
} else {
if (!setOperation) {
expression1.put(name).put('(');
expression2.put(name).put('(');
......@@ -246,6 +245,10 @@ public abstract class AbstractFunctionFactoryTest extends BaseFunctionFactoryTes
return ColumnType.LONG;
}
if (arg instanceof Float) {
return ColumnType.FLOAT;
}
Assert.fail("Unsupported type: " + arg.getClass());
return -1;
}
......@@ -266,12 +269,21 @@ public abstract class AbstractFunctionFactoryTest extends BaseFunctionFactoryTes
case ColumnType.LONG:
return (long) arg < 0 && (long) arg != Numbers.LONG_NaN;
case ColumnType.SHORT:
// short is passed as int
return (int) arg < 0;
case ColumnType.BYTE:
// byte is passed as int
// short is passed as int
return (int) arg < 0;
case ColumnType.DOUBLE:
// double can be auto-overloaded, e.g. lesser types passed
// into this method. Even though method accepts double we could
// have byte, short, int, long, float, timestamp and date
if (arg instanceof Integer) {
return (Integer) arg < 0;
}
if (arg instanceof Long) {
return (Long) arg < 0;
}
return (double) arg < 0;
case ColumnType.FLOAT:
return (float) arg < 0;
......@@ -339,7 +351,13 @@ public abstract class AbstractFunctionFactoryTest extends BaseFunctionFactoryTes
sink.put((Boolean) value);
break;
case ColumnType.DOUBLE:
sink.put((Double) value, 5);
if (value instanceof Integer) {
sink.put((Integer) value);
} else if (value instanceof Long) {
sink.put((Long) value);
} else {
sink.put((Double) value, 5);
}
break;
case ColumnType.FLOAT:
sink.put((Float) value, 5);
......@@ -383,6 +401,9 @@ public abstract class AbstractFunctionFactoryTest extends BaseFunctionFactoryTes
Assert.assertEquals(expected, function2.getBool(record));
}
public void andAssertOnlyColumnValues(boolean expected) {
Assert.assertEquals(expected, function2.getBool(record));
}
public void andAssert(CharSequence expected) {
if (function1.getType() == ColumnType.STRING) {
......@@ -509,9 +530,23 @@ public abstract class AbstractFunctionFactoryTest extends BaseFunctionFactoryTes
@Override
public double getDouble(int col) {
Object value = args[col];
if (value instanceof Integer) {
return ((Integer) value).doubleValue();
}
if (value instanceof Long) {
return ((Long) value).doubleValue();
}
return (double) args[col];
}
@Override
public float getFloat(int col) {
return (float) args[col];
}
@Override
public CharSequence getStr(int col) {
return (CharSequence) args[col];
......
......@@ -23,9 +23,17 @@
package com.questdb.griffin.engine.functions.eq;
import com.questdb.cairo.sql.Function;
import com.questdb.griffin.FunctionFactory;
import com.questdb.griffin.SqlException;
import com.questdb.griffin.engine.AbstractFunctionFactoryTest;
import com.questdb.griffin.engine.functions.constants.DateConstant;
import com.questdb.griffin.engine.functions.constants.DoubleConstant;
import com.questdb.griffin.engine.functions.constants.FloatConstant;
import com.questdb.griffin.engine.functions.constants.TimestampConstant;
import com.questdb.std.Numbers;
import com.questdb.std.ObjList;
import org.junit.Assert;
import org.junit.Test;
public class EqDoubleFunctionFactoryTest extends AbstractFunctionFactoryTest {
......@@ -50,16 +58,158 @@ public class EqDoubleFunctionFactoryTest extends AbstractFunctionFactoryTest {
call(Double.NaN, 77.9).andAssert(false);
}
@Test
public void testLeftNaNInt() throws SqlException {
call(Double.NaN, 98).andAssert(false);
}
@Test
public void testLeftNaNLong() throws SqlException {
call(Double.NaN, 99099112312313100L).andAssert(false);
}
@Test
public void testLeftNaNFloat() throws SqlException {
FunctionFactory factory = getFunctionFactory();
ObjList<Function> args = new ObjList<>();
args.add(new FloatConstant(1, 3.4f));
args.add(new DoubleConstant(2, Double.NaN));
Function function = factory.newInstance(args, 4, configuration);
Assert.assertFalse(function.getBool(null));
Assert.assertTrue(function.isConstant());
}
@Test
public void testRightNaNFloat() throws SqlException {
FunctionFactory factory = getFunctionFactory();
ObjList<Function> args = new ObjList<>();
args.add(new DoubleConstant(2, Double.NaN));
args.add(new FloatConstant(1, 5.1f) {
@Override
public boolean isConstant() {
return false;
}
});
Function function = factory.newInstance(args, 4, configuration);
Assert.assertFalse(function.getBool(null));
Assert.assertFalse(function.isConstant());
}
@Test
public void testLeftNaNLongNaN() throws SqlException {
// for constant expression this would generate
// NaN = NaN the outcome will be false
// however for col = NaN, where col is long this must be true
callCustomised(false, false, Double.NaN, Numbers.LONG_NaN).andAssertOnlyColumnValues(true);
}
@Test
public void testLeftNaNFloatNaN() throws SqlException {
FunctionFactory factory = getFunctionFactory();
ObjList<Function> args = new ObjList<>();
args.add(new FloatConstant(1, Float.NaN));
args.add(new DoubleConstant(2, Double.NaN));
Function function = factory.newInstance(args, 4, configuration);
Assert.assertTrue(function.getBool(null));
Assert.assertTrue(function.isConstant());
}
@Test
public void testLeftNaNIntNaN() throws SqlException {
// for constant expression this would generate
// NaN = NaN the outcome will be false
// however for col = NaN, where col is long this must be true
callCustomised(false, false, Double.NaN, Numbers.INT_NaN).andAssertOnlyColumnValues(true);
}
@Test
public void testRightNaN() throws SqlException {
call(77.1, Double.NaN).andAssert(false);
}
@Test
public void testRightNaNInt() throws SqlException {
call(123, Double.NaN).andAssert(false);
}
@Test
public void testRightNaNLong() throws SqlException {
call(9992290902224442L, Double.NaN).andAssert(false);
}
@Test
public void testNullEqualsNull() throws SqlException {
call(Double.NaN, Double.NaN).andAssert(true);
}
@Test
public void testRightNaNTimestamp() throws SqlException {
FunctionFactory factory = getFunctionFactory();
ObjList<Function> args = new ObjList<>();
args.add(new TimestampConstant(1, 20000L));
args.add(new DoubleConstant(2, Double.NaN));
Function function = factory.newInstance(args, 4, configuration);
Assert.assertFalse(function.getBool(null));
}
@Test
public void testRightNaNTimestampNaN() throws SqlException {
FunctionFactory factory = getFunctionFactory();
ObjList<Function> args = new ObjList<>();
args.add(new TimestampConstant(1, Numbers.LONG_NaN) {
@Override
public boolean isConstant() {
return false;
}
});
args.add(new DoubleConstant(2, Double.NaN));
Function function = factory.newInstance(args, 4, configuration);
Assert.assertTrue(function.getBool(null));
Assert.assertFalse(function.isConstant());
}
@Test
public void testLeftNaNTimestamp() throws SqlException {
FunctionFactory factory = getFunctionFactory();
ObjList<Function> args = new ObjList<>();
args.add(new DoubleConstant(2, Double.NaN));
args.add(new TimestampConstant(1, 20000L));
Function function = factory.newInstance(args, 4, configuration);
Assert.assertFalse(function.getBool(null));
Assert.assertTrue(function.isConstant());
}
@Test
public void testLeftNaNDate() throws SqlException {
FunctionFactory factory = getFunctionFactory();
ObjList<Function> args = new ObjList<>();
args.add(new DoubleConstant(2, Double.NaN));
args.add(new DateConstant(1, 10000L));
Function function = factory.newInstance(args, 4, configuration);
Assert.assertFalse(function.getBool(null));
}
@Test
public void testRightNaNDate() throws SqlException {
FunctionFactory factory = getFunctionFactory();
ObjList<Function> args = new ObjList<>();
args.add(new DateConstant(1, 10000L));
args.add(new DoubleConstant(2, Double.NaN));
Function function = factory.newInstance(args, 4, configuration);
Assert.assertFalse(function.getBool(null));
}
@Test
public void testRightNaNDateNaN() throws SqlException {
FunctionFactory factory = getFunctionFactory();
ObjList<Function> args = new ObjList<>();
args.add(new DateConstant(1, Numbers.LONG_NaN));
args.add(new DoubleConstant(2, Double.NaN));
Function function = factory.newInstance(args, 4, configuration);
Assert.assertTrue(function.getBool(null));
Assert.assertTrue(function.isConstant());
}
@Override
protected FunctionFactory getFunctionFactory() {
return new EqDoubleFunctionFactory();
......
......@@ -51,7 +51,7 @@ public class RndSymbolListFunctionFactoryTest extends AbstractFunctionFactoryTes
set1.add(null);
CharSequenceHashSet set2 = new CharSequenceHashSet();
Invocation invocation = callCustomised(true, "ABC", "CDE", "XYZ", null);
Invocation invocation = callCustomised(true, true, "ABC", "CDE", "XYZ", null);
for (int i = 0; i < 1000; i++) {
Assert.assertTrue(set1.contains(invocation.getFunction1().getSymbol(null)));
Assert.assertTrue(set1.contains(invocation.getFunction2().getSymbol(null)));
......
......@@ -90,6 +90,7 @@ import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;
@Ignore
public class HttpServerTest extends AbstractJournalTest {
private final static Log LOG = LogFactory.getLog(HttpServerTest.class);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册