Revision 1 (by moose, 2006/03/06 10:00:33) Initial Import
#include <qvaluelist.h>
#include "MadConf.h"
#include "MadException.h"

const char sql_create_key_table[] =
	"CREATE TABLE conf_key ("
		"id INTEGER PRIMARY KEY,"
		"parent_id INTEGER,"
		"type_id INTEGER,"
		"name TEXT"
	");";

const char sql_create_data_table[] =
	"CREATE TABLE conf_data ("
		"id INTEGER PRIMARY KEY,"
		"key_id INTEGER KEY,"
		"data TEXT"
	");";

const char sql_create_data_opt_table[] =
	"CREATE TABLE conf_data_opt ("
		"id INTEGER PRIMARY KEY,"
		"key_id INTEGER KEY,"
		"data TEXT"
	");";

/****************** MadConfKeyValue ******************/

bool MadConfKeyValue::set(int i)
{
	value.setNum(i);
	return conf->db->queryf("UPDATE conf_data SET data='%i' WHERE id=%i;", i, id);
}

bool MadConfKeyValue::set(bool b)
{
	value.setNum((int)b);
	return conf->db->queryf("UPDATE conf_data SET data='%i' WHERE id=%i;", (int)b, id);
}

bool MadConfKeyValue::set(const char *s)
{
	value = s;
	return conf->db->queryf("UPDATE conf_data SET data='%q' WHERE id=%i;", s, id);
}

bool MadConfKeyValue::set(double d)
{
	value.setNum(d);
	return conf->db->queryf("UPDATE conf_data SET data='%f' WHERE id=%i;", d, id);
}

void MadConfKeyValue::erase()
{
	//printf("erasing: %i\n", id);
	conf->db->queryf("DELETE FROM conf_data WHERE id=%i;", id);
}

/****************** MadConfKeyOpt ******************/

bool MadConfKeyOpt::set(int i)
{
	value.setNum(i);
	return conf->db->queryf("UPDATE conf_data_opt SET data='%i' WHERE id=%i;", i, id);
}

bool MadConfKeyOpt::set(bool b)
{
	value.setNum((int)b);
	return conf->db->queryf("UPDATE conf_data_opt SET data='%i' WHERE id=%i;", (int)b, id);
}

bool MadConfKeyOpt::set(const char *s)
{
	value = s;
	return conf->db->queryf("UPDATE conf_data_opt SET data='%q' WHERE id=%i;", s, id);
}

bool MadConfKeyOpt::set(double d)
{
	value.setNum(d);
	return conf->db->queryf("UPDATE conf_data_opt SET data='%f' WHERE id=%i;", d, id);
}

void MadConfKeyOpt::erase()
{
	//printf("erasing: %i\n", id);
	conf->db->queryf("DELETE FROM conf_data_opt WHERE id=%i;", id);
}

/****************** MadConfKey ******************/

bool MadConfKey::AUTO_COMMIT;

MadConfKey::MadConfKey(MadConf *c, int i, int t, const char *k) : key(k), id(i), type(t), conf(c), value_result(0), child_result(0), opt_result(0)
{
	printf("MadConfKey(): %s(%i)\n", (const char *)key, id);
}

MadConfKey::~MadConfKey()
{
	printf("~MadConfKey(): %s(%i)\n", (const char *)key, id);

	if(value_result)
		delete value_result;

	if(child_result)
		delete child_result;
	
	if(opt_result)
		delete opt_result;
}

bool MadConfKey::setName(const char *k)
{
	key = QString(k);
	bool ret = conf->db->queryf("UPDATE conf_key SET name='%q' WHERE id=%i", k, id);
	
	if(!ret)
		printf("err: %s\n", conf->db->error_message());
	
	return ret;
}

bool MadConfKey::setType(int t)
{
	type = t;
	return conf->db->queryf("UPDATE conf_key SET type_id=%i WHERE id=%i", t, id);
}

QString MadConfKey::name()
{
	MadSQLiteResult *res = conf->db->preparef("SELECT name FROM conf_key WHERE id=%i;", id);
	if(!res)
		return QString("");

	if(res->step() == MadDB_ROW) {
		QString tmp(res->columnText(0));
		delete res;
		return tmp;
	}

	delete res;

	return QString("");
}

bool MadConfKey::insert_key()
{
	bool res = conf->db->queryf("INSERT INTO conf_key (parent_id, type_id, name) VALUES (%i, %i, '%q');", id, type, (const char *)key);
	id = conf->db->last_insert_id();
	return res;
}

bool MadConfKey::value(MadConfKeyValue &v)
{
	printf("value()\n");

	if(value_result)
		delete value_result;

	value_result = conf->db->preparef("SELECT d.id,d.data FROM conf_key k INNER JOIN conf_data d ON k.id=d.key_id WHERE k.id=%i;", id);
	if(!value_result)
		return false;

	if(value_result->step() != MadDB_ROW) {
		printf("~value(): %s(%i)\n", (const char *)key, id);
		delete value_result;
		value_result = 0;
		return false;
	}

	v =  MadConfKeyValue(conf, value_result->columnInt(0), value_result->columnText(1));	

	delete value_result;
	value_result = 0;

	return true;
}

bool MadConfKey::beginValue()
{
	printf("beginValue\n");

	if(value_result)
		delete value_result;

	value_result = conf->db->preparef("SELECT d.id,d.data FROM conf_key k INNER JOIN conf_data d ON k.id=d.key_id WHERE k.id=%i;", id);
	if(!value_result)
		return false;
	
	return true;
}

bool MadConfKey::step(MadConfKeyValue &v)
{
//	printf("step (v)\n");

	if(!value_result)
		return false;

	if(value_result->step() != MadDB_ROW) {
		printf("~step(value): %s(%i)\n", (const char *)key, id);
		delete value_result;
		value_result = 0;
		return false;
	}

	v = MadConfKeyValue(conf, value_result->columnInt(0), value_result->columnText(1));
	
	return true;
}

bool MadConfKey::addValue(MadConfKeyValue &v, const char *txt)
{
	if(type && type != MCKT_VALUE && type != MCKT_VALUE_LIST) {
		MadConfKeyOpt opt;
		beginOpt();
		while(step(opt)) {
			if(opt.value == QString(txt)) {
				bool result = conf->db->queryf("INSERT INTO conf_data (key_id,data) VALUES (%i,'%q')", id, txt);
				if(!result)
					return false;

				v = MadConfKeyValue(conf, conf->db->last_insert_id(), txt);
				
				return true;
			}
		}
		
		return false;
	}
	else {
	
		bool result = conf->db->queryf("INSERT INTO conf_data (key_id,data) VALUES (%i,'%q')", id, txt);
		if(!result)
			return false;

		v = MadConfKeyValue(conf, conf->db->last_insert_id(), txt);
	}
	
	return true;
}

/*
MadConfKey *MadConfKey::getParent()
{
	MadSQLiteResult *result = conf->db->preparef("SELECT id,type_id,name FROM conf_key
}
*/

//MadConfKey *newChild(const char *keyname, int type = MCKT_VALUE);
bool MadConfKey::newChild(MadConfKey &k, const char *keyname, int type)
{
	k = MadConfKey(conf, id, type, keyname);
	
	if(!k.insert_key())
		return false;

	return true;
}

bool MadConfKey::beginChild()
{
	printf("beginChild\n");

	if(child_result)
		delete child_result;

	child_result = conf->db->preparef("SELECT id,type_id,name FROM conf_key WHERE parent_id=%i;", id);
	if(!child_result)
		return false;
	
	return true;
}

bool MadConfKey::step(MadConfKey &k)
{
//	printf("step (k)\n");

	if(!child_result)
		return false;

	if(child_result->step() != MadDB_ROW) {
		printf("~step(key): %s(%i)\n", (const char *)key, id);
		delete child_result;
		child_result = 0;
		return false;
	}

	k = MadConfKey(conf, child_result->columnInt(0), child_result->columnInt(1), child_result->columnText(2));
	
	return true;
}

bool MadConfKey::child(MadConfKey &key, const char *k)
{
//	printf("child()\n");

	if(child_result)
		delete child_result;

	child_result = conf->db->preparef("SELECT id,type_id FROM conf_key WHERE parent_id=%i AND name='%q';", id, k);
	if(!child_result)
		return false;

	if(child_result->step() != MadDB_ROW) {
		printf("~child(): %s(%i)\n", (const char *)this->key, id);
		delete child_result;
		child_result = 0;
		return false;
	}

	key = MadConfKey(conf, child_result->columnInt(0), child_result->columnInt(1), k);
	
	delete child_result;
	child_result = 0;
	
	return true;
}


bool MadConfKey::beginOpt()
{
	printf("beginOpt\n");

	if(opt_result)
		delete opt_result;

	opt_result = conf->db->preparef("SELECT d.id,d.data FROM conf_key k INNER JOIN conf_data_opt d ON k.id=d.key_id WHERE k.id=%i;", id);
	if(!opt_result)
		return false;
	
	return true;
}

bool MadConfKey::step(MadConfKeyOpt &v)
{
//	printf("step (v)\n");

	if(!opt_result)
		return false;

	if(opt_result->step() != MadDB_ROW) {
		printf("~step(opt): %s(%i)\n", (const char *)key, id);
		delete opt_result;
		opt_result = 0;
		return false;
	}

	v = MadConfKeyOpt(conf, opt_result->columnInt(0), opt_result->columnText(1));
	
	return true;
}

bool MadConfKey::addOpt(MadConfKeyOpt &v, const char *txt)
{
	if(!type || type == MCKT_VALUE || type == MCKT_VALUE_LIST)
		return false;

	bool result = conf->db->queryf("INSERT INTO conf_data_opt (key_id,data) VALUES (%i,'%q')", id, txt);
	if(!result)
		return false;

	v = MadConfKeyOpt(conf, conf->db->last_insert_id(), txt);
	
	return true;
}

bool MadConfKey::delete_key(int kid)
{
	typedef QValueList<int> intlist;
	intlist idlist;
	
	printf("delete_key(%i)\n", kid);
	
	MadSQLiteResult *res = conf->db->preparef("SELECT id FROM conf_key WHERE parent_id=%i;", kid);
	if(!res)
		return false;
	
	while(res->step() == MadDB_ROW) {
		idlist.append(res->columnInt(0));
	}
	
	delete res;
	
	intlist::iterator it;
	for(it=idlist.begin(); it != idlist.end(); ++it)
		if(!delete_key(*it))
			return false;
	
	bool r = conf->db->queryf("DELETE FROM conf_key WHERE id=%i;", id);
	if(!r) {
		printf("failed key: %s\n", conf->db->error_message());
		return false;
	}

	printf("DELETE KEY\n");
	
	r = conf->db->queryf("DELETE FROM conf_data WHERE key_id=%i;", id);
	if(!r) {
		printf("failed data: %s\n", conf->db->error_message());
		return false;
	}
	
	printf("DELETE DATA\n");
	
	r = conf->db->queryf("DELETE FROM conf_data_opt WHERE key_id=%i;", id);
	if(!r) {
		printf("failed opt: %s\n", conf->db->error_message());
		return false;
	}
	
	printf("DELETE DATA OPT\n");
	
	return true;
}

bool MadConfKey::erase()
{
	printf("erase\n");

	conf->db->begin();
	
	if(!delete_key(id)) {
		conf->db->rollback();
		return false;
	}
	
	conf->db->commit();
	
	printf("end erase\n");
	
	return true;
}

MadConf::MadConf(const char *name) : result(0)
{
	db = new MadSQLite();
	db->database(name);
	
	if(!db->connect()) {
		const char *msg = db->error_message();
		delete db;
		mad_throw(MadException, msg);
	}
	if(!initDB())
		mad_throw(MadException, db->error_message());
}

MadConf::~MadConf()
{
	if(result)
		delete result;

	if(db)
		delete db;
}

bool MadConf::beginKey()
{
	result = db->prepare("SELECT id,type_id,name FROM conf_key WHERE parent_id = 0;");
	if(!result)
		return false;
	
	return true;
}

bool MadConf::step(MadConfKey &k)
{
	if(!result)
		return false;

	if(result->step() != MadDB_ROW) {
		delete result;
		result = 0;
		return false;
	}

	k = MadConfKey(this, result->columnInt(0), result->columnInt(1), result->columnText(2));
	
	return true;
}

bool MadConf::key(MadConfKey &k, int id)
{
	MadSQLiteResult *result = 0;

	result = db->preparef("SELECT type_id,name FROM conf_key WHERE id=%i;", id);
	if(!result)
		return MadConfKey();

	if(result->step() != MadDB_ROW) {
		delete result;
		return MadConfKey();
	}

	k = MadConfKey(this, id, result->columnInt(0), result->columnText(1));
	delete result;
	result = 0;

	return true;
}

bool MadConf::key(MadConfKey &k, const char *key)
{
	MadSQLiteResult *result = 0;

	result = db->preparef("SELECT id,type_id,name FROM conf_key WHERE name='%q';", key);
	if(!result)
		return MadConfKey();

	if(result->step() != MadDB_ROW) {
		delete result;
		return MadConfKey();
	}

	k = MadConfKey(this, result->columnInt(0), result->columnInt(1), result->columnText(2));
	delete result;
	result = 0;

	return true;
}


bool MadConf::newKey(MadConfKey &k, const char *keyname, int type)
{
	k = MadConfKey(this, 0, type, keyname);

	if(!k.insert_key())
		return false;

	return true;
}

bool MadConf::initDB()
{
	bool seen_key = false, seen_data = false, seen_data_opt = false;

	db->begin();

	MadSQLiteResult *result = db->prepare("SELECT tbl_name FROM sqlite_master WHERE type='table';");
	if(!result) {
		printf("prepare failed: '%s'\n", db->error_message());
		db->rollback();
		return false;
	}

	while(1) {
		int res = result->step();
		if(res != MadDB_ROW)
			break;

		const char *table = (const char *)result->columnText(0);
		printf("table: '%s'\n", table);

		if(strcmp(table, "conf_key") == 0) {
			seen_key = true;
			continue;
		}

		if(strcmp(table, "conf_data") == 0) {
			seen_data = true;
			continue;
		}

		if(strcmp(table, "conf_data_opt") == 0) {
			seen_data_opt = true;
			continue;
		}
	}

	delete result;

	if(seen_key == false) {
		int status = db->query(sql_create_key_table);
		if(status != MadDB_OK) {
			db->rollback();
			return false;
		}
	}

	if(seen_data == false) {
		int status = db->query(sql_create_data_table);
		if(status != MadDB_OK) {
			db->rollback();
			return false;
		}
	}

	if(seen_data_opt == false) {
		int status = db->query(sql_create_data_opt_table);
		if(status != MadDB_OK) {
			db->rollback();
			return false;
		}
	}

	db->commit();

	return true;
}