| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| /*------------------------------------------------------------------------- |
| * |
| * gppc.c |
| * libgppc wrapper main |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres.h" |
| #include "funcapi.h" |
| #if GP_VERSION_NUM >= 40200 |
| #include "tablefuncapi.h" |
| #endif |
| |
| #include "executor/spi.h" |
| #include "mb/pg_wchar.h" |
| #include "parser/parse_expr.h" |
| #include "utils/builtins.h" |
| #include "utils/date.h" |
| #include "utils/datetime.h" |
| #include "utils/memutils.h" |
| #include "utils/numeric.h" |
| #include "utils/timestamp.h" |
| |
| /* |
| * GPPC_C_BUILD avoids definitions conflicting with actual backend definitions. |
| */ |
| #define GPPC_C_BUILD |
| #include "gppc.h" |
| |
| #define ENSURE_NODE(val, nodetag) do{ \ |
| if (val != NULL && !IsA(val, nodetag)) \ |
| elog(ERROR, "unexpected value %d", (val == NULL ? 0 : nodeTag(val))); \ |
| } while(0) |
| |
| /* |
| * libgppc.so has PG_MODULE_MAGIC, user libraries don't. Linking |
| * to libgppc, user libraries can be loaded by the backend. |
| */ |
| PG_MODULE_MAGIC; |
| |
| #define GPPC_MAP_FCINFO(info) ((FunctionCallInfo) info) |
| |
| #define CHECK_TUPLEDESC_ATTNO(tupdesc, attno) do{ \ |
| if ((attno) < 0) \ |
| elog(ERROR, "requested invalid attno(%d)", (attno)); \ |
| if ((tupdesc) == NULL) \ |
| elog(ERROR, "tuple desc is null"); \ |
| if (((TupleDesc) (tupdesc))->natts <= (attno)) \ |
| elog(ERROR, "requested attno(%d) over tuple desc length(%d)", \ |
| (attno), ((TupleDesc) (tupdesc))->natts); \ |
| }while(0) |
| |
| typedef struct GppcReportCallbackStateData |
| { |
| ErrorContextCallback errcontext; |
| void (*func)(GppcReportInfo, void *); |
| void *arg; |
| } GppcReportCallbackStateData; |
| |
| /* |
| * Type oids. Since we don't want to expose PG headers to the third-party and |
| * hard-coding in the header is not a good way to maintain, we assign them here. |
| */ |
| const GppcOid GppcOidInvalid = InvalidOid; |
| const GppcOid GppcOidBool = BOOLOID; |
| const GppcOid GppcOidChar = CHAROID; |
| const GppcOid GppcOidInt2 = INT2OID; |
| const GppcOid GppcOidInt4 = INT4OID; |
| const GppcOid GppcOidInt8 = INT8OID; |
| const GppcOid GppcOidFloat4 = FLOAT4OID; |
| const GppcOid GppcOidFloat8 = FLOAT8OID; |
| const GppcOid GppcOidText = TEXTOID; |
| const GppcOid GppcOidVarChar = VARCHAROID; |
| const GppcOid GppcOidBpChar = BPCHAROID; |
| const GppcOid GppcOidBytea = BYTEAOID; |
| const GppcOid GppcOidNumeric = NUMERICOID; |
| const GppcOid GppcOidDate = DATEOID; |
| const GppcOid GppcOidTime = TIMEOID; |
| const GppcOid GppcOidTimeTz = TIMETZOID; |
| const GppcOid GppcOidTimestamp = TIMESTAMPOID; |
| const GppcOid GppcOidTimestampTz = TIMESTAMPTZOID; |
| #if GP_VERSION_NUM >= 40200 |
| const GppcOid GppcOidAnyTable = ANYTABLEOID; |
| #endif |
| |
| /* |
| * V1 call convention helper function. This is just to avoid leaking |
| * the Pg_finfo_record struct. |
| */ |
| const void * |
| GppcFinfoV1(void) |
| { |
| static const Pg_finfo_record my_finfo = { 1 }; |
| return &my_finfo; |
| } |
| |
| /* |
| * PG_NARGS |
| */ |
| int |
| GppcNargs(GppcFcinfo info) |
| { |
| FunctionCallInfo fcinfo = GPPC_MAP_FCINFO(info); |
| |
| return fcinfo->nargs; |
| } |
| |
| /* |
| * PG_RETURN_NULL |
| */ |
| GppcDatum |
| GppcReturnNull(GppcFcinfo info) |
| { |
| FunctionCallInfo fcinfo = GPPC_MAP_FCINFO(info); |
| |
| fcinfo->isnull = true; |
| return (GppcDatum ) 0; |
| } |
| |
| /* |
| * PG_ARGISNULL |
| */ |
| bool |
| GppcArgIsNull(GppcFcinfo info, int n) |
| { |
| FunctionCallInfo fcinfo = GPPC_MAP_FCINFO(info); |
| |
| return PG_ARGISNULL(n); |
| } |
| |
| /* |
| * PG_GETARG_DATUM |
| */ |
| GppcDatum |
| GppcGetArgDatum(GppcFcinfo info, int n) |
| { |
| FunctionCallInfo fcinfo = GPPC_MAP_FCINFO(info); |
| |
| return PG_GETARG_DATUM(n); |
| } |
| |
| /* |
| * BoolGetDatum |
| */ |
| GppcDatum |
| GppcBoolGetDatum(GppcBool x) |
| { |
| return BoolGetDatum(x); |
| } |
| |
| /* |
| * CharGetDatum |
| */ |
| GppcDatum |
| GppcCharGetDatum(GppcChar x) |
| { |
| return CharGetDatum(x); |
| } |
| |
| /* |
| * Int16GetDatum |
| */ |
| GppcDatum |
| GppcInt2GetDatum(GppcInt2 x) |
| { |
| return Int16GetDatum(x); |
| } |
| |
| /* |
| * Int32GetDatum |
| */ |
| GppcDatum |
| GppcInt4GetDatum(GppcInt4 x) |
| { |
| return Int32GetDatum(x); |
| } |
| |
| /* |
| * Int64GetDatum |
| */ |
| GppcDatum |
| GppcInt8GetDatum(GppcInt8 x) |
| { |
| return Int64GetDatum(x); |
| } |
| |
| /* |
| * Float4GetDatum |
| */ |
| GppcDatum |
| GppcFloat4GetDatum(GppcFloat4 x) |
| { |
| return Float4GetDatum(x); |
| } |
| |
| /* |
| * Float8GetDatum |
| */ |
| GppcDatum |
| GppcFloat8GetDatum(GppcFloat8 x) |
| { |
| return Float8GetDatum(x); |
| } |
| |
| /* |
| * TextGetDatum |
| */ |
| GppcDatum |
| GppcTextGetDatum(GppcText x) |
| { |
| return PointerGetDatum(x); |
| } |
| |
| /* |
| * VarCharGetDatum |
| */ |
| GppcDatum |
| GppcVarCharGetDatum(GppcVarChar x) |
| { |
| return PointerGetDatum(x); |
| } |
| |
| /* |
| * BpCharGetDatum |
| */ |
| GppcDatum |
| GppcBpCharGetDatum(GppcBpChar x) |
| { |
| return PointerGetDatum(x); |
| } |
| |
| /* |
| * ByteaGetDatum |
| */ |
| GppcDatum |
| GppcByteaGetDatum(GppcBytea x) |
| { |
| return PointerGetDatum(x); |
| } |
| |
| /* |
| * NumericGetDatum |
| */ |
| GppcDatum |
| GppcNumericGetDatum(GppcNumeric x) |
| { |
| return NumericGetDatum(x); |
| } |
| |
| /* |
| * DateGetDatum |
| */ |
| GppcDatum |
| GppcDateGetDatum(GppcDate x) |
| { |
| return DateADTGetDatum(x); |
| } |
| |
| /* |
| * TimeGetDatum |
| */ |
| GppcDatum |
| GppcTimeGetDatum(GppcTime x) |
| { |
| return TimeADTGetDatum(x); |
| } |
| |
| /* |
| * TimeTzGetDatum |
| */ |
| GppcDatum |
| GppcTimeTzGetDatum(GppcTimeTz x) |
| { |
| return TimeTzADTPGetDatum(x); |
| } |
| |
| /* |
| * TimestampGetDatum |
| */ |
| GppcDatum |
| GppcTimestampGetDatum(GppcTimestamp x) |
| { |
| return TimestampGetDatum(x); |
| } |
| |
| /* |
| * TimestampTzGetDatum |
| */ |
| GppcDatum |
| GppcTimestampTzGetDatum(GppcTimestampTz x) |
| { |
| return TimestampTzGetDatum(x); |
| } |
| |
| /* |
| * AnyTableGetDatum |
| */ |
| GppcDatum |
| GppcAnyTableGetDatum(GppcAnyTable x) |
| { |
| return PointerGetDatum(x); |
| } |
| |
| /* |
| * TupleDescGetDatum |
| */ |
| GppcDatum |
| GppcTupleDescGetDatum(GppcTupleDesc x) |
| { |
| return PointerGetDatum(x); |
| } |
| |
| /* |
| * HeapTupleGetDatum |
| */ |
| GppcDatum |
| GppcHeapTupleGetDatum(GppcHeapTuple x) |
| { |
| return PointerGetDatum(x); |
| } |
| |
| /* |
| * DatumGetBool |
| */ |
| GppcBool |
| GppcDatumGetBool(GppcDatum x) |
| { |
| return DatumGetBool(x); |
| } |
| |
| /* |
| * DatumGetChar |
| */ |
| GppcChar |
| GppcDatumGetChar(GppcDatum x) |
| { |
| return DatumGetChar(x); |
| } |
| |
| /* |
| * DatumGetInt16 |
| */ |
| GppcInt2 |
| GppcDatumGetInt2(GppcDatum x) |
| { |
| return DatumGetInt16(x); |
| } |
| |
| /* |
| * DatumGetInt32 |
| */ |
| GppcInt4 |
| GppcDatumGetInt4(GppcDatum x) |
| { |
| return DatumGetInt32(x); |
| } |
| |
| /* |
| * DatumGetInt64 |
| */ |
| GppcInt8 |
| GppcDatumGetInt8(GppcDatum x) |
| { |
| return DatumGetInt64(x); |
| } |
| |
| /* |
| * DatumGetFloat4 |
| */ |
| GppcFloat4 |
| GppcDatumGetFloat4(GppcDatum x) |
| { |
| return DatumGetFloat4(x); |
| } |
| |
| /* |
| * DatumGetFloat8 |
| */ |
| GppcFloat8 |
| GppcDatumGetFloat8(GppcDatum x) |
| { |
| return DatumGetFloat8(x); |
| } |
| |
| /* |
| * DatumGetText |
| */ |
| GppcText |
| GppcDatumGetText(GppcDatum x) |
| { |
| return (GppcText) DatumGetTextP(x); |
| } |
| |
| /* |
| * DatumGetTextCopy |
| */ |
| GppcText |
| GppcDatumGetTextCopy(GppcDatum x) |
| { |
| return (GppcText) DatumGetTextPCopy(x); |
| } |
| |
| /* |
| * DatumGetVarChar |
| */ |
| GppcVarChar |
| GppcDatumGetVarChar(GppcDatum x) |
| { |
| return (GppcVarChar) DatumGetVarCharP(x); |
| } |
| |
| /* |
| * DatumGetVarCharCopy |
| */ |
| GppcVarChar |
| GppcDatumGetVarCharCopy(GppcDatum x) |
| { |
| return (GppcVarChar) DatumGetVarCharPCopy(x); |
| } |
| |
| /* |
| * DatumGetBpChar |
| */ |
| GppcBpChar |
| GppcDatumGetBpChar(GppcDatum x) |
| { |
| return (GppcBpChar) DatumGetBpCharP(x); |
| } |
| |
| /* |
| * DatumGetBpCharCopy |
| */ |
| GppcBpChar |
| GppcDatumGetBpCharCopy(GppcDatum x) |
| { |
| return (GppcBpChar) DatumGetBpCharPCopy(x); |
| } |
| |
| /* |
| * DatumGetBytea |
| */ |
| GppcBytea |
| GppcDatumGetBytea(GppcDatum x) |
| { |
| return (GppcBytea) DatumGetByteaP(x); |
| } |
| |
| /* |
| * DatumGetByteaCopy |
| */ |
| GppcBytea |
| GppcDatumGetByteaCopy(GppcDatum x) |
| { |
| return (GppcBytea) DatumGetByteaPCopy(x); |
| } |
| |
| /* |
| * DatumGetNumeric |
| */ |
| GppcNumeric |
| GppcDatumGetNumeric(GppcDatum x) |
| { |
| return (GppcNumeric) DatumGetNumeric(x); |
| } |
| |
| /* |
| * DatumGetDate |
| */ |
| GppcDate |
| GppcDatumGetDate(GppcDatum x) |
| { |
| return (GppcDate) DatumGetDateADT(x); |
| } |
| |
| /* |
| * DatumGetTime |
| */ |
| GppcTime |
| GppcDatumGetTime(GppcDatum x) |
| { |
| return (GppcTime) DatumGetTimeADT(x); |
| } |
| |
| /* |
| * DatumGetTimeTz |
| */ |
| GppcTimeTz |
| GppcDatumGetTimeTz(GppcDatum x) |
| { |
| return (GppcTimeTz) DatumGetTimeTzADTP(x); |
| } |
| |
| /* |
| * DatumGetTimestamp |
| */ |
| GppcTimestamp |
| GppcDatumGetTimestamp(GppcDatum x) |
| { |
| return (GppcTimestamp) DatumGetTimestamp(x); |
| } |
| |
| /* |
| * DatumGetTimestampTz |
| */ |
| GppcTimestampTz |
| GppcDatumGetTimestampTz(GppcDatum x) |
| { |
| return (GppcTimestampTz) DatumGetTimestampTz(x); |
| } |
| |
| /* |
| * DatumGetAnyTable |
| */ |
| GppcAnyTable |
| GppcDatumGetAnyTable(GppcDatum x) |
| { |
| return DatumGetPointer(x); |
| } |
| |
| /* |
| * DatumGetTupleDesc |
| */ |
| GppcTupleDesc |
| GppcDatumGetTupleDesc(GppcDatum x) |
| { |
| return DatumGetPointer(x); |
| } |
| |
| /* |
| * DatumGetHeapTuple |
| */ |
| GppcHeapTuple |
| GppcDatumGetHeapTuple(GppcDatum x) |
| { |
| return DatumGetPointer(x); |
| } |
| |
| /* |
| * |
| */ |
| size_t |
| GppcGetTextLength(GppcText t) |
| { |
| return VARSIZE(t) - VARHDRSZ; |
| } |
| |
| /* |
| * VARDATA(text) |
| */ |
| char * |
| GppcGetTextPointer(GppcText t) |
| { |
| return VARDATA(t); |
| } |
| |
| /* |
| * TextDatumGetCString |
| */ |
| char * |
| GppcTextGetCString(GppcText t) |
| { |
| return TextDatumGetCString(PointerGetDatum(t)); |
| } |
| |
| /* |
| * CStringGetTextDatum |
| */ |
| GppcText |
| GppcCStringGetText(const char *s) |
| { |
| return (GppcText) cstring_to_text(s); |
| } |
| |
| /* |
| * |
| */ |
| size_t |
| GppcGetVarCharLength(GppcVarChar t) |
| { |
| return VARSIZE(t) - VARHDRSZ; |
| } |
| |
| /* |
| * VARDATA(VarChar) |
| */ |
| char * |
| GppcGetVarCharPointer(GppcVarChar t) |
| { |
| return VARDATA(t); |
| } |
| |
| /* |
| * VarCharDatumGetCString |
| */ |
| char * |
| GppcVarCharGetCString(GppcVarChar t) |
| { |
| return TextDatumGetCString(PointerGetDatum(t)); |
| } |
| |
| /* |
| * CStringGetVarCharDatum |
| */ |
| GppcVarChar |
| GppcCStringGetVarChar(const char *s) |
| { |
| return (GppcVarChar) cstring_to_text(s); |
| } |
| |
| /* |
| * |
| */ |
| size_t |
| GppcGetBpCharLength(GppcBpChar t) |
| { |
| return VARSIZE(t) - VARHDRSZ; |
| } |
| |
| /* |
| * VARDATA(BpChar) |
| */ |
| char * |
| GppcGetBpCharPointer(GppcBpChar t) |
| { |
| return VARDATA(t); |
| } |
| |
| /* |
| * BpCharDatumGetCString |
| */ |
| char * |
| GppcBpCharGetCString(GppcBpChar t) |
| { |
| return TextDatumGetCString(PointerGetDatum(t)); |
| } |
| |
| /* |
| * CStringGetBpCharDatum |
| */ |
| GppcBpChar |
| GppcCStringGetBpChar(const char *s) |
| { |
| return (GppcBpChar) cstring_to_text(s); |
| } |
| |
| /* |
| * |
| */ |
| size_t |
| GppcGetByteaLength(GppcBytea x) |
| { |
| return VARSIZE(x) - VARHDRSZ; |
| } |
| |
| /* |
| * VARDATA(x) |
| */ |
| char * |
| GppcGetByteaPointer(GppcBytea x) |
| { |
| return VARDATA(x); |
| } |
| |
| /* |
| * numeric_out |
| */ |
| char * |
| GppcNumericGetCString(GppcNumeric n) |
| { |
| return DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum((Numeric *) n))); |
| } |
| |
| /* |
| * numeric_in |
| */ |
| GppcNumeric |
| GppcCStringGetNumeric(const char *s) |
| { |
| return (GppcNumeric) DatumGetPointer( |
| DirectFunctionCall3(numeric_in, CStringGetDatum(s), NUMERICOID, -1)); |
| } |
| |
| bool |
| GppcGetNumericDef(int32_t typmod, int16_t *precision, int16_t *scale) |
| { |
| *precision = 0; |
| *scale = 0; |
| if (typmod < (int32_t) (VARHDRSZ)) |
| return false; |
| |
| typmod -= VARHDRSZ; |
| *precision = (typmod >> 16) & 0xffff; |
| *scale = typmod & 0xffff; |
| |
| return true; |
| } |
| |
| GppcFloat8 |
| GppcNumericGetFloat8(GppcNumeric n) |
| { |
| return DatumGetFloat8( |
| DirectFunctionCall1(numeric_float8, NumericGetDatum((Numeric *) n))); |
| } |
| |
| GppcNumeric |
| GppcFloat8GetNumeric(GppcFloat8 f) |
| { |
| return (GppcNumeric) DatumGetPointer( |
| DirectFunctionCall1(float8_numeric, Float8GetDatum(f))); |
| } |
| |
| static void |
| timestamp2gppctm(Timestamp ts, GppcTm *tm) |
| { |
| struct pg_tm pgtm; |
| fsec_t fsec; |
| |
| if (timestamp2tm((Timestamp) ts, NULL, &pgtm, &fsec, NULL, NULL) != 0) |
| elog(ERROR, "failed to convert timestamp"); |
| |
| memcpy(tm, &pgtm, Min(sizeof(GppcTm), sizeof(struct pg_tm))); |
| tm->tm_fsec = fsec; |
| } |
| |
| static Timestamp |
| gppctm2timestamp(GppcTm *tm) |
| { |
| struct pg_tm pgtm; |
| Timestamp ts; |
| fsec_t fsec; |
| |
| memcpy(&pgtm, tm, Min(sizeof(GppcTm), sizeof(struct pg_tm))); |
| fsec = tm->tm_fsec; |
| |
| if (tm2timestamp(&pgtm, fsec, NULL, &ts) != 0) |
| elog(ERROR, "failed to convert to timestamp"); |
| |
| return ts; |
| } |
| |
| |
| void |
| GppcDateGetTm(GppcDate x, GppcTm *tm) |
| { |
| Timestamp ts; |
| |
| ts = DatumGetTimestamp(DirectFunctionCall1(date_timestamp, DateADTGetDatum(x))); |
| timestamp2gppctm(ts, tm); |
| } |
| |
| GppcDate |
| GppcTmGetDate(GppcTm *tm) |
| { |
| Timestamp ts; |
| |
| ts = gppctm2timestamp(tm); |
| |
| return DatumGetDateADT(DirectFunctionCall1(timestamp_date, TimestampGetDatum(ts))); |
| } |
| |
| void |
| GppcTimeGetTm(GppcTime x, GppcTm *tm) |
| { |
| Timestamp ts; |
| |
| ts = DatumGetTimestamp(DirectFunctionCall2(datetime_timestamp, |
| DateADTGetDatum(POSTGRES_EPOCH_JDATE), TimeADTGetDatum((TimeADT) x))); |
| |
| timestamp2gppctm(ts, tm); |
| } |
| |
| GppcTime |
| GppcTmGetTime(GppcTm *tm) |
| { |
| Timestamp ts; |
| |
| ts = gppctm2timestamp(tm); |
| |
| return DatumGetTimeADT(DirectFunctionCall1(timestamp_time, TimestampGetDatum(ts))); |
| } |
| |
| void |
| GppcTimeTzGetTm(GppcTimeTz x, GppcTm *tm) |
| { |
| TimeADT t; |
| Timestamp ts; |
| |
| /* |
| * Drop time zone, as we adjust it below. We do this because we don't have |
| * timestamptz2tm and always convert via timestamp. |
| */ |
| t = DatumGetTimeADT( |
| DirectFunctionCall1(timetz_time, TimeTzADTPGetDatum((TimeTzADT *) x))); |
| ts = DatumGetTimestamp(DirectFunctionCall2(datetime_timestamp, |
| DateADTGetDatum(POSTGRES_EPOCH_JDATE), TimeADTGetDatum(t))); |
| |
| timestamp2gppctm(ts, tm); |
| /* adjust time zone. gmtoff is in sec */ |
| tm->tm_isdst = 0; |
| tm->tm_gmtoff = ((TimeTzADT *) x)->zone; |
| } |
| |
| GppcTimeTz |
| GppcTmGetTimeTz(GppcTm *tm) |
| { |
| Timestamp ts; |
| TimeADT t; |
| TimeTzADT *result; |
| |
| ts = gppctm2timestamp(tm); |
| |
| t = DatumGetTimeADT(DirectFunctionCall1(timestamp_time, TimestampGetDatum(ts))); |
| result = DatumGetTimeTzADTP(DirectFunctionCall1(time_timetz, TimeADTGetDatum(t))); |
| /* adjust time zone. gmtoff is in sec */ |
| result->zone = tm->tm_gmtoff; |
| return (GppcTimeTz) result; |
| } |
| |
| void |
| GppcTimestampGetTm(GppcTimestamp x, GppcTm *tm) |
| { |
| timestamp2gppctm(x, tm); |
| } |
| |
| GppcTimestamp |
| GppcTmGetTimestamp(GppcTm *tm) |
| { |
| return gppctm2timestamp(tm); |
| } |
| |
| void |
| GppcTimestampTzGetTm(GppcTimestampTz x, GppcTm *tm) |
| { |
| timestamp2gppctm(x, tm); |
| } |
| |
| GppcTimestampTz |
| GppcTmGetTimestampTz(GppcTm *tm) |
| { |
| return gppctm2timestamp(tm); |
| } |
| |
| /* |
| * palloc |
| */ |
| void * |
| GppcAlloc(size_t bytes) |
| { |
| return palloc(bytes); |
| } |
| |
| /* |
| * palloc0 |
| */ |
| void * |
| GppcAlloc0(size_t bytes) |
| { |
| return palloc0(bytes); |
| } |
| |
| /* |
| * repalloc |
| */ |
| void * |
| GppcRealloc(void *ptr, size_t bytes) |
| { |
| return repalloc(ptr, bytes); |
| } |
| |
| /* |
| * pfree |
| */ |
| void |
| GppcFree(void *ptr) |
| { |
| pfree(ptr); |
| } |
| |
| /* |
| * clen is byte length of the content |
| */ |
| GppcText |
| GppcAllocText(size_t clen) |
| { |
| text *t; |
| size_t bytes; |
| |
| bytes = VARHDRSZ + clen; |
| t = (text *) palloc(bytes); |
| SET_VARSIZE(t, bytes); |
| return (GppcText) t; |
| } |
| |
| /* |
| * clen is byte length of the content |
| */ |
| GppcVarChar |
| GppcAllocVarChar(size_t clen) |
| { |
| text *t; |
| size_t bytes; |
| |
| bytes = VARHDRSZ + clen; |
| t = (text *) palloc(bytes); |
| SET_VARSIZE(t, bytes); |
| return (GppcVarChar) t; |
| } |
| |
| /* |
| * clen is byte length of the content |
| */ |
| GppcBpChar |
| GppcAllocBpChar(size_t clen) |
| { |
| text *t; |
| size_t bytes; |
| |
| bytes = VARHDRSZ + clen; |
| t = (text *) palloc(bytes); |
| SET_VARSIZE(t, bytes); |
| return (GppcBpChar) t; |
| } |
| |
| /* |
| * blen is byte length of the content |
| */ |
| GppcBytea |
| GppcAllocBytea(size_t blen) |
| { |
| bytea *b; |
| size_t bytes; |
| |
| bytes = VARHDRSZ + blen; |
| b = (bytea *) palloc(bytes); |
| SET_VARSIZE(b, bytes); |
| return (GppcBytea) b; |
| } |
| |
| /* |
| * SRF_IS_FIRSTCALL |
| */ |
| bool |
| GppcSRFIsFirstCall(GppcFcinfo info) |
| { |
| FunctionCallInfo fcinfo = GPPC_MAP_FCINFO(info); |
| |
| return fcinfo->flinfo->fn_extra == NULL; |
| } |
| |
| /* |
| * SRF_FIRSTCALL_INIT |
| */ |
| GppcFuncCallContext |
| GppcSRFFirstCallInit(GppcFcinfo info) |
| { |
| FunctionCallInfo fcinfo = GPPC_MAP_FCINFO(info); |
| |
| return (GppcFuncCallContext) init_MultiFuncCall(fcinfo); |
| } |
| |
| /* |
| * SRF_PERCALL_SETUP |
| */ |
| GppcFuncCallContext |
| GppcSRFPerCallSetup(GppcFcinfo info) |
| { |
| FunctionCallInfo fcinfo = GPPC_MAP_FCINFO(info); |
| |
| return (GppcFuncCallContext) per_MultiFuncCall(fcinfo); |
| } |
| |
| /* |
| * SRF_RETURN_NEXT |
| */ |
| GppcDatum |
| GppcSRFReturnNext(GppcFcinfo info, GppcFuncCallContext funcctx, GppcDatum result) |
| { |
| FunctionCallInfo fcinfo = GPPC_MAP_FCINFO(info); |
| ReturnSetInfo *rsi; |
| |
| ((FuncCallContext *) funcctx)->call_cntr++; |
| rsi = (ReturnSetInfo *) fcinfo->resultinfo; |
| rsi->isDone = ExprMultipleResult; |
| PG_RETURN_DATUM(result); |
| } |
| |
| /* |
| * SRF_RETURN_DONE |
| */ |
| GppcDatum |
| GppcSRFReturnDone(GppcFcinfo info, GppcFuncCallContext funcctx) |
| { |
| FunctionCallInfo fcinfo = GPPC_MAP_FCINFO(info); |
| ReturnSetInfo *rsi; |
| |
| end_MultiFuncCall(fcinfo, ((FuncCallContext *) funcctx)); |
| rsi = (ReturnSetInfo *) fcinfo->resultinfo; |
| rsi->isDone = ExprEndResult; |
| PG_RETURN_NULL(); |
| } |
| |
| /* |
| * Returns TupleDesc for the exptected output of the function |
| */ |
| GppcTupleDesc |
| GppcSRFResultDesc(GppcFcinfo info) |
| { |
| FunctionCallInfo fcinfo = GPPC_MAP_FCINFO(info); |
| ReturnSetInfo *rsi; |
| |
| rsi = (ReturnSetInfo *) fcinfo->resultinfo; |
| |
| return (GppcTupleDesc) rsi->expectedDesc; |
| } |
| |
| void * |
| GppcSRFAlloc(GppcFuncCallContext fctx, size_t bytes) |
| { |
| FuncCallContext *ctx = (FuncCallContext *) fctx; |
| |
| return MemoryContextAlloc(ctx->multi_call_memory_ctx, bytes); |
| } |
| |
| void * |
| GppcSRFAlloc0(GppcFuncCallContext fctx, size_t bytes) |
| { |
| FuncCallContext *ctx = (FuncCallContext *) fctx; |
| |
| return MemoryContextAllocZero(ctx->multi_call_memory_ctx, bytes); |
| } |
| |
| void |
| GppcSRFSave(GppcFuncCallContext fctx, void *ptr) |
| { |
| FuncCallContext *ctx = (FuncCallContext *) fctx; |
| |
| ctx->user_fctx = ptr; |
| } |
| |
| void * |
| GppcSRFRestore(GppcFuncCallContext fctx) |
| { |
| FuncCallContext *ctx = (FuncCallContext *) fctx; |
| |
| return ctx->user_fctx; |
| } |
| |
| int |
| GppcSPIConnect(void) |
| { |
| int rescode; |
| |
| rescode = SPI_connect(); |
| return rescode; |
| } |
| |
| int |
| GppcSPIFinish(void) |
| { |
| return SPI_finish(); |
| } |
| |
| GppcSPIResult |
| GppcSPIExec(const char *src, long tcount) |
| { |
| int rescode; |
| GppcSPIResult result; |
| |
| rescode = SPI_exec(src, tcount); |
| result = (GppcSPIResult) palloc0(sizeof(GppcSPIResultData)); |
| if (rescode >= 0) |
| { |
| result->tuptable = (struct GppcSPITupleTableData *) SPI_tuptable; |
| result->processed = SPI_processed; |
| } |
| result->rescode = rescode; |
| |
| return result; |
| } |
| |
| char * |
| GppcSPIGetValue(GppcSPIResult result, int fnumber, bool makecopy) |
| { |
| SPITupleTable *tuptable = (SPITupleTable *) result->tuptable; |
| char *value; |
| |
| value = SPI_getvalue(tuptable->vals[result->current], tuptable->tupdesc, fnumber); |
| /* |
| * If the copy is demanded, move value to the upper context. |
| */ |
| if (makecopy && value) |
| { |
| char *tmp; |
| size_t len; |
| |
| len = strlen(value); |
| tmp = SPI_palloc(len + 1); |
| strcpy(tmp, value); |
| value = tmp; |
| } |
| return value; |
| } |
| |
| GppcDatum |
| GppcSPIGetDatum(GppcSPIResult result, int fnumber, bool *isnull, bool makecopy) |
| { |
| SPITupleTable *tuptable = (SPITupleTable *) result->tuptable; |
| HeapTuple tuple; |
| |
| /* |
| * It might be better if we could copy only the datum, but SPI interface |
| * doesn't have such function and copying datumCopy code here is too ugly. |
| */ |
| if (makecopy) |
| tuple = SPI_copytuple(tuptable->vals[result->current]); |
| else |
| tuple = tuptable->vals[result->current]; |
| return SPI_getbinval(tuple, tuptable->tupdesc, fnumber, isnull); |
| } |
| |
| char * |
| GppcSPIGetValueByName(GppcSPIResult result, const char *fname, bool makecopy) |
| { |
| SPITupleTable *tuptable = (SPITupleTable *) result->tuptable; |
| int fnumber = SPI_fnumber(tuptable->tupdesc, fname); |
| |
| return GppcSPIGetValue(result, fnumber, makecopy); |
| } |
| |
| GppcDatum |
| GppcSPIGetDatumByName(GppcSPIResult result, const char *fname, bool *isnull, bool makecopy) |
| { |
| SPITupleTable *tuptable = (SPITupleTable *) result->tuptable; |
| int fnumber = SPI_fnumber(tuptable->tupdesc, fname); |
| |
| return GppcSPIGetDatum(result, fnumber, isnull, makecopy); |
| } |
| |
| /* |
| * Some APIs are since 40201, but cdb2/main never bumps SP number, |
| * so we assume the base version means the latest version of this major. |
| */ |
| #if GP_VERSION_NUM >= 40200 |
| /* |
| * TF_SET_USERDATA |
| */ |
| void |
| GppcTFSetUserData(void *GppcFcinfo, GppcBytea userdata) |
| { |
| FunctionCallInfo fcinfo = GPPC_MAP_FCINFO(GppcFcinfo); |
| |
| TF_SET_USERDATA((bytea *) userdata); |
| } |
| |
| /* |
| * TF_GET_USERDATA |
| */ |
| GppcBytea |
| GppcTFGetUserData(void *GppcFcinfo) |
| { |
| FunctionCallInfo fcinfo = GPPC_MAP_FCINFO(GppcFcinfo); |
| |
| return (GppcBytea) TF_GET_USERDATA(); |
| } |
| |
| /* |
| * AnyTable_GetTupleDesc |
| */ |
| GppcTupleDesc |
| GppcAnyTableGetTupleDesc(GppcAnyTable t) |
| { |
| return (GppcTupleDesc) AnyTable_GetTupleDesc((AnyTable) t); |
| } |
| |
| /* |
| * AnyTable_GetNextTuple |
| */ |
| GppcHeapTuple |
| GppcAnyTableGetNextTuple(GppcAnyTable t) |
| { |
| return (GppcHeapTuple) AnyTable_GetNextTuple((AnyTable) t); |
| } |
| |
| /* |
| * Common code for the describe function. |
| */ |
| static FuncExpr * |
| TFGetFuncExpr(GppcFcinfo info, int argno, Oid typid) |
| { |
| FunctionCallInfo fcinfo = GPPC_MAP_FCINFO(info); |
| FuncExpr *fexpr; |
| |
| /* Describe function argument is only 1 */ |
| if (PG_NARGS() < 1) |
| { |
| elog(DEBUG1, "invalid describe function or TFGetFuncExpr call"); |
| return NULL; |
| } |
| |
| fexpr = (FuncExpr *) PG_GETARG_POINTER(0); |
| if (!fexpr || !IsA(fexpr, FuncExpr)) |
| { |
| elog(DEBUG1, "expected FuncExpr, but something else is found"); |
| return NULL; |
| } |
| |
| if (list_length(fexpr->args) <= argno) |
| { |
| elog(DEBUG1, "argno is out of range"); |
| return NULL; |
| } |
| |
| if (OidIsValid(typid) && |
| exprType(list_nth(fexpr->args, argno)) != typid) |
| { |
| elog(DEBUG1, "expected type = %u, but the expression returns different", typid); |
| return NULL; |
| } |
| |
| return fexpr; |
| } |
| |
| /* |
| * Evaluates function argument to constant Datum. |
| * iserror is set if function is unexpectedly structured. |
| * This function is designed for describe function. |
| */ |
| GppcDatum |
| GppcTFGetArgDatum(GppcFcinfo info, GppcOid typid, int argno, bool *isnull, bool *iserror) |
| { |
| FuncExpr *fexpr; |
| |
| if (iserror) |
| *iserror = false; |
| |
| fexpr = TFGetFuncExpr(info, argno, (Oid) typid); |
| |
| if (!fexpr) |
| { |
| if (iserror) |
| *iserror = true; |
| if (isnull) |
| *isnull = true; |
| return (GppcDatum) 0; |
| } |
| |
| return ExecEvalFunctionArgToConst(fexpr, argno, isnull); |
| } |
| |
| /* |
| * Create TupleDesc for the table function input TABLE expression. |
| * iserror is set if function is unexpectedly structured. |
| * This function is designed for describe function. |
| */ |
| GppcTupleDesc |
| GppcTFInputDesc(GppcFcinfo info, int argno, bool *iserror) |
| { |
| FuncExpr *fexpr; |
| TableValueExpr *texpr; |
| Query *qexpr; |
| |
| if (iserror) |
| *iserror = false; |
| fexpr = TFGetFuncExpr(info, argno, ANYTABLEOID); |
| if (!fexpr) |
| { |
| if (iserror) |
| *iserror = true; |
| return NULL; |
| } |
| |
| texpr = (TableValueExpr *) list_nth(fexpr->args, argno); |
| if (!texpr || !IsA(texpr, TableValueExpr)) |
| { |
| if (iserror) |
| *iserror = true; |
| return NULL; |
| } |
| |
| qexpr = (Query *) texpr->subquery; |
| if (!qexpr || !IsA(qexpr, Query)) |
| { |
| if (iserror) |
| *iserror = true; |
| return NULL; |
| } |
| |
| return (GppcTupleDesc) ExecCleanTypeFromTL(qexpr->targetList, false); |
| } |
| |
| #endif |
| |
| /* |
| * elog |
| */ |
| void |
| GppcReport(GppcReportLevel elevel, const char *fmt, ...) |
| { |
| va_list ap; |
| char buf[1024]; |
| |
| /* elog doesn't accept va_list. There could be a better way... */ |
| va_start(ap, fmt); |
| vsnprintf(buf, sizeof(buf), fmt, ap); |
| va_end(ap); |
| elog(elevel, "%s", (const char *) buf); |
| } |
| |
| /* |
| * This gets in between user hook and error process and extracts error state from |
| * the stack to pass to the hook function. |
| */ |
| static void |
| ReportCallbackInvoker(void *arg) |
| { |
| GppcReportCallbackState cbstate = (GppcReportCallbackState) arg; |
| ErrorData *edata; |
| MemoryContext oldcontext; |
| |
| /* |
| * CopyErrorData() needs to be in other context than ErrorContext. |
| */ |
| oldcontext = MemoryContextSwitchTo(CurTransactionContext); |
| edata = CopyErrorData(); |
| cbstate->func((GppcReportInfo) edata, cbstate->arg); |
| pfree(edata); |
| MemoryContextSwitchTo(oldcontext); |
| } |
| |
| /* |
| * Add the error context callback to the stack. This searches the |
| * same callback with the same argument by comparing the callback |
| * and arg address, and if found return the stack pointer without |
| * installing new hook. The function signature of the hook is |
| * slightly different from PG's hook function, for we want to know |
| * the error state. |
| */ |
| GppcReportCallbackState |
| GppcInstallReportCallback(void (*func)(GppcReportInfo, void *), void *arg) |
| { |
| GppcReportCallbackState cbstate; |
| ErrorContextCallback *errcontext; |
| |
| errcontext = error_context_stack; |
| while (errcontext) |
| { |
| /* If found, just return it. */ |
| if (errcontext->callback == ReportCallbackInvoker && |
| ((GppcReportCallbackState) errcontext->arg)->func == func && |
| ((GppcReportCallbackState) errcontext->arg)->arg == arg) |
| return (GppcReportCallbackState) errcontext; |
| errcontext = errcontext->previous; |
| } |
| /* |
| * Allocate it in ErrorContext as the caller may expect it to live longer. |
| */ |
| cbstate = (GppcReportCallbackState) |
| MemoryContextAlloc(ErrorContext, sizeof(GppcReportCallbackStateData)); |
| errcontext = (ErrorContextCallback *) cbstate; |
| cbstate->func = func; |
| cbstate->arg = arg; |
| |
| errcontext->callback = ReportCallbackInvoker; |
| errcontext->arg = cbstate; |
| errcontext->previous = error_context_stack; |
| error_context_stack = errcontext; |
| |
| return cbstate; |
| } |
| |
| /* |
| * Uninstall the error context callback from the stack. The callback |
| * may be not on the top of stack, but we search it by the given pointer |
| * and fix the linked list in the middle of stack if found. In case |
| * the hook is not found, this function does not do anything and silently |
| * return. |
| */ |
| void |
| GppcUninstallReportCallback(GppcReportCallbackState cbstate) |
| { |
| ErrorContextCallback *errcontext, *next; |
| |
| errcontext = error_context_stack; |
| next = NULL; |
| while (errcontext) |
| { |
| if (errcontext == (ErrorContextCallback *) cbstate) |
| break; |
| next = errcontext; |
| errcontext = errcontext->previous; |
| } |
| if (errcontext) |
| { |
| if (next) |
| next->previous = errcontext->previous; |
| else |
| error_context_stack = errcontext->previous; |
| |
| pfree(errcontext); |
| } |
| } |
| |
| GppcReportLevel |
| GppcGetReportLevel(GppcReportInfo info) |
| { |
| return ((ErrorData *) info)->elevel; |
| } |
| |
| const char * |
| GppcGetReportMessage(GppcReportInfo info) |
| { |
| return ((ErrorData *) info)->message; |
| } |
| |
| /* |
| * CreateTemplateTupleDesc |
| * We omit unuseful last parameter. |
| */ |
| GppcTupleDesc |
| GppcCreateTemplateTupleDesc(int natts) |
| { |
| return (GppcTupleDesc) CreateTemplateTupleDesc(natts, false); |
| } |
| |
| /* |
| * TupleDescInitEntry |
| * We omit unuseful last parameter. |
| * attno starts from 1 |
| */ |
| void |
| GppcTupleDescInitEntry(GppcTupleDesc desc, |
| uint16_t attno, |
| const char *attname, |
| GppcOid typid, |
| int32_t typmod) |
| { |
| TupleDescInitEntry((TupleDesc) desc, attno, attname, typid, typmod, 0); |
| } |
| |
| /* |
| * heap_form_tuple |
| */ |
| GppcHeapTuple |
| GppcHeapFormTuple(GppcTupleDesc tupdesc, GppcDatum *values, bool *nulls) |
| { |
| return (GppcHeapTuple) heap_form_tuple((TupleDesc) tupdesc, values, nulls); |
| } |
| |
| /* |
| * Shortcut for heap_form_tuple + HeapTupleGetDatum |
| */ |
| GppcDatum |
| GppcBuildHeapTupleDatum(GppcTupleDesc tupdesc, GppcDatum *values, bool *nulls) |
| { |
| HeapTuple tuple; |
| |
| tuple = heap_form_tuple((TupleDesc) tupdesc, values, nulls); |
| return (GppcDatum) HeapTupleGetDatum(tuple); |
| } |
| |
| /* |
| * Accessor to HeapTuple |
| */ |
| GppcDatum |
| GppcGetAttributeByName(GppcHeapTuple tuple, const char *attname, bool *isnull) |
| { |
| return (GppcDatum) GetAttributeByName(((HeapTuple) tuple)->t_data, attname, isnull); |
| } |
| |
| /* attno starts from 1 */ |
| GppcDatum |
| GppcGetAttributeByNum(GppcHeapTuple tuple, int16_t attno, bool *isnull) |
| { |
| return (GppcDatum) GetAttributeByNum(((HeapTuple) tuple)->t_data, attno, isnull); |
| } |
| |
| /* |
| * Accessor to TupleDesc |
| */ |
| int |
| GppcTupleDescNattrs(GppcTupleDesc tupdesc) |
| { |
| return ((TupleDesc) tupdesc)->natts; |
| } |
| |
| /* attno starts from 0 */ |
| const char * |
| GppcTupleDescAttrName(GppcTupleDesc tupdesc, int16_t attno) |
| { |
| CHECK_TUPLEDESC_ATTNO(tupdesc, attno); |
| return NameStr(((TupleDesc) tupdesc)->attrs[attno]->attname); |
| } |
| |
| /* attno starts from 0 */ |
| GppcOid |
| GppcTupleDescAttrType(GppcTupleDesc tupdesc, int16_t attno) |
| { |
| CHECK_TUPLEDESC_ATTNO(tupdesc, attno); |
| return (GppcOid) ((TupleDesc) tupdesc)->attrs[attno]->atttypid; |
| } |
| |
| /* attno starts from 0 */ |
| int32_t |
| GppcTupleDescAttrTypmod(GppcTupleDesc tupdesc, int16_t attno) |
| { |
| CHECK_TUPLEDESC_ATTNO(tupdesc, attno); |
| return (int32_t) ((TupleDesc) tupdesc)->attrs[attno]->atttypmod; |
| } |
| |
| /* attno starts from 0 */ |
| int16_t |
| GppcTupleDescAttrLen(GppcTupleDesc tupdesc, int16_t attno) |
| { |
| CHECK_TUPLEDESC_ATTNO(tupdesc, attno); |
| return (int16_t) ((TupleDesc) tupdesc)->attrs[attno]->attlen; |
| } |
| |
| /* |
| * GetDatabaseEncoding |
| * Returns the value from GetDatabaseEncoding for now, but if we renumber pg_enc, |
| * we'll need to map it GppcEncoding to keep compatibility. |
| */ |
| GppcEncoding |
| GppcGetDatabaseEncoding(void) |
| { |
| return GetDatabaseEncoding(); |
| } |
| |
| /* |
| * pg_database_encoding_max_length |
| */ |
| int |
| GppcDatabaseEncodingMaxLength(void) |
| { |
| return pg_database_encoding_max_length(); |
| } |
| |
| /* |
| * Translates a GppcEncoding value to a human readable string. |
| */ |
| const char * |
| GppcDatabaseEncodingName(GppcEncoding enc) |
| { |
| return pg_encoding_to_char(enc); |
| } |