Subversion Repositories oidplus

Rev

Rev 1235 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
557 daniel-mar 1
#!/usr/bin/env php
374 daniel-mar 2
<?php
3
 
4
/*
5
 * OIDplus 2.0
783 daniel-mar 6
 * Copyright 2019 - 2022 Daniel Marschall, ViaThinkSoft
374 daniel-mar 7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
 
1155 daniel-mar 21
// This script is used to detect problems with your database plugins
374 daniel-mar 22
// ATTENTION: PLEASE DO NOT USE THIS SCRIPT ON A PRODUCTIVE DATABASE,
23
// BECAUSE IT ADDS AND CHANGES DATA DURING THE TESTING.
24
 
25
# ---
26
 
786 daniel-mar 27
// TODO: UTF-8 tests
28
 
374 daniel-mar 29
require_once __DIR__ . '/../includes/oidplus.inc.php';
30
 
1155 daniel-mar 31
use ViaThinkSoft\OIDplus\OIDplus;
32
use ViaThinkSoft\OIDplus\OIDplusDatabaseConnection;
33
 
374 daniel-mar 34
$num_errs = 0;
35
$num_succ = 0;
36
 
502 daniel-mar 37
if (PHP_SAPI != 'cli') echo '<pre>';
38
 
374 daniel-mar 39
echo "OIDplus Database plugin testcases\n\n";
40
 
41
OIDplus::init(true, true);
42
 
1238 daniel-mar 43
OIDplus::baseConfig()->delete('FORCE_DBMS_SLANG');
1226 daniel-mar 44
 
374 daniel-mar 45
# Test MySQL
502 daniel-mar 46
try {
47
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'MySQL');
48
	OIDplus::init(true, true);
49
	$db = OIDplus::db();
50
	OIDplus::init(true, true);
51
	if (function_exists('mysqli_fetch_all')) {
52
		OIDplus::baseConfig()->setValue('MYSQL_FORCE_MYSQLND_SUPPLEMENT', false);
53
		echo "[With MySQLnd support] ";
54
		dotest($db);
55
		OIDplus::baseConfig()->setValue('MYSQL_FORCE_MYSQLND_SUPPLEMENT', true);
56
	}
57
	echo "[Without MySQLnd support] ";
374 daniel-mar 58
	dotest($db);
502 daniel-mar 59
} catch (Exception $e) {
60
	echo "MySQL:\n";
1226 daniel-mar 61
	echo redtext($e->getMessage())."\n";
62
	echo redtext($e->getTraceAsString())."\n\n";
374 daniel-mar 63
}
64
 
65
# Test PDO
502 daniel-mar 66
try {
67
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'PDO');
68
	OIDplus::init(true, true);
69
	$db = OIDplus::db();
70
	dotest($db);
71
} catch (Exception $e) {
72
	echo "PDO:\n";
1226 daniel-mar 73
	echo redtext($e->getMessage())."\n";
74
	echo redtext($e->getTraceAsString())."\n\n";
502 daniel-mar 75
}
374 daniel-mar 76
 
77
# Test ODBC
502 daniel-mar 78
try {
79
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'ODBC');
80
	OIDplus::init(true, true);
81
	$db = OIDplus::db();
82
	dotest($db);
83
} catch (Exception $e) {
84
	echo "ODBC:\n";
1226 daniel-mar 85
	echo redtext($e->getMessage())."\n";
86
	echo redtext($e->getTraceAsString())."\n\n";
502 daniel-mar 87
}
374 daniel-mar 88
 
1219 daniel-mar 89
# Test ADO
90
try {
91
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'ADO');
92
	OIDplus::init(true, true);
93
	$db = OIDplus::db();
94
	dotest($db);
95
} catch (Exception $e) {
96
	echo "ADO:\n";
1226 daniel-mar 97
	echo redtext($e->getMessage())."\n";
98
	echo redtext($e->getTraceAsString())."\n\n";
1219 daniel-mar 99
}
100
 
1232 daniel-mar 101
# Test SQLSRV
102
try {
103
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'SQLSRV');
104
	OIDplus::init(true, true);
105
	$db = OIDplus::db();
106
	dotest($db);
107
} catch (Exception $e) {
108
	echo "ADO:\n";
109
	echo redtext($e->getMessage())."\n";
110
	echo redtext($e->getTraceAsString())."\n\n";
111
}
112
 
374 daniel-mar 113
# Test PgSQL
502 daniel-mar 114
try {
115
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'PgSQL');
116
	OIDplus::init(true, true);
117
	$db = OIDplus::db();
118
	dotest($db);
119
} catch (Exception $e) {
120
	echo "PgSQL:\n";
1226 daniel-mar 121
	echo redtext($e->getMessage())."\n";
122
	echo redtext($e->getTraceAsString())."\n\n";
502 daniel-mar 123
}
374 daniel-mar 124
 
125
# Test SQLite3
502 daniel-mar 126
try {
1160 daniel-mar 127
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'SQLite3');
502 daniel-mar 128
	OIDplus::init(true, true);
129
	$db = OIDplus::db();
130
	dotest($db);
131
} catch (Exception $e) {
132
	echo "SQLite3:\n";
1226 daniel-mar 133
	echo redtext($e->getMessage())."\n";
134
	echo redtext($e->getTraceAsString())."\n\n";
502 daniel-mar 135
}
374 daniel-mar 136
 
786 daniel-mar 137
# Test OCI8
138
try {
1160 daniel-mar 139
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'Oracle (OCI8)');
786 daniel-mar 140
	OIDplus::init(true, true);
141
	$db = OIDplus::db();
142
	dotest($db);
143
} catch (Exception $e) {
144
	echo "OCI8:\n";
1226 daniel-mar 145
	echo redtext($e->getMessage())."\n";
146
	echo redtext($e->getTraceAsString())."\n\n";
786 daniel-mar 147
}
148
 
374 daniel-mar 149
# ---
150
 
502 daniel-mar 151
if (PHP_SAPI != 'cli') {
152
	die('</pre>');
153
} else {
154
	exit($num_errs > 0 ? 1 : 0);
155
}
374 daniel-mar 156
 
157
# ---
158
 
1130 daniel-mar 159
/**
1155 daniel-mar 160
 * @param OIDplusDatabaseConnection $db
1130 daniel-mar 161
 * @return void
162
 */
1155 daniel-mar 163
function dotest(OIDplusDatabaseConnection $db) {
374 daniel-mar 164
	echo "Database: " . get_class($db) . "\n";
165
	try {
166
		$db->connect();
167
	} catch (Exception $e) {
168
		echo "Connection ".redtext("FAILED")." (check userdata/baseconfig/config.inc.php): ".$e->getMessage()."\n\n";
169
		return;
170
	}
171
	echo "Detected slang: " . $db->getSlang()::id()."\n";
172
	$db->query("delete from ###objects where parent = 'test:1'");
173
	$db->query("insert into ###objects (id, parent, title, description, confidential) values ('test:1.1', 'test:1', '', '', '0')");
174
	$db->query("insert into ###objects (id, parent, title, description, confidential) values ('test:1.2', 'test:1', '', '', '0')");
175
	try {
176
		// --- "SQL Date" handling
177
 
178
		try {
179
			$res = $db->query("update ###objects set created = ".$db->sqlDate()." where id = 'test:1.1'");
180
			echo "SQLDate (".$db->sqlDate().') '.greentext('PASSED')."\n";
181
		} catch (Exception $e) {
182
			echo "SQLDate (".$db->sqlDate().') '.redtext('FAILED')."\n";
183
		}
184
 
786 daniel-mar 185
		// --- Delete+Insert using prepared statements
186
 
187
		$db->query("delete from ###objects where parent = ?", array('test:11'));
188
		echo "Delete using Prepared Statements: ".greentext('PASSED')."\n";
189
		$db->query("insert into ###objects (id, parent, title, description, confidential) values (?, ?, ?, ?, ?)", array('oid:3.1.11', 'test:11', '', '', '0'));
190
		$db->query("insert into ###objects (id, parent, title, description, confidential) values (?, ?, ?, ?, ?)", array('oid:3.1.12', 'test:11', '', '', '0'));
191
		echo "Insert using Prepared Statements: ".greentext('PASSED')."\n";
192
		$db->query("update ###objects set confidential = ? where parent = ?", array(true, 'test:11'));
193
		echo "Update using Prepared Statements: ".greentext('PASSED')."\n";
194
 
374 daniel-mar 195
		// --- "Num rows" handling
783 daniel-mar 196
 
502 daniel-mar 197
		$res = $db->query("select id from ###objects where parent = ? order by id", array('test:XXXXXXXXX'));
198
		try {
199
			$num_rows = $res->num_rows();
200
		} catch (Exception $e) {
201
			$num_rows = $e->getMessage();
202
		}
203
		echo "Num rows empty: " . ($num_rows===0 ? greentext('PASSED') : redtext('FAILED'))." ($num_rows)\n";
374 daniel-mar 204
 
205
		$res = $db->query("select id from ###objects where parent = ? order by id", array('test:1'));
502 daniel-mar 206
		try {
207
			$num_rows = $res->num_rows();
208
		} catch (Exception $e) {
209
			$num_rows = $e->getMessage();
210
		}
211
		echo "Num rows: " . ($num_rows===2 ? greentext('PASSED') : redtext('FAILED'))." ($num_rows)\n";
374 daniel-mar 212
 
213
		$res->fetch_array();
502 daniel-mar 214
		try {
215
			$num_rows = $res->num_rows();
216
		} catch (Exception $e) {
217
			$num_rows = $e->getMessage();
218
		}
219
		echo "Num rows after something fetched: " . ($num_rows===2 ? greentext('PASSED') : redtext('FAILED'))." ($num_rows)\n";
374 daniel-mar 220
 
221
		$nextid = $res->fetch_array()['id'];
222
		echo "Num rows does not change cursor: " . ($nextid == 'test:1.2' ? greentext('PASSED') : redtext('FAILED'))."\n";
223
 
224
		$next = $res->fetch_array();
225
		echo "Fetch after EOF gives null: " . (is_null($next) ? greentext('PASSED') : redtext('FAILED'))."\n";
226
 
227
		// --- Simultanous prepared statements
783 daniel-mar 228
 
502 daniel-mar 229
		$errmsg = '';
230
		try {
231
			$res = $db->query("select id from ###objects where parent = ? order by id", array('test:1'));
232
			$passed = false;
233
			//$res->num_rows(); // sponge
234
			while ($row = $res->fetch_array()) {
235
				$res2 = $db->query("select id from ###objects where parent = ? order by id", array($row['id']));
236
				while ($row2 = $res2->fetch_array()) {
237
				}
238
				if ($row['id'] == 'test:1.2') {
239
					$passed = true;
240
				}
374 daniel-mar 241
			}
502 daniel-mar 242
		} catch (Exception $e) {
243
			$passed = false;
244
			$errmsg = ' ('.$e->getMessage().')';
374 daniel-mar 245
		}
502 daniel-mar 246
		echo "Simultanous prepared statements: ".($passed ? greentext('PASSED') : redtext('FAILED'))."$errmsg\n";
783 daniel-mar 247
 
502 daniel-mar 248
		if (!$passed) {
249
			// We cannot continue because the connection is in a state that cannot be recovered easily
250
			echo "Cancelled!\n\n";
251
			return;
252
		}
783 daniel-mar 253
 
374 daniel-mar 254
		// --- Exception handling
255
 
256
		try {
502 daniel-mar 257
			$db->query("SELECT * from ABCDEF");
374 daniel-mar 258
			echo "Exception for DirectQuery: ".redtext('FAILED').", no Exception thrown\n";
259
		} catch (Exception $e) {
1155 daniel-mar 260
			if ((stripos($e->getMessage(), 'ABCDEF') !== false) || is_known_errormsg($e->getMessage())) {
374 daniel-mar 261
				echo "Exception for DirectQuery: ".greentext('PASSED')."\n";
262
			} else {
1155 daniel-mar 263
				echo "Exception for DirectQuery: ".redtext('FAILED').", does probably not contain DBMS error string (".$e->getMessage().")\n";
374 daniel-mar 264
			}
265
		}
266
 
267
		$msg = $db->error();
1155 daniel-mar 268
		if ((stripos($msg, 'ABCDEF') !== false) || is_known_errormsg($msg)) {
374 daniel-mar 269
			echo "Error-Function after failed direct query: ".greentext('PASSED')."\n";
270
		} else {
271
			echo "Error-Function after failed direct query: ".redtext('FAILED').", does probably not contain DBMS error string ($msg)\n";
272
		}
273
 
274
		try {
1160 daniel-mar 275
			$db->query("SELECT * from FEDCBA", array());
374 daniel-mar 276
			echo "Exception for PreparedQuery: ".redtext('FAILED').", no Exception thrown\n";
277
		} catch (Exception $e) {
1155 daniel-mar 278
			if ((stripos($e->getMessage(), 'FEDCBA') !== false) || is_known_errormsg($e->getMessage())) {
374 daniel-mar 279
				echo "Exception for PreparedQuery: ".greentext('PASSED')."\n";
280
			} else {
1155 daniel-mar 281
				echo "Exception for PreparedQuery: ".redtext('FAILED').", does probably not contain DBMS error string (".$e->getMessage().")\n";
374 daniel-mar 282
			}
283
		}
284
 
285
		$msg = $db->error();
1155 daniel-mar 286
		if ((stripos($msg, 'FEDCBA') !== false) || is_known_errormsg($msg)) {
374 daniel-mar 287
			echo "Error-Function after failed prepared query: ".greentext('PASSED')."\n";
288
		} else {
289
			echo "Error-Function after failed prepared query: ".redtext('FAILED').", does probably not contain DBMS error string ($msg)\n";
290
		}
291
 
292
		$db->query("select 1");
293
		$msg = $db->error();
294
		if (!$msg) {
295
			echo "Error-Function gets cleared after non-failed query: ".greentext('PASSED')."\n";
296
		} else {
297
			echo "Error-Function gets cleared after non-failed query: ".redtext('FAILED').", does probably not contain DBMS error string\n";
298
		}
299
 
300
		// --- Boolean handling
301
 
302
		$db->query("update ###objects set confidential = ? where id = 'test:1.1'", array(true));
303
		$res = $db->query("select confidential from ###objects where id = 'test:1.1'");
304
		$val = $res->fetch_object()->confidential;
502 daniel-mar 305
		echo "Boolean handling TRUE with prepared statement (fetch): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
306
		$res = $db->query("select * from ###objects where id = 'test:1.1' and confidential = ?", array(true));
307
		$val = $res->fetch_object();
308
		echo "Boolean handling TRUE with prepared statement (where): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
374 daniel-mar 309
 
310
		$db->query("update ###objects set confidential = ? where id = 'test:1.1'", array(false));
311
		$res = $db->query("select confidential from ###objects where id = 'test:1.1'");
312
		$val = $res->fetch_object()->confidential;
502 daniel-mar 313
		echo "Boolean handling FALSE with prepared statement (fetch): " . (!$val ? greentext('PASSED') : redtext('FAILED'))."\n";
314
		$res = $db->query("select * from ###objects where id = 'test:1.1' and confidential = ?", array(false));
315
		$val = $res->fetch_object();
316
		echo "Boolean handling FALSE with prepared statement (where): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
374 daniel-mar 317
 
502 daniel-mar 318
		$db->query("update ###objects set confidential = '1' where id = 'test:1.1'"); // NOTE: DO NOT USE THIS IN THE SOURCE CODE! Always use prepared statements for constant booleans
374 daniel-mar 319
		$res = $db->query("select confidential from ###objects where id = 'test:1.1'");
320
		$val = $res->fetch_object()->confidential;
502 daniel-mar 321
		echo "Boolean handling TRUE with normal statement (fetch): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
1170 daniel-mar 322
		if ($db->getSlang()::id() == 'access') {
323
			// Note: For Access, it must be 1, not '1'
324
			$res = $db->query("select * from ###objects where id = 'test:1.1' and confidential = 1"); // NOTE: DO NOT USE THIS IN THE SOURCE CODE! Always use prepared statements for constant booleans
325
		} else {
326
			// Note: For PgSQL, it must be '1', true, 'true', 't', but not 1
327
			$res = $db->query("select * from ###objects where id = 'test:1.1' and confidential = '1'"); // NOTE: DO NOT USE THIS IN THE SOURCE CODE! Always use prepared statements for constant booleans
328
		}
502 daniel-mar 329
		$val = $res->fetch_object();
330
		echo "Boolean handling TRUE with normal statement (where): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
374 daniel-mar 331
 
502 daniel-mar 332
		$db->query("update ###objects set confidential = '0' where id = 'test:1.1'"); // NOTE: DO NOT USE THIS IN THE SOURCE CODE! Always use prepared statements for constant booleans
374 daniel-mar 333
		$res = $db->query("select confidential from ###objects where id = 'test:1.1'");
334
		$val = $res->fetch_object()->confidential;
502 daniel-mar 335
		echo "Boolean handling FALSE with normal statement (fetch): " . (!$val ? greentext('PASSED') : redtext('FAILED'))."\n";
1170 daniel-mar 336
		if ($db->getSlang()::id() == 'access') {
337
			// Note: For Access, it must be 0, not '0'
338
			$res = $db->query("select * from ###objects where id = 'test:1.1' and confidential = 0"); // NOTE: DO NOT USE THIS IN THE SOURCE CODE! Always use prepared statements for constant booleans
339
		} else {
340
			// Note: For PgSQL, it must be '0', false, 'false', 'f', but not 0
341
			$res = $db->query("select * from ###objects where id = 'test:1.1' and confidential = '0'"); // NOTE: DO NOT USE THIS IN THE SOURCE CODE! Always use prepared statements for constant booleans
342
		}
502 daniel-mar 343
		$val = $res->fetch_object();
344
		echo "Boolean handling FALSE with normal statement (where): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
783 daniel-mar 345
 
374 daniel-mar 346
		// --- Check if transactions work
783 daniel-mar 347
 
502 daniel-mar 348
		if ($db->transaction_supported()) {
349
			echo "Transaction support: " . greentext('Supported') ."\n";
374 daniel-mar 350
 
502 daniel-mar 351
			$db->query("update ###objects set title = 'A' where id = 'test:1.1'");
352
			$db->transaction_begin();
353
			$db->query("update ###objects set title = 'B' where id = 'test:1.1'");
354
			$db->transaction_rollback();
355
			$res = $db->query("select title from ###objects where id = 'test:1.1'");
356
			$val = $res->fetch_object()->title;
357
			echo "Transaction rollback: " . ($val == 'A' ? greentext('PASSED') : redtext('FAILED'))."\n";
374 daniel-mar 358
 
502 daniel-mar 359
			$db->query("update ###objects set title = 'A' where id = 'test:1.1'");
360
			$db->transaction_begin();
361
			$db->query("update ###objects set title = 'B' where id = 'test:1.1'");
362
			$db->transaction_commit();
363
			$res = $db->query("select title from ###objects where id = 'test:1.1'");
364
			$val = $res->fetch_object()->title;
365
			echo "Transaction commit: " . ($val == 'B' ? greentext('PASSED') : redtext('FAILED'))."\n";
366
		} else {
367
			echo "Transaction support: " . redtext('Not supported') ."\n";
368
		}
374 daniel-mar 369
 
370
		// --- Test insert_id()
371
 
372
		$db->query("delete from ###log_object where object = 'test:1'");
373
		$cur = $db->insert_id();
374
		echo "Insert ID on non-insert: " . ($cur == 0 ? greentext('PASSED') : redtext('FAILED'))." ($cur)\n";
502 daniel-mar 375
		$db->query("insert into ###log_object (log_id, severity, object) values (1000, 0, 'test:1')");
374 daniel-mar 376
		$prev = $db->insert_id();
502 daniel-mar 377
		$db->query("insert into ###log_object (log_id, severity, object) values (2000, 1, 'test:1')");
374 daniel-mar 378
		$cur = $db->insert_id();
379
		echo "Insert ID on actual inserts: " . ($cur == $prev+1 ? greentext('PASSED') : redtext('FAILED'))." ($prev => $cur)\n";
380
		if ($cur != $prev+1);
381
		$db->query("delete from ###log_object where object = 'test:1'");
382
		$cur = $db->insert_id();
383
		echo "Non-Insert query will reset insert ID: " . ($cur == 0 ? greentext('PASSED') : redtext('FAILED'))." ($cur)\n";
384
 
385
	} finally {
502 daniel-mar 386
		try {
387
			$db->query("delete from ###objects where parent = 'test:1'");
388
		}catch(Exception $e) {}
374 daniel-mar 389
	}
390
	$db->disconnect();
391
	echo "\n";
392
}
393
 
1130 daniel-mar 394
/**
395
 * @param string $str
396
 * @return string
397
 */
398
function redtext(string $str): string {
374 daniel-mar 399
	global $num_errs;
400
	$num_errs++;
502 daniel-mar 401
	return PHP_SAPI == 'cli' ? "\033[31m$str\033[0m" : '<font color="red">'.$str.'</font>';
374 daniel-mar 402
}
403
 
1130 daniel-mar 404
/**
405
 * @param string $str
406
 * @return string
407
 */
408
function greentext(string $str): string {
374 daniel-mar 409
	global $num_succ;
410
	$num_succ++;
502 daniel-mar 411
	return PHP_SAPI == 'cli' ? "\033[32m$str\033[0m" : '<font color="green">'.$str.'</font>';
374 daniel-mar 412
}
783 daniel-mar 413
 
1130 daniel-mar 414
/**
415
 * @param string $msg
416
 * @return bool
417
 */
418
function is_known_errormsg(string $msg): bool {
783 daniel-mar 419
	// Oracle:
420
	//Error-Function after failed direct query:
421
	//	==> OCIStmtExecute: ORA-00942: table or view does not exist  (ext\pdo_oci\oci_statement.c:155)
422
	//Error-Function after failed prepared query:
423
	//	==> OCIBindByPos: ORA-01036: illegal variable name/number (ext\pdo_oci\oci_statement.c:346)
1130 daniel-mar 424
	return strpos($msg,'ORA-') !== false;
783 daniel-mar 425
}