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