1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2011-2020 Shaun McCance <shaunm@gnome.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: Shaun McCance <shaunm@gnome.org>
19 */
20
21 #include "config.h"
22
23 #include <glib/gi18n.h>
24 #include <sqlite3.h>
25
26 #include "yelp-sqlite-storage.h"
27
28 static void yelp_sqlite_storage_iface_init (YelpStorageInterface *iface);
29 static void yelp_sqlite_storage_finalize (GObject *object);
30 static void yelp_sqlite_storage_get_property (GObject *object,
31 guint prop_id,
32 GValue *value,
33 GParamSpec *pspec);
34 static void yelp_sqlite_storage_set_property (GObject *object,
35 guint prop_id,
36 const GValue *value,
37 GParamSpec *pspec);
38
39 static void yelp_sqlite_storage_update (YelpStorage *storage,
40 const gchar *doc_uri,
41 const gchar *full_uri,
42 const gchar *title,
43 const gchar *desc,
44 const gchar *icon,
45 const gchar *text);
46 static GVariant * yelp_sqlite_storage_search (YelpStorage *storage,
47 const gchar *doc_uri,
48 const gchar *text);
49 static gchar * yelp_sqlite_storage_get_root_title (YelpStorage *storage,
50 const gchar *doc_uri);
51 static void yelp_sqlite_storage_set_root_title (YelpStorage *storage,
52 const gchar *doc_uri,
53 const gchar *title);
54
55 typedef struct _YelpSqliteStoragePrivate YelpSqliteStoragePrivate;
56 struct _YelpSqliteStoragePrivate {
57 gchar *filename;
58 sqlite3 *db;
59 GMutex mutex;
60 };
61
62 enum {
63 PROP_0,
64 PROP_FILENAME
65 };
66
G_DEFINE_TYPE_WITH_CODE(YelpSqliteStorage,yelp_sqlite_storage,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (YELP_TYPE_STORAGE,yelp_sqlite_storage_iface_init)G_ADD_PRIVATE (YelpSqliteStorage))67 G_DEFINE_TYPE_WITH_CODE (YelpSqliteStorage, yelp_sqlite_storage, G_TYPE_OBJECT,
68 G_IMPLEMENT_INTERFACE (YELP_TYPE_STORAGE,
69 yelp_sqlite_storage_iface_init)
70 G_ADD_PRIVATE (YelpSqliteStorage) )
71
72 static void
73 yelp_sqlite_storage_finalize (GObject *object)
74 {
75 YelpSqliteStoragePrivate *priv =
76 yelp_sqlite_storage_get_instance_private (YELP_SQLITE_STORAGE (object));
77
78 if (priv->filename)
79 g_free (priv->filename);
80
81 if (priv->db)
82 sqlite3_close (priv->db);
83
84 g_mutex_clear (&priv->mutex);
85
86 G_OBJECT_CLASS (yelp_sqlite_storage_parent_class)->finalize (object);
87 }
88
89 static void
yelp_sqlite_storage_init(YelpSqliteStorage * storage)90 yelp_sqlite_storage_init (YelpSqliteStorage *storage)
91 {
92 YelpSqliteStoragePrivate *priv = yelp_sqlite_storage_get_instance_private (storage);
93 g_mutex_init (&priv->mutex);
94 }
95
96 static void
yelp_sqlite_storage_constructed(GObject * object)97 yelp_sqlite_storage_constructed (GObject *object)
98 {
99 int status;
100 sqlite3_stmt *stmt = NULL;
101 YelpSqliteStoragePrivate *priv =
102 yelp_sqlite_storage_get_instance_private (YELP_SQLITE_STORAGE (object));
103
104 if (priv->filename != NULL)
105 status = sqlite3_open (priv->filename, &(priv->db));
106 else
107 status = sqlite3_open (":memory:", &(priv->db));
108
109 if (status != SQLITE_OK)
110 return;
111
112 status = sqlite3_prepare_v2 (priv->db,
113 "create virtual table pages using fts4("
114 " doc_uri, lang, full_uri,"
115 " title, desc, icon, body"
116 ");",
117 -1, &stmt, NULL);
118 if (status != SQLITE_OK)
119 return;
120 sqlite3_step (stmt);
121 sqlite3_finalize (stmt);
122
123 status = sqlite3_prepare_v2 (priv->db,
124 "create table titles (doc_uri text, lang text, title text);",
125 -1, &stmt, NULL);
126 if (status != SQLITE_OK)
127 return;
128 sqlite3_step (stmt);
129 sqlite3_finalize (stmt);
130 }
131
132 static void
yelp_sqlite_storage_class_init(YelpSqliteStorageClass * klass)133 yelp_sqlite_storage_class_init (YelpSqliteStorageClass *klass)
134 {
135 GObjectClass *object_class = G_OBJECT_CLASS (klass);
136
137 object_class->constructed = yelp_sqlite_storage_constructed;
138 object_class->finalize = yelp_sqlite_storage_finalize;
139 object_class->get_property = yelp_sqlite_storage_get_property;
140 object_class->set_property = yelp_sqlite_storage_set_property;
141
142 g_object_class_install_property (object_class,
143 PROP_FILENAME,
144 g_param_spec_string ("filename",
145 "Database filename",
146 "The filename of the sqlite database",
147 NULL,
148 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
149 G_PARAM_STATIC_STRINGS));
150 }
151
152 static void
yelp_sqlite_storage_iface_init(YelpStorageInterface * iface)153 yelp_sqlite_storage_iface_init (YelpStorageInterface *iface)
154 {
155 iface->update = yelp_sqlite_storage_update;
156 iface->search = yelp_sqlite_storage_search;
157 iface->get_root_title = yelp_sqlite_storage_get_root_title;
158 iface->set_root_title = yelp_sqlite_storage_set_root_title;
159 }
160
161 YelpStorage *
yelp_sqlite_storage_new(const gchar * filename)162 yelp_sqlite_storage_new (const gchar *filename)
163 {
164 YelpStorage *storage;
165
166 storage = g_object_new (YELP_TYPE_SQLITE_STORAGE,
167 "filename", filename,
168 NULL);
169
170 return storage;
171 }
172
173 static void
yelp_sqlite_storage_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)174 yelp_sqlite_storage_get_property (GObject *object,
175 guint prop_id,
176 GValue *value,
177 GParamSpec *pspec)
178 {
179 YelpSqliteStoragePrivate *priv =
180 yelp_sqlite_storage_get_instance_private (YELP_SQLITE_STORAGE (object));
181
182 switch (prop_id) {
183 case PROP_FILENAME:
184 g_value_set_string (value, priv->filename);
185 break;
186 default:
187 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
188 break;
189 }
190 }
191
192 static void
yelp_sqlite_storage_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)193 yelp_sqlite_storage_set_property (GObject *object,
194 guint prop_id,
195 const GValue *value,
196 GParamSpec *pspec)
197 {
198 YelpSqliteStoragePrivate *priv =
199 yelp_sqlite_storage_get_instance_private (YELP_SQLITE_STORAGE (object));
200
201 switch (prop_id) {
202 case PROP_FILENAME:
203 priv->filename = g_value_dup_string (value);
204 break;
205 default:
206 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
207 break;
208 }
209 }
210
211 /******************************************************************************/
212
213 static void
yelp_sqlite_storage_update(YelpStorage * storage,const gchar * doc_uri,const gchar * full_uri,const gchar * title,const gchar * desc,const gchar * icon,const gchar * text)214 yelp_sqlite_storage_update (YelpStorage *storage,
215 const gchar *doc_uri,
216 const gchar *full_uri,
217 const gchar *title,
218 const gchar *desc,
219 const gchar *icon,
220 const gchar *text)
221 {
222 sqlite3_stmt *stmt = NULL;
223 YelpSqliteStoragePrivate *priv =
224 yelp_sqlite_storage_get_instance_private (YELP_SQLITE_STORAGE (storage));
225
226 g_mutex_lock (&priv->mutex);
227
228 sqlite3_prepare_v2 (priv->db,
229 "delete from pages where doc_uri = ? and lang = ? and full_uri = ?;",
230 -1, &stmt, NULL);
231 sqlite3_bind_text (stmt, 1, doc_uri, -1, SQLITE_TRANSIENT);
232 sqlite3_bind_text (stmt, 2, g_get_language_names()[0], -1, SQLITE_STATIC);
233 sqlite3_bind_text (stmt, 3, full_uri, -1, SQLITE_TRANSIENT);
234 sqlite3_step (stmt);
235 sqlite3_finalize (stmt);
236
237 sqlite3_prepare_v2 (priv->db,
238 "insert into pages (doc_uri, lang, full_uri, title, desc, icon, body)"
239 " values (?, ?, ?, ?, ?, ?, ?);",
240 -1, &stmt, NULL);
241 sqlite3_bind_text (stmt, 1, doc_uri, -1, SQLITE_TRANSIENT);
242 sqlite3_bind_text (stmt, 2, g_get_language_names()[0], -1, SQLITE_STATIC);
243 sqlite3_bind_text (stmt, 3, full_uri, -1, SQLITE_TRANSIENT);
244 sqlite3_bind_text (stmt, 4, title, -1, SQLITE_TRANSIENT);
245 sqlite3_bind_text (stmt, 5, desc, -1, SQLITE_TRANSIENT);
246 sqlite3_bind_text (stmt, 6, icon, -1, SQLITE_TRANSIENT);
247 sqlite3_bind_text (stmt, 7, text, -1, SQLITE_TRANSIENT);
248 sqlite3_step (stmt);
249 sqlite3_finalize (stmt);
250
251 g_mutex_unlock (&priv->mutex);
252 }
253
254 static GVariant *
yelp_sqlite_storage_search(YelpStorage * storage,const gchar * doc_uri,const gchar * text)255 yelp_sqlite_storage_search (YelpStorage *storage,
256 const gchar *doc_uri,
257 const gchar *text)
258 {
259 sqlite3_stmt *stmt = NULL;
260 GVariantBuilder builder;
261 GVariant *ret;
262 YelpSqliteStoragePrivate *priv =
263 yelp_sqlite_storage_get_instance_private (YELP_SQLITE_STORAGE (storage));
264
265
266 g_mutex_lock (&priv->mutex);
267
268 sqlite3_prepare_v2 (priv->db,
269 "select full_uri, title, desc, icon from pages where"
270 " doc_uri = ? and lang = ? and body match ?;",
271 -1, &stmt, NULL);
272 sqlite3_bind_text (stmt, 1, doc_uri, -1, SQLITE_TRANSIENT);
273 sqlite3_bind_text (stmt, 2, g_get_language_names()[0], -1, SQLITE_STATIC);
274 sqlite3_bind_text (stmt, 3, text, -1, SQLITE_TRANSIENT);
275
276 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ssss)"));
277 while (sqlite3_step (stmt) == SQLITE_ROW) {
278 g_variant_builder_add (&builder, "(ssss)",
279 sqlite3_column_text (stmt, 0),
280 sqlite3_column_text (stmt, 1),
281 sqlite3_column_text (stmt, 2),
282 sqlite3_column_text (stmt, 3));
283 }
284 sqlite3_finalize (stmt);
285 ret = g_variant_new ("a(ssss)", &builder);
286
287 g_mutex_unlock (&priv->mutex);
288
289 return ret;
290 }
291
292 static gchar *
yelp_sqlite_storage_get_root_title(YelpStorage * storage,const gchar * doc_uri)293 yelp_sqlite_storage_get_root_title (YelpStorage *storage,
294 const gchar *doc_uri)
295 {
296 gchar *ret = NULL;
297 sqlite3_stmt *stmt = NULL;
298 YelpSqliteStoragePrivate *priv =
299 yelp_sqlite_storage_get_instance_private (YELP_SQLITE_STORAGE (storage));
300
301 g_mutex_lock (&priv->mutex);
302
303 sqlite3_prepare_v2 (priv->db,
304 "select title from titles where doc_uri = ? and lang = ?;",
305 -1, &stmt, NULL);
306 sqlite3_bind_text (stmt, 1, doc_uri, -1, SQLITE_TRANSIENT);
307 sqlite3_bind_text (stmt, 2, g_get_language_names()[0], -1, SQLITE_STATIC);
308 if (sqlite3_step (stmt) == SQLITE_ROW)
309 ret = g_strdup ((const gchar *) sqlite3_column_text (stmt, 0));
310 sqlite3_finalize (stmt);
311
312 g_mutex_unlock (&priv->mutex);
313 return ret;
314 }
315
316 static void
yelp_sqlite_storage_set_root_title(YelpStorage * storage,const gchar * doc_uri,const gchar * title)317 yelp_sqlite_storage_set_root_title (YelpStorage *storage,
318 const gchar *doc_uri,
319 const gchar *title)
320 {
321 sqlite3_stmt *stmt = NULL;
322 YelpSqliteStoragePrivate *priv =
323 yelp_sqlite_storage_get_instance_private (YELP_SQLITE_STORAGE (storage));
324
325 g_mutex_lock (&priv->mutex);
326
327 sqlite3_prepare_v2 (priv->db,
328 "delete from titles where doc_uri = ? and lang = ?;",
329 -1, &stmt, NULL);
330 sqlite3_bind_text (stmt, 1, doc_uri, -1, SQLITE_TRANSIENT);
331 sqlite3_bind_text (stmt, 2, g_get_language_names()[0], -1, SQLITE_STATIC);
332 sqlite3_step (stmt);
333 sqlite3_finalize (stmt);
334
335 sqlite3_prepare_v2 (priv->db,
336 "insert into titles (doc_uri, lang, title)"
337 " values (?, ?, ?);",
338 -1, &stmt, NULL);
339 sqlite3_bind_text (stmt, 1, doc_uri, -1, SQLITE_TRANSIENT);
340 sqlite3_bind_text (stmt, 2, g_get_language_names()[0], -1, SQLITE_STATIC);
341 sqlite3_bind_text (stmt, 3, title, -1, SQLITE_TRANSIENT);
342 sqlite3_step (stmt);
343 sqlite3_finalize (stmt);
344
345 g_mutex_unlock (&priv->mutex);
346 }
347