diff options
author | Vitaly Stoyan <[email protected]> | 2022-05-20 14:57:14 +0300 |
---|---|---|
committer | Vitaly Stoyan <[email protected]> | 2022-05-20 14:57:14 +0300 |
commit | 01824189524840c7b49f6e93a81a15a30188c25e (patch) | |
tree | 4c481c551127a7d7fe20ef9ff610492e9b645a3f | |
parent | 7370cdad1bcf0dacc21edefba942d7ace94f374e (diff) |
YQL-14728 typemods in casts
ref:ba0dfcfcfa2dd0ad76360ddba96b95c85764f557
-rw-r--r-- | ydb/library/yql/core/type_ann/type_ann_pg.cpp | 30 | ||||
-rw-r--r-- | ydb/library/yql/minikql/mkql_program_builder.cpp | 6 | ||||
-rw-r--r-- | ydb/library/yql/minikql/mkql_program_builder.h | 2 | ||||
-rw-r--r-- | ydb/library/yql/parser/pg_catalog/catalog.cpp | 26 | ||||
-rw-r--r-- | ydb/library/yql/parser/pg_catalog/catalog.h | 2 | ||||
-rw-r--r-- | ydb/library/yql/parser/pg_wrapper/comp_factory.cpp | 382 | ||||
-rw-r--r-- | ydb/library/yql/providers/common/mkql/yql_provider_mkql.cpp | 7 | ||||
-rw-r--r-- | ydb/library/yql/sql/pg/pg_sql.cpp | 74 |
8 files changed, 345 insertions, 184 deletions
diff --git a/ydb/library/yql/core/type_ann/type_ann_pg.cpp b/ydb/library/yql/core/type_ann/type_ann_pg.cpp index 36d4a8be987..ebe21f00c64 100644 --- a/ydb/library/yql/core/type_ann/type_ann_pg.cpp +++ b/ydb/library/yql/core/type_ann/type_ann_pg.cpp @@ -761,7 +761,11 @@ IGraphTransformer::TStatus PgInternal0Wrapper(const TExprNode::TPtr& input, TExp IGraphTransformer::TStatus PgCastWrapper(const TExprNode::TPtr& input, TExprNode::TPtr& output, TContext& ctx) { Y_UNUSED(output); - if (!EnsureArgsCount(*input, 2, ctx.Expr)) { + if (!EnsureMinArgsCount(*input, 2, ctx.Expr)) { + return IGraphTransformer::TStatus::Error; + } + + if (!EnsureMaxArgsCount(*input, 3, ctx.Expr)) { return IGraphTransformer::TStatus::Error; } @@ -777,10 +781,10 @@ IGraphTransformer::TStatus PgCastWrapper(const TExprNode::TPtr& input, TExprNode return IGraphTransformer::TStatus::Repeat; } - if (!EnsureTypePg(input->Tail(), ctx.Expr)) { + if (!EnsureTypePg(*input->Child(1), ctx.Expr)) { return IGraphTransformer::TStatus::Error; } - auto targetTypeId = input->Tail().GetTypeAnn()->Cast<TTypeExprType>()->GetType()->Cast<TPgExprType>()->GetId(); + auto targetTypeId = input->Child(1)->GetTypeAnn()->Cast<TTypeExprType>()->GetType()->Cast<TPgExprType>()->GetId(); if (inputTypeId != 0 && inputTypeId != targetTypeId) { bool fail = false; @@ -814,6 +818,26 @@ IGraphTransformer::TStatus PgCastWrapper(const TExprNode::TPtr& input, TExprNode } } + if (input->ChildrenSize() >= 3) { + auto type = input->Child(2)->GetTypeAnn(); + ui32 typeModType; + bool convertToPg; + if (!ExtractPgType(type, typeModType, convertToPg, input->Child(2)->Pos(), ctx.Expr)) { + return IGraphTransformer::TStatus::Error; + } + + if (convertToPg) { + input->ChildRef(2) = ctx.Expr.NewCallable(input->Child(2)->Pos(), "ToPg", { input->ChildPtr(2) }); + return IGraphTransformer::TStatus::Repeat; + } + + if (typeModType != NPg::LookupType("int4").TypeId) { + ctx.Expr.AddError(TIssue(ctx.Expr.GetPosition(input->Pos()), + TStringBuilder() << "Expected pg int4 as typemod, but got " << NPg::LookupType(typeModType).Name)); + return IGraphTransformer::TStatus::Error; + } + } + input->SetTypeAnn(ctx.Expr.MakeType<TPgExprType>(targetTypeId)); return IGraphTransformer::TStatus::Ok; } diff --git a/ydb/library/yql/minikql/mkql_program_builder.cpp b/ydb/library/yql/minikql/mkql_program_builder.cpp index 0ee8c9b96b5..f66a975fdb7 100644 --- a/ydb/library/yql/minikql/mkql_program_builder.cpp +++ b/ydb/library/yql/minikql/mkql_program_builder.cpp @@ -5075,13 +5075,17 @@ TRuntimeNode TProgramBuilder::PgArray(const TArrayRef<const TRuntimeNode>& args, return TRuntimeNode(callableBuilder.Build(), false); } -TRuntimeNode TProgramBuilder::PgCast(TRuntimeNode input, TType* returnType) { +TRuntimeNode TProgramBuilder::PgCast(TRuntimeNode input, TType* returnType, TRuntimeNode typeMod) { if constexpr (RuntimeVersion < 30U) { THROW yexception() << "Runtime version (" << RuntimeVersion << ") too old for " << __func__; } TCallableBuilder callableBuilder(Env, __func__, returnType); callableBuilder.Add(input); + if (typeMod) { + callableBuilder.Add(typeMod); + } + return TRuntimeNode(callableBuilder.Build(), false); } diff --git a/ydb/library/yql/minikql/mkql_program_builder.h b/ydb/library/yql/minikql/mkql_program_builder.h index 95554b0aa57..bf21ca94969 100644 --- a/ydb/library/yql/minikql/mkql_program_builder.h +++ b/ydb/library/yql/minikql/mkql_program_builder.h @@ -626,7 +626,7 @@ public: TRuntimeNode PgConst(TPgType* pgType, const std::string_view& value); TRuntimeNode PgResolvedCall(bool useContext, const std::string_view& name, ui32 id, const TArrayRef<const TRuntimeNode>& args, TType* returnType); - TRuntimeNode PgCast(TRuntimeNode input, TType* returnType); + TRuntimeNode PgCast(TRuntimeNode input, TType* returnType, TRuntimeNode typeMod = {}); TRuntimeNode FromPg(TRuntimeNode input, TType* returnType); TRuntimeNode ToPg(TRuntimeNode input, TType* returnType); TRuntimeNode WithContext(TRuntimeNode input, const std::string_view& contextType); diff --git a/ydb/library/yql/parser/pg_catalog/catalog.cpp b/ydb/library/yql/parser/pg_catalog/catalog.cpp index 6c7343bd6d7..9b2842e9813 100644 --- a/ydb/library/yql/parser/pg_catalog/catalog.cpp +++ b/ydb/library/yql/parser/pg_catalog/catalog.cpp @@ -331,6 +331,8 @@ struct TLazyTypeInfo { TString OutFunc; TString SendFunc; TString ReceiveFunc; + TString ModInFunc; + TString ModOutFunc; }; class TTypesParser : public TParser { @@ -378,6 +380,10 @@ public: LastLazyTypeInfo.SendFunc = value; // resolve later } else if (key == "typreceive") { LastLazyTypeInfo.ReceiveFunc = value; // resolve later + } else if (key == "typmodin") { + LastLazyTypeInfo.ModInFunc = value; // resolve later + } else if (key == "typmodout") { + LastLazyTypeInfo.ModOutFunc = value; // resolve later } else if (key == "typbyval") { if (value == "f") { LastType.PassByValue = false; @@ -1020,6 +1026,26 @@ struct TCatalog { Y_ENSURE(sendFuncPtr->ResultType == byteaId); typePtr->SendFuncId = sendFuncIdPtr->at(0); } + + if (v.ModInFunc) { + auto modInFuncIdPtr = ProcByName.FindPtr(v.ModInFunc); + Y_ENSURE(modInFuncIdPtr); + Y_ENSURE(modInFuncIdPtr->size() == 1); + auto modInFuncPtr = Procs.FindPtr(modInFuncIdPtr->at(0)); + Y_ENSURE(modInFuncPtr); + Y_ENSURE(modInFuncPtr->ArgTypes.size() == 1); + typePtr->TypeModInFuncId = modInFuncIdPtr->at(0); + } + + if (v.ModOutFunc) { + auto modOutFuncIdPtr = ProcByName.FindPtr(v.ModOutFunc); + Y_ENSURE(modOutFuncIdPtr); + Y_ENSURE(modOutFuncIdPtr->size() == 1); + auto modOutFuncPtr = Procs.FindPtr(modOutFuncIdPtr->at(0)); + Y_ENSURE(modOutFuncPtr); + Y_ENSURE(modOutFuncPtr->ArgTypes.size() == 1); + typePtr->TypeModOutFuncId = modOutFuncIdPtr->at(0); + } } Casts = ParseCasts(castData, TypeByName, Types, ProcByName, Procs); diff --git a/ydb/library/yql/parser/pg_catalog/catalog.h b/ydb/library/yql/parser/pg_catalog/catalog.h index c010808b4a7..e3a7bbb4ec4 100644 --- a/ydb/library/yql/parser/pg_catalog/catalog.h +++ b/ydb/library/yql/parser/pg_catalog/catalog.h @@ -51,6 +51,8 @@ struct TTypeDesc { ui32 OutFuncId = 0; ui32 SendFuncId = 0; ui32 ReceiveFuncId = 0; + ui32 TypeModInFuncId = 0; + ui32 TypeModOutFuncId = 0; i32 TypeLen = 0; // from opclass ui32 LessProcId = 0; diff --git a/ydb/library/yql/parser/pg_wrapper/comp_factory.cpp b/ydb/library/yql/parser/pg_wrapper/comp_factory.cpp index f16a148ff17..b1482f0ec80 100644 --- a/ydb/library/yql/parser/pg_wrapper/comp_factory.cpp +++ b/ydb/library/yql/parser/pg_wrapper/comp_factory.cpp @@ -248,8 +248,13 @@ public: , TypeDesc(NPg::LookupType(TypeId)) { Zero(FInfo); - Y_ENSURE(TypeDesc.InFuncId); - fmgr_info(TypeDesc.InFuncId, &FInfo); + ui32 inFuncId = TypeDesc.InFuncId; + if (TypeDesc.TypeId == TypeDesc.ArrayTypeId) { + inFuncId = NPg::LookupProc("array_in", { 0,0,0 }).ProcId; + } + + Y_ENSURE(inFuncId); + fmgr_info(inFuncId, &FInfo); Y_ENSURE(!FInfo.fn_retset); Y_ENSURE(FInfo.fn_addr); Y_ENSURE(FInfo.fn_nargs >=1 && FInfo.fn_nargs <= 3); @@ -687,12 +692,13 @@ public: class TPgCast : public TMutableComputationNode<TPgCast> { typedef TMutableComputationNode<TPgCast> TBaseComputation; public: - TPgCast(TComputationMutables& mutables, ui32 sourceId, ui32 targetId, IComputationNode* arg) + TPgCast(TComputationMutables& mutables, ui32 sourceId, ui32 targetId, IComputationNode* arg, IComputationNode* typeMod) : TBaseComputation(mutables) , StateIndex(mutables.CurValueIndex++) , SourceId(sourceId) , TargetId(targetId) , Arg(arg) + , TypeMod(typeMod) , SourceTypeDesc(SourceId ? NPg::LookupType(SourceId) : NPg::TTypeDesc()) , TargetTypeDesc(NPg::LookupType(targetId)) , IsSourceArray(SourceId && SourceTypeDesc.TypeId == SourceTypeDesc.ArrayTypeId) @@ -791,6 +797,11 @@ public: return value.Release(); } + i32 typeMod = -1; + if (TypeMod) { + typeMod = DatumGetInt32(ScalarDatumFromPod(TypeMod->GetValue(compCtx))); + } + if (!FInfo1.fn_addr) { // binary compatible if (!ArrayCast) { @@ -834,7 +845,7 @@ public: continue; } else { nulls[i] = false; - elems[i] = ConvertDatum(datum, state); + elems[i] = ConvertDatum(datum, state, typeMod); } } @@ -846,7 +857,7 @@ public: auto datum = SourceTypeDesc.PassByValue ? ScalarDatumFromPod(value) : PointerDatumFromPod(value); - auto ret = ConvertDatum(datum, state); + auto ret = ConvertDatum(datum, state, typeMod); return TargetTypeDesc.PassByValue ? ScalarDatumToPod(ret) : PointerDatumToPod(ret); } } @@ -854,6 +865,9 @@ public: private: void RegisterDependencies() const final { DependsOn(Arg); + if (TypeMod) { + DependsOn(TypeMod); + } } struct TState : public TComputationValue<TState> { @@ -876,7 +890,7 @@ private: return *static_cast<TState*>(result.AsBoxed().Get()); } - Datum ConvertDatum(Datum datum, TState& state) const { + Datum ConvertDatum(Datum datum, TState& state, i32 typeMod) const { auto& callInfo1 = state.CallInfo1.Ref(); callInfo1.isnull = false; NullableDatum argDatum = { datum, false }; @@ -889,7 +903,7 @@ private: callInfo1.args[0] = argDatum; callInfo1.args[1] = { ObjectIdGetDatum(TypeIOParam), false }; - callInfo1.args[2] = { Int32GetDatum(-1), false }; + callInfo1.args[2] = { Int32GetDatum(typeMod), false }; void* freeMem = nullptr; void* freeMem2 = nullptr; @@ -949,6 +963,7 @@ private: const ui32 SourceId; const ui32 TargetId; IComputationNode* const Arg; + IComputationNode* const TypeMod; const NPg::TTypeDesc SourceTypeDesc; const NPg::TTypeDesc TargetTypeDesc; const bool IsSourceArray; @@ -1119,193 +1134,207 @@ public: } } - int ndims = 0; - int dims[MAXDIM]; - int lbs[MAXDIM]; - if (!MultiDims) { - // 1D array - ndims = 1; - dims[0] = nelems; - lbs[0] = 1; - - auto result = construct_md_array(dvalues, dnulls, ndims, dims, lbs, - ElemTypeDesc.TypeId, - ElemTypeDesc.TypeLen, - ElemTypeDesc.PassByValue, - ElemTypeDesc.TypeAlign); - return PointerDatumToPod(PointerGetDatum(result)); - } else { - /* Must be nested array expressions */ - auto element_type = ElemTypeDesc.TypeId; - int nbytes = 0; - int nitems = 0; - int outer_nelems = 0; - int elem_ndims = 0; - int *elem_dims = NULL; - int *elem_lbs = NULL; - - bool firstone = true; - bool havenulls = false; - bool haveempty = false; - char **subdata; - bits8 **subbitmaps; - int *subbytes; - int *subnitems; - int32 dataoffset; - char *dat; - int iitem; - - subdata = (char **)palloc(nelems * sizeof(char *)); - subbitmaps = (bits8 **)palloc(nelems * sizeof(bits8 *)); - subbytes = (int *)palloc(nelems * sizeof(int)); - subnitems = (int *)palloc(nelems * sizeof(int)); - - /* loop through and get data area from each element */ - for (int elemoff = 0; elemoff < nelems; elemoff++) - { - Datum arraydatum; - bool eisnull; - ArrayType *array; - int this_ndims; - - arraydatum = dvalues[elemoff]; - eisnull = dnulls[elemoff]; - - /* temporarily ignore null subarrays */ - if (eisnull) + PG_TRY(); + { + int ndims = 0; + int dims[MAXDIM]; + int lbs[MAXDIM]; + if (!MultiDims) { + // 1D array + ndims = 1; + dims[0] = nelems; + lbs[0] = 1; + + auto result = construct_md_array(dvalues, dnulls, ndims, dims, lbs, + ElemTypeDesc.TypeId, + ElemTypeDesc.TypeLen, + ElemTypeDesc.PassByValue, + ElemTypeDesc.TypeAlign); + return PointerDatumToPod(PointerGetDatum(result)); + } + else { + /* Must be nested array expressions */ + auto element_type = ElemTypeDesc.TypeId; + int nbytes = 0; + int nitems = 0; + int outer_nelems = 0; + int elem_ndims = 0; + int *elem_dims = NULL; + int *elem_lbs = NULL; + + bool firstone = true; + bool havenulls = false; + bool haveempty = false; + char **subdata; + bits8 **subbitmaps; + int *subbytes; + int *subnitems; + int32 dataoffset; + char *dat; + int iitem; + + subdata = (char **)palloc(nelems * sizeof(char *)); + subbitmaps = (bits8 **)palloc(nelems * sizeof(bits8 *)); + subbytes = (int *)palloc(nelems * sizeof(int)); + subnitems = (int *)palloc(nelems * sizeof(int)); + + /* loop through and get data area from each element */ + for (int elemoff = 0; elemoff < nelems; elemoff++) { - haveempty = true; - continue; - } + Datum arraydatum; + bool eisnull; + ArrayType *array; + int this_ndims; + + arraydatum = dvalues[elemoff]; + eisnull = dnulls[elemoff]; + + /* temporarily ignore null subarrays */ + if (eisnull) + { + haveempty = true; + continue; + } - array = DatumGetArrayTypeP(arraydatum); + array = DatumGetArrayTypeP(arraydatum); - /* run-time double-check on element type */ - if (element_type != ARR_ELEMTYPE(array)) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("cannot merge incompatible arrays"), - errdetail("Array with element type %s cannot be " - "included in ARRAY construct with element type %s.", - format_type_be(ARR_ELEMTYPE(array)), - format_type_be(element_type)))); - - this_ndims = ARR_NDIM(array); - /* temporarily ignore zero-dimensional subarrays */ - if (this_ndims <= 0) - { - haveempty = true; - continue; - } - - if (firstone) - { - /* Get sub-array details from first member */ - elem_ndims = this_ndims; - ndims = elem_ndims + 1; - if (ndims <= 0 || ndims > MAXDIM) + /* run-time double-check on element type */ + if (element_type != ARR_ELEMTYPE(array)) ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", - ndims, MAXDIM))); + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("cannot merge incompatible arrays"), + errdetail("Array with element type %s cannot be " + "included in ARRAY construct with element type %s.", + format_type_be(ARR_ELEMTYPE(array)), + format_type_be(element_type)))); + + this_ndims = ARR_NDIM(array); + /* temporarily ignore zero-dimensional subarrays */ + if (this_ndims <= 0) + { + haveempty = true; + continue; + } - elem_dims = (int *)palloc(elem_ndims * sizeof(int)); - memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int)); - elem_lbs = (int *)palloc(elem_ndims * sizeof(int)); - memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int)); + if (firstone) + { + /* Get sub-array details from first member */ + elem_ndims = this_ndims; + ndims = elem_ndims + 1; + if (ndims <= 0 || ndims > MAXDIM) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", + ndims, MAXDIM))); + + elem_dims = (int *)palloc(elem_ndims * sizeof(int)); + memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int)); + elem_lbs = (int *)palloc(elem_ndims * sizeof(int)); + memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int)); + + firstone = false; + } + else + { + /* Check other sub-arrays are compatible */ + if (elem_ndims != this_ndims || + memcmp(elem_dims, ARR_DIMS(array), + elem_ndims * sizeof(int)) != 0 || + memcmp(elem_lbs, ARR_LBOUND(array), + elem_ndims * sizeof(int)) != 0) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("multidimensional arrays must have array " + "expressions with matching dimensions"))); + } - firstone = false; + subdata[outer_nelems] = ARR_DATA_PTR(array); + subbitmaps[outer_nelems] = ARR_NULLBITMAP(array); + subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array); + nbytes += subbytes[outer_nelems]; + subnitems[outer_nelems] = ArrayGetNItems(this_ndims, + ARR_DIMS(array)); + nitems += subnitems[outer_nelems]; + havenulls |= ARR_HASNULL(array); + outer_nelems++; } - else + + /* + * If all items were null or empty arrays, return an empty array; + * otherwise, if some were and some weren't, raise error. (Note: we + * must special-case this somehow to avoid trying to generate a 1-D + * array formed from empty arrays. It's not ideal...) + */ + if (haveempty) { - /* Check other sub-arrays are compatible */ - if (elem_ndims != this_ndims || - memcmp(elem_dims, ARR_DIMS(array), - elem_ndims * sizeof(int)) != 0 || - memcmp(elem_lbs, ARR_LBOUND(array), - elem_ndims * sizeof(int)) != 0) - ereport(ERROR, + if (ndims == 0) /* didn't find any nonempty array */ + { + return PointerDatumToPod(PointerGetDatum(construct_empty_array(element_type))); + } + ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("multidimensional arrays must have array " "expressions with matching dimensions"))); } - subdata[outer_nelems] = ARR_DATA_PTR(array); - subbitmaps[outer_nelems] = ARR_NULLBITMAP(array); - subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array); - nbytes += subbytes[outer_nelems]; - subnitems[outer_nelems] = ArrayGetNItems(this_ndims, - ARR_DIMS(array)); - nitems += subnitems[outer_nelems]; - havenulls |= ARR_HASNULL(array); - outer_nelems++; - } - - /* - * If all items were null or empty arrays, return an empty array; - * otherwise, if some were and some weren't, raise error. (Note: we - * must special-case this somehow to avoid trying to generate a 1-D - * array formed from empty arrays. It's not ideal...) - */ - if (haveempty) - { - if (ndims == 0) /* didn't find any nonempty array */ + /* setup for multi-D array */ + dims[0] = outer_nelems; + lbs[0] = 1; + for (int i = 1; i < ndims; i++) { - return PointerDatumToPod(PointerGetDatum(construct_empty_array(element_type))); + dims[i] = elem_dims[i - 1]; + lbs[i] = elem_lbs[i - 1]; } - ereport(ERROR, - (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), - errmsg("multidimensional arrays must have array " - "expressions with matching dimensions"))); - } - /* setup for multi-D array */ - dims[0] = outer_nelems; - lbs[0] = 1; - for (int i = 1; i < ndims; i++) - { - dims[i] = elem_dims[i - 1]; - lbs[i] = elem_lbs[i - 1]; - } + /* check for subscript overflow */ + (void)ArrayGetNItems(ndims, dims); + ArrayCheckBounds(ndims, dims, lbs); - /* check for subscript overflow */ - (void)ArrayGetNItems(ndims, dims); - ArrayCheckBounds(ndims, dims, lbs); + if (havenulls) + { + dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems); + nbytes += dataoffset; + } + else + { + dataoffset = 0; /* marker for no null bitmap */ + nbytes += ARR_OVERHEAD_NONULLS(ndims); + } - if (havenulls) - { - dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems); - nbytes += dataoffset; - } - else - { - dataoffset = 0; /* marker for no null bitmap */ - nbytes += ARR_OVERHEAD_NONULLS(ndims); - } + ArrayType* result = (ArrayType *)palloc(nbytes); + SET_VARSIZE(result, nbytes); + result->ndim = ndims; + result->dataoffset = dataoffset; + result->elemtype = element_type; + memcpy(ARR_DIMS(result), dims, ndims * sizeof(int)); + memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int)); + + dat = ARR_DATA_PTR(result); + iitem = 0; + for (int i = 0; i < outer_nelems; i++) + { + memcpy(dat, subdata[i], subbytes[i]); + dat += subbytes[i]; + if (havenulls) + array_bitmap_copy(ARR_NULLBITMAP(result), iitem, + subbitmaps[i], 0, + subnitems[i]); + iitem += subnitems[i]; + } - ArrayType* result = (ArrayType *)palloc(nbytes); - SET_VARSIZE(result, nbytes); - result->ndim = ndims; - result->dataoffset = dataoffset; - result->elemtype = element_type; - memcpy(ARR_DIMS(result), dims, ndims * sizeof(int)); - memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int)); - - dat = ARR_DATA_PTR(result); - iitem = 0; - for (int i = 0; i < outer_nelems; i++) - { - memcpy(dat, subdata[i], subbytes[i]); - dat += subbytes[i]; - if (havenulls) - array_bitmap_copy(ARR_NULLBITMAP(result), iitem, - subbitmaps[i], 0, - subnitems[i]); - iitem += subnitems[i]; + return PointerDatumToPod(PointerGetDatum(result)); } - - return PointerDatumToPod(PointerGetDatum(result)); } + PG_CATCH(); + { + auto error_data = CopyErrorData(); + TStringBuilder errMsg; + errMsg << "Error in PgArray, reason: " << error_data->message; + FreeErrorData(error_data); + FlushErrorState(); + UdfTerminate(errMsg.c_str()); + } + PG_END_TRY(); } private: @@ -1375,7 +1404,12 @@ TComputationNodeFactory GetPgFactory() { auto returnType = callable.GetType()->GetReturnType(); auto targetId = AS_TYPE(TPgType, returnType)->GetTypeId(); - return new TPgCast(ctx.Mutables, sourceId, targetId, arg); + IComputationNode* typeMod = nullptr; + if (callable.GetInputsCount() >= 2) { + typeMod = LocateNode(ctx.NodeLocator, callable, 1); + } + + return new TPgCast(ctx.Mutables, sourceId, targetId, arg, typeMod); } if (name == "FromPg") { diff --git a/ydb/library/yql/providers/common/mkql/yql_provider_mkql.cpp b/ydb/library/yql/providers/common/mkql/yql_provider_mkql.cpp index 9337d3a7667..2c710cc99bb 100644 --- a/ydb/library/yql/providers/common/mkql/yql_provider_mkql.cpp +++ b/ydb/library/yql/providers/common/mkql/yql_provider_mkql.cpp @@ -2278,7 +2278,12 @@ TMkqlCommonCallableCompiler::TShared::TShared() { AddCallable("PgCast", [](const TExprNode& node, TMkqlBuildContext& ctx) { auto input = MkqlBuildExpr(*node.Child(0), ctx); auto returnType = BuildType(node, *node.GetTypeAnn(), ctx.ProgramBuilder); - return ctx.ProgramBuilder.PgCast(input, returnType); + TRuntimeNode typeMod; + if (node.ChildrenSize() >= 3) { + typeMod = MkqlBuildExpr(*node.Child(2), ctx); + } + + return ctx.ProgramBuilder.PgCast(input, returnType, typeMod); }); AddCallable("FromPg", [](const TExprNode& node, TMkqlBuildContext& ctx) { diff --git a/ydb/library/yql/sql/pg/pg_sql.cpp b/ydb/library/yql/sql/pg/pg_sql.cpp index 82fc8019502..21969e76736 100644 --- a/ydb/library/yql/sql/pg/pg_sql.cpp +++ b/ydb/library/yql/sql/pg/pg_sql.cpp @@ -88,6 +88,29 @@ const char* StrVal(const Node* node) { return strVal((const Value*)node); } +bool ValueAsString(const Value& val, TString& ret) { + switch (NodeTag(val)) { + case T_Integer: { + ret = ToString(IntVal(val)); + return true; + } + case T_Float: { + ret = StrFloatVal(val); + return true; + } + case T_String: { + ret = StrVal(val); + return true; + } + case T_Null: { + ret = "NULL"; + return true; + } + default: + return false; + } +} + int ListLength(const List* list) { return list_length(list); } @@ -1264,17 +1287,17 @@ public: auto supportedTypeName = typeName->typeOid == 0 && !typeName->setof && !typeName->pct_type && - ListLength(typeName->typmods) == 0 && (ListLength(typeName->names) == 2 && NodeTag(ListNodeNth(typeName->names, 0)) == T_String && !StrCompare(StrVal(ListNodeNth(typeName->names, 0)), "pg_catalog") || ListLength(typeName->names) == 1) && - NodeTag(ListNodeNth(typeName->names, ListLength(typeName->names) - 1)) == T_String && - typeName->typemod == -1; + NodeTag(ListNodeNth(typeName->names, ListLength(typeName->names) - 1)) == T_String; if (NodeTag(arg) == T_A_Const && (NodeTag(CAST_NODE(A_Const, arg)->val) == T_String || NodeTag(CAST_NODE(A_Const, arg)->val) == T_Null) && supportedTypeName && + typeName->typemod == -1 && + ListLength(typeName->typmods) == 0 && ListLength(typeName->arrayBounds) == 0) { TStringBuf targetType = StrVal(ListNodeNth(typeName->names, ListLength(typeName->names) - 1)); if (NodeTag(CAST_NODE(A_Const, arg)->val) == T_String && targetType == "bool") { @@ -1295,7 +1318,50 @@ public: finalType = "_" + finalType; } - return L(A("PgCast"), input, L(A("PgType"), QAX(finalType))); + if (!NPg::HasType(finalType)) { + AddError(TStringBuilder() << "Unknown type: " << finalType); + return nullptr; + } + + if (ListLength(typeName->typmods) == 0 && typeName->typemod == -1) { + return L(A("PgCast"), input, L(A("PgType"), QAX(finalType))); + } else { + const auto& typeDesc = NPg::LookupType(finalType); + if (!typeDesc.TypeModInFuncId) { + AddError(TStringBuilder() << "Type " << finalType << " doesn't support modifiers"); + return nullptr; + } + + const auto& procDesc = NPg::LookupProc(typeDesc.TypeModInFuncId); + + TAstNode* typeMod; + if (typeName->typemod != -1) { + typeMod = L(A("PgConst"), QA(ToString(typeName->typemod)), L(A("PgType"), QA("int4"))); + } else { + TVector<TAstNode*> args; + args.push_back(A("PgArray")); + for (int i = 0; i < ListLength(typeName->typmods); ++i) { + auto typeMod = ListNodeNth(typeName->typmods, i); + if (NodeTag(typeMod) != T_A_Const) { + AddError("Expected T_A_Const as typmod"); + return nullptr; + } + + auto aConst = CAST_NODE(A_Const, typeMod); + TString s; + if (!ValueAsString(aConst->val, s)) { + AddError("Unsupported format of typmod"); + return nullptr; + } + + args.push_back(L(A("PgConst"), QAX(s), L(A("PgType"), QA("cstring")))); + } + + typeMod = L(A("PgCall"), QA(procDesc.Name), VL(args.data(), args.size())); + } + + return L(A("PgCast"), input, L(A("PgType"), QAX(finalType)), typeMod); + } } AddError("Unsupported form of type cast"); |