/* * PQfn - Send a function call to the POSTGRES backend. * * See fe-exec.c for documentation. */ PGresult * pqFunctionCall2(PGconn *conn, Oid fnid, int *result_buf, int *actual_result_len, int result_is_int, const PQArgBlock *args, int nargs) { bool needInput = false; ExecStatusType status = PGRES_FATAL_ERROR; char id; int i; /* PQfn already validated connection state */ if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */ pqPuts(" ", conn) < 0 || /* dummy string */ pqPutInt(fnid, 4, conn) != 0 || /* function id */ pqPutInt(nargs, 4, conn) != 0) /* # of args */ { pqHandleSendFailure(conn); return NULL; } for (i = 0; i < nargs; ++i) { /* len.int4 + contents */ if (pqPutInt(args[i].len, 4, conn)) { pqHandleSendFailure(conn); return NULL; } if (args[i].isint) { if (pqPutInt(args[i].u.integer, 4, conn)) { pqHandleSendFailure(conn); return NULL; } } else { if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn)) { pqHandleSendFailure(conn); return NULL; } } } if (pqPutMsgEnd(conn) < 0 || pqFlush(conn)) { pqHandleSendFailure(conn); return NULL; } for (;;) { if (needInput) { /* Wait for some data to arrive (or for the channel to close) */ if (pqWait(TRUE, FALSE, conn) || pqReadData(conn) < 0) break; } /* * Scan the message. If we run out of data, loop around to try again. */ conn->inCursor = conn->inStart; needInput = true; if (pqGetc(&id, conn)) continue; /* * We should see V or E response to the command, but might get N * and/or A notices first. We also need to swallow the final Z before * returning. */ switch (id) { case 'V': /* function result */ if (pqGetc(&id, conn)) continue; if (id == 'G') { /* function returned nonempty value */ if (pqGetInt(actual_result_len, 4, conn)) continue; if (result_is_int) { if (pqGetInt(result_buf, 4, conn)) continue; } else { if (pqGetnchar((char *) result_buf, *actual_result_len, conn)) continue; } if (pqGetc(&id, conn)) /* get the last '0' */ continue; } if (id == '0') { /* correctly finished function result message */ status = PGRES_COMMAND_OK; } else { /* The backend violates the protocol. */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("protocol error: id=0x%x\n"), id); pqSaveErrorResult(conn); conn->inStart = conn->inCursor; return pqPrepareAsyncResult(conn); } break; case 'E': /* error return */ if (pqGetErrorNotice2(conn, true)) continue; status = PGRES_FATAL_ERROR; break; case 'A': /* notify message */ /* handle notify and go back to processing return values */ if (getNotify(conn)) continue; break; case 'N': /* notice */ /* handle notice and go back to processing return values */ if (pqGetErrorNotice2(conn, false)) continue; break; case 'Z': /* backend is ready for new query */ /* consume the message and exit */ conn->inStart = conn->inCursor; /* if we saved a result object (probably an error), use it */ if (conn->result) return pqPrepareAsyncResult(conn); return PQmakeEmptyPGresult(conn, status); default: /* The backend violates the protocol. */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("protocol error: id=0x%x\n"), id); pqSaveErrorResult(conn); conn->inStart = conn->inCursor; return pqPrepareAsyncResult(conn); } /* Completed this message, keep going */ conn->inStart = conn->inCursor; needInput = false; } /* * We fall out of the loop only upon failing to read data. * conn->errorMessage has been set by pqWait or pqReadData. We want to * append it to any already-received error message. */ pqSaveErrorResult(conn); return pqPrepareAsyncResult(conn); }
/* * parseInput subroutine to read a 'B' or 'D' (row data) message. * We fill rowbuf with column pointers and then call the row processor. * Returns: 0 if completed message, EOF if error or not enough data * received yet. * * Note that if we run out of data, we have to suspend and reprocess * the message after more data is received. Otherwise, conn->inStart * must get advanced past the processed data. */ static int getAnotherTuple(PGconn *conn, bool binary) { PGresult *result = conn->result; int nfields = result->numAttributes; const char *errmsg; PGdataValue *rowbuf; /* the backend sends us a bitmap of which attributes are null */ char std_bitmap[64]; /* used unless it doesn't fit */ char *bitmap = std_bitmap; int i; size_t nbytes; /* the number of bytes in bitmap */ char bmap; /* One byte of the bitmap */ int bitmap_index; /* Its index */ int bitcnt; /* number of bits examined in current byte */ int vlen; /* length of the current field value */ /* Resize row buffer if needed */ rowbuf = conn->rowBuf; if (nfields > conn->rowBufLen) { rowbuf = (PGdataValue *) realloc(rowbuf, nfields * sizeof(PGdataValue)); if (!rowbuf) { errmsg = NULL; /* means "out of memory", see below */ goto advance_and_error; } conn->rowBuf = rowbuf; conn->rowBufLen = nfields; } /* Save format specifier */ result->binary = binary; /* * If it's binary, fix the column format indicators. We assume the * backend will consistently send either B or D, not a mix. */ if (binary) { for (i = 0; i < nfields; i++) result->attDescs[i].format = 1; } /* Get the null-value bitmap */ nbytes = (nfields + BITS_PER_BYTE - 1) / BITS_PER_BYTE; /* malloc() only for unusually large field counts... */ if (nbytes > sizeof(std_bitmap)) { bitmap = (char *) malloc(nbytes); if (!bitmap) { errmsg = NULL; /* means "out of memory", see below */ goto advance_and_error; } } if (pqGetnchar(bitmap, nbytes, conn)) goto EOFexit; /* Scan the fields */ bitmap_index = 0; bmap = bitmap[bitmap_index]; bitcnt = 0; for (i = 0; i < nfields; i++) { /* get the value length */ if (!(bmap & 0200)) vlen = NULL_LEN; else if (pqGetInt(&vlen, 4, conn)) goto EOFexit; else { if (!binary) vlen = vlen - 4; if (vlen < 0) vlen = 0; } rowbuf[i].len = vlen; /* * rowbuf[i].value always points to the next address in the data * buffer even if the value is NULL. This allows row processors to * estimate data sizes more easily. */ rowbuf[i].value = conn->inBuffer + conn->inCursor; /* Skip over the data value */ if (vlen > 0) { if (pqSkipnchar(vlen, conn)) goto EOFexit; } /* advance the bitmap stuff */ bitcnt++; if (bitcnt == BITS_PER_BYTE) { bitmap_index++; bmap = bitmap[bitmap_index]; bitcnt = 0; } else bmap <<= 1; } /* Release bitmap now if we allocated it */ if (bitmap != std_bitmap) free(bitmap); bitmap = NULL; /* Advance inStart to show that the "D" message has been processed. */ conn->inStart = conn->inCursor; /* Process the collected row */ errmsg = NULL; if (pqRowProcessor(conn, &errmsg)) return 0; /* normal, successful exit */ goto set_error_result; /* pqRowProcessor failed, report it */ advance_and_error: /* * Discard the failed message. Unfortunately we don't know for sure where * the end is, so just throw away everything in the input buffer. This is * not very desirable but it's the best we can do in protocol v2. */ conn->inStart = conn->inEnd; set_error_result: /* * Replace partially constructed result with an error result. First * discard the old result to try to win back some memory. */ pqClearAsyncResult(conn); /* * If preceding code didn't provide an error message, assume "out of * memory" was meant. The advantage of having this special case is that * freeing the old result first greatly improves the odds that gettext() * will succeed in providing a translation. */ if (!errmsg) errmsg = libpq_gettext("out of memory for query result"); printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); /* * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can * do to recover... */ conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); conn->asyncStatus = PGASYNC_READY; EOFexit: if (bitmap != NULL && bitmap != std_bitmap) free(bitmap); return EOF; }
/* * parseInput subroutine to read a 'D' (row data) message. * We add another tuple to the existing PGresult structure. * Returns: 0 if completed message, EOF if error or not enough data yet. * * Note that if we run out of data, we have to suspend and reprocess * the message after more data is received. We keep a partially constructed * tuple in conn->curTuple, and avoid reallocating already-allocated storage. */ static int getAnotherTuple(PGconn *conn, int msgLength) { PGresult *result = conn->result; int nfields = result->numAttributes; PGresAttValue *tup; int tupnfields; /* # fields from tuple */ int vlen; /* length of the current field value */ int i; /* Allocate tuple space if first time for this data message */ if (conn->curTuple == NULL) { conn->curTuple = (PGresAttValue *) pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE); if (conn->curTuple == NULL) goto outOfMemory; MemSet(conn->curTuple, 0, nfields * sizeof(PGresAttValue)); } tup = conn->curTuple; /* Get the field count and make sure it's what we expect */ if (pqGetInt(&tupnfields, 2, conn)) return EOF; if (tupnfields != nfields) { /* Replace partially constructed result with an error result */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("unexpected field count in \"D\" message\n")); pqSaveErrorResult(conn); /* Discard the failed message by pretending we read it */ conn->inCursor = conn->inStart + 5 + msgLength; return 0; } /* Scan the fields */ for (i = 0; i < nfields; i++) { /* get the value length */ if (pqGetInt(&vlen, 4, conn)) return EOF; if (vlen == -1) { /* null field */ tup[i].value = result->null_field; tup[i].len = NULL_LEN; continue; } if (vlen < 0) vlen = 0; if (tup[i].value == NULL) { bool isbinary = (result->attDescs[i].format != 0); tup[i].value = (char *) pqResultAlloc(result, vlen + 1, isbinary); if (tup[i].value == NULL) goto outOfMemory; } tup[i].len = vlen; /* read in the value */ if (vlen > 0) if (pqGetnchar((char *) (tup[i].value), vlen, conn)) return EOF; /* we have to terminate this ourselves */ tup[i].value[vlen] = '\0'; } /* Success! Store the completed tuple in the result */ if (!pqAddTuple(result, tup)) goto outOfMemory; /* and reset for a new message */ conn->curTuple = NULL; return 0; outOfMemory: /* * Replace partially constructed result with an error result. First * discard the old result to try to win back some memory. */ pqClearAsyncResult(conn); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory for query result\n")); pqSaveErrorResult(conn); /* Discard the failed message by pretending we read it */ conn->inCursor = conn->inStart + 5 + msgLength; return 0; }
/* * PQfn - Send a function call to the POSTGRES backend. * * See fe-exec.c for documentation. */ PGresult * pqFunctionCall3(PGconn *conn, Oid fnid, int *result_buf, int *actual_result_len, int result_is_int, const PQArgBlock *args, int nargs) { bool needInput = false; ExecStatusType status = PGRES_FATAL_ERROR; char id; int msgLength; int avail; int i; /* PQfn already validated connection state */ if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */ pqPutInt(fnid, 4, conn) < 0 || /* function id */ pqPutInt(1, 2, conn) < 0 || /* # of format codes */ pqPutInt(1, 2, conn) < 0 || /* format code: BINARY */ pqPutInt(nargs, 2, conn) < 0) /* # of args */ { pqHandleSendFailure(conn); return NULL; } for (i = 0; i < nargs; ++i) { /* len.int4 + contents */ if (pqPutInt(args[i].len, 4, conn)) { pqHandleSendFailure(conn); return NULL; } if (args[i].len == -1) continue; /* it's NULL */ if (args[i].isint) { if (pqPutInt(args[i].u.integer, args[i].len, conn)) { pqHandleSendFailure(conn); return NULL; } } else { if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn)) { pqHandleSendFailure(conn); return NULL; } } } if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */ { pqHandleSendFailure(conn); return NULL; } if (pqPutMsgEnd(conn) < 0 || pqFlush(conn)) { pqHandleSendFailure(conn); return NULL; } for (;;) { if (needInput) { /* Wait for some data to arrive (or for the channel to close) */ if (pqWait(TRUE, FALSE, conn) || pqReadData(conn) < 0) break; } /* * Scan the message. If we run out of data, loop around to try again. */ needInput = true; conn->inCursor = conn->inStart; if (pqGetc(&id, conn)) continue; if (pqGetInt(&msgLength, 4, conn)) continue; /* * Try to validate message type/length here. A length less than 4 is * definitely broken. Large lengths should only be believed for a few * message types. */ if (msgLength < 4) { handleSyncLoss(conn, id, msgLength); break; } if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id)) { handleSyncLoss(conn, id, msgLength); break; } /* * Can't process if message body isn't all here yet. */ msgLength -= 4; avail = conn->inEnd - conn->inCursor; if (avail < msgLength) { /* * Before looping, enlarge the input buffer if needed to hold the * whole message. See notes in parseInput. */ if (pqCheckInBufferSpace(conn->inCursor + msgLength, conn)) { /* * XXX add some better recovery code... plan is to skip over * the message using its length, then report an error. For the * moment, just treat this like loss of sync (which indeed it * might be!) */ handleSyncLoss(conn, id, msgLength); break; } continue; } /* * We should see V or E response to the command, but might get N * and/or A notices first. We also need to swallow the final Z before * returning. */ switch (id) { case 'V': /* function result */ if (pqGetInt(actual_result_len, 4, conn)) continue; if (*actual_result_len != -1) { if (result_is_int) { if (pqGetInt(result_buf, *actual_result_len, conn)) continue; } else { if (pqGetnchar((char *) result_buf, *actual_result_len, conn)) continue; } } /* correctly finished function result message */ status = PGRES_COMMAND_OK; break; case 'E': /* error return */ if (pqGetErrorNotice3(conn, true)) continue; status = PGRES_FATAL_ERROR; break; case 'A': /* notify message */ /* handle notify and go back to processing return values */ if (getNotify(conn)) continue; break; case 'N': /* notice */ /* handle notice and go back to processing return values */ if (pqGetErrorNotice3(conn, false)) continue; break; case 'Z': /* backend is ready for new query */ if (getReadyForQuery(conn)) continue; /* consume the message and exit */ conn->inStart += 5 + msgLength; /* if we saved a result object (probably an error), use it */ if (conn->result) return pqPrepareAsyncResult(conn); return PQmakeEmptyPGresult(conn, status); case 'S': /* parameter status */ if (getParameterStatus(conn)) continue; break; default: /* The backend violates the protocol. */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("protocol error: id=0x%x\n"), id); pqSaveErrorResult(conn); /* trust the specified message length as what to skip */ conn->inStart += 5 + msgLength; return pqPrepareAsyncResult(conn); } /* Completed this message, keep going */ /* trust the specified message length as what to skip */ conn->inStart += 5 + msgLength; needInput = false; } /* * We fall out of the loop only upon failing to read data. * conn->errorMessage has been set by pqWait or pqReadData. We want to * append it to any already-received error message. */ pqSaveErrorResult(conn); return pqPrepareAsyncResult(conn); }
/* * parseInput subroutine to read a 'B' or 'D' (row data) message. * We add another tuple to the existing PGresult structure. * Returns: 0 if completed message, EOF if error or not enough data yet. * * Note that if we run out of data, we have to suspend and reprocess * the message after more data is received. We keep a partially constructed * tuple in conn->curTuple, and avoid reallocating already-allocated storage. */ static int getAnotherTuple(PGconn *conn, bool binary) { PGresult *result = conn->result; int nfields = result->numAttributes; PGresAttValue *tup; /* the backend sends us a bitmap of which attributes are null */ char std_bitmap[64]; /* used unless it doesn't fit */ char *bitmap = std_bitmap; int i; size_t nbytes; /* the number of bytes in bitmap */ char bmap; /* One byte of the bitmap */ int bitmap_index; /* Its index */ int bitcnt; /* number of bits examined in current byte */ int vlen; /* length of the current field value */ result->binary = binary; /* Allocate tuple space if first time for this data message */ if (conn->curTuple == NULL) { conn->curTuple = (PGresAttValue *) pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE); if (conn->curTuple == NULL) goto outOfMemory; MemSet(conn->curTuple, 0, nfields * sizeof(PGresAttValue)); /* * If it's binary, fix the column format indicators. We assume the * backend will consistently send either B or D, not a mix. */ if (binary) { for (i = 0; i < nfields; i++) result->attDescs[i].format = 1; } } tup = conn->curTuple; /* Get the null-value bitmap */ nbytes = (nfields + BITS_PER_BYTE - 1) / BITS_PER_BYTE; /* malloc() only for unusually large field counts... */ if (nbytes > sizeof(std_bitmap)) { bitmap = (char *) malloc(nbytes); if (!bitmap) goto outOfMemory; } if (pqGetnchar(bitmap, nbytes, conn)) goto EOFexit; /* Scan the fields */ bitmap_index = 0; bmap = bitmap[bitmap_index]; bitcnt = 0; for (i = 0; i < nfields; i++) { if (!(bmap & 0200)) { /* if the field value is absent, make it a null string */ tup[i].value = result->null_field; tup[i].len = NULL_LEN; } else { /* get the value length (the first four bytes are for length) */ if (pqGetInt(&vlen, 4, conn)) goto EOFexit; if (!binary) vlen = vlen - 4; if (vlen < 0) vlen = 0; if (tup[i].value == NULL) { tup[i].value = (char *) pqResultAlloc(result, vlen + 1, binary); if (tup[i].value == NULL) goto outOfMemory; } tup[i].len = vlen; /* read in the value */ if (vlen > 0) if (pqGetnchar((char *) (tup[i].value), vlen, conn)) goto EOFexit; /* we have to terminate this ourselves */ tup[i].value[vlen] = '\0'; } /* advance the bitmap stuff */ bitcnt++; if (bitcnt == BITS_PER_BYTE) { bitmap_index++; bmap = bitmap[bitmap_index]; bitcnt = 0; } else bmap <<= 1; } /* Success! Store the completed tuple in the result */ if (!pqAddTuple(result, tup)) goto outOfMemory; /* and reset for a new message */ conn->curTuple = NULL; if (bitmap != std_bitmap) free(bitmap); return 0; outOfMemory: /* Replace partially constructed result with an error result */ /* * we do NOT use pqSaveErrorResult() here, because of the likelihood that * there's not enough memory to concatenate messages... */ pqClearAsyncResult(conn); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory for query result\n")); /* * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can * do to recover... */ conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); conn->asyncStatus = PGASYNC_READY; /* Discard the failed message --- good idea? */ conn->inStart = conn->inEnd; EOFexit: if (bitmap != NULL && bitmap != std_bitmap) free(bitmap); return EOF; }
/* * Continue SSPI authentication with next token as needed. */ static int pg_SSPI_continue(PGconn *conn, int payloadlen) { SECURITY_STATUS r; CtxtHandle newContext; ULONG contextAttr; SecBufferDesc inbuf; SecBufferDesc outbuf; SecBuffer OutBuffers[1]; SecBuffer InBuffers[1]; char *inputbuf = NULL; if (conn->sspictx != NULL) { /* * On runs other than the first we have some data to send. Put this * data in a SecBuffer type structure. */ inputbuf = malloc(payloadlen); if (!inputbuf) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory allocating SSPI buffer (%d)\n"), payloadlen); return STATUS_ERROR; } if (pqGetnchar(inputbuf, payloadlen, conn)) { /* * Shouldn't happen, because the caller should've ensured that the * whole message is already in the input buffer. */ free(inputbuf); return STATUS_ERROR; } inbuf.ulVersion = SECBUFFER_VERSION; inbuf.cBuffers = 1; inbuf.pBuffers = InBuffers; InBuffers[0].pvBuffer = inputbuf; InBuffers[0].cbBuffer = payloadlen; InBuffers[0].BufferType = SECBUFFER_TOKEN; } OutBuffers[0].pvBuffer = NULL; OutBuffers[0].BufferType = SECBUFFER_TOKEN; OutBuffers[0].cbBuffer = 0; outbuf.cBuffers = 1; outbuf.pBuffers = OutBuffers; outbuf.ulVersion = SECBUFFER_VERSION; r = InitializeSecurityContext(conn->sspicred, conn->sspictx, conn->sspitarget, ISC_REQ_ALLOCATE_MEMORY, 0, SECURITY_NETWORK_DREP, (conn->sspictx == NULL) ? NULL : &inbuf, 0, &newContext, &outbuf, &contextAttr, NULL); /* we don't need the input anymore */ if (inputbuf) free(inputbuf); if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED) { pg_SSPI_error(conn, libpq_gettext("SSPI continuation error"), r); return STATUS_ERROR; } if (conn->sspictx == NULL) { /* On first run, transfer retrieved context handle */ conn->sspictx = malloc(sizeof(CtxtHandle)); if (conn->sspictx == NULL) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return STATUS_ERROR; } memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle)); } /* * If SSPI returned any data to be sent to the server (as it normally * would), send this data as a password packet. */ if (outbuf.cBuffers > 0) { if (outbuf.cBuffers != 1) { /* * This should never happen, at least not for Kerberos * authentication. Keep check in case it shows up with other * authentication methods later. */ printfPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n"); return STATUS_ERROR; } /* * If the negotiation is complete, there may be zero bytes to send. * The server is at this point not expecting any more data, so don't * send it. */ if (outbuf.pBuffers[0].cbBuffer > 0) { if (pqPacketSend(conn, 'p', outbuf.pBuffers[0].pvBuffer, outbuf.pBuffers[0].cbBuffer)) { FreeContextBuffer(outbuf.pBuffers[0].pvBuffer); return STATUS_ERROR; } } FreeContextBuffer(outbuf.pBuffers[0].pvBuffer); } /* Cleanup is handled by the code in freePGconn() */ return STATUS_OK; }
/* * Continue GSS authentication with next token as needed. */ static int pg_GSS_continue(PGconn *conn, int payloadlen) { OM_uint32 maj_stat, min_stat, lmin_s; gss_buffer_desc ginbuf; gss_buffer_desc goutbuf; /* * On first call, there's no input token. On subsequent calls, read the * input token into a GSS buffer. */ if (conn->gctx != GSS_C_NO_CONTEXT) { ginbuf.length = payloadlen; ginbuf.value = malloc(payloadlen); if (!ginbuf.value) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory allocating GSSAPI buffer (%d)\n"), payloadlen); return STATUS_ERROR; } if (pqGetnchar(ginbuf.value, payloadlen, conn)) { /* * Shouldn't happen, because the caller should've ensured that the * whole message is already in the input buffer. */ free(ginbuf.value); return STATUS_ERROR; } } else { ginbuf.length = 0; ginbuf.value = NULL; } maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &conn->gctx, conn->gtarg_nam, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, (ginbuf.value == NULL) ? GSS_C_NO_BUFFER : &ginbuf, NULL, &goutbuf, NULL, NULL); if (ginbuf.value) free(ginbuf.value); if (goutbuf.length != 0) { /* * GSS generated data to send to the server. We don't care if it's the * first or subsequent packet, just send the same kind of password * packet. */ if (pqPacketSend(conn, 'p', goutbuf.value, goutbuf.length) != STATUS_OK) { gss_release_buffer(&lmin_s, &goutbuf); return STATUS_ERROR; } } gss_release_buffer(&lmin_s, &goutbuf); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { pg_GSS_error(libpq_gettext("GSSAPI continuation error"), conn, maj_stat, min_stat); gss_release_name(&lmin_s, &conn->gtarg_nam); if (conn->gctx) gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER); return STATUS_ERROR; } if (maj_stat == GSS_S_COMPLETE) gss_release_name(&lmin_s, &conn->gtarg_nam); return STATUS_OK; }