From ff2892ecd7ceebde13321bade5b9855433da3295 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Tue, 2 Jun 2020 16:47:26 -0700 Subject: [PATCH v2 7/9] wip: make send/recv calls in printtup.c & copy.c cheaper. Author: Reviewed-By: Discussion: https://postgr.es/m/ Backpatch: --- src/backend/access/common/printtup.c | 37 ++++- src/backend/commands/copy.c | 208 ++++++++++++++++++++------- 2 files changed, 189 insertions(+), 56 deletions(-) diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index dd1bac0aa9e..db23bb99c16 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -56,6 +56,15 @@ typedef struct bool typisvarlena; /* is it varlena (ie possibly toastable)? */ int16 format; /* format code for this column */ FmgrInfo finfo; /* Precomputed call info for output fn */ + + /* use union with FunctionCallInfoBaseData to guarantee alignment */ + union + { + FunctionCallInfoBaseData fcinfo; + /* ensure enough space for nargs args is available */ + char fcinfo_data[SizeForFunctionCallInfo(1)]; + } fcinfo_data; + } PrinttupAttrInfo; typedef struct @@ -355,6 +364,9 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs) &thisState->typoutput, &thisState->typisvarlena); fmgr_info(thisState->typoutput, &thisState->finfo); + InitFunctionCallInfoData(thisState->fcinfo_data.fcinfo, + &thisState->finfo, 1, InvalidOid, + NULL, NULL); } else if (format == 1) { @@ -362,6 +374,9 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs) &thisState->typsend, &thisState->typisvarlena); fmgr_info(thisState->typsend, &thisState->finfo); + InitFunctionCallInfoData(thisState->fcinfo_data.fcinfo, + &thisState->finfo, 1, InvalidOid, + NULL, NULL); } else ereport(ERROR, @@ -438,11 +453,25 @@ printtup(TupleTableSlot *slot, DestReceiver *self) { /* Binary output */ bytea *outputbytes; + int outputlen; + Datum result; + FunctionCallInfo fcinfo = &thisState->fcinfo_data.fcinfo; - outputbytes = SendFunctionCall(&thisState->finfo, attr); - pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ); - pq_sendbytes(buf, VARDATA(outputbytes), - VARSIZE(outputbytes) - VARHDRSZ); + fcinfo->args[0].value = attr; + fcinfo->args[0].isnull = false; + result = FunctionCallInvoke(fcinfo); + + /* Check for null result, since caller is clearly not expecting one */ + if (unlikely(fcinfo->isnull)) + elog(ERROR, "send function return null"); + + outputbytes = DatumGetByteaP(result); + outputlen = VARSIZE(outputbytes) - VARHDRSZ; + + Assert(outputlen > 0); + + pq_sendint32(buf, outputlen); + pq_sendbytes(buf, VARDATA(outputbytes), outputlen); } } diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index c12855a304a..bec1ce51260 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -95,6 +95,37 @@ typedef enum CopyInsertMethod CIM_MULTI_CONDITIONAL /* use table_multi_insert only if valid */ } CopyInsertMethod; +typedef struct CopyInAttributeInfo +{ + AttrNumber num; + const char *name; + + Oid typioparam; + int32 typmod; + + FmgrInfo in_finfo; + union + { + FunctionCallInfoBaseData fcinfo; + char fcinfo_data[SizeForFunctionCallInfo(3)]; + } in_fcinfo; + +} CopyInAttributeInfo; + +typedef struct CopyOutAttributeInfo +{ + AttrNumber num; + const char *name; + int32 typid; + + FmgrInfo out_finfo; + union + { + FunctionCallInfoBaseData fcinfo; + char fcinfo_data[SizeForFunctionCallInfo(1)]; + } out_fcinfo; +} CopyOutAttributeInfo; + /* * This struct contains all the state variables used throughout a COPY * operation. For simplicity, we use the same struct for all variants of COPY, @@ -169,15 +200,14 @@ typedef struct CopyStateData /* * Working state for COPY TO */ - FmgrInfo *out_functions; /* lookup info for output functions */ + CopyOutAttributeInfo *out_attributes; MemoryContext rowcontext; /* per-row evaluation context */ /* * Working state for COPY FROM */ AttrNumber num_defaults; - FmgrInfo *in_functions; /* array of input functions for each attrs */ - Oid *typioparams; /* array of element types for in_functions */ + CopyInAttributeInfo *in_attributes; int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ @@ -368,8 +398,8 @@ static bool CopyReadLineText(CopyState cstate); static int CopyReadAttributesText(CopyState cstate); static int CopyReadAttributesCSV(CopyState cstate); static Datum CopyReadBinaryAttribute(CopyState cstate, - int column_no, FmgrInfo *flinfo, - Oid typioparam, int32 typmod, + int column_no, + CopyInAttributeInfo *attr, bool *isnull); static void CopyAttributeOutText(CopyState cstate, char *string); static void CopyAttributeOutCSV(CopyState cstate, char *string, @@ -2027,23 +2057,34 @@ CopyTo(CopyState cstate) cstate->fe_msgbuf = makeStringInfo(); /* Get info about the columns we need to process. */ - cstate->out_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo)); + cstate->out_attributes = + (CopyOutAttributeInfo *) palloc(num_phys_attrs * sizeof(CopyOutAttributeInfo)); foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); + CopyOutAttributeInfo *attr = &cstate->out_attributes[attnum - 1]; + Form_pg_attribute pgatt; Oid out_func_oid; bool isvarlena; - Form_pg_attribute attr = TupleDescAttr(tupDesc, attnum - 1); + + pgatt = TupleDescAttr(tupDesc, attnum - 1); + attr->num = attnum; + attr->name = NameStr(pgatt->attname); + attr->typid = pgatt->atttypid; if (cstate->binary) - getTypeBinaryOutputInfo(attr->atttypid, + getTypeBinaryOutputInfo(attr->typid, &out_func_oid, &isvarlena); else - getTypeOutputInfo(attr->atttypid, + getTypeOutputInfo(attr->typid, &out_func_oid, &isvarlena); - fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); + + fmgr_info(out_func_oid, &attr->out_finfo); + InitFunctionCallInfoData(attr->out_fcinfo.fcinfo, &attr->out_finfo, + 1, InvalidOid, + NULL, NULL); } /* @@ -2156,7 +2197,7 @@ static void CopyOneRowTo(CopyState cstate, TupleTableSlot *slot) { bool need_delim = false; - FmgrInfo *out_functions = cstate->out_functions; + CopyOutAttributeInfo *out_attributes = cstate->out_attributes; MemoryContext oldcontext; ListCell *cur; char *string; @@ -2176,6 +2217,8 @@ CopyOneRowTo(CopyState cstate, TupleTableSlot *slot) foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); + CopyOutAttributeInfo *attr = &out_attributes[attnum - 1]; + FunctionCallInfo fcinfo = &attr->out_fcinfo.fcinfo; Datum value = slot->tts_values[attnum - 1]; bool isnull = slot->tts_isnull[attnum - 1]; @@ -2197,8 +2240,19 @@ CopyOneRowTo(CopyState cstate, TupleTableSlot *slot) { if (!cstate->binary) { - string = OutputFunctionCall(&out_functions[attnum - 1], - value); + Datum result; + + fcinfo->args[0].value = value; + fcinfo->args[0].isnull = false; + + result = FunctionCallInvoke(fcinfo); + + /* Check for null result, since caller is clearly not expecting one */ + if (unlikely(fcinfo->isnull)) + elog(ERROR, "send function return null"); + + string = DatumGetCString(result); + if (cstate->csv_mode) CopyAttributeOutCSV(cstate, string, cstate->force_quote_flags[attnum - 1], @@ -2208,13 +2262,23 @@ CopyOneRowTo(CopyState cstate, TupleTableSlot *slot) } else { + int outputlen; + Datum result; bytea *outputbytes; - outputbytes = SendFunctionCall(&out_functions[attnum - 1], - value); - CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); - CopySendData(cstate, VARDATA(outputbytes), - VARSIZE(outputbytes) - VARHDRSZ); + fcinfo->args[0].value = value; + fcinfo->args[0].isnull = false; + result = FunctionCallInvoke(fcinfo); + + /* Check for null result, since caller is clearly not expecting one */ + if (unlikely(fcinfo->isnull)) + elog(ERROR, "send function return null"); + + outputbytes = DatumGetByteaP(result); + outputlen = VARSIZE(outputbytes) - VARHDRSZ; + + CopySendInt32(cstate, outputlen); + CopySendData(cstate, VARDATA(outputbytes), outputlen); } } } @@ -3344,8 +3408,7 @@ BeginCopyFrom(ParseState *pstate, TupleDesc tupDesc; AttrNumber num_phys_attrs, num_defaults; - FmgrInfo *in_functions; - Oid *typioparams; + CopyInAttributeInfo *in_attributes; int attnum; Oid in_func_oid; int *defmap; @@ -3386,30 +3449,39 @@ BeginCopyFrom(ParseState *pstate, * the input function), and info about defaults and constraints. (Which * input function we use depends on text/binary format choice.) */ - in_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo)); - typioparams = (Oid *) palloc(num_phys_attrs * sizeof(Oid)); + in_attributes = (CopyInAttributeInfo * ) palloc0(num_phys_attrs * sizeof(CopyInAttributeInfo)); + defmap = (int *) palloc(num_phys_attrs * sizeof(int)); defexprs = (ExprState **) palloc(num_phys_attrs * sizeof(ExprState *)); for (attnum = 1; attnum <= num_phys_attrs; attnum++) { - Form_pg_attribute att = TupleDescAttr(tupDesc, attnum - 1); + CopyInAttributeInfo *attr = &in_attributes[attnum - 1]; + Form_pg_attribute pgatt = TupleDescAttr(tupDesc, attnum - 1); /* We don't need info for dropped attributes */ - if (att->attisdropped) + if (pgatt->attisdropped) continue; + attr->num = attnum; + attr->typmod = pgatt->atttypmod; + attr->name = NameStr(pgatt->attname); + /* Fetch the input function and typioparam info */ if (cstate->binary) - getTypeBinaryInputInfo(att->atttypid, - &in_func_oid, &typioparams[attnum - 1]); + getTypeBinaryInputInfo(pgatt->atttypid, + &in_func_oid, &attr->typioparam); else - getTypeInputInfo(att->atttypid, - &in_func_oid, &typioparams[attnum - 1]); - fmgr_info(in_func_oid, &in_functions[attnum - 1]); + getTypeInputInfo(pgatt->atttypid, + &in_func_oid, &attr->typioparam); + attr->typmod = pgatt->atttypmod; + + fmgr_info(in_func_oid, &attr->in_finfo); + InitFunctionCallInfoData(attr->in_fcinfo.fcinfo, &attr->in_finfo, 3, + InvalidOid, NULL, NULL); /* Get default info if needed */ - if (!list_member_int(cstate->attnumlist, attnum) && !att->attgenerated) + if (!list_member_int(cstate->attnumlist, attnum) && !pgatt->attgenerated) { /* attribute is NOT to be copied from input */ /* use default value if one exists */ @@ -3446,8 +3518,7 @@ BeginCopyFrom(ParseState *pstate, } /* We keep those variables in cstate. */ - cstate->in_functions = in_functions; - cstate->typioparams = typioparams; + cstate->in_attributes = in_attributes; cstate->defmap = defmap; cstate->defexprs = defexprs; cstate->volatile_defexprs = volatile_defexprs; @@ -3639,8 +3710,7 @@ NextCopyFrom(CopyState cstate, ExprContext *econtext, AttrNumber num_phys_attrs, attr_count, num_defaults = cstate->num_defaults; - FmgrInfo *in_functions = cstate->in_functions; - Oid *typioparams = cstate->typioparams; + CopyInAttributeInfo *in_attributes = cstate->in_attributes; int i; int *defmap = cstate->defmap; ExprState **defexprs = cstate->defexprs; @@ -3678,13 +3748,14 @@ NextCopyFrom(CopyState cstate, ExprContext *econtext, { int attnum = lfirst_int(cur); int m = attnum - 1; - Form_pg_attribute att = TupleDescAttr(tupDesc, m); + CopyInAttributeInfo *attr = &in_attributes[m]; + FunctionCallInfo fcinfo = &attr->in_fcinfo.fcinfo; if (fieldno >= fldct) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("missing data for column \"%s\"", - NameStr(att->attname)))); + attr->name))); string = field_strings[fieldno++]; if (cstate->convert_select_flags && @@ -3718,14 +3789,39 @@ NextCopyFrom(CopyState cstate, ExprContext *econtext, } } - cstate->cur_attname = NameStr(att->attname); + cstate->cur_attname = attr->name; cstate->cur_attval = string; - values[m] = InputFunctionCall(&in_functions[m], - string, - typioparams[m], - att->atttypmod); - if (string != NULL) - nulls[m] = false; + + if (string == NULL && fcinfo->flinfo->fn_strict) + nulls[m] = true; + else + { + fcinfo->args[0].value = CStringGetDatum(string); + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = ObjectIdGetDatum(attr->typioparam); + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = Int32GetDatum(attr->typmod); + fcinfo->args[2].isnull = false; + + values[m] = FunctionCallInvoke(fcinfo); + + /* Should get null result if and only if str is NULL */ + if (string == NULL) + { + if (unlikely(!fcinfo->isnull)) + elog(ERROR, "input function %u returned non-NULL", + fcinfo->flinfo->fn_oid); + } + else + { + if (unlikely(fcinfo->isnull)) + elog(ERROR, "input function %u returned NULL", + fcinfo->flinfo->fn_oid); + } + + nulls[m] = string == NULL; + } + cstate->cur_attname = NULL; cstate->cur_attval = NULL; } @@ -3787,9 +3883,7 @@ NextCopyFrom(CopyState cstate, ExprContext *econtext, i++; values[m] = CopyReadBinaryAttribute(cstate, i, - &in_functions[m], - typioparams[m], - att->atttypmod, + &in_attributes[m], &nulls[m]); cstate->cur_attname = NULL; } @@ -4714,13 +4808,13 @@ endfield: * Read a binary attribute */ static Datum -CopyReadBinaryAttribute(CopyState cstate, - int column_no, FmgrInfo *flinfo, - Oid typioparam, int32 typmod, +CopyReadBinaryAttribute(CopyState cstate, int column_no, + CopyInAttributeInfo *attr, bool *isnull) { int32 fld_size; Datum result; + FunctionCallInfo fcinfo = &attr->in_fcinfo.fcinfo; if (!CopyGetInt32(cstate, &fld_size)) ereport(ERROR, @@ -4729,7 +4823,7 @@ CopyReadBinaryAttribute(CopyState cstate, if (fld_size == -1) { *isnull = true; - return ReceiveFunctionCall(flinfo, NULL, typioparam, typmod); + return ReceiveFunctionCall(fcinfo->flinfo, NULL, attr->typioparam, attr->typmod); } if (fld_size < 0) ereport(ERROR, @@ -4750,8 +4844,18 @@ CopyReadBinaryAttribute(CopyState cstate, cstate->attribute_buf.data[fld_size] = '\0'; /* Call the column type's binary input converter */ - result = ReceiveFunctionCall(flinfo, &cstate->attribute_buf, - typioparam, typmod); + fcinfo->args[0].value = PointerGetDatum(&cstate->attribute_buf); + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = ObjectIdGetDatum(attr->typioparam); + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = Int32GetDatum(attr->typmod); + fcinfo->args[2].isnull = false; + + result = FunctionCallInvoke(fcinfo); + + if (unlikely(fcinfo->isnull)) + elog(ERROR, "receive function %u returned NULL", + fcinfo->flinfo->fn_oid); /* Trouble if it didn't eat the whole buffer */ if (cstate->attribute_buf.cursor != cstate->attribute_buf.len) -- 2.25.0.114.g5b0ca878e0