Skip to content

Commit de92cea

Browse files
committed
Improve error handling.
Factorize code and produce more descriptive errors
1 parent 5275839 commit de92cea

File tree

8 files changed

+2633
-2750
lines changed

8 files changed

+2633
-2750
lines changed

coffee/api-data.coffee

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,67 +2,36 @@
22
NULL = 0 # Null pointer
33

44
SQLite.OK=0
5-
SQLite.errorMessages[0]="Successful result"
65
SQLite.ERROR=1
7-
SQLite.errorMessages[1]="SQL error or missing database"
86
SQLite.INTERNAL=2
9-
SQLite.errorMessages[2]="Internal logic error in SQLite"
107
SQLite.PERM=3
11-
SQLite.errorMessages[3]="Access permission denied"
128
SQLite.ABORT=4
13-
SQLite.errorMessages[4]="Callback routine requested an abort"
149
SQLite.BUSY=5
15-
SQLite.errorMessages[5]="The database file is locked"
1610
SQLite.LOCKED=6
17-
SQLite.errorMessages[6]="A table in the database is locked"
1811
SQLite.NOMEM=7
19-
SQLite.errorMessages[7]="A malloc() failed"
2012
SQLite.READONLY=8
21-
SQLite.errorMessages[8]="Attempt to write a readonly database"
2213
SQLite.INTERRUPT=9
23-
SQLite.errorMessages[9]="Operation terminated by sqlite3_interrupt()"
2414
SQLite.IOERR=10
25-
SQLite.errorMessages[10]="Some kind of disk I/O error occurred"
2615
SQLite.CORRUPT=11
27-
SQLite.errorMessages[11]="The database disk image is malformed"
2816
SQLite.NOTFOUND=12
29-
SQLite.errorMessages[12]="Unknown opcode in sqlite3_file_control()"
3017
SQLite.FULL=13
31-
SQLite.errorMessages[13]="Insertion failed because database is full"
3218
SQLite.CANTOPEN=14
33-
SQLite.errorMessages[14]="Unable to open the database file"
3419
SQLite.PROTOCOL=15
35-
SQLite.errorMessages[15]="Database lock protocol error"
3620
SQLite.EMPTY=16
37-
SQLite.errorMessages[16]="Database is empty"
3821
SQLite.SCHEMA=17
39-
SQLite.errorMessages[17]="The database schema changed"
4022
SQLite.TOOBIG=18
41-
SQLite.errorMessages[18]="String or BLOB exceeds size limit"
4223
SQLite.CONSTRAINT=19
43-
SQLite.errorMessages[19]="Abort due to constraint violation"
4424
SQLite.MISMATCH=20
45-
SQLite.errorMessages[20]="Data type mismatch"
4625
SQLite.MISUSE=21
47-
SQLite.errorMessages[21]="Library used incorrectly"
4826
SQLite.NOLFS=22
49-
SQLite.errorMessages[22]="Uses OS features not supported on host"
5027
SQLite.AUTH=23
51-
SQLite.errorMessages[23]="Authorization denied"
5228
SQLite.FORMAT=24
53-
SQLite.errorMessages[24]="Auxiliary database format error"
5429
SQLite.RANGE=25
55-
SQLite.errorMessages[25]="2nd parameter to sqlite3_bind out of range"
5630
SQLite.NOTADB=26
57-
SQLite.errorMessages[26]="File opened that is not a database file"
5831
SQLite.NOTICE=27
59-
SQLite.errorMessages[27]="Notifications from sqlite3_log()"
6032
SQLite.WARNING=28
61-
SQLite.errorMessages[28]="Warnings from sqlite3_log()"
6233
SQLite.ROW=100
63-
SQLite.errorMessages[100]="sqlite3_step() has another row ready"
6434
SQLite.DONE=101
65-
SQLite.errorMessages[101]="sqlite3_step() has finished executing"
6635

6736
# Data types
6837
SQLite.INTEGER=1

coffee/api.coffee

Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22

33
apiTemp = Runtime.stackAlloc(4);
44

5-
SQLite = {
6-
# Constants are defined below
7-
errorMessages : [] # Will contain all SQLite error descriptions sorted by error codes
8-
};
5+
# Constants are defined in api-data.coffee
6+
SQLite = {};
97

108
### Represents an prepared statement.
119
@@ -25,7 +23,7 @@ class Statement
2523
# Statements can't be created by the API user, only by Database::prepare
2624
# @private
2725
# @nodoc
28-
constructor: (@stmt) ->
26+
constructor: (@stmt, @db) ->
2927
@pos = 1 # Index of the leftmost parameter is 1
3028
@allocatedmem = [] # Pointers to allocated memory, that need to be freed when the statemend is destroyed
3129

@@ -79,10 +77,10 @@ class Statement
7977
'step': ->
8078
if not @stmt then throw "Statement closed"
8179
@pos = 1
82-
ret = sqlite3_step @stmt
83-
if ret is SQLite.ROW then return true
84-
else if ret is SQLite.DONE then return false
85-
else throw 'SQLite error: ' + handleErrors ret
80+
switch ret = sqlite3_step @stmt
81+
when SQLite.ROW then return true
82+
when SQLite.DONE then return false
83+
else @db.handleError ret
8684

8785
# Internal methods to retrieve data from the results of a statement that has been executed
8886
# @nodoc
@@ -161,25 +159,22 @@ class Statement
161159
bindString: (string, pos = @pos++) ->
162160
bytes = intArrayFromString(string)
163161
@allocatedmem.push strptr = allocate bytes, 'i8', ALLOC_NORMAL
164-
ret = sqlite3_bind_text @stmt, pos, strptr, bytes.length, 0
165-
if ret is SQLite.OK then return true
166-
err = handleErrors ret
167-
if err isnt null then throw 'SQLite error : ' + err
162+
@db.handleError sqlite3_bind_text @stmt, pos, strptr, bytes.length, 0
163+
return true
164+
168165
# @nodoc
169166
bindBlob: (array, pos = @pos++) ->
170167
@allocatedmem.push blobptr = allocate array, 'i8', ALLOC_NORMAL
171-
ret = sqlite3_bind_blob @stmt, pos, blobptr, array.length, 0
172-
if ret is SQLite.OK then return true
173-
err = handleErrors ret
174-
if err isnt null then throw 'SQLite error : ' + err
168+
@db.handleError sqlite3_bind_blob @stmt, pos, blobptr, array.length, 0
169+
return true
170+
175171
# @private
176172
# @nodoc
177173
bindNumber: (num, pos = @pos++) ->
178174
bindfunc = if num is (num|0) then sqlite3_bind_int else sqlite3_bind_double
179-
ret = bindfunc @stmt, pos, num
180-
if ret is SQLite.OK then return true
181-
err = handleErrors ret
182-
if err isnt null then throw 'SQLite error : ' + err
175+
@db.handleError bindfunc @stmt, pos, num
176+
return true
177+
183178
# @nodoc
184179
bindNull: (pos = @pos++) -> sqlite3_bind_blob(@stmt, pos, 0,0,0) is SQLite.OK
185180
# Call bindNumber or bindString appropriatly
@@ -217,8 +212,8 @@ class Statement
217212
###
218213
'reset' : ->
219214
@freemem()
220-
sqlite3_reset(@stmt) is SQLite.OK and
221-
sqlite3_clear_bindings(@stmt) is SQLite.OK
215+
sqlite3_clear_bindings(@stmt) is SQLite.OK and
216+
sqlite3_reset(@stmt) is SQLite.OK
222217

223218
### Free the memory allocated during parameter binding
224219
###
@@ -243,8 +238,7 @@ class Database
243238
constructor: (data) ->
244239
@filename = 'dbfile_' + (0xffffffff*Math.random()>>>0)
245240
if data? then FS.createDataFile '/', @filename, data, true, true
246-
ret = sqlite3_open @filename, apiTemp
247-
if ret isnt SQLite.OK then throw 'SQLite error: ' + SQLite.errorMessages[ret]
241+
@handleError sqlite3_open @filename, apiTemp
248242
@db = getValue(apiTemp, 'i32')
249243
@statements = [] # A list of all prepared statements of the database
250244

@@ -268,9 +262,7 @@ class Database
268262
stmt['step']()
269263
stmt['free']()
270264
else
271-
ret = sqlite3_exec @db, sql, 0, 0, apiTemp
272-
err = handleErrors ret, apiTemp
273-
if err isnt null then throw 'SQLite error : ' + err
265+
@handleError sqlite3_exec @db, sql, 0, 0, apiTemp
274266
return @
275267

276268
### Execute an SQL query, and returns the result.
@@ -369,14 +361,12 @@ class Database
369361
###
370362
'prepare': (sql, params) ->
371363
setValue apiTemp, 0, 'i32'
372-
ret = sqlite3_prepare_v2 @db, sql, -1, apiTemp, NULL
373-
err = handleErrors ret, NULL
374-
if err isnt null then throw 'SQLite error: ' + err
364+
@handleError sqlite3_prepare_v2 @db, sql, -1, apiTemp, NULL
375365
pStmt = getValue apiTemp, 'i32' # pointer to a statement, or null
376366
if pStmt is NULL then throw 'Nothing to prepare'
377-
stmt = new Statement(pStmt)
367+
stmt = new Statement pStmt, this
378368
if params? then stmt.bind params
379-
@statements.push(stmt)
369+
@statements.push stmt
380370
return stmt
381371

382372
### Exports the contents of the database to a binary array
@@ -397,18 +387,17 @@ class Database
397387
###
398388
'close': ->
399389
stmt['free']() for stmt in @statements
400-
ret = sqlite3_close_v2 @db
401-
if ret isnt 0 then throw 'SQLite error: ' + SQLite_codes[ret].msg
390+
@handleError sqlite3_close_v2 @db
402391
FS.unlink '/' + @filename
403392
@db = null
404393

405-
handleErrors = (ret, errPtrPtr) ->
406-
if not errPtrPtr
407-
return if ret is SQLite.OK then null else SQLite.errorMessages[ret]
408-
else
409-
errPtr = getValue errPtrPtr, 'i32'
410-
if errPtr isnt NULL and errPtr isnt undefined
411-
msg = Pointer_stringify errPtr
412-
sqlite3_free errPtr
413-
return msg
414-
else return null
394+
### Analyze a result code, return null if no error occured, and throw
395+
an error with a descriptive message otherwise
396+
@nodoc
397+
###
398+
handleError: (returnCode) ->
399+
if returnCode is SQLite.OK
400+
null
401+
else
402+
errmsg = sqlite3_errmsg @db
403+
throw new Error(errmsg)

coffee/exports.coffee

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ sqlite3_bind_parameter_index = Module['cwrap'] 'sqlite3_bind_parameter_index', '
2222
## Get values
2323
# int sqlite3_step(sqlite3_stmt*)
2424
sqlite3_step = Module['cwrap'] 'sqlite3_step', 'number', ['number']
25+
sqlite3_errmsg = Module['cwrap'] 'sqlite3_errmsg', 'string', ['number']
2526
# int sqlite3_data_count(sqlite3_stmt *pStmt);
2627
sqlite3_data_count = Module['cwrap'] 'sqlite3_data_count', 'number', ['number']
2728
sqlite3_column_double = Module['cwrap'] 'sqlite3_column_double', 'number', ['number', 'number']

exported_functions

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"_sqlite3_open",
55
"_sqlite3_exec",
66
"_sqlite3_free",
7+
"_sqlite3_errmsg",
78
"_sqlite3_prepare_v2",
89
"_sqlite3_bind_text",
910
"_sqlite3_bind_blob",

0 commit comments

Comments
 (0)