Merge lp:~midori/midori/makeAstatement into lp:midori

Proposed by Cris Dywan
Status: Merged
Approved by: André Stösel
Approved revision: 6511
Merged at revision: 6514
Proposed branch: lp:~midori/midori/makeAstatement
Merge into: lp:midori
Diff against target: 259 lines (+173/-48)
2 files modified
midori/midori-database.vala (+136/-2)
midori/midori-historydatabase.vala (+37/-46)
To merge this branch: bzr merge lp:~midori/midori/makeAstatement
Reviewer Review Type Date Requested Status
André Stösel Approve
Review via email: mp+198463@code.launchpad.net

Commit message

Introduce high-level prepare/ DatabaseStatement API

To post a comment you must log in.
Revision history for this message
André Stösel (ivaldi) wrote :

"statement = prepare (sqlcmd," is quite confusing if you just look at the diff (I would prefer this.prepare (..) (same for "exec", "exec_script",..)), but I guess it has nothing to do with this patch/branch -> approved

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'midori/midori-database.vala'
2--- midori/midori-database.vala 2013-10-12 11:13:09 +0000
3+++ midori/midori-database.vala 2013-12-10 21:57:30 +0000
4@@ -10,13 +10,120 @@
5 */
6
7 namespace Midori {
8+ /*
9+ * Since: 0.5.6
10+ */
11 public errordomain DatabaseError {
12 OPEN,
13 NAMING,
14 FILENAME,
15 EXECUTE,
16- }
17-
18+ COMPILE,
19+ TYPE,
20+ }
21+
22+ /*
23+ * Since: 0.5.7
24+ */
25+ public class DatabaseStatement : GLib.Object, GLib.Initable {
26+ public Sqlite.Statement? stmt { get { return _stmt; } }
27+ protected Sqlite.Statement _stmt = null;
28+ public Database? database { get; set construct; }
29+ public string? query { get; set construct; }
30+
31+ public DatabaseStatement (Database database, string query) throws DatabaseError {
32+ Object (database: database, query: query);
33+ init ();
34+ }
35+
36+ public virtual bool init (GLib.Cancellable? cancellable = null) throws DatabaseError {
37+ int result = database.db.prepare_v2 (query, -1, out _stmt, null);
38+ if (result != Sqlite.OK)
39+ throw new DatabaseError.COMPILE ("Failed to compile statement: %s".printf (query));
40+ return true;
41+ }
42+
43+ /*
44+ * Bind values to named parameters.
45+ * SQL: "SELECT foo FROM bar WHERE id = :session_id"
46+ * Vala: statement.bind(":session_id", typeof (int64), 12345);
47+ * Supported types: string, int64, double
48+ */
49+ public void bind (string pname, ...) throws DatabaseError {
50+ int pindex = stmt.bind_parameter_index (pname);
51+ var args = va_list ();
52+ Type ptype = args.arg ();
53+ if (ptype == typeof (string))
54+ stmt.bind_text (pindex, args.arg ());
55+ else if (ptype == typeof (int64))
56+ stmt.bind_int64 (pindex, args.arg ());
57+ else if (ptype == typeof (double))
58+ stmt.bind_double (pindex, args.arg ());
59+ else
60+ throw new DatabaseError.TYPE ("Invalid type '%s' for '%s' in statement: %s".printf (ptype.name (), pname, query));
61+ }
62+
63+ /*
64+ * Execute the statement, it's an error if there are more rows.
65+ */
66+ public bool exec () throws DatabaseError {
67+ if (step ())
68+ throw new DatabaseError.EXECUTE ("More rows available - use step instead of exec");
69+ return true;
70+ }
71+
72+ /*
73+ * Proceed to the next row, returns false when the end is nigh.
74+ */
75+ public bool step () throws DatabaseError {
76+ int result = stmt.step ();
77+ if (result != Sqlite.DONE && result != Sqlite.ROW)
78+ throw new DatabaseError.EXECUTE (database.db.errmsg ());
79+ return result == Sqlite.ROW;
80+ }
81+
82+ private int column_index (string name) throws DatabaseError {
83+ for (int i = 0; i < stmt.column_count (); i++) {
84+ if (name == stmt.column_name (i))
85+ return i;
86+ }
87+ throw new DatabaseError.TYPE ("No such column '%s' in row: %s".printf (name, query));
88+ }
89+
90+ /*
91+ * Get a string value by its named parameter, for example ":uri".
92+ */
93+ public string? get_string (string name) throws DatabaseError {
94+ int index = column_index (name);
95+ if (stmt.column_type (index) != Sqlite.TEXT)
96+ throw new DatabaseError.TYPE ("Getting '%s' with wrong type in row: %s".printf (name, query));
97+ return stmt.column_text (index);
98+ }
99+
100+ /*
101+ * Get an integer value by its named parameter, for example ":day".
102+ */
103+ public int64 get_int64 (string name) throws DatabaseError {
104+ int index = column_index (name);
105+ if (stmt.column_type (index) != Sqlite.INTEGER)
106+ throw new DatabaseError.TYPE ("Getting '%s' with wrong type in row: %s".printf (name, query));
107+ return stmt.column_int64 (index);
108+ }
109+
110+ /*
111+ * Get a double value by its named parameter, for example ":session_id".
112+ */
113+ public double get_double (string name) throws DatabaseError {
114+ int index = column_index (name);
115+ if (stmt.column_type (index) != Sqlite.FLOAT)
116+ throw new DatabaseError.TYPE ("Getting '%s' with wrong type in row: %s".printf (name, query));
117+ return stmt.column_double (index);
118+ }
119+ }
120+
121+ /*
122+ * Since: 0.5.6
123+ */
124 public class Database : GLib.Object, GLib.Initable {
125 public Sqlite.Database? db { get { return _db; } }
126 protected Sqlite.Database? _db = null;
127@@ -104,5 +211,32 @@
128 throw new DatabaseError.EXECUTE (db.errmsg ());
129 return true;
130 }
131+
132+ /*
133+ * Prepare a statement with optionally binding parameters by name.
134+ * See also DatabaseStatement.bind().
135+ * Since: 0.5.7
136+ */
137+ public DatabaseStatement prepare (string query, ...) throws DatabaseError {
138+ var statement = new DatabaseStatement (this, query);
139+ var args = va_list ();
140+ unowned string? pname = args.arg ();
141+ while (pname != null) {
142+ Type ptype = args.arg ();
143+ if (ptype == typeof (string)) {
144+ string pvalue = args.arg ();
145+ statement.bind (pname, ptype, pvalue);
146+ } else if (ptype == typeof (int64)) {
147+ int64 pvalue = args.arg ();
148+ statement.bind (pname, ptype, pvalue);
149+ } else if (ptype == typeof (double)) {
150+ double pvalue = args.arg ();
151+ statement.bind (pname, ptype, pvalue);
152+ } else
153+ throw new DatabaseError.TYPE ("Invalid type '%s' in statement: %s".printf (ptype.name (), query));
154+ pname = args.arg ();
155+ }
156+ return statement;
157+ }
158 }
159 }
160
161=== modified file 'midori/midori-historydatabase.vala'
162--- midori/midori-historydatabase.vala 2013-10-11 22:43:02 +0000
163+++ midori/midori-historydatabase.vala 2013-12-10 21:57:30 +0000
164@@ -52,58 +52,49 @@
165 public async List<HistoryItem>? query (string sqlcmd, string? filter, int day, int max_items, Cancellable cancellable) {
166 return_val_if_fail (db != null, null);
167
168- Sqlite.Statement stmt;
169- int result;
170-
171- result = db.prepare_v2 (sqlcmd, -1, out stmt, null);
172- if (result != Sqlite.OK) {
173- critical (_("Failed to select from history: %s"), db.errmsg ());
174- return null;
175- }
176-
177- if (":filter" in sqlcmd) {
178+ Midori.DatabaseStatement statement;
179+
180+ try {
181 string real_filter = "%" + filter.replace (" ", "%") + "%";
182- stmt.bind_text (stmt.bind_parameter_index (":filter"), real_filter);
183- }
184- if (":day" in sqlcmd)
185- stmt.bind_int64 (stmt.bind_parameter_index (":day"), day);
186- if (":limit" in sqlcmd)
187- stmt.bind_int64 (stmt.bind_parameter_index (":limit"), max_items);
188-
189- result = stmt.step ();
190- if (!(result == Sqlite.DONE || result == Sqlite.ROW)) {
191- critical (_("Failed to select from history: %s"), db.errmsg ());
192+ statement = prepare (sqlcmd,
193+ ":filter", typeof (string), real_filter,
194+ ":day", typeof (int64), day,
195+ ":limit", typeof (int64), max_items);
196+ } catch (Error error) {
197+ critical (_("Failed to select from history: %s"), error.message);
198 return null;
199 }
200
201 var items = new List<HistoryItem> ();
202- while (result == Sqlite.ROW) {
203- int64 type = stmt.column_int64 (0);
204- int64 date = stmt.column_int64 (1);
205- switch (type) {
206- case 1:
207- string uri = stmt.column_text (2);
208- string title = stmt.column_text (3);
209- items.append (new HistoryWebsite (uri, title, date));
210- break;
211- case 2:
212- string uri = stmt.column_text (2);
213- string title = stmt.column_text (3);
214- items.append (new HistorySearch (uri, title, date));
215- break;
216- default:
217- warn_if_reached ();
218- break;
219+ try {
220+ while (statement.step ()) {
221+ int64 type = statement.get_int64 ("type");
222+ int64 date = statement.get_int64 ("date");
223+ switch (type) {
224+ case 1:
225+ string uri = statement.get_string ("uri");
226+ string title = statement.get_string ("title");
227+ items.append (new HistoryWebsite (uri, title, date));
228+ break;
229+ case 2:
230+ string uri = statement.get_string ("uri");
231+ string title = statement.get_string ("title");
232+ items.append (new HistorySearch (uri, title, date));
233+ break;
234+ default:
235+ warn_if_reached ();
236+ break;
237+ }
238+
239+ uint src = Idle.add (query.callback);
240+ yield;
241+ Source.remove (src);
242+
243+ if (cancellable.is_cancelled ())
244+ return null;
245 }
246-
247- uint src = Idle.add (query.callback);
248- yield;
249- Source.remove (src);
250-
251- if (cancellable.is_cancelled ())
252- return null;
253-
254- result = stmt.step ();
255+ } catch (Error error) {
256+ critical (_("Failed to select from history: %s"), error.message);
257 }
258
259 if (cancellable.is_cancelled ())

Subscribers

People subscribed via source and target branches

to all changes: