Esempio n. 1
0
int cMsgList::DeliverMessagesSinceSync(unsigned sync)
{
	db_iterator it;
	int n = 0;
	cUser *user = NULL;
	nMySQL::cQuery DelQ(mQuery);

	SetBaseTo(&mModel);
	mQuery.Clear();
	SelectFields(mQuery.OStream());
	mQuery.OStream() << "WHERE date_sent >=" << sync;


	for(it = db_begin(); it != db_end(); ++it, ++n ) {
		if (!user || user->mNick != mModel.mReceiver)
			user = mServer->mUserList.GetUserByNick(mModel.mReceiver);

		if(user) {
			DeliverModelToUser(user);
			DelQ.Clear();
			DelQ.OStream() << "DELETE FROM " << mMySQLTable.mName;
			WherePKey(DelQ.OStream());
			DelQ.Query();
		}
	}

	DelQ.Clear();
	mQuery.Clear();
	return n;
}
Esempio n. 2
0
void cIPLog::GetLastLogin(const string &who, bool isNick, int limit, ostream &os)
{
	string ip;
	if(isNick)
		os << autosprintf(_("Nick %s has lately been in the hub with the following IP"), who.c_str()) << "\n";
	else
		os << autosprintf(_("IP %s has lately been in the hub with following nicknames"), who.c_str()) << "\n";

	MakeSearchQuery(who, isNick, 1, limit);
	SetBaseTo(&mModel);

	os << "\n ";
	os << setw(25) << setiosflags(ios::left) << toUpper(_("Date"));
	os << (isNick ? "IP" : toUpper(_("Nickname"))) << "\n";
	os << " " << string(25+20,'=') << endl;

	db_iterator it;
	for(it = db_begin(); it != db_end(); ++it) {
		cBanList::Num2Ip(mModel.mIP, ip);
		os << " " << setw(25) << setiosflags(ios::left) << cTime(mModel.mDate,0).AsDate();
		os << (isNick ? ip : mModel.mNick) << endl;
	}

	mQuery.Clear();
}
Esempio n. 3
0
int cMsgList::DeliverMessagesForUser(cUser *dest)
{
	db_iterator it;
	int n = 0;
	long max_date = 0;

	mQuery.Clear();
	SelectFields(mQuery.OStream());
	mQuery.OStream() << "WHERE "  << "receiver" << "='" ;
	WriteStringConstant(mQuery.OStream(),dest->mNick);
	mQuery.OStream()<< "'";

	SetBaseTo(&mModel);

	for( it = db_begin(); it != db_end(); ++it, ++n ) {
		if (mModel.mDateSent > max_date)
			max_date = mModel.mDateSent;
		DeliverModelToUser(dest);
	}

	mQuery.Clear();
	mQuery.OStream() << "DELETE FROM " << mMySQLTable.mName << " WHERE receiver = '" ;
	WriteStringConstant(mQuery.OStream(),dest->mNick);
	mQuery.OStream() << "' AND date_sent <= " << max_date;
	mQuery.Query();

	return n;
}
Esempio n. 4
0
void cIPLog::GetHistory(const string &who, bool isNick, int limit, ostream &os)
{
	string ip;
	if(isNick)
		os << autosprintf(_("Last %d events of nick %s:"), limit, who.c_str()) << "\r\n";
	else
		os << autosprintf(_("Last %d events of IP %s:"), limit, who.c_str()) << "\r\n";

	MakeSearchQuery(who, isNick, -1, limit);
	SetBaseTo(&mModel);

	const char *Actions[]={_("connect"),_("login"),_("logout"),_("disconnect")};
	const char *Infos[]={
	    "--",
		_("bad nick or nick temporarily banned"),
		_("used different nick in chat"),
		_("kicked"),
		_("redirected"),
		_("exit from the hub"),
		_("critical hub load"),
		_("timeout"),
		_("user did nothing for too long time"),
		_("hub full"),
		_("share limit"),
		_("no tag or not valid"),
		_("tag breaks hub rules"),
		_("wrong password"),
		_("error in login sequence"),
		_("syntax error in some messages"),
		_("invalid key")
	};

	os << "\n ";
	os << setw(20) << setiosflags(ios::left) << toUpper(_("Date"));
	os << setw(20) << setiosflags(ios::left) << toUpper(_("Action"));
	os << setw(15) << setiosflags(ios::left) << (isNick ? "IP" : toUpper(_("Nickname")));
	os << toUpper(_("Info")) << "\n";
	os << " " << string(20+20+15+25,'=') << endl;

	db_iterator it;
	for(it = db_begin(); it != db_end(); ++it) {
		cBanList::Num2Ip(mModel.mIP, ip);
		os << " " <<  setw(20) << setiosflags(ios::left) << cTime(mModel.mDate,0).AsDate();
		os << setw(20) << setiosflags(ios::left);
		if(mModel.mType < 4)
			os << Actions[mModel.mType];
		else
			os << mModel.mType;
		os << setw(15) << setiosflags(ios::left) << (isNick ? ip : mModel.mNick.substr(0,14));
		if(mModel.mInfo < 16) {
			if(strlen(Infos[mModel.mInfo]) > 0)
				os << Infos[mModel.mInfo];
		} else
			os << mModel.mInfo;
		os << endl;
	}

	mQuery.Clear();
}
Esempio n. 5
0
int cMsgList::PrintSubjects(ostream &os, const string &nick, bool IsSender)
{
	int n = 0;
	mQuery.Clear();
	SelectFields(mQuery.OStream());
	mQuery.OStream() << "WHERE "  << (IsSender ? "sender" : "receiver") << "='";
	WriteStringConstant(mQuery.OStream(), nick );
	mQuery.OStream() << "'";
	db_iterator it;
	SetBaseTo(&mModel);
	for(it = db_begin(); it != db_end(); ++it) {
		os << mModel.AsSubj() << endl;
		n++;
	}
	mQuery.Clear();
	return 0;
}
Esempio n. 6
0
void mbb_stat_pool_save(struct mbb_stat_pool *pool)
{
	static GStaticMutex mutex = G_STATIC_MUTEX_INIT;

	mbb_log_lvl_t mask;

	mbb_log_mask(LOG_MASK_DEL, MBB_LOG_QUERY, &mask);

	if (! g_static_mutex_trylock(&mutex)) {
		mbb_log("wait for mutex");
		g_static_mutex_lock(&mutex);
	}

	if (! mbb_task_poll_state())
		goto out;

	mbb_log("save to db");

	if (! db_begin())
		goto out;

	if (! save_records(pool->ustat, unit_rec_save))
		goto rollback;

	if (! save_records(pool->lstat, link_rec_save))
		goto rollback;

	if (! save_records(pool->ulstat, unit_link_rec_save))
		goto rollback;

	db_commit();
	goto out;

rollback:
	db_rollback();
out:
	mbb_log_mask(LOG_MASK_SET, mask, NULL);
	g_static_mutex_unlock(&mutex);
	mbb_stat_pool_free(pool);
}
Esempio n. 7
0
cConfMySQL::db_iterator &cConfMySQL::db_begin()
{
	return db_begin(mQuery);
}
Esempio n. 8
0
/**
 * The core parse function.  Parses an XML file and stores it in the database according to
 * the xmlparser.xsl template.
 *
 * @param thrdata  Pointer to a threadData_t structure with database connection, log context, settings, etc
 * @param job      Pointer to a parseJob_t structure containing the job information
 *
 * @return Return values:
 * @code
 *          STAT_SUCCESS  : Successfully registered report
 *          STAT_FTOOBIG  : XML report file is too big
 *          STAT_XMLFAIL  : Could not parse the XML report file
 *          STAT_SYSREG   : Failed to register the system into the systems or systems_hostname tables
 *          STAT_RTERIDREG: Failed to get a new rterid value
 *          STAT_GENDB    : Failed to start an SQL transaction (BEGIN)
 *          STAT_RTEVRUNS : Failed to register the rteval run into rtevalruns or rtevalruns_details
 *          STAT_CYCLIC   : Failed to register the data into cyclic_statistics or cyclic_rawdata tables
 *          STAT_REPMOVE  : Failed to move the report file
 * @endcode
 */
inline int parse_report(threadData_t *thrdata, parseJob_t *job)
{
	int syskey = -1, rterid = -1;
	int rc = -1;
	xmlDoc *repxml = NULL;
	char *destfname;

	// Check file size - and reject too big files
	if( check_filesize(thrdata, job->filename) == 0 ) {
		writelog(thrdata->dbc->log, LOG_ERR,
			 "[Thread %i] (submid: %i) Report file '%s' is too big, rejected",
			 thrdata->id, job->submid, job->filename);
		return STAT_FTOOBIG;
	}


	repxml = xmlParseFile(job->filename);
	if( !repxml ) {
		writelog(thrdata->dbc->log, LOG_ERR,
			 "[Thread %i] (submid: %i) Could not parse XML file: %s",
			 thrdata->id, job->submid, job->filename);
	        return STAT_XMLFAIL;
	}

	pthread_mutex_lock(thrdata->mtx_sysreg);
	syskey = db_register_system(thrdata->dbc, thrdata->xslt, repxml);
	if( syskey < 0 ) {
		writelog(thrdata->dbc->log, LOG_ERR,
			 "[Thread %i] Failed to register system (submid: %i, XML file: %s)",
			 thrdata->id, job->submid, job->filename);
		rc = STAT_SYSREG;
		goto exit;

	}
	rterid = db_get_new_rterid(thrdata->dbc);
	if( rterid < 0 ) {
		writelog(thrdata->dbc->log, LOG_ERR,
			 "[Thread %i] Failed to register rteval run (submid: %i, XML file: %s)",
			 thrdata->id, job->submid, job->filename);
		rc = STAT_RTERIDREG;
		goto exit;
	}
	pthread_mutex_unlock(thrdata->mtx_sysreg);

	if( db_begin(thrdata->dbc) < 1 ) {
		rc = STAT_GENDB;
		goto exit;
	}

	// Create a new filename of where to save the report
	destfname = get_destination_path(thrdata->dbc->log, thrdata->destdir, job, rterid);
	if( !destfname ) {
		writelog(thrdata->dbc->log, LOG_ERR,
			 "[Thread %i] Failed to generate local report filename for (submid: %i) %s",
			 thrdata->id, job->submid, job->filename);
		db_rollback(thrdata->dbc);
		rc = STAT_UNKNFAIL;
		goto exit;
	}

	if( db_register_rtevalrun(thrdata->dbc, thrdata->xslt, repxml, job->submid,
				  syskey, rterid, destfname) < 0 ) {
		writelog(thrdata->dbc->log, LOG_ERR,
			 "[Thread %i] Failed to register rteval run (submid: %i, XML file: %s)",
			 thrdata->id, job->submid, job->filename);
		db_rollback(thrdata->dbc);
		rc = STAT_RTEVRUNS;
		goto exit;
	}

	if( db_register_cyclictest(thrdata->dbc, thrdata->xslt, repxml, rterid) != 1 ) {
		writelog(thrdata->dbc->log, LOG_ERR,
			 "[Thread %i] Failed to register cyclictest data (submid: %i, XML file: %s)",
			 thrdata->id, job->submid, job->filename);
		db_rollback(thrdata->dbc);
		rc = STAT_CYCLIC;
		goto exit;
	}

	// When all database registrations are done, move the file to it's right place
	if( make_report_dir(thrdata->dbc->log, destfname) < 1 ) { // Make sure report directory exists
		db_rollback(thrdata->dbc);
		rc = STAT_REPMOVE;
		goto exit;
	}

	if( rename(job->filename, destfname) < 0 ) { // Move the file
		writelog(thrdata->dbc->log, LOG_ERR,
			 "[Thread %i] (submid: %i) Failed to move report file from %s to %s (%s)",
			 thrdata->id, job->submid, job->filename, destfname, strerror(errno));
		db_rollback(thrdata->dbc);
		rc = STAT_REPMOVE;
		goto exit;
	}
	free_nullsafe(destfname);

	rc = STAT_SUCCESS;
	db_commit(thrdata->dbc);
	writelog(thrdata->dbc->log, LOG_INFO,
		 "[Thread %i] Report parsed and stored (submid: %i, rterid: %i)",
		 thrdata->id, job->submid, rterid);
 exit:
	xmlFreeDoc(repxml);
	return rc;
}
Esempio n. 9
0
static void
blobcache_prune(sqlite3 *db)
{
    int rc;
    sqlite3_stmt *sel, *del;
    int64_t currentsize, pruned_bytes = 0;
    int pruned_items = 0;
    if(db_begin(db))
        return;

    if(db_get_int64_from_query(db, "SELECT sum(length(payload)) FROM item",
                               &currentsize)) {
        db_rollback(db);
        return;
    }
    estimated_cache_size = currentsize;

    uint64_t limit = blobcache_compute_maxsize(currentsize) * 9 / 10;
    if(currentsize <= limit) {
        db_rollback(db);
        return;
    }

    rc = sqlite3_prepare_v2(db,
                            "SELECT _rowid_,length(payload) "
                            "FROM item "
                            "ORDER BY lastaccess",
                            -1, &sel, NULL);
    if(rc != SQLITE_OK) {
        TRACE(TRACE_ERROR, "SQLITE", "SQL Error %d at %s:%d",
              rc, __FUNCTION__, __LINE__);
        db_rollback(db);
        return;
    }

    while((rc = sqlite3_step(sel)) == SQLITE_ROW) {
        int itemsize = sqlite3_column_int(sel, 1);
        int64_t id = sqlite3_column_int64(sel, 0);

        rc = sqlite3_prepare_v2(db, "DELETE FROM item WHERE _rowid_ = ?1",
                                -1, &del, NULL);
        if(rc != SQLITE_OK) {
            TRACE(TRACE_ERROR, "SQLITE", "SQL Error at %s:%d",
                  __FUNCTION__, __LINE__);
            sqlite3_finalize(sel);
            db_rollback(db);
            return;
        }
        sqlite3_bind_int(del, 1, id);
        rc = sqlite3_step(del);
        sqlite3_finalize(del);

        if(rc != SQLITE_DONE) {
            sqlite3_finalize(sel);
            db_rollback(db);
            return;
        }

        currentsize -= itemsize;
        pruned_bytes += itemsize;
        pruned_items++;
        if(currentsize <= limit)
            break;
    }
    TRACE(TRACE_DEBUG, "CACHE",
          "Pruned %d items, %"PRId64" bytes from cache",
          pruned_items, pruned_bytes);
    estimated_cache_size = currentsize;
    sqlite3_finalize(sel);
    db_commit(db);

}
Esempio n. 10
0
static void *
blobcache_get0(sqlite3 *db, const char *key, const char *stash,
               size_t *sizep, int pad, int *is_expired,
               char **etagp, time_t *mtimep)
{
    int rc;
    void *rval = NULL;
    sqlite3_stmt *stmt;
    time_t now;

    if(db_begin(db))
        return NULL;

    rc = sqlite3_prepare_v2(db,
                            "SELECT payload,expiry,etag,modtime FROM item "
                            "WHERE k=?1 AND stash=?2",
                            -1, &stmt, NULL);
    if(rc) {
        db_rollback(db);
        return NULL;
    }

    time(&now);

    sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, stash, -1, SQLITE_STATIC);
    rc = sqlite3_step(stmt);

    if(rc != SQLITE_ROW) {
        sqlite3_finalize(stmt);
        db_rollback(db);
        return NULL;
    }

    int expired = now > sqlite3_column_int64(stmt, 1);

    if(!expired || is_expired != NULL) {
        sqlite3_column_blob(stmt, 0);
        size_t size = sqlite3_column_bytes(stmt, 0);
        if(size > 0) {
            rval = malloc(size + pad);
            memset(rval + size, 0, pad);
            memcpy(rval, sqlite3_column_blob(stmt, 0), size);
            *sizep = size;
        }
    }

    if(is_expired != NULL)
        *is_expired = expired;

    if(etagp != NULL) {
        const char *str = (const char *)sqlite3_column_text(stmt, 2);
        if(str != NULL)
            *etagp = strdup(str);
    }

    if(mtimep != NULL)
        *mtimep = sqlite3_column_int(stmt, 3);


    sqlite3_finalize(stmt);

    // Update atime

    rc = sqlite3_prepare_v2(db,
                            "UPDATE item SET "
                            "lastaccess = ?3 "
                            "WHERE k = ?1 AND stash = ?2",
                            -1, &stmt, NULL);

    if(rc != SQLITE_OK) {
        TRACE(TRACE_ERROR, "SQLITE", "SQL Error at %s:%d",
              __FUNCTION__, __LINE__);
        db_rollback(db);
        return rval;
    } else {
        sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
        sqlite3_bind_text(stmt, 2, stash, -1, SQLITE_STATIC);
        sqlite3_bind_int(stmt, 3, time(NULL));
        rc = sqlite3_step(stmt);
        sqlite3_finalize(stmt);
    }

    db_commit(db);
    return rval;
}
Esempio n. 11
0
static JSBool
js_db_txn(JSContext *cx, JSObject *obj, uintN argc,
          jsval *argv, jsval *rval)
{
    js_db_t *jd = JS_GetPrivate(cx, obj);

    if(js_db_check(cx, jd))
        return JS_FALSE;

    if(argc != 1) {
        JS_ReportError(cx, "Invalid number of arguments");
        return JS_FALSE;
    }

    if(jd->jd_transaction) {
        JS_ReportError(cx, "Nesting transactions is not allowed");
        return JS_FALSE;
    }

retry:
    if(db_begin(jd->jd_db)) {
        JS_ReportError(cx, "Failed to start transaction");
        return JS_FALSE;
    }

    jd->jd_transaction = 1;

    int r = JS_CallFunctionValue(cx, NULL, argv[0], 0, NULL, rval);
    jd->jd_transaction = 0;

    if(jd->jd_stmt) {
        sqlite3_finalize(jd->jd_stmt);
        jd->jd_stmt = NULL;
    }

    if(!r) {
        if(jd->jd_debug)
            TRACE(TRACE_DEBUG, "JS", "Transaction rollbacked due to error");
        db_rollback(jd->jd_db);
        if(JS_IsExceptionPending(cx)) {
            jsval exn;
            if(!JS_GetPendingException(cx, &exn)) {
                return JS_FALSE;
            }
            if(JSVAL_IS_OBJECT(exn)) {
                JSClass *c = JS_GetClass(cx, JSVAL_TO_OBJECT(exn));
                if(c == &db_deadlock_exn) {
                    if(jd->jd_debug)
                        TRACE(TRACE_DEBUG, "JS", "Catched deadlock exception, retrying");
                    JS_ClearPendingException(cx);
                    JS_BeginRequest(cx);
                    usleep(100000);
                    JS_EndRequest(cx);
                    goto retry;
                }
            }
        }
        return JS_FALSE;
    }

    if(*rval == JSVAL_TRUE) {
        if(jd->jd_debug)
            TRACE(TRACE_DEBUG, "JS", "Transaction committed");

        db_commit(jd->jd_db);
    } else {
        if(jd->jd_debug)
            TRACE(TRACE_DEBUG, "JS", "Transaction rollbacked");
        db_rollback(jd->jd_db);
    }
    return JS_TRUE;
}
Esempio n. 12
0
File: brain.c Progetto: lp0/sqlhal
int main(int argc, char *argv[]) {
	char *action;
	char *name;
	char *prefix;
	int ret;
	int fail = 0;
	char *state;

	if (argc != 4 || (strcmp(argv[1], "load") && strcmp(argv[1], "save") && strcmp(argv[1], "save+"))) {
		printf("Brain manipulation\n");
		printf("Usage: %s load  <name> <filename prefix>\n", argv[0]);
		printf("       %s save  <name> <filename prefix>\n", argv[0]);
		printf("       %s save+ <name> <filename prefix>\n", argv[0]);
		return 1;
	}

	action = argv[1];
	name = argv[2];
	prefix = argv[3];

	state = "db_connect";
	ret = db_connect();
	if (ret) goto fail;
	else log_info("brain", ret, state);

	state = "db_begin";
	ret = db_begin();
	if (ret) goto fail;
	else log_info("brain", ret, state);

	if (!strcmp(action, "load")) {
		state = "input_list aux";
		ret = input_list(name, prefix, "aux", LIST_AUX);
		if (ret) { log_warn("brain", ret, state); fail = 1; }
		else log_info("brain", ret, state);

		state = "input_list ban";
		ret = input_list(name, prefix, "ban", LIST_BAN);
		if (ret) { log_warn("brain", ret, state); fail = 1; }
		else log_info("brain", ret, state);

		state = "input_list grt";
		ret = input_list(name, prefix, "grt", LIST_GREET);
		if (ret) { log_warn("brain", ret, state); fail = 1; }
		else log_info("brain", ret, state);

		state = "input_map swp";
		ret = input_map(name, prefix, "swp", MAP_SWAP);
		if (ret) { log_warn("brain", ret, state); fail = 1; }
		else log_info("brain", ret, state);

		state = "input_brain";
		ret = input_brain(name, prefix);
		if (ret) { log_warn("brain", ret, state); fail = 1; }
		else log_info("brain", ret, state);
	} else if (!strcmp(action, "save") || !strcmp(action, "save+")) {
		enum file_type type = FILETYPE_MEGAHAL8;
		if (!strcmp(action, "save+"))
			type = FILETYPE_SQLHAL0;

		state = "output_list aux";
		ret = output_list(name, prefix, "aux", LIST_AUX);
		if (ret) { log_warn("brain", ret, state); fail = 1; }
		else log_info("brain", ret, state);

		state = "output_list ban";
		ret = output_list(name, prefix, "ban", LIST_BAN);
		if (ret) { log_warn("brain", ret, state); fail = 1; }
		else log_info("brain", ret, state);

		state = "output_list grt";
		ret = output_list(name, prefix, "grt", LIST_GREET);
		if (ret) { log_warn("brain", ret, state); fail = 1; }
		else log_info("brain", ret, state);

		state = "output_map swp";
		ret = output_map(name, prefix, "swp", MAP_SWAP);
		if (ret) { log_warn("brain", ret, state); fail = 1; }
		else log_info("brain", ret, state);

		state = "output_brain";
		ret = output_brain(name, type, prefix);
		if (ret) { log_warn("brain", ret, state); fail = 1; }
		else log_info("brain", ret, state);
	} else {
		fail = 1;
	}

	if (fail) {
		state = "db_rollback";
		ret = db_rollback();
		if (ret) goto fail;
		else log_info("brain", ret, state);
	} else {
		state = "db_commit";
		ret = db_commit();
		if (ret) goto fail;
		else log_info("brain", ret, state);
	}

	state = "db_disconnect";
	ret = db_disconnect();
	if (ret) goto fail;
	else log_info("brain", ret, state);

	return 0;

fail:
	log_fatal("brain", ret, state);
	return 1;
}
Esempio n. 13
0
int db_connect(void) {
	if (conn == NULL) {
		conn = PQconnectdb("");

		if (conn == NULL)
			return -EDB;

		if (PQstatus(conn) != CONNECTION_OK) {
			log_error("DB", PQstatus(conn), PQerrorMessage(conn));
			PQfinish(conn);
			conn = NULL;
		} else {
			PGresult *res = NULL;
			const char *brains[] = { "brains" };
			const char *words[] = { "words" };
			const char *lists[] = { "lists" };
			const char *maps[] = { "maps" };
			const char *models[] = { "models" };
			const char *nodes[] = { "nodes" };
			int nodes_created = 0;
			int server_ver;

			server_ver = PQserverVersion(conn);
			if (server_ver < 80400) {
				log_error("DB", server_ver, "Server version must be 8.4.0+");
				PQfinish(conn);
				conn = NULL;
				return -EDB;
			}

			if (db_begin()) goto fail2;

			res = PQprepare(conn, "table_exists", "SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND tablename = $1", 1, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			/* BRAIN */

			res = PQexecPrepared(conn, "table_exists", 1, brains, NULL, NULL, 1);
			if (PQresultStatus(res) != PGRES_TUPLES_OK) goto fail;
			if (PQntuples(res) != 1) {
				PQclear(res);

				res = PQexec(conn, "CREATE TABLE brains (id BIGSERIAL UNIQUE, name TEXT,"\
					" PRIMARY KEY (name),"\
					" CONSTRAINT valid_id CHECK (id > 0))");
				if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			}
			PQclear(res);

			/* WORD */

			res = PQexecPrepared(conn, "table_exists", 1, words, NULL, NULL, 1);
			if (PQresultStatus(res) != PGRES_TUPLES_OK) goto fail;
			if (PQntuples(res) != 1) {
				PQclear(res);

				res = PQexec(conn, "CREATE TABLE words (id SERIAL UNIQUE, word TEXT, added TIMESTAMP NOT NULL DEFAULT NOW(),"\
					" PRIMARY KEY (word),"\
					" CONSTRAINT valid_id CHECK (id > 0))");
				if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			}
			PQclear(res);

			/* LIST */

			res = PQexecPrepared(conn, "table_exists", 1, lists, NULL, NULL, 1);
			if (PQresultStatus(res) != PGRES_TUPLES_OK) goto fail;
			if (PQntuples(res) != 1) {
				PQclear(res);

				res = PQexec(conn, "CREATE TABLE lists (type INT NOT NULL, brain BIGINT NOT NULL, word BIGINT NOT NULL,"\
					" PRIMARY KEY (brain, type, word),"\
					" FOREIGN KEY (brain) REFERENCES brains (id) ON UPDATE CASCADE ON DELETE CASCADE,"\
					" FOREIGN KEY (word) REFERENCES words (id) ON UPDATE CASCADE ON DELETE CASCADE,"\
					" CONSTRAINT valid_type CHECK (type >= 1 AND type <= 3))");
				if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
				PQclear(res);

				res = PQexec(conn, "CREATE INDEX lists_words ON lists (word)");
				if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			}
			PQclear(res);

			/* MAP */

			res = PQexecPrepared(conn, "table_exists", 1, maps, NULL, NULL, 1);
			if (PQresultStatus(res) != PGRES_TUPLES_OK) goto fail;
			if (PQntuples(res) != 1) {
				PQclear(res);

				res = PQexec(conn, "CREATE TABLE maps (type INT NOT NULL, brain BIGINT NOT NULL, key BIGINT NOT NULL, value BIGINT NOT NULL,"\
					" PRIMARY KEY (brain, key),"\
					" FOREIGN KEY (brain) REFERENCES brains (id) ON UPDATE CASCADE ON DELETE CASCADE,"\
					" FOREIGN KEY (key) REFERENCES words (id) ON UPDATE CASCADE ON DELETE CASCADE,"\
					" FOREIGN KEY (value) REFERENCES words (id) ON UPDATE CASCADE ON DELETE CASCADE,"\
					" CONSTRAINT valid_type CHECK (type = 4))");
				if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
				PQclear(res);

				res = PQexec(conn, "CREATE INDEX maps_keys ON maps (key)");
				if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
				PQclear(res);

				res = PQexec(conn, "CREATE INDEX maps_values ON maps (value)");
				if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			}
			PQclear(res);

			/* MODEL */

			res = PQexecPrepared(conn, "table_exists", 1, nodes, NULL, NULL, 1);
			if (PQresultStatus(res) != PGRES_TUPLES_OK) goto fail;
			if (PQntuples(res) != 1) {
				PQclear(res);

				res = PQexec(conn, "CREATE TABLE nodes (id BIGSERIAL UNIQUE, brain BIGINT NOT NULL, parent BIGINT, word BIGINT, usage BIGINT NOT NULL, count BIGINT NOT NULL,"\
					" PRIMARY KEY (brain, id),"\
					" FOREIGN KEY (parent) REFERENCES nodes (id) ON UPDATE CASCADE ON DELETE CASCADE,"\
					" FOREIGN KEY (word) REFERENCES words (id) ON UPDATE CASCADE ON DELETE CASCADE,"\
					" CONSTRAINT valid_id CHECK (id > 0),"\
					" CONSTRAINT valid_usage CHECK (usage >= 0),"\
					" CONSTRAINT valid_count CHECK (count >= 0),"\
					" CONSTRAINT valid_root CHECK (parent IS NOT NULL OR word IS NULL),"\
					" CONSTRAINT valid_fin CHECK (parent IS NULL OR word IS NOT NULL OR usage = 0))");
				if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
				PQclear(res);

				res = PQexec(conn, "CREATE INDEX nodes_words ON nodes (word)");
				if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
				PQclear(res);

				res = PQexec(conn, "CREATE UNIQUE INDEX nodes_child ON nodes (parent, word)");
				if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;

				nodes_created = 1;
			}
			PQclear(res);

			res = PQexecPrepared(conn, "table_exists", 1, models, NULL, NULL, 1);
			if (PQresultStatus(res) != PGRES_TUPLES_OK) goto fail;
			if (PQntuples(res) != 1) {
				PQclear(res);

				res = PQexec(conn, "CREATE TABLE models (brain BIGINT NOT NULL, contexts BIGINT NOT NULL, forward BIGINT, backward BIGINT,"\
					" PRIMARY KEY (brain),"\
					" FOREIGN KEY (brain) REFERENCES brains (id) ON UPDATE CASCADE ON DELETE CASCADE,"\
					" FOREIGN KEY (forward) REFERENCES nodes (id),"\
					" FOREIGN KEY (backward) REFERENCES nodes (id),"\
					" CONSTRAINT valid_order CHECK (contexts >= 0))");
				if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			}
			PQclear(res);

			if (nodes_created) {
				res = PQexec(conn, "ALTER TABLE nodes"\
					" ADD FOREIGN KEY (brain) REFERENCES models (brain) ON UPDATE CASCADE ON DELETE CASCADE");
				if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
				PQclear(res);
			}

			/* BRAIN */

			res = PQprepare(conn, "brain_add", "INSERT INTO brains (name) VALUES($1)", 1, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "brain_add_id", "SELECT currval('brains_id_seq')", 0, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "brain_get", "SELECT id FROM brains WHERE name = $1", 1, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			/* WORD */

			res = PQprepare(conn, "word_add", "INSERT INTO words (word) VALUES($1)", 1, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "word_add_id", "SELECT currval('words_id_seq')", 0, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "word_get", "SELECT id FROM words WHERE word = $1", 1, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "word_str", "SELECT word FROM words WHERE id = $1", 1, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			/* LIST */

			res = PQprepare(conn, "list_add", "INSERT INTO lists (brain, type, word) VALUES($1, $2, $3)", 3, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "list_get", "SELECT word FROM lists WHERE brain = $1 AND type = $2 AND word = $3", 3, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "list_iter", "SELECT lists.word, words.word FROM lists, words"\
				" WHERE brain = $1 AND type = $2 AND words.id = lists.word"\
				" ORDER BY words.word NULLS LAST", 2, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "list_zap", "DELETE FROM lists WHERE brain = $1 AND type = $2", 2, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			/* MAP */

			res = PQprepare(conn, "map_add", "INSERT INTO maps (brain, type, key, value) VALUES($1, $2, $3, $4)", 4, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "map_get", "SELECT value FROM maps WHERE brain = $1 AND type = $2 AND key = $3", 3, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "map_iter", "SELECT maps.key, maps.value, words_k.word, words_v.word"\
				" FROM maps, words AS words_k, words AS words_v"\
				" WHERE brain = $1 AND type = $2 AND words_k.id = maps.key AND words_v.id = maps.value"\
				" ORDER BY words_k.word NULLS LAST", 2, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "map_zap", "DELETE FROM maps WHERE brain = $1 AND type = $2", 2, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			/* MODEL */

			res = PQprepare(conn, "model_add", "INSERT INTO models (brain, contexts) VALUES($1, $2)", 2, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_get", "SELECT contexts FROM models WHERE brain = $1", 1, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_set", "UPDATE models SET contexts = $2 WHERE brain = $1", 1, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_zap", "DELETE FROM models WHERE brain = $1", 1, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_create", "INSERT INTO nodes (brain, usage, count) VALUES($1, 0, 0)", 1, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_fastcreate", "INSERT INTO nodes (brain, usage, count, word, parent) VALUES($1, $2, $3, $4, $5)", 5, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_create_id", "SELECT currval('nodes_id_seq')", 0, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_rootupdate", "UPDATE nodes SET parent = NULL, usage = $2, count = $3 WHERE id = $1", 3, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_update", "UPDATE nodes SET usage = $2, count = $3 WHERE id = $1", 3, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_root_get", "SELECT forward, backward FROM models WHERE brain = $1", 1, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_root_set", "UPDATE models SET forward = $2, backward = $3 WHERE brain = $1", 3, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_node_get", "SELECT id, word, usage, count FROM nodes"\
				" WHERE brain = $1 AND (id = $2 OR parent = $2)"\
				" ORDER BY (SELECT words.word FROM words WHERE words.id = nodes.word) NULLS LAST", 2, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_node_find", "SELECT id, word, usage, count FROM nodes"\
				" WHERE brain = $1 AND parent = $2 AND word = $3", 3, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_word_exists", "SELECT word FROM nodes WHERE brain = $1 AND word = $2 LIMIT 1", 2, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_word_random", "SELECT word FROM nodes WHERE brain = $1 AND parent = $2"\
				" ORDER BY random() LIMIT 1", 2, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_node_random", "SELECT id, word, usage, count FROM nodes"\
				" WHERE brain = $1 AND parent = $2"\
				" ORDER BY random() LIMIT 1", 2, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_node_first", "SELECT id, parent, word, usage, count FROM nodes"\
				" WHERE brain = $1 AND parent = $2"\
				" ORDER BY id LIMIT 1", 2, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_node_prev", "SELECT id, parent, word, usage, count FROM nodes"\
				" WHERE brain = $1 AND parent = $2 AND id < $3"\
				" ORDER BY id DESC LIMIT 1", 3, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_node_next", "SELECT id, parent, word, usage, count FROM nodes"\
				" WHERE brain = $1 AND parent = $2 AND id > $3"\
				" ORDER BY id LIMIT 1", 3, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_node_last", "SELECT id, parent, word, usage, count FROM nodes"\
				" WHERE brain = $1 AND parent = $2"\
				" ORDER BY id DESC LIMIT 1", 2, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = PQprepare(conn, "model_brain_words", "SELECT id, ROW_NUMBER() OVER (ORDER BY id) - 1, word "\
				" FROM words WHERE id IN (SELECT word FROM nodes WHERE brain=$1) ORDER BY word", 1, NULL);
			if (PQresultStatus(res) != PGRES_COMMAND_OK) goto fail;
			PQclear(res);

			res = NULL;

			if(db_commit()) goto fail2;

fail:
			if (res != NULL) {
				log_error("db_connect", PQresultStatus(res), PQresultErrorMessage(res));
				PQclear(res);
fail2:
				PQfinish(conn);
				conn = NULL;
			}
		}
	}

	if (conn == NULL)
		return -EDB;

	return OK;
}