commit 89afdd3b4593a01e06095aed6ce49ac86ce2d8d1 Author: kuroda.hayato%40jp.fujitsu.com Date: Fri Mar 5 01:44:44 2021 +0000 allow ipv6 diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml index 9310a71166..4708974fb2 100644 --- a/doc/src/sgml/ecpg.sgml +++ b/doc/src/sgml/ecpg.sgml @@ -163,6 +163,15 @@ 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, 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 d2a326c04a..1d8ecd5e8d 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