📄 fe-connect.c
字号:
SOCKET_SIZE_TYPE laddrlen; int portno, family; char beresp; int on = 1; /* * parse dbName to get all additional info in it, if any */ if (update_db_info(conn) != 0) goto connect_errReturn; /* * Initialize the startup packet. */ MemSet((char *) &sp, 0, sizeof(StartupPacket)); sp.protoVersion = (ProtocolVersion) htonl(PG_PROTOCOL_LIBPQ); strncpy(sp.user, conn->pguser, SM_USER); strncpy(sp.database, conn->dbName, SM_DATABASE); strncpy(sp.tty, conn->pgtty, SM_TTY); if (conn->pgoptions) strncpy(sp.options, conn->pgoptions, SM_OPTIONS); /* * Open a connection to postmaster/backend. */ if (conn->pghost != NULL) { hp = gethostbyname(conn->pghost); if ((hp == NULL) || (hp->h_addrtype != AF_INET)) { (void) sprintf(conn->errorMessage, "connectDB() -- unknown hostname: %s\n", conn->pghost); goto connect_errReturn; } family = AF_INET; } else { hp = NULL; family = AF_UNIX; } MemSet((char *) &conn->raddr, 0, sizeof(conn->raddr)); conn->raddr.sa.sa_family = family; portno = atoi(conn->pgport); if (family == AF_INET) { memmove((char *) &(conn->raddr.in.sin_addr), (char *) hp->h_addr, hp->h_length); conn->raddr.in.sin_port = htons((unsigned short) (portno)); conn->raddr_len = sizeof(struct sockaddr_in); }#if !defined(WIN32) && !defined(__CYGWIN32__) else conn->raddr_len = UNIXSOCK_PATH(conn->raddr.un, portno);#endif /* Connect to the server */ if ((conn->sock = socket(family, SOCK_STREAM, 0)) < 0) { (void) sprintf(conn->errorMessage, "connectDB() -- socket() failed: errno=%d\n%s\n", errno, strerror(errno)); goto connect_errReturn; } if (connect(conn->sock, &conn->raddr.sa, conn->raddr_len) < 0) { (void) sprintf(conn->errorMessage, "connectDB() -- connect() failed: %s\n" "Is the postmaster running%s at '%s' and accepting connections on %s '%s'?\n", strerror(errno), (family == AF_INET) ? " (with -i)" : "", conn->pghost ? conn->pghost : "localhost", (family == AF_INET) ? "TCP/IP port" : "Unix socket", conn->pgport); goto connect_errReturn; } /* * Set the right options. We need nonblocking I/O, and we don't want * delay of outgoing data. */#ifndef WIN32 if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) < 0)#else if (ioctlsocket(conn->sock, FIONBIO, &on) != 0)#endif { (void) sprintf(conn->errorMessage, "connectDB() -- fcntl() failed: errno=%d\n%s\n", errno, strerror(errno)); goto connect_errReturn; } if (family == AF_INET) { struct protoent *pe; pe = getprotobyname("TCP"); if (pe == NULL) { (void) sprintf(conn->errorMessage, "connectDB(): getprotobyname failed\n"); goto connect_errReturn; } if (setsockopt(conn->sock, pe->p_proto, TCP_NODELAY,#ifdef WIN32 (char *)#endif &on, sizeof(on)) < 0) { (void) sprintf(conn->errorMessage, "connectDB() -- setsockopt failed: errno=%d\n%s\n", errno, strerror(errno));#ifdef WIN32 printf("Winsock error: %i\n", WSAGetLastError());#endif goto connect_errReturn; } } /* Fill in the client address */ laddrlen = sizeof(conn->laddr); if (getsockname(conn->sock, &conn->laddr.sa, &laddrlen) < 0) { (void) sprintf(conn->errorMessage, "connectDB() -- getsockname() failed: errno=%d\n%s\n", errno, strerror(errno)); goto connect_errReturn; } /* Ensure our buffers are empty */ conn->inStart = conn->inCursor = conn->inEnd = 0; conn->outCount = 0; /* Send the startup packet. */ if (pqPacketSend(conn, (char *) &sp, sizeof(StartupPacket)) != STATUS_OK) { sprintf(conn->errorMessage, "connectDB() -- couldn't send startup packet: errno=%d\n%s\n", errno, strerror(errno)); goto connect_errReturn; } /* * Perform the authentication exchange: wait for backend messages and * respond as necessary. We fall out of this loop when done talking to * the postmaster. */ for (;;) { /* Wait for some data to arrive (or for the channel to close) */ if (pqWait(TRUE, FALSE, conn)) goto connect_errReturn; /* Load data, or detect EOF */ if (pqReadData(conn) < 0) goto connect_errReturn; /* * Scan the message. If we run out of data, loop around to try * again. */ conn->inCursor = conn->inStart; if (pqGetc(&beresp, conn)) continue; /* no data yet */ /* Handle errors. */ if (beresp == 'E') { if (pqGets(conn->errorMessage, sizeof(conn->errorMessage), conn)) continue; goto connect_errReturn; } /* Otherwise it should be an authentication request. */ if (beresp != 'R') { (void) sprintf(conn->errorMessage, "connectDB() -- expected authentication request\n"); goto connect_errReturn; } /* Get the type of request. */ if (pqGetInt((int *) &areq, 4, conn)) continue; /* Get the password salt if there is one. */ if (areq == AUTH_REQ_CRYPT) { if (pqGetnchar(conn->salt, sizeof(conn->salt), conn)) continue; } /* OK, we successfully read the message; mark data consumed */ conn->inStart = conn->inCursor; /* Respond to the request if necessary. */ if (fe_sendauth(areq, conn, conn->pghost, conn->pgpass, conn->errorMessage) != STATUS_OK) goto connect_errReturn; if (pqFlush(conn)) goto connect_errReturn; /* Are we done? */ if (areq == AUTH_REQ_OK) break; } /* * Now we expect to hear from the backend. A ReadyForQuery message * indicates that startup is successful, but we might also get an * Error message indicating failure. (Notice messages indicating * nonfatal warnings are also allowed by the protocol, as is a * BackendKeyData message.) Easiest way to handle this is to let * PQgetResult() read the messages. We just have to fake it out about * the state of the connection. */ conn->status = CONNECTION_OK; conn->asyncStatus = PGASYNC_BUSY; res = PQgetResult(conn); /* NULL return indicating we have gone to IDLE state is expected */ if (res) { if (res->resultStatus != PGRES_FATAL_ERROR) sprintf(conn->errorMessage, "connectDB() -- unexpected message during startup\n"); PQclear(res); goto connect_errReturn; } /* * Given the new protocol that sends a ReadyForQuery message after * successful backend startup, it should no longer be necessary to * send an empty query to test for startup. */#ifdef NOT_USED /* * Send a blank query to make sure everything works; in particular, * that the database exists. */ res = PQexec(conn, " "); if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY) { /* PQexec has put error message in conn->errorMessage */ closePGconn(conn); PQclear(res); goto connect_errReturn; } PQclear(res);#endif /* * Post-connection housekeeping. Send environment variables to server */ PQsetenv(conn); return CONNECTION_OK;connect_errReturn: if (conn->sock >= 0) {#ifdef WIN32 closesocket(conn->sock);#else close(conn->sock);#endif conn->sock = -1; } return CONNECTION_BAD;}voidPQsetenv(PGconn *conn){ struct EnvironmentOptions *eo; char setQuery[80]; /* mjl: size okay? XXX */#ifdef MULTIBYTE char *envname = "PGCLIENTENCODING"; static char envbuf[64]; /* big enough? */ char *env; char *encoding = 0; PGresult *rtn;#endif#ifdef MULTIBYTE /* query server encoding */ env = getenv(envname); if (!env || *env == '\0') { rtn = PQexec(conn, "select getdatabaseencoding()"); if (rtn && PQresultStatus(rtn) == PGRES_TUPLES_OK) { encoding = PQgetvalue(rtn, 0, 0); if (encoding) { /* set client encoding */ sprintf(envbuf, "%s=%s", envname, encoding); putenv(envbuf); } } PQclear(rtn); if (!encoding) { /* this should not happen */ sprintf(envbuf, "%s=%s", envname, pg_encoding_to_char(MULTIBYTE)); putenv(envbuf); } }#endif for (eo = EnvironmentOptions; eo->envName; eo++) { const char *val; if ((val = getenv(eo->envName))) { PGresult *res; if (strcasecmp(val, "default") == 0) sprintf(setQuery, "SET %s = %.60s", eo->pgName, val); else sprintf(setQuery, "SET %s = '%.60s'", eo->pgName, val);#ifdef CONNECTDEBUG printf("Use environment variable %s to send %s\n", eo->envName, setQuery);#endif res = PQexec(conn, setQuery); PQclear(res); /* Don't care? */ } }} /* PQsetenv() *//* * makeEmptyPGconn * - create a PGconn data structure with (as yet) no interesting data */static PGconn *makeEmptyPGconn(void){ PGconn *conn = (PGconn *) malloc(sizeof(PGconn)); if (conn == NULL) return conn; /* Zero all pointers */ MemSet((char *) conn, 0, sizeof(PGconn)); conn->noticeHook = defaultNoticeProcessor; conn->status = CONNECTION_BAD; conn->asyncStatus = PGASYNC_IDLE; conn->notifyList = DLNewList(); conn->sock = -1; conn->inBufSize = PQ_BUFFER_SIZE; conn->inBuffer = (char *) malloc(conn->inBufSize); conn->outBufSize = PQ_BUFFER_SIZE; conn->outBuffer = (char *) malloc(conn->outBufSize); if (conn->inBuffer == NULL || conn->outBuffer == NULL) { freePGconn(conn); conn = NULL; } return conn;}/* * freePGconn * - free the PGconn data structure * */static voidfreePGconn(PGconn *conn){ if (!conn) return; pqClearAsyncResult(conn); /* deallocate result and curTuple */ if (conn->sock >= 0)#ifdef WIN32 closesocket(conn->sock);#else close(conn->sock);#endif if (conn->pghost) free(conn->pghost); if (conn->pgport) free(conn->pgport); if (conn->pgtty) free(conn->pgtty); if (conn->pgoptions) free(conn->pgoptions); if (conn->dbName) free(conn->dbName); if (conn->pguser) free(conn->pguser); if (conn->pgpass) free(conn->pgpass); /* Note that conn->Pfdebug is not ours to close or free */ if (conn->notifyList) DLFreeList(conn->notifyList); if (conn->lobjfuncs) free(conn->lobjfuncs); if (conn->inBuffer) free(conn->inBuffer); if (conn->outBuffer) free(conn->outBuffer); free(conn);}/* closePGconn - properly close a connection to the backend*/static voidclosePGconn(PGconn *conn){ if (conn->sock >= 0) { /* * Try to send "close connection" message to backend. Ignore any * error. Note: this routine used to go to substantial lengths to * avoid getting SIGPIPE'd if the connection were already closed. * Now we rely on pqFlush to avoid the signal. */ (void) pqPuts("X", conn); (void) pqFlush(conn); } /* * Close the connection, reset all transient state, flush I/O buffers. */ if (conn->sock >= 0)#ifdef WIN32 closesocket(conn->sock);#else close(conn->sock);#endif conn->sock = -1; conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just * absent */ conn->asyncStatus = PGASYNC_IDLE; pqClearAsyncResult(conn); /* deallocate result and curTuple */ if (conn->lobjfuncs) free(conn->lobjfuncs); conn->lobjfuncs = NULL; conn->inStart = conn->inCursor = conn->inEnd = 0; conn->outCount = 0;}/* PQfinish: properly close a connection to the backend also frees the PGconn data structure so it shouldn't be re-used after this*/voidPQfinish(PGconn *conn){ if (conn) { closePGconn(conn); freePGconn(conn); }}/* PQreset : resets the connection to the backend closes the existing connection and makes a new one*/voidPQreset(PGconn *conn){ if (conn) { closePGconn(conn); conn->status = connectDB(conn); }}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -