diff options
author | uzhas <uzhas@ydb.tech> | 2022-08-08 17:11:48 +0300 |
---|---|---|
committer | uzhas <uzhas@ydb.tech> | 2022-08-08 17:11:48 +0300 |
commit | dcc7af40b4a3c36f67fb1dca7c8d839a824a1598 (patch) | |
tree | 22af84799d5795208d2eb7dd8aa42c1cb75e7578 | |
parent | 1edc976c2b2250754d482f29ef153c77ebffba59 (diff) | |
download | ydb-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.cpp | 181 | ||||
-rw-r--r-- | ydb/library/yql/parser/pg_wrapper/comp_factory.cpp | 104 |
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); } }; |