Commit | Line | Data |
---|---|---|
2589cc10 | 1 | /* |
5039dede | 2 | ** nxdbmgr - NetXMS database manager |
43a6f3ca | 3 | ** Copyright (C) 2004-2015 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 |
33 | DB_HANDLE g_hCoreDB; |
34 | BOOL g_bIgnoreErrors = FALSE; | |
35 | BOOL g_bTrace = FALSE; | |
a4743a0f | 36 | bool g_isGuiMode = false; |
0d9b58ef | 37 | bool g_checkData = false; |
daf3c104 | 38 | bool g_checkDataTablesOnly = false; |
ffaafdce VK |
39 | bool g_dataOnlyMigration = false; |
40 | bool g_skipDataMigration = false; | |
09f1c9bf VK |
41 | bool g_skipDataSchemaMigration = false; |
42 | int g_migrationTxnSize = 4096; | |
2a964810 | 43 | int g_dbSyntax; |
5039dede | 44 | const TCHAR *g_pszTableSuffix = _T(""); |
2589cc10 | 45 | const TCHAR *g_pszSqlType[6][3] = |
5039dede | 46 | { |
50da2d20 VK |
47 | { _T("text"), _T("text"), _T("bigint") }, // MySQL |
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 | 58 | static char m_szCodePage[MAX_PATH] = ICONV_DEFAULT_CODEPAGE; |
b8c1ec69 VK |
59 | static TCHAR s_dbDriver[MAX_PATH] = _T(""); |
60 | static TCHAR s_dbDrvParams[MAX_PATH] = _T(""); | |
61 | static TCHAR s_dbServer[MAX_PATH] = _T("127.0.0.1"); | |
62 | static TCHAR s_dbLogin[MAX_DB_LOGIN] = _T("netxms"); | |
07ca7d19 | 63 | static TCHAR s_dbPassword[MAX_PASSWORD] = _T(""); |
b8c1ec69 | 64 | static TCHAR s_dbName[MAX_DB_NAME] = _T("netxms_db"); |
f3c30cf5 | 65 | static TCHAR s_dbSchema[MAX_DB_NAME] = _T(""); |
5039dede AK |
66 | static 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 | }; |
79 | static BOOL m_bForce = FALSE; | |
63604cda | 80 | static DB_DRIVER s_driver = NULL; |
5039dede | 81 | |
69bb7f47 VK |
82 | /** |
83 | * Show query if trace mode is ON | |
84 | */ | |
5039dede AK |
85 | void 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 | 93 | bool 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 |
159 | DB_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 | 176 | DB_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 | 191 | bool 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 | 207 | bool 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 | 231 | bool 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(); |
5039dede AK |
244 | while(1) |
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 | ||
5039dede AK |
280 | ptr++; |
281 | pszQuery = ptr; | |
282 | } | |
c85c8ef2 | 283 | return success; |
5039dede AK |
284 | } |
285 | ||
69bb7f47 VK |
286 | /** |
287 | * Drop column from the table | |
288 | */ | |
c85c8ef2 | 289 | bool SQLDropColumn(const TCHAR *table, const TCHAR *column) |
8ec9bcc9 AK |
290 | { |
291 | TCHAR query[1024]; | |
292 | DB_RESULT hResult; | |
c85c8ef2 | 293 | bool success = false; |
8ec9bcc9 | 294 | |
2a964810 | 295 | if (g_dbSyntax != DB_SYNTAX_SQLITE) |
8ec9bcc9 AK |
296 | { |
297 | _sntprintf(query, 1024, _T("ALTER TABLE %s DROP COLUMN %s"), table, column); | |
298 | success = SQLQuery(query); | |
c849da55 VK |
299 | if (g_dbSyntax == DB_SYNTAX_DB2) |
300 | { | |
301 | _sntprintf(query, 1024, _T("CALL Sysproc.admin_cmd('REORG TABLE %s')"), table); | |
302 | success = SQLQuery(query); | |
303 | } | |
8ec9bcc9 AK |
304 | } |
305 | else | |
306 | { | |
307 | _sntprintf(query, 1024, _T("PRAGMA TABLE_INFO('%s')"), table); | |
308 | hResult = SQLSelect(query); | |
309 | if (hResult != NULL) | |
310 | { | |
311 | int rows = DBGetNumRows(hResult); | |
312 | const int blen = 2048; | |
313 | TCHAR buffer[blen]; | |
314 | // Intermediate buffers for SQLs | |
2589cc10 | 315 | TCHAR columnList[1024], createList[1024]; |
8ec9bcc9 AK |
316 | // TABLE_INFO() columns |
317 | TCHAR tabColName[128], tabColType[64], tabColNull[10], tabColDefault[128]; | |
318 | columnList[0] = createList[0] = _T('\0'); | |
319 | for (int i = 0; i < rows; i++) | |
320 | { | |
321 | DBGetField(hResult, i, 1, tabColName, 128); | |
322 | DBGetField(hResult, i, 2, tabColType, 64); | |
323 | DBGetField(hResult, i, 3, tabColNull, 10); | |
324 | DBGetField(hResult, i, 4, tabColDefault, 128); | |
325 | if (_tcsnicmp(tabColName, column, 128)) | |
326 | { | |
327 | _tcscat(columnList, tabColName); | |
328 | if (columnList[0] != _T('\0')) | |
329 | _tcscat(columnList, _T(",")); | |
330 | _tcscat(createList, tabColName); | |
331 | _tcscat(createList, tabColType); | |
332 | if (tabColDefault[0] != _T('\0')) | |
333 | { | |
334 | _tcscat(createList, _T("DEFAULT ")); | |
335 | _tcscat(createList, tabColDefault); | |
336 | } | |
337 | if (tabColNull[0] == _T('1')) | |
338 | _tcscat(createList, _T(" NOT NULL")); | |
339 | _tcscat(createList, _T(",")); | |
340 | } | |
341 | } | |
342 | DBFreeResult(hResult); | |
343 | if (rows > 0) | |
344 | { | |
417ab143 | 345 | int cllen = (int)_tcslen(columnList); |
8ec9bcc9 AK |
346 | if (cllen > 0 && columnList[cllen - 1] == _T(',')) |
347 | columnList[cllen - 1] = _T('\0'); | |
348 | // TODO: figure out if SQLite transactions will work here | |
349 | _sntprintf(buffer, blen, _T("CREATE TABLE %s__backup__ (%s)"), table, columnList); | |
8ec9bcc9 | 350 | CHK_EXEC(SQLQuery(buffer)); |
2589cc10 | 351 | _sntprintf(buffer, blen, _T("INSERT INTO %s__backup__ (%s) SELECT %s FROM %s"), |
8ec9bcc9 | 352 | table, columnList, columnList, table); |
8ec9bcc9 AK |
353 | CHK_EXEC(SQLQuery(buffer)); |
354 | _sntprintf(buffer, blen, _T("DROP TABLE %s"), table); | |
8ec9bcc9 AK |
355 | CHK_EXEC(SQLQuery(buffer)); |
356 | _sntprintf(buffer, blen, _T("ALTER TABLE %s__backup__ RENAME to %s"), table, table); | |
8ec9bcc9 | 357 | CHK_EXEC(SQLQuery(buffer)); |
c85c8ef2 | 358 | success = true; |
8ec9bcc9 AK |
359 | } |
360 | } | |
361 | } | |
362 | ||
363 | // TODO: preserve indices and constraints?? | |
364 | ||
365 | return success; | |
366 | } | |
5039dede | 367 | |
a43a5e7d VK |
368 | /** |
369 | * Read string value from metadata table | |
370 | */ | |
3783d300 VK |
371 | BOOL MetaDataReadStr(const TCHAR *pszVar, TCHAR *pszBuffer, int iBufSize, const TCHAR *pszDefault) |
372 | { | |
373 | DB_RESULT hResult; | |
374 | TCHAR szQuery[256]; | |
375 | BOOL bSuccess = FALSE; | |
376 | ||
377 | nx_strncpy(pszBuffer, pszDefault, iBufSize); | |
378 | if (_tcslen(pszVar) > 127) | |
379 | return FALSE; | |
380 | ||
381 | _sntprintf(szQuery, 256, _T("SELECT var_value FROM metadata WHERE var_name='%s'"), pszVar); | |
382 | hResult = SQLSelect(szQuery); | |
383 | if (hResult == NULL) | |
384 | return FALSE; | |
385 | ||
386 | if (DBGetNumRows(hResult) > 0) | |
387 | { | |
388 | DBGetField(hResult, 0, 0, pszBuffer, iBufSize); | |
389 | bSuccess = TRUE; | |
390 | } | |
391 | ||
392 | DBFreeResult(hResult); | |
393 | return bSuccess; | |
394 | } | |
395 | ||
2f1bc68b VK |
396 | /** |
397 | * Read integer value from configuration table | |
398 | */ | |
399 | int MetaDataReadInt(const TCHAR *pszVar, int iDefault) | |
400 | { | |
401 | TCHAR szBuffer[64]; | |
402 | ||
403 | if (MetaDataReadStr(pszVar, szBuffer, 64, _T(""))) | |
404 | return _tcstol(szBuffer, NULL, 0); | |
405 | else | |
406 | return iDefault; | |
407 | } | |
408 | ||
a43a5e7d VK |
409 | /** |
410 | * Read string value from configuration table | |
411 | */ | |
5039dede AK |
412 | BOOL ConfigReadStr(const TCHAR *pszVar, TCHAR *pszBuffer, int iBufSize, const TCHAR *pszDefault) |
413 | { | |
414 | DB_RESULT hResult; | |
a50beeec | 415 | TCHAR szQuery[256]; |
5039dede AK |
416 | BOOL bSuccess = FALSE; |
417 | ||
418 | nx_strncpy(pszBuffer, pszDefault, iBufSize); | |
419 | if (_tcslen(pszVar) > 127) | |
420 | return FALSE; | |
421 | ||
a50beeec | 422 | _sntprintf(szQuery, 256, _T("SELECT var_value FROM config WHERE var_name='%s'"), pszVar); |
5039dede | 423 | hResult = SQLSelect(szQuery); |
3783d300 | 424 | if (hResult == NULL) |
5039dede AK |
425 | return FALSE; |
426 | ||
427 | if (DBGetNumRows(hResult) > 0) | |
428 | { | |
429 | DBGetField(hResult, 0, 0, pszBuffer, iBufSize); | |
5039dede AK |
430 | bSuccess = TRUE; |
431 | } | |
432 | ||
433 | DBFreeResult(hResult); | |
434 | return bSuccess; | |
435 | } | |
436 | ||
c59466d2 VK |
437 | /** |
438 | * Read integer value from configuration table | |
439 | */ | |
5039dede AK |
440 | int ConfigReadInt(const TCHAR *pszVar, int iDefault) |
441 | { | |
442 | TCHAR szBuffer[64]; | |
443 | ||
444 | if (ConfigReadStr(pszVar, szBuffer, 64, _T(""))) | |
445 | return _tcstol(szBuffer, NULL, 0); | |
446 | else | |
447 | return iDefault; | |
448 | } | |
449 | ||
c59466d2 VK |
450 | /** |
451 | * Read unsigned long value from configuration table | |
452 | */ | |
5039dede AK |
453 | DWORD ConfigReadULong(const TCHAR *pszVar, DWORD dwDefault) |
454 | { | |
455 | TCHAR szBuffer[64]; | |
456 | ||
457 | if (ConfigReadStr(pszVar, szBuffer, 64, _T(""))) | |
458 | return _tcstoul(szBuffer, NULL, 0); | |
459 | else | |
460 | return dwDefault; | |
461 | } | |
462 | ||
06b83321 VK |
463 | /** |
464 | * Check if given record exists in database | |
465 | */ | |
466 | bool IsDatabaseRecordExist(const TCHAR *table, const TCHAR *idColumn, UINT32 id) | |
467 | { | |
468 | bool exist = false; | |
469 | ||
470 | TCHAR query[256]; | |
471 | _sntprintf(query, 256, _T("SELECT %s FROM %s WHERE %s=?"), idColumn, table, idColumn); | |
472 | ||
473 | DB_STATEMENT hStmt = DBPrepare(g_hCoreDB, query); | |
474 | if (hStmt != NULL) | |
475 | { | |
476 | DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, id); | |
477 | DB_RESULT hResult = DBSelectPrepared(hStmt); | |
478 | if (hResult != NULL) | |
479 | { | |
480 | exist = (DBGetNumRows(hResult) > 0); | |
481 | DBFreeResult(hResult); | |
482 | } | |
483 | DBFreeStatement(hStmt); | |
484 | } | |
485 | return exist; | |
486 | } | |
487 | ||
c59466d2 VK |
488 | /** |
489 | * Check that database has correct schema version and is not locked | |
490 | */ | |
c85c8ef2 | 491 | bool ValidateDatabase() |
1e558daf VK |
492 | { |
493 | DB_RESULT hResult; | |
494 | LONG nVersion = 0; | |
bd802b08 | 495 | BOOL bLocked = FALSE; |
1e558daf VK |
496 | TCHAR szLockStatus[MAX_DB_STRING], szLockInfo[MAX_DB_STRING]; |
497 | ||
498 | // Get database format version | |
28f5b9a4 | 499 | nVersion = DBGetSchemaVersion(g_hCoreDB); |
1e558daf VK |
500 | if (nVersion < DB_FORMAT_VERSION) |
501 | { | |
502 | _tprintf(_T("Your database has format version %d, this tool is compiled for version %d.\nUse \"upgrade\" command to upgrade your database first.\n"), | |
503 | nVersion, DB_FORMAT_VERSION); | |
c85c8ef2 | 504 | return false; |
1e558daf VK |
505 | } |
506 | else if (nVersion > DB_FORMAT_VERSION) | |
507 | { | |
9f24efb3 VK |
508 | _tprintf(_T("Your database has format version %d, this tool is compiled for version %d.\n") |
509 | _T("You need to upgrade your server before using this database.\n"), | |
510 | nVersion, DB_FORMAT_VERSION); | |
c85c8ef2 | 511 | return false; |
1e558daf VK |
512 | } |
513 | ||
514 | // Check if database is locked | |
515 | hResult = DBSelect(g_hCoreDB, _T("SELECT var_value FROM config WHERE var_name='DBLockStatus'")); | |
516 | if (hResult != NULL) | |
517 | { | |
518 | if (DBGetNumRows(hResult) > 0) | |
519 | { | |
bf7f9d94 | 520 | DBGetField(hResult, 0, 0, szLockStatus, MAX_DB_STRING); |
1e558daf VK |
521 | bLocked = _tcscmp(szLockStatus, _T("UNLOCKED")); |
522 | } | |
523 | DBFreeResult(hResult); | |
524 | ||
525 | if (bLocked) | |
526 | { | |
527 | hResult = DBSelect(g_hCoreDB, _T("SELECT var_value FROM config WHERE var_name='DBLockInfo'")); | |
528 | if (hResult != NULL) | |
529 | { | |
530 | if (DBGetNumRows(hResult) > 0) | |
531 | { | |
bf7f9d94 | 532 | DBGetField(hResult, 0, 0, szLockInfo, MAX_DB_STRING); |
1e558daf VK |
533 | } |
534 | DBFreeResult(hResult); | |
535 | } | |
536 | } | |
537 | } | |
538 | ||
539 | if (bLocked) | |
540 | { | |
541 | _tprintf(_T("Database is locked by server %s [%s]\n"), szLockStatus, szLockInfo); | |
c85c8ef2 | 542 | return false; |
1e558daf VK |
543 | } |
544 | ||
c85c8ef2 | 545 | return true; |
1e558daf VK |
546 | } |
547 | ||
63604cda VK |
548 | /** |
549 | * Open database connection | |
550 | */ | |
551 | DB_HANDLE ConnectToDatabase() | |
552 | { | |
553 | TCHAR errorText[DBDRV_MAX_ERROR_TEXT]; | |
554 | DB_HANDLE hdb = DBConnect(s_driver, s_dbServer, s_dbName, s_dbLogin, s_dbPassword, s_dbSchema, errorText); | |
555 | if (hdb == NULL) | |
556 | { | |
557 | _tprintf(_T("Unable to connect to database %s@%s as %s: %s\n"), s_dbName, s_dbServer, s_dbLogin, errorText); | |
558 | } | |
559 | return hdb; | |
560 | } | |
561 | ||
c59466d2 VK |
562 | /** |
563 | * Startup | |
564 | */ | |
5039dede AK |
565 | int main(int argc, char *argv[]) |
566 | { | |
6ba36557 | 567 | BOOL bStart = TRUE, bQuiet = FALSE; |
c85c8ef2 | 568 | bool replaceValue = true; |
5039dede | 569 | int ch; |
5039dede | 570 | |
fe8ea784 | 571 | InitNetXMSProcess(); |
5039dede | 572 | |
1039d7ee VK |
573 | TCHAR configFile[MAX_PATH] = _T(""); |
574 | ||
575 | // Try to read config location | |
5039dede | 576 | #ifdef _WIN32 |
38ffd4b0 | 577 | HKEY hKey; |
1039d7ee | 578 | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Server"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) |
5039dede | 579 | { |
38ffd4b0 | 580 | DWORD dwSize = MAX_PATH * sizeof(TCHAR); |
1039d7ee | 581 | RegQueryValueEx(hKey, _T("ConfigFile"), NULL, NULL, (BYTE *)configFile, &dwSize); |
5039dede AK |
582 | RegCloseKey(hKey); |
583 | } | |
584 | #else | |
38ffd4b0 VK |
585 | const TCHAR *env = _tgetenv(_T("NETXMSD_CONFIG")); |
586 | if ((env != NULL) && (*env != 0)) | |
1039d7ee VK |
587 | nx_strncpy(configFile, env, MAX_PATH); |
588 | #endif | |
589 | ||
590 | // Search for config | |
591 | if (configFile[0] == 0) | |
592 | { | |
593 | #ifdef _WIN32 | |
594 | TCHAR path[MAX_PATH]; | |
595 | GetNetXMSDirectory(nxDirEtc, path); | |
596 | _tcscat(path, _T("\\netxmsd.conf")); | |
597 | if (_taccess(path, 4) == 0) | |
598 | { | |
599 | _tcscpy(configFile, path); | |
600 | } | |
601 | else | |
602 | { | |
603 | _tcscpy(configFile, _T("C:\\netxmsd.conf")); | |
604 | } | |
605 | #else | |
606 | const TCHAR *homeDir = _tgetenv(_T("NETXMS_HOME")); | |
607 | if ((homeDir != NULL) && (*homeDir != 0)) | |
608 | { | |
609 | TCHAR config[MAX_PATH]; | |
610 | _sntprintf(config, MAX_PATH, _T("%s/etc/netxmsd.conf"), homeDir); | |
611 | if (_taccess(config, 4) == 0) | |
612 | { | |
613 | _tcscpy(configFile, config); | |
614 | goto stop_search; | |
615 | } | |
616 | } | |
617 | if (_taccess(PREFIX _T("/etc/netxmsd.conf"), 4) == 0) | |
618 | { | |
619 | _tcscpy(configFile, PREFIX _T("/etc/netxmsd.conf")); | |
620 | } | |
621 | else if (_taccess(_T("/usr/etc/netxmsd.conf"), 4) == 0) | |
622 | { | |
623 | _tcscpy(configFile, _T("/usr/etc/netxmsd.conf")); | |
624 | } | |
625 | else | |
626 | { | |
627 | _tcscpy(configFile, _T("/etc/netxmsd.conf")); | |
628 | } | |
629 | stop_search: | |
630 | ; | |
5039dede | 631 | #endif |
1039d7ee | 632 | } |
5039dede AK |
633 | |
634 | // Parse command line | |
635 | opterr = 1; | |
09f1c9bf | 636 | while((ch = getopt(argc, argv, "c:dDfGhIMNqsStT:vX")) != -1) |
5039dede AK |
637 | { |
638 | switch(ch) | |
639 | { | |
640 | case 'h': // Display help and exit | |
f62b7eab | 641 | _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 |
642 | _tprintf(_T("Usage: nxdbmgr [<options>] <command>\n") |
643 | _T("Valid commands are:\n") | |
a43a5e7d VK |
644 | _T(" batch <file> : Run SQL batch file\n") |
645 | _T(" check : Check database for errors\n") | |
daf3c104 | 646 | _T(" check-data-tables : Check database for missing data tables\n") |
a43a5e7d VK |
647 | _T(" export <file> : Export database to file\n") |
648 | _T(" get <name> : Get value of server configuration variable\n") | |
649 | _T(" import <file> : Import database from file\n") | |
650 | _T(" init <file> : Initialize database\n") | |
7541bca2 VK |
651 | _T(" migrate <source> : Migrate database from given source\n") |
652 | _T(" resetadmin : Unlock user \"admin\" and reset password to default (\"netxms\")\n") | |
a43a5e7d VK |
653 | _T(" set <name> <value> : Set value of server configuration variable\n") |
654 | _T(" unlock : Forced database unlock\n") | |
655 | _T(" upgrade : Upgrade database to new version\n") | |
08b214c6 | 656 | _T("Valid options are:\n") |
1039d7ee | 657 | _T(" -c <config> : Use alternate configuration file. Default is %s\n") |
0d9b58ef | 658 | _T(" -d : Check collected data (may take very long time).\n") |
ffaafdce | 659 | _T(" -D : Migrate only collected data.\n") |
08b214c6 | 660 | _T(" -f : Force repair - do not ask for confirmation.\n") |
a4743a0f | 661 | #ifdef _WIN32 |
08b214c6 | 662 | _T(" -G : GUI mode.\n") |
a4743a0f | 663 | #endif |
08b214c6 VK |
664 | _T(" -h : Display help and exit.\n") |
665 | _T(" -I : MySQL only - specify TYPE=InnoDB for new tables.\n") | |
666 | _T(" -M : MySQL only - specify TYPE=MyISAM for new tables.\n") | |
8038d371 | 667 | _T(" -N : Do not replace existing configuration value (\"set\" command only).\n") |
a43a5e7d | 668 | _T(" -q : Quiet mode (don't show startup banner).\n") |
4da7f408 VK |
669 | _T(" -s : Skip collected data during migration or export.\n") |
670 | _T(" -S : Skip collected data during migration or export and do not clear or create data tables.\n") | |
08b214c6 | 671 | _T(" -t : Enable trace mode (show executed SQL queries).\n") |
09f1c9bf | 672 | _T(" -T <recs> : Transaction size for migration.\n") |
08b214c6 | 673 | _T(" -v : Display version and exit.\n") |
300c7968 | 674 | _T(" -X : Ignore SQL errors when upgrading (USE WITH CAUTION!!!)\n") |
1039d7ee | 675 | _T("\n"), configFile); |
5039dede AK |
676 | bStart = FALSE; |
677 | break; | |
678 | case 'v': // Print version and exit | |
f62b7eab | 679 | _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 |
680 | bStart = FALSE; |
681 | break; | |
682 | case 'c': | |
08b214c6 | 683 | #ifdef UNICODE |
1039d7ee VK |
684 | MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, configFile, MAX_PATH); |
685 | configFile[MAX_PATH - 1] = 0; | |
08b214c6 | 686 | #else |
1039d7ee | 687 | nx_strncpy(configFile, optarg, MAX_PATH); |
08b214c6 | 688 | #endif |
5039dede | 689 | break; |
0d9b58ef VK |
690 | case 'd': |
691 | g_checkData = true; | |
692 | break; | |
ffaafdce VK |
693 | case 'D': |
694 | g_dataOnlyMigration = true; | |
695 | break; | |
5039dede AK |
696 | case 'f': |
697 | m_bForce = TRUE; | |
698 | break; | |
a4743a0f VK |
699 | case 'G': |
700 | g_isGuiMode = true; | |
701 | break; | |
8038d371 | 702 | case 'N': |
c85c8ef2 | 703 | replaceValue = false; |
8038d371 | 704 | break; |
a43a5e7d VK |
705 | case 'q': |
706 | bQuiet = TRUE; | |
707 | break; | |
ffaafdce VK |
708 | case 's': |
709 | g_skipDataMigration = true; | |
710 | break; | |
09f1c9bf VK |
711 | case 'S': |
712 | g_skipDataMigration = true; | |
713 | g_skipDataSchemaMigration = true; | |
714 | break; | |
5039dede AK |
715 | case 't': |
716 | g_bTrace = TRUE; | |
717 | break; | |
09f1c9bf VK |
718 | case 'T': |
719 | g_migrationTxnSize = strtol(optarg, NULL, 0); | |
720 | if ((g_migrationTxnSize < 1) || (g_migrationTxnSize > 100000)) | |
721 | { | |
722 | _tprintf(_T("WARNING: invalid transaction size, reset to default")); | |
723 | g_migrationTxnSize = 4096; | |
724 | } | |
725 | break; | |
5039dede AK |
726 | case 'I': |
727 | g_pszTableSuffix = _T(" TYPE=InnoDB"); | |
728 | break; | |
729 | case 'M': | |
730 | g_pszTableSuffix = _T(" TYPE=MyISAM"); | |
731 | break; | |
732 | case 'X': | |
733 | g_bIgnoreErrors = TRUE; | |
734 | break; | |
735 | case '?': | |
736 | bStart = FALSE; | |
737 | break; | |
738 | default: | |
739 | break; | |
740 | } | |
741 | } | |
742 | ||
743 | if (!bStart) | |
744 | return 1; | |
745 | ||
a43a5e7d | 746 | if (!bQuiet) |
f62b7eab | 747 | _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 | 748 | |
5039dede AK |
749 | // Check parameter correctness |
750 | if (argc - optind == 0) | |
751 | { | |
9f24efb3 | 752 | _tprintf(_T("Command missing. Type nxdbmgr -h for command line syntax.\n")); |
5039dede AK |
753 | return 1; |
754 | } | |
2589cc10 | 755 | if (strcmp(argv[optind], "batch") && |
756 | strcmp(argv[optind], "check") && | |
757 | strcmp(argv[optind], "check-data-tables") && | |
758 | strcmp(argv[optind], "export") && | |
759 | strcmp(argv[optind], "get") && | |
760 | strcmp(argv[optind], "import") && | |
a43a5e7d | 761 | strcmp(argv[optind], "init") && |
84ee0f9c | 762 | strcmp(argv[optind], "migrate") && |
84ee0f9c | 763 | strcmp(argv[optind], "resetadmin") && |
a43a5e7d | 764 | strcmp(argv[optind], "set") && |
5039dede | 765 | strcmp(argv[optind], "unlock") && |
84ee0f9c | 766 | strcmp(argv[optind], "upgrade")) |
5039dede | 767 | { |
9f24efb3 | 768 | _tprintf(_T("Invalid command \"%hs\". Type nxdbmgr -h for command line syntax.\n"), argv[optind]); |
5039dede AK |
769 | return 1; |
770 | } | |
84ee0f9c | 771 | 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 | 772 | (!strcmp(argv[optind], "set") && (argc - optind < 3))) |
5039dede | 773 | { |
2467ed57 | 774 | _tprintf(_T("Required command argument(s) missing\n")); |
5039dede AK |
775 | return 1; |
776 | } | |
777 | ||
778 | // Read configuration file | |
e6c91aac | 779 | Config *config = new Config(); |
1039d7ee | 780 | if (!config->loadIniConfig(configFile, _T("server")) || !config->parseTemplate(_T("server"), m_cfgTemplate)) |
5039dede | 781 | { |
9f24efb3 | 782 | _tprintf(_T("Error loading configuration file\n")); |
5039dede AK |
783 | return 2; |
784 | } | |
e6c91aac VK |
785 | delete config; |
786 | ||
d96bd4c7 | 787 | // Decrypt password |
07ca7d19 | 788 | DecryptPassword(s_dbLogin, s_dbPassword, s_dbPassword, MAX_PASSWORD); |
d96bd4c7 | 789 | |
5039dede | 790 | #ifndef _WIN32 |
35f836fe | 791 | SetDefaultCodepage(m_szCodePage); |
5039dede AK |
792 | #endif |
793 | ||
794 | // Connect to database | |
b8c1ec69 | 795 | if (!DBInit(0, 0)) |
5039dede | 796 | { |
9f24efb3 | 797 | _tprintf(_T("Unable to initialize database library\n")); |
5039dede AK |
798 | return 3; |
799 | } | |
800 | ||
63604cda VK |
801 | s_driver = DBLoadDriver(s_dbDriver, s_dbDrvParams, false, NULL, NULL); |
802 | if (s_driver == NULL) | |
b8c1ec69 VK |
803 | { |
804 | _tprintf(_T("Unable to load and initialize database driver \"%s\"\n"), s_dbDriver); | |
805 | return 3; | |
806 | } | |
807 | ||
465b3f2d | 808 | TCHAR errorText[DBDRV_MAX_ERROR_TEXT]; |
63604cda | 809 | g_hCoreDB = ConnectToDatabase(); |
5039dede AK |
810 | if (g_hCoreDB == NULL) |
811 | { | |
63604cda | 812 | DBUnloadDriver(s_driver); |
5039dede AK |
813 | return 4; |
814 | } | |
815 | ||
816 | if (!strcmp(argv[optind], "init")) | |
817 | { | |
818 | InitDatabase(argv[optind + 1]); | |
819 | } | |
820 | else | |
821 | { | |
822 | // Get database syntax | |
2a964810 VK |
823 | g_dbSyntax = DBGetSyntax(g_hCoreDB); |
824 | if (g_dbSyntax == DB_SYNTAX_UNKNOWN) | |
28f5b9a4 | 825 | { |
5039dede AK |
826 | _tprintf(_T("Unable to determine database syntax\n")); |
827 | DBDisconnect(g_hCoreDB); | |
63604cda | 828 | DBUnloadDriver(s_driver); |
5039dede AK |
829 | return 5; |
830 | } | |
831 | ||
5039dede AK |
832 | // Do requested operation |
833 | if (!strcmp(argv[optind], "batch")) | |
daf3c104 | 834 | { |
5039dede | 835 | ExecSQLBatch(argv[optind + 1]); |
daf3c104 | 836 | } |
5039dede | 837 | else if (!strcmp(argv[optind], "check")) |
daf3c104 VK |
838 | { |
839 | CheckDatabase(); | |
840 | } | |
841 | else if (!strcmp(argv[optind], "check-data-tables")) | |
842 | { | |
843 | g_checkDataTablesOnly = true; | |
5039dede | 844 | CheckDatabase(); |
daf3c104 | 845 | } |
5039dede | 846 | else if (!strcmp(argv[optind], "upgrade")) |
daf3c104 | 847 | { |
5039dede | 848 | UpgradeDatabase(); |
daf3c104 | 849 | } |
5039dede | 850 | else if (!strcmp(argv[optind], "unlock")) |
daf3c104 | 851 | { |
5039dede | 852 | UnlockDatabase(); |
daf3c104 | 853 | } |
1e558daf | 854 | else if (!strcmp(argv[optind], "export")) |
daf3c104 | 855 | { |
4f23eecc | 856 | ExportDatabase(argv[optind + 1]); |
daf3c104 | 857 | } |
890a0930 | 858 | else if (!strcmp(argv[optind], "import")) |
daf3c104 | 859 | { |
890a0930 | 860 | ImportDatabase(argv[optind + 1]); |
daf3c104 | 861 | } |
84ee0f9c VK |
862 | else if (!strcmp(argv[optind], "migrate")) |
863 | { | |
864 | #ifdef UNICODE | |
865 | WCHAR *sourceConfig = WideStringFromMBString(argv[optind + 1]); | |
866 | #else | |
867 | char *sourceConfig = argv[optind + 1]; | |
868 | #endif | |
869 | MigrateDatabase(sourceConfig); | |
870 | #ifdef UNICODE | |
871 | free(sourceConfig); | |
872 | #endif | |
873 | } | |
a43a5e7d VK |
874 | else if (!strcmp(argv[optind], "get")) |
875 | { | |
876 | #ifdef UNICODE | |
877 | WCHAR *var = WideStringFromMBString(argv[optind + 1]); | |
878 | #else | |
879 | char *var = argv[optind + 1]; | |
880 | #endif | |
2589cc10 | 881 | TCHAR buffer[MAX_CONFIG_VALUE]; |
882 | ConfigReadStr(var, buffer, MAX_CONFIG_VALUE, _T("")); | |
a43a5e7d VK |
883 | _tprintf(_T("%s\n"), buffer); |
884 | #ifdef UNICODE | |
885 | free(var); | |
886 | #endif | |
887 | } | |
888 | else if (!strcmp(argv[optind], "set")) | |
889 | { | |
890 | #ifdef UNICODE | |
891 | WCHAR *var = WideStringFromMBString(argv[optind + 1]); | |
892 | WCHAR *value = WideStringFromMBString(argv[optind + 2]); | |
893 | #else | |
894 | char *var = argv[optind + 1]; | |
895 | char *value = argv[optind + 2]; | |
896 | #endif | |
c85c8ef2 | 897 | CreateConfigParam(var, value, true, false, replaceValue); |
a43a5e7d VK |
898 | #ifdef UNICODE |
899 | free(var); | |
900 | free(value); | |
901 | #endif | |
902 | } | |
c614ca60 AK |
903 | else if (!strcmp(argv[optind], "resetadmin")) |
904 | { | |
905 | ResetAdmin(); | |
906 | } | |
5039dede AK |
907 | } |
908 | ||
909 | // Shutdown | |
910 | DBDisconnect(g_hCoreDB); | |
63604cda | 911 | DBUnloadDriver(s_driver); |
5039dede AK |
912 | return 0; |
913 | } |