Hi, Attached is the patch that improves usage of '*' wildcard in .pgpass, particularly in the host part. The use case is below.
I work with multiple environments (like staging, production, performance and so on), each one containing tens of different database clusters, each cluster has its own subdomain in DNS, i.e. foo.test.db, bar.test.db and so on. Each user has the same credentials on every database of a single domain, i.e. .test.db databases, but different ones in other domains. For those databases, each line in pgpass currently corresponds to a single database, i.e. foo1.test.db:5432:postgres:postgres:keep!tester foo2.test.db:5432:postgres:postgres:keep!tsecret ... foo99.test.db:5432:postgres:postgres:keep!tsecret .... bar1.another.db.:5432:postgres:postgres:trustno1 bar2.another.db.:5432:postgres:postgres:trustno1 ... The problem I'm having is that there are just too many lines like those (tens or even hundreds) and the new databases are added very frequently, making it hard to keep .pgpass up-to-date. What I'd like to have is an ability to specify a pattern in the hostname of .pgpass, to replace the plenty of lines with one: *.test.db:5432:postgres:postgres:keep!secret bar*.*.db:5432:postgres:postgres:trustno1 The patch in attachment implements exactly this, by allowing '*' in the hostname to substitute arbitrary number of characters until the dot. The pattern is only matched when there is a '.' or ':' after the * and only in the hostname, otherwise, '*' is treated like a normal character. It appears that it can only be used for IPv4 addresses, i.e. instead of 255 hosts for 192.168.1.0/24 one can just specify 192.168.1.*. I do understand that it might be a very limited use case in terms of community plans for improving .pgpass, but I doubt that I'm the only one to stumble upon the current limitation; the patch is quite small and might be the first step into extending the functionality of .pgpass It's currently missing the documentation, which I will add in case there will be an interest in making this patch a part of the core. Your feedback is greatly appreciated. -- Regards, Alexey Klyukin
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c new file mode 100644 index 18fcb0c..003739f *** a/src/interfaces/libpq/fe-connect.c --- b/src/interfaces/libpq/fe-connect.c *************** static int parseServiceFile(const char * *** 373,379 **** PQconninfoOption *options, PQExpBuffer errorMessage, bool *group_found); ! static char *pwdfMatchesString(char *buf, char *token); static char *PasswordFromFile(char *hostname, char *port, char *dbname, char *username); static bool getPgPassFilename(char *pgpassfile); --- 373,379 ---- PQconninfoOption *options, PQExpBuffer errorMessage, bool *group_found); ! static char *pwdfMatchesString(char *buf, char *token, bool is_hostname); static char *PasswordFromFile(char *hostname, char *port, char *dbname, char *username); static bool getPgPassFilename(char *pgpassfile); *************** defaultNoticeProcessor(void *arg, const *** 5466,5472 **** * token doesn't match */ static char * ! pwdfMatchesString(char *buf, char *token) { char *tbuf, *ttok; --- 5466,5472 ---- * token doesn't match */ static char * ! pwdfMatchesString(char *buf, char *token, bool is_hostname) { char *tbuf, *ttok; *************** pwdfMatchesString(char *buf, char *token *** 5480,5485 **** --- 5480,5497 ---- return tbuf + 2; while (*tbuf != 0) { + /* '*' matches everything until '.' or end, but only for the hostname */ + if (*tbuf == '*' && (*(tbuf + 1) == '.' || *(tbuf + 1) == ':') && + !bslash && is_hostname) + { + tbuf++; + while (*ttok != *tbuf) + { + if (*ttok == 0) + return (*tbuf == ':') ? tbuf + 1 : NULL; + ttok++; + } + } if (*tbuf == '\\' && !bslash) { tbuf++; *************** PasswordFromFile(char *hostname, char *p *** 5588,5597 **** if (buf[len - 1] == '\n') buf[len - 1] = 0; ! if ((t = pwdfMatchesString(t, hostname)) == NULL || ! (t = pwdfMatchesString(t, port)) == NULL || ! (t = pwdfMatchesString(t, dbname)) == NULL || ! (t = pwdfMatchesString(t, username)) == NULL) continue; ret = strdup(t); fclose(fp); --- 5600,5609 ---- if (buf[len - 1] == '\n') buf[len - 1] = 0; ! if ((t = pwdfMatchesString(t, hostname, true)) == NULL || ! (t = pwdfMatchesString(t, port, false)) == NULL || ! (t = pwdfMatchesString(t, dbname, false)) == NULL || ! (t = pwdfMatchesString(t, username, false)) == NULL) continue; ret = strdup(t); fclose(fp);
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers