324 lines
10 KiB
GDScript
324 lines
10 KiB
GDScript
"""
|
|
GDDatabase class
|
|
"""
|
|
|
|
class_name GDDatabase
|
|
|
|
extends Object
|
|
|
|
# database types
|
|
enum {
|
|
e_db_type_json = 0, # JSON database
|
|
e_db_type_binary = 1 # binary database - to be designed / implemented
|
|
}
|
|
|
|
# database type
|
|
var m_db_type : int = e_db_type_json
|
|
|
|
# the id of the database
|
|
var m_db_id : int = gddb_constants.c_invalid_id
|
|
|
|
# the name of the database
|
|
var m_db_name : String = ""
|
|
|
|
# tables in the database
|
|
var m_tables : Array = []
|
|
|
|
# the path of the database
|
|
var m_db_filepath : String = ""
|
|
|
|
# if the database is not saved, it is dirty
|
|
var m_is_dirty : bool = false
|
|
|
|
# sets the id of the database
|
|
func set_db_id(db_id : int) -> void :
|
|
m_db_id = db_id
|
|
|
|
# returns the database id
|
|
func get_db_id() -> int :
|
|
return m_db_id
|
|
|
|
# set the name of the database
|
|
# the name of the database should not contain special characters
|
|
func set_db_name(db_name : String) -> bool :
|
|
if(!gddb_globals.check_db_name(db_name)):
|
|
print("ERROR: GDDatabase::set_db_name(" + db_name + ") - the name of the database contains invalid characters")
|
|
return false
|
|
# print("GDDatabase::set_db_name(" + db_name + ")")
|
|
m_db_name = db_name
|
|
return true
|
|
|
|
# returns the name of the database
|
|
func get_db_name() -> String :
|
|
return m_db_name
|
|
|
|
# sets the database file path
|
|
func set_db_filepath(filepath : String) -> void :
|
|
m_db_filepath = filepath
|
|
|
|
# returns the path of the database
|
|
func get_db_filepath() -> String :
|
|
return m_db_filepath
|
|
|
|
# checks if a table with the name "table_name" can be added into database
|
|
func can_add_table(table_name : String, table_id : int = -1) -> bool :
|
|
# print("GDDatabase::can_add_table(" + table_name + ", " + str(table_id) + ")")
|
|
for idx in range(0, m_tables.size()):
|
|
if(m_tables[idx].get_table_name() == table_name):
|
|
if(m_tables[idx].get_table_id() == table_id):
|
|
continue
|
|
print("WARNING: GDDatabase::can_add_table(" + table_name + ", " + str(table_id) + ") - table already exists")
|
|
return false
|
|
return true
|
|
|
|
# adds a new table
|
|
# returns the table id; if already exists, it will fire an warning on output console and returns an invalid id
|
|
func add_table(table_name : String) -> int :
|
|
if(!can_add_table(table_name)):
|
|
print("WARNING: GDDatabase::add_table(" + table_name + ") - cannot add table")
|
|
return gddb_constants.c_invalid_id
|
|
|
|
# print("GDDatabase::add_table(" + table_name + ") to \"" + m_db_name + "\" database")
|
|
|
|
var table_id = generate_new_table_id()
|
|
var table = load(gddb_constants.c_addon_main_path + "core/db_table.gd").new()
|
|
table.set_table_id(table_id)
|
|
table.set_table_name(table_name)
|
|
table.set_parent_database(self)
|
|
m_tables.push_back(table)
|
|
return table_id
|
|
|
|
# edits a table name
|
|
func edit_table_name(table_name : String, table_id : int) -> bool :
|
|
if(!can_add_table(table_name, table_id)):
|
|
return false
|
|
for idx in range(0, m_tables.size()):
|
|
if(m_tables[idx].get_table_id() == table_id):
|
|
m_tables[idx].set_table_name(table_name)
|
|
break
|
|
return true
|
|
|
|
# deletes a table at index
|
|
func delete_table_at(idx : int) -> void :
|
|
if(idx < 0 || idx > m_tables.size() - 1):
|
|
print("GDDatabase::delete_table_at(" + str(idx) + ") - index out of bounds")
|
|
return
|
|
m_tables[idx].clear()
|
|
m_tables[idx].free()
|
|
m_tables.remove(idx)
|
|
|
|
# deletes a table by id
|
|
func delete_table_by_id(table_id: int) -> void :
|
|
for idx in range(0, m_tables.size()):
|
|
if(m_tables[idx].get_table_id() == table_id):
|
|
# print("GDDatabase::delete_table_by_id(" + str(table_id) + ")")
|
|
m_tables[idx].clear()
|
|
m_tables[idx].free()
|
|
m_tables.remove(idx)
|
|
return
|
|
print("ERROR: GDDatabase::delete_table_by_id(" + str(table_id) + ") - cannot erase table; id not found")
|
|
|
|
# deletes a table by name
|
|
func delete_table_by_name(table_name: String) -> void :
|
|
for idx in range(0, m_tables.size()):
|
|
if(m_tables[idx].get_table_name() == table_name):
|
|
# print("GDDatabase::delete_table_by_name(" + table_name + ")")
|
|
m_tables[idx].clear()
|
|
m_tables[idx].free()
|
|
m_tables.remove(idx)
|
|
return
|
|
print("ERROR: GDDatabase::delete_table_by_name(" + table_name + ") - cannot erase table; name not found")
|
|
|
|
# generates a new table id
|
|
func generate_new_table_id() -> int :
|
|
if(m_tables.size() == 0):
|
|
return 0
|
|
return m_tables[m_tables.size()-1].get_table_id() + 1
|
|
|
|
# returns the count of the tables in the database
|
|
func get_tables_count() -> int :
|
|
return m_tables.size()
|
|
|
|
# returns true if the table exists in the database, false otherwise
|
|
# this is equivalent with (get_table_by_name(table name) != null) function, but without firing the error in case the table doesn't exist
|
|
func is_table_exists(table_name : String) -> bool :
|
|
for idx in range(0, m_tables.size()):
|
|
if(m_tables[idx].get_table_name() == table_name):
|
|
return true
|
|
return false
|
|
|
|
# returns a table by an index or null if the index is invalid
|
|
func get_table_at(idx: int) -> Object :
|
|
if(idx < 0 || idx >= m_tables.size()):
|
|
print("ERROR: GDDatabase::get_table_at(" + str(idx) + ") - cannot obtain table with index")
|
|
return null
|
|
return m_tables[idx]
|
|
|
|
# returns a table by its id or null if a table with id doesn's exist
|
|
func get_table_by_id(table_id: int) -> Object :
|
|
for idx in range(0, m_tables.size()):
|
|
if(m_tables[idx].get_table_id() == table_id):
|
|
return m_tables[idx]
|
|
print("ERROR: GDDatabase::get_table_by_id(" + str(table_id) + ") - cannot obtain table with id")
|
|
return null
|
|
|
|
# returns a table by its name or null if the name of the table doesn's exist
|
|
func get_table_by_name(table_name: String) -> Object :
|
|
for idx in range(0, m_tables.size()):
|
|
if(m_tables[idx].get_table_name() == table_name):
|
|
return m_tables[idx]
|
|
print("ERROR: GDDatabase::get_table_by_id(" + table_name + ") - cannot obtain table with name")
|
|
return null
|
|
|
|
# deletes all the tables
|
|
func clear() -> void :
|
|
# print("GDDatabase::clear()")
|
|
m_db_name = ""
|
|
for idx in range(0, m_tables.size()):
|
|
m_tables[idx].clear()
|
|
m_tables.clear()
|
|
|
|
# sets the database dirty; it is not saved
|
|
func set_dirty(dirty : bool) -> void :
|
|
m_is_dirty = dirty
|
|
|
|
# returns true if a database is dirty (should be saved), false otherwise
|
|
func is_dirty() -> bool :
|
|
return m_is_dirty
|
|
|
|
# serialization
|
|
func save_db() -> void :
|
|
if(m_db_name.empty()):
|
|
print("ERROR: GDDatabase::save_db() - current database doesn't have a name")
|
|
return
|
|
|
|
if(m_db_filepath.empty()):
|
|
print("ERROR: GDDatabase::save_db() - current database doesn't have a path file")
|
|
return
|
|
|
|
# print("GDDatabase::save_db() - " + m_db_name + " to: " + m_db_filepath)
|
|
var text = "{"
|
|
text += "\n\t\"" + gddb_constants.c_gddb_signature + "\":\"" + gddb_constants.c_gddb_ver + "\","
|
|
text += "\n\t\"db_name\":\"" + m_db_name + "\","
|
|
text += "\n\t\"tables\":["
|
|
for idx in range(0, m_tables.size()):
|
|
text += "\n\t\t{"
|
|
text += "\n\t\t\t\"table_name\":\"" + m_tables[idx].get_table_name() + "\","
|
|
text += "\n\t\t\t\"props\":["
|
|
for jdx in range(0, m_tables[idx].get_props_count()):
|
|
var db_prop = m_tables[idx].get_prop_at(jdx)
|
|
text += "\n\t\t\t\t\t{"
|
|
text += "\"name\":\"" + str(db_prop.get_prop_name()) + "\","
|
|
|
|
var prop_type = db_prop.get_prop_type()
|
|
if(prop_type < gddb_types.e_prop_types_count):
|
|
text += "\"type\":\"" + str(prop_type) + "\","
|
|
else:
|
|
# print("GDDatabase::save_db() - prop_type: " + str(prop_type))
|
|
var table_id = prop_type - gddb_types.e_prop_types_count
|
|
var table = get_table_by_id(table_id)
|
|
if(null == table):
|
|
print("GDDatabase::save_db() - table not found with id: " + str(table_id))
|
|
text += "\"type\":\"" + "table" + "\","
|
|
text += "\"table_name\":\"" + table.get_table_name() + "\","
|
|
|
|
text += "\"auto_increment\":\"" + str(int(db_prop.has_autoincrement())) + "\""
|
|
|
|
text += "}"
|
|
if(jdx < m_tables[idx].get_props_count() - 1):
|
|
text += ","
|
|
text += "\n\t\t\t],"
|
|
|
|
text += "\n\t\t\t\"data\":["
|
|
for jdx in range(0, m_tables[idx].get_data_size()):
|
|
#var the_data = m_tables[idx].get_data_at(jdx)
|
|
#print("getting data at " + str(jdx) + " : " + the_data)
|
|
text += "\"" + m_tables[idx].get_data_at(jdx) + "\""
|
|
if(jdx < m_tables[idx].get_data_size() - 1):
|
|
text += ","
|
|
text += "]" # end of data
|
|
text += "\n\t\t}" # end of table
|
|
|
|
if(idx < m_tables.size() - 1):
|
|
text += ","
|
|
|
|
text += "\n\t]\n}"
|
|
|
|
var save_file = File.new()
|
|
save_file.open(m_db_filepath, File.WRITE)
|
|
save_file.store_string(text)
|
|
save_file.close()
|
|
|
|
set_dirty(false)
|
|
|
|
# deserialization
|
|
func load_db() -> int :
|
|
var file = File.new()
|
|
file.open(get_db_filepath(), File.READ)
|
|
var content = file.get_as_text()
|
|
file.close()
|
|
var dictionary = JSON.parse(content).result
|
|
|
|
# check the signature
|
|
if(!dictionary.has(gddb_constants.c_gddb_signature)):
|
|
print("GDDatabase::load_db() - invalid database file")
|
|
return gddb_types.e_db_invalid_file
|
|
|
|
var gddb_signature = dictionary[gddb_constants.c_gddb_signature]
|
|
if(gddb_signature != gddb_constants.c_gddb_ver):
|
|
print("GDDatabase::load_db() - invalid database version")
|
|
return gddb_types.e_db_invalid_ver
|
|
|
|
clear()
|
|
m_db_name = dictionary["db_name"]
|
|
var tables = dictionary["tables"]
|
|
for idx in range(0, tables.size()):
|
|
var table_id = add_table(tables[idx]["table_name"])
|
|
var table = get_table_by_id(table_id)
|
|
|
|
var props_count = tables[idx]["props"].size()
|
|
if(props_count == 0):
|
|
continue
|
|
|
|
for jdx in range(0, props_count):
|
|
var prop_type = tables[idx]["props"][jdx]["type"]
|
|
|
|
var prop_id = -1
|
|
if(prop_type == "table"):
|
|
var table_name = tables[idx]["props"][jdx]["table_name"]
|
|
prop_id = table.add_table_prop(tables[idx]["props"][jdx]["name"], table_name)
|
|
else:
|
|
prop_id = table.add_prop(prop_type.to_int(), tables[idx]["props"][jdx]["name"])
|
|
|
|
var prop = table.get_prop_by_id(prop_id)
|
|
var enable_autoincrement = tables[idx]["props"][jdx]["auto_increment"].to_int()
|
|
prop.enable_autoincrement(bool(enable_autoincrement))
|
|
|
|
var data_count = tables[idx]["data"].size()
|
|
#print("********* set data to db - begin")
|
|
for jdx in range(0, data_count / props_count):
|
|
var row_data = []
|
|
for kdx in range(0, props_count):
|
|
var cell_data = tables[idx]["data"][jdx * props_count + kdx]
|
|
# print("cell_data: " + cell_data)
|
|
row_data.push_back(cell_data)
|
|
#print("row_data: " + str(row_data))
|
|
table.add_row(row_data)
|
|
#print("********* set data to db - end")
|
|
|
|
# link custom data to tables
|
|
for idx in range(0, m_tables.size()):
|
|
m_tables[idx].link_tables_props()
|
|
|
|
return gddb_types.e_db_valid
|
|
|
|
# dumps the database
|
|
func dump() -> String :
|
|
var dump_text = "\nDatabase dump. id: " + str(m_db_id) + ", name: " + m_db_name + ", filepath: " + m_db_filepath
|
|
dump_text += "\n------------------------------------------------------------------------------------\n"
|
|
|
|
for idx in range(0, m_tables.size()):
|
|
dump_text += "\n" + m_tables[idx].dump()
|
|
|
|
return dump_text
|