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 4099aff799..0463346876 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/preproc/ecpg.tokens b/src/interfaces/ecpg/preproc/ecpg.tokens
index 8e0527fdb7..def81d453f 100644
--- a/src/interfaces/ecpg/preproc/ecpg.tokens
+++ b/src/interfaces/ecpg/preproc/ecpg.tokens
@@ -23,4 +23,4 @@
S_STATIC S_SUB S_VOLATILE
S_TYPEDEF
-%token CSTRING CVARIABLE CPP_LINE IP
+%token CSTRING CVARIABLE CPP_LINE IPV4 ENCLOSEDIPV6
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index 0e4a041393..0f3ad6ef3f 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -94,7 +94,8 @@ connection_target: opt_database_name opt_server opt_port
if (strncmp($1, "unix", strlen("unix")) == 0 &&
strncmp($3 + strlen("//"), "localhost", strlen("localhost")) != 0 &&
- strncmp($3 + strlen("//"), "127.0.0.1", strlen("127.0.0.1")) != 0)
+ strncmp($3 + strlen("//"), "127.0.0.1", strlen("127.0.0.1")) != 0 &&
+ strncmp($3 + strlen("//"), "[::1]", strlen("[::1]")) != 0)
mmerror(PARSE_ERROR, ET_ERROR, "Unix-domain sockets only work on \"localhost\" but not on \"%s\"", $3 + strlen("//"));
$$ = make3_str(make3_str(mm_strdup("\""), $1, mm_strdup(":")), $3, make3_str(make3_str($4, mm_strdup("/"), $6), $7, mm_strdup("\"")));
@@ -145,7 +146,8 @@ opt_server: server { $$ = $1; }
server_name: ColId { $$ = $1; }
| ColId '.' server_name { $$ = make3_str($1, mm_strdup("."), $3); }
- | IP { $$ = make_name(); }
+ | IPV4 { $$ = make_name(); }
+ | ENCLOSEDIPV6 { $$ = make_name(); }
;
opt_port: ':' Iconst { $$ = make2_str(mm_strdup(":"), $2); }
diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l
index 7a0356638d..e75e2d5337 100644
--- a/src/interfaces/ecpg/preproc/pgc.l
+++ b/src/interfaces/ecpg/preproc/pgc.l
@@ -409,7 +409,13 @@ struct [sS][tT][rR][uU][cC][tT]
exec_sql {exec}{space}*{sql}{space}*
ipdigit ({digit}|{digit}{digit}|{digit}{digit}{digit})
-ip {ipdigit}\.{ipdigit}\.{ipdigit}\.{ipdigit}
+ipv4 {ipdigit}\.{ipdigit}\.{ipdigit}\.{ipdigit}
+
+/* for ipv6 */
+hexadecimal [0-9A-Fa-f]
+onegroup {hexadecimal}{1,4}
+ipv6 ({onegroup}:){7,7}{onegroup}|({onegroup}:){1,7}:|({onegroup}:){1,6}:{onegroup}|({onegroup}:){1,5}(:{onegroup}){1,2}|({onegroup}:){1,4}(:{onegroup}){1,3}|({onegroup}:){1,3}(:{onegroup}){1,4}|({onegroup}:){1,2}(:{onegroup}){1,5}|{onegroup}:((:{onegroup}){1,6})|:((:{onegroup}){1,7}|:)|fe80:(:{hexadecimal}{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|({onegroup}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])
+enclosedipv6 "\["{ipv6}"\]"
/* we might want to parse all cpp include files */
cppinclude {space}*#{include}{space}*
@@ -916,9 +922,13 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
return PARAM;
}
-{ip} {
+{ipv4} {
+ base_yylval.str = mm_strdup(yytext);
+ return IPV4;
+ }
+{enclosedipv6} {
base_yylval.str = mm_strdup(yytext);
- return IP;
+ return ENCLOSEDIPV6;
}
} /* */
diff --git a/src/interfaces/ecpg/test/connect/test5.pgc b/src/interfaces/ecpg/test/connect/test5.pgc
index e712fa8778..e001997ef4 100644
--- a/src/interfaces/ecpg/test/connect/test5.pgc
+++ b/src/interfaces/ecpg/test/connect/test5.pgc
@@ -72,5 +72,18 @@ 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 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..f3f574ac86 100644
--- a/src/interfaces/ecpg/test/expected/connect-test5.c
+++ b/src/interfaces/ecpg/test/expected/connect-test5.c
@@ -158,5 +158,32 @@ 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"
+
+ { ECPGdisconnect(__LINE__, "main");}
+#line 80 "test5.pgc"
+
+
+ { ECPGconnect(__LINE__, 0, "unix:postgresql://[::1/ecpg2_regression" , NULL, NULL , "main", 0); }
+#line 82 "test5.pgc"
+
+
+ { ECPGconnect(__LINE__, 0, "unix:postgresql://[]/ecpg2_regression" , NULL, NULL , "main", 0); }
+#line 84 "test5.pgc"
+
+
+ { ECPGconnect(__LINE__, 0, "unix:postgresql://[::1]&ecpg2_regression" , NULL, NULL , "main", 0); }
+#line 86 "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..dbfe31cf79 100644
--- a/src/interfaces/ecpg/test/expected/connect-test5.stderr
+++ b/src/interfaces/ecpg/test/expected/connect-test5.stderr
@@ -88,3 +88,17 @@
[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]: 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