📄 fe-connect.c
字号:
* * A copy is needed to be able to cancel a running query from a different * thread. If the same structure is used all structure members would have * to be individually locked (if the entire structure was locked, it would * be impossible to cancel a synchronous query becuase the structure would * have to stay locked for the duration of the query). */PGcancel *PQgetCancel(PGconn *conn){ PGcancel *cancel; if (!conn) return NULL; if (conn->sock < 0) return NULL; cancel = malloc(sizeof(PGcancel)); if (cancel == NULL) return NULL; memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr)); cancel->be_pid = conn->be_pid; cancel->be_key = conn->be_key; return cancel;}/* PQfreeCancel: free a cancel structure */voidPQfreeCancel(PGcancel *cancel){ if (cancel) free(cancel);}/* * PQcancel and PQrequestCancel: attempt to request cancellation of the * current operation. * * The return value is TRUE if the cancel request was successfully * dispatched, FALSE if not (in which case an error message is available). * Note: successful dispatch is no guarantee that there will be any effect at * the backend. The application must read the operation result as usual. * * CAUTION: we want this routine to be safely callable from a signal handler * (for example, an application might want to call it in a SIGINT handler). * This means we cannot use any C library routine that might be non-reentrant. * malloc/free are often non-reentrant, and anything that might call them is * just as dangerous. We avoid sprintf here for that reason. Building up * error messages with strcpy/strcat is tedious but should be quite safe. * We also save/restore errno in case the signal handler support doesn't. * * internal_cancel() is an internal helper function to make code-sharing * between the two versions of the cancel function possible. */static intinternal_cancel(SockAddr *raddr, int be_pid, int be_key, char *errbuf, int errbufsize){ int save_errno = SOCK_ERRNO; int tmpsock = -1; char sebuf[256]; int maxlen; struct { uint32 packetlen; CancelRequestPacket cp; } crp; /* * We need to open a temporary connection to the postmaster. Do this with * only kernel calls. */ if ((tmpsock = socket(raddr->addr.ss_family, SOCK_STREAM, 0)) < 0) { StrNCpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize); goto cancel_errReturn; }retry3: if (connect(tmpsock, (struct sockaddr *) & raddr->addr, raddr->salen) < 0) { if (SOCK_ERRNO == EINTR) /* Interrupted system call - we'll just try again */ goto retry3; StrNCpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize); goto cancel_errReturn; } /* * We needn't set nonblocking I/O or NODELAY options here. */ /* Create and send the cancel request packet. */ crp.packetlen = htonl((uint32) sizeof(crp)); crp.cp.cancelRequestCode = (MsgType) htonl(CANCEL_REQUEST_CODE); crp.cp.backendPID = htonl(be_pid); crp.cp.cancelAuthCode = htonl(be_key);retry4: if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp)) { if (SOCK_ERRNO == EINTR) /* Interrupted system call - we'll just try again */ goto retry4; StrNCpy(errbuf, "PQcancel() -- send() failed: ", errbufsize); goto cancel_errReturn; } /* * Wait for the postmaster to close the connection, which indicates that * it's processed the request. Without this delay, we might issue another * command only to find that our cancel zaps that command instead of the * one we thought we were canceling. Note we don't actually expect this * read to obtain any data, we are just waiting for EOF to be signaled. */retry5: if (recv(tmpsock, (char *) &crp, 1, 0) < 0) { if (SOCK_ERRNO == EINTR) /* Interrupted system call - we'll just try again */ goto retry5; /* we ignore other error conditions */ } /* All done */ closesocket(tmpsock); SOCK_ERRNO_SET(save_errno); return TRUE;cancel_errReturn: /* * Make sure we don't overflow the error buffer. Leave space for the \n at * the end, and for the terminating zero. */ maxlen = errbufsize - strlen(errbuf) - 2; if (maxlen >= 0) { strncat(errbuf, SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)), maxlen); strcat(errbuf, "\n"); } if (tmpsock >= 0) closesocket(tmpsock); SOCK_ERRNO_SET(save_errno); return FALSE;}/* * PQcancel: request query cancel * * Returns TRUE if able to send the cancel request, FALSE if not. * * On failure, an error message is stored in *errbuf, which must be of size * errbufsize (recommended size is 256 bytes). *errbuf is not changed on * success return. */intPQcancel(PGcancel *cancel, char *errbuf, int errbufsize){ if (!cancel) { StrNCpy(errbuf, "PQcancel() -- no cancel object supplied", errbufsize); return FALSE; } return internal_cancel(&cancel->raddr, cancel->be_pid, cancel->be_key, errbuf, errbufsize);}/* * PQrequestCancel: old, not thread-safe function for requesting query cancel * * Returns TRUE if able to send the cancel request, FALSE if not. * * On failure, the error message is saved in conn->errorMessage; this means * that this can't be used when there might be other active operations on * the connection object. * * NOTE: error messages will be cut off at the current size of the * error message buffer, since we dare not try to expand conn->errorMessage! */intPQrequestCancel(PGconn *conn){ int r; /* Check we have an open connection */ if (!conn) return FALSE; if (conn->sock < 0) { StrNCpy(conn->errorMessage.data, "PQrequestCancel() -- connection is not open\n", conn->errorMessage.maxlen); conn->errorMessage.len = strlen(conn->errorMessage.data); return FALSE; } r = internal_cancel(&conn->raddr, conn->be_pid, conn->be_key, conn->errorMessage.data, conn->errorMessage.maxlen); if (!r) conn->errorMessage.len = strlen(conn->errorMessage.data); return r;}/* * pqPacketSend() -- convenience routine to send a message to server. * * pack_type: the single-byte message type code. (Pass zero for startup * packets, which have no message type code.) * * buf, buf_len: contents of message. The given length includes only what * is in buf; the message type and message length fields are added here. * * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise. * SIDE_EFFECTS: may block. * * Note: all messages sent with this routine have a length word, whether * it's protocol 2.0 or 3.0. */intpqPacketSend(PGconn *conn, char pack_type, const void *buf, size_t buf_len){ /* Start the message. */ if (pqPutMsgStart(pack_type, true, conn)) return STATUS_ERROR; /* Send the message body. */ if (pqPutnchar(buf, buf_len, conn)) return STATUS_ERROR; /* Finish the message. */ if (pqPutMsgEnd(conn)) return STATUS_ERROR; /* Flush to ensure backend gets it. */ if (pqFlush(conn)) return STATUS_ERROR; return STATUS_OK;}#define MAXBUFSIZE 256static intparseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage){ char *service = conninfo_getval(options, "service"); char serviceFile[MAXPGPATH]; bool group_found = false; int linenr = 0, i; /* * We have to special-case the environment variable PGSERVICE here, since * this is and should be called before inserting environment defaults for * other connection options. */ if (service == NULL) service = getenv("PGSERVICE"); /* * This could be used by any application so we can't use the binary * location to find our config files. */ snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf", getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR); if (service != NULL) { FILE *f; char buf[MAXBUFSIZE], *line; f = fopen(serviceFile, "r"); if (f == NULL) { printfPQExpBuffer(errorMessage, libpq_gettext("ERROR: service file \"%s\" not found\n"), serviceFile); return 1; } while ((line = fgets(buf, MAXBUFSIZE - 1, f)) != NULL) { linenr++; if (strlen(line) >= MAXBUFSIZE - 2) { fclose(f); printfPQExpBuffer(errorMessage, libpq_gettext("ERROR: line %d too long in service file \"%s\"\n"), linenr, serviceFile); return 2; } /* ignore EOL at end of line */ if (strlen(line) && line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = 0; /* ignore leading blanks */ while (*line && isspace((unsigned char) line[0])) line++; /* ignore comments and empty lines */ if (strlen(line) == 0 || line[0] == '#') continue; /* Check for right groupname */ if (line[0] == '[') { if (group_found) { /* group info already read */ fclose(f); return 0; } if (strncmp(line + 1, service, strlen(service)) == 0 && line[strlen(service) + 1] == ']') group_found = true; else group_found = false; } else { if (group_found) { /* * Finally, we are in the right group and can parse the * line */ char *key, *val; bool found_keyword; key = line; val = strchr(line, '='); if (val == NULL) { printfPQExpBuffer(errorMessage, libpq_gettext("ERROR: syntax error in service file \"%s\", line %d\n"), serviceFile, linenr); fclose(f); return 3; } *val++ = '\0'; /* * Set the parameter --- but don't override any previous * explicit setting. */ found_keyword = false; for (i = 0; options[i].keyword; i++) { if (strcmp(options[i].keyword, key) == 0) { if (options[i].val == NULL) options[i].val = strdup(val); found_keyword = true; break; } } if (!found_keyword) { printfPQExpBuffer(errorMessage, libpq_gettext("ERROR: syntax error in service file \"%s\", line %d\n"), serviceFile, linenr); fclose(f); return 3; } } } } fclose(f); } return 0;}/* * Conninfo parser routine * * If successful, a malloc'd PQconninfoOption array is returned. * If not successful, NULL is returned and an error message is * left in errorMessage. */static PQconninfoOption *conninfo_parse(const char *conninfo, PQExpBuffer errorMessage){ char *pname; char *pval; char *buf; char *tmp; char *cp; char *cp2; PQconninfoOption *options; PQconninfoOption *option; char errortmp[PQERRORMSG_LENGTH]; /* Make a working copy of PQconninfoOptions */ options = malloc(sizeof(PQconninfoOptions)); if (options == NULL) { printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); return NULL; } memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions)); /* Need a modifiable copy of the input string */ if ((buf = strdup(conninfo)) == NULL) { printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); PQconninfoFree(options); return NULL; } cp = buf; while (*cp) { /* Skip blanks before the parameter name */ if (isspace((unsigned char) *cp)) { cp++; continue; } /* Get the parameter name */ pname = cp; while (*cp) { if (*cp == '=') break; if (isspace((unsigned char) *cp)) { *cp++ = '\0'; while (*cp) { if (!isspace((unsigned char) *cp)) break; cp++; } break; } cp++; } /* Check that there is a following '=' */ if (*cp != '=') { printfPQExpBuffer(errorMessage, libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"), pname); PQconninfoFree(options); free(buf); return NULL; } *cp++ = '\0'; /* Skip blanks after the '=' */ while (*cp) { if (!isspace((unsigned char) *cp)) break; cp++; } /* Get the parameter value */ pval = cp; if (*cp != '\'') { cp2 = pval; while (*cp) { if (isspace((unsigned char) *cp)) { *cp++ = '\0'; break; } if (*cp == '\\') { cp++; if (*cp != '\0') *cp2++ = *cp++; } else *cp2++ = *cp++; } *cp2 = '\0'; } else { cp2 = pval; cp++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -