Subversion Repositories oidplus

Rev

Rev 1170 | Rev 1226 | Go to most recent revision | 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
 
43
# Test MySQL
502 daniel-mar 44
try {
45
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'MySQL');
46
	OIDplus::init(true, true);
47
	$db = OIDplus::db();
48
	OIDplus::init(true, true);
49
	if (function_exists('mysqli_fetch_all')) {
50
		OIDplus::baseConfig()->setValue('MYSQL_FORCE_MYSQLND_SUPPLEMENT', false);
51
		echo "[With MySQLnd support] ";
52
		dotest($db);
53
		OIDplus::baseConfig()->setValue('MYSQL_FORCE_MYSQLND_SUPPLEMENT', true);
54
	}
55
	echo "[Without MySQLnd support] ";
374 daniel-mar 56
	dotest($db);
502 daniel-mar 57
} catch (Exception $e) {
58
	echo "MySQL:\n";
59
	echo redtext($e->getMessage())."\n\n";
374 daniel-mar 60
}
61
 
62
# Test PDO
502 daniel-mar 63
try {
64
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'PDO');
65
	OIDplus::init(true, true);
66
	$db = OIDplus::db();
67
	dotest($db);
68
} catch (Exception $e) {
69
	echo "PDO:\n";
70
	echo redtext($e->getMessage())."\n\n";
71
}
374 daniel-mar 72
 
73
# Test ODBC
502 daniel-mar 74
try {
75
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'ODBC');
76
	OIDplus::init(true, true);
77
	$db = OIDplus::db();
78
	dotest($db);
79
} catch (Exception $e) {
80
	echo "ODBC:\n";
81
	echo redtext($e->getMessage())."\n\n";
82
}
374 daniel-mar 83
 
1219 daniel-mar 84
# Test ADO
85
try {
86
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'ADO');
87
	OIDplus::init(true, true);
88
	$db = OIDplus::db();
89
	dotest($db);
90
} catch (Exception $e) {
91
	echo "ADO:\n";
92
	echo redtext($e->getMessage())."\n\n";
93
}
94
 
374 daniel-mar 95
# Test PgSQL
502 daniel-mar 96
try {
97
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'PgSQL');
98
	OIDplus::init(true, true);
99
	$db = OIDplus::db();
100
	dotest($db);
101
} catch (Exception $e) {
102
	echo "PgSQL:\n";
103
	echo redtext($e->getMessage())."\n\n";
104
}
374 daniel-mar 105
 
106
# Test SQLite3
502 daniel-mar 107
try {
1160 daniel-mar 108
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'SQLite3');
502 daniel-mar 109
	OIDplus::init(true, true);
110
	$db = OIDplus::db();
111
	dotest($db);
112
} catch (Exception $e) {
113
	echo "SQLite3:\n";
114
	echo redtext($e->getMessage())."\n\n";
115
}
374 daniel-mar 116
 
786 daniel-mar 117
# Test OCI8
118
try {
1160 daniel-mar 119
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'Oracle (OCI8)');
786 daniel-mar 120
	OIDplus::init(true, true);
121
	$db = OIDplus::db();
122
	dotest($db);
123
} catch (Exception $e) {
124
	echo "OCI8:\n";
125
	echo redtext($e->getMessage())."\n\n";
126
}
127
 
374 daniel-mar 128
# ---
129
 
502 daniel-mar 130
if (PHP_SAPI != 'cli') {
131
	die('</pre>');
132
} else {
133
	exit($num_errs > 0 ? 1 : 0);
134
}
374 daniel-mar 135
 
136
# ---
137
 
1130 daniel-mar 138
/**
1155 daniel-mar 139
 * @param OIDplusDatabaseConnection $db
1130 daniel-mar 140
 * @return void
141
 */
1155 daniel-mar 142
function dotest(OIDplusDatabaseConnection $db) {
374 daniel-mar 143
	echo "Database: " . get_class($db) . "\n";
144
	try {
145
		$db->connect();
146
	} catch (Exception $e) {
147
		echo "Connection ".redtext("FAILED")." (check userdata/baseconfig/config.inc.php): ".$e->getMessage()."\n\n";
148
		return;
149
	}
150
	echo "Detected slang: " . $db->getSlang()::id()."\n";
151
	$db->query("delete from ###objects where parent = 'test:1'");
152
	$db->query("insert into ###objects (id, parent, title, description, confidential) values ('test:1.1', 'test:1', '', '', '0')");
153
	$db->query("insert into ###objects (id, parent, title, description, confidential) values ('test:1.2', 'test:1', '', '', '0')");
154
	try {
155
		// --- "SQL Date" handling
156
 
157
		try {
158
			$res = $db->query("update ###objects set created = ".$db->sqlDate()." where id = 'test:1.1'");
159
			echo "SQLDate (".$db->sqlDate().') '.greentext('PASSED')."\n";
160
		} catch (Exception $e) {
161
			echo "SQLDate (".$db->sqlDate().') '.redtext('FAILED')."\n";
162
		}
163
 
786 daniel-mar 164
		// --- Delete+Insert using prepared statements
165
 
166
		$db->query("delete from ###objects where parent = ?", array('test:11'));
167
		echo "Delete using Prepared Statements: ".greentext('PASSED')."\n";
168
		$db->query("insert into ###objects (id, parent, title, description, confidential) values (?, ?, ?, ?, ?)", array('oid:3.1.11', 'test:11', '', '', '0'));
169
		$db->query("insert into ###objects (id, parent, title, description, confidential) values (?, ?, ?, ?, ?)", array('oid:3.1.12', 'test:11', '', '', '0'));
170
		echo "Insert using Prepared Statements: ".greentext('PASSED')."\n";
171
		$db->query("update ###objects set confidential = ? where parent = ?", array(true, 'test:11'));
172
		echo "Update using Prepared Statements: ".greentext('PASSED')."\n";
173
 
374 daniel-mar 174
		// --- "Num rows" handling
783 daniel-mar 175
 
502 daniel-mar 176
		$res = $db->query("select id from ###objects where parent = ? order by id", array('test:XXXXXXXXX'));
177
		try {
178
			$num_rows = $res->num_rows();
179
		} catch (Exception $e) {
180
			$num_rows = $e->getMessage();
181
		}
182
		echo "Num rows empty: " . ($num_rows===0 ? greentext('PASSED') : redtext('FAILED'))." ($num_rows)\n";
374 daniel-mar 183
 
184
		$res = $db->query("select id from ###objects where parent = ? order by id", array('test:1'));
502 daniel-mar 185
		try {
186
			$num_rows = $res->num_rows();
187
		} catch (Exception $e) {
188
			$num_rows = $e->getMessage();
189
		}
190
		echo "Num rows: " . ($num_rows===2 ? greentext('PASSED') : redtext('FAILED'))." ($num_rows)\n";
374 daniel-mar 191
 
192
		$res->fetch_array();
502 daniel-mar 193
		try {
194
			$num_rows = $res->num_rows();
195
		} catch (Exception $e) {
196
			$num_rows = $e->getMessage();
197
		}
198
		echo "Num rows after something fetched: " . ($num_rows===2 ? greentext('PASSED') : redtext('FAILED'))." ($num_rows)\n";
374 daniel-mar 199
 
200
		$nextid = $res->fetch_array()['id'];
201
		echo "Num rows does not change cursor: " . ($nextid == 'test:1.2' ? greentext('PASSED') : redtext('FAILED'))."\n";
202
 
203
		$next = $res->fetch_array();
204
		echo "Fetch after EOF gives null: " . (is_null($next) ? greentext('PASSED') : redtext('FAILED'))."\n";
205
 
206
		// --- Simultanous prepared statements
783 daniel-mar 207
 
502 daniel-mar 208
		$errmsg = '';
209
		try {
210
			$res = $db->query("select id from ###objects where parent = ? order by id", array('test:1'));
211
			$passed = false;
212
			//$res->num_rows(); // sponge
213
			while ($row = $res->fetch_array()) {
214
				$res2 = $db->query("select id from ###objects where parent = ? order by id", array($row['id']));
215
				while ($row2 = $res2->fetch_array()) {
216
				}
217
				if ($row['id'] == 'test:1.2') {
218
					$passed = true;
219
				}
374 daniel-mar 220
			}
502 daniel-mar 221
		} catch (Exception $e) {
222
			$passed = false;
223
			$errmsg = ' ('.$e->getMessage().')';
374 daniel-mar 224
		}
502 daniel-mar 225
		echo "Simultanous prepared statements: ".($passed ? greentext('PASSED') : redtext('FAILED'))."$errmsg\n";
783 daniel-mar 226
 
502 daniel-mar 227
		if (!$passed) {
228
			// We cannot continue because the connection is in a state that cannot be recovered easily
229
			echo "Cancelled!\n\n";
230
			return;
231
		}
783 daniel-mar 232
 
374 daniel-mar 233
		// --- Exception handling
234
 
235
		try {
502 daniel-mar 236
			$db->query("SELECT * from ABCDEF");
374 daniel-mar 237
			echo "Exception for DirectQuery: ".redtext('FAILED').", no Exception thrown\n";
238
		} catch (Exception $e) {
1155 daniel-mar 239
			if ((stripos($e->getMessage(), 'ABCDEF') !== false) || is_known_errormsg($e->getMessage())) {
374 daniel-mar 240
				echo "Exception for DirectQuery: ".greentext('PASSED')."\n";
241
			} else {
1155 daniel-mar 242
				echo "Exception for DirectQuery: ".redtext('FAILED').", does probably not contain DBMS error string (".$e->getMessage().")\n";
374 daniel-mar 243
			}
244
		}
245
 
246
		$msg = $db->error();
1155 daniel-mar 247
		if ((stripos($msg, 'ABCDEF') !== false) || is_known_errormsg($msg)) {
374 daniel-mar 248
			echo "Error-Function after failed direct query: ".greentext('PASSED')."\n";
249
		} else {
250
			echo "Error-Function after failed direct query: ".redtext('FAILED').", does probably not contain DBMS error string ($msg)\n";
251
		}
252
 
253
		try {
1160 daniel-mar 254
			$db->query("SELECT * from FEDCBA", array());
374 daniel-mar 255
			echo "Exception for PreparedQuery: ".redtext('FAILED').", no Exception thrown\n";
256
		} catch (Exception $e) {
1155 daniel-mar 257
			if ((stripos($e->getMessage(), 'FEDCBA') !== false) || is_known_errormsg($e->getMessage())) {
374 daniel-mar 258
				echo "Exception for PreparedQuery: ".greentext('PASSED')."\n";
259
			} else {
1155 daniel-mar 260
				echo "Exception for PreparedQuery: ".redtext('FAILED').", does probably not contain DBMS error string (".$e->getMessage().")\n";
374 daniel-mar 261
			}
262
		}
263
 
264
		$msg = $db->error();
1155 daniel-mar 265
		if ((stripos($msg, 'FEDCBA') !== false) || is_known_errormsg($msg)) {
374 daniel-mar 266
			echo "Error-Function after failed prepared query: ".greentext('PASSED')."\n";
267
		} else {
268
			echo "Error-Function after failed prepared query: ".redtext('FAILED').", does probably not contain DBMS error string ($msg)\n";
269
		}
270
 
271
		$db->query("select 1");
272
		$msg = $db->error();
273
		if (!$msg) {
274
			echo "Error-Function gets cleared after non-failed query: ".greentext('PASSED')."\n";
275
		} else {
276
			echo "Error-Function gets cleared after non-failed query: ".redtext('FAILED').", does probably not contain DBMS error string\n";
277
		}
278
 
279
		// --- Boolean handling
280
 
281
		$db->query("update ###objects set confidential = ? where id = 'test:1.1'", array(true));
282
		$res = $db->query("select confidential from ###objects where id = 'test:1.1'");
283
		$val = $res->fetch_object()->confidential;
502 daniel-mar 284
		echo "Boolean handling TRUE with prepared statement (fetch): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
285
		$res = $db->query("select * from ###objects where id = 'test:1.1' and confidential = ?", array(true));
286
		$val = $res->fetch_object();
287
		echo "Boolean handling TRUE with prepared statement (where): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
374 daniel-mar 288
 
289
		$db->query("update ###objects set confidential = ? where id = 'test:1.1'", array(false));
290
		$res = $db->query("select confidential from ###objects where id = 'test:1.1'");
291
		$val = $res->fetch_object()->confidential;
502 daniel-mar 292
		echo "Boolean handling FALSE with prepared statement (fetch): " . (!$val ? greentext('PASSED') : redtext('FAILED'))."\n";
293
		$res = $db->query("select * from ###objects where id = 'test:1.1' and confidential = ?", array(false));
294
		$val = $res->fetch_object();
295
		echo "Boolean handling FALSE with prepared statement (where): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
374 daniel-mar 296
 
502 daniel-mar 297
		$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 298
		$res = $db->query("select confidential from ###objects where id = 'test:1.1'");
299
		$val = $res->fetch_object()->confidential;
502 daniel-mar 300
		echo "Boolean handling TRUE with normal statement (fetch): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
1170 daniel-mar 301
		if ($db->getSlang()::id() == 'access') {
302
			// Note: For Access, it must be 1, not '1'
303
			$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
304
		} else {
305
			// Note: For PgSQL, it must be '1', true, 'true', 't', but not 1
306
			$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
307
		}
502 daniel-mar 308
		$val = $res->fetch_object();
309
		echo "Boolean handling TRUE with normal statement (where): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
374 daniel-mar 310
 
502 daniel-mar 311
		$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 312
		$res = $db->query("select confidential from ###objects where id = 'test:1.1'");
313
		$val = $res->fetch_object()->confidential;
502 daniel-mar 314
		echo "Boolean handling FALSE with normal statement (fetch): " . (!$val ? greentext('PASSED') : redtext('FAILED'))."\n";
1170 daniel-mar 315
		if ($db->getSlang()::id() == 'access') {
316
			// Note: For Access, it must be 0, not '0'
317
			$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
318
		} else {
319
			// Note: For PgSQL, it must be '0', false, 'false', 'f', but not 0
320
			$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
321
		}
502 daniel-mar 322
		$val = $res->fetch_object();
323
		echo "Boolean handling FALSE with normal statement (where): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
783 daniel-mar 324
 
374 daniel-mar 325
		// --- Check if transactions work
783 daniel-mar 326
 
502 daniel-mar 327
		if ($db->transaction_supported()) {
328
			echo "Transaction support: " . greentext('Supported') ."\n";
374 daniel-mar 329
 
502 daniel-mar 330
			$db->query("update ###objects set title = 'A' where id = 'test:1.1'");
331
			$db->transaction_begin();
332
			$db->query("update ###objects set title = 'B' where id = 'test:1.1'");
333
			$db->transaction_rollback();
334
			$res = $db->query("select title from ###objects where id = 'test:1.1'");
335
			$val = $res->fetch_object()->title;
336
			echo "Transaction rollback: " . ($val == 'A' ? greentext('PASSED') : redtext('FAILED'))."\n";
374 daniel-mar 337
 
502 daniel-mar 338
			$db->query("update ###objects set title = 'A' where id = 'test:1.1'");
339
			$db->transaction_begin();
340
			$db->query("update ###objects set title = 'B' where id = 'test:1.1'");
341
			$db->transaction_commit();
342
			$res = $db->query("select title from ###objects where id = 'test:1.1'");
343
			$val = $res->fetch_object()->title;
344
			echo "Transaction commit: " . ($val == 'B' ? greentext('PASSED') : redtext('FAILED'))."\n";
345
		} else {
346
			echo "Transaction support: " . redtext('Not supported') ."\n";
347
		}
374 daniel-mar 348
 
349
		// --- Test insert_id()
350
 
351
		$db->query("delete from ###log_object where object = 'test:1'");
352
		$cur = $db->insert_id();
353
		echo "Insert ID on non-insert: " . ($cur == 0 ? greentext('PASSED') : redtext('FAILED'))." ($cur)\n";
502 daniel-mar 354
		$db->query("insert into ###log_object (log_id, severity, object) values (1000, 0, 'test:1')");
374 daniel-mar 355
		$prev = $db->insert_id();
502 daniel-mar 356
		$db->query("insert into ###log_object (log_id, severity, object) values (2000, 1, 'test:1')");
374 daniel-mar 357
		$cur = $db->insert_id();
358
		echo "Insert ID on actual inserts: " . ($cur == $prev+1 ? greentext('PASSED') : redtext('FAILED'))." ($prev => $cur)\n";
359
		if ($cur != $prev+1);
360
		$db->query("delete from ###log_object where object = 'test:1'");
361
		$cur = $db->insert_id();
362
		echo "Non-Insert query will reset insert ID: " . ($cur == 0 ? greentext('PASSED') : redtext('FAILED'))." ($cur)\n";
363
 
364
	} finally {
502 daniel-mar 365
		try {
366
			$db->query("delete from ###objects where parent = 'test:1'");
367
		}catch(Exception $e) {}
374 daniel-mar 368
	}
369
	$db->disconnect();
370
	echo "\n";
371
}
372
 
1130 daniel-mar 373
/**
374
 * @param string $str
375
 * @return string
376
 */
377
function redtext(string $str): string {
374 daniel-mar 378
	global $num_errs;
379
	$num_errs++;
502 daniel-mar 380
	return PHP_SAPI == 'cli' ? "\033[31m$str\033[0m" : '<font color="red">'.$str.'</font>';
374 daniel-mar 381
}
382
 
1130 daniel-mar 383
/**
384
 * @param string $str
385
 * @return string
386
 */
387
function greentext(string $str): string {
374 daniel-mar 388
	global $num_succ;
389
	$num_succ++;
502 daniel-mar 390
	return PHP_SAPI == 'cli' ? "\033[32m$str\033[0m" : '<font color="green">'.$str.'</font>';
374 daniel-mar 391
}
783 daniel-mar 392
 
1130 daniel-mar 393
/**
394
 * @param string $msg
395
 * @return bool
396
 */
397
function is_known_errormsg(string $msg): bool {
783 daniel-mar 398
	// Oracle:
399
	//Error-Function after failed direct query:
400
	//	==> OCIStmtExecute: ORA-00942: table or view does not exist  (ext\pdo_oci\oci_statement.c:155)
401
	//Error-Function after failed prepared query:
402
	//	==> OCIBindByPos: ORA-01036: illegal variable name/number (ext\pdo_oci\oci_statement.c:346)
1130 daniel-mar 403
	return strpos($msg,'ORA-') !== false;
783 daniel-mar 404
}