📄 fe-connect.c
字号:
* and should be freed when no longer needed via PQconninfoFree(). (In prior * versions, the returned array was static, but that's not thread-safe.) * Pre-7.0 applications that use this function will see a small memory leak * until they are updated to call PQconninfoFree. */PQconninfoOption *PQconndefaults(void){ PQExpBufferData errorBuf; PQconninfoOption *connOptions; initPQExpBuffer(&errorBuf); connOptions = conninfo_parse("", &errorBuf); termPQExpBuffer(&errorBuf); return connOptions;}/* ---------------- * PQsetdbLogin * * establishes a connection to a postgres backend through the postmaster * at the specified host and port. * * returns a PGconn* which is needed for all subsequent libpq calls * * if the status field of the connection returned is CONNECTION_BAD, * then only the errorMessage is likely to be useful. * ---------------- */PGconn *PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, const char *pgtty, const char *dbName, const char *login, const char *pwd){ PGconn *conn; /* * Allocate memory for the conn structure */ conn = makeEmptyPGconn(); if (conn == NULL) return NULL; /* * Parse an empty conninfo string in order to set up the same defaults * that PQconnectdb() would use. */ if (!connectOptions1(conn, "")) return conn; /* * Absorb specified options into conn structure, overriding defaults */ if (pghost && pghost[0] != '\0') { if (conn->pghost) free(conn->pghost); conn->pghost = strdup(pghost); } if (pgport && pgport[0] != '\0') { if (conn->pgport) free(conn->pgport); conn->pgport = strdup(pgport); } if (pgoptions && pgoptions[0] != '\0') { if (conn->pgoptions) free(conn->pgoptions); conn->pgoptions = strdup(pgoptions); } if (pgtty && pgtty[0] != '\0') { if (conn->pgtty) free(conn->pgtty); conn->pgtty = strdup(pgtty); } if (dbName && dbName[0] != '\0') { if (conn->dbName) free(conn->dbName); conn->dbName = strdup(dbName); } if (login && login[0] != '\0') { if (conn->pguser) free(conn->pguser); conn->pguser = strdup(login); } if (pwd && pwd[0] != '\0') { if (conn->pgpass) free(conn->pgpass); conn->pgpass = strdup(pwd); } /* * Compute derived options */ if (!connectOptions2(conn)) return conn; /* * Connect to the database */ if (connectDBStart(conn)) (void) connectDBComplete(conn); return conn;}/* ---------- * connectNoDelay - * Sets the TCP_NODELAY socket option. * Returns 1 if successful, 0 if not. * ---------- */static intconnectNoDelay(PGconn *conn){#ifdef TCP_NODELAY int on = 1; if (setsockopt(conn->sock, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0) { char sebuf[256]; printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not set socket to TCP no delay mode: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); return 0; }#endif return 1;}/* ---------- * connectFailureMessage - * create a friendly error message on connection failure. * ---------- */static voidconnectFailureMessage(PGconn *conn, int errorno){ char sebuf[256];#ifdef HAVE_UNIX_SOCKETS if (IS_AF_UNIX(conn->raddr.addr.ss_family)) { char service[NI_MAXHOST]; pg_getnameinfo_all(&conn->raddr.addr, conn->raddr.salen, NULL, 0, service, sizeof(service), NI_NUMERICSERV); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not connect to server: %s\n" "\tIs the server running locally and accepting\n" "\tconnections on Unix domain socket \"%s\"?\n"), SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), service); } else#endif /* HAVE_UNIX_SOCKETS */ { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not connect to server: %s\n" "\tIs the server running on host \"%s\" and accepting\n" "\tTCP/IP connections on port %s?\n"), SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), conn->pghostaddr ? conn->pghostaddr : (conn->pghost ? conn->pghost : "???"), conn->pgport); }}/* ---------- * connectDBStart - * Begin the process of making a connection to the backend. * * Returns 1 if successful, 0 if not. * ---------- */static intconnectDBStart(PGconn *conn){ int portnum; char portstr[128]; struct addrinfo *addrs = NULL; struct addrinfo hint; const char *node; int ret; if (!conn) return 0; /* Ensure our buffers are empty */ conn->inStart = conn->inCursor = conn->inEnd = 0; conn->outCount = 0; /* * Determine the parameters to pass to pg_getaddrinfo_all. */ /* Initialize hint structure */ MemSet(&hint, 0, sizeof(hint)); hint.ai_socktype = SOCK_STREAM; hint.ai_family = AF_UNSPEC; /* Set up port number as a string */ if (conn->pgport != NULL && conn->pgport[0] != '\0') portnum = atoi(conn->pgport); else portnum = DEF_PGPORT; snprintf(portstr, sizeof(portstr), "%d", portnum); if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0') { /* Using pghostaddr avoids a hostname lookup */ node = conn->pghostaddr; hint.ai_family = AF_UNSPEC; hint.ai_flags = AI_NUMERICHOST; } else if (conn->pghost != NULL && conn->pghost[0] != '\0') { /* Using pghost, so we have to look-up the hostname */ node = conn->pghost; hint.ai_family = AF_UNSPEC; } else {#ifdef HAVE_UNIX_SOCKETS /* pghostaddr and pghost are NULL, so use Unix domain socket */ node = NULL; hint.ai_family = AF_UNIX; UNIXSOCK_PATH(portstr, portnum, conn->pgunixsocket);#else /* Without Unix sockets, default to localhost instead */ node = "localhost"; hint.ai_family = AF_UNSPEC;#endif /* HAVE_UNIX_SOCKETS */ } /* Use pg_getaddrinfo_all() to resolve the address */ ret = pg_getaddrinfo_all(node, portstr, &hint, &addrs); if (ret || !addrs) { if (node) printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not translate host name \"%s\" to address: %s\n"), node, gai_strerror(ret)); else printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"), portstr, gai_strerror(ret)); if (addrs) pg_freeaddrinfo_all(hint.ai_family, addrs); goto connect_errReturn; }#ifdef USE_SSL /* setup values based on SSL mode */ if (conn->sslmode[0] == 'd') /* "disable" */ conn->allow_ssl_try = false; else if (conn->sslmode[0] == 'a') /* "allow" */ conn->wait_ssl_try = true;#endif /* * Set up to try to connect, with protocol 3.0 as the first attempt. */ conn->addrlist = addrs; conn->addr_cur = addrs; conn->addrlist_family = hint.ai_family; conn->pversion = PG_PROTOCOL(3, 0); conn->status = CONNECTION_NEEDED; /* * The code for processing CONNECTION_NEEDED state is in PQconnectPoll(), * so that it can easily be re-executed if needed again during the * asynchronous startup process. However, we must run it once here, * because callers expect a success return from this routine to mean that * we are in PGRES_POLLING_WRITING connection state. */ if (PQconnectPoll(conn) == PGRES_POLLING_WRITING) return 1;connect_errReturn: if (conn->sock >= 0) { pqsecure_close(conn); closesocket(conn->sock); conn->sock = -1; } conn->status = CONNECTION_BAD; return 0;}/* * connectDBComplete * * Block and complete a connection. * * Returns 1 on success, 0 on failure. */static intconnectDBComplete(PGconn *conn){ PostgresPollingStatusType flag = PGRES_POLLING_WRITING; time_t finish_time = ((time_t) -1); if (conn == NULL || conn->status == CONNECTION_BAD) return 0; /* * Set up a time limit, if connect_timeout isn't zero. */ if (conn->connect_timeout != NULL) { int timeout = atoi(conn->connect_timeout); if (timeout > 0) { /* * Rounding could cause connection to fail; need at least 2 secs */ if (timeout < 2) timeout = 2; /* calculate the finish time based on start + timeout */ finish_time = time(NULL) + timeout; } } for (;;) { /* * Wait, if necessary. Note that the initial state (just after * PQconnectStart) is to wait for the socket to select for writing. */ switch (flag) { case PGRES_POLLING_OK: return 1; /* success! */ case PGRES_POLLING_READING: if (pqWaitTimed(1, 0, conn, finish_time)) { conn->status = CONNECTION_BAD; return 0; } break; case PGRES_POLLING_WRITING: if (pqWaitTimed(0, 1, conn, finish_time)) { conn->status = CONNECTION_BAD; return 0; } break; default: /* Just in case we failed to set it in PQconnectPoll */ conn->status = CONNECTION_BAD; return 0; } /* * Now try to advance the state machine. */ flag = PQconnectPoll(conn); }}/* ---------------- * PQconnectPoll * * Poll an asynchronous connection. * * Returns a PostgresPollingStatusType. * Before calling this function, use select(2) to determine when data * has arrived.. * * You must call PQfinish whether or not this fails. * * This function and PQconnectStart are intended to allow connections to be * made without blocking the execution of your program on remote I/O. However, * there are a number of caveats: * * o If you call PQtrace, ensure that the stream object into which you trace * will not block. * o If you do not supply an IP address for the remote host (i.e. you * supply a host name instead) then PQconnectStart will block on * gethostbyname. You will be fine if using Unix sockets (i.e. by * supplying neither a host name nor a host address). * o If your backend wants to use Kerberos authentication then you must * supply both a host name and a host address, otherwise this function * may block on gethostname. * * ---------------- */PostgresPollingStatusTypePQconnectPoll(PGconn *conn){ PGresult *res; char sebuf[256]; if (conn == NULL) return PGRES_POLLING_FAILED; /* Get the new data */ switch (conn->status) { /* * We really shouldn't have been polled in these two cases, but we * can handle it. */ case CONNECTION_BAD: return PGRES_POLLING_FAILED; case CONNECTION_OK: return PGRES_POLLING_OK; /* These are reading states */ case CONNECTION_AWAITING_RESPONSE: case CONNECTION_AUTH_OK: { /* Load waiting data */ int n = pqReadData(conn); if (n < 0) goto error_return; if (n == 0) return PGRES_POLLING_READING; break; } /* These are writing states, so we just proceed. */ case CONNECTION_STARTED: case CONNECTION_MADE: break; /* We allow pqSetenvPoll to decide whether to proceed. */ case CONNECTION_SETENV: break; /* Special cases: proceed without waiting. */ case CONNECTION_SSL_STARTUP: case CONNECTION_NEEDED: break; default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext( "invalid connection state, " "probably indicative of memory corruption\n" )); goto error_return; }keep_going: /* We will come back to here until there is * nothing left to do. */ switch (conn->status) { case CONNECTION_NEEDED: { /* * Try to initiate a connection to one of the addresses * returned by pg_getaddrinfo_all(). conn->addr_cur is the * next one to try. We fail when we run out of addresses * (reporting the error returned for the *last* alternative, * which may not be what users expect :-(). */ while (conn->addr_cur != NULL) { struct addrinfo *addr_cur = conn->addr_cur; /* Remember current address for possible error msg */ memcpy(&conn->raddr.addr, addr_cur->ai_addr, addr_cur->ai_addrlen); conn->raddr.salen = addr_cur->ai_addrlen; /* Open a socket */ conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0); if (conn->sock < 0) { /* * ignore socket() failure if we have more addresses * to try */ if (addr_cur->ai_next != NULL) { conn->addr_cur = addr_cur->ai_next; continue; } printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not create socket: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); break; } /* * Select socket options: no delay of outgoing data for * TCP sockets, nonblock mode, close-on-exec. Fail if any
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -