MySQL、Perl DBI 和 JSON 中的布尔值
考虑一个简单的 mysql 表,其中包含 id 列(整数)和 flag 列(布尔值)。 用 JavaScript 编写的用户界面通过 JSON 与 CGI 后端通信,既可以从表中接收数据,也可以更新数据。
现在,当将数据 {id: 5, flag: true}
发送到服务器时,我当然使用 from_json
来获取 perl hash(ref)。然后
my $sth = $dbh->prepare('UPDATE my_table SET flag = ? WHERE id = ?);
$sth->execute($data->{flag}, $data->{id});
总是会导致插入错误值。我相信这是因为 JSON::true 和 JSON::false 的神奇行为,它们分别字符串化为“true”和“false”(但会数字化为 1 和 0)。显然,execute 语句提供了字符串上下文,而不是数字上下文,因此 mysql 服务器接收到一个不包含数字的字符串,然后导致插入值 0。现在,我通过输入 ? 来解决这个问题
。$data->{flag}
之后为 1 : 0
在另一个方向上,会发生相反的错误事情:根据 DBI 的文档,“大多数数据作为字符串返回到 Perl 脚本。”,因此当我使用 to_json
点击 SELECT 查询的结果时,这些值由 UI 作为 JavaScript 字符串“0”和“1”接收,这在 JavaScript 中都是 true。因此,现在我在应用 to_json
之前执行 $_->{flag} += 0 foreach (@result)
。
问题:我应该在链中的哪个点插入这些“黑客”?例如,在 JavaScript 端,我可以确保发送值 1 和 0,而不是 true 和 false,并将标志重新读取为数字。我可以告诉 DBI 让它返回数字列(JSON 会认为是标量)吗?
Consider a simple mysql table with columns id
(an integer) and flag
(a boolean).
A user interface written in JavaScript communicates to a CGI backend via JSON, both for receiving data from the table and for updating it.
Now, when sending the data {id: 5, flag: true}
to the server, I of course use from_json
to get a perl hash(ref). Then
my $sth = $dbh->prepare('UPDATE my_table SET flag = ? WHERE id = ?);
$sth->execute($data->{flag}, $data->{id});
always results in a false value being inserted. I believe this is because of the magic behaviour of JSON::true and JSON::false, which stringify to 'true' and 'false', respectively (but would numify to 1 and 0). Apparently, the execute statement provides string context, and not numeric context, so the mysql server receives a string containing no numbers, which then causes the value 0 to be inserted. For now, I fix this by putting ? 1 : 0
after $data->{flag}
.
In the other direction, the opposite wrong thing happens: According to DBI's doc, "Most data is returned to the Perl script as strings.", so when I hit the result of a SELECT query with to_json
, the values are received by the UI as JavaScript strings "0" and "1", which are both true in JavaScript. So for now I do a $_->{flag} += 0 foreach (@result)
before applying to_json
.
Question: At which point(s) in the chain should I insert these 'hacks'? In the JavaScript end, I could for example make sure to send the values 1 and 0 instead for true and false, and reread the flags as numbers. Is there something I can tell DBI to make it return numeric columns as (scalars which JSON would consider as) numbers?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
去过那里并做到了。最初,我在 Perl 中解码的 JSON 中的数字中添加了 0,但这很烦人且耗时,因为我必须循环遍历一个大型结构,为所有数字添加 0。然后我帮助 Tim Bunce 将 sql_type_cast 添加到 DBI,以便对 DBD::Oracle 进行一些小的更改可以使用它返回数字而不是字符串(它们仍然是 Perl 标量,但进行了一些内部修补 - 见下文)。
我遇到的问题是我使用的是 JSON::XS,它会查看encode_json 中的标量,以查看 pv 或 iv 是否设置为决定是否在编码的 JSON 中生成“0”或 0。 DBI 的 sql_type_cast 和新的 DBIstcf_DISCARD_STRING 属性意味着我现在将数字列绑定为 $s->bind_col(1, \my $col, {TYPE => SQL_INTEGER, DiscardString => 1}) ,并且 sql_type_cast 将删除标量的 pv,如果它看起来像一个数字。问题是 DBD 需要更改为使用 sql_type_cast。 DBD::Oracle 已更改,我刚刚在 1.31 中更改了 DBD::ODBC 来执行此操作。
也许你可以以同样的方式更改 DBD::mysql 或说服其他人(我不使用 mysql)。
有关 DiscardString 实现的详细讨论,请参阅此处 。
您可以在 DBD::ODBC 中找到一个名为 sql_type_cast.t 的测试,它测试 DiscardString 是否可以在不使用 JSON::whatever 的情况下工作
更新:另一个关于 dbi-dev 讨论的链接
Been there and done that. Initially I added 0 to the numbers in the JSON we decoded in Perl but it was tiresome and time consuming because I had to loop through a large structure adding 0 to all the numbers. Then I helped Tim Bunce add sql_type_cast to DBI so that a few minor changes to DBD::Oracle could use it to return numbers instead of strings (they are still Perl scalars but with some internal tinkering - see below).
The problem I had was I was using JSON::XS and it looks at scalars in encode_json to see if the pv or iv is set to decide whether to produce "0" or 0 in the encoded JSON. DBI's sql_type_cast and the new DBIstcf_DISCARD_STRING attribute means I now bind numeric columns as $s->bind_col(1, \my $col, {TYPE => SQL_INTEGER, DiscardString => 1}) and sql_type_cast will remove the scalar's pv if it looks like a number. The problem is that the DBD needs to change to use sql_type_cast. DBD::Oracle is changed and I just changed DBD::ODBC in 1.31 to do it as well.
Maybe you could change DBD::mysql in the same way or persuade someone else to (I don't use mysql).
See here for a long discussion on the implementation of DiscardString etc.
You can find a test in DBD::ODBC called sql_type_cast.t which tests DiscardString works without using JSON::whatever.
UPDATED: another link to discussion on dbi-dev