ec9f90e796823e297038397487ca476213723c50
[lms] / lightmediascanner / src / lib / lightmediascanner_db_playlist.c
1 #include <lightmediascanner_db.h>
2 #include "lightmediascanner_db_private.h"
3 #include <stdlib.h>
4 #include <stdio.h>
5
6 struct lms_db_playlist {
7     sqlite3 *db;
8     sqlite3_stmt *insert;
9     unsigned int _references;
10     unsigned int _is_started:1;
11 };
12
13 static struct lms_db_cache _cache = {0, NULL};
14
15 static int
16 _db_table_updater_playlists_0(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run) {
17     char *errmsg;
18     int r, ret;
19
20     errmsg = NULL;
21     r = sqlite3_exec(db,
22                      "CREATE TABLE IF NOT EXISTS playlists ("
23                      "id INTEGER PRIMARY KEY, "
24                      "title TEXT, "
25                      "n_entries INTEGER NOT NULL"
26                      ")",
27                      NULL, NULL, &errmsg);
28     if (r != SQLITE_OK) {
29         fprintf(stderr, "ERROR: could not create 'playlists' table: %s\n",
30                 errmsg);
31         sqlite3_free(errmsg);
32         return -1;
33     }
34
35     r = sqlite3_exec(db,
36                      "CREATE INDEX IF NOT EXISTS playlists_title_idx ON "
37                      "playlists (title)",
38                      NULL, NULL, &errmsg);
39     if (r != SQLITE_OK) {
40         fprintf(stderr,
41                 "ERROR: could not create 'playlists_title_idx' index: %s\n",
42                 errmsg);
43         sqlite3_free(errmsg);
44         return -2;
45     }
46
47     ret = lms_db_create_trigger_if_not_exists(db,
48         "delete_playlists_on_files_deleted "
49         "DELETE ON files FOR EACH ROW BEGIN "
50         " DELETE FROM playlists WHERE id = OLD.id; END;");
51     if (ret != 0)
52         goto done;
53
54     ret = lms_db_create_trigger_if_not_exists(db,
55         "delete_files_on_playlists_deleted "
56         "DELETE ON playlists FOR EACH ROW BEGIN "
57         " DELETE FROM files WHERE id = OLD.id; END;");
58
59   done:
60     return ret;
61 }
62
63 static lms_db_table_updater_t _db_table_updater_playlists[] = {
64     _db_table_updater_playlists_0
65 };
66
67
68 static int
69 _db_create_table_if_required(sqlite3 *db)
70 {
71     return lms_db_table_update_if_required(db, "playlists",
72          LMS_ARRAY_SIZE(_db_table_updater_playlists),
73          _db_table_updater_playlists);
74 }
75
76 /**
77  * Create playlist DB access tool.
78  *
79  * Creates or get a reference to tools to access 'playlists' table in an
80  * optimized and easy way.
81  *
82  * This is usually called from plugin's @b setup() callback with the @p db
83  * got from @c ctxt.
84  *
85  * @param db database connection.
86  *
87  * @return DB access tool handle.
88  * @ingroup LMS_Plugins
89  */
90 lms_db_playlist_t *
91 lms_db_playlist_new(sqlite3 *db)
92 {
93     lms_db_playlist_t *ldp;
94     void *p;
95
96     if (lms_db_cache_get(&_cache, db, &p) == 0) {
97         ldp = p;
98         ldp->_references++;
99         return ldp;
100     }
101
102     if (!db)
103         return NULL;
104
105     if (_db_create_table_if_required(db) != 0) {
106         fprintf(stderr, "ERROR: could not create table.\n");
107         return NULL;
108     }
109
110     ldp = calloc(1, sizeof(lms_db_playlist_t));
111     ldp->_references = 1;
112     ldp->db = db;
113
114     if (lms_db_cache_add(&_cache, db, ldp) != 0) {
115         lms_db_playlist_free(ldp);
116         return NULL;
117     }
118
119     return ldp;
120 }
121
122 /**
123  * Start playlist DB access tool.
124  *
125  * Compile SQL statements and other initialization functions.
126  *
127  * This is usually called from plugin's @b start() callback.
128  *
129  * @param ldp handle returned by lms_db_playlist_new().
130  *
131  * @return On success 0 is returned.
132  * @ingroup LMS_Plugins
133  */
134 int
135 lms_db_playlist_start(lms_db_playlist_t *ldp)
136 {
137     if (!ldp)
138         return -1;
139     if (ldp->_is_started)
140         return 0;
141
142     ldp->insert = lms_db_compile_stmt(ldp->db,
143         "INSERT OR REPLACE INTO playlists (id, title, n_entries) "
144         "VALUES (?, ?, ?)");
145     if (!ldp->insert)
146         return -2;
147
148     ldp->_is_started = 1;
149     return 0;
150 }
151
152 /**
153  * Free playlist DB access tool.
154  *
155  * Unreference and possible free resources allocated to access tool.
156  *
157  * This is usually called from plugin's @b finish() callback.
158  *
159  * @param ldp handle returned by lms_db_playlist_new().
160  *
161  * @return On success 0 is returned.
162  * @ingroup LMS_Plugins
163  */
164 int
165 lms_db_playlist_free(lms_db_playlist_t *ldp)
166 {
167     int r;
168
169     if (!ldp)
170         return -1;
171     if (ldp->_references == 0) {
172         fprintf(stderr, "ERROR: over-called lms_db_playlist_free(%p)\n", ldp);
173         return -1;
174     }
175
176     ldp->_references--;
177     if (ldp->_references > 0)
178         return 0;
179
180     if (ldp->insert)
181         lms_db_finalize_stmt(ldp->insert, "insert");
182
183     r = lms_db_cache_del(&_cache, ldp->db, ldp);
184     free(ldp);
185
186     return r;
187 }
188
189 static int
190 _db_insert(lms_db_playlist_t *ldp, const struct lms_playlist_info *info)
191 {
192     sqlite3_stmt *stmt;
193     int r, ret;
194
195     stmt = ldp->insert;
196
197     ret = lms_db_bind_int64(stmt, 1, info->id);
198     if (ret != 0)
199         goto done;
200
201     ret = lms_db_bind_text(stmt, 2, info->title.str, info->title.len);
202     if (ret != 0)
203         goto done;
204
205     ret = lms_db_bind_int(stmt, 3, info->n_entries);
206     if (ret != 0)
207         goto done;
208
209     r = sqlite3_step(stmt);
210     if (r != SQLITE_DONE) {
211         fprintf(stderr, "ERROR: could not insert playlist info: %s\n",
212                 sqlite3_errmsg(ldp->db));
213         ret = -4;
214         goto done;
215     }
216
217     ret = 0;
218
219   done:
220     lms_db_reset_stmt(stmt);
221
222     return ret;
223 }
224
225 /**
226  * Add playlist file to DB.
227  *
228  * This is usually called from plugin's @b parse() callback.
229  *
230  * @param ldp handle returned by lms_db_playlist_new().
231  * @param info playlist information to store.
232  *
233  * @return On success 0 is returned.
234  * @ingroup LMS_Plugins
235  */
236 int
237 lms_db_playlist_add(lms_db_playlist_t *ldp, struct lms_playlist_info *info)
238 {
239     if (!ldp)
240         return -1;
241     if (!info)
242         return -2;
243     if (info->id < 1)
244         return -3;
245
246     return _db_insert(ldp, info);
247 }