我是否有理由选择包含 SHA1 十六进制摘要的字符串列?
我有一个邀请表,看起来像这样
sqlite> .schema invitations
CREATE TABLE "invitations"
( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
, "sender_id" integer
, "recipient_email" varchar(255)
, "token" varchar(255)
, "sent_at" datetime
, "team_id" integer
, "created_at" datetime
, "updated_at" datetime
);
CREATE UNIQUE INDEX "index_invitations_on_recipient_email_and_team_id"
ON "invitations" ("recipient_email", "team_id");
CREATE INDEX "index_invitations_on_sender_id"
ON "invitations" ("sender_id");
CREATE INDEX "index_invitations_on_team_id"
ON "invitations" ("team_id");
令牌列存储在记录创建时生成的十六进制摘要,如下所示(Ruby):
self.token = Digest::SHA1.hexdigest([Time.now, rand].join)
当我将邀请插入数据库时,我可以检索它,
SELECT * FROM "invitations" where "invitations"."recipient_email" = "an email"
但
SELECT * FROM "invitations" where "invitations"."token" = "an token"
即使我复制/也会返回注释粘贴插入语句中的确切标记?
编辑
事实证明,这
SELECT * FROM "invitations" where "invitations"."token" LIKE "an token"
将正确检索记录。
为什么“LIKE”有效,但“=”不行?我尝试在插入之前剥离十六进制并进行不区分大小写的选择。两者都不起作用。
编辑2 看来我只能使用 rubygem“sqlite3”和命令行来复制这个问题。这是没有 Rails 等的
情况。流程如下:
stuff $ gem install sqlite3
Fetching: sqlite3-1.3.3.gem (100%)
Building native extensions. This could take a while...
Successfully installed sqlite3-1.3.3
1 gem installed
Installing ri documentation for sqlite3-1.3.3...
Installing RDoc documentation for sqlite3-1.3.3...
stuff $ irb
ruby-1.9.2-head :001 > require "sqlite3"
ruby-1.9.2-head :017 > rows = db.execute <<-SQL
ruby-1.9.2-head :018"> create table invitations (
ruby-1.9.2-head :019"> token varchar(40)
ruby-1.9.2-head :020"> );
ruby-1.9.2-head :021"> SQL
# with normal strings for comparison
ruby-1.9.2-head :022 > ['4535435', 'jfeu833'].each {|hash| db.execute "insert into 'invitations' ('token') values (?)", hash }
=> ["4535435", "jfeu833"]
ruby-1.9.2-head :023 > db.execute("select * from invitations where invitations.token = '4535435'") {|row| p row }
# it finds the row successfully
["4535435"]
=> #<SQLite3::Statement:0x000001011741c8>
ruby-1.9.2-head :028 > require "digest/sha1"
=> true
# now to try it with a hash
ruby-1.9.2-head :029 > [Digest::SHA1.hexdigest("banana")].each {|hash| db.execute "insert into 'invitations' ('token') values (?)", hash }
=> ["250e77f12a5ab6972a0895d290c4792f0a326ea8"]
ruby-1.9.2-head :031 > db.execute("select * from invitations where invitations.token = '250e77f12a5ab6972a0895d290c4792f0a326ea8'") {|row| p row }
# notice that no record is printed
=> #<SQLite3::Statement:0x0000010107c630>
I have an invitations table which looks like this
sqlite> .schema invitations
CREATE TABLE "invitations"
( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
, "sender_id" integer
, "recipient_email" varchar(255)
, "token" varchar(255)
, "sent_at" datetime
, "team_id" integer
, "created_at" datetime
, "updated_at" datetime
);
CREATE UNIQUE INDEX "index_invitations_on_recipient_email_and_team_id"
ON "invitations" ("recipient_email", "team_id");
CREATE INDEX "index_invitations_on_sender_id"
ON "invitations" ("sender_id");
CREATE INDEX "index_invitations_on_team_id"
ON "invitations" ("team_id");
The token column stores hexdigests which are generated on record create like so (Ruby):
self.token = Digest::SHA1.hexdigest([Time.now, rand].join)
When I insert an invitation into the database, I can retrieve it with
SELECT * FROM "invitations" where "invitations"."recipient_email" = "an email"
but
SELECT * FROM "invitations" where "invitations"."token" = "an token"
returns noting even though I'm copy/pasting the exact token from the insert statement?
Edit
It turns out that
SELECT * FROM "invitations" where "invitations"."token" LIKE "an token"
will retrive the record correctly.
Why would "LIKE" work, but "=" not? I've tried stripping the hex before insert and doing a case insensitive select. Neither worked.
Edit 2
It seems I'm able to replicate this issue using only the rubygem "sqlite3" and the command line. That is without Rails etc.
Here's the process:
stuff $ gem install sqlite3
Fetching: sqlite3-1.3.3.gem (100%)
Building native extensions. This could take a while...
Successfully installed sqlite3-1.3.3
1 gem installed
Installing ri documentation for sqlite3-1.3.3...
Installing RDoc documentation for sqlite3-1.3.3...
stuff $ irb
ruby-1.9.2-head :001 > require "sqlite3"
ruby-1.9.2-head :017 > rows = db.execute <<-SQL
ruby-1.9.2-head :018"> create table invitations (
ruby-1.9.2-head :019"> token varchar(40)
ruby-1.9.2-head :020"> );
ruby-1.9.2-head :021"> SQL
# with normal strings for comparison
ruby-1.9.2-head :022 > ['4535435', 'jfeu833'].each {|hash| db.execute "insert into 'invitations' ('token') values (?)", hash }
=> ["4535435", "jfeu833"]
ruby-1.9.2-head :023 > db.execute("select * from invitations where invitations.token = '4535435'") {|row| p row }
# it finds the row successfully
["4535435"]
=> #<SQLite3::Statement:0x000001011741c8>
ruby-1.9.2-head :028 > require "digest/sha1"
=> true
# now to try it with a hash
ruby-1.9.2-head :029 > [Digest::SHA1.hexdigest("banana")].each {|hash| db.execute "insert into 'invitations' ('token') values (?)", hash }
=> ["250e77f12a5ab6972a0895d290c4792f0a326ea8"]
ruby-1.9.2-head :031 > db.execute("select * from invitations where invitations.token = '250e77f12a5ab6972a0895d290c4792f0a326ea8'") {|row| p row }
# notice that no record is printed
=> #<SQLite3::Statement:0x0000010107c630>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我在聊天中与 duckyfuzz(OP)讨论了这个问题,我们发现哈希值在 sqlite 中存储为 BLOB:
因此,出于某种原因,即使 ruby 说插入的是一个字符串:
它最终会出现在sqlite 作为 BLOB。
插入值或按文字插入,而不是使用参数插入,可以使问题消失(经OP测试):
该问题是由 二进制字符串将由 sqlite3 存储为 blob红宝石。防止这种情况的方法是在插入之前将哈希编码为 UTF-8。
完成此操作后,哈希值将存储为文本。
I discussed this with the duckyfuzz (the OP) in a chat, and we found that the hash is stored as a BLOB in sqlite:
So for some reason, even though ruby says that what is being inserted is a string:
it ends up in sqlite as a BLOB.
Interpolating the value, or inserting as literal, instead of inserting using parameters makes the problem go away (as tested by the OP):
The issue is caused by the fact that binary strings will be stored as blobs by the sqlite3 rubygem. The way to prevent this is to encode the hash as UTF-8 before insert.
Once you do that, the hash will be stored as text.
将
token
从varchar(255)
更改为char(40)
可能会有所帮助。另一件让我烦恼的事情是你使用双引号:
"
我一直认为这
不同于:
和不同于:
完全不确定 Rails 和 SQLite 在这件事上的表现如何。我'我总是使用(在 MySQL 和 SQL-Server 中):
Changing
token
fromvarchar(255)
tochar(40)
may help.One other thing that bugs me is that you use double quotes:
"
I've always thought that
is different than:
and different than:
Not at all sure how Rails and SQLite behaves on this matter. I'm always using (in MySQL and SQL-Server):