Skip to content

Commit 09b38e9

Browse files
committed
Added access to the human-readable error message for all platforms
1 parent 4c5c778 commit 09b38e9

File tree

7 files changed

+105
-69
lines changed

7 files changed

+105
-69
lines changed

README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@ An example project, named "demo", can also be downloaded from the releases tab.
2020

2121
Path to the database, should be set before opening the database with .open_db(). If no database with this name exists, a new one at the supplied path will be created. Both *res://* and *user://* keywords can be used to define the path.
2222

23+
- **error_message** (String, default='')
24+
25+
Contains the zErrMsg returned by the SQLite query in human-readable form. An empty string corresponds with the case in which the query executed succesfully.
26+
2327
- **verbose_mode** (Boolean, default=false)
2428

25-
Setting verbose_mode on True results in an information dump in the Godot console that is handy for debugging your ( possibly faulty) SQLite queries.
29+
Setting verbose_mode on True results in an information dump in the Godot console that is handy for debugging your (possibly faulty) SQLite queries.
2630

2731
- **query_result** (Array, default=[])
2832

demo/bin/osx/libgdsqlite.dylib

-19 KB
Binary file not shown.

demo/bin/win64/libgdsqlite.dll

-1 KB
Binary file not shown.

demo/bin/x11/libgdsqlite.so

-1.47 MB
Binary file not shown.

demo/database.gd

+18-13
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@ extends Node
33
const SQLite = preload("res://bin/gdsqlite.gdns")
44
var db
55
#var db_name = "user://test"
6-
var db_name = "res://data/test"
6+
var db_name : String = "res://data/test"
77
#var db_name = "test"
8-
var json_name = "data/test_backup"
9-
var table_name = "company"
8+
var json_name : String = "data/test_backup"
9+
var table_name : String = "company"
10+
var other_table_name : String = "expenses"
1011

11-
var ids = [1,2,3,4,5,6,7]
12-
var names = ["Paul","Allen","Teddy","Mark","Robert","Julia","Amanda"]
13-
var ages = [32,25,23,25,30,63,13]
14-
var addresses = ["California","Texas","Baltimore","Richmond","Texas","Atlanta","New-York"]
15-
var salaries = [20000.00,15000.00,20000.00,65000.00,65000.00,65000.00,65000.00]
12+
var ids : Array = [1,2,3,4,5,6,7]
13+
var names : Array = ["Paul","Allen","Teddy","Mark","Robert","Julia","Amanda"]
14+
var ages : Array = [32,25,23,25,30,63,13]
15+
var addresses : Array = ["California","Texas","Baltimore","Richmond","Texas","Atlanta","New-York"]
16+
var salaries : Array = [20000.00,15000.00,20000.00,65000.00,65000.00,65000.00,65000.00]
1617

1718
func _ready():
1819

19-
var table_dict = Dictionary()
20+
var table_dict : Dictionary = Dictionary()
2021
table_dict["id"] = {"data_type":"int", "primary_key": true, "not_null": true}
2122
table_dict["name"] = {"data_type":"text", "not_null": true}
2223
table_dict["age"] = {"data_type":"int", "not_null": true}
@@ -34,8 +35,8 @@ func _ready():
3435
# Create a table with the structure found in table_dict and add it to the database
3536
db.create_table(table_name, table_dict)
3637

37-
var row_array = []
38-
var row_dict = Dictionary()
38+
var row_array : Array = []
39+
var row_dict : Dictionary = Dictionary()
3940
for i in range(0,ids.size()):
4041
row_dict["id"] = ids[i]
4142
row_dict["name"] = names[i]
@@ -50,8 +51,8 @@ func _ready():
5051
#print(row_array)
5152

5253
# Select the id and age of the employees that are older than 30
53-
var select_condition = "age > 30"
54-
var selected_array = db.select_rows(table_name, select_condition, ["id", "age"])
54+
var select_condition : String = "age > 30"
55+
var selected_array : Array = db.select_rows(table_name, select_condition, ["id", "age"])
5556
print("condition: " + select_condition)
5657
print("result: ", selected_array)
5758

@@ -116,6 +117,10 @@ func _ready():
116117
print("Overwriting database content again with latest backup...")
117118
db.import_from_json(json_name + "_new")
118119

120+
# Try to delete a non-existant table from the database.
121+
if not db.delete_rows(other_table_name, "*"):
122+
print("SQL error: " + db.error_message)
123+
119124
# Close the imported database
120125
db.close_db()
121126

src/gdsqlite.cpp

+81-55
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ void SQLite::_register_methods()
2323

2424
register_property<SQLite, String>("path", &SQLite::set_path, &SQLite::get_path, "default");
2525
register_property<SQLite, bool>("verbose_mode", &SQLite::verbose_mode, false);
26+
register_property<SQLite, String>("error_message", &SQLite::error_message, "");
2627
register_property<SQLite, Array>("query_result", &SQLite::query_result, Array());
2728
}
2829

@@ -107,7 +108,7 @@ bool SQLite::import_from_json(String import_path)
107108

108109
/* Attempt to open the json and, if unsuccessful, throw a parse error specifying the erroneous line */
109110
Ref<JSONParseResult> result = JSON::get_singleton()->parse(json_string);
110-
if (result->get_error()!=Error::OK)
111+
if (result->get_error() != Error::OK)
111112
{
112113
/* Throw a parsing error */
113114
Godot::print("GDSQLite Error: parsing failed! reason: " + result->get_error_string() + ", at line: " + String::num_int64(result->get_error_line()));
@@ -153,65 +154,64 @@ bool SQLite::import_from_json(String import_path)
153154
return true;
154155
}
155156

157+
bool SQLite::export_to_json(String export_path)
158+
{
159+
/* Get all names and sql templates for all tables present in the database */
160+
query(String("SELECT name,sql FROM sqlite_master WHERE type = 'table';"));
161+
int number_of_tables = query_result.size();
162+
Array database_dict;
163+
for (int i = 0; i <= number_of_tables - 1; i++)
164+
{
165+
/* Deep copy of the Dictionary is required as Dictionaries are passed with reference */
166+
/* Without doing this, future queries would overwrite the table information */
167+
database_dict.append(deep_copy(query_result[i]));
168+
}
156169

157-
bool SQLite::export_to_json(String export_path)
158-
{
159-
/* Get all names and sql templates for all tables present in the database */
160-
query(String("SELECT name,sql FROM sqlite_master WHERE type = 'table';"));
161-
int number_of_tables = query_result.size();
162-
Array database_dict;
163-
for (int i = 0; i <= number_of_tables - 1; i++)
164-
{
165-
/* Deep copy of the Dictionary is required as Dictionaries are passed with reference */
166-
/* Without doing this, future queries would overwrite the table information */
167-
database_dict.append(deep_copy(query_result[i]));
168-
}
169-
170170
/* Add .json to the import_path String if not present */
171-
String ending = String(".json");
172-
if (!export_path.ends_with(ending))
173-
{
174-
export_path += ending;
171+
String ending = String(".json");
172+
if (!export_path.ends_with(ending))
173+
{
174+
export_path += ending;
175175
}
176176
/* Find the real path */
177177
export_path = ProjectSettings::get_singleton()->globalize_path(export_path.strip_edges());
178-
179-
/* CharString object goes out-of-scope when not assigned explicitely */
180-
const CharString dummy_path = export_path.utf8();
181-
const char *char_path = dummy_path.get_data();
182-
183-
std::ofstream ofs(char_path, std::ios::trunc);
178+
179+
/* CharString object goes out-of-scope when not assigned explicitely */
180+
const CharString dummy_path = export_path.utf8();
181+
const char *char_path = dummy_path.get_data();
182+
183+
std::ofstream ofs(char_path, std::ios::trunc);
184184
if (ofs.fail())
185185
{
186186
Godot::print("GDSQLite Error: Can't open specified json-file, file does not exist or is locked");
187187
return false;
188188
}
189189

190-
/* Construct a Dictionary for each table, convert it to JSON and write it to file */
191-
ofs << "[";
192-
for (int i = 0; i <= number_of_tables - 1; i++)
193-
{
194-
Dictionary table_dict = database_dict[i];
195-
String json_string, query_string;
196-
197-
query_string = "SELECT * FROM " + (const String &)table_dict["name"] + ";";
198-
query(query_string);
199-
table_dict["row_array"] = query_result;
200-
190+
/* Construct a Dictionary for each table, convert it to JSON and write it to file */
191+
ofs << "[";
192+
for (int i = 0; i <= number_of_tables - 1; i++)
193+
{
194+
Dictionary table_dict = database_dict[i];
195+
String json_string, query_string;
196+
197+
query_string = "SELECT * FROM " + (const String &)table_dict["name"] + ";";
198+
query(query_string);
199+
table_dict["row_array"] = query_result;
200+
201201
json_string = JSON::get_singleton()->print(table_dict);
202-
/* CharString object goes out-of-scope when not assigned explicitely */
203-
const CharString dummy_string = json_string.utf8();
204-
const char *json_char = dummy_string.get_data();
205-
ofs << json_char;
206-
if (i != number_of_tables - 1)
207-
{
208-
ofs << ",";
209-
}
210-
}
211-
ofs << "]";
212-
ofs.close();
213-
return true;
214-
}
202+
/* CharString object goes out-of-scope when not assigned explicitely */
203+
const CharString dummy_string = json_string.utf8();
204+
const char *json_char = dummy_string.get_data();
205+
ofs << json_char;
206+
if (i != number_of_tables - 1)
207+
{
208+
ofs << ",";
209+
}
210+
}
211+
ofs << "]";
212+
ofs.close();
213+
return true;
214+
}
215215

216216
void SQLite::close_db()
217217
{
@@ -246,7 +246,7 @@ static int callback(void *closure, int argc, char **argv, char **azColName)
246246
switch (sqlite3_column_type(stmt, i))
247247
{
248248
case SQLITE_INTEGER:
249-
column_value = Variant(sqlite3_column_int(stmt, i));
249+
column_value = Variant((int)sqlite3_column_int64(stmt, i));
250250
break;
251251

252252
case SQLITE_FLOAT:
@@ -289,9 +289,10 @@ bool SQLite::query(String p_query)
289289
/* Execute SQL statement */
290290
rc = sqlite3_exec(db, sql, callback, (void *)this, &zErrMsg);
291291

292+
error_message = String(zErrMsg);
292293
if (rc != SQLITE_OK)
293294
{
294-
Godot::print(" --> SQL error: " + String(zErrMsg));
295+
Godot::print(" --> SQL error: " + error_message);
295296
sqlite3_free(zErrMsg);
296297
return false;
297298
}
@@ -305,7 +306,8 @@ bool SQLite::query(String p_query)
305306
bool SQLite::create_table(String p_name, Dictionary p_table_dict)
306307
{
307308

308-
String query_string;
309+
String query_string, type_string;
310+
String integer_datatype = "int";
309311
/* Create SQL statement */
310312
query_string = "CREATE TABLE IF NOT EXISTS " + p_name + " (";
311313

@@ -320,15 +322,39 @@ bool SQLite::create_table(String p_name, Dictionary p_table_dict)
320322
Godot::print("GDSQLite Error: The field 'data_type' is a required part of the table dictionary");
321323
return false;
322324
}
323-
query_string += (const String &)columns[i] + " " + column_dict["data_type"];
324-
325+
query_string += (const String &)columns[i] + " ";
326+
type_string = (const String &)column_dict["data_type"];
327+
if (type_string.to_lower().begins_with(integer_datatype))
328+
{
329+
query_string += String("INTEGER");
330+
}
331+
else
332+
{
333+
query_string += type_string;
334+
}
335+
/* Will be cleaned up whenever godot-cpp receives decent Dictionary get() with default... */
336+
/* Primary key check */
325337
if (column_dict.has("primary_key"))
326338
{
327339
if (column_dict["primary_key"])
328340
{
329341
query_string += String(" PRIMARY KEY");
330342
}
331343
}
344+
/* Default check */
345+
if (column_dict.has("default"))
346+
{
347+
query_string += String(" DEFAULT ") + (const String &)column_dict["default"];
348+
}
349+
/* Autoincrement check */
350+
if (column_dict.has("auto_increment"))
351+
{
352+
if (column_dict["auto_increment"])
353+
{
354+
query_string += String(" AUTOINCREMENT");
355+
}
356+
}
357+
/* Not null check */
332358
if (column_dict.has("not_null"))
333359
{
334360
if (column_dict["not_null"])
@@ -379,7 +405,7 @@ bool SQLite::insert_row(String p_name, Dictionary p_row_dict)
379405
{
380406
value_string += (const String &)values[i];
381407
}
382-
if (i!=number_of_keys-1)
408+
if (i != number_of_keys - 1)
383409
{
384410
key_string += ",";
385411
value_string += ",";
@@ -558,4 +584,4 @@ Dictionary SQLite::deep_copy(Dictionary original_dict)
558584
copy_dict[keys[i]] = original_dict[keys[i]];
559585
}
560586
return copy_dict;
561-
}
587+
}

src/gdsqlite.h

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class SQLite : public Reference {
3232

3333
public:
3434
sqlite3 *db;
35+
String error_message;
3536
Array query_result;
3637

3738
static void _register_methods();

0 commit comments

Comments
 (0)