3913 lines
103 KiB
C++
3913 lines
103 KiB
C++
/************************************************************************
|
|
MeOS - Orienteering Software
|
|
Copyright (C) 2009-2019 Melin Software HB
|
|
|
|
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 3 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
Melin Software HB - software@melin.nu - www.melin.nu
|
|
Eksoppsvägen 16, SE-75646 UPPSALA, Sweden
|
|
|
|
************************************************************************/
|
|
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include <fstream>
|
|
#include <cassert>
|
|
#include <typeinfo>
|
|
|
|
#include "MeosSQL.h"
|
|
|
|
#include "../oRunner.h"
|
|
#include "../oEvent.h"
|
|
#include "../meos_util.h"
|
|
#include "../RunnerDB.h"
|
|
#include "../progress.h"
|
|
#include "../metalist.h"
|
|
#include "../MeOSFeatures.h"
|
|
#include "../meosexception.h"
|
|
|
|
using namespace mysqlpp;
|
|
|
|
wstring fromUTF(const string w) {
|
|
const int buff_pre_alloc = 1024*8;
|
|
static wchar_t buff[buff_pre_alloc];
|
|
int len = w.length();
|
|
len = min(len+1, buff_pre_alloc-10);
|
|
int wlen = MultiByteToWideChar(CP_UTF8, 0, w.c_str(), len, buff, buff_pre_alloc);
|
|
buff[wlen-1] = 0;
|
|
return buff;
|
|
}
|
|
|
|
string toString(const wstring &w) {
|
|
string &output = StringCache::getInstance().get();
|
|
size_t alloc = w.length()*4+4;
|
|
output.resize(alloc);
|
|
WideCharToMultiByte(CP_UTF8, 0, w.c_str(), w.length()+1, (char *)output.c_str(), alloc, 0, 0);
|
|
output.resize(strlen(output.c_str()));
|
|
return output;
|
|
}
|
|
|
|
MeosSQL::MeosSQL(void)
|
|
{
|
|
monitorId=0;
|
|
warnedOldVersion=false;
|
|
buildVersion=getMeosBuild();
|
|
}
|
|
|
|
MeosSQL::~MeosSQL(void)
|
|
{
|
|
}
|
|
|
|
void MeosSQL::alert(const string &s)
|
|
{
|
|
errorMessage=s;
|
|
}
|
|
|
|
string C_INT(string name)
|
|
{
|
|
return " "+name+" INT NOT NULL DEFAULT 0, ";
|
|
}
|
|
|
|
string C_INT64(string name)
|
|
{
|
|
return " "+name+" BIGINT NOT NULL DEFAULT 0, ";
|
|
}
|
|
|
|
string C_STRING(string name, int len=64)
|
|
{
|
|
char bf[16];
|
|
sprintf_s(bf, "%d", len);
|
|
return " "+name+" VARCHAR("+ bf +") NOT NULL DEFAULT '', ";
|
|
}
|
|
|
|
string C_TEXT(string name)
|
|
{
|
|
return " "+name+" TEXT NOT NULL, ";
|
|
}
|
|
|
|
string C_MTEXT(string name)
|
|
{
|
|
return " "+name+" MEDIUMTEXT NOT NULL, ";
|
|
}
|
|
|
|
|
|
string C_UINT(string name)
|
|
{
|
|
return " "+name+" INT UNSIGNED NOT NULL DEFAULT 0, ";
|
|
}
|
|
|
|
string C_START(string name)
|
|
{
|
|
return "CREATE TABLE IF NOT EXISTS "+name+" (" +
|
|
" Id INT AUTO_INCREMENT NOT NULL, PRIMARY KEY (Id), ";
|
|
}
|
|
|
|
string C_START_noid(string name)
|
|
{
|
|
return "CREATE TABLE IF NOT EXISTS "+name+" (";
|
|
}
|
|
|
|
string C_END()
|
|
{
|
|
return " Modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, "
|
|
"Counter INT UNSIGNED NOT NULL DEFAULT 0, "
|
|
"INDEX(Counter), INDEX(Modified), Removed BOOL NOT NULL DEFAULT 0) "
|
|
"ENGINE = MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci";
|
|
}
|
|
|
|
string C_END_noindex()
|
|
{
|
|
return " Modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) "
|
|
"ENGINE = MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci";
|
|
}
|
|
|
|
string limitLength(const string &in, size_t max) {
|
|
if (in.length() < max)
|
|
return in;
|
|
else
|
|
return in.substr(0, max);
|
|
}
|
|
|
|
string limitLength(const wstring &in, size_t max) {
|
|
if (in.length() < max)
|
|
return toString(in);
|
|
else
|
|
return toString(in.substr(0, max));
|
|
}
|
|
|
|
|
|
bool MeosSQL::listCompetitions(oEvent *oe, bool keepConnection) {
|
|
errorMessage.clear();
|
|
CmpDataBase="";
|
|
if (oe->isClient())
|
|
throw std::exception("Runtime error.");
|
|
|
|
oe->serverName.clear();
|
|
|
|
if (!keepConnection) {
|
|
try {
|
|
con.connect("", oe->MySQLServer.c_str(), oe->MySQLUser.c_str(),
|
|
oe->MySQLPassword.c_str(), oe->MySQLPort);
|
|
}
|
|
catch (const Exception& er) {
|
|
alert(string(er.what()) + " MySQL Error");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!con.connected()) {
|
|
errorMessage = "Internal error connecting to MySQL";
|
|
return false;
|
|
}
|
|
|
|
string serverInfo = con.server_info();
|
|
|
|
if (serverInfo < "5.0.3") {
|
|
errorMessage = "Minst MySQL X krävs. Du använder version Y.#5.0.3#" + serverInfo;
|
|
return false;
|
|
}
|
|
|
|
serverName=oe->MySQLServer;
|
|
serverUser=oe->MySQLUser;
|
|
serverPassword=oe->MySQLPassword;
|
|
serverPort=oe->MySQLPort;
|
|
|
|
//Store verified server name
|
|
oe->serverName=serverName;
|
|
|
|
NoExceptions ne(con);
|
|
|
|
if (!con.select_db("MeOSMain")){
|
|
con.create_db("MeOSMain");
|
|
con.select_db("MeOSMain");
|
|
}
|
|
|
|
Query query = con.query();
|
|
|
|
try{
|
|
mysqlpp::Query queryset = con.query();
|
|
queryset << "SET NAMES UTF8";
|
|
queryset.execute();
|
|
}
|
|
catch (const mysqlpp::Exception& ){
|
|
}
|
|
|
|
query.reset();
|
|
|
|
try{
|
|
query << C_START("oEvent")
|
|
<< C_STRING("Name", 128)
|
|
<< C_STRING("Annotation", 128)
|
|
<< C_STRING("Date", 32)
|
|
<< C_UINT("ZeroTime")
|
|
<< C_STRING("NameId", 64)
|
|
<< " Version INT UNSIGNED DEFAULT 1, " << C_END();
|
|
|
|
query.execute();
|
|
|
|
query.reset();
|
|
Result res = query.store("DESCRIBE oEvent");
|
|
int nr = (int)res.num_rows();
|
|
if (nr == 9) {
|
|
query.execute("ALTER TABLE oEvent ADD COLUMN "
|
|
"Annotation VARCHAR(128) NOT NULL DEFAULT '' AFTER Name");
|
|
}
|
|
}
|
|
catch (const Exception& er) {
|
|
alert(string(er.what())+ " MySQL Error");
|
|
// Try a repair operation
|
|
try {
|
|
query.execute("REPAIR TABLE oEvent EXTENDED");
|
|
}
|
|
catch (const Exception&) {
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
query.reset();
|
|
|
|
try {
|
|
query << "SELECT * FROM oEvent";
|
|
|
|
Result res = query.store();
|
|
|
|
if (res) {
|
|
for (int i=0; i<res.num_rows(); i++) {
|
|
Row row=res.at(i);
|
|
|
|
if (int(row["Version"]) <= oe->dbVersion) {
|
|
CompetitionInfo ci;
|
|
ci.Name = fromUTF((string)row["Name"]);
|
|
ci.Annotation = fromUTF((string)row["Annotation"]);
|
|
ci.Id = row["Id"];
|
|
ci.Date = fromUTF((string)row["Date"]);
|
|
ci.FullPath = fromUTF((string)row["NameId"]);
|
|
ci.NameId = fromUTF((string)row["NameId"]);
|
|
ci.Server = oe->MySQLServer;
|
|
ci.ServerPassword = oe->MySQLPassword;
|
|
ci.ServerUser = oe->MySQLUser;
|
|
ci.ServerPort = oe->MySQLPort;
|
|
|
|
oe->cinfo.push_front(ci);
|
|
}
|
|
else {
|
|
CompetitionInfo ci;
|
|
ci.Name = fromUTF(string(row["Name"]));
|
|
ci.Date = fromUTF(string(row["Date"]));
|
|
ci.Annotation = fromUTF(string(row["Annotation"]));
|
|
ci.Id=0;
|
|
ci.Server="bad";
|
|
ci.FullPath=fromUTF(string(row["NameId"]));
|
|
oe->cinfo.push_front(ci);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (const Exception& er) {
|
|
// Try a repair operation
|
|
try {
|
|
query.execute("REPAIR TABLE oEvent EXTENDED");
|
|
}
|
|
catch (const Exception&) {
|
|
}
|
|
|
|
setDefaultDB();
|
|
alert(string(er.what()) + " MySQL Error");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool MeosSQL::repairTables(const string &db, vector<string> &output) {
|
|
// Update list database;
|
|
con.select_db(db);
|
|
output.clear();
|
|
|
|
if (!con.connected()) {
|
|
errorMessage = "Internal error connecting to MySQL";
|
|
return false;
|
|
}
|
|
|
|
Query q = con.query();
|
|
Result res = q.store("SHOW TABLES");
|
|
int numtab = (int)res.num_rows();
|
|
vector<string> tb;
|
|
for (int k = 0; k < numtab; k++)
|
|
tb.push_back(res.at(k).at(0).c_str());
|
|
|
|
for (int k = 0; k < numtab; k++) {
|
|
string sql = "REPAIR TABLE " + tb[k] + " EXTENDED";
|
|
try {
|
|
res = q.store(sql);
|
|
string msg;
|
|
Row row = res.at(0);
|
|
for (size_t j = 0; j < row.size(); j++) {
|
|
string t = row.at(j).get_string();
|
|
if (!msg.empty())
|
|
msg += ", ";
|
|
msg += t;
|
|
}
|
|
output.push_back(msg);
|
|
}
|
|
catch (const Exception &ex) {
|
|
string err1 = "FAILED: " + sql;
|
|
output.push_back(err1);
|
|
output.push_back(ex.what());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MeosSQL::createRunnerDB(oEvent *oe, Query &query)
|
|
{
|
|
query.reset();
|
|
|
|
query << C_START_noid("dbRunner")
|
|
<< C_STRING("Name", 64) << C_INT("CardNo")
|
|
<< C_INT("Club") << C_STRING("Nation", 3)
|
|
<< C_STRING("Sex", 1) << C_INT("BirthYear")
|
|
<< C_INT64("ExtId") << C_END_noindex();
|
|
|
|
query.execute();
|
|
|
|
query.reset();
|
|
query << C_START_noid("dbClub")
|
|
<< " Id INT NOT NULL, "
|
|
<< C_STRING("Name", 64)
|
|
<< oe->oClubData->generateSQLDefinition() << C_END_noindex();
|
|
query.execute();
|
|
|
|
// Ugrade dbClub
|
|
upgradeDB("dbClub", oe->oClubData);
|
|
|
|
return true;
|
|
}
|
|
|
|
void MeosSQL::getColumns(const string &table, set<string> &output) {
|
|
Query query = con.query();
|
|
output.clear();
|
|
Result res = query.store("DESCRIBE " + table);
|
|
for (size_t k = 0; k < res.size(); k++) {
|
|
output.insert((const char *)res.at(k).at(0));
|
|
}
|
|
}
|
|
|
|
void MeosSQL::upgradeDB(const string &db, oDataContainer const * dc) {
|
|
set<string> eCol;
|
|
getColumns(db, eCol);
|
|
|
|
Query query = con.query();
|
|
|
|
if (db == "oEvent") {
|
|
if (!eCol.count("Annotation")) {
|
|
query.execute("ALTER TABLE oEvent ADD COLUMN "
|
|
"Annotation VARCHAR(128) NOT NULL DEFAULT '' AFTER Name");
|
|
}
|
|
if (!eCol.count("Lists")) {
|
|
string sql = "ALTER TABLE oEvent ADD COLUMN " + C_MTEXT("Lists");
|
|
sql = sql.substr(0, sql.length() - 2);
|
|
query.execute(sql);
|
|
}
|
|
}
|
|
else if (db == "oCourse") {
|
|
if (!eCol.count("Legs")) {
|
|
string sql = "ALTER TABLE oCourse ADD COLUMN " + C_STRING("Legs", 1024);
|
|
sql = sql.substr(0, sql.length() - 2);
|
|
query.execute(sql);
|
|
}
|
|
}
|
|
else if (db == "oRunner" || db == "oTeam") {
|
|
if (!eCol.count("InputTime")) {
|
|
string sql = "ALTER TABLE " + db + " ";
|
|
sql += "ADD COLUMN " + C_INT("InputTime");
|
|
sql += "ADD COLUMN " + C_INT("InputStatus");
|
|
sql += "ADD COLUMN " + C_INT("InputPoints");
|
|
sql += "ADD COLUMN " + C_INT("InputPlace");
|
|
sql = sql.substr(0, sql.length() - 2);
|
|
query.execute(sql);
|
|
}
|
|
}
|
|
|
|
// Ugrade table
|
|
string sqlAdd = dc->generateSQLDefinition(eCol);
|
|
if (!sqlAdd.empty()) {
|
|
query.execute("ALTER TABLE " + db + " " + sqlAdd);
|
|
}
|
|
}
|
|
|
|
bool MeosSQL::openDB(oEvent *oe)
|
|
{
|
|
clearReadTimes();
|
|
errorMessage.clear();
|
|
if (!con.connected() && !listCompetitions(oe, false))
|
|
return false;
|
|
|
|
try{
|
|
con.select_db("MeOSMain");
|
|
}
|
|
catch (const mysqlpp::Exception& er) {
|
|
setDefaultDB();
|
|
alert(string(er.what()) + " MySQL Error. Select MeosMain");
|
|
return 0;
|
|
}
|
|
monitorId=0;
|
|
string dbname(oe->currentNameId.begin(), oe->currentNameId.end());//WCS
|
|
|
|
try {
|
|
Query query = con.query();
|
|
query << "SELECT * FROM oEvent WHERE NameId=" << quote << dbname;
|
|
Result res = query.store();
|
|
|
|
if (res && res.num_rows()>=1) {
|
|
Row row=res.at(0);
|
|
|
|
int version = row["Version"];
|
|
|
|
if (version < oEvent::dbVersion) {
|
|
query.reset();
|
|
query << "UPDATE oEvent SET Version=" << oEvent::dbVersion << " WHERE Id=" << row["Id"];
|
|
query.execute();
|
|
}
|
|
else if (version > oEvent::dbVersion) {
|
|
alert("A newer version av MeOS is required.");
|
|
return false;
|
|
}
|
|
/*
|
|
if (version != oe->dbVersion) {
|
|
// Wrong version. Drop and reset.
|
|
query.reset();
|
|
query << "DELETE FROM oEvent WHERE Id=" << row["Id"];
|
|
query.execute();
|
|
con.drop_db(dbname);
|
|
return openDB(oe);
|
|
}*/
|
|
|
|
oe->Id=row["Id"]; //Don't synchronize more here...
|
|
}
|
|
else {
|
|
query.reset();
|
|
query << "INSERT INTO oEvent SET Name='-', Date='', NameId=" << quote << dbname
|
|
<< ", Version=" << oe->dbVersion;
|
|
|
|
ResNSel res=query.execute();
|
|
|
|
if (res){
|
|
oe->Id=static_cast<int>(res.insert_id);
|
|
}
|
|
}
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
setDefaultDB();
|
|
alert(string(er.what()) + " MySQL Error. Select DB.");
|
|
return 0;
|
|
}
|
|
|
|
{
|
|
mysqlpp::NoExceptions ne(con);
|
|
|
|
if (!con.select_db(dbname)){
|
|
con.create_db(dbname);
|
|
con.select_db(dbname);
|
|
}
|
|
}
|
|
|
|
CmpDataBase=dbname;
|
|
|
|
Query query = con.query();
|
|
try {
|
|
//Real version of oEvent db
|
|
query << C_START("oEvent")
|
|
<< C_STRING("Name", 128)
|
|
<< C_STRING("Annotation", 128)
|
|
<< C_STRING("Date", 32)
|
|
<< C_UINT("ZeroTime")
|
|
<< C_STRING("NameId", 64)
|
|
<< C_UINT("BuildVersion")
|
|
<< oe->getDI().generateSQLDefinition()
|
|
<< C_MTEXT("Lists") << C_END();
|
|
query.execute();
|
|
|
|
// Upgrade oEvent
|
|
upgradeDB("oEvent", oe->oEventData);
|
|
|
|
query.reset();
|
|
query << C_START("oRunner")
|
|
<< C_STRING("Name") << C_INT("CardNo")
|
|
<< C_INT("Club") << C_INT("Class") << C_INT("Course") << C_INT("StartNo")
|
|
<< C_INT("StartTime") << C_INT("FinishTime")
|
|
<< C_INT("Status") << C_INT("Card") << C_STRING("MultiR", 200)
|
|
<< C_INT("InputTime") << C_INT("InputStatus") << C_INT("InputPoints") << C_INT("InputPlace")
|
|
<< oe->oRunnerData->generateSQLDefinition() << C_END();
|
|
|
|
query.execute();
|
|
|
|
// Ugrade oRunner
|
|
upgradeDB("oRunner", oe->oRunnerData);
|
|
|
|
query.reset();
|
|
query << C_START("oCard")
|
|
<< C_INT("CardNo")
|
|
<< C_UINT("ReadId")
|
|
<< C_STRING("Punches", 16*190) << C_END();
|
|
|
|
query.execute();
|
|
|
|
query.reset();
|
|
query << C_START("oClass")
|
|
<< C_STRING("Name", 128)
|
|
<< C_INT("Course")
|
|
<< C_MTEXT("MultiCourse")
|
|
<< C_STRING("LegMethod", 1024)
|
|
<< oe->oClassData->generateSQLDefinition() << C_END();
|
|
query.execute();
|
|
|
|
// Ugrade oClass
|
|
upgradeDB("oClass", oe->oClassData);
|
|
|
|
query.reset();
|
|
query << C_START("oClub")
|
|
<< C_STRING("Name", 128)
|
|
<< oe->oClubData->generateSQLDefinition() << C_END();
|
|
query.execute();
|
|
|
|
// Ugrade oClub
|
|
upgradeDB("oClub", oe->oClubData);
|
|
|
|
query.reset();
|
|
query << C_START("oControl")
|
|
<< C_STRING("Name", 128)
|
|
<< C_STRING("Numbers", 128)
|
|
<< C_UINT("Status")
|
|
<< oe->oControlData->generateSQLDefinition() << C_END();
|
|
query.execute();
|
|
|
|
// Ugrade oRunner
|
|
upgradeDB("oControl", oe->oControlData);
|
|
|
|
query.reset();
|
|
query << C_START("oCourse")
|
|
<< C_STRING("Name")
|
|
<< C_STRING("Controls", 512)
|
|
<< C_UINT("Length")
|
|
<< C_STRING("Legs", 1024)
|
|
<< oe->oCourseData->generateSQLDefinition() << C_END();
|
|
query.execute();
|
|
|
|
// Ugrade oCourse
|
|
upgradeDB("oCourse", oe->oCourseData);
|
|
|
|
query.reset();
|
|
query << C_START("oTeam")
|
|
<< C_STRING("Name") << C_STRING("Runners", 256)
|
|
<< C_INT("Club") << C_INT("Class")
|
|
<< C_INT("StartTime") << C_INT("FinishTime")
|
|
<< C_INT("Status") << C_INT("StartNo")
|
|
<< C_INT("InputTime") << C_INT("InputStatus") << C_INT("InputPoints") << C_INT("InputPlace")
|
|
<< oe->oTeamData->generateSQLDefinition() << C_END();
|
|
query.execute();
|
|
|
|
// Ugrade oTeam
|
|
upgradeDB("oTeam", oe->oTeamData);
|
|
|
|
query.reset();
|
|
query << C_START("oPunch")
|
|
<< C_INT("CardNo")
|
|
<< C_INT("Time")
|
|
<< C_INT("Type") << C_END();
|
|
query.execute();
|
|
|
|
query.reset();
|
|
query << C_START("oMonitor")
|
|
<< C_STRING("Client")
|
|
<< C_UINT("Count")
|
|
<< C_END();
|
|
query.execute();
|
|
|
|
query.reset();
|
|
query << "CREATE TABLE IF NOT EXISTS oCounter ("
|
|
<< "CounterId INT NOT NULL, "
|
|
<< C_UINT("oControl")
|
|
<< C_UINT("oCourse")
|
|
<< C_UINT("oClass")
|
|
<< C_UINT("oCard")
|
|
<< C_UINT("oClub")
|
|
<< C_UINT("oPunch")
|
|
<< C_UINT("oRunner")
|
|
<< C_UINT("oTeam")
|
|
<< C_UINT("oEvent")
|
|
<< " Modified TIMESTAMP) ENGINE = MyISAM";
|
|
query.execute();
|
|
|
|
mysqlpp::Result res = query.store("SELECT CounterId FROM oCounter");
|
|
if (res.num_rows()==0) {
|
|
query.reset();
|
|
query << "INSERT INTO oCounter SET CounterId=1, oPunch=1, oTeam=1, oRunner=1";
|
|
query.execute();
|
|
}
|
|
|
|
// Create runner/club DB
|
|
createRunnerDB(oe, query);
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what()) + " MySQL Error.");
|
|
return 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool MeosSQL::getErrorMessage(char *bf)
|
|
{
|
|
strcpy_s(bf, 256, errorMessage.c_str());
|
|
return !errorMessage.empty();
|
|
}
|
|
|
|
bool MeosSQL::closeDB()
|
|
{
|
|
CmpDataBase="";
|
|
errorMessage.clear();
|
|
|
|
try {
|
|
con.close();
|
|
}
|
|
catch (const mysqlpp::Exception&) {
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//CAN BE RUN IN A SEPARTE THREAD. Access nothing without thinking...
|
|
//No other MySQL-call will take place in parallell.
|
|
bool MeosSQL::reConnect()
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty()) {
|
|
errorMessage="No database selected.";
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
con.close();
|
|
}
|
|
catch (const mysqlpp::Exception&) {
|
|
}
|
|
|
|
try {
|
|
con.connect("", serverName.c_str(), serverUser.c_str(),
|
|
serverPassword.c_str(), serverPort);
|
|
}
|
|
catch (const Exception& er) {
|
|
errorMessage=er.what();
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
con.select_db(CmpDataBase);
|
|
}
|
|
catch (const Exception& er) {
|
|
errorMessage=er.what();
|
|
return false;
|
|
}
|
|
|
|
try{
|
|
mysqlpp::Query queryset = con.query();
|
|
queryset << "SET NAMES UTF8";
|
|
queryset.execute();
|
|
}
|
|
catch (const mysqlpp::Exception& ){
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::SyncUpdate(oEvent *oe)
|
|
{
|
|
OpFailStatus retValue = opStatusOK;
|
|
errorMessage.clear();
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
try{
|
|
con.select_db("MeOSMain");
|
|
|
|
mysqlpp::Query queryset = con.query();
|
|
queryset << "UPDATE oEvent SET Name=" << quote << limitLength(oe->Name, 128) << ", "
|
|
<< " Annotation=" << quote << limitLength(oe->Annotation, 128) << ", "
|
|
<< " Date=" << quote << toString(oe->Date) << ", "
|
|
<< " NameId=" << quote << toString(oe->currentNameId) << ", "
|
|
<< " ZeroTime=" << unsigned(oe->ZeroTime)
|
|
<< " WHERE Id=" << oe->Id;
|
|
|
|
queryset.execute();
|
|
//syncUpdate(queryset, "oEvent", oe, true);
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
setDefaultDB();
|
|
alert(string(er.what()) + " [UPDATING oEvent]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
try{
|
|
con.select_db(CmpDataBase);
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [OPENING] "+CmpDataBase);
|
|
return opStatusFail;
|
|
}
|
|
|
|
{
|
|
|
|
string listEnc;
|
|
try {
|
|
encodeLists(oe, listEnc);
|
|
}
|
|
catch (std::exception &ex) {
|
|
retValue = opStatusWarning;
|
|
alert(ex.what());
|
|
}
|
|
|
|
mysqlpp::Query queryset = con.query();
|
|
queryset << " Name=" << quote << limitLength(oe->Name, 128) << ", "
|
|
<< " Annotation=" << quote << limitLength(oe->Annotation, 128) << ", "
|
|
<< " Date=" << quote << toString(oe->Date) << ", "
|
|
<< " NameId=" << quote << toString(oe->currentNameId) << ", "
|
|
<< " ZeroTime=" << unsigned(oe->ZeroTime) << ", "
|
|
<< " BuildVersion=" << buildVersion << ", "
|
|
<< " Lists=" << quote << listEnc
|
|
<< oe->getDI().generateSQLSet(true);
|
|
|
|
if (syncUpdate(queryset, "oEvent", oe) == opStatusFail)
|
|
return opStatusFail;
|
|
}
|
|
|
|
con.query().exec("DELETE FROM oCard");
|
|
{
|
|
list<oCard>::iterator it=oe->Cards.begin();
|
|
while(it!=oe->Cards.end()){
|
|
if (!it->isRemoved() && syncUpdate(&*it, true) == opStatusFail)
|
|
return opStatusFail;
|
|
++it;
|
|
}
|
|
}
|
|
|
|
con.query().exec("DELETE FROM oClub");
|
|
{
|
|
list<oClub>::iterator it=oe->Clubs.begin();
|
|
while(it!=oe->Clubs.end()){
|
|
if (!it->isRemoved() && syncUpdate(&*it, true) == opStatusFail)
|
|
return opStatusFail;
|
|
++it;
|
|
}
|
|
}
|
|
con.query().exec("DELETE FROM oControl");
|
|
{
|
|
list<oControl>::iterator it=oe->Controls.begin();
|
|
while(it!=oe->Controls.end()){
|
|
if (!it->isRemoved() && syncUpdate(&*it, true) == opStatusFail)
|
|
return opStatusFail;
|
|
++it;
|
|
}
|
|
}
|
|
con.query().exec("DELETE FROM oCourse");
|
|
{
|
|
list<oCourse>::iterator it=oe->Courses.begin();
|
|
while(it!=oe->Courses.end()){
|
|
if (!it->isRemoved() && syncUpdate(&*it, true) == opStatusFail)
|
|
return opStatusFail;
|
|
++it;
|
|
}
|
|
}
|
|
con.query().exec("DELETE FROM oClass");
|
|
{
|
|
list<oClass>::iterator it=oe->Classes.begin();
|
|
while(it!=oe->Classes.end()){
|
|
if (!it->isRemoved() && syncUpdate(&*it, true) == opStatusFail)
|
|
return opStatusFail;
|
|
++it;
|
|
}
|
|
}
|
|
con.query().exec("DELETE FROM oRunner");
|
|
{
|
|
list<oRunner>::iterator it=oe->Runners.begin();
|
|
while(it!=oe->Runners.end()){
|
|
if (!it->isRemoved() && syncUpdate(&*it, true) == opStatusFail)
|
|
return opStatusFail;
|
|
++it;
|
|
}
|
|
}
|
|
|
|
con.query().exec("DELETE FROM oTeam");
|
|
{
|
|
list<oTeam>::iterator it=oe->Teams.begin();
|
|
while(it!=oe->Teams.end()){
|
|
if (!it->isRemoved() && syncUpdate(&*it, true) == opStatusFail)
|
|
return opStatusFail;
|
|
++it;
|
|
}
|
|
}
|
|
|
|
con.query().exec("DELETE FROM oPunch");
|
|
{
|
|
list<oFreePunch>::iterator it=oe->punches.begin();
|
|
while(it!=oe->punches.end()){
|
|
if (!it->isRemoved() && syncUpdate(&*it, true) == opStatusFail)
|
|
return opStatusFail;
|
|
++it;
|
|
}
|
|
}
|
|
return retValue;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::uploadRunnerDB(oEvent *oe)
|
|
{
|
|
errorMessage.clear();
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
int errorCount = 0;
|
|
int totErrorCount = 0;
|
|
ProgressWindow pw(oe->gdiBase().getHWNDTarget());
|
|
try {
|
|
const vector<oDBClubEntry> &cdb = oe->runnerDB->getClubDB(true);
|
|
size_t size = cdb.size();
|
|
|
|
const vector<RunnerDBEntry> &rdb = oe->runnerDB->getRunnerDBN();
|
|
const vector<RunnerWDBEntry> &rwdb = oe->runnerDB->getRunnerDB();
|
|
|
|
if (cdb.size() + rdb.size() > 2000)
|
|
pw.init();
|
|
|
|
size_t tz = cdb.size() + rdb.size();
|
|
int s1 = (1000 * cdb.size())/tz;
|
|
int s2 = (1000 * rdb.size())/tz;
|
|
|
|
// Reset databases
|
|
con.query().exec("DELETE FROM dbClub");
|
|
con.query().exec("DELETE FROM dbRunner");
|
|
|
|
for (size_t k = 0; k<size; k++) {
|
|
if (cdb[k].isRemoved())
|
|
continue;
|
|
|
|
mysqlpp::Query query = con.query();
|
|
string setId = "Id=" + itos(cdb[k].Id) + ", ";
|
|
query << "INSERT INTO dbClub SET " << setId << "Name=" << quote << toString(cdb[k].name)
|
|
<< cdb[k].getDCI().generateSQLSet(true);
|
|
|
|
try {
|
|
query.execute();
|
|
errorCount = 0;
|
|
}
|
|
catch (const mysqlpp::Exception& ex) {
|
|
errorMessage = ex.what();
|
|
totErrorCount++;
|
|
if (++errorCount > 5)
|
|
throw;
|
|
}
|
|
|
|
if (k%200 == 150)
|
|
pw.setProgress((k*s1)/size);
|
|
}
|
|
|
|
size = rdb.size();
|
|
for (size_t k = 0; k<size; k++) {
|
|
if (rdb[k].isRemoved())
|
|
continue;
|
|
if (!rdb[k].isUTF()) {
|
|
rwdb[k].recode(rdb[k]);
|
|
}
|
|
|
|
mysqlpp::Query query = con.query();
|
|
query << "INSERT INTO dbRunner SET " <<
|
|
"Name=" << quote << rdb[k].name <<
|
|
", ExtId=" << rdb[k].extId << ", Club=" << rdb[k].clubNo <<
|
|
", CardNo=" << rdb[k].cardNo << ", Sex=" << quote << rdb[k].getSex() <<
|
|
", Nation=" << quote << rdb[k].getNationality() << ", BirthYear=" << rdb[k].birthYear;
|
|
|
|
try {
|
|
query.execute();
|
|
errorCount = 0;
|
|
}
|
|
catch (const mysqlpp::Exception& ex) {
|
|
totErrorCount++;
|
|
errorMessage = ex.what();
|
|
if (++errorCount > 5)
|
|
throw;
|
|
}
|
|
|
|
if (k%200 == 150)
|
|
pw.setProgress(s1 + (k*s2)/size);
|
|
}
|
|
|
|
Result cnt = con.query().store("SELECT DATE_FORMAT(NOW(),'%Y-%m-%d %H:%i:%s')");
|
|
string dateTime = cnt.at(0).at(0);
|
|
oe->runnerDB->setDataDate(dateTime);
|
|
}
|
|
catch (const mysqlpp::Exception& er) {
|
|
errorMessage = er.what();
|
|
return opStatusFail;
|
|
}
|
|
if (errorCount > 0)
|
|
return opStatusWarning;
|
|
|
|
return opStatusOK;
|
|
}
|
|
|
|
bool MeosSQL::storeData(oDataInterface odi, const Row &row, unsigned long &revision) {
|
|
//errorMessage.clear();
|
|
list<oVariableInt> varint;
|
|
list<oVariableString> varstring;
|
|
bool success=true;
|
|
bool updated = false;
|
|
try{
|
|
odi.getVariableInt(varint);
|
|
list<oVariableInt>::iterator it_int;
|
|
for(it_int=varint.begin(); it_int!=varint.end(); it_int++) {
|
|
if (it_int->data32) {
|
|
int val = int(row[it_int->name]);
|
|
|
|
if (val != *(it_int->data32)) {
|
|
*(it_int->data32) = val;
|
|
updated = true;
|
|
}
|
|
}
|
|
else {
|
|
__int64 val = row[it_int->name].operator mysqlpp::ulonglong();
|
|
__int64 oldVal = *(it_int->data64);
|
|
if (val != oldVal) {
|
|
memcpy(it_int->data64, &val, 8);
|
|
updated = true;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
catch (const BadFieldName&) {
|
|
success=false;
|
|
}
|
|
|
|
try {
|
|
odi.getVariableString(varstring);
|
|
list<oVariableString>::iterator it_string;
|
|
for(it_string=varstring.begin(); it_string!=varstring.end(); it_string++) {
|
|
wstring w(fromUTF(row[it_string->name].c_str()));
|
|
if (it_string->store(w.c_str()))
|
|
updated = true;
|
|
}
|
|
}
|
|
catch(const BadFieldName&){
|
|
success=false;
|
|
}
|
|
|
|
// Mark all data as stored in memory
|
|
odi.allDataStored();
|
|
|
|
if (updated)
|
|
revision++;
|
|
|
|
return success;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::SyncRead(oEvent *oe) {
|
|
OpFailStatus retValue = opStatusOK;
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!oe || !con.connected())
|
|
return opStatusFail;
|
|
|
|
if (oe->HasDBConnection) {
|
|
//We already have established connectation, and just want to sync data.
|
|
return SyncEvent(oe);
|
|
}
|
|
warnedOldVersion=false;
|
|
|
|
if (!oe->Id) return SyncUpdate(oe);
|
|
|
|
ProgressWindow pw(oe->gdiBase().getHWNDTarget());
|
|
|
|
try {
|
|
con.select_db("MeOSMain");
|
|
|
|
Query query = con.query();
|
|
query << "SELECT * FROM oEvent WHERE Id=" << oe->Id;
|
|
Result res = query.store();
|
|
|
|
Row row;
|
|
if (row=res.at(0)){
|
|
oe->Name = fromUTF(string(row["Name"]));
|
|
oe->Annotation = fromUTF(string(row["Annotation"]));
|
|
oe->Date = fromUTF(string(row["Date"]));
|
|
oe->ZeroTime = row["ZeroTime"];
|
|
oe->currentNameId = fromUTF(string(row["NameId"]));
|
|
}
|
|
|
|
con.select_db(CmpDataBase);
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
setDefaultDB();
|
|
alert(string(er.what())+" [SYNCREAD oEvent]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
Query query = con.query();
|
|
|
|
int nRunner = 0;
|
|
int nCard = 0;
|
|
int nTeam = 0;
|
|
int nClubDB = 0;
|
|
int nRunnerDB = 0;
|
|
|
|
int nSum = 1;
|
|
|
|
try {
|
|
mysqlpp::Result cnt = query.store("SELECT COUNT(*) FROM dbClub");
|
|
nClubDB = cnt.at(0).at(0);
|
|
|
|
cnt = query.store("SELECT COUNT(*) FROM dbRunner");
|
|
nRunnerDB = cnt.at(0).at(0);
|
|
|
|
string time = oe->runnerDB->getDataDate();
|
|
|
|
cnt = query.store("SELECT COUNT(*) FROM dbClub WHERE Modified>'" + time + "'");
|
|
int modclub = cnt.at(0).at(0);
|
|
cnt = query.store("SELECT COUNT(*) FROM dbRunner WHERE Modified>'" + time + "'");
|
|
int modrunner = cnt.at(0).at(0);
|
|
|
|
bool skipDB = modclub==0 && modrunner==0 && nClubDB == oe->runnerDB->getClubDB(false).size() &&
|
|
nRunnerDB == oe->runnerDB->getRunnerDB().size();
|
|
|
|
if (skipDB) {
|
|
nClubDB = 0;
|
|
nRunnerDB = 0;
|
|
}
|
|
|
|
cnt = query.store("SELECT COUNT(*) FROM oRunner");
|
|
nRunner = cnt.at(0).at(0);
|
|
|
|
cnt = query.store("SELECT COUNT(*) FROM oCard");
|
|
nCard = cnt.at(0).at(0);
|
|
|
|
cnt = query.store("SELECT COUNT(*) FROM oTeam");
|
|
nTeam = cnt.at(0).at(0);
|
|
|
|
nSum = nClubDB + nRunnerDB + nRunner + nTeam + nCard + 50;
|
|
|
|
if (nSum > 400)
|
|
pw.init();
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD INFO]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
int pStart = 0, pPart = 50;
|
|
|
|
try {
|
|
//Update oEvent
|
|
query << "SELECT * FROM oEvent";
|
|
Result res = query.store();
|
|
|
|
Row row;
|
|
if (row=res.at(0)) {
|
|
oe->Name = fromUTF(string(row["Name"]));
|
|
oe->Annotation = fromUTF(string(row["Annotation"]));
|
|
oe->Date = fromUTF(string(row["Date"]));
|
|
oe->ZeroTime = row["ZeroTime"];
|
|
oe->currentNameId = fromUTF(string(row["NameId"]));
|
|
oe->sqlUpdated = row["Modified"];
|
|
oe->counter = row["Counter"];
|
|
|
|
if (checkOldVersion(oe, row)) {
|
|
warnOldDB();
|
|
retValue = opStatusWarning;
|
|
}
|
|
|
|
const string &lRaw = row.raw_string(res.field_num("Lists"));
|
|
try {
|
|
importLists(oe, lRaw.c_str());
|
|
}
|
|
catch (std::exception &ex) {
|
|
alert(ex.what());
|
|
retValue = opStatusWarning;
|
|
}
|
|
|
|
oDataInterface odi=oe->getDI();
|
|
storeData(odi, row, oe->dataRevision);
|
|
oe->changed = false;
|
|
oe->setCurrency(-1, L"", L"", false); // Set currency tmp data
|
|
oe->getMeOSFeatures().deserialize(oe->getDCI().getString("Features"), *oe);
|
|
}
|
|
}
|
|
catch (const EndOfResults& ) {
|
|
errorMessage = "Unexpected error, oEvent table was empty";
|
|
return opStatusFail;
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oEvent/Club]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
pw.setProgress(20);
|
|
|
|
try {
|
|
ResUse res = query.use("SELECT * FROM oClub WHERE Removed=0");
|
|
|
|
// Retreive result rows one by one.
|
|
if (res){
|
|
// Get each row in result set.
|
|
Row row;
|
|
|
|
while (row = res.fetch_row()) {
|
|
oClub c(oe, row["Id"]);
|
|
storeClub(row, c);
|
|
oe->addClub(c);
|
|
|
|
oe->sqlUpdateClubs = max(c.sqlUpdated, oe->sqlUpdateClubs);
|
|
oe->sqlCounterClubs = max(c.counter, oe->sqlCounterClubs);
|
|
}
|
|
}
|
|
}
|
|
catch (const EndOfResults&) {
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oEvent/Club]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
pw.setProgress(30);
|
|
|
|
oe->sqlCounterControls=0;
|
|
try {
|
|
ResUse res = query.use("SELECT * FROM oControl WHERE Removed=0");
|
|
|
|
if (res) {
|
|
// Get each row in result set.
|
|
Row row;
|
|
|
|
while (row = res.fetch_row()) {
|
|
oControl c(oe, row["Id"]);
|
|
storeControl(row, c);
|
|
oe->addControl(c);
|
|
|
|
oe->sqlUpdateControls = max(c.sqlUpdated, oe->sqlUpdateControls);
|
|
oe->sqlCounterControls = max(c.counter, oe->sqlCounterControls);
|
|
}
|
|
}
|
|
}
|
|
catch (const EndOfResults& ) {
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oEvent/Control]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
oe->sqlCounterCourses = 0;
|
|
pw.setProgress(40);
|
|
|
|
try{
|
|
ResUse res = query.use("SELECT * FROM oCourse WHERE Removed=0");
|
|
|
|
if (res){
|
|
// Get each row in result set.
|
|
Row row;
|
|
set<int> tmp;
|
|
while (row = res.fetch_row()) {
|
|
oCourse c(oe, row["Id"]);
|
|
storeCourse(row, c, tmp, false);
|
|
oe->addCourse(c);
|
|
|
|
oe->sqlUpdateCourses = max(c.sqlUpdated, oe->sqlUpdateCourses);
|
|
oe->sqlCounterCourses = max(c.counter, oe->sqlCounterCourses);
|
|
}
|
|
}
|
|
}
|
|
catch (const EndOfResults& ) {
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oEvent/Course]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
pw.setProgress(50);
|
|
|
|
oe->sqlCounterClasses = 0;
|
|
try{
|
|
ResUse res = query.use("SELECT * FROM oClass WHERE Removed=0");
|
|
|
|
if (res) {
|
|
Row row;
|
|
while (row = res.fetch_row()) {
|
|
oClass c(oe, row["Id"]);
|
|
storeClass(row, c, false, false);
|
|
|
|
c.changed = false;
|
|
c.reChanged = false;
|
|
oe->addClass(c);
|
|
|
|
oe->sqlUpdateClasses = max(c.sqlUpdated, oe->sqlUpdateClasses);
|
|
oe->sqlCounterClasses = max(c.counter, oe->sqlCounterClasses);
|
|
}
|
|
}
|
|
}
|
|
catch (const EndOfResults& ) {
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oEvent/Class]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
oe->sqlCounterCards=0;
|
|
|
|
try{
|
|
ResUse res = query.use("SELECT * FROM oCard WHERE Removed=0");
|
|
int counter = 0;
|
|
pStart += pPart;
|
|
pPart = (1000 * nCard) / nSum;
|
|
|
|
if (res){
|
|
Row row;
|
|
while (row = res.fetch_row()) {
|
|
oCard c(oe, row["Id"]);
|
|
storeCard(row, c);
|
|
oe->addCard(c);
|
|
assert(!c.changed);
|
|
|
|
oe->sqlCounterCards = max(c.counter, oe->sqlCounterCards);
|
|
oe->sqlUpdateCards = max(c.sqlUpdated, oe->sqlUpdateCards);
|
|
|
|
if (++counter % 100 == 50)
|
|
pw.setProgress(pStart + (counter * pPart) / nCard);
|
|
}
|
|
}
|
|
|
|
}
|
|
catch (const EndOfResults& ) {
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oEvent/Card]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
oe->sqlCounterRunners = 0;
|
|
try{
|
|
ResUse res = query.use("SELECT * FROM oRunner WHERE Removed=0");
|
|
int counter = 0;
|
|
pStart += pPart;
|
|
pPart = (1000 * nRunner) / nSum;
|
|
|
|
if (res){
|
|
Row row;
|
|
while (row = res.fetch_row()) {
|
|
oRunner r(oe, row["Id"]);
|
|
storeRunner(row, r, false, false, false, false);
|
|
assert(!r.changed);
|
|
oe->addRunner(r, false);
|
|
|
|
oe->sqlUpdateRunners = max(r.sqlUpdated, oe->sqlUpdateRunners);
|
|
oe->sqlCounterRunners = max(r.counter,oe->sqlCounterRunners);
|
|
|
|
if (++counter % 100 == 50)
|
|
pw.setProgress(pStart + (counter * pPart) / nRunner);
|
|
}
|
|
}
|
|
}
|
|
catch (const EndOfResults& ) {
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oEvent/Runner]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
oe->sqlCounterTeams=0;
|
|
|
|
try{
|
|
ResUse res = query.use("SELECT * FROM oTeam WHERE Removed=0");
|
|
int counter = 0;
|
|
pStart += pPart;
|
|
pPart = (1000 * nTeam) / nSum;
|
|
|
|
if (res){
|
|
Row row;
|
|
while (row = res.fetch_row()) {
|
|
oTeam t(oe, row["Id"]);
|
|
|
|
storeTeam(row, t, false, false);
|
|
|
|
pTeam at = oe->addTeam(t, false);
|
|
|
|
if (at) {
|
|
at->apply(false, 0, true);
|
|
at->changed = false;
|
|
for (size_t k = 0; k<at->Runners.size(); k++) {
|
|
if (at->Runners[k]) {
|
|
assert(!at->Runners[k]->changed);
|
|
at->Runners[k]->changed = false;
|
|
}
|
|
}
|
|
}
|
|
oe->sqlUpdateTeams = max(t.sqlUpdated, oe->sqlUpdateTeams);
|
|
oe->sqlCounterTeams = max(t.counter, oe->sqlCounterTeams);
|
|
|
|
if (++counter % 100 == 50)
|
|
pw.setProgress(pStart + (counter * pPart) / nTeam);
|
|
}
|
|
}
|
|
}
|
|
catch (const EndOfResults& ) {
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oEvent/Team]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
string dateTime;
|
|
try {
|
|
if (nClubDB == 0 && nRunnerDB == 0)
|
|
return retValue; // Not modified
|
|
|
|
Result cnt;
|
|
// Note dbRunner is stored after dbClub
|
|
if (nRunnerDB>0)
|
|
cnt = query.store("SELECT DATE_FORMAT(MAX(Modified),'%Y-%m-%d %H:%i:%s') FROM dbRunner");
|
|
else
|
|
cnt = query.store("SELECT DATE_FORMAT(NOW(),'%Y-%m-%d %H:%i:%s')");
|
|
|
|
dateTime = cnt.at(0).at(0);
|
|
|
|
oe->runnerDB->prepareLoadFromServer(nRunnerDB, nClubDB);
|
|
|
|
ResUse res = query.use("SELECT * FROM dbClub");
|
|
int counter = 0;
|
|
pStart += pPart;
|
|
pPart = (1000 * nClubDB) / nSum;
|
|
|
|
if (res) {
|
|
Row row;
|
|
while (row = res.fetch_row()) {
|
|
oClub t(oe, row["Id"]);
|
|
|
|
string n = row["Name"];
|
|
t.internalSetName(fromUTF(n));
|
|
storeData(t.getDI(), row, oe->dataRevision);
|
|
|
|
oe->runnerDB->addClub(t, false);
|
|
|
|
if (++counter % 100 == 50)
|
|
pw.setProgress(pStart + (counter * pPart) / nClubDB);
|
|
}
|
|
}
|
|
}
|
|
catch (const EndOfResults& ) {
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD dbClub]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
try {
|
|
ResUse res = query.use("SELECT * FROM dbRunner");
|
|
int counter = 0;
|
|
pStart += pPart;
|
|
pPart = (1000 * nRunnerDB) / nSum;
|
|
|
|
if (res) {
|
|
Row row;
|
|
while (row = res.fetch_row()) {
|
|
string name = (string)row["Name"];
|
|
string ext = row["ExtId"];
|
|
string club = row["Club"];
|
|
string card = row["CardNo"];
|
|
string sex = row["Sex"];
|
|
string nat = row["Nation"];
|
|
string birth = row["BirthYear"];
|
|
RunnerWDBEntry *db = oe->runnerDB->addRunner(name.c_str(), _atoi64(ext.c_str()),
|
|
atoi(club.c_str()), atoi(card.c_str()));
|
|
if (db) {
|
|
RunnerDBEntry &dbn = db->dbe();
|
|
if (sex.length()==1)
|
|
dbn.sex = sex[0];
|
|
dbn.birthYear = short(atoi(birth.c_str()));
|
|
|
|
if (nat.length()==3) {
|
|
dbn.national[0] = nat[0];
|
|
dbn.national[1] = nat[1];
|
|
dbn.national[2] = nat[2];
|
|
}
|
|
}
|
|
|
|
if (++counter % 100 == 50)
|
|
pw.setProgress(pStart + (counter * pPart) / nRunnerDB);
|
|
|
|
}
|
|
}
|
|
}
|
|
catch (const EndOfResults& ) {
|
|
}
|
|
catch (const mysqlpp::Exception& er) {
|
|
alert(string(er.what())+" [SYNCREAD dbRunner]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
oe->runnerDB->setDataDate(dateTime);
|
|
processMissingObjects();
|
|
|
|
return retValue;
|
|
}
|
|
|
|
void MeosSQL::storeClub(const Row &row, oClub &c)
|
|
{
|
|
string n = row["Name"];
|
|
c.internalSetName(fromUTF(n));
|
|
|
|
c.sqlUpdated = row["Modified"];
|
|
c.counter = row["Counter"];
|
|
c.Removed = row["Removed"];
|
|
|
|
c.oe->sqlChangedClubs = true;
|
|
c.changedObject();
|
|
|
|
synchronized(c);
|
|
storeData(c.getDI(), row, c.oe->dataRevision);
|
|
}
|
|
|
|
void MeosSQL::storeControl(const Row &row, oControl &c)
|
|
{
|
|
c.Name = fromUTF((string)row["Name"]);
|
|
c.setNumbers(fromUTF((string)row["Numbers"]));
|
|
oControl::ControlStatus oldStat = c.Status;
|
|
c.Status = oControl::ControlStatus(int(row["Status"]));
|
|
|
|
c.sqlUpdated = row["Modified"];
|
|
c.counter = row["Counter"];
|
|
c.Removed = row["Removed"];
|
|
|
|
c.oe->sqlChangedControls = true;
|
|
if (c.changed || oldStat != c.Status) {
|
|
c.oe->dataRevision++;
|
|
c.changed = false;
|
|
}
|
|
|
|
c.changedObject();
|
|
|
|
synchronized(c);
|
|
storeData(c.getDI(), row, c.oe->dataRevision);
|
|
}
|
|
|
|
void MeosSQL::storeCard(const Row &row, oCard &c)
|
|
{
|
|
c.cardNo = row["CardNo"];
|
|
c.readId = row["ReadId"];
|
|
c.importPunches(string(row["Punches"]));
|
|
|
|
c.sqlUpdated = row["Modified"];
|
|
c.counter = row["Counter"];
|
|
c.Removed = row["Removed"];
|
|
|
|
pRunner r = c.getOwner();
|
|
if (r) {
|
|
r->sqlChanged = true;
|
|
}
|
|
c.changedObject();
|
|
synchronized(c);
|
|
c.oe->sqlChangedCards = true;
|
|
}
|
|
|
|
void MeosSQL::storePunch(const Row &row, oFreePunch &p, bool rehash)
|
|
{
|
|
if (rehash) {
|
|
p.setCardNo(row["CardNo"], true);
|
|
p.setTimeInt(row["Time"], true);
|
|
p.setType(fromUTF(string(row["Type"])), true);
|
|
}
|
|
else {
|
|
p.CardNo = row["CardNo"];
|
|
p.Time = row["Time"];
|
|
p.Type = row["Type"];
|
|
}
|
|
|
|
p.sqlUpdated = row["Modified"];
|
|
p.counter = row["Counter"];
|
|
p.Removed = row["Removed"];
|
|
|
|
p.changedObject();
|
|
synchronized(p);
|
|
p.oe->sqlChangedPunches = true;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::storeClass(const Row &row, oClass &c,
|
|
bool readCourses, bool allowSubRead)
|
|
{
|
|
OpFailStatus success = opStatusOK;
|
|
|
|
c.Name=fromUTF(string(row["Name"]));
|
|
string multi = row["MultiCourse"];
|
|
|
|
string lm(row["LegMethod"]);
|
|
c.importLegMethod(lm);
|
|
|
|
set<int> cid;
|
|
vector<vector<int>> multip;
|
|
oClass::parseCourses(multi, multip, cid);
|
|
|
|
int classCourse = row["Course"];
|
|
if (classCourse != 0)
|
|
cid.insert(classCourse);
|
|
|
|
if (!readCourses) {
|
|
for (set<int>::iterator clsIt = cid.begin(); clsIt != cid.end(); ++clsIt) {
|
|
if (!c.oe->getCourse(*clsIt))
|
|
readCourses = true; // There are missing courses. Force read.
|
|
}
|
|
}
|
|
|
|
if (readCourses) {
|
|
if (allowSubRead) {
|
|
success = min(success, syncReadClassCourses(&c, cid, readCourses));
|
|
}
|
|
else {
|
|
// Cannot read from database here. Add implicitly added courses
|
|
for (int x : cid) {
|
|
if (c.oe->getCourse(x) == nullptr) {
|
|
oCourse oc(c.oe, x);
|
|
oc.setImplicitlyCreated();
|
|
addedFromDatabase(c.oe->addCourse(oc));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (classCourse != 0)
|
|
c.Course = c.oe->getCourse(classCourse);
|
|
else
|
|
c.Course = nullptr;
|
|
|
|
c.importCourses(multip);
|
|
|
|
c.sqlUpdated = row["Modified"];
|
|
c.counter = row["Counter"];
|
|
c.Removed = row["Removed"];
|
|
|
|
storeData(c.getDI(), row, c.oe->dataRevision);
|
|
|
|
c.changed = false;
|
|
|
|
c.changedObject();
|
|
c.oe->sqlChangedClasses = true;
|
|
synchronized(c);
|
|
return success;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::storeCourse(const Row &row, oCourse &c,
|
|
set<int> &readControls,
|
|
bool allowSubRead) {
|
|
OpFailStatus success = opStatusOK;
|
|
|
|
c.Name = fromUTF((string)row["Name"]);
|
|
c.importControls(string(row["Controls"]), false);
|
|
c.Length = row["Length"];
|
|
c.importLegLengths(string(row["Legs"]), false);
|
|
|
|
for (int i=0;i<c.nControls; i++) {
|
|
if (c.Controls[i]) {
|
|
// Might have been created during last call.
|
|
// Then just read to update
|
|
if (!c.Controls[i]->existInDB()) {
|
|
c.Controls[i]->setImplicitlyCreated();
|
|
if (allowSubRead) {
|
|
c.Controls[i]->changed = false;
|
|
success = min(success, syncRead(true, c.Controls[i]));
|
|
}
|
|
addedFromDatabase(c.Controls[i]);
|
|
}
|
|
else {
|
|
readControls.insert(c.Controls[i]->getId());
|
|
}
|
|
}
|
|
}
|
|
|
|
c.sqlUpdated = row["Modified"];
|
|
c.counter = row["Counter"];
|
|
c.Removed = row["Removed"];
|
|
|
|
storeData(c.getDI(), row, c.oe->dataRevision);
|
|
c.oe->dataRevision++;
|
|
c.changed = false;
|
|
c.changedObject();
|
|
c.oe->sqlChangedCourses = true;
|
|
synchronized(c);
|
|
return success;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::storeRunner(const Row &row, oRunner &r,
|
|
bool readCourseCard,
|
|
bool readClassClub,
|
|
bool readRunners,
|
|
bool allowSubRead)
|
|
{
|
|
OpFailStatus success = opStatusOK;
|
|
oEvent *oe=r.oe;
|
|
|
|
// Mark old class as changed
|
|
if (r.Class)
|
|
r.markClassChanged(-1);
|
|
|
|
int oldSno = r.StartNo;
|
|
const wstring &oldBib = r.getBib();
|
|
|
|
r.sName = fromUTF((string)row["Name"]);
|
|
oRunner::getRealName(r.sName, r.tRealName);
|
|
r.setCardNo(row["CardNo"], false, true);
|
|
r.StartNo = row["StartNo"];
|
|
r.tStartTime = r.startTime = row["StartTime"];
|
|
r.FinishTime = row["FinishTime"];
|
|
r.tStatus = r.status = RunnerStatus(int(row["Status"]));
|
|
|
|
r.inputTime = row["InputTime"];
|
|
r.inputPoints = row["InputPoints"];
|
|
r.inputStatus = RunnerStatus(int(row["InputStatus"]));
|
|
r.inputPlace = row["InputPlace"];
|
|
|
|
r.Removed = row["Removed"];
|
|
r.sqlUpdated = row["Modified"];
|
|
r.counter = row["Counter"];
|
|
|
|
storeData(r.getDI(), row, oe->dataRevision);
|
|
|
|
if (oldSno != r.StartNo || oldBib != r.getBib())
|
|
oe->bibStartNoToRunnerTeam.clear(); // Clear quick map (lazy setup)
|
|
|
|
if (int(row["Course"])!=0) {
|
|
r.Course = oe->getCourse(int(row["Course"]));
|
|
set<int> controlIds;
|
|
if (!r.Course) {
|
|
oCourse oc(oe, row["Course"]);
|
|
oc.setImplicitlyCreated();
|
|
if (allowSubRead)
|
|
success = min(success, syncReadCourse(true, &oc, controlIds));
|
|
if (!oc.isRemoved()) {
|
|
r.Course = oe->addCourse(oc);
|
|
addedFromDatabase(r.Course);
|
|
}
|
|
}
|
|
else if (readCourseCard && allowSubRead)
|
|
success = min(success, syncReadCourse(false, r.Course, controlIds));
|
|
|
|
if (readCourseCard)
|
|
success = min(success, syncReadControls(oe, controlIds));
|
|
}
|
|
else r.Course=0;
|
|
|
|
if (int(row["Class"])!=0) {
|
|
r.Class=oe->getClass(int(row["Class"]));
|
|
|
|
if (!r.Class) {
|
|
oClass oc(oe, row["Class"]);
|
|
oc.setImplicitlyCreated();
|
|
if (allowSubRead)
|
|
success = min(success, syncRead(true, &oc, readClassClub));
|
|
if (!oc.isRemoved()) {
|
|
r.Class = oe->addClass(oc);
|
|
addedFromDatabase(r.Class);
|
|
}
|
|
}
|
|
else if (readClassClub && allowSubRead)
|
|
success = min(success, syncRead(false, r.Class, true));
|
|
|
|
if (r.tInTeam && r.tInTeam->Class!=r.Class)
|
|
r.tInTeam = 0; //Temporaraly disable belonging. Restored on next apply.
|
|
}
|
|
else r.Class=0;
|
|
|
|
if (int(row["Club"])!=0){
|
|
r.Club = oe->getClub(int(row["Club"]));
|
|
|
|
if (!r.Club) {
|
|
oClub oc(oe, row["Club"]);
|
|
oc.setImplicitlyCreated();
|
|
if (allowSubRead)
|
|
success = min(success, syncRead(true, &oc));
|
|
if (!oc.isRemoved()) {
|
|
r.Club = oe->addClub(oc);
|
|
addedFromDatabase(r.Club);
|
|
}
|
|
}
|
|
else if (readClassClub && allowSubRead)
|
|
success = min(success, syncRead(false, r.Club));
|
|
}
|
|
else r.Club=0;
|
|
|
|
pCard oldCard = r.Card;
|
|
|
|
if (int(row["Card"])!=0) {
|
|
r.Card = oe->getCard(int(row["Card"]));
|
|
|
|
if (!r.Card){
|
|
oCard oc(oe, row["Card"]);
|
|
oc.setImplicitlyCreated();
|
|
if (allowSubRead)
|
|
success = min(success, syncRead(true, &oc));
|
|
if (!oc.isRemoved()) {
|
|
r.Card = oe->addCard(oc);
|
|
r.Card->changed = false;
|
|
}
|
|
else {
|
|
addedFromDatabase(r.Card);
|
|
}
|
|
}
|
|
else if (readCourseCard && allowSubRead)
|
|
success = min(success, syncRead(false, r.Card));
|
|
}
|
|
else r.Card=0;
|
|
|
|
// Update card ownership
|
|
if (oldCard && oldCard != r.Card && oldCard->tOwner == &r)
|
|
oldCard->tOwner = 0;
|
|
|
|
// This is updated by addRunner if this is a temporary copy.
|
|
if (r.Card)
|
|
r.Card->tOwner=&r;
|
|
|
|
// This only loads indexes
|
|
r.decodeMultiR(string(row["MultiR"]));
|
|
|
|
// We now load/reload required other runners.
|
|
if (readRunners) {
|
|
for (size_t i=0;i<r.multiRunnerId.size();i++) {
|
|
int rid = r.multiRunnerId[i];
|
|
if (rid>0) {
|
|
pRunner pr = oe->getRunner(rid, 0);
|
|
if (pr==0) {
|
|
oRunner or(oe, rid);
|
|
or.setImplicitlyCreated();
|
|
if (allowSubRead)
|
|
success = min(success, syncRead(true, &or, false, readCourseCard));
|
|
if (!or.isRemoved()) {
|
|
pr = oe->addRunner(or , false);
|
|
addedFromDatabase(pr);
|
|
}
|
|
else {
|
|
r.multiRunnerId[i] = 0;
|
|
}
|
|
}
|
|
else if (allowSubRead)
|
|
success = min(success, syncRead(false, pr, false, readCourseCard));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mark new class as changed
|
|
r.changedObject();
|
|
r.sqlChanged = true;
|
|
r.oe->sqlChangedRunners = true;
|
|
|
|
synchronized(r);
|
|
return success;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::storeTeam(const Row &row, oTeam &t,
|
|
bool readRecursive, bool allowSubRead)
|
|
{
|
|
oEvent *oe=t.oe;
|
|
OpFailStatus success = opStatusOK;
|
|
|
|
// Mark old class as changed
|
|
if (t.Class)
|
|
t.Class->markSQLChanged(-1,-1);
|
|
|
|
int oldSno = t.StartNo;
|
|
const wstring &oldBib = t.getBib();
|
|
|
|
t.sName=fromUTF((string)row["Name"]);
|
|
t.StartNo=row["StartNo"];
|
|
t.tStartTime = t.startTime = row["StartTime"];
|
|
t.FinishTime = row["FinishTime"];
|
|
t.tStatus = t.status = RunnerStatus(int(row["Status"]));
|
|
|
|
t.inputTime = row["InputTime"];
|
|
t.inputPoints = row["InputPoints"];
|
|
t.inputStatus = RunnerStatus(int(row["InputStatus"]));
|
|
t.inputPlace = row["InputPlace"];
|
|
|
|
storeData(t.getDI(), row, oe->dataRevision);
|
|
|
|
if (oldSno != t.StartNo || oldBib != t.getBib())
|
|
oe->bibStartNoToRunnerTeam.clear(); // Clear quick map (lazy setup)
|
|
|
|
t.Removed = row["Removed"];
|
|
if (t.Removed)
|
|
t.prepareRemove();
|
|
|
|
t.sqlUpdated = row["Modified"];
|
|
t.counter = row["Counter"];
|
|
|
|
if (!t.Removed) {
|
|
int classId = row["Class"];
|
|
if (classId!=0) {
|
|
t.Class = oe->getClass(classId);
|
|
|
|
if (!t.Class) {
|
|
oClass oc(oe, classId);
|
|
oc.setImplicitlyCreated();
|
|
if (allowSubRead)
|
|
success = min(success, syncRead(true, &oc, readRecursive));
|
|
if (!oc.isRemoved()) {
|
|
t.Class = oe->addClass(oc);
|
|
addedFromDatabase(t.Class);
|
|
}
|
|
}
|
|
else if (readRecursive && allowSubRead)
|
|
success = min(success, syncRead(false, t.Class, readRecursive));
|
|
}
|
|
else t.Class=0;
|
|
|
|
int clubId = row["Club"];
|
|
if (clubId!=0) {
|
|
t.Club=oe->getClub(clubId);
|
|
|
|
if (!t.Club) {
|
|
oClub oc(oe, clubId);
|
|
oc.setImplicitlyCreated();
|
|
if (allowSubRead)
|
|
success = min(success, syncRead(true, &oc));
|
|
if (!oc.isRemoved()) {
|
|
t.Club = oe->addClub(oc);
|
|
addedFromDatabase(t.Club);
|
|
}
|
|
}
|
|
else if (readRecursive && allowSubRead)
|
|
success = min(success, syncRead(false, t.Club));
|
|
}
|
|
else t.Club = 0;
|
|
|
|
vector<int> rns;
|
|
vector<pRunner> pRns;
|
|
t.decodeRunners(static_cast<string>(row["Runners"]), rns);
|
|
|
|
pRns.resize(rns.size());
|
|
for (size_t k=0;k<rns.size(); k++) {
|
|
if (rns[k]>0) {
|
|
pRns[k] = oe->getRunner(rns[k], 0);
|
|
if (!pRns[k]) {
|
|
oRunner or(oe, rns[k]);
|
|
or.setImplicitlyCreated();
|
|
if (allowSubRead)
|
|
success = min(success, syncRead(true, &or, readRecursive, readRecursive));
|
|
|
|
if (or.sName.empty()) {
|
|
or.sName = L"@AutoCorrection";
|
|
oRunner::getRealName(or.sName, or.tRealName);
|
|
}
|
|
|
|
if (!or.isRemoved()) {
|
|
pRns[k] = oe->addRunner(or , false);
|
|
addedFromDatabase(pRns[k]);
|
|
assert(pRns[k] && !pRns[k]->changed);
|
|
}
|
|
}
|
|
else if (readRecursive && allowSubRead)
|
|
success = min(success, syncRead(false, pRns[k]));
|
|
}
|
|
}
|
|
t.importRunners(pRns);
|
|
}
|
|
|
|
// Mark new class as changed.
|
|
if (t.Class)
|
|
t.Class->markSQLChanged(-1,-1);
|
|
|
|
t.sqlChanged = true;
|
|
t.oe->sqlChangedTeams = true;
|
|
synchronized(t);
|
|
return success;
|
|
}
|
|
|
|
bool MeosSQL::Remove(oBase *ob)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return false;
|
|
|
|
if (!ob || !con.connected())
|
|
return false;
|
|
|
|
if (!ob->Id)
|
|
return true; //Not in DB.
|
|
|
|
Query query = con.query();
|
|
|
|
string oTable;
|
|
|
|
if (typeid(*ob)==typeid(oRunner)){
|
|
oTable="oRunner";
|
|
}
|
|
else if (typeid(*ob)==typeid(oClass)){
|
|
oTable="oClass";
|
|
}
|
|
else if (typeid(*ob)==typeid(oCourse)){
|
|
oTable="oCourse";
|
|
}
|
|
else if (typeid(*ob)==typeid(oControl)){
|
|
oTable="oControl";
|
|
}
|
|
else if (typeid(*ob)==typeid(oClub)){
|
|
oTable="oClub";
|
|
}
|
|
else if (typeid(*ob)==typeid(oCard)){
|
|
oTable="oCard";
|
|
}
|
|
else if (typeid(*ob)==typeid(oFreePunch)){
|
|
oTable="oPunch";
|
|
}
|
|
else if (typeid(*ob)==typeid(oTeam)){
|
|
oTable="oTeam";
|
|
}
|
|
else if (typeid(*ob)==typeid(oEvent)){
|
|
oTable="oEvent";
|
|
//Must change db!
|
|
return 0;
|
|
}
|
|
|
|
query << "Removed=1";
|
|
try{
|
|
ResNSel res = updateCounter(oTable.c_str(), ob->Id, &query);
|
|
ob->Removed = true;
|
|
ob->changed = false;
|
|
ob->reChanged = false;
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [REMOVE " +oTable +"]");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
OpFailStatus MeosSQL::syncUpdate(oRunner *r, bool forceWriteAll)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!r || !con.connected())
|
|
return opStatusFail;
|
|
|
|
mysqlpp::Query queryset = con.query();
|
|
queryset << " Name=" << quote << toString(r->sName) << ", "
|
|
<< " CardNo=" << r->cardNumber << ", "
|
|
<< " StartNo=" << r->StartNo << ", "
|
|
<< " StartTime=" << r->startTime << ", "
|
|
<< " FinishTime=" << r->FinishTime << ", "
|
|
<< " Course=" << r->getCourseId() << ", "
|
|
<< " Class=" << r->getClassId(false) << ", "
|
|
<< " Club=" << r->getClubId() << ", "
|
|
<< " Card=" << r->getCardId() << ", "
|
|
<< " Status=" << r->status << ", "
|
|
<< " InputTime=" << r->inputTime << ", "
|
|
<< " InputStatus=" << r->inputStatus << ", "
|
|
<< " InputPoints=" << r->inputPoints << ", "
|
|
<< " InputPlace=" << r->inputPlace << ", "
|
|
<< " MultiR=" << quote << r->codeMultiR()
|
|
<< r->getDI().generateSQLSet(forceWriteAll);
|
|
|
|
/*
|
|
wstring str = L"write runner " + r->sName + L", st = " + itow(r->startTime) + L"\n";
|
|
OutputDebugString(str.c_str());
|
|
*/
|
|
return syncUpdate(queryset, "oRunner", r);
|
|
}
|
|
|
|
bool MeosSQL::isOld(int counter, const string &time, oBase *ob)
|
|
{
|
|
return counter>ob->counter || time>ob->sqlUpdated;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncRead(bool forceRead, oRunner *r)
|
|
{
|
|
return syncRead(forceRead, r, true, true);
|
|
}
|
|
|
|
string MeosSQL::andWhereOld(oBase *ob) {
|
|
if (ob->sqlUpdated.empty())
|
|
return " AND Counter!=" + itos(ob->counter);
|
|
else
|
|
return " AND (Counter!=" + itos(ob->counter) + " OR Modified!='" + ob->sqlUpdated + "')";
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncRead(bool forceRead, oRunner *r, bool readClassClub, bool readCourseCard)
|
|
{
|
|
errorMessage.clear();
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!r || !con.connected())
|
|
return opStatusFail;
|
|
|
|
if (!forceRead && !r->existInDB())
|
|
return syncUpdate(r, true);
|
|
|
|
if (!r->changed && skipSynchronize(*r))
|
|
return opStatusOK;
|
|
|
|
try {
|
|
Query query = con.query();
|
|
query << "SELECT * FROM oRunner WHERE Id=" << r->Id << andWhereOld(r);
|
|
Result res = query.store();
|
|
|
|
Row row;
|
|
if (!res.empty()) {
|
|
row=res.at(0);
|
|
// Remotly changed update!
|
|
OpFailStatus success=opStatusOK;
|
|
if (r->changed)
|
|
success=opStatusWarning;
|
|
|
|
success = min (success, storeRunner(row, *r, readCourseCard, readClassClub, true, true));
|
|
|
|
r->oe->dataRevision++;
|
|
r->Modified.update();
|
|
r->changed = false;
|
|
|
|
vector<int> mp;
|
|
r->evaluateCard(true, mp, 0, false);
|
|
|
|
//Forget evaluated changes. Not our buisness to update.
|
|
r->changed = false;
|
|
|
|
return success;
|
|
}
|
|
else {
|
|
if (r->Card && readCourseCard)
|
|
syncRead(false, r->Card);
|
|
if (r->Class && readClassClub)
|
|
syncRead(false, r->Class, readClassClub);
|
|
if (r->Course && readCourseCard) {
|
|
set<int> controlIds;
|
|
syncReadCourse(false, r->Course, controlIds);
|
|
if (readClassClub)
|
|
syncReadControls(r->oe, controlIds);
|
|
}
|
|
if (r->Club && readClassClub)
|
|
syncRead(false, r->Club);
|
|
|
|
if (r->changed)
|
|
return syncUpdate(r, false);
|
|
|
|
vector<int> mp;
|
|
r->evaluateCard(true, mp, 0, false);
|
|
r->changed = false;
|
|
r->reChanged = false;
|
|
return opStatusOK;
|
|
}
|
|
|
|
return opStatusOK;
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oRunner]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
return opStatusFail;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncUpdate(oCard *c, bool forceWriteAll)
|
|
{
|
|
errorMessage.clear();
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!c || !con.connected())
|
|
return opStatusFail;
|
|
|
|
mysqlpp::Query queryset = con.query();
|
|
queryset << " CardNo=" << c->cardNo << ", "
|
|
<< " ReadId=" << c->readId << ", "
|
|
<< " Punches=" << quote << c->getPunchString();
|
|
|
|
return syncUpdate(queryset, "oCard", c);
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncRead(bool forceRead, oCard *c)
|
|
{
|
|
errorMessage.clear();
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!c || !con.connected())
|
|
return opStatusFail;
|
|
|
|
if (!forceRead && !c->existInDB())
|
|
return syncUpdate(c, true);
|
|
|
|
if (!c->changed && skipSynchronize(*c))
|
|
return opStatusOK;
|
|
|
|
try{
|
|
Query query = con.query();
|
|
query << "SELECT * FROM oCard WHERE Id=" << c->Id;
|
|
Result res = query.store();
|
|
|
|
Row row;
|
|
if (!res.empty()){
|
|
row=res.at(0);
|
|
if (!c->changed || isOld(row["Counter"], string(row["Modified"]), c)){
|
|
|
|
OpFailStatus success=opStatusOK;
|
|
if (c->changed)
|
|
success=opStatusWarning;
|
|
|
|
storeCard(row, *c);
|
|
c->oe->dataRevision++;
|
|
c->Modified.update();
|
|
c->changed=false;
|
|
return success;
|
|
}
|
|
else if (c->changed){
|
|
return syncUpdate(c, false);
|
|
}
|
|
}
|
|
else{
|
|
//Something is wrong!? Deleted?
|
|
return syncUpdate(c, true);
|
|
}
|
|
|
|
return opStatusOK;
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oCard]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
return opStatusFail;
|
|
}
|
|
|
|
|
|
OpFailStatus MeosSQL::syncUpdate(oTeam *t, bool forceWriteAll) {
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!t || !con.connected())
|
|
return opStatusFail;
|
|
|
|
mysqlpp::Query queryset = con.query();
|
|
|
|
queryset << " Name=" << quote << toString(t->sName) << ", "
|
|
<< " Runners=" << quote << t->getRunners() << ", "
|
|
<< " StartTime=" << t->startTime << ", "
|
|
<< " FinishTime=" << t->FinishTime << ", "
|
|
<< " Class=" << t->getClassId(false) << ", "
|
|
<< " Club=" << t->getClubId() << ", "
|
|
<< " StartNo=" << t->getStartNo() << ", "
|
|
<< " Status=" << t->status << ", "
|
|
<< " InputTime=" << t->inputTime << ", "
|
|
<< " InputStatus=" << t->inputStatus << ", "
|
|
<< " InputPoints=" << t->inputPoints << ", "
|
|
<< " InputPlace=" << t->inputPlace
|
|
<< t->getDI().generateSQLSet(forceWriteAll);
|
|
|
|
//wstring str = L"write team " + t->sName + L"\n";
|
|
//OutputDebugString(str.c_str());
|
|
return syncUpdate(queryset, "oTeam", t);
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncRead(bool forceRead, oTeam *t)
|
|
{
|
|
return syncRead(forceRead, t, true);
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncRead(bool forceRead, oTeam *t, bool readRecursive)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!t || !con.connected())
|
|
return opStatusFail;
|
|
|
|
if (!forceRead && !t->existInDB())
|
|
return syncUpdate(t, true);
|
|
|
|
if (!t->changed && skipSynchronize(*t))
|
|
return opStatusOK;
|
|
|
|
try{
|
|
Query query = con.query();
|
|
query << "SELECT * FROM oTeam WHERE Id=" << t->Id << andWhereOld(t);
|
|
Result res = query.store();
|
|
|
|
Row row;
|
|
if (!res.empty()) {
|
|
row=res.at(0);
|
|
|
|
OpFailStatus success=opStatusOK;
|
|
if (t->changed)
|
|
success=opStatusWarning;
|
|
|
|
storeTeam(row, *t, readRecursive, true);
|
|
t->oe->dataRevision++;
|
|
t->Modified.update();
|
|
t->changed = false;
|
|
t->reChanged = false;
|
|
return success;
|
|
}
|
|
else {
|
|
OpFailStatus success = opStatusOK;
|
|
|
|
if (readRecursive) {
|
|
if (t->Class)
|
|
success = min(success, syncRead(false, t->Class, readRecursive));
|
|
if (t->Club)
|
|
success = min(success, syncRead(false, t->Club));
|
|
for (size_t k = 0; k<t->Runners.size(); k++) {
|
|
if (t->Runners[k])
|
|
success = min(success, syncRead(false, t->Runners[k], false, readRecursive));
|
|
}
|
|
}
|
|
if (t->changed)
|
|
return min(success, syncUpdate(t, false));
|
|
else
|
|
return success;
|
|
}
|
|
|
|
return opStatusOK;
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oTeam]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
return opStatusFail;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncUpdate(oClass *c, bool forceWriteAll)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!c || !con.connected())
|
|
return opStatusFail;
|
|
mysqlpp::Query queryset = con.query();
|
|
|
|
queryset << " Name=" << quote << toString(c->Name) << ","
|
|
<< " Course=" << c->getCourseId() << ","
|
|
<< " MultiCourse=" << quote << c->codeMultiCourse() << ","
|
|
<< " LegMethod=" << quote << c->codeLegMethod()
|
|
<< c->getDI().generateSQLSet(forceWriteAll);
|
|
|
|
return syncUpdate(queryset, "oClass", c);
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncRead(bool forceRead, oClass *c)
|
|
{
|
|
return syncRead(forceRead, c, true);
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncRead(bool forceRead, oClass *c, bool readCourses)
|
|
{
|
|
errorMessage.clear();
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!c || !con.connected())
|
|
return opStatusFail;
|
|
|
|
if (!forceRead && !c->existInDB())
|
|
return syncUpdate(c, true);
|
|
|
|
if (!c->changed && skipSynchronize(*c))
|
|
return opStatusOK;
|
|
|
|
try {
|
|
Query query = con.query();
|
|
query << "SELECT * FROM oClass WHERE Id=" << c->Id << andWhereOld(c);
|
|
Result res = query.store();
|
|
|
|
Row row;
|
|
if (!res.empty()){
|
|
row=res.at(0);
|
|
OpFailStatus success = opStatusOK;
|
|
|
|
if (c->changed)
|
|
success=opStatusWarning;
|
|
|
|
storeClass(row, *c, readCourses, true);
|
|
c->oe->dataRevision++;
|
|
c->Modified.update();
|
|
c->changed = false;
|
|
c->reChanged = false;
|
|
return opStatusOK;
|
|
}
|
|
else {
|
|
OpFailStatus success = opStatusOK;
|
|
if (readCourses) {
|
|
set<int> d;
|
|
c->getMCourseIdSet(d);
|
|
if (c->getCourseId() != 0)
|
|
d.insert(c->getCourseId());
|
|
success = syncReadClassCourses(c, d, true);
|
|
}
|
|
|
|
if (c->changed && !forceRead)
|
|
success = min(success, syncUpdate(c, false));
|
|
|
|
return success;
|
|
}
|
|
|
|
return opStatusOK;
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oClass]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
return opStatusFail;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncReadClassCourses(oClass *c, const set<int> &courses,
|
|
bool readRecursive) {
|
|
OpFailStatus success = opStatusOK;
|
|
if (courses.empty())
|
|
return success;
|
|
oEvent *oe = c->oe;
|
|
try {
|
|
Query query = con.query();
|
|
string in;
|
|
for(set<int>::const_iterator it=courses.begin(); it!=courses.end(); ++it) {
|
|
if (!in.empty())
|
|
in += ",";
|
|
in += itos(*it);
|
|
}
|
|
query << "SELECT Id, Counter, Modified FROM oCourse WHERE Id IN (" << in << ")";
|
|
Result res = query.store();
|
|
set<int> processedCourses(courses);
|
|
set<int> controlIds;
|
|
for (size_t k = 0; k < res.size(); k++) {
|
|
Row row = res.at(k);
|
|
int id = row["Id"];
|
|
int counter = row["Counter"];
|
|
string modified = row["Modified"];
|
|
|
|
pCourse pc = oe->getCourse(id);
|
|
if (!pc) {
|
|
oCourse oc(oe, id);
|
|
oc.setImplicitlyCreated();
|
|
success = min(success, syncReadCourse(true, &oc, controlIds));
|
|
if (!oc.isRemoved())
|
|
addedFromDatabase(oe->addCourse(oc));
|
|
}
|
|
else if (pc->changed || isOld(counter, modified, pc)) {
|
|
success = min(success, syncReadCourse(false, pc, controlIds));
|
|
}
|
|
else {
|
|
for (int m = 0; m <pc->nControls; m++)
|
|
if (pc->Controls[m])
|
|
controlIds.insert(pc->Controls[m]->getId());
|
|
}
|
|
processedCourses.erase(id);
|
|
}
|
|
|
|
// processedCourses should now be empty. The only change it is not empty is that
|
|
// there are locally added courses that are not on the server (which is an error).
|
|
for(set<int>::iterator it = processedCourses.begin(); it != processedCourses.end(); ++it) {
|
|
assert(false);
|
|
pCourse pc = oe->getCourse(*it);
|
|
if (pc) {
|
|
success = min(success, syncUpdate(pc, true));
|
|
}
|
|
}
|
|
|
|
if (readRecursive)
|
|
success = min(success, syncReadControls(oe, controlIds));
|
|
|
|
return success;
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oClassCourse]");
|
|
return opStatusFail;
|
|
}
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncReadControls(oEvent *oe, const set<int> &controls) {
|
|
OpFailStatus success = opStatusOK;
|
|
if (controls.empty())
|
|
return success;
|
|
try {
|
|
Query query = con.query();
|
|
string in;
|
|
for(set<int>::const_iterator it=controls.begin(); it!=controls.end(); ++it) {
|
|
if (!in.empty())
|
|
in += ",";
|
|
in += itos(*it);
|
|
}
|
|
query << "SELECT Id, Counter, Modified FROM oControl WHERE Id IN (" << in << ")";
|
|
Result res = query.store();
|
|
set<int> processedControls(controls);
|
|
for (size_t k = 0; k < res.size(); k++) {
|
|
Row row = res.at(k);
|
|
int id = row["Id"];
|
|
int counter = row["Counter"];
|
|
string modified = row["Modified"];
|
|
|
|
pControl pc = oe->getControl(id, false);
|
|
if (!pc) {
|
|
oControl oc(oe, id);
|
|
success = min(success, syncRead(true, &oc));
|
|
oe->addControl(oc);
|
|
}
|
|
else if (pc->changed || isOld(counter, modified, pc)) {
|
|
success = min(success, syncRead(false, pc));
|
|
}
|
|
processedControls.erase(id);
|
|
}
|
|
|
|
// processedCourses should now be empty, unless there are local controls not yet added.
|
|
for(set<int>::iterator it = processedControls.begin(); it != processedControls.end(); ++it) {
|
|
pControl pc = oe->getControl(*it, false);
|
|
if (pc) {
|
|
success = min(success, syncUpdate(pc, true));
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oClass]");
|
|
return opStatusFail;
|
|
}
|
|
}
|
|
|
|
|
|
OpFailStatus MeosSQL::syncUpdate(oClub *c, bool forceWriteAll)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!c || !con.connected())
|
|
return opStatusFail;
|
|
mysqlpp::Query queryset = con.query();
|
|
queryset << " Name=" << quote << toString(c->name)
|
|
<< c->getDI().generateSQLSet(forceWriteAll);
|
|
|
|
return syncUpdate(queryset, "oClub", c);
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncRead(bool forceRead, oClub *c)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!c || !con.connected())
|
|
return opStatusFail;
|
|
|
|
if (!forceRead && !c->existInDB())
|
|
return syncUpdate(c, true);
|
|
|
|
try{
|
|
Query query = con.query();
|
|
query << "SELECT * FROM oClub WHERE Id=" << c->Id;
|
|
Result res = query.store();
|
|
|
|
Row row;
|
|
if (!res.empty()){
|
|
row=res.at(0);
|
|
if (!c->changed || isOld(row["Counter"], string(row["Modified"]), c)) {
|
|
|
|
OpFailStatus success = opStatusOK;
|
|
if (c->changed)
|
|
success = opStatusWarning;
|
|
|
|
storeClub(row, *c);
|
|
|
|
c->Modified.update();
|
|
c->changed=false;
|
|
return success;
|
|
}
|
|
else if (c->changed){
|
|
return syncUpdate(c, false);
|
|
}
|
|
}
|
|
else{
|
|
//Something is wrong!? Deleted?
|
|
return syncUpdate(c, true);
|
|
}
|
|
return opStatusOK;
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oClub]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
return opStatusFail;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncUpdate(oControl *c, bool forceWriteAll) {
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!c || !con.connected())
|
|
return opStatusFail;
|
|
|
|
mysqlpp::Query queryset = con.query();
|
|
queryset << " Name=" << quote << toString(c->Name) << ", "
|
|
<< " Numbers=" << quote << toString(c->codeNumbers()) << ","
|
|
<< " Status=" << c->Status
|
|
<< c->getDI().generateSQLSet(forceWriteAll);
|
|
|
|
return syncUpdate(queryset, "oControl", c);
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncRead(bool forceRead, oControl *c)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!c || !con.connected())
|
|
return opStatusFail;
|
|
|
|
if (!forceRead && !c->existInDB())
|
|
return syncUpdate(c, true);
|
|
|
|
try{
|
|
Query query = con.query();
|
|
query << "SELECT * FROM oControl WHERE Id=" << c->Id << andWhereOld(c);
|
|
Result res = query.store();
|
|
|
|
Row row;
|
|
if (!res.empty()){
|
|
row=res.at(0);
|
|
|
|
OpFailStatus success=opStatusOK;
|
|
if (c->changed)
|
|
success=opStatusWarning;
|
|
|
|
storeControl(row, *c);
|
|
c->oe->dataRevision++;
|
|
c->Modified.update();
|
|
c->changed=false;
|
|
return success;
|
|
}
|
|
else if (c->changed) {
|
|
return syncUpdate(c, false);
|
|
}
|
|
|
|
return opStatusOK;
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oControl]");
|
|
return opStatusFail;
|
|
}
|
|
return opStatusFail;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncUpdate(oCourse *c, bool forceWriteAll)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
bool isTMP = c->sqlUpdated == "TMP";
|
|
assert(!isTMP);
|
|
if (isTMP)
|
|
return opStatusFail;
|
|
|
|
if (!c || !con.connected())
|
|
return opStatusFail;
|
|
mysqlpp::Query queryset = con.query();
|
|
queryset << " Name=" << quote << toString(c->Name) << ", "
|
|
<< " Length=" << unsigned(c->Length) << ", "
|
|
<< " Controls=" << quote << c->getControls() << ", "
|
|
<< " Legs=" << quote << c->getLegLengths()
|
|
<< c->getDI().generateSQLSet(true);
|
|
|
|
return syncUpdate(queryset, "oCourse", c);
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncRead(bool forceRead, oCourse *c)
|
|
{
|
|
set<int> controls;
|
|
OpFailStatus res = syncReadCourse(forceRead, c, controls);
|
|
res = min( res, syncReadControls(c->oe, controls));
|
|
return res;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncReadCourse(bool forceRead, oCourse *c, set<int> &readControls) {
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!c || !con.connected())
|
|
return opStatusFail;
|
|
|
|
bool isTMP = c->sqlUpdated == "TMP";
|
|
assert(!isTMP);
|
|
if (isTMP)
|
|
return opStatusFail;
|
|
|
|
if (!forceRead && !c->existInDB())
|
|
return syncUpdate(c, true);
|
|
|
|
if (!c->changed && skipSynchronize(*c))
|
|
return opStatusOK; // Skipped readout
|
|
|
|
try{
|
|
Query query = con.query();
|
|
query << "SELECT * FROM oCourse WHERE Id=" << c->Id << andWhereOld(c);
|
|
Result res = query.store();
|
|
|
|
Row row;
|
|
if (!res.empty()) {
|
|
row=res.at(0);
|
|
|
|
OpFailStatus success = opStatusOK;
|
|
if (c->changed)
|
|
success = opStatusWarning;
|
|
|
|
storeCourse(row, *c, readControls, true);
|
|
c->oe->dataRevision++;
|
|
c->Modified.update();
|
|
c->changed=false;
|
|
c->reChanged=false;
|
|
return success;
|
|
}
|
|
else {
|
|
OpFailStatus success = opStatusOK;
|
|
|
|
// Plain read controls
|
|
for (int i=0;i<c->nControls; i++) {
|
|
if (c->Controls[i])
|
|
readControls.insert(c->Controls[i]->getId());
|
|
//success = min(success, syncRead(false, c->Controls[i]));
|
|
}
|
|
|
|
if (c->changed && !forceRead)
|
|
return min(success, syncUpdate(c, false));
|
|
else
|
|
return success;
|
|
}
|
|
|
|
return opStatusOK;
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oCourse]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
return opStatusFail;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncUpdate(oFreePunch *c, bool forceWriteAll)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty()) {
|
|
errorMessage = "Not connected";
|
|
return opStatusFail;
|
|
}
|
|
|
|
if (!c || !con.connected()) {
|
|
errorMessage = "Not connected";
|
|
return opStatusFail;
|
|
}
|
|
mysqlpp::Query queryset = con.query();
|
|
queryset << " CardNo=" << c->CardNo << ", "
|
|
<< " Type=" << c->Type << ","
|
|
<< " Time=" << c->Time;
|
|
|
|
return syncUpdate(queryset, "oPunch", c);
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncRead(bool forceRead, oFreePunch *c, bool rehash)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return opStatusFail;
|
|
|
|
if (!c || !con.connected())
|
|
return opStatusFail;
|
|
|
|
if (!forceRead && !c->existInDB())
|
|
return syncUpdate(c, true);
|
|
|
|
if (!c->changed && skipSynchronize(*c))
|
|
return opStatusOK;
|
|
|
|
try{
|
|
Query query = con.query();
|
|
query << "SELECT * FROM oPunch WHERE Id=" << c->Id << andWhereOld(c);
|
|
Result res = query.store();
|
|
|
|
Row row;
|
|
if (!res.empty()) {
|
|
row=res.at(0);
|
|
OpFailStatus success = opStatusOK;
|
|
if (c->changed)
|
|
success = opStatusWarning;
|
|
|
|
storePunch(row, *c, rehash);
|
|
c->oe->dataRevision++;
|
|
c->Modified.update();
|
|
c->changed=false;
|
|
return success;
|
|
}
|
|
else if (c->changed) {
|
|
return syncUpdate(c, false);
|
|
}
|
|
|
|
return opStatusOK;
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCREAD oPunch]");
|
|
return opStatusFail;
|
|
}
|
|
return opStatusFail;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::updateTime(const char *oTable, oBase *ob)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
mysqlpp::Query query = con.query();
|
|
|
|
query << "SELECT Modified, Counter FROM " << oTable << " WHERE Id=" << ob->Id;
|
|
|
|
mysqlpp::Result res = query.store();
|
|
|
|
if (!res.empty()) {
|
|
ob->sqlUpdated=res.at(0)["Modified"];
|
|
ob->counter = res.at(0)["Counter"];
|
|
ob->changed=false; //Mark as saved.
|
|
// Mark all data as stored in memory
|
|
if (ob->getDISize() >= 0)
|
|
ob->getDI().allDataStored();
|
|
return opStatusOK;
|
|
}
|
|
else {
|
|
alert("Update time failed for " + string(oTable));
|
|
return opStatusFail;
|
|
}
|
|
}
|
|
|
|
static int nUpdate = 0;
|
|
|
|
mysqlpp::ResNSel MeosSQL::updateCounter(const char *oTable, int id, mysqlpp::Query *updateqry) {
|
|
Query query = con.query();
|
|
|
|
try {
|
|
query.exec(string("LOCK TABLES ") + oTable + string(" WRITE"));
|
|
query << "SELECT MAX(Counter) FROM " << oTable;
|
|
int counter;
|
|
{
|
|
const mysqlpp::ColData c = query.store().at(0).at(0);
|
|
bool null = c.is_null();
|
|
counter = null ? 1 : int(c) + 1;
|
|
}
|
|
query.reset();
|
|
query << "UPDATE " << oTable << " SET Counter=" << counter;
|
|
|
|
if (updateqry != 0)
|
|
query << "," << updateqry->str();
|
|
|
|
query << " WHERE Id=" << id;
|
|
|
|
mysqlpp::ResNSel res = query.execute();
|
|
|
|
query.exec("UNLOCK TABLES");
|
|
|
|
query.reset();
|
|
query << "UPDATE oCounter SET " << oTable << "=GREATEST(" << counter << "," << oTable << ")";
|
|
query.execute();
|
|
return res;
|
|
}
|
|
catch(...) {
|
|
query.exec("UNLOCK TABLES");
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
OpFailStatus MeosSQL::syncUpdate(mysqlpp::Query &updateqry,
|
|
const char *oTable, oBase *ob)
|
|
{
|
|
nUpdate++;
|
|
if (nUpdate % 100 == 99)
|
|
OutputDebugStringA((itos(nUpdate) +" updates\n").c_str());
|
|
|
|
assert(ob->getEvent());
|
|
if (!ob->getEvent())
|
|
return opStatusFail;
|
|
|
|
if (ob->getEvent()->isReadOnly())
|
|
return opStatusOK;
|
|
|
|
errorMessage.clear();
|
|
|
|
if (!con.connected()) {
|
|
errorMessage = "Not connected";
|
|
return opStatusFail;
|
|
}
|
|
|
|
mysqlpp::Query query = con.query();
|
|
try{
|
|
if (!ob->existInDB()) {
|
|
bool setId = false;
|
|
|
|
if (ob->Id > 0) {
|
|
query << "SELECT Id FROM " << oTable << " WHERE Id=" << ob->Id;
|
|
Result res=query.store();
|
|
if (res && res.num_rows()==0)
|
|
setId = true;
|
|
else if (ob->isImplicitlyCreated()) {
|
|
return opStatusWarning;//XXX Should we read this object?
|
|
}
|
|
}
|
|
else {
|
|
assert(!ob->isImplicitlyCreated());
|
|
}
|
|
|
|
query.reset();
|
|
query << "INSERT INTO " << oTable << " SET " << updateqry.str();
|
|
|
|
if (setId)
|
|
query << ", Id=" << ob->Id;
|
|
|
|
mysqlpp::ResNSel res=query.execute();
|
|
if (res) {
|
|
if (ob->Id > 0 && ob->Id!=(int)res.insert_id) {
|
|
ob->correctionNeeded = true;
|
|
}
|
|
|
|
if (ob->Id != res.insert_id)
|
|
ob->changeId((int)res.insert_id);
|
|
|
|
updateCounter(oTable, ob->Id, 0);
|
|
ob->oe->updateFreeId(ob);
|
|
|
|
return updateTime(oTable, ob);
|
|
}
|
|
else {
|
|
errorMessage = "Unexpected error: update failed";
|
|
return opStatusFail;
|
|
}
|
|
}
|
|
else {
|
|
|
|
mysqlpp::ResNSel res = updateCounter(oTable, ob->Id, &updateqry);
|
|
|
|
if (res){
|
|
if (res.rows==0){
|
|
query.reset();
|
|
|
|
query << "SELECT Id FROM " << oTable << " WHERE Id=" << ob->Id;
|
|
mysqlpp::Result store_res = query.store();
|
|
|
|
if (store_res.num_rows()==0){
|
|
query.reset();
|
|
query << "INSERT INTO " << oTable << " SET " <<
|
|
updateqry.str() << ", Id=" << ob->Id;
|
|
|
|
res=query.execute();
|
|
if (!res) {
|
|
errorMessage = "Unexpected error: insert failed";
|
|
return opStatusFail;
|
|
}
|
|
|
|
updateCounter(oTable, ob->Id, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return updateTime(oTable, ob);
|
|
}
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [" + oTable + " \n\n(" + query.str() + ")]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
return opStatusFail;
|
|
}
|
|
|
|
bool MeosSQL::checkOldVersion(oEvent *oe, Row &row) {
|
|
int dbv=int(row["BuildVersion"]);
|
|
if ( dbv<buildVersion )
|
|
oe->updateChanged();
|
|
else if (dbv>buildVersion)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
OpFailStatus MeosSQL::SyncEvent(oEvent *oe) {
|
|
errorMessage.clear();
|
|
OpFailStatus retValue = opStatusOK;
|
|
if (!con.connected())
|
|
return opStatusOK;
|
|
|
|
bool oldVersion=false;
|
|
try{
|
|
Query query = con.query();
|
|
|
|
query << "SELECT * FROM oEvent";
|
|
query << " WHERE Counter>" << oe->counter;
|
|
|
|
Result res = query.store();
|
|
|
|
if (res && res.num_rows()>0) {
|
|
Row row=res.at(0);
|
|
string Modified=row["Modified"];
|
|
int counter = row["Counter"];
|
|
|
|
oldVersion = checkOldVersion(oe, row);
|
|
/* int dbv=int(row["BuildVersion"]);
|
|
if ( dbv<buildVersion )
|
|
oe->updateChanged();
|
|
else if (dbv>buildVersion)
|
|
oldVersion=true;
|
|
*/
|
|
if (isOld(counter, Modified, oe)) {
|
|
oe->Name=fromUTF(string(row["Name"]));
|
|
oe->Annotation = fromUTF(string(row["Annotation"]));
|
|
oe->Date=fromUTF(string(row["Date"]));
|
|
oe->ZeroTime=row["ZeroTime"];
|
|
oe->sqlUpdated=Modified;
|
|
const string &lRaw = row.raw_string(res.field_num("Lists"));
|
|
try {
|
|
importLists(oe, lRaw.c_str());
|
|
}
|
|
catch (std::exception &ex) {
|
|
alert(ex.what());
|
|
}
|
|
oe->counter = counter;
|
|
oDataInterface odi=oe->getDI();
|
|
storeData(odi, row, oe->dataRevision);
|
|
oe->setCurrency(-1, L"", L"", false);//Init temp data from stored data
|
|
oe->getMeOSFeatures().deserialize(oe->getDCI().getString("Features"), *oe);
|
|
oe->changed=false;
|
|
oe->changedObject();
|
|
}
|
|
else if (oe->isChanged()) {
|
|
|
|
string listEnc;
|
|
try {
|
|
encodeLists(oe, listEnc);
|
|
}
|
|
catch (std::exception &ex) {
|
|
retValue = opStatusWarning;
|
|
alert(ex.what());
|
|
}
|
|
|
|
mysqlpp::Query queryset = con.query();
|
|
queryset << " Name=" << quote << limitLength(oe->Name, 128) << ", "
|
|
<< " Annotation=" << quote << limitLength(oe->Annotation, 128) << ", "
|
|
<< " Date=" << quote << toString(oe->Date) << ", "
|
|
<< " NameId=" << quote << toString(oe->currentNameId) << ", "
|
|
<< " ZeroTime=" << unsigned(oe->ZeroTime) << ", "
|
|
<< " BuildVersion=if (BuildVersion<" <<
|
|
buildVersion << "," << buildVersion << ",BuildVersion), "
|
|
<< " Lists=" << quote << listEnc
|
|
<< oe->getDI().generateSQLSet(false);
|
|
|
|
syncUpdate(queryset, "oEvent", oe);
|
|
|
|
// Update list database;
|
|
con.select_db("MeOSMain");
|
|
queryset.reset();
|
|
queryset << "UPDATE oEvent SET Name=" << quote << limitLength(oe->Name, 128) << ", "
|
|
<< " Annotation=" << quote << limitLength(oe->Annotation, 128) << ", "
|
|
<< " Date=" << quote << toString(oe->Date) << ", "
|
|
<< " NameId=" << quote << toString(oe->currentNameId) << ", "
|
|
<< " ZeroTime=" << unsigned(oe->ZeroTime)
|
|
<< " WHERE Id=" << oe->Id;
|
|
|
|
queryset.execute();
|
|
//syncUpdate(queryset, "oEvent", oe, true);
|
|
con.select_db(CmpDataBase);
|
|
}
|
|
}
|
|
else if ( oe->isChanged() ){
|
|
string listEnc;
|
|
encodeLists(oe, listEnc);
|
|
|
|
mysqlpp::Query queryset = con.query();
|
|
queryset << " Name=" << quote << limitLength(oe->Name, 128) << ", "
|
|
<< " Annotation=" << quote << limitLength(oe->Annotation, 128) << ", "
|
|
<< " Date=" << quote << toString(oe->Date) << ","
|
|
<< " NameId=" << quote << toString(oe->currentNameId) << ","
|
|
<< " ZeroTime=" << unsigned(oe->ZeroTime) << ","
|
|
<< " BuildVersion=if (BuildVersion<" <<
|
|
buildVersion << "," << buildVersion << ",BuildVersion),"
|
|
<< " Lists=" << quote << listEnc
|
|
<< oe->getDI().generateSQLSet(false);
|
|
|
|
syncUpdate(queryset, "oEvent", oe);
|
|
|
|
// Update list database;
|
|
con.select_db("MeOSMain");
|
|
queryset.reset();
|
|
queryset << "UPDATE oEvent SET Name=" << quote << limitLength(oe->Name, 128) << ", "
|
|
<< " Annotation=" << quote << limitLength(oe->Annotation, 128) << ", "
|
|
<< " Date=" << quote << toString(oe->Date) << ", "
|
|
<< " NameId=" << quote << toString(oe->currentNameId) << ", "
|
|
<< " ZeroTime=" << unsigned(oe->ZeroTime)
|
|
<< " WHERE Id=" << oe->Id;
|
|
|
|
queryset.execute();
|
|
//syncUpdate(queryset, "oEvent", oe, true);
|
|
con.select_db(CmpDataBase);
|
|
}
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
setDefaultDB();
|
|
alert(string(er.what())+" [SYNCLIST oEvent]");
|
|
return opStatusFail;
|
|
}
|
|
|
|
if (oldVersion) {
|
|
warnOldDB();
|
|
return opStatusWarning;
|
|
}
|
|
|
|
return retValue;
|
|
}
|
|
|
|
void MeosSQL::warnOldDB() {
|
|
if (!warnedOldVersion) {
|
|
warnedOldVersion=true;
|
|
alert("warn:olddbversion");
|
|
}
|
|
}
|
|
|
|
bool MeosSQL::syncListRunner(oEvent *oe)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (!con.connected())
|
|
return false;
|
|
|
|
try{
|
|
Query query = con.query();
|
|
|
|
/*query << "SELECT Id, Counter, Modified, Removed FROM oRunner";
|
|
query << " WHERE Counter > " << oe->sqlCounterRunners;
|
|
query << " OR Modified > '" << oe->sqlUpdateRunners << "'";*/
|
|
Result res = query.store(selectUpdated("oRunner", oe->sqlUpdateRunners, oe->sqlCounterRunners));
|
|
|
|
if (res) {
|
|
for (int i=0; i<res.num_rows(); i++) {
|
|
Row row=res.at(i);
|
|
int Id = row["Id"];
|
|
int counter = row["Counter"];
|
|
string modified = row["Modified"];
|
|
|
|
if (int(row["Removed"])==1){
|
|
oRunner *r=oe->getRunner(Id, 0);
|
|
|
|
if (r) {
|
|
r->Removed=true;
|
|
if (r->tInTeam)
|
|
r->tInTeam->correctRemove(r);
|
|
|
|
if (r->tParentRunner) {
|
|
r->tParentRunner->correctRemove(r);
|
|
}
|
|
|
|
r->changedObject();
|
|
oe->sqlChangedRunners = true;
|
|
}
|
|
}
|
|
else{
|
|
oRunner *r=oe->getRunner(Id, 0);
|
|
|
|
if (r){
|
|
if (isOld(counter, modified, r))
|
|
syncRead(false, r);
|
|
}
|
|
else {
|
|
oRunner or(oe, Id);
|
|
syncRead(true, &or, false, false);
|
|
r = oe->addRunner(or, false);
|
|
}
|
|
}
|
|
oe->sqlCounterRunners = max(counter, oe->sqlCounterRunners);
|
|
oe->sqlUpdateRunners = max(modified, oe->sqlUpdateRunners);
|
|
}
|
|
}
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCLIST oRunner]");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MeosSQL::syncListClass(oEvent *oe)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (!con.connected())
|
|
return false;
|
|
|
|
try{
|
|
Query query = con.query();
|
|
/*
|
|
query << "SELECT Id, Counter, Modified, Removed FROM oClass";
|
|
query << " WHERE Counter > " << oe->sqlCounterClasses;
|
|
query << " OR Modified > '" << oe->sqlUpdateClasses << "'";*/
|
|
|
|
//Result res = query.store();
|
|
Result res = query.store(selectUpdated("oClass", oe->sqlUpdateClasses, oe->sqlCounterClasses));
|
|
|
|
if (res) {
|
|
|
|
for (int i=0; i<res.num_rows(); i++) {
|
|
Row row=res.at(i);
|
|
int counter = row["Counter"];
|
|
string modified = row["Modified"];
|
|
|
|
int Id=row["Id"];
|
|
|
|
if (int(row["Removed"])) {
|
|
oClass *c=oe->getClass(Id);
|
|
if (c) {
|
|
c->changedObject();
|
|
c->Removed=true;
|
|
}
|
|
}
|
|
else {
|
|
oClass *c=oe->getClass(Id);
|
|
|
|
if (!c) {
|
|
oClass oc(oe, Id);
|
|
syncRead(true, &oc, false);
|
|
c=oe->addClass(oc);
|
|
if (c!=0) {
|
|
c->changed = false;
|
|
//syncRead(true, c, false);
|
|
}
|
|
}
|
|
else if (isOld(counter, modified, c))
|
|
syncRead(false, c, false);
|
|
}
|
|
oe->sqlCounterClasses = max(counter, oe->sqlCounterClasses);
|
|
oe->sqlUpdateClasses = max(modified, oe->sqlUpdateClasses);
|
|
}
|
|
}
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCLIST oClass]");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MeosSQL::syncListClub(oEvent *oe)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (!con.connected())
|
|
return false;
|
|
|
|
try{
|
|
Query query = con.query();
|
|
|
|
/*query << "SELECT Id, Counter, Modified, Removed FROM oClub";
|
|
query << " WHERE Counter > " << oe->sqlCounterClubs;
|
|
query << " OR Modified > '" << oe->sqlUpdateClubs << "'";*/
|
|
Result res = query.store(selectUpdated("oClub", oe->sqlUpdateClubs, oe->sqlCounterClubs));
|
|
|
|
if (res) {
|
|
for(int i=0; i<res.num_rows(); i++){
|
|
Row row=res.at(i);
|
|
|
|
int counter = row["Counter"];
|
|
string modified = row["Modified"];
|
|
int Id=row["Id"];
|
|
|
|
if (int(row["Removed"])){
|
|
oClub *c=oe->getClub(Id);
|
|
|
|
if (c) {
|
|
c->Removed=true;
|
|
c->changedObject();
|
|
}
|
|
}
|
|
else {
|
|
oClub *c=oe->getClub(Id);
|
|
|
|
if (c==0) {
|
|
oClub oc(oe, Id);
|
|
syncRead(true, &oc);
|
|
oe->addClub(oc);
|
|
}
|
|
else if (isOld(counter, modified, c))
|
|
syncRead(false, c);
|
|
}
|
|
oe->sqlCounterClubs = max(counter, oe->sqlCounterClubs);
|
|
oe->sqlUpdateClubs = max(modified, oe->sqlUpdateClubs);
|
|
}
|
|
}
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCLIST oClub]");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MeosSQL::syncListCourse(oEvent *oe)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (!con.connected())
|
|
return false;
|
|
|
|
try{
|
|
Query query = con.query();
|
|
/*
|
|
query << "SELECT Id, Counter, Modified, Removed FROM oCourse";
|
|
query << " WHERE Counter > " << oe->sqlCounterCourses;
|
|
query << " OR Modified > '" << oe->sqlUpdateCourses << "'";
|
|
*/
|
|
Result res = query.store(selectUpdated("oCourse", oe->sqlUpdateCourses, oe->sqlCounterCourses));
|
|
|
|
|
|
if (res) {
|
|
set<int> tmp;
|
|
for(int i=0; i<res.num_rows(); i++){
|
|
Row row=res.at(i);
|
|
int counter = row["Counter"];
|
|
string modified(row["Modified"]);
|
|
|
|
int Id = row["Id"];
|
|
|
|
if (int(row["Removed"])){
|
|
oCourse *c=oe->getCourse(Id);
|
|
|
|
if (c) {
|
|
c->Removed=true;
|
|
c->changedObject();
|
|
}
|
|
}
|
|
else{
|
|
oCourse *c=oe->getCourse(Id);
|
|
|
|
if (c==0) {
|
|
oCourse oc(oe, Id);
|
|
syncReadCourse(true, &oc, tmp);
|
|
oe->addCourse(oc);
|
|
}
|
|
else if (isOld(counter, modified, c))
|
|
syncReadCourse(false, c, tmp);
|
|
}
|
|
oe->sqlCounterCourses = max(counter, oe->sqlCounterCourses);
|
|
oe->sqlUpdateCourses = max(modified, oe->sqlUpdateCourses);
|
|
}
|
|
}
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCLIST oCourse]");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MeosSQL::syncListCard(oEvent *oe)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (!con.connected())
|
|
return false;
|
|
|
|
try{
|
|
Query query = con.query();
|
|
|
|
/*query << "SELECT Id, Counter, Modified, Removed FROM oCard";
|
|
query << " WHERE Counter>0 AND (Counter>" << oe->sqlCounterCards;
|
|
query << " OR Modified>'" << oe->sqlUpdateCards << "')";*/
|
|
|
|
Result res = query.store(selectUpdated("oCard", oe->sqlUpdateCards, oe->sqlCounterCards));
|
|
|
|
if (res) {
|
|
for (int i=0; i<res.num_rows(); i++) {
|
|
Row row = res.at(i);
|
|
int counter = row["Counter"];
|
|
string modified(row["Modified"]);
|
|
int Id = row["Id"];
|
|
|
|
if (int(row["Removed"])) {
|
|
oCard *c=oe->getCard(Id);
|
|
if (c) {
|
|
c->changedObject();
|
|
c->Removed=true;
|
|
}
|
|
}
|
|
else {
|
|
oCard *c=oe->getCard(Id);
|
|
|
|
if (c) {
|
|
if (isOld(counter, modified, c))
|
|
syncRead(false, c);
|
|
}
|
|
else {
|
|
oCard oc(oe, Id);
|
|
c = oe->addCard(oc);
|
|
if (c!=0)
|
|
syncRead(true, c);
|
|
}
|
|
}
|
|
oe->sqlCounterCards = max(counter, oe->sqlCounterCards);
|
|
oe->sqlUpdateCards = max(modified, oe->sqlUpdateCards);
|
|
}
|
|
}
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCLIST oCard]");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MeosSQL::syncListControl(oEvent *oe)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (!con.connected())
|
|
return false;
|
|
|
|
try{
|
|
Query query = con.query();
|
|
|
|
/*query << "SELECT Id, Counter, Modified, Removed FROM oControl";
|
|
query << " WHERE Counter > " << oe->sqlCounterControls;
|
|
query << " OR Modified > '" << oe->sqlUpdateControls << "'";*/
|
|
|
|
Result res = query.store(selectUpdated("oControl", oe->sqlUpdateControls, oe->sqlCounterControls));
|
|
|
|
//Result res = query.store();
|
|
|
|
if (res) {
|
|
for(int i=0; i<res.num_rows(); i++){
|
|
Row row=res.at(i);
|
|
int counter = row["Counter"];
|
|
string modified(row["Modified"]);
|
|
int Id = row["Id"];
|
|
|
|
if (int(row["Removed"])){
|
|
oControl *c=oe->getControl(Id, false);
|
|
|
|
if (c) {
|
|
c->Removed=true;
|
|
c->changedObject();
|
|
}
|
|
}
|
|
else {
|
|
oControl *c=oe->getControl(Id, false);
|
|
if (c) {
|
|
if (isOld(counter, modified, c))
|
|
syncRead(false, c);
|
|
}
|
|
else {
|
|
oControl oc(oe, Id);
|
|
syncRead(true, &oc);
|
|
c = oe->addControl(oc);
|
|
// if (c!=0)
|
|
// syncRead(true, c);
|
|
}
|
|
}
|
|
oe->sqlCounterControls = max(counter, oe->sqlCounterControls);
|
|
oe->sqlUpdateControls = max(modified, oe->sqlUpdateControls);
|
|
}
|
|
}
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCLIST oControl]");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MeosSQL::syncListPunch(oEvent *oe)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (!con.connected())
|
|
return false;
|
|
|
|
try{
|
|
Query query = con.query();
|
|
|
|
/*query << "SELECT Id, Counter, Modified, Removed FROM oPunch";
|
|
query << " WHERE Counter > " << oe->sqlCounterPunches;
|
|
query << " OR Modified > '" << oe->sqlUpdatePunches << "' ORDER BY Id";*/
|
|
Result res = query.store(selectUpdated("oPunch", oe->sqlUpdatePunches, oe->sqlCounterPunches) + " ORDER BY Id");
|
|
//Result res = query.store();
|
|
|
|
if (res) {
|
|
for(int i=0; i<res.num_rows(); i++){
|
|
Row row=res.at(i);
|
|
int counter = row["Counter"];
|
|
string modified(row["Modified"]);
|
|
int Id=row["Id"];
|
|
|
|
if (int(row["Removed"])) {
|
|
oFreePunch *c=oe->getPunch(Id);
|
|
if (c) {
|
|
c->Removed=true;
|
|
int cid = c->getControlId();
|
|
oFreePunch::rehashPunches(*oe, c->CardNo, 0);
|
|
pRunner r = oe->getRunner(c->tRunnerId, 0);
|
|
if (r)
|
|
r->markClassChanged(cid);
|
|
}
|
|
}
|
|
else {
|
|
oFreePunch *c=oe->getPunch(Id);
|
|
|
|
if (c) {
|
|
if (isOld(counter, modified, c))
|
|
syncRead(false, c, true);
|
|
}
|
|
else {
|
|
oFreePunch p(oe, Id);
|
|
syncRead(true, &p, false);
|
|
oe->addFreePunch(p);
|
|
}
|
|
}
|
|
oe->sqlCounterPunches = max(counter, oe->sqlCounterPunches);
|
|
oe->sqlUpdatePunches = max(modified, oe->sqlUpdatePunches);
|
|
}
|
|
}
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCLIST oPunch]");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MeosSQL::syncListTeam(oEvent *oe)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (!con.connected())
|
|
return false;
|
|
|
|
try{
|
|
Query query = con.query();
|
|
/*
|
|
query << "SELECT Id, Counter, Modified, Removed FROM oTeam";
|
|
query << " WHERE Counter > " << oe->sqlCounterTeams;
|
|
query << " OR Modified > '" << oe->sqlUpdateTeams << "'";*/
|
|
|
|
Result res = query.store(selectUpdated("oTeam", oe->sqlUpdateTeams, oe->sqlCounterTeams));
|
|
|
|
if (res) {
|
|
for (int i=0; i<res.num_rows(); i++) {
|
|
Row row=res.at(i);
|
|
int counter = row["Counter"];
|
|
string modified(row["Modified"]);
|
|
int Id = row["Id"];
|
|
|
|
if (int(row["Removed"])){
|
|
oTeam *t=oe->getTeam(Id);
|
|
if (t) {
|
|
t->changedObject();
|
|
t->prepareRemove();
|
|
t->Removed=true;
|
|
oe->sqlChangedTeams = true;
|
|
}
|
|
}
|
|
else {
|
|
oTeam *t=oe->getTeam(Id);
|
|
|
|
if (t) {
|
|
if (isOld(counter, modified, t))
|
|
syncRead(false, t, false);
|
|
}
|
|
else{
|
|
oTeam ot(oe, Id);
|
|
t = oe->addTeam(ot, false);
|
|
if (t) {
|
|
syncRead(true, t, false);
|
|
t->apply(false, 0, false);
|
|
t->changed = false;
|
|
}
|
|
}
|
|
}
|
|
oe->sqlCounterTeams = max(counter, oe->sqlCounterTeams);
|
|
oe->sqlUpdateTeams = max(modified, oe->sqlUpdateTeams);
|
|
}
|
|
}
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
alert(string(er.what())+" [SYNCLIST oTeam]");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
string MeosSQL::selectUpdated(const char *oTable, const string &updated, int counter) {
|
|
string p1 = string("SELECT Id, Counter, Modified, Removed FROM ") + oTable;
|
|
|
|
string q = "(" + p1 + " WHERE Counter>" + itos(counter) + ") UNION ALL ("+
|
|
p1 + " WHERE Modified>'" + updated + "' AND Counter<" + itos(counter) + " AND Counter>0)";
|
|
|
|
return q;
|
|
}
|
|
|
|
bool MeosSQL::checkConnection(oEvent *oe)
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (!oe) {
|
|
if (monitorId && con.connected()) {
|
|
try {
|
|
Query query = con.query();
|
|
query << "Update oMonitor SET Removed=1 WHERE Id = " << monitorId;
|
|
query.execute();
|
|
}
|
|
catch(...) {
|
|
return false; //Not an important error.
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
oe->connectedClients.clear();
|
|
if (monitorId==0) {
|
|
try {
|
|
Query query = con.query();
|
|
query << "INSERT INTO oMonitor SET Count=1, Client=" << quote << toString(oe->clientName);
|
|
ResNSel res=query.execute();
|
|
if (res)
|
|
monitorId=static_cast<int>(res.insert_id);
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
oe->connectedClients.push_back(L"Error: " + fromUTF(er.what()));
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
try {
|
|
Query query = con.query();
|
|
query << "Update oMonitor SET Count=Count+1, Client=" << quote << toString(oe->clientName)
|
|
<< " WHERE Id = " << monitorId;
|
|
query.execute();
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
oe->connectedClients.push_back(L"Error: " + fromUTF(er.what()));
|
|
return false;
|
|
}
|
|
}
|
|
bool callback=false;
|
|
|
|
try {
|
|
Query query = con.query();
|
|
query << "SELECT Id, Client FROM oMonitor WHERE Modified>TIMESTAMPADD(SECOND, -30, NOW())"
|
|
" AND Removed=0 ORDER BY Client";
|
|
|
|
Result res = query.store();
|
|
|
|
if (res) {
|
|
for (int i=0; i<res.num_rows(); i++) {
|
|
Row row=res.at(i);
|
|
oe->connectedClients.push_back(fromUTF(string(row["Client"])));
|
|
|
|
if (int(row["Id"])==monitorId)
|
|
callback=true;
|
|
}
|
|
}
|
|
}
|
|
catch (const mysqlpp::Exception& er){
|
|
oe->connectedClients.push_back(L"Error: " + fromUTF(er.what()));
|
|
return false;
|
|
}
|
|
return callback;
|
|
}
|
|
|
|
void MeosSQL::setDefaultDB()
|
|
{
|
|
errorMessage.clear();
|
|
|
|
if (CmpDataBase.empty())
|
|
return;
|
|
|
|
try {
|
|
if (!con.connected())
|
|
return;
|
|
|
|
con.select_db(CmpDataBase);
|
|
}
|
|
catch(...) {
|
|
}
|
|
}
|
|
|
|
bool MeosSQL::dropDatabase(oEvent *oe)
|
|
{
|
|
// Check if other clients are connected.
|
|
if ( !checkConnection(oe) ) {
|
|
if (!oe->connectedClients.empty())
|
|
throw meosException("Database is used and cannot be deleted.");
|
|
}
|
|
|
|
if (oe->connectedClients.size()!=1) {
|
|
throw meosException("Database is used and cannot be deleted.");
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
con.select_db("MeOSMain");
|
|
}
|
|
catch (const mysqlpp::Exception& er) {
|
|
alert(string(er.what()) + " MySQL Error. Select MeosMain");
|
|
setDefaultDB();
|
|
return 0;
|
|
}
|
|
|
|
try {
|
|
con.drop_db(CmpDataBase);
|
|
}
|
|
catch (const mysqlpp::Exception& ) {
|
|
//Don't care if we fail.
|
|
}
|
|
|
|
try {
|
|
Query query = con.query();
|
|
query << "DELETE FROM oEvent WHERE NameId=" << quote << CmpDataBase;
|
|
query.execute();
|
|
}
|
|
catch (const mysqlpp::Exception& ) {
|
|
//Don't care if we fail.
|
|
}
|
|
|
|
CmpDataBase.clear();
|
|
|
|
errorMessage.clear();
|
|
|
|
try {
|
|
con.close();
|
|
}
|
|
catch (const mysqlpp::Exception&) {
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void MeosSQL::importLists(oEvent *oe, const char *bf) {
|
|
xmlparser xml;
|
|
xml.readMemory(bf, 0);
|
|
oe->listContainer->clearExternal();
|
|
oe->listContainer->load(MetaListContainer::ExternalList, xml.getObject("Lists"), false);
|
|
}
|
|
|
|
void MeosSQL::encodeLists(const oEvent *oe, string &listEnc) const {
|
|
xmlparser parser;
|
|
parser.openMemoryOutput(true);
|
|
parser.startTag("Lists");
|
|
oe->listContainer->save(MetaListContainer::ExternalList, parser, oe);
|
|
parser.endTag();
|
|
parser.getMemoryOutput(listEnc);
|
|
}
|
|
|
|
void MeosSQL::clearReadTimes() {
|
|
readTimes.clear();
|
|
}
|
|
|
|
int getTypeId(const oBase &ob)
|
|
{
|
|
if (typeid(ob)==typeid(oRunner)){
|
|
return 1;
|
|
}
|
|
else if (typeid(ob)==typeid(oClass)){
|
|
return 2;
|
|
}
|
|
else if (typeid(ob)==typeid(oCourse)){
|
|
return 3;
|
|
}
|
|
else if (typeid(ob)==typeid(oControl)){
|
|
return 4;
|
|
}
|
|
else if (typeid(ob)==typeid(oClub)){
|
|
return 5;
|
|
}
|
|
else if (typeid(ob)==typeid(oCard)){
|
|
return 6;
|
|
}
|
|
else if (typeid(ob)==typeid(oFreePunch)){
|
|
return 7;
|
|
}
|
|
else if (typeid(ob)==typeid(oTeam)){
|
|
return 8;
|
|
}
|
|
else if (typeid(ob)==typeid(oEvent)){
|
|
return 9;
|
|
}
|
|
return -1;
|
|
}
|
|
static int skipped = 0, notskipped = 0, readent = 0;
|
|
|
|
void MeosSQL::synchronized(const oBase &entity) {
|
|
int id = getTypeId(entity);
|
|
readTimes[make_pair(id, entity.getId())] = GetTickCount();
|
|
readent++;
|
|
if (readent % 100 == 99)
|
|
OutputDebugStringA("Read 100 entities\n");
|
|
}
|
|
|
|
bool MeosSQL::skipSynchronize(const oBase &entity) const {
|
|
int id = getTypeId(entity);
|
|
map<pair<int, int>, DWORD>::const_iterator res = readTimes.find(make_pair(id, entity.getId()));
|
|
|
|
if (res != readTimes.end()) {
|
|
DWORD t = GetTickCount();
|
|
if (t > res->second && (t - res->second) < 1000) {
|
|
skipped++;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
notskipped++;
|
|
return false;
|
|
}
|
|
namespace {
|
|
int encode(oListId id) {
|
|
return int(id);
|
|
}
|
|
}
|
|
|
|
int MeosSQL::getModifiedMask(oEvent &oe) {
|
|
try {
|
|
Query query = con.query();
|
|
int res = 0;
|
|
Result store_res = query.store("SELECT * FROM oCounter");
|
|
if (store_res.num_rows()>0) {
|
|
Row r = store_res.at(0);
|
|
int ctrl = r["oControl"];
|
|
int crs = r["oCourse"];
|
|
int cls = r["oClass"];
|
|
int card = r["oCard"];
|
|
int club = r["oClub"];
|
|
int punch = r["oPunch"];
|
|
int runner = r["oRunner"];
|
|
int t = r["oTeam"];
|
|
int e = r["oEvent"];
|
|
|
|
if (ctrl > oe.sqlCounterControls)
|
|
res |= encode(oListId::oLControlId);
|
|
if (crs > oe.sqlCounterCourses)
|
|
res |= encode(oListId::oLCourseId);
|
|
if (cls > oe.sqlCounterClasses)
|
|
res |= encode(oListId::oLClassId);
|
|
if (card > oe.sqlCounterCards)
|
|
res |= encode(oListId::oLCardId);
|
|
if (club > oe.sqlCounterClubs)
|
|
res |= encode(oListId::oLClubId);
|
|
if (punch > oe.sqlCounterPunches)
|
|
res |= encode(oListId::oLPunchId);
|
|
if (runner > oe.sqlCounterRunners)
|
|
res |= encode(oListId::oLRunnerId);
|
|
if (t > oe.sqlCounterTeams)
|
|
res |= encode(oListId::oLTeamId);
|
|
if (e > oe.counter)
|
|
res |= encode(oListId::oLEventId);
|
|
|
|
return res;
|
|
}
|
|
}
|
|
catch(...) {
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void MeosSQL::addedFromDatabase(oBase *object) {
|
|
assert(object);
|
|
if (object && !object->existInDB()) {
|
|
missingObjects.push_back(object);
|
|
}
|
|
}
|
|
|
|
void MeosSQL::processMissingObjects() {
|
|
if (!missingObjects.empty()) {
|
|
auto cpyMissing = missingObjects;
|
|
missingObjects.clear();
|
|
for (oBase *obj : cpyMissing) {
|
|
obj->changed = true;
|
|
syncRead(true, obj);
|
|
obj->changed = false;
|
|
assert(obj->existInDB());
|
|
{
|
|
oRunner *r = dynamic_cast<oRunner *>(obj);
|
|
if (r && r->getName().empty()) {
|
|
r->setName(L"@AutoCorrection", false);
|
|
syncUpdate(r, false);
|
|
}
|
|
}
|
|
{
|
|
oTeam *t = dynamic_cast<oTeam *>(obj);
|
|
if (t && t->getName().empty()) {
|
|
t->setName(L"@AutoCorrection", false);
|
|
syncUpdate(t, false);
|
|
}
|
|
}
|
|
{
|
|
oClass *cls = dynamic_cast<oClass *>(obj);
|
|
if (cls && cls->getName().empty()) {
|
|
cls->setName(L"@AutoCorrection");
|
|
syncUpdate(cls, false);
|
|
}
|
|
}
|
|
{
|
|
oCourse *crs = dynamic_cast<oCourse *>(obj);
|
|
if (crs && crs->getName().empty()) {
|
|
crs->setName(L"@AutoCorrection");
|
|
syncUpdate(crs, false);
|
|
}
|
|
}
|
|
{
|
|
oClub *clb = dynamic_cast<oClub *>(obj);
|
|
if (clb && clb->getName().empty()) {
|
|
clb->setName(L"@AutoCorrection");
|
|
syncUpdate(clb, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
missingObjects.clear();
|
|
}
|
|
|
|
OpFailStatus MeosSQL::syncRead(bool forceRead, oBase *obj) {
|
|
OpFailStatus ret = OpFailStatus::opStatusFail;
|
|
|
|
if (typeid(*obj) == typeid(oRunner)) {
|
|
ret = syncRead(forceRead, (oRunner *)obj);
|
|
}
|
|
else if (typeid(*obj) == typeid(oClass)) {
|
|
ret = syncRead(forceRead, (oClass *)obj);
|
|
}
|
|
else if (typeid(*obj) == typeid(oCourse)) {
|
|
ret = syncRead(forceRead, (oCourse *)obj);
|
|
}
|
|
else if (typeid(*obj) == typeid(oControl)) {
|
|
ret = syncRead(forceRead, (oControl *)obj);
|
|
}
|
|
else if (typeid(*obj) == typeid(oClub)) {
|
|
ret = syncRead(forceRead, (oClub *)obj);
|
|
}
|
|
else if (typeid(*obj) == typeid(oCard)) {
|
|
ret = syncRead(forceRead, (oCard *)obj);
|
|
}
|
|
else if (typeid(*obj) == typeid(oFreePunch)) {
|
|
ret = syncRead(forceRead, (oFreePunch *)obj, true);
|
|
}
|
|
else if (typeid(*obj) == typeid(oTeam)) {
|
|
ret = syncRead(forceRead, (oTeam *)obj);
|
|
}
|
|
else if (typeid(*obj) == typeid(oEvent)) {
|
|
ret = SyncRead((oEvent *)obj);
|
|
}
|
|
else
|
|
throw std::exception("Database error");
|
|
|
|
processMissingObjects();
|
|
|
|
return ret;
|
|
}
|