/* * charrecv - converts external binary format to char * * The external representation is one byte, with no character set * conversion. This is somewhat dubious, perhaps, but in many * cases people use char for a 1-byte binary type. */ Datum charrecv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); PG_RETURN_CHAR(pq_getmsgbyte(buf)); }
/* * boolrecv - converts external binary format to bool * * The external representation is one byte. Any nonzero value is taken * as "true". */ Datum boolrecv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); int ext; ext = pq_getmsgbyte(buf); PG_RETURN_BOOL((ext != 0) ? true : false); }
Datum ipaddr_recv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); IP ip; int af, bits, flag, nbytes; /* we copy the external format used by inet/cidr, just because. */ af = pq_getmsgbyte(buf); if (af != PGSQL_AF_INET && af != PGSQL_AF_INET6) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("invalid address family in external IP value"))); bits = pq_getmsgbyte(buf); if (bits != ip_maxbits(af)) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("invalid bit length in external IP value"))); flag = pq_getmsgbyte(buf); /* ignored */ nbytes = pq_getmsgbyte(buf); if (nbytes*8 != bits) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("invalid address length in external IP value"))); switch (af) { case PGSQL_AF_INET: ip.ip4 = (IP4) pq_getmsgint(buf, sizeof(IP4)); break; case PGSQL_AF_INET6: ip.ip6.bits[0] = pq_getmsgint64(buf); ip.ip6.bits[1] = pq_getmsgint64(buf); break; } PG_RETURN_IP_P(ip_pack(af, &ip)); }
/* * Read transaction COMMIT from the stream. */ void logicalrep_read_commit(StringInfo in, LogicalRepCommitData *commit_data) { /* read flags (unused for now) */ uint8 flags = pq_getmsgbyte(in); if (flags != 0) elog(ERROR, "unrecognized flags %u in commit message", flags); /* read fields */ commit_data->commit_lsn = pq_getmsgint64(in); commit_data->end_lsn = pq_getmsgint64(in); commit_data->committime = pq_getmsgint64(in); }
/* * Read UPDATE from stream. */ LogicalRepRelId logicalrep_read_update(StringInfo in, bool *has_oldtuple, LogicalRepTupleData *oldtup, LogicalRepTupleData *newtup) { char action; LogicalRepRelId relid; /* read the relation id */ relid = pq_getmsgint(in, 4); /* read and verify action */ action = pq_getmsgbyte(in); if (action != 'K' && action != 'O' && action != 'N') elog(ERROR, "expected action 'N', 'O' or 'K', got %c", action); /* check for old tuple */ if (action == 'K' || action == 'O') { logicalrep_read_tuple(in, oldtup); *has_oldtuple = true; action = pq_getmsgbyte(in); } else *has_oldtuple = false; /* check for new tuple */ if (action != 'N') elog(ERROR, "expected action 'N', got %c", action); logicalrep_read_tuple(in, newtup); return relid; }
/* * Read tuple in remote format from stream. * * The returned tuple points into the input stringinfo. */ static void logicalrep_read_tuple(StringInfo in, LogicalRepTupleData *tuple) { int i; int natts; /* Get number of attributes */ natts = pq_getmsgint(in, 2); memset(tuple->changed, 0, sizeof(tuple->changed)); /* Read the data */ for (i = 0; i < natts; i++) { char kind; kind = pq_getmsgbyte(in); switch (kind) { case 'n': /* null */ tuple->values[i] = NULL; tuple->changed[i] = true; break; case 'u': /* unchanged column */ /* we don't receive the value of an unchanged column */ tuple->values[i] = NULL; break; case 't': /* text formatted value */ { int len; tuple->changed[i] = true; len = pq_getmsgint(in, 4); /* read length */ /* and data */ tuple->values[i] = palloc(len + 1); pq_copymsgbytes(in, tuple->values[i], len); tuple->values[i][len] = '\0'; } break; default: elog(ERROR, "unrecognized data representation type '%c'", kind); } } }
/* * Read DELETE from stream. * * Fills the old tuple. */ LogicalRepRelId logicalrep_read_delete(StringInfo in, LogicalRepTupleData *oldtup) { char action; LogicalRepRelId relid; /* read the relation id */ relid = pq_getmsgint(in, 4); /* read and verify action */ action = pq_getmsgbyte(in); if (action != 'K' && action != 'O') elog(ERROR, "expected action 'O' or 'K', got %c", action); logicalrep_read_tuple(in, oldtup); return relid; }
/* * Read INSERT from stream. * * Fills the new tuple. */ LogicalRepRelId logicalrep_read_insert(StringInfo in, LogicalRepTupleData *newtup) { char action; LogicalRepRelId relid; /* read the relation id */ relid = pq_getmsgint(in, 4); action = pq_getmsgbyte(in); if (action != 'N') elog(ERROR, "expected new tuple but got %d", action); logicalrep_read_tuple(in, newtup); return relid; }
/* * Read the relation info from stream and return as LogicalRepRelation. */ LogicalRepRelation * logicalrep_read_rel(StringInfo in) { LogicalRepRelation *rel = palloc(sizeof(LogicalRepRelation)); rel->remoteid = pq_getmsgint(in, 4); /* Read relation name from stream */ rel->nspname = pstrdup(logicalrep_read_namespace(in)); rel->relname = pstrdup(pq_getmsgstring(in)); /* Read the replica identity. */ rel->replident = pq_getmsgbyte(in); /* Get attribute description */ logicalrep_read_attrs(in, rel); return rel; }
/* * Read tuple in remote format from stream. * * The returned tuple points into the input stringinfo. */ static void logicalrep_read_tuple(StringInfo in, LogicalRepTupleData *tuple) { int i; int natts; /* Get of attributes. */ natts = pq_getmsgint(in, 2); memset(tuple->changed, 0, sizeof(tuple->changed)); /* Read the data */ for (i = 0; i < natts; i++) { char kind; int len; kind = pq_getmsgbyte(in); switch (kind) { case 'n': /* null */ tuple->values[i] = NULL; tuple->changed[i] = true; break; case 'u': /* unchanged column */ tuple->values[i] = (char *) 0xdeadbeef; /* make bad usage more obvious */ break; case 't': /* text formatted value */ { tuple->changed[i] = true; len = pq_getmsgint(in, 4); /* read length */ /* and data */ tuple->values[i] = (char *) pq_getmsgbytes(in, len); } break; default: elog(ERROR, "unknown data representation type '%c'", kind); } } }
/* * Process a status update message received from standby. */ static void ProcessStandbyMessage(void) { char msgtype; resetStringInfo(&reply_message); /* * Read the message contents. */ if (pq_getmessage(&reply_message, 0)) { ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("unexpected EOF on standby connection"))); proc_exit(0); } /* * Check message type from the first byte. */ msgtype = pq_getmsgbyte(&reply_message); switch (msgtype) { case 'r': ProcessStandbyReplyMessage(); break; case 'h': ProcessStandbyHSFeedbackMessage(); break; default: ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("unexpected message type \"%c\"", msgtype))); proc_exit(0); } }
/* * macaddr_recv - converts external binary format to macaddr * * The external representation is just the six bytes, MSB first. */ Datum macaddr_recv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); macaddr *addr; addr = (macaddr *) palloc(sizeof(macaddr)); addr->a = pq_getmsgbyte(buf); addr->b = pq_getmsgbyte(buf); addr->c = pq_getmsgbyte(buf); addr->d = pq_getmsgbyte(buf); addr->e = pq_getmsgbyte(buf); addr->f = pq_getmsgbyte(buf); PG_RETURN_MACADDR_P(addr); }
/* * Read relation attribute names from the stream. */ static void logicalrep_read_attrs(StringInfo in, LogicalRepRelation *rel) { int i; int natts; char **attnames; Oid *atttyps; Bitmapset *attkeys = NULL; natts = pq_getmsgint(in, 2); attnames = palloc(natts * sizeof(char *)); atttyps = palloc(natts * sizeof(Oid)); /* read the attributes */ for (i = 0; i < natts; i++) { uint8 flags; /* Check for replica identity column */ flags = pq_getmsgbyte(in); if (flags & LOGICALREP_IS_REPLICA_IDENTITY) attkeys = bms_add_member(attkeys, i); /* attribute name */ attnames[i] = pstrdup(pq_getmsgstring(in)); /* attribute type id */ atttyps[i] = (Oid) pq_getmsgint(in, 4); /* we ignore attribute mode for now */ (void) pq_getmsgint(in, 4); } rel->attnames = attnames; rel->atttyps = atttyps; rel->attkeys = attkeys; rel->natts = natts; }
/* * Deserialize a HeapTuple's data from a byte-array. * * This code is based on the binary input handling functions in copy.c. */ HeapTuple DeserializeTuple(SerTupInfo * pSerInfo, StringInfo serialTup) { MemoryContext oldCtxt; TupleDesc tupdesc; HeapTuple htup; int natts; SerAttrInfo *attrInfo; uint32 attr_size; int i; StringInfoData attr_data; bool fHandled; AssertArg(pSerInfo != NULL); AssertArg(serialTup != NULL); tupdesc = pSerInfo->tupdesc; natts = tupdesc->natts; /* * Flip to our tuple-serialization memory-context, to speed up memory * reclamation operations. */ AssertState(s_tupSerMemCtxt != NULL); oldCtxt = MemoryContextSwitchTo(s_tupSerMemCtxt); /* Receive nulls character-array. */ pq_copymsgbytes(serialTup, pSerInfo->nulls, natts); skipPadding(serialTup); /* Deserialize the non-NULL attributes of this tuple */ initStringInfo(&attr_data); for (i = 0; i < natts; ++i) { attrInfo = pSerInfo->myinfo + i; if (pSerInfo->nulls[i]) /* NULL field. */ { pSerInfo->values[i] = (Datum) 0; continue; } /* * Assume that the data's output will be handled by the special IO * code, and if not then we can handle it the slow way. */ fHandled = true; switch (attrInfo->atttypid) { case INT4OID: pSerInfo->values[i] = Int32GetDatum(stringInfoGetInt32(serialTup)); break; case CHAROID: pSerInfo->values[i] = CharGetDatum(pq_getmsgbyte(serialTup)); skipPadding(serialTup); break; case BPCHAROID: case VARCHAROID: case INT2VECTOROID: /* postgres serialization logic broken, use our own */ case OIDVECTOROID: /* postgres serialization logic broken, use our own */ case ANYARRAYOID: { text *pText; int textSize; textSize = stringInfoGetInt32(serialTup); #ifdef TUPSER_SCRATCH_SPACE if (textSize + VARHDRSZ <= attrInfo->varlen_scratch_size) pText = (text *) attrInfo->pv_varlen_scratch; else pText = (text *) palloc(textSize + VARHDRSZ); #else pText = (text *) palloc(textSize + VARHDRSZ); #endif SET_VARSIZE(pText, textSize + VARHDRSZ); pq_copymsgbytes(serialTup, VARDATA(pText), textSize); skipPadding(serialTup); pSerInfo->values[i] = PointerGetDatum(pText); break; } case DATEOID: { /* * TODO: I would LIKE to do something more efficient, but * DateADT is not strictly limited to 4 bytes by its * definition. */ DateADT date; pq_copymsgbytes(serialTup, (char *) &date, sizeof(DateADT)); skipPadding(serialTup); pSerInfo->values[i] = DateADTGetDatum(date); break; } case NUMERICOID: { /* * Treat the numeric as a varlena variable, and just push * the whole shebang to the output-buffer. We don't care * about the guts of the numeric. */ Numeric num; int numSize; numSize = stringInfoGetInt32(serialTup); #ifdef TUPSER_SCRATCH_SPACE if (numSize + VARHDRSZ <= attrInfo->varlen_scratch_size) num = (Numeric) attrInfo->pv_varlen_scratch; else num = (Numeric) palloc(numSize + VARHDRSZ); #else num = (Numeric) palloc(numSize + VARHDRSZ); #endif SET_VARSIZE(num, numSize + VARHDRSZ); pq_copymsgbytes(serialTup, VARDATA(num), numSize); skipPadding(serialTup); pSerInfo->values[i] = NumericGetDatum(num); break; } case ACLITEMOID: { int aclSize, k, cnt; char *inputstring, *starsfree; aclSize = stringInfoGetInt32(serialTup); inputstring = (char*) palloc(aclSize + 1); starsfree = (char*) palloc(aclSize + 1); cnt = 0; pq_copymsgbytes(serialTup, inputstring, aclSize); skipPadding(serialTup); inputstring[aclSize] = '\0'; for(k=0; k<aclSize; k++) { if( inputstring[k] != '*') { starsfree[cnt] = inputstring[k]; cnt++; } } starsfree[cnt] = '\0'; pSerInfo->values[i] = DirectFunctionCall1(aclitemin, CStringGetDatum(starsfree)); pfree(inputstring); break; } case 210: { int strsize; char *smgrstr; strsize = stringInfoGetInt32(serialTup); smgrstr = (char*) palloc(strsize + 1); pq_copymsgbytes(serialTup, smgrstr, strsize); skipPadding(serialTup); smgrstr[strsize] = '\0'; pSerInfo->values[i] = DirectFunctionCall1(smgrin, CStringGetDatum(smgrstr)); break; } default: fHandled = false; } if (fHandled) continue; attr_size = stringInfoGetInt32(serialTup); /* reset attr_data to empty, and load raw data into it */ attr_data.len = 0; attr_data.data[0] = '\0'; attr_data.cursor = 0; appendBinaryStringInfo(&attr_data, pq_getmsgbytes(serialTup, attr_size), attr_size); skipPadding(serialTup); /* Call the attribute type's binary input converter. */ if (attrInfo->recv_finfo.fn_nargs == 1) pSerInfo->values[i] = FunctionCall1(&attrInfo->recv_finfo, PointerGetDatum(&attr_data)); else if (attrInfo->recv_finfo.fn_nargs == 2) pSerInfo->values[i] = FunctionCall2(&attrInfo->recv_finfo, PointerGetDatum(&attr_data), ObjectIdGetDatum(attrInfo->recv_typio_param)); else if (attrInfo->recv_finfo.fn_nargs == 3) pSerInfo->values[i] = FunctionCall3(&attrInfo->recv_finfo, PointerGetDatum(&attr_data), ObjectIdGetDatum(attrInfo->recv_typio_param), Int32GetDatum(tupdesc->attrs[i]->atttypmod) ); else { ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("Conversion function takes %d args",attrInfo->recv_finfo.fn_nargs))); } /* Trouble if it didn't eat the whole buffer */ if (attr_data.cursor != attr_data.len) { ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("incorrect binary data format"))); } } /* * Construct the tuple from the Datums and nulls values. NOTE: Switch * out of our temporary context before we form the tuple! */ MemoryContextSwitchTo(oldCtxt); htup = heap_form_tuple(tupdesc, pSerInfo->values, pSerInfo->nulls); MemoryContextReset(s_tupSerMemCtxt); /* All done. Return the result. */ return htup; }
/* * Parse an ErrorResponse or NoticeResponse payload and populate an ErrorData * structure with the results. */ void pq_parse_errornotice(StringInfo msg, ErrorData *edata) { /* Initialize edata with reasonable defaults. */ MemSet(edata, 0, sizeof(ErrorData)); edata->elevel = ERROR; edata->assoc_context = CurrentMemoryContext; /* Loop over fields and extract each one. */ for (;;) { char code = pq_getmsgbyte(msg); const char *value; if (code == '\0') { pq_getmsgend(msg); break; } value = pq_getmsgstring(msg); switch (code) { case PG_DIAG_SEVERITY: if (strcmp(value, "DEBUG") == 0) edata->elevel = DEBUG1; /* or some other DEBUG level */ else if (strcmp(value, "LOG") == 0) edata->elevel = LOG; /* can't be COMMERROR */ else if (strcmp(value, "INFO") == 0) edata->elevel = INFO; else if (strcmp(value, "NOTICE") == 0) edata->elevel = NOTICE; else if (strcmp(value, "WARNING") == 0) edata->elevel = WARNING; else if (strcmp(value, "ERROR") == 0) edata->elevel = ERROR; else if (strcmp(value, "FATAL") == 0) edata->elevel = FATAL; else if (strcmp(value, "PANIC") == 0) edata->elevel = PANIC; else elog(ERROR, "unknown error severity"); break; case PG_DIAG_SQLSTATE: if (strlen(value) != 5) elog(ERROR, "malformed sql state"); edata->sqlerrcode = MAKE_SQLSTATE(value[0], value[1], value[2], value[3], value[4]); break; case PG_DIAG_MESSAGE_PRIMARY: edata->message = pstrdup(value); break; case PG_DIAG_MESSAGE_DETAIL: edata->detail = pstrdup(value); break; case PG_DIAG_MESSAGE_HINT: edata->hint = pstrdup(value); break; case PG_DIAG_STATEMENT_POSITION: edata->cursorpos = pg_atoi(value, sizeof(int), '\0'); break; case PG_DIAG_INTERNAL_POSITION: edata->internalpos = pg_atoi(value, sizeof(int), '\0'); break; case PG_DIAG_INTERNAL_QUERY: edata->internalquery = pstrdup(value); break; case PG_DIAG_CONTEXT: edata->context = pstrdup(value); break; case PG_DIAG_SCHEMA_NAME: edata->schema_name = pstrdup(value); break; case PG_DIAG_TABLE_NAME: edata->table_name = pstrdup(value); break; case PG_DIAG_COLUMN_NAME: edata->column_name = pstrdup(value); break; case PG_DIAG_DATATYPE_NAME: edata->datatype_name = pstrdup(value); break; case PG_DIAG_CONSTRAINT_NAME: edata->constraint_name = pstrdup(value); break; case PG_DIAG_SOURCE_FILE: edata->filename = pstrdup(value); break; case PG_DIAG_SOURCE_LINE: edata->lineno = pg_atoi(value, sizeof(int), '\0'); break; case PG_DIAG_SOURCE_FUNCTION: edata->funcname = pstrdup(value); break; default: elog(ERROR, "unknown error field: %d", (int) code); break; } } }
/* * Parse an ErrorResponse or NoticeResponse payload and populate an ErrorData * structure with the results. */ void pq_parse_errornotice(StringInfo msg, ErrorData *edata) { /* Initialize edata with reasonable defaults. */ MemSet(edata, 0, sizeof(ErrorData)); edata->elevel = ERROR; edata->assoc_context = CurrentMemoryContext; /* Loop over fields and extract each one. */ for (;;) { char code = pq_getmsgbyte(msg); const char *value; if (code == '\0') { pq_getmsgend(msg); break; } value = pq_getmsgrawstring(msg); switch (code) { case PG_DIAG_SEVERITY: /* ignore, trusting we'll get a nonlocalized version */ break; case PG_DIAG_SEVERITY_NONLOCALIZED: if (strcmp(value, "DEBUG") == 0) { /* * We can't reconstruct the exact DEBUG level, but * presumably it was >= client_min_messages, so select * DEBUG1 to ensure we'll pass it on to the client. */ edata->elevel = DEBUG1; } else if (strcmp(value, "LOG") == 0) { /* * It can't be LOG_SERVER_ONLY, or the worker wouldn't * have sent it to us; so LOG is the correct value. */ edata->elevel = LOG; } else if (strcmp(value, "INFO") == 0) edata->elevel = INFO; else if (strcmp(value, "NOTICE") == 0) edata->elevel = NOTICE; else if (strcmp(value, "WARNING") == 0) edata->elevel = WARNING; else if (strcmp(value, "ERROR") == 0) edata->elevel = ERROR; else if (strcmp(value, "FATAL") == 0) edata->elevel = FATAL; else if (strcmp(value, "PANIC") == 0) edata->elevel = PANIC; else elog(ERROR, "unrecognized error severity: \"%s\"", value); break; case PG_DIAG_SQLSTATE: if (strlen(value) != 5) elog(ERROR, "invalid SQLSTATE: \"%s\"", value); edata->sqlerrcode = MAKE_SQLSTATE(value[0], value[1], value[2], value[3], value[4]); break; case PG_DIAG_MESSAGE_PRIMARY: edata->message = pstrdup(value); break; case PG_DIAG_MESSAGE_DETAIL: edata->detail = pstrdup(value); break; case PG_DIAG_MESSAGE_HINT: edata->hint = pstrdup(value); break; case PG_DIAG_STATEMENT_POSITION: edata->cursorpos = pg_strtoint32(value); break; case PG_DIAG_INTERNAL_POSITION: edata->internalpos = pg_strtoint32(value); break; case PG_DIAG_INTERNAL_QUERY: edata->internalquery = pstrdup(value); break; case PG_DIAG_CONTEXT: edata->context = pstrdup(value); break; case PG_DIAG_SCHEMA_NAME: edata->schema_name = pstrdup(value); break; case PG_DIAG_TABLE_NAME: edata->table_name = pstrdup(value); break; case PG_DIAG_COLUMN_NAME: edata->column_name = pstrdup(value); break; case PG_DIAG_DATATYPE_NAME: edata->datatype_name = pstrdup(value); break; case PG_DIAG_CONSTRAINT_NAME: edata->constraint_name = pstrdup(value); break; case PG_DIAG_SOURCE_FILE: edata->filename = pstrdup(value); break; case PG_DIAG_SOURCE_LINE: edata->lineno = pg_strtoint32(value); break; case PG_DIAG_SOURCE_FUNCTION: edata->funcname = pstrdup(value); break; default: elog(ERROR, "unrecognized error field code: %d", (int) code); break; } } }