Skip to content

Commit

Permalink
Added access to the human-readable error message for all platforms
Browse files Browse the repository at this point in the history
  • Loading branch information
2shady4u committed Oct 25, 2019
1 parent 4c5c778 commit 09b38e9
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 69 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ An example project, named "demo", can also be downloaded from the releases tab.

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.

- **error_message** (String, default='')

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.

- **verbose_mode** (Boolean, default=false)

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

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

Expand Down
Binary file modified demo/bin/osx/libgdsqlite.dylib
Binary file not shown.
Binary file modified demo/bin/win64/libgdsqlite.dll
Binary file not shown.
Binary file modified demo/bin/x11/libgdsqlite.so
Binary file not shown.
31 changes: 18 additions & 13 deletions demo/database.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ extends Node
const SQLite = preload("res://bin/gdsqlite.gdns")
var db
#var db_name = "user://test"
var db_name = "res://data/test"
var db_name : String = "res://data/test"
#var db_name = "test"
var json_name = "data/test_backup"
var table_name = "company"
var json_name : String = "data/test_backup"
var table_name : String = "company"
var other_table_name : String = "expenses"

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

func _ready():

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

var row_array = []
var row_dict = Dictionary()
var row_array : Array = []
var row_dict : Dictionary = Dictionary()
for i in range(0,ids.size()):
row_dict["id"] = ids[i]
row_dict["name"] = names[i]
Expand All @@ -50,8 +51,8 @@ func _ready():
#print(row_array)

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

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

# Try to delete a non-existant table from the database.
if not db.delete_rows(other_table_name, "*"):
print("SQL error: " + db.error_message)

# Close the imported database
db.close_db()

Expand Down
136 changes: 81 additions & 55 deletions src/gdsqlite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ void SQLite::_register_methods()

register_property<SQLite, String>("path", &SQLite::set_path, &SQLite::get_path, "default");
register_property<SQLite, bool>("verbose_mode", &SQLite::verbose_mode, false);
register_property<SQLite, String>("error_message", &SQLite::error_message, "");
register_property<SQLite, Array>("query_result", &SQLite::query_result, Array());
}

Expand Down Expand Up @@ -107,7 +108,7 @@ bool SQLite::import_from_json(String import_path)

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

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

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

/* Add .json to the import_path String if not present */
String ending = String(".json");
if (!export_path.ends_with(ending))
{
export_path += ending;
String ending = String(".json");
if (!export_path.ends_with(ending))
{
export_path += ending;
}
/* Find the real path */
export_path = ProjectSettings::get_singleton()->globalize_path(export_path.strip_edges());
/* CharString object goes out-of-scope when not assigned explicitely */
const CharString dummy_path = export_path.utf8();
const char *char_path = dummy_path.get_data();
std::ofstream ofs(char_path, std::ios::trunc);

/* CharString object goes out-of-scope when not assigned explicitely */
const CharString dummy_path = export_path.utf8();
const char *char_path = dummy_path.get_data();

std::ofstream ofs(char_path, std::ios::trunc);
if (ofs.fail())
{
Godot::print("GDSQLite Error: Can't open specified json-file, file does not exist or is locked");
return false;
}

/* Construct a Dictionary for each table, convert it to JSON and write it to file */
ofs << "[";
for (int i = 0; i <= number_of_tables - 1; i++)
{
Dictionary table_dict = database_dict[i];
String json_string, query_string;
query_string = "SELECT * FROM " + (const String &)table_dict["name"] + ";";
query(query_string);
table_dict["row_array"] = query_result;
/* Construct a Dictionary for each table, convert it to JSON and write it to file */
ofs << "[";
for (int i = 0; i <= number_of_tables - 1; i++)
{
Dictionary table_dict = database_dict[i];
String json_string, query_string;

query_string = "SELECT * FROM " + (const String &)table_dict["name"] + ";";
query(query_string);
table_dict["row_array"] = query_result;

json_string = JSON::get_singleton()->print(table_dict);
/* CharString object goes out-of-scope when not assigned explicitely */
const CharString dummy_string = json_string.utf8();
const char *json_char = dummy_string.get_data();
ofs << json_char;
if (i != number_of_tables - 1)
{
ofs << ",";
}
}
ofs << "]";
ofs.close();
return true;
}
/* CharString object goes out-of-scope when not assigned explicitely */
const CharString dummy_string = json_string.utf8();
const char *json_char = dummy_string.get_data();
ofs << json_char;
if (i != number_of_tables - 1)
{
ofs << ",";
}
}
ofs << "]";
ofs.close();
return true;
}

void SQLite::close_db()
{
Expand Down Expand Up @@ -246,7 +246,7 @@ static int callback(void *closure, int argc, char **argv, char **azColName)
switch (sqlite3_column_type(stmt, i))
{
case SQLITE_INTEGER:
column_value = Variant(sqlite3_column_int(stmt, i));
column_value = Variant((int)sqlite3_column_int64(stmt, i));
break;

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

error_message = String(zErrMsg);
if (rc != SQLITE_OK)
{
Godot::print(" --> SQL error: " + String(zErrMsg));
Godot::print(" --> SQL error: " + error_message);
sqlite3_free(zErrMsg);
return false;
}
Expand All @@ -305,7 +306,8 @@ bool SQLite::query(String p_query)
bool SQLite::create_table(String p_name, Dictionary p_table_dict)
{

String query_string;
String query_string, type_string;
String integer_datatype = "int";
/* Create SQL statement */
query_string = "CREATE TABLE IF NOT EXISTS " + p_name + " (";

Expand All @@ -320,15 +322,39 @@ bool SQLite::create_table(String p_name, Dictionary p_table_dict)
Godot::print("GDSQLite Error: The field 'data_type' is a required part of the table dictionary");
return false;
}
query_string += (const String &)columns[i] + " " + column_dict["data_type"];

query_string += (const String &)columns[i] + " ";
type_string = (const String &)column_dict["data_type"];
if (type_string.to_lower().begins_with(integer_datatype))
{
query_string += String("INTEGER");
}
else
{
query_string += type_string;
}
/* Will be cleaned up whenever godot-cpp receives decent Dictionary get() with default... */
/* Primary key check */
if (column_dict.has("primary_key"))
{
if (column_dict["primary_key"])
{
query_string += String(" PRIMARY KEY");
}
}
/* Default check */
if (column_dict.has("default"))
{
query_string += String(" DEFAULT ") + (const String &)column_dict["default"];
}
/* Autoincrement check */
if (column_dict.has("auto_increment"))
{
if (column_dict["auto_increment"])
{
query_string += String(" AUTOINCREMENT");
}
}
/* Not null check */
if (column_dict.has("not_null"))
{
if (column_dict["not_null"])
Expand Down Expand Up @@ -379,7 +405,7 @@ bool SQLite::insert_row(String p_name, Dictionary p_row_dict)
{
value_string += (const String &)values[i];
}
if (i!=number_of_keys-1)
if (i != number_of_keys - 1)
{
key_string += ",";
value_string += ",";
Expand Down Expand Up @@ -558,4 +584,4 @@ Dictionary SQLite::deep_copy(Dictionary original_dict)
copy_dict[keys[i]] = original_dict[keys[i]];
}
return copy_dict;
}
}
1 change: 1 addition & 0 deletions src/gdsqlite.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class SQLite : public Reference {

public:
sqlite3 *db;
String error_message;
Array query_result;

static void _register_methods();
Expand Down

0 comments on commit 09b38e9

Please sign in to comment.