Есть ли причина, по которой я выбираю строковые столбцы, содержащие шестнадцатеричные SHA1? - PullRequest
3 голосов
/ 13 июля 2011

У меня есть таблица приглашений, которая выглядит следующим образом

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> 

Ответы [ 2 ]

5 голосов
/ 13 июля 2011

Я обсуждал это с duckyfuzz (OP) в чате, и мы обнаружили, что хеш хранится как BLOB в sqlite:

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

По какой-то причине, хотя ruby ​​говорит, что вставляемая строка является строкой:

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

это заканчивается в sqlite как BLOB.

Интерполяция значения или вставка в виде литерала вместо вставки с использованием параметров устраняет проблему (как проверено 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!

Проблема вызвана тем, что двоичные строки будут храниться в виде больших двоичных объектов в rubygem sqlite3. Чтобы избежать этого, нужно кодировать хеш как UTF-8 перед вставкой.

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

Как только вы это сделаете, хеш будет сохранен как текст.

0 голосов
/ 13 июля 2011

Изменение token с varchar(255) на char(40) может помочь.


Еще одна вещь, которая меня беспокоит, это то, что вы используете двойные кавычки: "

Я всегда думал, что

select *
from "invitations" 
where "invitations"."token" = "e8c6ab9d5d1df98c906952c58e1be4d640d3c5ae"

отличается от:

select *
from 'invitations' 
where 'invitations'.'token' = 'e8c6ab9d5d1df98c906952c58e1be4d640d3c5ae'

и отличается от:

select *
from `invitations` 
where `invitations`.token` = `e8c6ab9d5d1df98c906952c58e1be4d640d3c5ae`

Не совсем уверен, как Rails и SQLite ведут себя в этом вопросе. Я всегда использую (в MySQL и SQL-Server):

select *
from invitations 
where invitations.token = 'e8c6ab9d5d1df98c906952c58e1be4d640d3c5ae'
...