~ubuntu-branches/ubuntu/quantal/zeitgeist/quantal

« back to all changes in this revision

Viewing changes to src/sql.vala

  • Committer: Package Import Robot
  • Author(s): Didier Roche
  • Date: 2011-11-15 11:15:56 UTC
  • mto: (6.2.2 experimental) (1.3.1)
  • mto: This revision was merged to the branch mainline in revision 19.
  • Revision ID: package-import@ubuntu.com-20111115111556-so7cmhfbqongw7hf
Tags: upstream-0.8.99~alpha1
Import upstream version 0.8.99~alpha1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* sql.vala
 
2
 *
 
3
 * Copyright © 2011 Collabora Ltd.
 
4
 *             By Siegfried-Angel Gevatter Pujals <siegfried@gevatter.com>
 
5
 *             By Seif Lotfy <seif@lotfy.com>
 
6
 * Copyright © 2011 Manish Sinha <manishsinha@ubuntu.com>
 
7
 *
 
8
 * This program is free software: you can redistribute it and/or modify
 
9
 * it under the terms of the GNU Lesser General Public License as published by
 
10
 * the Free Software Foundation, either version 2.1 of the License, or
 
11
 * (at your option) any later version.
 
12
 *
 
13
 * This program is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 * GNU General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU Lesser General Public License
 
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
 *
 
21
 */
 
22
 
 
23
using Zeitgeist;
 
24
 
 
25
namespace Zeitgeist.SQLite
 
26
{
 
27
 
 
28
    public enum EventViewRows
 
29
    {
 
30
        ID,
 
31
        TIMESTAMP,
 
32
        INTERPRETATION,
 
33
        MANIFESTATION,
 
34
        ACTOR,
 
35
        PAYLOAD,
 
36
        SUBJECT_URI,
 
37
        SUBJECT_ID,
 
38
        SUBJECT_INTERPRETATION,
 
39
        SUBJECT_MANIFESTATION,
 
40
        SUBJECT_ORIGIN,
 
41
        SUBJECT_ORIGIN_URI,
 
42
        SUBJECT_MIMETYPE,
 
43
        SUBJECT_TEXT,
 
44
        SUBJECT_STORAGE,
 
45
        SUBJECT_STORAGE_STATE,
 
46
        ORIGIN,
 
47
        EVENT_ORIGIN_URI,
 
48
        SUBJECT_CURRENT_URI,
 
49
        SUBJECT_ID_CURRENT
 
50
    }
 
51
 
 
52
    public delegate void DeletionCallback (string table, int64 rowid);
 
53
 
 
54
    public class ZeitgeistDatabase : Object
 
55
    {
 
56
 
 
57
        public Sqlite.Statement event_insertion_stmt;
 
58
        public Sqlite.Statement id_retrieval_stmt;
 
59
        public Sqlite.Statement move_handling_stmt;
 
60
        public Sqlite.Statement payload_insertion_stmt;
 
61
 
 
62
        // The DB should be accessible from engine for statement preperations
 
63
        //  as well as allowing extensions to add tables to it.
 
64
        public Sqlite.Database database;
 
65
 
 
66
        private DeletionCallback? deletion_callback = null;
 
67
 
 
68
        public ZeitgeistDatabase () throws EngineError
 
69
        {
 
70
            int rc = Sqlite.Database.open_v2 (
 
71
                Utils.get_database_file_path (),
 
72
                out database);
 
73
            assert_query_success (rc, "Can't open database");
 
74
 
 
75
            DatabaseSchema.ensure_schema (database);
 
76
 
 
77
            prepare_queries ();
 
78
 
 
79
            // Register a data change notification callback to look for
 
80
            // deletions, so we can keep the TableLookups up to date.
 
81
            database.update_hook (update_callback);
 
82
        }
 
83
 
 
84
        public uint32 get_last_id () throws EngineError
 
85
        {
 
86
            int last_id = -1;
 
87
            int rc = database.exec ("SELECT MAX(id) FROM event",
 
88
                (n_columns, values, column_names) =>
 
89
                {
 
90
                    if (values[0] == null)
 
91
                        last_id = 0;
 
92
                    else
 
93
                        last_id = int.parse (values[0]);
 
94
                    return 0;
 
95
                }, null);
 
96
            assert_query_success (rc, "Can't query database");
 
97
            assert (last_id != -1);
 
98
            return last_id;
 
99
        }
 
100
 
 
101
        public void set_deletion_callback (DeletionCallback? callback)
 
102
        {
 
103
            deletion_callback = callback;
 
104
        }
 
105
 
 
106
        /**
 
107
         * Join all given event_ids into a comma-separated string suitable
 
108
         * for use in a SQL query like "WHERE id IN (...)".
 
109
         */
 
110
        public string get_sql_string_from_event_ids (uint32[] event_ids)
 
111
            requires (event_ids.length > 0)
 
112
        {
 
113
            var sql_condition = new StringBuilder ();
 
114
            sql_condition.append_printf ("%u", event_ids[0]);
 
115
            for (int i = 1; i < event_ids.length; ++i) {
 
116
                sql_condition.append_printf (", %u", event_ids[i]);
 
117
            }
 
118
            return sql_condition.str;
 
119
        }
 
120
 
 
121
        public TimeRange? get_time_range_for_event_ids (uint32[] event_ids)
 
122
            throws EngineError
 
123
        {
 
124
            if (event_ids.length == 0)
 
125
                return null;
 
126
 
 
127
            string sql = """
 
128
                SELECT MIN(timestamp), MAX(timestamp)
 
129
                FROM event
 
130
                WHERE id IN (%s)
 
131
                """.printf (get_sql_string_from_event_ids (event_ids));
 
132
 
 
133
            TimeRange? time_range = null;
 
134
            int rc = database.exec (sql,
 
135
                (n_columns, values, column_names) =>
 
136
                {
 
137
                    if (values[0] != null)
 
138
                    {
 
139
                        int64 start = int64.parse (values[0]);
 
140
                        int64 end = int64.parse (values[1]);
 
141
                        time_range = new TimeRange (start, end);
 
142
                    }
 
143
                    return 0;
 
144
                }, null);
 
145
            assert_query_success (rc, "SQL Error");
 
146
 
 
147
            return time_range;
 
148
        }
 
149
 
 
150
        public void insert_or_ignore_into_table (string table_name,
 
151
            GenericArray<string> values) throws EngineError
 
152
        {
 
153
            int rc;
 
154
 
 
155
            assert (values.length > 0);
 
156
            var sql = new StringBuilder ();
 
157
            sql.append ("INSERT OR IGNORE INTO ");
 
158
            sql.append (table_name);
 
159
            sql.append (" (value) SELECT ?");
 
160
            for (int i = 1; i < values.length; ++i)
 
161
                sql.append (" UNION SELECT ?");
 
162
 
 
163
            Sqlite.Statement stmt;
 
164
            rc = database.prepare_v2 (sql.str, -1, out stmt);
 
165
            assert_query_success (rc, "SQL error");
 
166
 
 
167
            for (int i = 0; i < values.length; ++i)
 
168
                stmt.bind_text (i+1, values[i]);
 
169
 
 
170
            rc = stmt.step ();
 
171
            assert_query_success (rc, "SQL error", Sqlite.DONE);
 
172
        }
 
173
 
 
174
        public void begin_transaction () throws EngineError
 
175
        {
 
176
            int rc = database.exec ("BEGIN");
 
177
            assert_query_success (rc, "Can't start transaction");
 
178
        }
 
179
 
 
180
        public void end_transaction () throws EngineError
 
181
        {
 
182
            int rc = database.exec ("COMMIT");
 
183
            assert_query_success (rc, "Can't commit transaction");
 
184
        }
 
185
 
 
186
        public void close ()
 
187
        {
 
188
            // SQLite connection is implicitly closed upon destruction
 
189
            database = null;
 
190
        }
 
191
 
 
192
        /**
 
193
         * Ensure `rc' is SQLITE_OK. If it isn't, print an error message
 
194
         * and throw an error.
 
195
         *
 
196
         * @param rc error code returned by a SQLite call
 
197
         * @param msg message to print if `rc' indicates an error
 
198
         * @throws EngineError
 
199
         */
 
200
        public void assert_query_success (int rc, string msg,
 
201
            int success_code=Sqlite.OK) throws EngineError
 
202
        {
 
203
            if (rc != success_code)
 
204
            {
 
205
                string error_message = "%s: %d, %s".printf(
 
206
                    msg, rc, database.errmsg ());
 
207
                warning ("%s\n", error_message);
 
208
                throw new EngineError.DATABASE_ERROR (error_message);
 
209
            }
 
210
        }
 
211
 
 
212
        private void prepare_queries () throws EngineError
 
213
        {
 
214
            int rc;
 
215
            string sql;
 
216
 
 
217
            // Event insertion statement
 
218
            sql = """
 
219
                INSERT INTO event (
 
220
                    id, timestamp, interpretation, manifestation, actor,
 
221
                    origin, payload, subj_id, subj_id_current,
 
222
                    subj_interpretation, subj_manifestation, subj_origin,
 
223
                    subj_mimetype, subj_text, subj_storage
 
224
                ) VALUES (
 
225
                    ?, ?, ?, ?, ?,
 
226
                    (SELECT id FROM uri WHERE value=?),
 
227
                    ?,
 
228
                    (SELECT id FROM uri WHERE value=?),
 
229
                    (SELECT id FROM uri WHERE value=?),
 
230
                    ?, ?,
 
231
                    (SELECT id FROM uri WHERE value=?),
 
232
                    ?,
 
233
                    (SELECT id FROM text WHERE value=?),
 
234
                    (SELECT id FROM storage WHERE value=?)
 
235
                )""";
 
236
 
 
237
            rc = database.prepare_v2 (sql, -1, out event_insertion_stmt);
 
238
            assert_query_success (rc, "Insertion query error");
 
239
 
 
240
            // Event ID retrieval statement
 
241
            sql = """
 
242
                SELECT id FROM event
 
243
                WHERE timestamp=? AND interpretation=? AND
 
244
                    manifestation=? AND actor=?
 
245
                """;
 
246
            rc = database.prepare_v2 (sql, -1, out id_retrieval_stmt);
 
247
            assert_query_success (rc, "Event ID retrieval query error");
 
248
 
 
249
            // Move handling statment
 
250
            sql = """
 
251
            UPDATE event
 
252
                SET subj_id_current=(SELECT id FROM uri WHERE value=?)
 
253
                    WHERE subj_id_current=(SELECT id FROM uri WHERE value=?)
 
254
                    AND interpretation!=? AND timestamp<?
 
255
            """;
 
256
            rc = database.prepare_v2 (sql, -1, out move_handling_stmt);
 
257
            assert_query_success (rc, "Move handling error");
 
258
 
 
259
            // Payload insertion statment
 
260
            sql = """
 
261
                INSERT INTO payload (value) VALUES (?)
 
262
            """;
 
263
            rc = database.prepare_v2 (sql, -1, out payload_insertion_stmt);
 
264
            assert_query_success (rc, "Payload insertion query error");
 
265
        }
 
266
 
 
267
 
 
268
        protected void update_callback (Sqlite.Action action,
 
269
            string dbname, string table, int64 rowid)
 
270
        {
 
271
            if (action != Sqlite.Action.DELETE)
 
272
                return;
 
273
            if (deletion_callback != null)
 
274
                deletion_callback (table, rowid);
 
275
            //interpretations_table
 
276
            // manifestations_
 
277
            //mimetypes_table - mimetype table
 
278
            // actors_  . actor table
 
279
            // FIXME!
 
280
            /*
 
281
            stdout.printf ("%s", dbname); // = main
 
282
            stdout.printf ("%s", table);
 
283
            stdout.printf ("%li", (long) rowid);
 
284
            */
 
285
        }
 
286
 
 
287
    }
 
288
 
 
289
}
 
290
 
 
291
// vim:expandtab:ts=4:sw=4