diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml index 9310a71166..a21ee477d7 100644 --- a/doc/src/sgml/ecpg.sgml +++ b/doc/src/sgml/ecpg.sgml @@ -163,6 +163,16 @@ EXEC SQL CONNECT TO target AS target directly. + + Same as libpq, the host part can be specified an IPv6 address. + To specify an IPv6 address, the string must be specified as + SQL string literal and enclose it in square brackets: + + + +EXEC SQL CONNECT TO 'tcp:postgresql://[2001:db8::1234]/database' + + There are also different ways to specify the user name: diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c index ac65c7ac8b..231d762db3 100644 --- a/src/interfaces/ecpg/ecpglib/connect.c +++ b/src/interfaces/ecpg/ecpglib/connect.c @@ -295,9 +295,44 @@ parse_newstyle(int lineno, char *buf, char **host, char **dbname, char **port, c start = buf + prefix_len + strlen("postgresql://"); p = start; - /* Look ahead for possible user credentials designator */ - while (*p && *p != ':' && *p != '/' && *p != '?') - ++p; + + /* + * Look for IPv6 address. + */ + if (*p == '[') + { + start = ++p; + while (*p && *p != ']') + ++p; + if (!*p) + { + ecpg_log("end of string reached when looking for matching \"]\" in IPv6 host address: \"%s\"\n", buf); + return -1; + } + if (p == start) + { + ecpg_log("IPv6 host address may not be empty: \"%s\"\n", buf); + return -1; + } + /* Cut off the bracket and advance */ + *(p++) = '\0'; + + /* + * The address may be followed by a port specifier or a slash or a + * query. + */ + if (*p && *p != ':' && *p != '/' && *p != '?') + { + ecpg_log("unexpected character \"%c\" at position %d (expected \":\", \"/\" or \"?\"): \"%s]%s\"\n", *p, (int) (p - buf + 1), buf, p); + return -1; + } + } + else + { + /* Look ahead for possible user credentials designator */ + while (*p && *p != ':' && *p != '/' && *p != '?') + ++p; + } /* Save the hostname terminator before we null it */ prevchar = *p; @@ -328,7 +363,8 @@ parse_newstyle(int lineno, char *buf, char **host, char **dbname, char **port, c *port = ecpg_strdup(start, lineno); if (!(*port)) { - ecpg_free(*host); + if (!is_unix) + ecpg_free(*host); return -1; } connect_params++; @@ -352,7 +388,8 @@ parse_newstyle(int lineno, char *buf, char **host, char **dbname, char **port, c *dbname = ecpg_strdup(start, lineno); if (!(*dbname)) { - ecpg_free(*host); + if (!is_unix) + ecpg_free(*host); ecpg_free(*port); return -1; } @@ -368,7 +405,8 @@ parse_newstyle(int lineno, char *buf, char **host, char **dbname, char **port, c *options = ecpg_strdup(start, lineno); if (!(*options)) { - ecpg_free(*host); + if (!is_unix) + ecpg_free(*host); ecpg_free(*port); ecpg_free(*dbname); return -1; @@ -379,12 +417,13 @@ parse_newstyle(int lineno, char *buf, char **host, char **dbname, char **port, c if (is_unix) { if (strcmp(*host, "localhost") && - strcmp(*host, "127.0.0.1")) + strcmp(*host, "127.0.0.1") && + strcmp(*host, "::1")) { /* * The alternative of using "127.0.0.1" here is deprecated * and undocumented; we'll keep it for backward - * compatibility's sake, but not extend it to allow IPv6. + * compatibility's sake. */ ecpg_log("ECPGconnect: non-localhost access via sockets on line %d\n", lineno); ecpg_raise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, *dbname ? *dbname : ecpg_gettext("")); @@ -435,8 +474,43 @@ parse_oldstyle(int lineno, char *buf, char **host, char **dbname, char **port) { /* hostname is found */ start = ++p; - while(*p && *p != ':') - ++p; + /* + * Look for IPv6 address. + */ + if (*p == '[') + { + start = ++p; + while (*p && *p != ']') + ++p; + if (!*p) + { + ecpg_log("end of string reached when looking for matching \"]\" in IPv6 host address: \"%s\"\n", buf); + return -1; + } + if (p == start) + { + ecpg_log("IPv6 host address may not be empty: \"%s\"\n", buf); + return -1; + } + /* Cut off the bracket and advance */ + *(p++) = '\0'; + + /* + * The address may be followed by a port specifier or a slash or a + * query. + */ + if (*p && *p != ':') + { + ecpg_log("unexpected character \"%c\": \"%s]%s\"\n", *p, buf, p); + return -1; + } + } + else + { + while(*p && *p != ':') + ++p; + + } prevchar = *p; *p = '\0'; if (strcmp(start, "localhost") && strcmp(start, "127.0.0.1")) diff --git a/src/interfaces/ecpg/test/connect/test5.pgc b/src/interfaces/ecpg/test/connect/test5.pgc index e712fa8778..5a6326deeb 100644 --- a/src/interfaces/ecpg/test/connect/test5.pgc +++ b/src/interfaces/ecpg/test/connect/test5.pgc @@ -72,5 +72,15 @@ exec sql end declare section; /* not connected */ exec sql disconnect nonexistent; + /* use IPv6 */ + exec sql connect to 'unix:postgresql://[::1]/ecpg2_regression' as main; + exec sql disconnect main; + + exec sql connect to 'unix:postgresql://[::1/ecpg2_regression' as main; + + exec sql connect to 'unix:postgresql://[]/ecpg2_regression' as main; + + exec sql connect to 'unix:postgresql://[::1]&ecpg2_regression' as main; + return 0; } diff --git a/src/interfaces/ecpg/test/expected/connect-test5.c b/src/interfaces/ecpg/test/expected/connect-test5.c index 6ae5b589de..9ae7a3e144 100644 --- a/src/interfaces/ecpg/test/expected/connect-test5.c +++ b/src/interfaces/ecpg/test/expected/connect-test5.c @@ -158,5 +158,25 @@ main(void) #line 73 "test5.pgc" + /* use IPv6 */ + { ECPGconnect(__LINE__, 0, "unix:postgresql://[::1]/ecpg2_regression" , NULL, NULL , "main", 0); } +#line 76 "test5.pgc" + + { ECPGdisconnect(__LINE__, "main");} +#line 77 "test5.pgc" + + + { ECPGconnect(__LINE__, 0, "unix:postgresql://[::1/ecpg2_regression" , NULL, NULL , "main", 0); } +#line 79 "test5.pgc" + + + { ECPGconnect(__LINE__, 0, "unix:postgresql://[]/ecpg2_regression" , NULL, NULL , "main", 0); } +#line 81 "test5.pgc" + + + { ECPGconnect(__LINE__, 0, "unix:postgresql://[::1]&ecpg2_regression" , NULL, NULL , "main", 0); } +#line 83 "test5.pgc" + + return 0; } diff --git a/src/interfaces/ecpg/test/expected/connect-test5.stderr b/src/interfaces/ecpg/test/expected/connect-test5.stderr index a15f344320..63bc450329 100644 --- a/src/interfaces/ecpg/test/expected/connect-test5.stderr +++ b/src/interfaces/ecpg/test/expected/connect-test5.stderr @@ -88,3 +88,13 @@ [NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: raising sqlcode -220 on line 73: connection "nonexistent" does not exist on line 73 [NO_PID]: sqlca: code: -220, state: 08003 +[NO_PID]: ECPGconnect: opening database ecpg2_regression on port +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_finish: connection main closed +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: end of string reached when looking for matching "]" in IPv6 host address: "unix:postgresql://[::1/ecpg2_regression" +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: IPv6 host address may not be empty: "unix:postgresql://[]/ecpg2_regression" +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: unexpected character "&" at position 24 (expected ":", "/" or "?"): "unix:postgresql://[::1]&ecpg2_regression" +[NO_PID]: sqlca: code: 0, state: 00000