function ResizeColumn moved to libnxdb from nxdbmgr (as DBResizeColumn)
[public/netxms.git] / src / server / tools / nxdbmgr / nxdbmgr.cpp
CommitLineData
2589cc10 1/*
5039dede 2** nxdbmgr - NetXMS database manager
74724c20 3** Copyright (C) 2004-2017 Victor Kirhenshtein
5039dede
AK
4**
5** This program is free software; you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation; either version 2 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program; if not, write to the Free Software
17** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18**
19** File: nxdbmgr.cpp
20**
21**/
22
23#include "nxdbmgr.h"
e6c91aac 24#include <nxconfig.h>
5039dede
AK
25
26#ifdef _WIN32
27#include <conio.h>
28#endif
29
69bb7f47
VK
30/**
31 * Global variables
32 */
5039dede
AK
33DB_HANDLE g_hCoreDB;
34BOOL g_bIgnoreErrors = FALSE;
35BOOL g_bTrace = FALSE;
a4743a0f 36bool g_isGuiMode = false;
0d9b58ef 37bool g_checkData = false;
daf3c104 38bool g_checkDataTablesOnly = false;
ffaafdce
VK
39bool g_dataOnlyMigration = false;
40bool g_skipDataMigration = false;
09f1c9bf
VK
41bool g_skipDataSchemaMigration = false;
42int g_migrationTxnSize = 4096;
2a964810 43int g_dbSyntax;
5039dede 44const TCHAR *g_pszTableSuffix = _T("");
2589cc10 45const TCHAR *g_pszSqlType[6][3] =
5039dede 46{
49e749f3 47 { _T("longtext"), _T("text"), _T("bigint") }, // MySQL
50da2d20
VK
48 { _T("text"), _T("varchar(4000)"), _T("bigint") }, // PostgreSQL
49 { _T("text"), _T("varchar(4000)"), _T("bigint") }, // Microsoft SQL
50 { _T("clob"), _T("varchar(4000)"), _T("number(20)") }, // Oracle
51 { _T("varchar"), _T("varchar(4000)"), _T("number(20)") }, // SQLite
52 { _T("long varchar"), _T("varchar(4000)"), _T("bigint") } // DB/2
5039dede
AK
53};
54
69bb7f47
VK
55/**
56 * Static data
57 */
29dc8792 58static char m_szCodePage[MAX_PATH] = ICONV_DEFAULT_CODEPAGE;
b8c1ec69
VK
59static TCHAR s_dbDriver[MAX_PATH] = _T("");
60static TCHAR s_dbDrvParams[MAX_PATH] = _T("");
61static TCHAR s_dbServer[MAX_PATH] = _T("127.0.0.1");
62static TCHAR s_dbLogin[MAX_DB_LOGIN] = _T("netxms");
07ca7d19 63static TCHAR s_dbPassword[MAX_PASSWORD] = _T("");
b8c1ec69 64static TCHAR s_dbName[MAX_DB_NAME] = _T("netxms_db");
f3c30cf5 65static TCHAR s_dbSchema[MAX_DB_NAME] = _T("");
5039dede
AK
66static NX_CFG_TEMPLATE m_cfgTemplate[] =
67{
dda7c270 68 { _T("CodePage"), CT_MB_STRING, 0, 0, MAX_PATH, 0, m_szCodePage },
08b214c6
VK
69 { _T("DBDriver"), CT_STRING, 0, 0, MAX_PATH, 0, s_dbDriver },
70 { _T("DBDrvParams"), CT_STRING, 0, 0, MAX_PATH, 0, s_dbDrvParams },
08b214c6
VK
71 { _T("DBLogin"), CT_STRING, 0, 0, MAX_DB_LOGIN, 0, s_dbLogin },
72 { _T("DBName"), CT_STRING, 0, 0, MAX_DB_NAME, 0, s_dbName },
07ca7d19 73 { _T("DBPassword"), CT_STRING, 0, 0, MAX_PASSWORD, 0, s_dbPassword },
74 { _T("DBEncryptedPassword"), CT_STRING, 0, 0, MAX_PASSWORD, 0, s_dbPassword },
d010c10e 75 { _T("DBSchema"), CT_STRING, 0, 0, MAX_DB_NAME, 0, s_dbSchema },
08b214c6 76 { _T("DBServer"), CT_STRING, 0, 0, MAX_PATH, 0, s_dbServer },
08b214c6 77 { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL }
5039dede
AK
78};
79static BOOL m_bForce = FALSE;
63604cda 80static DB_DRIVER s_driver = NULL;
5039dede 81
69bb7f47
VK
82/**
83 * Show query if trace mode is ON
84 */
5039dede
AK
85void ShowQuery(const TCHAR *pszQuery)
86{
f669df41 87 WriteToTerminalEx(_T("\x1b[1m>>> \x1b[32;1m%s\x1b[0m\n"), pszQuery);
5039dede
AK
88}
89
69bb7f47
VK
90/**
91 * Get Yes or No answer from keyboard
92 */
a4743a0f 93bool GetYesNo(const TCHAR *format, ...)
5039dede 94{
a4743a0f 95 va_list args;
5039dede 96
a4743a0f
VK
97 if (g_isGuiMode)
98 {
99 if (m_bForce)
100 return true;
101
102 TCHAR message[4096];
103 va_start(args, format);
104 _vsntprintf(message, 4096, format, args);
105 va_end(args);
106
107#ifdef _WIN32
108 return MessageBox(NULL, message, _T("NetXMS Database Manager"), MB_YESNO | MB_ICONQUESTION) == IDYES;
109#else
110 return false;
111#endif
112 }
113 else
114 {
115 va_start(args, format);
116 _vtprintf(format, args);
117 va_end(args);
9f24efb3 118 _tprintf(_T(" (Y/N) "));
a4743a0f
VK
119
120 if (m_bForce)
121 {
9f24efb3 122 _tprintf(_T("Y\n"));
a4743a0f
VK
123 return true;
124 }
125 else
126 {
127#ifdef _WIN32
128 int ch;
129
130 while(1)
131 {
132 ch = _getch();
133 if ((ch == 'y') || (ch == 'Y'))
134 {
9f24efb3 135 _tprintf(_T("Y\n"));
a4743a0f
VK
136 return true;
137 }
138 if ((ch == 'n') || (ch == 'N'))
139 {
9f24efb3 140 _tprintf(_T("N\n"));
a4743a0f
VK
141 return false;
142 }
143 }
5039dede 144#else
a4743a0f 145 TCHAR szBuffer[16];
5039dede 146
a4743a0f
VK
147 fflush(stdout);
148 _fgetts(szBuffer, 16, stdin);
149 StrStrip(szBuffer);
150 return ((szBuffer[0] == 'y') || (szBuffer[0] == 'Y'));
5039dede 151#endif
a4743a0f
VK
152 }
153 }
5039dede
AK
154}
155
69bb7f47
VK
156/**
157 * Execute SQL SELECT query and print error message on screen if query failed
158 */
5039dede
AK
159DB_RESULT SQLSelect(const TCHAR *pszQuery)
160{
161 DB_RESULT hResult;
162 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
163
164 if (g_bTrace)
165 ShowQuery(pszQuery);
166
167 hResult = DBSelectEx(g_hCoreDB, pszQuery, errorText);
168 if (hResult == NULL)
f669df41 169 WriteToTerminalEx(_T("SQL query failed (%s):\n\x1b[33;1m%s\x1b[0m\n"), errorText, pszQuery);
4c4c9b03
VK
170 return hResult;
171}
172
69bb7f47 173/**
f17cf019 174 * Execute SQL SELECT query via DBSelectUnbuffered and print error message on screen if query failed
69bb7f47 175 */
f17cf019 176DB_UNBUFFERED_RESULT SQLSelectUnbuffered(const TCHAR *pszQuery)
4c4c9b03 177{
4c4c9b03
VK
178 if (g_bTrace)
179 ShowQuery(pszQuery);
180
f17cf019
VK
181 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
182 DB_UNBUFFERED_RESULT hResult = DBSelectUnbufferedEx(g_hCoreDB, pszQuery, errorText);
4c4c9b03 183 if (hResult == NULL)
f669df41 184 WriteToTerminalEx(_T("SQL query failed (%s):\n\x1b[33;1m%s\x1b[0m\n"), errorText, pszQuery);
5039dede
AK
185 return hResult;
186}
187
84ee0f9c
VK
188/**
189 * Execute prepared statement and print error message on screen if query failed
190 */
c85c8ef2 191bool SQLExecute(DB_STATEMENT hStmt)
84ee0f9c
VK
192{
193 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
194
195 if (g_bTrace)
196 ShowQuery(DBGetStatementSource(hStmt));
197
c85c8ef2 198 bool result = DBExecuteEx(hStmt, errorText);
84ee0f9c
VK
199 if (!result)
200 WriteToTerminalEx(_T("SQL query failed (%s):\n\x1b[33;1m%s\x1b[0m\n"), errorText, DBGetStatementSource(hStmt));
201 return result;
202}
203
69bb7f47
VK
204/**
205 * Execute SQL query and print error message on screen if query failed
206 */
c85c8ef2 207bool SQLQuery(const TCHAR *pszQuery)
5039dede 208{
5039dede 209 if (*pszQuery == 0)
c85c8ef2 210 return true;
5039dede 211
701726bc
VK
212 String query(pszQuery);
213
2a964810
VK
214 query.replace(_T("$SQL:TEXT"), g_pszSqlType[g_dbSyntax][SQL_TYPE_TEXT]);
215 query.replace(_T("$SQL:TXT4K"), g_pszSqlType[g_dbSyntax][SQL_TYPE_TEXT4K]);
216 query.replace(_T("$SQL:INT64"), g_pszSqlType[g_dbSyntax][SQL_TYPE_INT64]);
701726bc 217
5039dede 218 if (g_bTrace)
701726bc 219 ShowQuery(query);
5039dede 220
c85c8ef2
VK
221 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
222 bool success = DBQueryEx(g_hCoreDB, (const TCHAR *)query, errorText);
223 if (!success)
e10e890e 224 WriteToTerminalEx(_T("SQL query failed (%s):\n\x1b[33;1m%s\x1b[0m\n"), errorText, (const TCHAR *)query);
c85c8ef2 225 return success;
5039dede
AK
226}
227
69bb7f47
VK
228/**
229 * Execute SQL batch
230 */
c85c8ef2 231bool SQLBatch(const TCHAR *pszBatch)
5039dede 232{
8ec9bcc9 233 String batch(pszBatch);
5039dede
AK
234 TCHAR *pszBuffer, *pszQuery, *ptr;
235 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
c85c8ef2 236 bool success = true;
8ec9bcc9 237 TCHAR table[128], column[128];
5039dede 238
2a964810
VK
239 batch.replace(_T("$SQL:TEXT"), g_pszSqlType[g_dbSyntax][SQL_TYPE_TEXT]);
240 batch.replace(_T("$SQL:TXT4K"), g_pszSqlType[g_dbSyntax][SQL_TYPE_TEXT4K]);
241 batch.replace(_T("$SQL:INT64"), g_pszSqlType[g_dbSyntax][SQL_TYPE_INT64]);
5039dede 242
7618e362 243 pszQuery = pszBuffer = batch.getBuffer();
df5a4953 244 while(true)
5039dede
AK
245 {
246 ptr = _tcschr(pszQuery, _T('\n'));
247 if (ptr != NULL)
248 *ptr = 0;
249 if (!_tcscmp(pszQuery, _T("<END>")))
250 break;
251
8ec9bcc9
AK
252 if (_stscanf(pszQuery, _T("ALTER TABLE %128s DROP COLUMN %128s"), table, column) == 2)
253 {
254 if (!SQLDropColumn(table, column))
255 {
dc215562 256 WriteToTerminalEx(_T("Cannot drop column \x1b[37;1m%s.%s\x1b[0m\n"), table, column);
8ec9bcc9
AK
257 if (!g_bIgnoreErrors)
258 {
c85c8ef2 259 success = false;
8ec9bcc9
AK
260 break;
261 }
262 }
263 }
264 else
265 {
af0d0e51
VK
266 if (g_bTrace)
267 ShowQuery(pszQuery);
268
8ec9bcc9
AK
269 if (!DBQueryEx(g_hCoreDB, pszQuery, errorText))
270 {
271 WriteToTerminalEx(_T("SQL query failed (%s):\n\x1b[33;1m%s\x1b[0m\n"), errorText, pszQuery);
272 if (!g_bIgnoreErrors)
273 {
c85c8ef2 274 success = false;
8ec9bcc9
AK
275 break;
276 }
277 }
278 }
279
df5a4953
VK
280 if (ptr == NULL)
281 break;
5039dede
AK
282 ptr++;
283 pszQuery = ptr;
284 }
c85c8ef2 285 return success;
5039dede
AK
286}
287
69bb7f47
VK
288/**
289 * Drop column from the table
290 */
c85c8ef2 291bool SQLDropColumn(const TCHAR *table, const TCHAR *column)
8ec9bcc9
AK
292{
293 TCHAR query[1024];
294 DB_RESULT hResult;
c85c8ef2 295 bool success = false;
8ec9bcc9 296
2a964810 297 if (g_dbSyntax != DB_SYNTAX_SQLITE)
8ec9bcc9
AK
298 {
299 _sntprintf(query, 1024, _T("ALTER TABLE %s DROP COLUMN %s"), table, column);
300 success = SQLQuery(query);
c849da55
VK
301 if (g_dbSyntax == DB_SYNTAX_DB2)
302 {
303 _sntprintf(query, 1024, _T("CALL Sysproc.admin_cmd('REORG TABLE %s')"), table);
304 success = SQLQuery(query);
305 }
8ec9bcc9
AK
306 }
307 else
308 {
309 _sntprintf(query, 1024, _T("PRAGMA TABLE_INFO('%s')"), table);
310 hResult = SQLSelect(query);
311 if (hResult != NULL)
312 {
313 int rows = DBGetNumRows(hResult);
314 const int blen = 2048;
315 TCHAR buffer[blen];
316 // Intermediate buffers for SQLs
2589cc10 317 TCHAR columnList[1024], createList[1024];
8ec9bcc9
AK
318 // TABLE_INFO() columns
319 TCHAR tabColName[128], tabColType[64], tabColNull[10], tabColDefault[128];
320 columnList[0] = createList[0] = _T('\0');
321 for (int i = 0; i < rows; i++)
322 {
323 DBGetField(hResult, i, 1, tabColName, 128);
324 DBGetField(hResult, i, 2, tabColType, 64);
325 DBGetField(hResult, i, 3, tabColNull, 10);
326 DBGetField(hResult, i, 4, tabColDefault, 128);
327 if (_tcsnicmp(tabColName, column, 128))
328 {
329 _tcscat(columnList, tabColName);
330 if (columnList[0] != _T('\0'))
331 _tcscat(columnList, _T(","));
332 _tcscat(createList, tabColName);
333 _tcscat(createList, tabColType);
334 if (tabColDefault[0] != _T('\0'))
335 {
336 _tcscat(createList, _T("DEFAULT "));
337 _tcscat(createList, tabColDefault);
338 }
339 if (tabColNull[0] == _T('1'))
340 _tcscat(createList, _T(" NOT NULL"));
341 _tcscat(createList, _T(","));
342 }
343 }
344 DBFreeResult(hResult);
345 if (rows > 0)
346 {
417ab143 347 int cllen = (int)_tcslen(columnList);
8ec9bcc9
AK
348 if (cllen > 0 && columnList[cllen - 1] == _T(','))
349 columnList[cllen - 1] = _T('\0');
350 // TODO: figure out if SQLite transactions will work here
351 _sntprintf(buffer, blen, _T("CREATE TABLE %s__backup__ (%s)"), table, columnList);
8ec9bcc9 352 CHK_EXEC(SQLQuery(buffer));
2589cc10 353 _sntprintf(buffer, blen, _T("INSERT INTO %s__backup__ (%s) SELECT %s FROM %s"),
8ec9bcc9 354 table, columnList, columnList, table);
8ec9bcc9
AK
355 CHK_EXEC(SQLQuery(buffer));
356 _sntprintf(buffer, blen, _T("DROP TABLE %s"), table);
8ec9bcc9
AK
357 CHK_EXEC(SQLQuery(buffer));
358 _sntprintf(buffer, blen, _T("ALTER TABLE %s__backup__ RENAME to %s"), table, table);
8ec9bcc9 359 CHK_EXEC(SQLQuery(buffer));
c85c8ef2 360 success = true;
8ec9bcc9
AK
361 }
362 }
363 }
364
365 // TODO: preserve indices and constraints??
366
367 return success;
368}
5039dede 369
a43a5e7d
VK
370/**
371 * Read string value from metadata table
372 */
3783d300
VK
373BOOL MetaDataReadStr(const TCHAR *pszVar, TCHAR *pszBuffer, int iBufSize, const TCHAR *pszDefault)
374{
375 DB_RESULT hResult;
376 TCHAR szQuery[256];
377 BOOL bSuccess = FALSE;
378
379 nx_strncpy(pszBuffer, pszDefault, iBufSize);
380 if (_tcslen(pszVar) > 127)
381 return FALSE;
382
383 _sntprintf(szQuery, 256, _T("SELECT var_value FROM metadata WHERE var_name='%s'"), pszVar);
384 hResult = SQLSelect(szQuery);
385 if (hResult == NULL)
386 return FALSE;
387
388 if (DBGetNumRows(hResult) > 0)
389 {
390 DBGetField(hResult, 0, 0, pszBuffer, iBufSize);
391 bSuccess = TRUE;
392 }
393
394 DBFreeResult(hResult);
395 return bSuccess;
396}
397
2f1bc68b
VK
398/**
399 * Read integer value from configuration table
400 */
401int MetaDataReadInt(const TCHAR *pszVar, int iDefault)
402{
403 TCHAR szBuffer[64];
404
405 if (MetaDataReadStr(pszVar, szBuffer, 64, _T("")))
406 return _tcstol(szBuffer, NULL, 0);
407 else
408 return iDefault;
409}
410
a43a5e7d
VK
411/**
412 * Read string value from configuration table
413 */
5039dede
AK
414BOOL ConfigReadStr(const TCHAR *pszVar, TCHAR *pszBuffer, int iBufSize, const TCHAR *pszDefault)
415{
416 DB_RESULT hResult;
a50beeec 417 TCHAR szQuery[256];
5039dede
AK
418 BOOL bSuccess = FALSE;
419
420 nx_strncpy(pszBuffer, pszDefault, iBufSize);
421 if (_tcslen(pszVar) > 127)
422 return FALSE;
423
a50beeec 424 _sntprintf(szQuery, 256, _T("SELECT var_value FROM config WHERE var_name='%s'"), pszVar);
5039dede 425 hResult = SQLSelect(szQuery);
3783d300 426 if (hResult == NULL)
5039dede
AK
427 return FALSE;
428
429 if (DBGetNumRows(hResult) > 0)
430 {
431 DBGetField(hResult, 0, 0, pszBuffer, iBufSize);
5039dede
AK
432 bSuccess = TRUE;
433 }
434
435 DBFreeResult(hResult);
436 return bSuccess;
437}
438
c59466d2
VK
439/**
440 * Read integer value from configuration table
441 */
5039dede
AK
442int ConfigReadInt(const TCHAR *pszVar, int iDefault)
443{
444 TCHAR szBuffer[64];
445
446 if (ConfigReadStr(pszVar, szBuffer, 64, _T("")))
447 return _tcstol(szBuffer, NULL, 0);
448 else
449 return iDefault;
450}
451
c59466d2
VK
452/**
453 * Read unsigned long value from configuration table
454 */
5039dede
AK
455DWORD ConfigReadULong(const TCHAR *pszVar, DWORD dwDefault)
456{
457 TCHAR szBuffer[64];
458
459 if (ConfigReadStr(pszVar, szBuffer, 64, _T("")))
460 return _tcstoul(szBuffer, NULL, 0);
461 else
462 return dwDefault;
463}
464
06b83321
VK
465/**
466 * Check if given record exists in database
467 */
468bool IsDatabaseRecordExist(const TCHAR *table, const TCHAR *idColumn, UINT32 id)
469{
470 bool exist = false;
471
472 TCHAR query[256];
473 _sntprintf(query, 256, _T("SELECT %s FROM %s WHERE %s=?"), idColumn, table, idColumn);
474
475 DB_STATEMENT hStmt = DBPrepare(g_hCoreDB, query);
476 if (hStmt != NULL)
477 {
478 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, id);
479 DB_RESULT hResult = DBSelectPrepared(hStmt);
480 if (hResult != NULL)
481 {
482 exist = (DBGetNumRows(hResult) > 0);
483 DBFreeResult(hResult);
484 }
485 DBFreeStatement(hStmt);
486 }
487 return exist;
488}
489
c59466d2
VK
490/**
491 * Check that database has correct schema version and is not locked
492 */
c85c8ef2 493bool ValidateDatabase()
1e558daf
VK
494{
495 DB_RESULT hResult;
496 LONG nVersion = 0;
bd802b08 497 BOOL bLocked = FALSE;
1e558daf
VK
498 TCHAR szLockStatus[MAX_DB_STRING], szLockInfo[MAX_DB_STRING];
499
500 // Get database format version
28f5b9a4 501 nVersion = DBGetSchemaVersion(g_hCoreDB);
1e558daf
VK
502 if (nVersion < DB_FORMAT_VERSION)
503 {
504 _tprintf(_T("Your database has format version %d, this tool is compiled for version %d.\nUse \"upgrade\" command to upgrade your database first.\n"),
505 nVersion, DB_FORMAT_VERSION);
c85c8ef2 506 return false;
1e558daf
VK
507 }
508 else if (nVersion > DB_FORMAT_VERSION)
509 {
9f24efb3
VK
510 _tprintf(_T("Your database has format version %d, this tool is compiled for version %d.\n")
511 _T("You need to upgrade your server before using this database.\n"),
512 nVersion, DB_FORMAT_VERSION);
c85c8ef2 513 return false;
1e558daf
VK
514 }
515
516 // Check if database is locked
517 hResult = DBSelect(g_hCoreDB, _T("SELECT var_value FROM config WHERE var_name='DBLockStatus'"));
518 if (hResult != NULL)
519 {
520 if (DBGetNumRows(hResult) > 0)
521 {
bf7f9d94 522 DBGetField(hResult, 0, 0, szLockStatus, MAX_DB_STRING);
1e558daf
VK
523 bLocked = _tcscmp(szLockStatus, _T("UNLOCKED"));
524 }
525 DBFreeResult(hResult);
526
527 if (bLocked)
528 {
529 hResult = DBSelect(g_hCoreDB, _T("SELECT var_value FROM config WHERE var_name='DBLockInfo'"));
530 if (hResult != NULL)
531 {
532 if (DBGetNumRows(hResult) > 0)
533 {
bf7f9d94 534 DBGetField(hResult, 0, 0, szLockInfo, MAX_DB_STRING);
1e558daf
VK
535 }
536 DBFreeResult(hResult);
537 }
538 }
539 }
540
541 if (bLocked)
542 {
543 _tprintf(_T("Database is locked by server %s [%s]\n"), szLockStatus, szLockInfo);
c85c8ef2 544 return false;
1e558daf
VK
545 }
546
c85c8ef2 547 return true;
1e558daf
VK
548}
549
63604cda
VK
550/**
551 * Open database connection
552 */
553DB_HANDLE ConnectToDatabase()
554{
555 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
556 DB_HANDLE hdb = DBConnect(s_driver, s_dbServer, s_dbName, s_dbLogin, s_dbPassword, s_dbSchema, errorText);
557 if (hdb == NULL)
558 {
559 _tprintf(_T("Unable to connect to database %s@%s as %s: %s\n"), s_dbName, s_dbServer, s_dbLogin, errorText);
560 }
561 return hdb;
562}
563
c59466d2
VK
564/**
565 * Startup
566 */
5039dede
AK
567int main(int argc, char *argv[])
568{
6ba36557 569 BOOL bStart = TRUE, bQuiet = FALSE;
c85c8ef2 570 bool replaceValue = true;
5039dede 571 int ch;
5039dede 572
d37e7f10 573 InitNetXMSProcess(true);
5039dede 574
1039d7ee
VK
575 TCHAR configFile[MAX_PATH] = _T("");
576
577 // Try to read config location
5039dede 578#ifdef _WIN32
38ffd4b0 579 HKEY hKey;
1039d7ee 580 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Server"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
5039dede 581 {
38ffd4b0 582 DWORD dwSize = MAX_PATH * sizeof(TCHAR);
1039d7ee 583 RegQueryValueEx(hKey, _T("ConfigFile"), NULL, NULL, (BYTE *)configFile, &dwSize);
5039dede
AK
584 RegCloseKey(hKey);
585 }
586#else
38ffd4b0
VK
587 const TCHAR *env = _tgetenv(_T("NETXMSD_CONFIG"));
588 if ((env != NULL) && (*env != 0))
1039d7ee
VK
589 nx_strncpy(configFile, env, MAX_PATH);
590#endif
591
592 // Search for config
593 if (configFile[0] == 0)
594 {
595#ifdef _WIN32
596 TCHAR path[MAX_PATH];
597 GetNetXMSDirectory(nxDirEtc, path);
598 _tcscat(path, _T("\\netxmsd.conf"));
599 if (_taccess(path, 4) == 0)
600 {
601 _tcscpy(configFile, path);
602 }
603 else
604 {
605 _tcscpy(configFile, _T("C:\\netxmsd.conf"));
606 }
607#else
608 const TCHAR *homeDir = _tgetenv(_T("NETXMS_HOME"));
609 if ((homeDir != NULL) && (*homeDir != 0))
610 {
611 TCHAR config[MAX_PATH];
612 _sntprintf(config, MAX_PATH, _T("%s/etc/netxmsd.conf"), homeDir);
613 if (_taccess(config, 4) == 0)
614 {
615 _tcscpy(configFile, config);
616 goto stop_search;
617 }
618 }
619 if (_taccess(PREFIX _T("/etc/netxmsd.conf"), 4) == 0)
620 {
621 _tcscpy(configFile, PREFIX _T("/etc/netxmsd.conf"));
622 }
623 else if (_taccess(_T("/usr/etc/netxmsd.conf"), 4) == 0)
624 {
625 _tcscpy(configFile, _T("/usr/etc/netxmsd.conf"));
626 }
627 else
628 {
629 _tcscpy(configFile, _T("/etc/netxmsd.conf"));
630 }
631stop_search:
632 ;
5039dede 633#endif
1039d7ee 634 }
5039dede
AK
635
636 // Parse command line
637 opterr = 1;
09f1c9bf 638 while((ch = getopt(argc, argv, "c:dDfGhIMNqsStT:vX")) != -1)
5039dede
AK
639 {
640 switch(ch)
641 {
642 case 'h': // Display help and exit
f62b7eab 643 _tprintf(_T("NetXMS Database Manager Version ") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING _T(" (") NETXMS_BUILD_TAG _T(")") IS_UNICODE_BUILD_STRING _T("\n\n"));
08b214c6
VK
644 _tprintf(_T("Usage: nxdbmgr [<options>] <command>\n")
645 _T("Valid commands are:\n")
be737a59
VK
646 _T(" batch <file> : Run SQL batch file\n")
647 _T(" check : Check database for errors\n")
648 _T(" check-data-tables : Check database for missing data tables\n")
649 _T(" export <file> : Export database to file\n")
650 _T(" get <name> : Get value of server configuration variable\n")
651 _T(" import <file> : Import database from file\n")
652 _T(" init <file> : Initialize database\n")
653 _T(" migrate <source> : Migrate database from given source\n")
654 _T(" reset-system-account : Unlock user \"system\" and reset it's password to default\n")
655 _T(" set <name> <value> : Set value of server configuration variable\n")
656 _T(" unlock : Forced database unlock\n")
657 _T(" upgrade : Upgrade database to new version\n")
08b214c6 658 _T("Valid options are:\n")
1039d7ee 659 _T(" -c <config> : Use alternate configuration file. Default is %s\n")
0d9b58ef 660 _T(" -d : Check collected data (may take very long time).\n")
ffaafdce 661 _T(" -D : Migrate only collected data.\n")
08b214c6 662 _T(" -f : Force repair - do not ask for confirmation.\n")
a4743a0f 663#ifdef _WIN32
08b214c6 664 _T(" -G : GUI mode.\n")
a4743a0f 665#endif
08b214c6
VK
666 _T(" -h : Display help and exit.\n")
667 _T(" -I : MySQL only - specify TYPE=InnoDB for new tables.\n")
668 _T(" -M : MySQL only - specify TYPE=MyISAM for new tables.\n")
8038d371 669 _T(" -N : Do not replace existing configuration value (\"set\" command only).\n")
a43a5e7d 670 _T(" -q : Quiet mode (don't show startup banner).\n")
4da7f408
VK
671 _T(" -s : Skip collected data during migration or export.\n")
672 _T(" -S : Skip collected data during migration or export and do not clear or create data tables.\n")
08b214c6 673 _T(" -t : Enable trace mode (show executed SQL queries).\n")
09f1c9bf 674 _T(" -T <recs> : Transaction size for migration.\n")
08b214c6 675 _T(" -v : Display version and exit.\n")
300c7968 676 _T(" -X : Ignore SQL errors when upgrading (USE WITH CAUTION!!!)\n")
1039d7ee 677 _T("\n"), configFile);
5039dede
AK
678 bStart = FALSE;
679 break;
680 case 'v': // Print version and exit
f62b7eab 681 _tprintf(_T("NetXMS Database Manager Version ") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING _T(" (") NETXMS_BUILD_TAG _T(")") IS_UNICODE_BUILD_STRING _T("\n\n"));
5039dede
AK
682 bStart = FALSE;
683 break;
684 case 'c':
08b214c6 685#ifdef UNICODE
1039d7ee
VK
686 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, configFile, MAX_PATH);
687 configFile[MAX_PATH - 1] = 0;
08b214c6 688#else
1039d7ee 689 nx_strncpy(configFile, optarg, MAX_PATH);
08b214c6 690#endif
5039dede 691 break;
0d9b58ef
VK
692 case 'd':
693 g_checkData = true;
694 break;
ffaafdce
VK
695 case 'D':
696 g_dataOnlyMigration = true;
697 break;
5039dede
AK
698 case 'f':
699 m_bForce = TRUE;
700 break;
a4743a0f
VK
701 case 'G':
702 g_isGuiMode = true;
703 break;
8038d371 704 case 'N':
c85c8ef2 705 replaceValue = false;
8038d371 706 break;
a43a5e7d
VK
707 case 'q':
708 bQuiet = TRUE;
709 break;
ffaafdce
VK
710 case 's':
711 g_skipDataMigration = true;
712 break;
09f1c9bf
VK
713 case 'S':
714 g_skipDataMigration = true;
715 g_skipDataSchemaMigration = true;
716 break;
5039dede
AK
717 case 't':
718 g_bTrace = TRUE;
719 break;
09f1c9bf
VK
720 case 'T':
721 g_migrationTxnSize = strtol(optarg, NULL, 0);
722 if ((g_migrationTxnSize < 1) || (g_migrationTxnSize > 100000))
723 {
724 _tprintf(_T("WARNING: invalid transaction size, reset to default"));
725 g_migrationTxnSize = 4096;
726 }
727 break;
5039dede
AK
728 case 'I':
729 g_pszTableSuffix = _T(" TYPE=InnoDB");
730 break;
731 case 'M':
732 g_pszTableSuffix = _T(" TYPE=MyISAM");
733 break;
734 case 'X':
735 g_bIgnoreErrors = TRUE;
736 break;
737 case '?':
738 bStart = FALSE;
739 break;
740 default:
741 break;
742 }
743 }
744
745 if (!bStart)
746 return 1;
747
a43a5e7d 748 if (!bQuiet)
f62b7eab 749 _tprintf(_T("NetXMS Database Manager Version ") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING _T(" (") NETXMS_BUILD_TAG _T(")") IS_UNICODE_BUILD_STRING _T("\n\n"));
a43a5e7d 750
5039dede
AK
751 // Check parameter correctness
752 if (argc - optind == 0)
753 {
9f24efb3 754 _tprintf(_T("Command missing. Type nxdbmgr -h for command line syntax.\n"));
5039dede
AK
755 return 1;
756 }
2589cc10 757 if (strcmp(argv[optind], "batch") &&
758 strcmp(argv[optind], "check") &&
759 strcmp(argv[optind], "check-data-tables") &&
760 strcmp(argv[optind], "export") &&
761 strcmp(argv[optind], "get") &&
762 strcmp(argv[optind], "import") &&
a43a5e7d 763 strcmp(argv[optind], "init") &&
84ee0f9c 764 strcmp(argv[optind], "migrate") &&
be737a59 765 strcmp(argv[optind], "reset-system-account") &&
a43a5e7d 766 strcmp(argv[optind], "set") &&
5039dede 767 strcmp(argv[optind], "unlock") &&
84ee0f9c 768 strcmp(argv[optind], "upgrade"))
5039dede 769 {
9f24efb3 770 _tprintf(_T("Invalid command \"%hs\". Type nxdbmgr -h for command line syntax.\n"), argv[optind]);
5039dede
AK
771 return 1;
772 }
84ee0f9c 773 if (((!strcmp(argv[optind], "init") || !strcmp(argv[optind], "batch") || !strcmp(argv[optind], "export") || !strcmp(argv[optind], "import") || !strcmp(argv[optind], "get") || !strcmp(argv[optind], "migrate")) && (argc - optind < 2)) ||
a43a5e7d 774 (!strcmp(argv[optind], "set") && (argc - optind < 3)))
5039dede 775 {
2467ed57 776 _tprintf(_T("Required command argument(s) missing\n"));
5039dede
AK
777 return 1;
778 }
779
780 // Read configuration file
e6c91aac 781 Config *config = new Config();
1039d7ee 782 if (!config->loadIniConfig(configFile, _T("server")) || !config->parseTemplate(_T("server"), m_cfgTemplate))
5039dede 783 {
9f24efb3 784 _tprintf(_T("Error loading configuration file\n"));
5039dede
AK
785 return 2;
786 }
e6c91aac
VK
787 delete config;
788
d96bd4c7 789 // Decrypt password
07ca7d19 790 DecryptPassword(s_dbLogin, s_dbPassword, s_dbPassword, MAX_PASSWORD);
d96bd4c7 791
5039dede 792#ifndef _WIN32
35f836fe 793 SetDefaultCodepage(m_szCodePage);
5039dede
AK
794#endif
795
796 // Connect to database
b8c1ec69 797 if (!DBInit(0, 0))
5039dede 798 {
9f24efb3 799 _tprintf(_T("Unable to initialize database library\n"));
5039dede
AK
800 return 3;
801 }
802
63604cda
VK
803 s_driver = DBLoadDriver(s_dbDriver, s_dbDrvParams, false, NULL, NULL);
804 if (s_driver == NULL)
b8c1ec69
VK
805 {
806 _tprintf(_T("Unable to load and initialize database driver \"%s\"\n"), s_dbDriver);
807 return 3;
808 }
809
63604cda 810 g_hCoreDB = ConnectToDatabase();
5039dede
AK
811 if (g_hCoreDB == NULL)
812 {
63604cda 813 DBUnloadDriver(s_driver);
5039dede
AK
814 return 4;
815 }
816
817 if (!strcmp(argv[optind], "init"))
818 {
819 InitDatabase(argv[optind + 1]);
820 }
821 else
822 {
823 // Get database syntax
2a964810
VK
824 g_dbSyntax = DBGetSyntax(g_hCoreDB);
825 if (g_dbSyntax == DB_SYNTAX_UNKNOWN)
28f5b9a4 826 {
5039dede
AK
827 _tprintf(_T("Unable to determine database syntax\n"));
828 DBDisconnect(g_hCoreDB);
63604cda 829 DBUnloadDriver(s_driver);
5039dede
AK
830 return 5;
831 }
832
5039dede
AK
833 // Do requested operation
834 if (!strcmp(argv[optind], "batch"))
daf3c104 835 {
5039dede 836 ExecSQLBatch(argv[optind + 1]);
daf3c104 837 }
5039dede 838 else if (!strcmp(argv[optind], "check"))
daf3c104
VK
839 {
840 CheckDatabase();
841 }
842 else if (!strcmp(argv[optind], "check-data-tables"))
843 {
844 g_checkDataTablesOnly = true;
5039dede 845 CheckDatabase();
daf3c104 846 }
5039dede 847 else if (!strcmp(argv[optind], "upgrade"))
daf3c104 848 {
5039dede 849 UpgradeDatabase();
daf3c104 850 }
5039dede 851 else if (!strcmp(argv[optind], "unlock"))
daf3c104 852 {
5039dede 853 UnlockDatabase();
daf3c104 854 }
1e558daf 855 else if (!strcmp(argv[optind], "export"))
daf3c104 856 {
4f23eecc 857 ExportDatabase(argv[optind + 1]);
daf3c104 858 }
890a0930 859 else if (!strcmp(argv[optind], "import"))
daf3c104 860 {
890a0930 861 ImportDatabase(argv[optind + 1]);
daf3c104 862 }
84ee0f9c
VK
863 else if (!strcmp(argv[optind], "migrate"))
864 {
865#ifdef UNICODE
866 WCHAR *sourceConfig = WideStringFromMBString(argv[optind + 1]);
867#else
868 char *sourceConfig = argv[optind + 1];
869#endif
f82c865a
EJ
870 TCHAR destConfFields[2048];
871 _sntprintf(destConfFields, 2048, _T("\tDB Name: %s\n\tDB Server: %s\n\tDB Login: %s"), s_dbName, s_dbServer, s_dbLogin);
872 MigrateDatabase(sourceConfig, destConfFields);
84ee0f9c
VK
873#ifdef UNICODE
874 free(sourceConfig);
875#endif
876 }
a43a5e7d
VK
877 else if (!strcmp(argv[optind], "get"))
878 {
879#ifdef UNICODE
880 WCHAR *var = WideStringFromMBString(argv[optind + 1]);
881#else
882 char *var = argv[optind + 1];
883#endif
2589cc10 884 TCHAR buffer[MAX_CONFIG_VALUE];
885 ConfigReadStr(var, buffer, MAX_CONFIG_VALUE, _T(""));
a43a5e7d
VK
886 _tprintf(_T("%s\n"), buffer);
887#ifdef UNICODE
888 free(var);
889#endif
890 }
891 else if (!strcmp(argv[optind], "set"))
892 {
893#ifdef UNICODE
894 WCHAR *var = WideStringFromMBString(argv[optind + 1]);
895 WCHAR *value = WideStringFromMBString(argv[optind + 2]);
896#else
897 char *var = argv[optind + 1];
898 char *value = argv[optind + 2];
899#endif
c85c8ef2 900 CreateConfigParam(var, value, true, false, replaceValue);
a43a5e7d
VK
901#ifdef UNICODE
902 free(var);
903 free(value);
904#endif
905 }
be737a59 906 else if (!strcmp(argv[optind], "reset-system-account"))
c614ca60 907 {
be737a59 908 ResetSystemAccount();
c614ca60 909 }
5039dede
AK
910 }
911
912 // Shutdown
913 DBDisconnect(g_hCoreDB);
63604cda 914 DBUnloadDriver(s_driver);
5039dede
AK
915 return 0;
916}