17 #include <mysql/mysql.h>
30 cout <<
"Usage : " << pname <<
" "
31 "-d {database name(s)} -r {remote server to read from} "
32 "-U {username on read server} -P {pasword on read server} "
33 "-w {local server to write to} -u {username on write server} "
34 "-p {password on write server} [-o {port on read server} "
35 "-i {port on write server}]\n"
44 template<
typename T,
typename U>
58 main(
int argc,
char* argv[])
67 string localPortString =
"3306";
68 string remotePortString =
"3306";
73 for (
int i = 1; i < argc && *argv[i] ==
'-'; ++i)
74 for (
const char*
p = &argv[i][1]; *
p !=
'\0'; ++
p)
78 databaseName = argv[++i];
82 dbserverMain = argv[++i];
86 usernameMain = argv[++i];
90 passwordMain = argv[++i];
94 dbserverLocal = argv[++i];
98 usernameLocal = argv[++i];
102 passwordLocal = argv[++i];
106 remotePortString = argv[++i];
110 localPortString = argv[++i];
116 if (databaseName.empty() ||
117 dbserverMain.empty() ||
118 usernameMain.empty() ||
119 passwordMain.empty() ||
120 dbserverLocal.empty() ||
121 usernameLocal.empty() ||
122 passwordLocal.empty()) {
123 cout <<
"Required argument is missing." << endl;
128 istringstream databaseNameStream(databaseName);
129 const vector<string> databaseNames{istream_iterator<string>(databaseNameStream),
130 istream_iterator<string>()};
137 "=========================================================================================="
140 const time_t curTime = time(
nullptr);
141 cout << ctime(&curTime) <<
"\n"
142 "Synchronizing databases from server "
143 << dbserverMain <<
" "
144 "(port " << remotePortString <<
") "
145 "to server " << dbserverLocal <<
" "
146 "(port " << localPortString <<
")\n"
147 "The following database(s) will be affected:\n";
149 for (
const auto& db : databaseNames)
157 MYSQL* sqlMain =
nullptr;
158 MYSQL* sqlLocal =
nullptr;
160 for (
const auto& db : databaseNames) {
162 cout <<
"------------------- Updating database: " << db <<
" -----------------------" << endl;
164 sqlMain = mysql_init(
nullptr);
165 sqlLocal = mysql_init(
nullptr);
167 const int localPort = LexicalCast<int>(localPortString);
168 const int remotePort = LexicalCast<int>(remotePortString);
170 if (!mysql_real_connect(sqlMain, dbserverMain.c_str(),
171 usernameMain.c_str(), passwordMain.c_str(),
172 db.c_str(), remotePort, 0, 0)) {
173 cerr << sqlMain <<
' ' << dbserverMain.c_str() <<
' '
174 << usernameMain.c_str() <<
' '
175 << passwordMain.c_str() <<
' '
177 << remotePort <<
"\n"
178 "Failed to connect to remote database: "
179 "Error: " << mysql_error(sqlMain) << endl;
180 mysql_close(sqlMain);
181 mysql_close(sqlLocal);
183 }
else if (!mysql_real_connect(sqlLocal, dbserverLocal.c_str(),
184 usernameLocal.c_str(), passwordLocal.c_str(),
185 db.c_str(), localPort, 0, 0)) {
186 cerr <<
"Failed to connect to local database: "
187 "Error: " << mysql_error(sqlLocal) << endl;
188 mysql_close(sqlMain);
189 mysql_close(sqlLocal);
198 query <<
"SHOW TABLES";
199 if (mysql_query(sqlMain, query.str().c_str())) {
201 cerr <<
"**Error: Could not get a list of tables from Main server: " << dbserverMain <<
"\n"
202 " Query failed : " << query.str().c_str() << endl;
208 MYSQL_RES* databaseRes =
nullptr;
209 databaseRes = mysql_store_result(sqlMain);
210 const int numTables = mysql_num_rows(databaseRes);
213 cerr <<
"*Warning: database contains no tables!" << endl;
215 for (
int i = 0; i < numTables; ++i) {
220 row = mysql_fetch_row(databaseRes);
221 const string tableName = row[0];
224 query <<
"SELECT " << tableName <<
"_id FROM "
225 << tableName <<
" ORDER BY " << tableName <<
"_id DESC LIMIT 1";
227 if (mysql_query(sqlLocal, query.str().c_str())) {
229 cerr <<
"** Error trying to determine the last modified time for " << tableName <<
"\n"
230 " Query failed: " << query.str().c_str() << endl;
234 MYSQL_RES*
const localPrimaryKeyRes = mysql_store_result(sqlLocal);
235 const int numLocalRecords = mysql_num_rows(localPrimaryKeyRes);
236 int lastLocalPrimaryKey = 0;
237 int lastPrimaryKey = 0;
239 if (numLocalRecords > 0) {
240 const MYSQL_ROW localPrimaryKeyRow = mysql_fetch_row(localPrimaryKeyRes);
241 lastLocalPrimaryKey = LexicalCast<int>(localPrimaryKeyRow[0]);
242 cout <<
"Updating table: " << tableName <<
". Last primary key for local table = " << lastLocalPrimaryKey << endl;
244 lastLocalPrimaryKey = 0;
245 cout <<
"No records in local table: " << tableName <<
". "
246 "Attempting to fill entire local table." << endl;
253 query <<
"SELECT " << tableName <<
"_id FROM "
257 <<
" ORDER BY " << tableName <<
"_id DESC LIMIT 1";
259 if (!mysql_query(sqlMain, query.str().c_str())) {
261 MYSQL_RES*
const tableRes = mysql_store_result(sqlMain);
262 const int numRows = mysql_num_rows(tableRes);
265 MYSQL_ROW firstKeyRow = mysql_fetch_row(tableRes);
266 lastPrimaryKey = LexicalCast<int>(firstKeyRow[0]);
269 if (numRows < numLocalRecords || lastPrimaryKey < lastLocalPrimaryKey) {
270 cout <<
"MAJOR BIG-TIME ERROR!\n"
271 "The number of records in the local table '" << tableName <<
"' "
272 "(" << numLocalRecords <<
")\n and the number of records in the master table "
273 "(" << numRows <<
") are not equal\n"
274 "or the last key in the local table (" << lastLocalPrimaryKey <<
") "
275 "and the last key in the master table (" << lastPrimaryKey <<
") "
277 "Has someone been writing to the local database?" << endl;
280 mysql_free_result(tableRes);
283 const int limit = 1000;
285 ostringstream insertString;
290 query <<
"SELECT * FROM " << tableName <<
" WHERE " << tableName <<
"_id > " << lastLocalPrimaryKey <<
" AND "
291 << tableName <<
"_id <= " << lastLocalPrimaryKey + limit <<
';';
295 if (mysql_query(sqlMain, query.str().c_str())) {
297 cerr <<
"**Error Could not get records from Main database\n"
298 " Query failed: " << query.str().c_str() << endl;
299 mysql_close(sqlMain);
300 mysql_close(sqlLocal);
305 MYSQL_RES*
const tableRes = mysql_store_result(sqlMain);
306 numRows = mysql_num_rows(tableRes);
310 cout <<
"No new records. Nothing is being written in the database." << endl;
314 const int numFields = mysql_num_fields(tableRes);
317 cerr <<
"*Warning: zero fields!" << endl;
319 cout <<
"*********** Writing " << ++numWrites <<
": " << numRows
320 <<
" new records in " << tableName
321 <<
" (limit per query = " << limit <<
") " << endl;
323 insertString.str(
"");
324 insertString <<
"INSERT INTO " << tableName <<
" VALUES ";
326 for (
int j = 0; j < numRows; ++j) {
328 row2 = mysql_fetch_row(tableRes);
329 insertString <<
"( ";
331 for (
int k = 0; k < numFields; ++k) {
334 insertString <<
'"' << row2[k] <<
'"';
336 insertString <<
"NULL";
338 if (k < (numFields - 1))
339 insertString <<
", ";
343 if (j < (numRows - 1))
344 insertString <<
" ), ";
346 insertString <<
" );";
350 if (mysql_query(sqlLocal, insertString.str().c_str()))
351 cerr <<
"**ERROR. Insert query to the database failed.\n"
352 "query : " << insertString.str() <<
"\n"
353 "reported error: " << mysql_error(sqlLocal) << endl;
357 mysql_free_result(tableRes);
361 if (lastPrimaryKey <= lastLocalPrimaryKey)
364 lastLocalPrimaryKey += limit;
371 mysql_close(sqlMain);
372 mysql_close(sqlLocal);
T LexicalCast(const U &x)
int main(int argc, char *argv[])
void DisplayUsage(const char *const pname)