minor changes
[public/netxms.git] / src / server / tools / nxdbmgr / nxdbmgr.cpp
CommitLineData
5039dede
AK
1/*
2** nxdbmgr - NetXMS database manager
3** Copyright (C) 2004, 2005, 2006, 2007 Victor Kirhenshtein
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"
24
25#ifdef _WIN32
26#include <conio.h>
27#endif
28
29
30//
31// Global variables
32//
33
34DB_HANDLE g_hCoreDB;
35BOOL g_bIgnoreErrors = FALSE;
36BOOL g_bTrace = FALSE;
37int g_iSyntax;
38const TCHAR *g_pszTableSuffix = _T("");
39const TCHAR *g_pszSqlType[5][2] =
40{
41 { _T("text"), _T("bigint") }, // MySQL
42 { _T("varchar"), _T("bigint") }, // PostgreSQL
43 { _T("text"), _T("bigint") }, // Microsoft SQL
44 { _T("clob"), _T("number(20)") }, // Oracle
45 { _T("varchar"), _T("number(20)") } // SQLite
46};
47
48
49//
50// Static data
51//
52
53static TCHAR m_szCodePage[MAX_PATH] = ICONV_DEFAULT_CODEPAGE;
54static NX_CFG_TEMPLATE m_cfgTemplate[] =
55{
56 { "CodePage", CT_STRING, 0, 0, MAX_PATH, 0, m_szCodePage },
57 { "CreateCrashDumps", CT_IGNORE, 0, 0, 0, 0, NULL },
58 { "DBDriver", CT_STRING, 0, 0, MAX_PATH, 0, g_szDbDriver },
59 { "DBDrvParams", CT_STRING, 0, 0, MAX_PATH, 0, g_szDbDrvParams },
60 { "DBLogin", CT_STRING, 0, 0, MAX_DB_LOGIN, 0, g_szDbLogin },
61 { "DBName", CT_STRING, 0, 0, MAX_DB_NAME, 0, g_szDbName },
62 { "DBPassword", CT_STRING, 0, 0, MAX_DB_PASSWORD, 0, g_szDbPassword },
63 { "DBServer", CT_STRING, 0, 0, MAX_PATH, 0, g_szDbServer },
64 { "DumpDirectory", CT_IGNORE, 0, 0, 0, 0, NULL },
65 { "LogFailedSQLQueries", CT_IGNORE, 0, 0, 0, 0, NULL },
66 { "LogFile", CT_IGNORE, 0, 0, 0, 0, NULL },
67 { "Module", CT_IGNORE, 0, 0, 0, 0, NULL },
68 { "", CT_END_OF_LIST, 0, 0, 0, 0, NULL }
69};
70static BOOL m_bForce = FALSE;
71
72
73//
74// Show query if trace mode is ON
75//
76
77void ShowQuery(const TCHAR *pszQuery)
78{
79#ifdef _WIN32
80 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0F);
81 _tprintf(_T(">>> "));
82 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0A);
83 puts(pszQuery);
84 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);
85#else
86 _tprintf(_T(">>> %s\n"), pszQuery);
87#endif
88}
89
90
91//
92// Get Yes or No answer from keyboard
93//
94
95BOOL GetYesNo(void)
96{
97 if (m_bForce)
98 {
99 printf("Y\n");
100 return TRUE;
101 }
102 else
103 {
104#ifdef _WIN32
105 int ch;
106
107 while(1)
108 {
5036c088 109 ch = _getch();
5039dede
AK
110 if ((ch == 'y') || (ch == 'Y'))
111 {
112 printf("Y\n");
113 return TRUE;
114 }
115 if ((ch == 'n') || (ch == 'N'))
116 {
117 printf("N\n");
118 return FALSE;
119 }
120 }
121#else
122 TCHAR szBuffer[16];
123
124 fflush(stdout);
125 _fgetts(szBuffer, 16, stdin);
126 StrStrip(szBuffer);
127 return ((szBuffer[0] == 'y') || (szBuffer[0] == 'Y'));
128#endif
129 }
130}
131
132
133//
134// Execute SQL SELECT query and print error message on screen if query failed
135//
136
137DB_RESULT SQLSelect(const TCHAR *pszQuery)
138{
139 DB_RESULT hResult;
140 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
141
142 if (g_bTrace)
143 ShowQuery(pszQuery);
144
145 hResult = DBSelectEx(g_hCoreDB, pszQuery, errorText);
146 if (hResult == NULL)
147 {
148#ifdef _WIN32
149 _tprintf(_T("SQL query failed (%s):\n"), errorText);
150 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0E);
151 _tprintf(_T("%s\n"), pszQuery);
152 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);
153#else
154 _tprintf(_T("SQL query failed (%s):\n%s\n"), errorText, pszQuery);
155#endif
156 }
157 return hResult;
158}
159
160
161//
162// Execute SQL query and print error message on screen if query failed
163//
164
165BOOL SQLQuery(const TCHAR *pszQuery)
166{
167 BOOL bResult;
168 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
169
170 if (*pszQuery == 0)
171 return TRUE;
172
173 if (g_bTrace)
174 ShowQuery(pszQuery);
175
176 bResult = DBQueryEx(g_hCoreDB, pszQuery, errorText);
177 if (!bResult)
178 {
179#ifdef _WIN32
180 _tprintf(_T("SQL query failed (%s):\n"), errorText);
181 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0E);
182 _tprintf(_T("%s\n"), pszQuery);
183 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);
184#else
185 _tprintf(_T("SQL query failed (%s):\n%s\n"), errorText, pszQuery);
186#endif
187 }
188 return bResult;
189}
190
191
192//
193// Execute SQL batch
194//
195
196BOOL SQLBatch(const TCHAR *pszBatch)
197{
198 TCHAR *pszBuffer, *pszQuery, *ptr;
199 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
200 BOOL bRet = TRUE;
201
202 pszBuffer = _tcsdup(pszBatch);
203 TranslateStr(pszBuffer, _T("$SQL:TEXT"), g_pszSqlType[g_iSyntax][SQL_TYPE_TEXT]);
204 TranslateStr(pszBuffer, _T("$SQL:INT64"), g_pszSqlType[g_iSyntax][SQL_TYPE_INT64]);
205
206 pszQuery = pszBuffer;
207 while(1)
208 {
209 ptr = _tcschr(pszQuery, _T('\n'));
210 if (ptr != NULL)
211 *ptr = 0;
212 if (!_tcscmp(pszQuery, _T("<END>")))
213 break;
214
215 if (g_bTrace)
216 ShowQuery(pszQuery);
217
218 if (!DBQueryEx(g_hCoreDB, pszQuery, errorText))
219 {
220#ifdef _WIN32
221 _tprintf(_T("SQL query failed (%s):\n"), errorText);
222 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0E);
223 _tprintf(_T("%s\n"), pszQuery);
224 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);
225#else
226 _tprintf(_T("SQL query failed (%s):\n%s\n"), errorText, pszQuery);
227#endif
228 if (!g_bIgnoreErrors)
229 {
230 bRet = FALSE;
231 break;
232 }
233 }
234 ptr++;
235 pszQuery = ptr;
236 }
237 free(pszBuffer);
238 return bRet;
239}
240
241
242//
243// Read string value from configuration table
244//
245
246BOOL ConfigReadStr(const TCHAR *pszVar, TCHAR *pszBuffer, int iBufSize, const TCHAR *pszDefault)
247{
248 DB_RESULT hResult;
249 TCHAR szQuery[256];
250 BOOL bSuccess = FALSE;
251
252 nx_strncpy(pszBuffer, pszDefault, iBufSize);
253 if (_tcslen(pszVar) > 127)
254 return FALSE;
255
256 _sntprintf(szQuery, 256, _T("SELECT var_value FROM config WHERE var_name='%s'"), pszVar);
257 hResult = SQLSelect(szQuery);
258 if (hResult == 0)
259 return FALSE;
260
261 if (DBGetNumRows(hResult) > 0)
262 {
263 DBGetField(hResult, 0, 0, pszBuffer, iBufSize);
264 DecodeSQLString(pszBuffer);
265 bSuccess = TRUE;
266 }
267
268 DBFreeResult(hResult);
269 return bSuccess;
270}
271
272
273//
274// Read integer value from configuration table
275//
276
277int ConfigReadInt(const TCHAR *pszVar, int iDefault)
278{
279 TCHAR szBuffer[64];
280
281 if (ConfigReadStr(pszVar, szBuffer, 64, _T("")))
282 return _tcstol(szBuffer, NULL, 0);
283 else
284 return iDefault;
285}
286
287
288//
289// Read unsigned long value from configuration table
290//
291
292DWORD ConfigReadULong(const TCHAR *pszVar, DWORD dwDefault)
293{
294 TCHAR szBuffer[64];
295
296 if (ConfigReadStr(pszVar, szBuffer, 64, _T("")))
297 return _tcstoul(szBuffer, NULL, 0);
298 else
299 return dwDefault;
300}
301
302
303//
304// Startup
305//
306
307int main(int argc, char *argv[])
308{
309 BOOL bStart = TRUE, bForce = FALSE;
310 int ch;
311 TCHAR szSyntaxId[16], szConfigFile[MAX_PATH] = DEFAULT_CONFIG_FILE;
312 DB_RESULT hResult;
313#ifdef _WIN32
314 HKEY hKey;
315 DWORD dwSize;
316#else
317 char *pszEnv;
318#endif
319
320 InitThreadLibrary();
321
322 printf("NetXMS Database Manager Version " NETXMS_VERSION_STRING "\n\n");
323
324 // Check for alternate config file location
325#ifdef _WIN32
326 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Server"), 0,
327 KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
328 {
329 dwSize = MAX_PATH * sizeof(TCHAR);
330 RegQueryValueEx(hKey, _T("ConfigFile"), NULL, NULL, (BYTE *)szConfigFile, &dwSize);
331 RegCloseKey(hKey);
332 }
333#else
334 pszEnv = getenv("NETXMSD_CONFIG");
335 if (pszEnv != NULL)
336 nx_strncpy(szConfigFile, pszEnv, MAX_PATH);
337#endif
338
339 // Parse command line
340 opterr = 1;
341 while((ch = getopt(argc, argv, "c:fhIMtvX")) != -1)
342 {
343 switch(ch)
344 {
345 case 'h': // Display help and exit
346 _tprintf(_T("Usage: nxdbmgr [<options>] <command>\n"
347 "Valid commands are:\n"
348 " batch <file> : Run SQL batch file\n"
349 " check : Check database for errors\n"
350 " init <file> : Initialize database\n"
351 " reindex : Reindex database\n"
352 " unlock : Forced database unlock\n"
353 " upgrade : Upgrade database to new version\n"
354 "Valid options are:\n"
355 " -c <config> : Use alternate configuration file. Default is " DEFAULT_CONFIG_FILE "\n"
356 " -f : Force repair - do not ask for confirmation.\n"
357 " -h : Display help and exit.\n"
358 " -I : MySQL only - specify TYPE=InnoDB for new tables.\n"
359 " -M : MySQL only - specify TYPE=MyISAM for new tables.\n"
360 " -t : Enable trace moded (show executed SQL queries).\n"
361 " -v : Display version and exit.\n"
362 " -X : Ignore SQL errors when upgrading (USE WITH CARE!!!)\n"
363 "\n"));
364 bStart = FALSE;
365 break;
366 case 'v': // Print version and exit
367 bStart = FALSE;
368 break;
369 case 'c':
370 nx_strncpy(szConfigFile, optarg, MAX_PATH);
371 break;
372 case 'f':
373 m_bForce = TRUE;
374 break;
375 case 't':
376 g_bTrace = TRUE;
377 break;
378 case 'I':
379 g_pszTableSuffix = _T(" TYPE=InnoDB");
380 break;
381 case 'M':
382 g_pszTableSuffix = _T(" TYPE=MyISAM");
383 break;
384 case 'X':
385 g_bIgnoreErrors = TRUE;
386 break;
387 case '?':
388 bStart = FALSE;
389 break;
390 default:
391 break;
392 }
393 }
394
395 if (!bStart)
396 return 1;
397
398 // Check parameter correctness
399 if (argc - optind == 0)
400 {
401 _tprintf(_T("Command missing. Type nxdbmgr -h for command line syntax.\n"));
402 return 1;
403 }
404 if (strcmp(argv[optind], "batch") &&
405 strcmp(argv[optind], "check") &&
406 strcmp(argv[optind], "reindex") &&
407 strcmp(argv[optind], "upgrade") &&
408 strcmp(argv[optind], "unlock") &&
409 strcmp(argv[optind], "init"))
410 {
411 _tprintf(_T("Invalid command \"%s\". Type nxdbmgr -h for command line syntax.\n"), argv[optind]);
412 return 1;
413 }
414 if ((!strcmp(argv[optind], "init") || !strcmp(argv[optind], "batch")) && (argc - optind < 2))
415 {
416 _tprintf("Required command argument missing\n");
417 return 1;
418 }
419
420 // Read configuration file
421#if !defined(_WIN32) && !defined(_NETWARE)
422 if (!_tcscmp(szConfigFile, _T("{search}")))
423 {
424 if (access(PREFIX "/etc/netxmsd.conf", 4) == 0)
425 {
426 _tcscpy(szConfigFile, PREFIX "/etc/netxmsd.conf");
427 }
428 else if (access("/usr/etc/netxmsd.conf", 4) == 0)
429 {
430 _tcscpy(szConfigFile, "/usr/etc/netxmsd.conf");
431 }
432 else
433 {
434 _tcscpy(szConfigFile, "/etc/netxmsd.conf");
435 }
436 }
437#endif
438
439 if (NxLoadConfig(szConfigFile, _T(""), m_cfgTemplate, TRUE) != NXCFG_ERR_OK)
440 {
441 _tprintf(_T("Error loading configuration file\n"));
442 return 2;
443 }
444#ifndef _WIN32
445 SetDefaultCodepage(m_szCodePage);
446#endif
447
448 // Connect to database
449 if (!DBInit(FALSE, FALSE, FALSE, NULL))
450 {
451 _tprintf(_T("Unable to load and initialize database driver \"%s\"\n"), g_szDbDriver);
452 return 3;
453 }
454
455 g_hCoreDB = DBConnect();
456 if (g_hCoreDB == NULL)
457 {
458 _tprintf(_T("Unable to connect to database %s@%s as %s\n"), g_szDbName,
459 g_szDbServer, g_szDbLogin);
460 DBUnloadDriver();
461 return 4;
462 }
463
464 if (!strcmp(argv[optind], "init"))
465 {
466 InitDatabase(argv[optind + 1]);
467 }
468 else
469 {
470 // Get database syntax
471 hResult = DBSelect(g_hCoreDB, _T("SELECT var_value FROM config WHERE var_name='DBSyntax'"));
472 if (hResult != NULL)
473 {
474 if (DBGetNumRows(hResult) > 0)
475 {
476 DBGetField(hResult, 0, 0, szSyntaxId, sizeof(szSyntaxId));
477 DecodeSQLString(szSyntaxId);
478 }
479 else
480 {
481 _tcscpy(szSyntaxId, _T("UNKNOWN"));
482 }
483 DBFreeResult(hResult);
484 }
485 else
486 {
487 _tprintf(_T("Unable to determine database syntax\n"));
488 DBDisconnect(g_hCoreDB);
489 DBUnloadDriver();
490 return 5;
491 }
492
493 if (!_tcscmp(szSyntaxId, _T("MYSQL")))
494 {
495 g_iSyntax = DB_SYNTAX_MYSQL;
496 }
497 else if (!_tcscmp(szSyntaxId, _T("PGSQL")))
498 {
499 g_iSyntax = DB_SYNTAX_PGSQL;
500 }
501 else if (!_tcscmp(szSyntaxId, _T("MSSQL")))
502 {
503 g_iSyntax = DB_SYNTAX_MSSQL;
504 }
505 else if (!_tcscmp(szSyntaxId, _T("ORACLE")))
506 {
507 g_iSyntax = DB_SYNTAX_ORACLE;
508 }
509 else if (!_tcscmp(szSyntaxId, _T("SQLITE")))
510 {
511 g_iSyntax = DB_SYNTAX_SQLITE;
512 }
513 else
514 {
515 _tprintf(_T("Unknown database syntax %s\n"), szSyntaxId);
516 DBDisconnect(g_hCoreDB);
517 DBUnloadDriver();
518 return 6;
519 }
520
521 // Do requested operation
522 if (!strcmp(argv[optind], "batch"))
523 ExecSQLBatch(argv[optind + 1]);
524 else if (!strcmp(argv[optind], "check"))
525 CheckDatabase();
526 else if (!strcmp(argv[optind], "upgrade"))
527 UpgradeDatabase();
528 else if (!strcmp(argv[optind], "unlock"))
529 UnlockDatabase();
530 else if (!strcmp(argv[optind], "reindex"))
531 ReindexDatabase();
532 }
533
534 // Shutdown
535 DBDisconnect(g_hCoreDB);
536 DBUnloadDriver();
537 return 0;
538}