IMDb SQLite class: increase the arbitrary result limit to 100
[cinaest] / src / imdb / imdb-sqlite.vala
1 /* This file is part of Cinaest.
2  *
3  * Copyright (C) 2009 Philipp Zabel
4  *
5  * Cinaest is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * Cinaest 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
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Cinaest. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 using Sqlite;
20
21 class IMDbSqlite : Object {
22         Database db;
23         List<string> genres;
24
25         public delegate void ReceiveMovieFunction (string title, int year, int rating);
26
27         public IMDbSqlite (string filename) {
28                 int rc;
29
30                 genres = new List<string> ();
31
32                 rc = Database.open (filename, out db);
33                 if (rc != Sqlite.OK) {
34                         stderr.printf ("Can't open database: %d, %s\n", rc, db.errmsg ());
35                         return;
36                 }
37
38                 rc = db.exec ("PRAGMA journal_mode = OFF;", callback, null);
39                 if (rc != Sqlite.OK) {
40                         stderr.printf ("Can't turn off journal mode: %d, %s\n", rc, db.errmsg ());
41                         return;
42                 }
43
44                 rc = db.exec ("PRAGMA locking_mode = EXCLUSIVE;", callback, null);
45                 if (rc != Sqlite.OK) {
46                         stderr.printf ("Can't get exclusive lock: %d, %s\n", rc, db.errmsg ());
47                         return;
48                 }
49
50                 rc = db.exec ("PRAGMA synchronous = OFF;", callback, null);
51                 if (rc != Sqlite.OK)
52                         stderr.printf ("Can't turn off synchronous access: %d, %s\n", rc, db.errmsg ());
53
54         }
55
56         public static int callback (int n_columns, string[] values,
57                                     string[] column_names) {
58                 for (int i = 0; i < n_columns; i++) {
59                         stdout.printf ("%s = %s\n", column_names[i], values[i]);
60                 }
61                 stdout.printf ("\n");
62
63                 return 0;
64         }
65
66         public int add_movie (string _title, int year) {
67                 string md5 = Checksum.compute_for_string (ChecksumType.MD5, _title);
68                 uint64 half_md5 = (md5.ndup (16)).to_uint64 (null, 16);
69
70                 string title = strip_year (_title, year);
71
72                 string sql = "INSERT INTO Movies(ID, Title, Year) VALUES (%lld, \"%s\", %d);".printf ((int64) half_md5, title, year);
73                 int rc;
74
75                 rc = db.exec (sql, callback, null);
76                 if (rc != Sqlite.OK) {
77                         stderr.printf ("Failed to insert movie \"%s\" (%d): %d, %s\n", title, year, rc, db.errmsg ());
78                         return 1;
79                 }
80
81                 return 0;
82         }
83
84         private string strip_year (string title, int year) {
85                 string year_suffix = " (%d)".printf (year);
86                 if (title.has_suffix (year_suffix)) {
87                         return title.substring (0, title.length - year_suffix.length);
88                 } else {
89                         return title.dup ();
90                 }
91         }
92
93         public int movie_set_rating (string title, int rating) {
94                 string md5 = Checksum.compute_for_string (ChecksumType.MD5, title);
95                 uint64 half_md5 = (md5.ndup (16)).to_uint64 (null, 16);
96
97                 var sql = "UPDATE Movies SET Rating=%d WHERE ID=%lld;".printf (rating, (int64) half_md5);
98                 int rc;
99
100                 rc = db.exec (sql, callback, null);
101                 if (rc != Sqlite.OK) {
102                         stderr.printf ("SQL error: %d, %s\n", rc, db.errmsg ());
103                         return 1;
104                 }
105
106                 return 0;
107         }
108
109         public int movie_add_genre (string title, string genre) {
110                 string md5 = Checksum.compute_for_string (ChecksumType.MD5, title);
111                 uint64 half_md5 = (md5.ndup (16)).to_uint64 (null, 16);
112                 string sql;
113                 int bit;
114                 int rc;
115
116                 bit = genre_bit (genre);
117                 if (bit == 0) {
118                         genres.append (genre);
119                         bit = genre_bit (genre);
120
121                         sql = "INSERT INTO Genres(Bit, Genre) VALUES (%d, \"%s\");".printf (bit, genre);
122
123                         rc = db.exec (sql, callback, null);
124                         if (rc != Sqlite.OK) {
125                                 stderr.printf ("SQL error: %d, %s\n", rc, db.errmsg ());
126                                 return 1;
127                         }
128                 }
129
130                 sql = "UPDATE Movies SET Genres=Genres|%d WHERE ID=%lld;".printf (bit, (int64) half_md5);
131                 rc = db.exec (sql, callback, null);
132                 if (rc != Sqlite.OK) {
133                         stderr.printf ("SQL error: %d, %s\n", rc, db.errmsg ());
134                         return 1;
135                 }
136
137                 return 0;
138         }
139
140         int genre_bit (string genre) {
141                 for (int i = 0; i < genres.length (); i++) {
142                         if (genres.nth_data (i) == genre)
143                                 return 1 << i;
144                 }
145                 return 0;
146         }
147
148         public int clear () {
149                 int rc;
150
151                 rc = db.exec ("DROP TABLE IF EXISTS Movies; CREATE TABLE Movies (ID INTEGER PRIMARY KEY, Title TEXT NOT NULL, Year INTEGER, Rating INTEGER, Genres INTEGER NOT NULL DEFAULT 0); DROP TABLE IF EXISTS Genres; CREATE TABLE Genres (Bit INTEGER PRIMARY KEY, Genre TEXT NOT NULL);", callback, null);
152                 if (rc != Sqlite.OK) {
153                         stderr.printf ("SQL error: %d, %s\n", rc, db.errmsg ());
154                         return 1;
155                 }
156
157                 return 0;
158         }
159
160         public int query (string filter, ReceiveMovieFunction receive_movie) {
161                 var sql = "SELECT Title, Year, Rating FROM Movies WHERE Title LIKE \"%s%%\" LIMIT 100;".printf (filter);
162                 Statement stmt;
163                 int rc;
164
165                 rc = db.prepare_v2 (sql, -1, out stmt);
166                 if (rc != Sqlite.OK) {
167                         stderr.printf ("SQL error: %d, %s\n", rc, db.errmsg ());
168                         return 1;
169                 }
170
171                 do {
172                         rc = stmt.step ();
173                         if (rc == Sqlite.ROW) {
174                                 string title = stmt.column_text (0);
175                                 int year = stmt.column_int (1);
176                                 int rating = stmt.column_int (2);
177                                 receive_movie (title, year, rating);
178                         }
179                 } while (rc == Sqlite.ROW);
180
181                 return 0;
182         }
183 }