OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "components/bookmarks_enhanced/persistent_image_store.h" | |
6 | |
7 #include "components/bookmarks_enhanced/image_store_util.h" | |
8 #include "sql/statement.h" | |
9 #include "sql/transaction.h" | |
10 #include "ui/gfx/geometry/size.h" | |
11 #include "url/gurl.h" | |
12 | |
13 namespace { | |
14 | |
15 bool InitTables(sql::Connection& db) { | |
16 const char kTableSql[] = | |
17 "CREATE TABLE IF NOT EXISTS images_by_url (" | |
18 " page_url LONGVARCHAR NOT NULL," | |
19 " image_url LONGVARCHAR NOT NULL," | |
20 " image_data BLOB," | |
21 " width INTEGER," | |
22 " height INTEGER" | |
23 ")"; | |
24 if (!db.Execute(kTableSql)) | |
25 return false; | |
26 return true; | |
27 } | |
28 | |
29 bool InitIndices(sql::Connection& db) { | |
30 const char kIndexSql[] = | |
31 "CREATE INDEX IF NOT EXISTS images_by_url_idx ON images_by_url(page_url)"; | |
32 if (!db.Execute(kIndexSql)) | |
33 return false; | |
34 return true; | |
35 } | |
36 | |
37 sql::InitStatus OpenDatabaseImpl(sql::Connection& db, | |
38 const base::FilePath& db_path) { | |
39 DCHECK(!db.is_open()); | |
40 | |
41 db.set_histogram_tag("BookmarkImages"); | |
42 // TODO(noyau): Set page and cache sizes? | |
43 // TODO(noyau): Set error callback? | |
44 | |
45 // Run the database in exclusive mode. Nobody else should be accessing the | |
46 // database while we're running, and this will give somewhat improved perf. | |
47 db.set_exclusive_locking(); | |
48 | |
49 if (!db.Open(db_path)) | |
50 return sql::INIT_FAILURE; | |
51 | |
52 // Scope initialization in a transaction so we can't be partially initialized. | |
53 sql::Transaction transaction(&db); | |
54 if (!transaction.Begin()) | |
55 return sql::INIT_FAILURE; | |
56 | |
57 // Create the tables. | |
58 if (!InitTables(db) || | |
59 !InitIndices(db)) { | |
60 return sql::INIT_FAILURE; | |
61 } | |
62 | |
63 // Initialization is complete. | |
64 if (!transaction.Commit()) | |
65 return sql::INIT_FAILURE; | |
66 | |
67 return sql::INIT_OK; | |
68 } | |
69 | |
70 } // namespace | |
71 | |
72 PersistentImageStore::PersistentImageStore(const base::FilePath& path) | |
73 : ImageStore(), path_(path.Append("BookmarkImageAndUrlStore.db")) {} | |
74 | |
75 PersistentImageStore::~PersistentImageStore() { | |
sky
2014/05/06 16:14:20
Order should match header.
Kibeom Kim (inactive)
2014/05/07 00:09:46
Done.
| |
76 DCHECK(thread_checker_.CalledOnValidThread()); | |
77 } | |
78 | |
79 bool PersistentImageStore::HasKey(const GURL& page_url) { | |
80 DCHECK(thread_checker_.CalledOnValidThread()); | |
81 | |
82 OpenDatabase(); | |
83 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
84 "SELECT COUNT(*) FROM images_by_url WHERE page_url = ?")); | |
85 statement.BindString(0, page_url.possibly_invalid_spec()); | |
86 | |
87 int count = statement.Step() ? statement.ColumnInt(0) : 0; | |
88 | |
89 return !!count; | |
90 } | |
91 | |
92 void PersistentImageStore::Insert(const GURL& page_url, | |
93 const GURL& image_url, | |
94 const gfx::Image& image) { | |
95 DCHECK(thread_checker_.CalledOnValidThread()); | |
96 | |
97 Erase(page_url); // Remove previous image for this url, if any. | |
98 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
99 "INSERT INTO images_by_url " | |
100 " (page_url, image_url, image_data, width, height)" | |
101 " VALUES (?, ?, ?, ?, ?)")); | |
102 | |
103 statement.BindString(0, page_url.possibly_invalid_spec()); | |
104 statement.BindString(1, image_url.possibly_invalid_spec()); | |
105 | |
106 scoped_refptr<base::RefCountedMemory> data = | |
107 image_store_util::BytesForImage(image); | |
108 statement.BindBlob(2, data->front(), data->size()); | |
109 | |
110 statement.BindInt64(3, image.Size().width()); | |
111 statement.BindInt64(4, image.Size().height()); | |
112 statement.Run(); | |
113 } | |
114 | |
115 void PersistentImageStore::Erase(const GURL& page_url) { | |
116 DCHECK(thread_checker_.CalledOnValidThread()); | |
117 | |
118 OpenDatabase(); | |
119 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
120 "DELETE FROM images_by_url WHERE page_url = ?")); | |
121 statement.BindString(0, page_url.possibly_invalid_spec()); | |
122 statement.Run(); | |
123 } | |
124 | |
125 gfx::Image PersistentImageStore::Get(const GURL& page_url, GURL* image_url) { | |
126 DCHECK(thread_checker_.CalledOnValidThread()); | |
127 DCHECK(image_url); | |
128 | |
129 OpenDatabase(); | |
130 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
131 "SELECT image_data, image_url FROM images_by_url WHERE page_url = ?")); | |
132 | |
133 statement.BindString(0, page_url.possibly_invalid_spec()); | |
134 | |
135 while (statement.Step()) { | |
136 if (statement.ColumnByteLength(0) > 0) { | |
137 scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes()); | |
138 statement.ColumnBlobAsVector(0, &data->data()); | |
139 *image_url = GURL(statement.ColumnString(1)); | |
140 return image_store_util::ImageForBytes(data); | |
141 } | |
142 } | |
143 return gfx::Image(); | |
144 } | |
145 | |
146 gfx::Size PersistentImageStore::GetSize(const GURL& page_url) { | |
147 DCHECK(thread_checker_.CalledOnValidThread()); | |
148 | |
149 OpenDatabase(); | |
150 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
151 "SELECT width, height FROM images_by_url WHERE page_url = ?")); | |
152 | |
153 statement.BindString(0, page_url.possibly_invalid_spec()); | |
154 | |
155 while (statement.Step()) { | |
156 if (statement.ColumnByteLength(0) > 0) { | |
157 int64 width = statement.ColumnInt64(0); | |
sky
2014/05/06 16:14:20
Why the int64? gfx::Size is only int.
Kibeom Kim (inactive)
2014/05/07 00:09:46
Done. Also added GetSize() check in TYPED_TEST(Ima
| |
158 int64 height = statement.ColumnInt64(1); | |
159 return gfx::Size(width, height); | |
160 } | |
161 } | |
162 return gfx::Size(); | |
163 } | |
164 | |
165 void PersistentImageStore::GetAllPageUrls(std::vector<GURL>* urls) { | |
166 DCHECK(thread_checker_.CalledOnValidThread()); | |
167 DCHECK(urls->empty()); | |
168 | |
169 OpenDatabase(); | |
sky
2014/05/06 16:14:20
Shouldn't you check the status every where?
Kibeom Kim (inactive)
2014/05/07 00:09:46
Actually, we just crash on OpenDatabase failure (#
sky
2014/05/08 15:33:08
Isn't it possible that openning the database can f
Kibeom Kim (inactive)
2014/05/08 17:56:33
Done. (I will add failure returns for public funct
Scott Hess - ex-Googler
2014/05/09 17:51:36
Random peanut-gallery comment: When adding in erro
| |
170 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
171 "SELECT page_url FROM images_by_url")); | |
172 while (statement.Step()) | |
173 urls->push_back(GURL(statement.ColumnString(0))); | |
174 } | |
175 | |
176 sql::InitStatus PersistentImageStore::OpenDatabase() { | |
177 DCHECK(thread_checker_.CalledOnValidThread()); | |
178 | |
179 if (db_.is_open()) | |
180 return sql::INIT_OK; | |
181 | |
182 const size_t kAttempts = 2; | |
183 | |
184 sql::InitStatus status = sql::INIT_FAILURE; | |
185 for (size_t i = 0; i < kAttempts; ++i) { | |
186 status = OpenDatabaseImpl(db_, path_); | |
187 if (status == sql::INIT_OK) | |
188 return status; | |
189 | |
190 // Can't open, raze(). | |
191 if (db_.is_open()) | |
192 db_.Raze(); | |
193 db_.Close(); | |
194 } | |
195 CHECK(false) << "Can't open image DB"; | |
196 return sql::INIT_FAILURE; | |
197 } | |
OLD | NEW |