aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoruzhas <uzhas@ydb.tech>2022-08-08 17:11:48 +0300
committeruzhas <uzhas@ydb.tech>2022-08-08 17:11:48 +0300
commitdcc7af40b4a3c36f67fb1dca7c8d839a824a1598 (patch)
tree22af84799d5795208d2eb7dd8aa42c1cb75e7578
parent1edc976c2b2250754d482f29ef153c77ebffba59 (diff)
downloadydb-dcc7af40b4a3c36f67fb1dca7c8d839a824a1598.tar.gz
add initial tests for PgBuilder, add comments to IPgBuilder methods
-rw-r--r--ydb/library/yql/minikql/computation/mkql_value_builder_ut.cpp181
-rw-r--r--ydb/library/yql/parser/pg_wrapper/comp_factory.cpp104
2 files changed, 257 insertions, 28 deletions
diff --git a/ydb/library/yql/minikql/computation/mkql_value_builder_ut.cpp b/ydb/library/yql/minikql/computation/mkql_value_builder_ut.cpp
index 5f54b2dd24..11fd59e5f3 100644
--- a/ydb/library/yql/minikql/computation/mkql_value_builder_ut.cpp
+++ b/ydb/library/yql/minikql/computation/mkql_value_builder_ut.cpp
@@ -2,15 +2,33 @@
#include "mkql_computation_node_holders.h"
#include <ydb/library/yql/minikql/mkql_function_registry.h>
+#include <ydb/library/yql/minikql/mkql_type_builder.h>
#include <ydb/library/yql/minikql/invoke_builtins/mkql_builtins.h>
+#include <ydb/library/yql/parser/pg_catalog/catalog.h>
#include <library/cpp/testing/unittest/registar.h>
+namespace NYql {
+namespace NCommon {
+
+TString PgValueToNativeText(const NUdf::TUnboxedValuePod& value, ui32 pgTypeId);
+TString PgValueToNativeBinary(const NUdf::TUnboxedValuePod& value, ui32 pgTypeId);
+
+}
+}
+
namespace NKikimr {
using namespace NUdf;
+using namespace NYql::NCommon;
namespace NMiniKQL {
+namespace {
+ TString AsString(const TStringValue& v) {
+ return { v.Data(), v.Size() };
+ }
+}
+
class TMiniKQLValueBuilderTest: public TTestBase {
public:
TMiniKQLValueBuilderTest()
@@ -18,8 +36,15 @@ public:
, Env(Alloc)
, MemInfo("Memory")
, HolderFactory(Alloc.Ref(), MemInfo, FunctionRegistry.Get())
- , Builder(HolderFactory)
+ , Builder(HolderFactory, NUdf::EValidatePolicy::Exception)
+ , TypeInfoHelper(new TTypeInfoHelper())
+ , FunctionTypeInfoBuilder(Env, TypeInfoHelper, "", nullptr, {})
{
+ BoolOid = NYql::NPg::LookupType("bool").TypeId;
+ }
+
+ const IPgBuilder& GetPgBuilder() const {
+ return Builder.GetPgBuilder();
}
private:
@@ -29,11 +54,20 @@ private:
TMemoryUsageInfo MemInfo;
THolderFactory HolderFactory;
TDefaultValueBuilder Builder;
+ NUdf::ITypeInfoHelper::TPtr TypeInfoHelper;
+ TFunctionTypeInfoBuilder FunctionTypeInfoBuilder;
+ ui32 BoolOid = 0;
UNIT_TEST_SUITE(TMiniKQLValueBuilderTest);
UNIT_TEST(TestEmbeddedVariant);
UNIT_TEST(TestBoxedVariant);
UNIT_TEST(TestSubstring);
+ UNIT_TEST(TestPgValueFromErrors);
+ UNIT_TEST(TestPgValueFromText);
+ UNIT_TEST(TestPgValueFromBinary);
+ UNIT_TEST(TestConvertToFromPg);
+ UNIT_TEST(TestConvertToFromPgNulls);
+ UNIT_TEST(TestPgNewString);
UNIT_TEST_SUITE_END();
@@ -75,6 +109,151 @@ private:
UNIT_ASSERT_VALUES_EQUAL(string.AsStringValue().Data(), two.AsStringValue().Data());
}
+ void TestPgValueFromErrors() {
+ const TBindTerminator bind(&Builder); // to raise exception instead of abort
+ {
+ TStringValue error("");
+ auto r = GetPgBuilder().ValueFromText(BoolOid, "", error);
+ UNIT_ASSERT(!r);
+ UNIT_ASSERT_STRING_CONTAINS(AsString(error), "invalid input syntax for type boolean: \"\"");
+ }
+
+ {
+ TStringValue error("");
+ auto r = GetPgBuilder().ValueFromText(BoolOid, "zzzz", error);
+ UNIT_ASSERT(!r);
+ UNIT_ASSERT_STRING_CONTAINS(AsString(error), "Terminate was called, reason(85): Error in 'in' function: boolin, reason: invalid input syntax for type boolean: \"zzzz\"");
+ }
+
+ {
+ TStringValue error("");
+ auto r = GetPgBuilder().ValueFromBinary(BoolOid, "", error);
+ UNIT_ASSERT(!r);
+ UNIT_ASSERT_STRING_CONTAINS(AsString(error), "Error in 'recv' function: boolrecv, reason: no data left in message");
+ }
+
+ {
+ TStringValue error("");
+ auto r = GetPgBuilder().ValueFromBinary(BoolOid, "zzzz", error);
+ UNIT_ASSERT(!r);
+ UNIT_ASSERT_STRING_CONTAINS(AsString(error), "Not all data has been consumed by 'recv' function: boolrecv, data size: 4, consumed size: 1");
+ }
+ }
+
+ void TestPgValueFromText() {
+ const TBindTerminator bind(&Builder);
+ for (auto validTrue : { "t"sv, "true"sv }) {
+ TStringValue error("");
+ auto r = GetPgBuilder().ValueFromText(BoolOid, validTrue, error);
+ UNIT_ASSERT(r);
+ UNIT_ASSERT_VALUES_EQUAL(AsString(error), "");
+ auto s = PgValueToNativeText(r, BoolOid);
+ UNIT_ASSERT_VALUES_EQUAL(s, "t");
+ }
+
+ for (auto validFalse : { "f"sv, "false"sv }) {
+ TStringValue error("");
+ auto r = GetPgBuilder().ValueFromText(BoolOid, validFalse, error);
+ UNIT_ASSERT(r);
+ UNIT_ASSERT_VALUES_EQUAL(AsString(error), "");
+ auto s = PgValueToNativeText(r, BoolOid);
+ UNIT_ASSERT_VALUES_EQUAL(s, "f");
+ }
+ }
+
+ void TestPgValueFromBinary() {
+ const TBindTerminator bind(&Builder);
+ TStringValue error("");
+ auto t = GetPgBuilder().ValueFromText(BoolOid, "true", error);
+ UNIT_ASSERT(t);
+ auto f = GetPgBuilder().ValueFromText(BoolOid, "false", error);
+ UNIT_ASSERT(f);
+
+ auto ts = PgValueToNativeBinary(t, BoolOid);
+ auto fs = PgValueToNativeBinary(f, BoolOid);
+ {
+ auto r = GetPgBuilder().ValueFromBinary(BoolOid, ts, error);
+ UNIT_ASSERT(r);
+ auto s = PgValueToNativeText(r, BoolOid);
+ UNIT_ASSERT_VALUES_EQUAL(s, "t");
+ }
+
+ {
+ auto r = GetPgBuilder().ValueFromBinary(BoolOid, fs, error);
+ UNIT_ASSERT(r);
+ auto s = PgValueToNativeText(r, BoolOid);
+ UNIT_ASSERT_VALUES_EQUAL(s, "f");
+ }
+ }
+
+ void TestConvertToFromPg() {
+ const TBindTerminator bind(&Builder);
+ auto boolType = FunctionTypeInfoBuilder.SimpleType<bool>();
+ {
+ auto v = GetPgBuilder().ConvertToPg(TUnboxedValuePod(true), boolType, BoolOid);
+ auto s = PgValueToNativeText(v, BoolOid);
+ UNIT_ASSERT_VALUES_EQUAL(s, "t");
+
+ auto from = GetPgBuilder().ConvertFromPg(v, BoolOid, boolType);
+ UNIT_ASSERT_VALUES_EQUAL(from.Get<bool>(), true);
+ }
+
+ {
+ auto v = GetPgBuilder().ConvertToPg(TUnboxedValuePod(false), boolType, BoolOid);
+ auto s = PgValueToNativeText(v, BoolOid);
+ UNIT_ASSERT_VALUES_EQUAL(s, "f");
+
+ auto from = GetPgBuilder().ConvertFromPg(v, BoolOid, boolType);
+ UNIT_ASSERT_VALUES_EQUAL(from.Get<bool>(), false);
+ }
+ }
+
+ void TestConvertToFromPgNulls() {
+ const TBindTerminator bind(&Builder);
+ auto boolOptionalType = FunctionTypeInfoBuilder.Optional()->Item<bool>().Build();
+
+ {
+ auto v = GetPgBuilder().ConvertToPg(TUnboxedValuePod(), boolOptionalType, BoolOid);
+ UNIT_ASSERT(!v);
+ }
+
+ {
+ auto v = GetPgBuilder().ConvertFromPg(TUnboxedValuePod(), BoolOid, boolOptionalType);
+ UNIT_ASSERT(!v);
+ }
+ }
+
+ void TestPgNewString() {
+ {
+ auto& pgText = NYql::NPg::LookupType("text");
+ UNIT_ASSERT_VALUES_EQUAL(pgText.TypeLen, -1);
+
+ auto s = GetPgBuilder().NewString(pgText.TypeLen, pgText.TypeId, "ABC");
+ auto utf8Type = FunctionTypeInfoBuilder.SimpleType<TUtf8>();
+ auto from = GetPgBuilder().ConvertFromPg(s, pgText.TypeId, utf8Type);
+ UNIT_ASSERT_VALUES_EQUAL((TStringBuf)from.AsStringRef(), "ABC"sv);
+ }
+
+ {
+ auto& pgCString = NYql::NPg::LookupType("cstring");
+ UNIT_ASSERT_VALUES_EQUAL(pgCString.TypeLen, -2);
+
+ auto s = GetPgBuilder().NewString(pgCString.TypeLen, pgCString.TypeId, "ABC");
+ auto utf8Type = FunctionTypeInfoBuilder.SimpleType<TUtf8>();
+ auto from = GetPgBuilder().ConvertFromPg(s, pgCString.TypeId, utf8Type);
+ UNIT_ASSERT_VALUES_EQUAL((TStringBuf)from.AsStringRef(), "ABC"sv);
+ }
+
+ {
+ auto& byteaString = NYql::NPg::LookupType("bytea");
+ UNIT_ASSERT_VALUES_EQUAL(byteaString.TypeLen, -1);
+
+ auto s = GetPgBuilder().NewString(byteaString.TypeLen, byteaString.TypeId, "ABC");
+ auto stringType = FunctionTypeInfoBuilder.SimpleType<char*>();
+ auto from = GetPgBuilder().ConvertFromPg(s, byteaString.TypeId, stringType);
+ UNIT_ASSERT_VALUES_EQUAL((TStringBuf)from.AsStringRef(), "ABC"sv);
+ }
+ }
};
UNIT_TEST_SUITE_REGISTRATION(TMiniKQLValueBuilderTest);
diff --git a/ydb/library/yql/parser/pg_wrapper/comp_factory.cpp b/ydb/library/yql/parser/pg_wrapper/comp_factory.cpp
index 3267c60de7..f79300954c 100644
--- a/ydb/library/yql/parser/pg_wrapper/comp_factory.cpp
+++ b/ydb/library/yql/parser/pg_wrapper/comp_factory.cpp
@@ -149,6 +149,21 @@ Datum PointerDatumFromPod(const NUdf::TUnboxedValuePod& value) {
return (Datum)(((const char*)value.AsBoxed().Get()) + PallocHdrSize);
}
+NUdf::TUnboxedValue CreatePgString(i32 typeLen, ui32 targetTypeId, TStringBuf data) {
+ // typname => 'cstring', typlen => '-2', the only type with typlen == -2
+ // typname = > 'text', typlen = > '-1'
+ Y_UNUSED(targetTypeId); // todo: verify typeLen
+ Y_ENSURE(typeLen == -1 || typeLen == -2);
+ switch (typeLen) {
+ case -1:
+ return PointerDatumToPod((Datum)MakeVar(data));
+ case -2:
+ return PointerDatumToPod((Datum)MakeCString(data));
+ default:
+ Y_UNREACHABLE();
+ }
+}
+
void *MkqlAllocSetAlloc(MemoryContext context, Size size) {
auto fullSize = size + PallocHdrSize;
auto ptr = (char *)MKQLAllocDeprecated(fullSize);
@@ -1028,7 +1043,16 @@ NUdf::TUnboxedValuePod ConvertToPgValue(NUdf::TUnboxedValuePod value, TMaybe<NUd
}
template <NUdf::EDataSlot Slot, bool IsCString>
-NUdf::TUnboxedValuePod ConvertFromPgValue(NUdf::TUnboxedValuePod value) {
+NUdf::TUnboxedValuePod ConvertFromPgValue(NUdf::TUnboxedValuePod value, TMaybe<NUdf::EDataSlot> actualSlot = {}) {
+#ifndef NDEBUG
+ // todo: improve checks
+ if (actualSlot && Slot != *actualSlot) {
+ throw yexception() << "Invalid data slot in ConvertFromPgValue, expected " << Slot << ", but actual: " << *actualSlot;
+ }
+#else
+ Y_UNUSED(actualSlot);
+#endif
+
switch (Slot) {
case NUdf::EDataSlot::Bool:
return NUdf::TUnboxedValuePod((bool)DatumGetBool(ScalarDatumFromPod(value)));
@@ -1057,59 +1081,85 @@ NUdf::TUnboxedValuePod ConvertFromPgValue(NUdf::TUnboxedValuePod value) {
}
NUdf::TUnboxedValuePod ConvertFromPgValue(NUdf::TUnboxedValuePod source, ui32 sourceTypeId, NKikimr::NMiniKQL::TType* targetType) {
+ TMaybe<NUdf::EDataSlot> targetDataTypeSlot;
#ifndef NDEBUG
- // todo: vallidate targetType
- Y_UNUSED(targetType);
+ bool isOptional = false;
+ auto targetDataType = UnpackOptionalData(targetType, isOptional);
+ YQL_ENSURE(targetDataType);
+
+ targetDataTypeSlot = targetDataType->GetDataSlot();
+ if (!source && !isOptional) {
+ throw yexception() << "Null value is not allowed for non-optional data type " << *targetType;
+ }
#else
Y_UNUSED(targetType);
#endif
+ if (!source) {
+ return source;
+ }
+
switch (sourceTypeId) {
case BOOLOID:
- return ConvertFromPgValue<NUdf::EDataSlot::Bool, false>(source);
+ return ConvertFromPgValue<NUdf::EDataSlot::Bool, false>(source, targetDataTypeSlot);
case INT2OID:
- return ConvertFromPgValue<NUdf::EDataSlot::Int16, false>(source);
+ return ConvertFromPgValue<NUdf::EDataSlot::Int16, false>(source, targetDataTypeSlot);
case INT4OID:
- return ConvertFromPgValue<NUdf::EDataSlot::Int32, false>(source);
+ return ConvertFromPgValue<NUdf::EDataSlot::Int32, false>(source, targetDataTypeSlot);
case INT8OID:
- return ConvertFromPgValue<NUdf::EDataSlot::Int64, false>(source);
+ return ConvertFromPgValue<NUdf::EDataSlot::Int64, false>(source, targetDataTypeSlot);
case FLOAT4OID:
- return ConvertFromPgValue<NUdf::EDataSlot::Float, false>(source);
+ return ConvertFromPgValue<NUdf::EDataSlot::Float, false>(source, targetDataTypeSlot);
case FLOAT8OID:
- return ConvertFromPgValue<NUdf::EDataSlot::Double, false>(source);
+ return ConvertFromPgValue<NUdf::EDataSlot::Double, false>(source, targetDataTypeSlot);
case TEXTOID:
case VARCHAROID:
- return ConvertFromPgValue<NUdf::EDataSlot::Utf8, false>(source);
+ return ConvertFromPgValue<NUdf::EDataSlot::Utf8, false>(source, targetDataTypeSlot);
case BYTEAOID:
- return ConvertFromPgValue<NUdf::EDataSlot::String, false>(source);
+ return ConvertFromPgValue<NUdf::EDataSlot::String, false>(source, targetDataTypeSlot);
case CSTRINGOID:
- return ConvertFromPgValue<NUdf::EDataSlot::Utf8, true>(source);
+ return ConvertFromPgValue<NUdf::EDataSlot::Utf8, true>(source, targetDataTypeSlot);
default:
ythrow yexception() << "Unsupported type: " << NPg::LookupType(sourceTypeId).Name;
}
}
NUdf::TUnboxedValuePod ConvertToPgValue(NUdf::TUnboxedValuePod source, NKikimr::NMiniKQL::TType* sourceType, ui32 targetTypeId) {
- auto sourceDataType = AS_TYPE(TDataType, sourceType);
+ TMaybe<NUdf::EDataSlot> sourceDataTypeSlot;
+#ifndef NDEBUG
+ bool isOptional = false;
+ auto sourceDataType = UnpackOptionalData(sourceType, isOptional);
YQL_ENSURE(sourceDataType);
+ sourceDataTypeSlot = sourceDataType->GetDataSlot();
+
+ if (!source && !isOptional) {
+ throw yexception() << "Null value is not allowed for non-optional data type " << *sourceType;
+ }
+#else
+ Y_UNUSED(sourceType);
+#endif
+
+ if (!source) {
+ return source;
+ }
switch (targetTypeId) {
case BOOLOID:
- return ConvertToPgValue<NUdf::EDataSlot::Bool>(source, sourceDataType->GetDataSlot());
+ return ConvertToPgValue<NUdf::EDataSlot::Bool>(source, sourceDataTypeSlot);
case INT2OID:
- return ConvertToPgValue<NUdf::EDataSlot::Int16>(source, sourceDataType->GetDataSlot());
+ return ConvertToPgValue<NUdf::EDataSlot::Int16>(source, sourceDataTypeSlot);
case INT4OID:
- return ConvertToPgValue<NUdf::EDataSlot::Int32>(source, sourceDataType->GetDataSlot());
+ return ConvertToPgValue<NUdf::EDataSlot::Int32>(source, sourceDataTypeSlot);
case INT8OID:
- return ConvertToPgValue<NUdf::EDataSlot::Int64>(source, sourceDataType->GetDataSlot());
+ return ConvertToPgValue<NUdf::EDataSlot::Int64>(source, sourceDataTypeSlot);
case FLOAT4OID:
- return ConvertToPgValue<NUdf::EDataSlot::Float>(source, sourceDataType->GetDataSlot());
+ return ConvertToPgValue<NUdf::EDataSlot::Float>(source, sourceDataTypeSlot);
case FLOAT8OID:
- return ConvertToPgValue<NUdf::EDataSlot::Double>(source, sourceDataType->GetDataSlot());
+ return ConvertToPgValue<NUdf::EDataSlot::Double>(source, sourceDataTypeSlot);
case TEXTOID:
- return ConvertToPgValue<NUdf::EDataSlot::Utf8>(source, sourceDataType->GetDataSlot());
+ return ConvertToPgValue<NUdf::EDataSlot::Utf8>(source, sourceDataTypeSlot);
case BYTEAOID:
- return ConvertToPgValue<NUdf::EDataSlot::String>(source, sourceDataType->GetDataSlot());
+ return ConvertToPgValue<NUdf::EDataSlot::String>(source, sourceDataTypeSlot);
default:
ythrow yexception() << "Unsupported type: " << NPg::LookupType(targetTypeId).Name;
}
@@ -1892,7 +1942,11 @@ NUdf::TUnboxedValue PgValueFromNativeBinary(const TStringBuf binary, ui32 pgType
auto x = finfo.fn_addr(callInfo);
Y_ENSURE(!callInfo->isnull);
- Y_ENSURE(stringInfo.cursor == stringInfo.len);
+ if (stringInfo.cursor != stringInfo.len) {
+ TStringBuilder errMsg;
+ errMsg << "Not all data has been consumed by 'recv' function: " << NPg::LookupProc(receiveFuncId).Name << ", data size: " << stringInfo.len << ", consumed size: " << stringInfo.cursor;
+ UdfTerminate(errMsg.c_str());
+ }
return typeInfo.PassByValue ? ScalarDatumToPod(x) : PointerDatumToPod(x);
}
PG_CATCH();
@@ -2763,11 +2817,7 @@ public:
}
NUdf::TUnboxedValue NewString(i32 typeLen, ui32 targetTypeId, NUdf::TStringRef data) const override {
- // todo: implement
- Y_UNUSED(typeLen);
- Y_UNUSED(targetTypeId);
- Y_UNUSED(data);
- return NUdf::TUnboxedValue();
+ return CreatePgString(typeLen, targetTypeId, data);
}
};