Subversion Repositories oidplus

Rev

Rev 786 | Rev 1150 | 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
 
21
// This script is used to detect problems with your database plugin
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
 
31
$num_errs = 0;
32
$num_succ = 0;
33
 
502 daniel-mar 34
if (PHP_SAPI != 'cli') echo '<pre>';
35
 
374 daniel-mar 36
echo "OIDplus Database plugin testcases\n\n";
37
 
38
OIDplus::init(true, true);
39
 
40
# Test MySQL
502 daniel-mar 41
try {
42
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'MySQL');
43
	OIDplus::init(true, true);
44
	$db = OIDplus::db();
45
	OIDplus::init(true, true);
46
	if (function_exists('mysqli_fetch_all')) {
47
		OIDplus::baseConfig()->setValue('MYSQL_FORCE_MYSQLND_SUPPLEMENT', false);
48
		echo "[With MySQLnd support] ";
49
		dotest($db);
50
		OIDplus::baseConfig()->setValue('MYSQL_FORCE_MYSQLND_SUPPLEMENT', true);
51
	}
52
	echo "[Without MySQLnd support] ";
374 daniel-mar 53
	dotest($db);
502 daniel-mar 54
} catch (Exception $e) {
55
	echo "MySQL:\n";
56
	echo redtext($e->getMessage())."\n\n";
374 daniel-mar 57
}
58
 
59
# Test PDO
502 daniel-mar 60
try {
61
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'PDO');
62
	OIDplus::init(true, true);
63
	$db = OIDplus::db();
64
	dotest($db);
65
} catch (Exception $e) {
66
	echo "PDO:\n";
67
	echo redtext($e->getMessage())."\n\n";
68
}
374 daniel-mar 69
 
70
# Test ODBC
502 daniel-mar 71
try {
72
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'ODBC');
73
	OIDplus::init(true, true);
74
	$db = OIDplus::db();
75
	dotest($db);
76
} catch (Exception $e) {
77
	echo "ODBC:\n";
78
	echo redtext($e->getMessage())."\n\n";
79
}
374 daniel-mar 80
 
81
# Test PgSQL
502 daniel-mar 82
try {
83
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'PgSQL');
84
	OIDplus::init(true, true);
85
	$db = OIDplus::db();
86
	dotest($db);
87
} catch (Exception $e) {
88
	echo "PgSQL:\n";
89
	echo redtext($e->getMessage())."\n\n";
90
}
374 daniel-mar 91
 
92
# Test SQLite3
502 daniel-mar 93
try {
94
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'SQLite');
95
	OIDplus::init(true, true);
96
	$db = OIDplus::db();
97
	dotest($db);
98
} catch (Exception $e) {
99
	echo "SQLite3:\n";
100
	echo redtext($e->getMessage())."\n\n";
101
}
374 daniel-mar 102
 
786 daniel-mar 103
# Test OCI8
104
try {
105
	OIDplus::baseConfig()->setValue('DATABASE_PLUGIN', 'OCI8');
106
	OIDplus::init(true, true);
107
	$db = OIDplus::db();
108
	dotest($db);
109
} catch (Exception $e) {
110
	echo "OCI8:\n";
111
	echo redtext($e->getMessage())."\n\n";
112
}
113
 
374 daniel-mar 114
# ---
115
 
502 daniel-mar 116
if (PHP_SAPI != 'cli') {
117
	die('</pre>');
118
} else {
119
	exit($num_errs > 0 ? 1 : 0);
120
}
374 daniel-mar 121
 
122
# ---
123
 
1130 daniel-mar 124
/**
125
 * @param \ViaThinkSoft\OIDplus\OIDplusDatabaseConnection $db
126
 * @return void
127
 */
128
function dotest(\ViaThinkSoft\OIDplus\OIDplusDatabaseConnection $db): string {
374 daniel-mar 129
	echo "Database: " . get_class($db) . "\n";
130
	try {
131
		$db->connect();
132
	} catch (Exception $e) {
133
		echo "Connection ".redtext("FAILED")." (check userdata/baseconfig/config.inc.php): ".$e->getMessage()."\n\n";
134
		return;
135
	}
136
	echo "Detected slang: " . $db->getSlang()::id()."\n";
137
	$db->query("delete from ###objects where parent = 'test:1'");
138
	$db->query("insert into ###objects (id, parent, title, description, confidential) values ('test:1.1', 'test:1', '', '', '0')");
139
	$db->query("insert into ###objects (id, parent, title, description, confidential) values ('test:1.2', 'test:1', '', '', '0')");
140
	try {
141
		// --- "SQL Date" handling
142
 
143
		try {
144
			$res = $db->query("update ###objects set created = ".$db->sqlDate()." where id = 'test:1.1'");
145
			echo "SQLDate (".$db->sqlDate().') '.greentext('PASSED')."\n";
146
		} catch (Exception $e) {
147
			echo "SQLDate (".$db->sqlDate().') '.redtext('FAILED')."\n";
148
		}
149
 
786 daniel-mar 150
		// --- Delete+Insert using prepared statements
151
 
152
		$db->query("delete from ###objects where parent = ?", array('test:11'));
153
		echo "Delete using Prepared Statements: ".greentext('PASSED')."\n";
154
		$db->query("insert into ###objects (id, parent, title, description, confidential) values (?, ?, ?, ?, ?)", array('oid:3.1.11', 'test:11', '', '', '0'));
155
		$db->query("insert into ###objects (id, parent, title, description, confidential) values (?, ?, ?, ?, ?)", array('oid:3.1.12', 'test:11', '', '', '0'));
156
		echo "Insert using Prepared Statements: ".greentext('PASSED')."\n";
157
		$db->query("update ###objects set confidential = ? where parent = ?", array(true, 'test:11'));
158
		echo "Update using Prepared Statements: ".greentext('PASSED')."\n";
159
 
374 daniel-mar 160
		// --- "Num rows" handling
783 daniel-mar 161
 
502 daniel-mar 162
		$res = $db->query("select id from ###objects where parent = ? order by id", array('test:XXXXXXXXX'));
163
		try {
164
			$num_rows = $res->num_rows();
165
		} catch (Exception $e) {
166
			$num_rows = $e->getMessage();
167
		}
168
		echo "Num rows empty: " . ($num_rows===0 ? greentext('PASSED') : redtext('FAILED'))." ($num_rows)\n";
374 daniel-mar 169
 
170
		$res = $db->query("select id from ###objects where parent = ? order by id", array('test:1'));
502 daniel-mar 171
		try {
172
			$num_rows = $res->num_rows();
173
		} catch (Exception $e) {
174
			$num_rows = $e->getMessage();
175
		}
176
		echo "Num rows: " . ($num_rows===2 ? greentext('PASSED') : redtext('FAILED'))." ($num_rows)\n";
374 daniel-mar 177
 
178
		$res->fetch_array();
502 daniel-mar 179
		try {
180
			$num_rows = $res->num_rows();
181
		} catch (Exception $e) {
182
			$num_rows = $e->getMessage();
183
		}
184
		echo "Num rows after something fetched: " . ($num_rows===2 ? greentext('PASSED') : redtext('FAILED'))." ($num_rows)\n";
374 daniel-mar 185
 
186
		$nextid = $res->fetch_array()['id'];
187
		echo "Num rows does not change cursor: " . ($nextid == 'test:1.2' ? greentext('PASSED') : redtext('FAILED'))."\n";
188
 
189
		$next = $res->fetch_array();
190
		echo "Fetch after EOF gives null: " . (is_null($next) ? greentext('PASSED') : redtext('FAILED'))."\n";
191
 
192
		// --- Simultanous prepared statements
783 daniel-mar 193
 
502 daniel-mar 194
		$errmsg = '';
195
		try {
196
			$res = $db->query("select id from ###objects where parent = ? order by id", array('test:1'));
197
			$passed = false;
198
			//$res->num_rows(); // sponge
199
			while ($row = $res->fetch_array()) {
200
				$res2 = $db->query("select id from ###objects where parent = ? order by id", array($row['id']));
201
				while ($row2 = $res2->fetch_array()) {
202
				}
203
				if ($row['id'] == 'test:1.2') {
204
					$passed = true;
205
				}
374 daniel-mar 206
			}
502 daniel-mar 207
		} catch (Exception $e) {
208
			$passed = false;
209
			$errmsg = ' ('.$e->getMessage().')';
374 daniel-mar 210
		}
502 daniel-mar 211
		echo "Simultanous prepared statements: ".($passed ? greentext('PASSED') : redtext('FAILED'))."$errmsg\n";
783 daniel-mar 212
 
502 daniel-mar 213
		if (!$passed) {
214
			// We cannot continue because the connection is in a state that cannot be recovered easily
215
			echo "Cancelled!\n\n";
216
			return;
217
		}
783 daniel-mar 218
 
374 daniel-mar 219
		// --- Exception handling
220
 
221
		try {
502 daniel-mar 222
			$db->query("SELECT * from ABCDEF");
374 daniel-mar 223
			echo "Exception for DirectQuery: ".redtext('FAILED').", no Exception thrown\n";
224
		} catch (Exception $e) {
783 daniel-mar 225
			if ((strpos($e->getMessage(), 'ABCDEF') !== false) || is_known_errormsg($e->getMessage())) {
374 daniel-mar 226
				echo "Exception for DirectQuery: ".greentext('PASSED')."\n";
227
			} else {
228
				echo "Exception for DirectQuery: ".redtext('FAILED').", does probably not contain DBMS error string\n";
229
			}
230
		}
231
 
232
		$msg = $db->error();
783 daniel-mar 233
		if ((strpos($msg, 'ABCDEF') !== false) || is_known_errormsg($msg)) {
374 daniel-mar 234
			echo "Error-Function after failed direct query: ".greentext('PASSED')."\n";
235
		} else {
236
			echo "Error-Function after failed direct query: ".redtext('FAILED').", does probably not contain DBMS error string ($msg)\n";
237
		}
238
 
239
		try {
502 daniel-mar 240
			$db->query("SELECT * from FEDCBA", array(''));
374 daniel-mar 241
			echo "Exception for PreparedQuery: ".redtext('FAILED').", no Exception thrown\n";
242
		} catch (Exception $e) {
783 daniel-mar 243
			if ((strpos($e->getMessage(), 'FEDCBA') !== false) || is_known_errormsg($e->getMessage())) {
374 daniel-mar 244
				echo "Exception for PreparedQuery: ".greentext('PASSED')."\n";
245
			} else {
246
				echo "Exception for PreparedQuery: ".redtext('FAILED').", does probably not contain DBMS error string\n";
247
			}
248
		}
249
 
250
		$msg = $db->error();
783 daniel-mar 251
		if ((strpos($msg, 'FEDCBA') !== false) || is_known_errormsg($msg)) {
374 daniel-mar 252
			echo "Error-Function after failed prepared query: ".greentext('PASSED')."\n";
253
		} else {
254
			echo "Error-Function after failed prepared query: ".redtext('FAILED').", does probably not contain DBMS error string ($msg)\n";
255
		}
256
 
257
		$db->query("select 1");
258
		$msg = $db->error();
259
		if (!$msg) {
260
			echo "Error-Function gets cleared after non-failed query: ".greentext('PASSED')."\n";
261
		} else {
262
			echo "Error-Function gets cleared after non-failed query: ".redtext('FAILED').", does probably not contain DBMS error string\n";
263
		}
264
 
265
		// --- Boolean handling
266
 
267
		$db->query("update ###objects set confidential = ? where id = 'test:1.1'", array(true));
268
		$res = $db->query("select confidential from ###objects where id = 'test:1.1'");
269
		$val = $res->fetch_object()->confidential;
502 daniel-mar 270
		echo "Boolean handling TRUE with prepared statement (fetch): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
271
		$res = $db->query("select * from ###objects where id = 'test:1.1' and confidential = ?", array(true));
272
		$val = $res->fetch_object();
273
		echo "Boolean handling TRUE with prepared statement (where): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
374 daniel-mar 274
 
275
		$db->query("update ###objects set confidential = ? where id = 'test:1.1'", array(false));
276
		$res = $db->query("select confidential from ###objects where id = 'test:1.1'");
277
		$val = $res->fetch_object()->confidential;
502 daniel-mar 278
		echo "Boolean handling FALSE with prepared statement (fetch): " . (!$val ? greentext('PASSED') : redtext('FAILED'))."\n";
279
		$res = $db->query("select * from ###objects where id = 'test:1.1' and confidential = ?", array(false));
280
		$val = $res->fetch_object();
281
		echo "Boolean handling FALSE with prepared statement (where): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
374 daniel-mar 282
 
502 daniel-mar 283
		$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 284
		$res = $db->query("select confidential from ###objects where id = 'test:1.1'");
285
		$val = $res->fetch_object()->confidential;
502 daniel-mar 286
		echo "Boolean handling TRUE with normal statement (fetch): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
287
		$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
288
		$val = $res->fetch_object();
289
		echo "Boolean handling TRUE with normal statement (where): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
374 daniel-mar 290
 
502 daniel-mar 291
		$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 292
		$res = $db->query("select confidential from ###objects where id = 'test:1.1'");
293
		$val = $res->fetch_object()->confidential;
502 daniel-mar 294
		echo "Boolean handling FALSE with normal statement (fetch): " . (!$val ? greentext('PASSED') : redtext('FAILED'))."\n";
295
		$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
296
		$val = $res->fetch_object();
297
		echo "Boolean handling FALSE with normal statement (where): " . ($val ? greentext('PASSED') : redtext('FAILED'))."\n";
783 daniel-mar 298
 
374 daniel-mar 299
		// --- Check if transactions work
783 daniel-mar 300
 
502 daniel-mar 301
		if ($db->transaction_supported()) {
302
			echo "Transaction support: " . greentext('Supported') ."\n";
374 daniel-mar 303
 
502 daniel-mar 304
			$db->query("update ###objects set title = 'A' where id = 'test:1.1'");
305
			$db->transaction_begin();
306
			$db->query("update ###objects set title = 'B' where id = 'test:1.1'");
307
			$db->transaction_rollback();
308
			$res = $db->query("select title from ###objects where id = 'test:1.1'");
309
			$val = $res->fetch_object()->title;
310
			echo "Transaction rollback: " . ($val == 'A' ? greentext('PASSED') : redtext('FAILED'))."\n";
374 daniel-mar 311
 
502 daniel-mar 312
			$db->query("update ###objects set title = 'A' where id = 'test:1.1'");
313
			$db->transaction_begin();
314
			$db->query("update ###objects set title = 'B' where id = 'test:1.1'");
315
			$db->transaction_commit();
316
			$res = $db->query("select title from ###objects where id = 'test:1.1'");
317
			$val = $res->fetch_object()->title;
318
			echo "Transaction commit: " . ($val == 'B' ? greentext('PASSED') : redtext('FAILED'))."\n";
319
		} else {
320
			echo "Transaction support: " . redtext('Not supported') ."\n";
321
		}
374 daniel-mar 322
 
323
		// --- Check natOrder feature
324
 
325
		$db->query("delete from ###objects where parent = 'test:1'");
326
		$db->query("insert into ###objects (id, parent, title, description, confidential) values ('oid:3.1.10', 'test:1', '', '', '0')");
327
		$db->query("insert into ###objects (id, parent, title, description, confidential) values ('oid:3.1.2', 'test:1', '', '', '0')");
328
		$res = $db->query("select id from ###objects where parent = ? order by ".$db->natOrder('id'), array('test:1'));
502 daniel-mar 329
		//$res->num_rows(); // sponge
374 daniel-mar 330
		$val = $res->fetch_object()->id;
331
		echo "Natural OID Sorting (< 16 Bit): " . ($val == 'oid:3.1.2' ? greentext('PASSED') : redtext('FAILED'))."\n";
332
 
333
		$db->query("delete from ###objects where parent = 'test:1'");
334
		$db->query("insert into ###objects (id, parent, title, description, confidential) values ('oid:2.25.317919736312109525688528068157180855579', 'test:1', '', '', '0')");
335
		$db->query("insert into ###objects (id, parent, title, description, confidential) values ('oid:2.25.67919736312109525688528068157180855579', 'test:1', '', '', '0')");
336
		$res = $db->query("select id from ###objects where parent = ? order by ".$db->natOrder('id'), array('test:1'));
502 daniel-mar 337
		//$res->num_rows(); // sponge
374 daniel-mar 338
		$val = $res->fetch_object()->id;
339
		echo "Natural OID Sorting (128 Bit): " . ($val == 'oid:2.25.67919736312109525688528068157180855579' ? greentext('PASSED') : redtext('FAILED'))."\n";
340
 
341
		$db->query("delete from ###objects where parent = 'test:1'");
342
		$db->query("insert into ###objects (id, parent, title, description, confidential) values ('abc:3.1.10', 'test:1', '', '', '0')");
343
		$db->query("insert into ###objects (id, parent, title, description, confidential) values ('abc:3.1.2', 'test:1', '', '', '0')");
344
		$res = $db->query("select id from ###objects where parent = ? order by ".$db->natOrder('id'), array('test:1'));
502 daniel-mar 345
		//$res->num_rows(); // sponge
374 daniel-mar 346
		$val = $res->fetch_object()->id;
347
		echo "Non-Natural Sorting for Non-OIDs: " . ($val == 'abc:3.1.10' ? greentext('PASSED') : redtext('FAILED'))."\n";
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
}