/* * AOPPA -- A program for extracting the AnarchyOnline resource database * Auno.org PostgreSQL output plugin. * * Copyright (C) 2003, 2004 Oskari Saarenmaa . * All rights reserved. * * $Id: aunoorg-output.cc,v 1.19 2004/02/17 11:48:18 pickett Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include #include #include #include #include #include #include #include "aoppa.h" #include "misc.h" #include "parser.h" using namespace std; #define AOPF_RO 0x01 #define AOPF_IDX 0x80 static void aunoorg_new_finish(); static void aunoorg_new_store(void *); static void load_database(); static int flush_sqlmap(); static inline void copyescape(char *, char *, size_t); static inline PGresult *execf(const char *, ...); map > sqlmap; map idpatch; map noncurrent; map patchadded; PGconn *dbconn; int options, qid, xid, tid; string tableprefix; static void auno_init(config_vars_t *cfg) { config_vars_t::const_iterator cfgval; const char *dbname, *dbuser, *tmp; options = qid = xid = tid = 0; if(((cfgval = cfg->find("ro")) != cfg->end()) && cfgval->second == "yes") { debugf(1, "Read-only mode.\n"); options |= AOPF_RO; } dbname = aoppa_get_config(cfg, "dbname"); dbuser = aoppa_get_config(cfg, "dbuser"); if((tmp = aoppa_get_config(cfg, "xid"))) { xid = atoi(tmp); } if((tmp = aoppa_get_config(cfg, "prefix"))) { tableprefix = tmp; } else { tableprefix = "aodb"; } if(dbname && dbuser) { char connstr[0xff]; snprintf(connstr, 0xff, "dbname=%s user=%s", dbname, dbuser); dbconn = PQconnectdb(connstr); if(dbconn == NULL) { debugf(0, "Database structure allocation failed, out of memory?\n"); exit(1); } if(PQstatus(dbconn) != CONNECTION_OK) { debugf(0, "Database login failed.\n"); exit(1); } } else { debugf(0, "Auno.org-database plugin requires valid pgsql login credentials.\n"); exit(1); } load_database(); } static void load_database() { PGresult *q; int rows, i; debugf(1, "Loading hashes from the database..."); debugf(5, "\n"); q = execf("select aoid, hash, patchadded from %s where current is true", tableprefix.c_str()); rows = PQntuples(q); if(!rows) { debugf(1, "the database is empty.\n"); PQclear(q); options |= AOPF_IDX; return; } for(i=0; i %08x [%d]\n", id, sum, pad); set_item_hash(id, (unsigned int)sum); noncurrent[id] = 0; patchadded[id] = pad; } PQclear(q); debugf(5, "OK, read "); debugf(1, "%d\n", rows); debugf(1, "Loading nano crystals from the database..."); debugf(5, "\n"); q = execf("select A.aoid, N.crystal, C.ql from %s as A join %s_nano as N on (A.current and A.xid = N.xid and N.crystal != -1) join %s C on (C.current and C.aoid = N.crystal)", tableprefix.c_str(), tableprefix.c_str(), tableprefix.c_str()); rows = PQntuples(q); for(i=0; iaoid) != noncurrent.end()) noncurrent[item->aoid] = item->patch; else noncurrent[item->aoid] = 0; if(patchadded[item->aoid] == 0) patchadded[item->aoid] = item->patch; ++xid; snprintf(sql, BLOB, "%d\t%d\t%c\t" "%d\t%d\t%c\t" "%d\t%d\t%d\t" "%d\t%d\t%d\n", xid, (int)(item->hash), item->metatype, item->aoid, item->patch, 't', item->flags, item->props, item->ql, item->type, item->slot, patchadded[item->aoid]); debugf(7, "AODB: %s", sql); sqlmap[tableprefix].push_back(sql); snprintf(sql, BLOB, "%d\t%d\t%d\t" "%d\t%d\t%d\t" "%d\t%d\t%d\t" "%d\t%d\t%d\t" "%d\t%d\t%d\t", xid, item->icon, item->defslot, item->value, item->tequip, item->tattack, item->trecharge, item->dmin, item->dmax, item->dcrit, item->dtype, item->clip, item->atype, item->duration, item->range); ESCAPEAPPEND(sql, item->name, "\t"); ESCAPEAPPEND(sql, item->info, "\n"); debugf(7, "AODB_EXT: %s", sql); sqlmap["ext"].push_back(sql); if(item->metatype == 'n') { snprintf(sql, BLOB, "%d\t%d\t%d\t" "%d\t%d\t%d\n", xid, item->crystal, item->ncu, item->nanocost, item->school, item->strain); debugf(7, "AODB_NANO: %s", sql); sqlmap["nano"].push_back(sql); } debugf(8, "Starting to store requirements\n"); list::iterator reqi; for(reqi=item->reqs.begin(); reqi!=item->reqs.end(); reqi++) { snprintf(sql, BLOB, "%d\t%d\t%d\t%d\t%d\t%d\t%d\n", xid, reqi->id, reqi->type, reqi->attribute, reqi->count, reqi->op, reqi->opmod); debugf(7, "AODB_REQ: %s", sql); sqlmap["req"].push_back(sql); } debugf(8, "Starting to store effects\n"); list::iterator effi; for(effi=item->effs.begin(); effi!=item->effs.end(); effi++) { int reqid = 0; reqi=effi->reqs.begin(); if(reqi != effi->reqs.end()) { reqid = --qid; for(; reqi!=effi->reqs.end(); reqi++) { snprintf(sql, BLOB, "%d\t%d\t%d\t%d\t%d\t%d\t%d\n", reqid, reqi->id, reqi->type, reqi->attribute, reqi->count, reqi->op, reqi->opmod); debugf(7, "AODB_REQ: %s", sql); sqlmap["req"].push_back(sql); } } snprintf(sql, BLOB, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", xid, effi->hook, effi->type, reqid, effi->hits, effi->delay, effi->target, effi->values[0], effi->values[1], effi->values[2], effi->values[3], effi->values[4]); ESCAPEAPPEND(sql, effi->text, "\n"); debugf(7, "AODB_EFF: %s", sql); sqlmap["eff"].push_back(sql); } debugf(8, "Starting to store 'other' data\n"); map::iterator iii; for(iii=item->other.begin(); iii!=item->other.end(); iii++) { snprintf(sql, BLOB, "%d\t%d\t%d\n", xid, iii->first, iii->second); debugf(7, "AODB_OTHER: %s", sql); sqlmap["other"].push_back(sql); } debugf(8, "Starting to store skills/att\n"); for(iii=item->attmap.begin(); iii!=item->attmap.end(); iii++) { snprintf(sql, BLOB, "%d\t%d\t%d\t%d\n", xid, 'a', iii->first, iii->second); debugf(7, "AODB_SKILLS[a]: %s", sql); sqlmap["skill"].push_back(sql); } debugf(8, "Starting to store skills/def\n"); for(iii=item->defmap.begin(); iii!=item->defmap.end(); iii++) { snprintf(sql, BLOB, "%d\t%d\t%d\t%d\n", xid, 'd', iii->first, iii->second); debugf(7, "AODB_SKILLS[d]: %s", sql); sqlmap["skill"].push_back(sql); } debugf(6, "Done storing item.\n"); } static int flush_sqlmap() { int cnt = 0; debugf(1, "Entering tables in the database.. "); map >::iterator sli; for(sli=sqlmap.begin(); sli!=sqlmap.end(); sli++) { char table[20]; debugf(1, "%s:%d ", sli->first.c_str(), sli->second.size()); debugf(5, "\n"); if(sli->first == tableprefix) strcpy(table, tableprefix.c_str()); else sprintf(table, "%s_%s", tableprefix.c_str(), sli->first.c_str()); if(!(options & AOPF_RO)) execf("copy %s from stdin", table); list::iterator sti; for(sti=sli->second.begin(); sti!=sli->second.end(); sti++) { debugf(5, "Putting line: %s", sti->c_str()); if(!(options & AOPF_RO)) PQputline(dbconn, sti->c_str()); cnt ++; } if(!(options & AOPF_RO)) { PQputline(dbconn, "\\.\n"); PQendcopy(dbconn); } } sqlmap.clear(); debugf(1, ".. done\n"); return cnt; } static void aunoorg_new_finish() { int cnt = 0; list update_sql; char indexname[0x20]; map::iterator ii; for(ii=noncurrent.begin(); ii!=noncurrent.end(); ii++) { if(ii->second > 0) { char sqltmp[0xff]; sprintf(sqltmp, "update %s set current = false where current and aoid = %d and patch < %d", tableprefix.c_str(), ii->first, ii->second); debugf(DEBUG_INFO, "SQL: %s\n", sqltmp); if(!(options & AOPF_RO)) update_sql.push_back(sqltmp); cnt ++; } } noncurrent.clear(); if(cnt == 0) { debugf(1, "The database's current fields do not need updating.\n"); PQfinish(dbconn); return; } debugf(1, "Updating the database's 'current' fields %d (%d) times..", cnt, update_sql.size()); debugf(DEBUG_INFO, "\n"); if(options & AOPF_IDX) { debugf(DEBUG_INFO, "Creating temporary index for aoid and patch.\n"); if(!(options & AOPF_RO)) { snprintf(indexname, sizeof indexname, "aoppa_tmp_idx_%ld", (long)time(NULL)); execf("create index %s on %s (aoid, patch)", indexname, tableprefix.c_str()); execf("analyze %s", tableprefix.c_str()); } } if(!(options & AOPF_RO)) { for(list::iterator si=update_sql.begin(); si!=update_sql.end(); si++) execf("%s", si->c_str()); update_sql.clear(); } if(options & AOPF_IDX) { debugf(DEBUG_INFO, "Dropping temporary index for aoid and patch.\n"); if(!(options & AOPF_RO)) execf("drop index %s", indexname); } debugf(1, "..done\n"); PQfinish(dbconn); return; } static inline void copyescape(char *to, char *from, size_t len) { unsigned int i; for(i=0; i