如何在 DBI->connect 的参数中使用空格?

How do I use spaces in parameters for DBI->connect?

我正在尝试使用 DBIDBD::Pg 连接 SSL 客户端密钥。

use strict;
use warnings 'all';
use DBI;

my $dsn = "dbi:Pg:db=mydb;sslmode=require;host=localhost;"
    ."sslcert=C:\path with\spaces.crt;"
    ."sslkey=C:\path with\spaces.key";

my $dbh = DBI->connect( $dsn, 'username', '' );

我收到以下错误:

Can't connect to database: missing "=" after "with\spaces.crt" in connection info string!

我试过在值周围使用单引号或双引号,但没有用,而且我在文档中找不到任何内容。

更新

单引号如下:

my $dsn = "dbi:Pg:db=mydb;sslmode=require;host=localhost;"
    ."sslcert='C:\path with\spaces.crt';"
    ."sslkey='C:\path with\spaces.key'";

我收到以下错误:

failed: FATAL:  connection requires a valid client certificate

我知道此配置有效,因为它适用于 Python。

事实证明这行得通:

my $dsn = "dbi:Pg:db=mydb;sslmode=require;host=localhost;"
    ."sslcert='C:\\path with\\spaces.crt';"
    ."sslkey='C:\\path with\\spaces.key'";

为什么我需要双转义反斜杠?

问题是space。目前尚不清楚是否可以提供其中包含 space 的路径。如果是,则可能是特定于驱动程序的语法。您可能需要深入研究 DBI and/or DBD::Pg 以确定语法是否受支持。有些人已经这样做并在评论中提到您可以使用以下内容:

my $dsn = join(';',
   "dbi:Pg:db=mydb",
   "sslmode=require",
   "sslcert='$ssl_cert_qfn'",
   "sslkey='$ssl_key_qfn'",
);

或者你可以从另一个角度来解决这个问题。 Windows 有一个向后兼容系统,允许只支持 DOS 风格路径的应用程序。值得注意的是 DOS 不允许在路径中使用 spaces。通过使用 DOS 风格的路径,您可以避免这个问题。

use Win32 qw( );

my $dsn = join(';',
   "dbi:Pg:db=mydb",
   "sslmode=require",
   "sslcert=".Win32::GetShortPathName($ssl_cert_qfn),
   "sslkey=".Win32::GetShortPathName($ssl_key_qfn),
);

另一种解决方案是使用 DBD::Pg's documentation 中详述的配置文件。

要在您的 DSN 中包含包含空格的属性,请用单引号将值括起来:

my $dsn = q{dbi:Pg:db=mydb;sslmode=require;host=localhost;}
        . q{sslcert='C:\\path with\\spaces.crt';}
        . q{sslkey='C:\\path with\\spaces.key'};

请注意,用于分隔连接属性的分号必须 单引号之外。另请注意,属性内的反斜杠和单引号必须使用反斜杠进行转义(您必须在上面使用三个反斜杠,因为 Perl 将单引号字符串中的 \ 转换为 \)。

如果您的 DSN 包含在双引号中,您必须使用四个反斜杠,因为 Perl 在双引号字符串中插入转义序列,如 \n

my $dsn = qq{dbi:Pg:db=mydb;sslmode=require;host=localhost;}
        . qq{sslcert='C:\\path with\\spaces.crt';}
        . qq{sslkey='C:\\path with\\spaces.key'};

至于文档,我没有在 DBD::Pg 中看到这一点,但您可以通过查看源代码看到它是受支持的。处理 DSN 的代码在 DBD::Pg 分布中的 dbdimp.c 中:

    /* DBD::Pg syntax: 'dbname=dbname;host=host;port=port', 'User', 'Pass' */
    /* libpq syntax: 'dbname=dbname host=host port=port user=uid password=pwd' */

...

    /* Change all semi-colons in dbname to a space, unless single-quoted */
    dest = conn_str;
    while (*dbname != '[=12=]') {
            if (';' == *dbname && !inquote)
                    *dest++ = ' ';
            else {
                    if ('\'' == *dbname)
                            inquote = !inquote;
                    *dest++ = *dbname;
            }
            dbname++;
    }
    *dest = '[=12=]';

这会将 DBI 样式的连接字符串转换为 libpq-style connection string (libpq is the Postgres C API, which DBD::Pg uses behind the scenes). Since the generated DSN is passed straight to libpq, it needs to follow the rules for quoting and escaping described in the libpq documentation

DBD::Pg 的文档补丁肯定是有序的。