sql - 我是否有理由选择包含 SHA1 十六进制摘要的字符串列?

标签 sql sqlite sqlite3-ruby

我有一个如下所示的邀请表

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");

token 列存储在记录创建时生成的十六进制摘要,如下所示(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 看来我只能使用 ruby​​gem“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> 

最佳答案

我在聊天中与 duckyfuzz(OP)讨论了这个问题,我们发现哈希值在 sqlite 中存储为 BLOB:

sqlite> select typeof(token) from invitations; 
blob 
blob

由于某种原因,即使 ruby​​ 说插入的是一个字符串:

irb(main):002:0> (Digest::SHA1.hexdigest("banana")).class() 
=> String

它最终以 BLOB 的形式出现在 sqlite 中。

插值值,或作为文字插入,而不是使用参数插入,可以使问题消失(经OP测试):

oh ok got it
ruby-1.9.2-head :010 > db.execute("insert into invitations (token) VALUES ('#{the_hash}')") 
=> []
ok now the dump..

INSERT INTO "invitations" VALUES('bda04628ea94f26cac0793eac103258eb515c505');
much better!

该问题是由 binary strings will be stored as blobs 引起的由 sqlite3 ruby​​gem 提供。防止这种情况的方法是在插入之前将哈希编码为 UTF-8。

hash = Digest::SHA1.hexdigest("banana").encode("UTF-8")
db.execute("insert into invitations (token) values (?)", hash)

完成此操作后,哈希值将存储为文本。

关于sql - 我是否有理由选择包含 SHA1 十六进制摘要的字符串列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6674644/

相关文章:

ruby - SQLite3::Database 和 SQLite3::Statement 的初始化方法在哪里

Ruby/SQLite3 结果作为散列返回重复数据

mysql - 获取最近记录百分比的差异

mysql - 表条目限制

postgresql - 使用 Sinatra + DataMapper + Postgres + Heroku 部署一个简单的测试应用程序返回 : Installing do_sqlite3 (0. 10.7) 错误

android - 编译时在 "NOT": syntax error: , 附近:如果不存在则创建虚拟表

mysql - 选择一个数字范围

sql - SQL语句中选择比较结果

python - 如何在python中使用最新的sqlite3版本

ruby-on-rails - 获取数据库中存在的任何用户的任何id