summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVitaly Stoyan <[email protected]>2022-05-20 14:57:14 +0300
committerVitaly Stoyan <[email protected]>2022-05-20 14:57:14 +0300
commit01824189524840c7b49f6e93a81a15a30188c25e (patch)
tree4c481c551127a7d7fe20ef9ff610492e9b645a3f
parent7370cdad1bcf0dacc21edefba942d7ace94f374e (diff)
YQL-14728 typemods in casts
ref:ba0dfcfcfa2dd0ad76360ddba96b95c85764f557
-rw-r--r--ydb/library/yql/core/type_ann/type_ann_pg.cpp30
-rw-r--r--ydb/library/yql/minikql/mkql_program_builder.cpp6
-rw-r--r--ydb/library/yql/minikql/mkql_program_builder.h2
-rw-r--r--ydb/library/yql/parser/pg_catalog/catalog.cpp26
-rw-r--r--ydb/library/yql/parser/pg_catalog/catalog.h2
-rw-r--r--ydb/library/yql/parser/pg_wrapper/comp_factory.cpp382
-rw-r--r--ydb/library/yql/providers/common/mkql/yql_provider_mkql.cpp7
-rw-r--r--ydb/library/yql/sql/pg/pg_sql.cpp74
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");