From 002f0141ff6c02fa0a218e3241893ad39667035b Mon Sep 17 00:00:00 2001 From: Joan Augsburger Date: Thu, 27 Aug 2020 08:57:37 +0100 Subject: [PATCH] fix(cutlass): pg-wire - bound parameters in nodejs fix #290 (#566) --- .../cutlass/pgwire/PGConnectionContext.java | 283 +++++++++++------- .../cutlass/pgwire/PGJobContextTest.java | 178 ++++++++++- 2 files changed, 344 insertions(+), 117 deletions(-) diff --git a/core/src/main/java/io/questdb/cutlass/pgwire/PGConnectionContext.java b/core/src/main/java/io/questdb/cutlass/pgwire/PGConnectionContext.java index 307ce22c2..26d32a5f1 100644 --- a/core/src/main/java/io/questdb/cutlass/pgwire/PGConnectionContext.java +++ b/core/src/main/java/io/questdb/cutlass/pgwire/PGConnectionContext.java @@ -30,6 +30,8 @@ import io.questdb.cairo.*; import io.questdb.cairo.security.AllowAllCairoSecurityContext; import io.questdb.cairo.sql.*; import io.questdb.cutlass.text.TextLoader; +import io.questdb.cutlass.text.types.TypeAdapter; +import io.questdb.cutlass.text.types.TypeManager; import io.questdb.griffin.*; import io.questdb.griffin.engine.functions.bind.BindVariableService; import io.questdb.log.Log; @@ -107,6 +109,7 @@ public class PGConnectionContext implements IOContext, Mutable { private final BindVariableSetter byteSetter = this::setByteBindVariable; private final BindVariableSetter byteTxtSetter = this::setByteTextBindVariable; private final BindVariableSetter booleanSetter = this::setBooleanBindVariable; + private final BindVariableSetter charSetter = this::setCharBindVariable; private final BindVariableSetter strSetter = this::setStrBindVariable; private final BindVariableSetter noopSetter = this::setNoopBindVariable; private final ObjList columnAppenders = new ObjList<>(); @@ -135,6 +138,11 @@ public class PGConnectionContext implements IOContext, Mutable { private Rnd rnd; private int rowCount; private boolean isEmptyQuery; + // private final ObjList probes = new ObjList<>(); + private final DirectByteCharSequence parameterHolder = new DirectByteCharSequence(); + private final IntList parameterFormats = new IntList(); + private final DirectCharSink utf8Sink; + private final TypeManager typeManager; public PGConnectionContext( CairoEngine engine, @@ -142,6 +150,8 @@ public class PGConnectionContext implements IOContext, Mutable { @Nullable MessageBus messageBus, int workerCount ) { + this.utf8Sink = new DirectCharSink(engine.getConfiguration().getTextConfiguration().getUtf8SinkSize()); + this.typeManager = new TypeManager(engine.getConfiguration().getTextConfiguration(), utf8Sink); this.nf = configuration.getNetworkFacade(); this.recvBufferSize = Numbers.ceilPow2(configuration.getRecvBufferSize()); this.recvBuffer = Unsafe.malloc(this.recvBufferSize); @@ -233,6 +243,7 @@ public class PGConnectionContext implements IOContext, Mutable { // todo: test that both of these are cleared (unit test) authenticationRequired = true; username = null; + typeManager.clear(); } @Override @@ -243,6 +254,7 @@ public class PGConnectionContext implements IOContext, Mutable { Unsafe.free(sendBuffer, sendBufferSize); Unsafe.free(recvBuffer, recvBufferSize); Misc.free(path); + Misc.free(utf8Sink); } @Override @@ -460,6 +472,16 @@ public class PGConnectionContext implements IOContext, Mutable { } } + public void setCharBindVariable(int index, long address, int valueLen) throws BadProtocolException { + CharacterStoreEntry e = queryCharacterStore.newEntry(); + if (Chars.utf8Decode(address, address + valueLen, e)) { + bindVariableService.setChar(index, queryCharacterStore.toImmutable().charAt(0)); // + } else { + LOG.error().$("invalid UTF8 bytes [index=").$(index).$(']').$(); + throw BadProtocolException.INSTANCE; + } + } + private static void ensureValueLength(int required, int valueLen) throws BadProtocolException { if (required != valueLen) { LOG.error().$("bad parameter value length [required=").$(required).$(", actual=").$(valueLen).$(']').$(); @@ -627,48 +649,32 @@ public class PGConnectionContext implements IOContext, Mutable { } } - private void bindVariables( - long lo, - long msgLimit, - short parameterCount, - @Transient ObjList bindVariableSetters - ) throws BadProtocolException, SqlException { - // do we have enough data for all codes? - if (lo + Short.BYTES * parameterCount > msgLimit) { - LOG.error().$("invalid format code count [value=").$(parameterCount).$(']').$(); + private void bindParameterFormats(long lo, + long msgLimit, + short parameterFormatCount) throws BadProtocolException { + if (lo + Short.BYTES * parameterFormatCount > msgLimit) { + LOG.error().$("invalid format code count [value=").$(parameterFormatCount).$(']').$(); throw BadProtocolException.INSTANCE; } - for (int j = 0; j < parameterCount; j++) { - final short code = getShort(lo + j * Short.BYTES); - if (code == 1) { - continue; - } - - if (code == 0) { - bindVariableSetters.setQuick(j * 2, bindVariableSetters.getQuick(j * 2 + 1)); - } else { - LOG.error().$("unsupported code [index=").$(j).$(", code=").$(code).$(']').$(); - throw BadProtocolException.INSTANCE; - } + for (int i = 0; i < parameterFormatCount; i++) { + final short code = getShort(lo + i * Short.BYTES); + parameterFormats.add(code); } + } - lo += parameterCount * Short.BYTES; - - checkNotTrue(lo + Short.BYTES > msgLimit, "could not read parameter value count"); - parameterCount = getShort(lo); - - if (parameterCount != bindVariableService.getIndexedVariableCount()) { - LOG.error() - .$("parameter count from parse message does not match parameter value count [valueCount=").$(parameterCount) - .$(", typeCount=").$(bindVariableService.getIndexedVariableCount()) - .$(']').$(); - throw BadProtocolException.INSTANCE; - } + private void bindParameterValues( + long lo, + long msgLimit, + short parameterValueCount, + @Transient ObjList bindVariableSetters + ) throws BadProtocolException, SqlException { - lo += Short.BYTES; + boolean inferTypes = parameterValueCount != bindVariableService.getIndexedVariableCount(); + boolean allTextFormat = parameterFormats.size() == 0 || (parameterFormats.size() == 1 && parameterFormats.get(0) == 0); + boolean allBinaryFormat = parameterFormats.size() == 1 && parameterFormats.get(0) == 1; - for (int j = 0; j < parameterCount; j++) { + for (int j = 0; j < parameterValueCount; j++) { if (lo + Integer.BYTES > msgLimit) { LOG.error().$("could not read parameter value length [index=").$(j).$(']').$(); throw BadProtocolException.INSTANCE; @@ -690,11 +696,54 @@ public class PGConnectionContext implements IOContext, Mutable { throw BadProtocolException.INSTANCE; } ensureData(lo, valueLen, msgLimit, j); + //infer type if needed + if (inferTypes) { + int pgType = inferParameterType(lo, valueLen); + if (pgType == -1) { + LOG.error().$("invalid parameter type for parameter #[").$(j).$(']').$(); + throw BadProtocolException.INSTANCE; + } + setupBindVariable(bindVariableSetters, j, pgType); + } + // apply parameter format + if (allTextFormat || (!allBinaryFormat) && parameterFormats.get(j) == 0) { + bindVariableSetters.setQuick(j * 2, bindVariableSetters.getQuick(j * 2 + 1)); + } + // bind parameter value bindVariableSetters.getQuick(j * 2).set(j, lo, valueLen); lo += valueLen; } } + private void compileQuery(SqlCompiler compiler, AssociativeCache factoryCache) throws SqlException, PeerDisconnectedException, PeerIsSlowToReadException { + final CompiledQuery cc = compiler.compile(queryText, sqlExecutionContext); + sqlExecutionContext.storeTelemetry(cc.getType(), TelemetryOrigin.PG_WIRE); + + switch (cc.getType()) { + case CompiledQuery.SELECT: + currentFactory = cc.getRecordCursorFactory(); + queryTag = TAG_SELECT; + factoryCache.put(queryText, currentFactory); + break; + case CompiledQuery.INSERT: + currentInsertStatement = cc.getInsertStatement(); + queryTag = TAG_INSERT; + factoryCache.put(queryText, currentInsertStatement); + break; + case CompiledQuery.COPY_LOCAL: + queryTag = TAG_COPY; + sendCopyInResponse(compiler.getEngine(), cc.getTextLoader()); + break; + case CompiledQuery.SET: + queryTag = TAG_SET; + break; + default: + // DDL SQL + queryTag = TAG_OK; + break; + } + } + private void checkNotTrue(boolean check, String message) throws BadProtocolException { if (check) { // we did not find 0 within message limit @@ -817,6 +866,16 @@ public class PGConnectionContext implements IOContext, Mutable { return statementName; } + private int inferParameterType(long lo, int valueLen) { + for (int i = 0; i < typeManager.getProbeCount(); i++) { + TypeAdapter typeAdapter = typeManager.getProbe(i); + if (typeAdapter.probe(parameterHolder.of(lo, lo + valueLen))) { + return typeOids.get(typeAdapter.getType()); + } + } + return -1; + } + /** * returns address of where parsing stopped. If there are remaining bytes left * int the buffer they need to be passed again in parse function along with @@ -886,7 +945,7 @@ public class PGConnectionContext implements IOContext, Mutable { } switch (type) { case 'P': - processParse(address, lo, msgLimit, compiler, factoryCache, namedStatementMap, bindVariableSetters); + processParse(address, lo, msgLimit, factoryCache, namedStatementMap, bindVariableSetters); break; case 'X': // 'Terminate' @@ -896,11 +955,16 @@ public class PGConnectionContext implements IOContext, Mutable { processClose(lo, msgLimit, namedStatementMap); break; case 'B': // bind - processBind(bindVariableSetters, msgLimit, lo, namedStatementMap); + processBind(bindVariableSetters, compiler, factoryCache, msgLimit, lo, namedStatementMap); break; case 'E': // execute processExecute(); break; + case 'H': // flush + send(); + responseAsciiSink.reset(); + prepareForNewQuery(); + break; case 'S': // sync prepareReadyForQuery(responseAsciiSink); send(); @@ -934,21 +998,6 @@ public class PGConnectionContext implements IOContext, Mutable { } } - private void populateAppender() { - columnAppenders.extendAndSet(ColumnType.INT, this::appendIntCol); - columnAppenders.extendAndSet(ColumnType.STRING, this::appendStrColumn); - columnAppenders.extendAndSet(ColumnType.SYMBOL, this::appendSymbolColumn); - columnAppenders.extendAndSet(ColumnType.LONG, this::appendLongColumn); - columnAppenders.extendAndSet(ColumnType.SHORT, this::appendShortColumn); - columnAppenders.extendAndSet(ColumnType.DOUBLE, this::appendDoubleColumn); - columnAppenders.extendAndSet(ColumnType.FLOAT, this::appendFloatColumn); - columnAppenders.extendAndSet(ColumnType.TIMESTAMP, this::appendTimestampColumn); - columnAppenders.extendAndSet(ColumnType.DATE, this::appendDateColumn); - columnAppenders.extendAndSet(ColumnType.BOOLEAN, this::appendBooleanColumn); - columnAppenders.extendAndSet(ColumnType.BYTE, this::appendByteColumn); - columnAppenders.extendAndSet(ColumnType.BINARY, this::appendBinColumn); - } - private void prepareError(SqlException e) { responseAsciiSink.put(MESSAGE_TYPE_ERROR_RESPONSE); long addr = responseAsciiSink.skip(); @@ -1033,40 +1082,19 @@ public class PGConnectionContext implements IOContext, Mutable { currentInsertStatement = null; } - private void processBind(@Transient ObjList bindVariableSetters, - long msgLimit, - long lo, - @Transient CharSequenceObjHashMap namedStatementMap) throws BadProtocolException, SqlException { - long hi; - short parameterCount; - hi = getStringLength(lo, msgLimit); - checkNotTrue(hi == -1, "bad portal name length [msgType='B']"); - - lo = hi + 1; - hi = getStringLength(lo, msgLimit); - checkNotTrue(hi == -1, "bad prepared statement name length [msgType='B']"); - - CharSequence statementName = getStatementName(lo, hi); - if (statementName != null) { - setupNamedStatement(bindVariableSetters, namedStatementMap, statementName); - } - - lo = hi + 1; - checkNotTrue(lo + Short.BYTES > msgLimit, "could not read parameter format code count"); - - parameterCount = getShort(lo); - if (parameterCount != bindVariableService.getIndexedVariableCount()) { - LOG.error() - .$("parameter count from parse message does not match format code count [fmtCodeCount=").$(parameterCount) - .$(", typeCount=").$(bindVariableService.getIndexedVariableCount()) - .$(']').$(); - throw BadProtocolException.INSTANCE; - } - if (parameterCount > 0) { - lo += Short.BYTES; - bindVariables(lo, msgLimit, parameterCount, bindVariableSetters); - } - prepareBindComplete(); + private void populateAppender() { + columnAppenders.extendAndSet(ColumnType.INT, this::appendIntCol); + columnAppenders.extendAndSet(ColumnType.STRING, this::appendStrColumn); + columnAppenders.extendAndSet(ColumnType.SYMBOL, this::appendSymbolColumn); + columnAppenders.extendAndSet(ColumnType.LONG, this::appendLongColumn); + columnAppenders.extendAndSet(ColumnType.SHORT, this::appendShortColumn); + columnAppenders.extendAndSet(ColumnType.DOUBLE, this::appendDoubleColumn); + columnAppenders.extendAndSet(ColumnType.FLOAT, this::appendFloatColumn); + columnAppenders.extendAndSet(ColumnType.TIMESTAMP, this::appendTimestampColumn); + columnAppenders.extendAndSet(ColumnType.DATE, this::appendDateColumn); + columnAppenders.extendAndSet(ColumnType.BOOLEAN, this::appendBooleanColumn); + columnAppenders.extendAndSet(ColumnType.BYTE, this::appendByteColumn); + columnAppenders.extendAndSet(ColumnType.BINARY, this::appendBinColumn); } private void processClose(long lo, long msgLimit, CharSequenceObjHashMap namedStatementMap) throws BadProtocolException { @@ -1117,11 +1145,56 @@ public class PGConnectionContext implements IOContext, Mutable { } } + private void processBind(@Transient ObjList bindVariableSetters, + @Transient SqlCompiler compiler, + @Transient AssociativeCache factoryCache, + long msgLimit, + long lo, + @Transient CharSequenceObjHashMap namedStatementMap) throws BadProtocolException, SqlException, PeerDisconnectedException, PeerIsSlowToReadException { + long hi; + short parameterFormatCount; + short parameterValueCount; + + hi = getStringLength(lo, msgLimit); + checkNotTrue(hi == -1, "bad portal name length [msgType='B']"); + + lo = hi + 1; + hi = getStringLength(lo, msgLimit); + checkNotTrue(hi == -1, "bad prepared statement name length [msgType='B']"); + + CharSequence statementName = getStatementName(lo, hi); + if (statementName != null) { + setupNamedStatement(bindVariableSetters, namedStatementMap, statementName); + } + + lo = hi + 1; + checkNotTrue(lo + Short.BYTES > msgLimit, "could not read parameter format code count"); + + parameterFormats.clear(); + parameterFormatCount = getShort(lo); + lo += Short.BYTES; + if (parameterFormatCount > 0) { + bindParameterFormats(lo, msgLimit, parameterFormatCount); + } + + lo += parameterFormatCount * Short.BYTES; + checkNotTrue(lo + Short.BYTES > msgLimit, "could not read parameter value count"); + parameterValueCount = getShort(lo); + + if (parameterValueCount > 0) { + lo += Short.BYTES; + bindParameterValues(lo, msgLimit, parameterValueCount, bindVariableSetters); + } + if (statementName == null && queryText.length() > 0) { + compileQuery(compiler, factoryCache); + } + prepareBindComplete(); + } + private void processParse( long address, long lo, long msgLimit, - @Transient SqlCompiler compiler, @Transient AssociativeCache factoryCache, @Transient CharSequenceObjHashMap namedStatementMap, @Transient ObjList bindVariableSetters @@ -1150,6 +1223,7 @@ public class PGConnectionContext implements IOContext, Mutable { short parameterCount = getShort(lo); IntList bindVariableTypes = null; + bindVariableSetters.clear(); if (parameterCount > 0) { if (lo + Short.BYTES + parameterCount * Integer.BYTES > msgLimit) { LOG.error() @@ -1179,34 +1253,7 @@ public class PGConnectionContext implements IOContext, Mutable { // of all of them, which is looked up by query text final Object statement = factoryCache.peek(queryText); if (statement == null) { - if (queryText.length() > 0) { - final CompiledQuery cc = compiler.compile(queryText, sqlExecutionContext); - sqlExecutionContext.storeTelemetry(cc.getType(), TelemetryOrigin.PG_WIRE); - - switch (cc.getType()) { - case CompiledQuery.SELECT: - currentFactory = cc.getRecordCursorFactory(); - queryTag = TAG_SELECT; - factoryCache.put(queryText, currentFactory); - break; - case CompiledQuery.INSERT: - currentInsertStatement = cc.getInsertStatement(); - queryTag = TAG_INSERT; - factoryCache.put(queryText, currentInsertStatement); - break; - case CompiledQuery.COPY_LOCAL: - queryTag = TAG_COPY; - sendCopyInResponse(compiler.getEngine(), cc.getTextLoader()); - break; - case CompiledQuery.SET: - queryTag = TAG_SET; - break; - default: - // DDL SQL - queryTag = TAG_OK; - break; - } - } else { + if (queryText.length() <= 0) { isEmptyQuery = true; } } else { @@ -1623,6 +1670,11 @@ public class PGConnectionContext implements IOContext, Mutable { bindVariableSetters.add(strSetter); bindVariableSetters.add(strSetter); break; + case PG_CHAR: + bindVariableService.setChar(idx, (char) 0); + bindVariableSetters.add(charSetter); + bindVariableSetters.add(charSetter); + break; case PG_DATE: bindVariableService.setDate(idx, Numbers.LONG_NaN); bindVariableSetters.add(noopSetter); @@ -1651,7 +1703,6 @@ public class PGConnectionContext implements IOContext, Mutable { short pc, @Transient ObjList bindVariableSetters, IntList bindVariableTypes) throws SqlException { - bindVariableSetters.clear(); for (int idx = 0; idx < pc; idx++) { int pgType = getInt(lo + idx * Integer.BYTES); bindVariableTypes.add(pgType); diff --git a/core/src/test/java/io/questdb/cutlass/pgwire/PGJobContextTest.java b/core/src/test/java/io/questdb/cutlass/pgwire/PGJobContextTest.java index 6f711b3ee..a12e31012 100644 --- a/core/src/test/java/io/questdb/cutlass/pgwire/PGJobContextTest.java +++ b/core/src/test/java/io/questdb/cutlass/pgwire/PGJobContextTest.java @@ -1872,7 +1872,7 @@ public class PGJobContextTest extends AbstractGriffinTest { @Test public void testInsertTableDoesNotExistPrepared() throws Exception { - testInsertTableDoesNotExist(false, "Cannot append. File does not exist"); + testInsertTableDoesNotExist(false, "table 'x' does not exist"); } @Test @@ -2171,6 +2171,182 @@ public class PGJobContextTest extends AbstractGriffinTest { }); } + @Test + public void testCharIntLongDoubleBooleanParametersWithoutExplicitParameterTypeHex() throws Exception { + String script = ">0000006e00030000757365720078797a0064617461626173650071646200636c69656e745f656e636f64696e67005554463800446174655374796c650049534f0054696d655a6f6e65004575726f70652f4c6f6e646f6e0065787472615f666c6f61745f64696769747300320000\n" + + "<520000000800000003\n" + + ">70000000076f6800\n" + + "<520000000800000000530000001154696d655a6f6e6500474d5400530000001d6170706c69636174696f6e5f6e616d6500517565737444420053000000187365727665725f76657273696f6e0031312e33005300000019696e74656765725f6461746574696d6573006f6e005300000019636c69656e745f656e636f64696e670055544638005a0000000549\n" + + ">50000000300073656c65637420782c202024312c2024322066726f6d206c6f6e675f73657175656e63652832293b000000\n" + + ">4200000021000000010000000200000001330000000a353030303030303030300000\n" + + ">44000000065000\n" + + ">45000000090000000000\n" + + ">4800000004\n" + + "<31000000043200000004540000004400037800000040010001000000140004ffffffff0000243100000040010002000000170004ffffffff0000243200000040010003000000140004ffffffff0000440000001e0003000000013100000001330000000a35303030303030303030440000001e0003000000013200000001330000000a35303030303030303030430000000d53454c454354203200\n"; + + assertHexScript(NetworkFacadeImpl.INSTANCE, + NetworkFacadeImpl.INSTANCE, + script, + new DefaultPGWireConfiguration() { + @Override + public String getDefaultPassword() { + return "oh"; + } + + @Override + public String getDefaultUsername() { + return "xyz"; + } + }); + } + + @Test + public void testInferringBadParamenterWithoutExplicitParameterTypeHex2() throws Exception { + String script = ">0000006e00030000757365720078797a0064617461626173650071646200636c69656e745f656e636f64696e67005554463800446174655374796c650049534f0054696d655a6f6e65004575726f70652f4c6f6e646f6e0065787472615f666c6f61745f64696769747300320000\n" + + "<520000000800000003\n" + + ">70000000076f6800\n" + + "<520000000800000000530000001154696d655a6f6e6500474d5400530000001d6170706c69636174696f6e5f6e616d6500517565737444420053000000187365727665725f76657273696f6e0031312e33005300000019696e74656765725f6461746574696d6573006f6e005300000019636c69656e745f656e636f64696e670055544638005a0000000549\n" + + ">50000000300073656c65637420782c202024312c2024322066726f6d206c6f6e675f73657175656e63652832293b000000\n" + + ">4200000021000000010000000200000001000000000a353030303030303030300000\n" + + "0000006e00030000757365720078797a0064617461626173650071646200636c69656e745f656e636f64696e67005554463800446174655374796c650049534f0054696d655a6f6e65004575726f70652f4c6f6e646f6e0065787472615f666c6f61745f64696769747300320000\n" + + "<520000000800000003\n" + + ">70000000076f6800\n" + + "<520000000800000000530000001154696d655a6f6e6500474d5400530000001d6170706c69636174696f6e5f6e616d6500517565737444420053000000187365727665725f76657273696f6e0031312e33005300000019696e74656765725f6461746574696d6573006f6e005300000019636c69656e745f656e636f64696e670055544638005a0000000549\n" + + ">500000002c0073656c65637420782c202024312066726f6d206c6f6e675f73657175656e63652832293b000000\n" + + ">420000001100000000000100000001330000\n" + + ">44000000065000\n" + + ">45000000090000000000\n" + + ">4800000004\n" + + "<31000000043200000004540000002f00027800000040010001000000140004ffffffff0000243100000040010002000000170004ffffffff000044000000100002000000013100000001334400000010000200000001320000000133430000000d53454c454354203200\n"; + + assertHexScript(NetworkFacadeImpl.INSTANCE, + NetworkFacadeImpl.INSTANCE, + script, + new DefaultPGWireConfiguration() { + @Override + public String getDefaultPassword() { + return "oh"; + } + + @Override + public String getDefaultUsername() { + return "xyz"; + } + }); + } + + @Test + public void testIntAndLongParametersWithoutExplicitParameterTypeButOneExplicitTextFormatHex() throws Exception { + String script = ">0000006e00030000757365720078797a0064617461626173650071646200636c69656e745f656e636f64696e67005554463800446174655374796c650049534f0054696d655a6f6e65004575726f70652f4c6f6e646f6e0065787472615f666c6f61745f64696769747300320000\n" + + "<520000000800000003\n" + + ">70000000076f6800\n" + + "<520000000800000000530000001154696d655a6f6e6500474d5400530000001d6170706c69636174696f6e5f6e616d6500517565737444420053000000187365727665725f76657273696f6e0031312e33005300000019696e74656765725f6461746574696d6573006f6e005300000019636c69656e745f656e636f64696e670055544638005a0000000549\n" + + ">50000000300073656c65637420782c202024312c2024322066726f6d206c6f6e675f73657175656e63652832293b000000\n" + + ">4200000021000000010000000200000001330000000a353030303030303030300000\n" + + ">44000000065000\n" + + ">45000000090000000000\n" + + ">4800000004\n" + + "<31000000043200000004540000004400037800000040010001000000140004ffffffff0000243100000040010002000000170004ffffffff0000243200000040010003000000140004ffffffff0000440000001e0003000000013100000001330000000a35303030303030303030440000001e0003000000013200000001330000000a35303030303030303030430000000d53454c454354203200\n"; + + assertHexScript(NetworkFacadeImpl.INSTANCE, + NetworkFacadeImpl.INSTANCE, + script, + new DefaultPGWireConfiguration() { + @Override + public String getDefaultPassword() { + return "oh"; + } + + @Override + public String getDefaultUsername() { + return "xyz"; + } + }); + } + + @Test + public void testIntParameterWithoutExplicitParameterTypeButExplicitTextFormatHex() throws Exception { + String script = ">0000006e00030000757365720078797a0064617461626173650071646200636c69656e745f656e636f64696e67005554463800446174655374796c650049534f0054696d655a6f6e65004575726f70652f4c6f6e646f6e0065787472615f666c6f61745f64696769747300320000\n" + + "<520000000800000003\n" + + ">70000000076f6800\n" + + "<520000000800000000530000001154696d655a6f6e6500474d5400530000001d6170706c69636174696f6e5f6e616d6500517565737444420053000000187365727665725f76657273696f6e0031312e33005300000019696e74656765725f6461746574696d6573006f6e005300000019636c69656e745f656e636f64696e670055544638005a0000000549\n" + + ">500000002c0073656c65637420782c202024312066726f6d206c6f6e675f73657175656e63652832293b000000\n" + + ">4200000013000000010000000100000001330000\n" + + ">44000000065000\n" + + ">45000000090000000000\n" + + ">4800000004\n" + + "<31000000043200000004540000002f00027800000040010001000000140004ffffffff0000243100000040010002000000170004ffffffff000044000000100002000000013100000001334400000010000200000001320000000133430000000d53454c454354203200\n"; + + assertHexScript(NetworkFacadeImpl.INSTANCE, + NetworkFacadeImpl.INSTANCE, + script, + new DefaultPGWireConfiguration() { + @Override + public String getDefaultPassword() { + return "oh"; + } + + @Override + public String getDefaultUsername() { + return "xyz"; + } + }); + } + + @Test + public void testSendingBufferWhenFlushMessageReceivedeHex() throws Exception { + String script = ">0000006e00030000757365720078797a0064617461626173650071646200636c69656e745f656e636f64696e67005554463800446174655374796c650049534f0054696d655a6f6e65004575726f70652f4c6f6e646f6e0065787472615f666c6f61745f64696769747300320000\n" + + "<520000000800000003\n" + + ">70000000076f6800\n" + + "<520000000800000000530000001154696d655a6f6e6500474d5400530000001d6170706c69636174696f6e5f6e616d6500517565737444420053000000187365727665725f76657273696f6e0031312e33005300000019696e74656765725f6461746574696d6573006f6e005300000019636c69656e745f656e636f64696e670055544638005a0000000549\n" + + ">500000002c0073656c65637420782c202024312066726f6d206c6f6e675f73657175656e63652832293b000000\n" + + ">420000001100000000000100000001330000\n" + + ">44000000065000\n" + + ">45000000090000000000\n" + + ">4800000004\n" + + "<31000000043200000004540000002f00027800000040010001000000140004ffffffff0000243100000040010002000000170004ffffffff000044000000100002000000013100000001334400000010000200000001320000000133430000000d53454c454354203200\n" + + ">4800000004\n" + + ">5300000004\n" + + "<5a0000000549\n" + + ">5800000004"; + + assertHexScript(NetworkFacadeImpl.INSTANCE, + NetworkFacadeImpl.INSTANCE, + script, + new DefaultPGWireConfiguration() { + @Override + public String getDefaultPassword() { + return "oh"; + } + + @Override + public String getDefaultUsername() { + return "xyz"; + } + }); + } + + private void testInsertTableDoesNotExist(boolean simple, String expectedError) throws Exception { // we are going to: // 1. create a table -- GitLab